Re: Serial port arbitration (UUCP device locking protocol?)
Re: Serial port arbitration (UUCP device locking protocol?)
- Subject: Re: Serial port arbitration (UUCP device locking protocol?)
- From: Dan Bernstein <email@hidden>
- Date: Mon, 14 Feb 2005 19:56:55 +0200
Thanks! It doesn't get any better than a detailed reply from an Apple
engineer that includes sample code and doesn't include "please file a
bug" :-)
One question, though. When you say
> // 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.
How do I do that? Can I just write to the dialin device or should I
open the dialout?
-- Dan
On Sat, 12 Feb 2005 11:47:00 -0800, Godfrey van der Linden
<email@hidden> wrote:
> G'day Dan,
>
> 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.
>
> #include <IOKit/serial/ioss.h>
> #include <IOKit/serial/IOSerialKeys.h>
>
> // 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>
>
> 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
>
> // 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.
>
> 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 (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden