Re: Serial port arbitration (UUCP device locking protocol?)
site_archiver@lists.apple.com Delivered-To: darwin-dev@lists.apple.com G'day Dan, #include <IOKit/serial/ioss.h> #include <IOKit/serial/IOSerialKeys.h> int fd; int preempt; tryAgain: for (;;) { do { fd = open(<tty.dialindevice>, O_NONBLOCK); if (-1 == fd) if (EBUSY == errno) continue; else exit(1); // Fatal error // clear O_NONBLOCK flag if (-1 == fcntl(fd, F_SETFL, 0)) exit(1); // fatal error if (-1 == ioctl(fd, TIOCEXCL, 0)) if (EBUSY == errno) { close(fd); fd = -1; continue; } else exit(1); // Fatal error } while(false); if (-1 != fd) break; // We must have had an EBUSY error to be here so wait for idle if (kIOReturnSuccess != IORegistryEntrySetProperty(portre, CFSTR(kIOTTYWaitForIdleKey), kCFBooleanTrue) exit(1); // Fatal error }; // At this point you have the port exclusively and non-preemtibly // so now is a good time to program up the modem or do any other // equipment initialisation you desire. preempt = 0;` if (-1 == ioctl(fd, IOSSPREEMPT, &preempt)) exit(1); // Fatal error // Now the port is in the preemptible - blocking state // time to issue a blocking read or a select as needed // waiting for whatever event you are want. if (-1 == read(fd, buf, sizeof(buf)) // Pseudo code if (EIO == errno) { close(fd); // We have been preempted. goto tryAgain; } else exit(1); // probably fatal // We have a connection and it is time to say we are no longer // preemptible preempt = 1; if (-1 == ioctl(fd, IOSSPREEMPT, &preempt)) exit(1); // Fatal error Hope this helps Godfrey van der Linden IOSerialFamily (for my sins) _______________________________________________ Do not post admin requests to the list. They will be ignored. Darwin-dev mailing list (Darwin-dev@lists.apple.com) Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/darwin-dev/site_archiver%40lists.appl... I'll use this email to write up IOSerialFamily arbitration then I'll add a man page, probably ioss(4). The traditional UNIX arbitration has some serious limitations. One of which is that it is very difficult to have a port open waiting for a fax or data connection and then allow the computer to dial out. Traditionally this problem was solved with lock files that often contain PIDs. However there are so many different types and implementations of the lock file method it is rarely reliable, especially in an opensource environment. So when I studied the various different implementations around when asked to work on the NeXT serial ports I felt that the tty.*/cu.* mechanism was the cleanest. In those days the modem cable could be relied upon to correctly wire the carrier detect line. The semantic was clean that is the getty could open the tty.* line and it would block until carrier was raised. While the getty was blocked in open the cu.* line could be opened arbitrarily. The worked very well. The problem came with fax modems which do not raise the carrier and Mac's which don't usually wire carrier into their serial cables. When this happens the enitire NeXT/BSD arbitration model breaks down. Yet I still liked the basic model. A couple of years ago (yes it has taken a long time to document this hasn't it, my only excuse is that I have been busy ;-) I had a hard look at serial arbitration and did a bit of comparative research and it seemed that the state of the art was to use UUCP style lock files. I just hated that solution. So I chose to stick with the basic design of the NeXT/BSD model but implement the concept of preemptable ports. One other thing before I go into the details. Traditionally UNIX will allow multiple clients to open the same serial port. It provided the TIOCEXCL flag as a mechanism to fail future opens of the same port but TIOCEXCL had the problem of not being atomic. Hence it was possible for 2 processes to successfully open the port then both attempt to promote it to exclusive access, they would both succeed. I changed the implementation of TIOCEXCL to provide for a true atomic test. If one process attempts to lock exclusively and another process has beaten it then the ioctl will fail. Finally there had to be a mechanism to work out whether an exclusive port became idle. I couldn't use a blocking open() statement as it was supposed to return failure if the port was set to be exclusive. And I didn't like the idea of creating another minor device to exclusively for blocking a port and I obviously couldn't use an ioctl (you need an open port to do that). Being an IOKit architect I decided to use the IOKit model to control this behaviour. To the end I have created a new "property" called waitForIdle that will block, interruptibly, till the serial port goes idle, i.e. neither the cu.* nor the tty.* line is currently open. So putting all of this together to reliably open a serial port on MacOSX use the following procedure, note there are still race conditions involved but you can be reasonably certain that you have a port reliably. To open a dialin device (getty/efax), NB this is code I have written of the top of my head and hasn't been compiled, I make no promises that it works as written you may need to fix minor errors in spelling. // iterate over I/O registry to find desired node and keep your // registry entry node (See DTS docs for a write up on how to do this) io_registry_entry_t portre = <do the search> // That's it while we are running non-preemtibly/exclusive further // attempts to open the port on either cu.* or tty.* will fail with EBUSY That is about it, I should note that the callout line is much simpler but if it gets EBUSY returned from the open it should also spin around kIOTTYWaitForIdleKey property. This email sent to site_archiver@lists.apple.com
participants (1)
-
Godfrey van der Linden