Re: Serial device open(2) hang.
Re: Serial device open(2) hang.
- Subject: Re: Serial device open(2) hang.
- From: Terry Lambert <email@hidden>
- Date: Tue, 30 Jun 2009 03:51:07 -0700
On Jun 28, 2009, at 2:22 PM, David Elliott wrote:
Hi Steve,
On Jun 28, 2009, at 2:03 PM, Steve Checkoway wrote:
On Jun 27, 2009, at 9:14 PM, David Elliott wrote:
Change your open call to O_RDWR | O_NONBLOCK. This will cause
open not to block thus allowing you to get a working file
descriptor and keeping a handle open to the device (this is
important).
Once you have that you need to run stty against it or I suppose
you can use termios functions but honestly I am not all that
familiar with serial programming, I just happened to be doing it
the other week on another project.
What I came up with was this:
This worked perfectly! I now have my computer talking to itself via
a null modem cable with both ends plugged into the usb to serial
adapter. Time to see if I can talk to this microcontroller.
Glad to hear it. If you look over on darwin-kernel I've just posted
a message about SerialKDPProxy which is where that code comes from.
That is the project I referred to that I happened to be working on
the other week.
The file that the code snippet comes from can be found in SVN here:
http://tgwbd.org/svn/Darwin/SerialKDPProxy/trunk/src/SerialKDPProxy.c
Probably of no interest to you but there you have it.
And Kevin (kvv) is right. I could have and probably should have
used posix_spawn rather than fork/dup2/exec except I completely
forgot that it existed in relatively modern POSIX.
-Dave
There are two types of serial devices; those that have modem-control,
and those that don't.
Your open is hanging because you are opening a modem-control device,
but you are doing it without a real null-modem cable. A real null-
modem cable would be holding DCD and RTS high, so the open would
complete because it believed that carrier was present and hardware
flow control, if enabled, was actively soliciting output (this is the
crtscts you're enabling, probably unnecessarily, unless the device on
the other end needs it).
You can either elect to open the other (non-modem control) device, or
you can use O_NONBLOCK. If you use the O_NONBLOCK, on the modem
control device, you should set the CLOCAL flag to indicate that it's a
local connection (this is the same as using the non-modem control
device). Since O_NONBLOCK also sets non-blocking I/O, after setting
the CLOCAL flag, you're going to want to use fcntl() to clear non-
blocking I/O, or you will have to be careful to internally buffer the
data yourself.
Generally, it's usually easier to just treat everything as a modem-
control device, just in case the vendor messed up the non-modem-
control driver.
You also really don't want to use the fork/exec or spawn of an stty
approach, since not all serial devices support non-exclusive opens,
and the stty as a child process gets its descriptor from the parent
process, and if it's an exclusive open, it can fail based on what the
device driver implements under the covers. For example, there have
historically been RS-422 cards that fanned out to multiple serial
ports on a daughter box, and supported only a single unidirectional I/
O channel; you read, or you write, but you don't both read and write
at the same time. Similarly, if you were on a SVR3 system, then you
would need to deal with the partial open hack, since getty attempts to
open the device exclusively, and if you are switching off, you have to
force the O_EXCL flag off by doing a blocking open and alarming out of
it followed by a non-blocking open. For SVR4 / Solaris streams-based
serial devices, you also have to pop of the canonical processing
module, which is pushed on to the tty by the kernel.
Also, you will want to make sure that if your program is multithreaded
that it correctly uses pthread_kill() to interrupt outstanding
operations on the other threads before you close the device, since BSD-
based systems don't do a drain-wait and won't interrupt the
outstanding blocking read/write call merely because you're closing the
descriptor out from under them. Yeah, that means you have to record
outstanding reads and writes and the threads involved in your own
session structure and resource-track them yourself. This is basically
becauses you can't do an arbitrary wakeup, since there are multiple
addresses that the underlying driver might be sleeping on, and you
won't know which one is the right one, based on whether you're in a
mutext in the line discipline, the termios processing, a separate
"other" line discipline, or an IOWorkloop or a mutex in the subclass
of the IOSerialFamily.
For Mac OS X, and other UNIX conformant systems, it's basically
something like:
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <fcntl.h>
int
main(int ac, char *av[])
{
int fd;
int flags;
struct termios termios;
if (ac != 2) {
fprintf(stderr, "usage: ser <devicename>\n");
exit(2);
}
/* Open with non-blocking I/O because we are using the wrong device */
if ((fd = open(av[1], O_RDWR | O_NDELAY)) == -1) {
perror("open");
exit(1);
}
/* get the current settings */
if (tcgetattr(fd, &termios) == -1) {
perror("tcgetattr");
exit(1);
}
/* tell it to ignore modem control signals */
termios.c_cflag |= CLOCAL;
/* Set the input/output speed */
/* NOTE: B115200 not available in a strict POSIX environment */
if (cfsetispeed(&termios, B115200) == -1) {
perror("cfsetispeed");
exit(1);
}
if (cfsetospeed(&termios, B115200) == -1) {
perror("cfsetispeed");
exit(1);
}
if (tcsetattr(fd, TCSANOW, &termios) == -1) {
perror("tcgetattr");
exit(1);
}
/* Turn blocking I/O back on */
if ((flags = fcntl(fd, F_GETFL)) == -1) {
perror("tcgetattr");
exit(1);
}
flags &= ~O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) == -1) {
perror("tcgetattr");
exit(1);
}
printf("Do the rest of your stuff here...\n");
exit(0);
}
-- Terry
_______________________________________________
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