Comment 12 for bug 661321

Revision history for this message
Michael Stevens (mail-michael-stevens) wrote :

I was hit with this problem using my GPS tracking application on a 2.6.38 Kernel. After much researching it seems this is NOT a driver bug but a CHANGE in the kernel Serial API semantics.

What has happened is that the API now blocks when a Serial device is not indicating it is ready. This included 'open' on a serial device which now can block.

Since lots of RS232 serial devices do not bother with any control signals such as DCD (data carrier detect) they never indicate they are ready and open blocks. This is the 'HANG' we are seeing, There are several workarounds:

1. Wire the RS232 "DCD" signal correctly. This is often not an option. In my case I am reading from a GPS mouse which has a built in RS232 to USB convertor.

2. Open the serial device non-blocking and set the CLOCAL flag on the device. From the tty_ioctl documentation:

Marking a line as local

If the CLOCAL flag for a line is off, the hardware carrier detect (DCD) signal is significant, and an open(2) of the corresponding tty will block until DCD is asserted, unless the O_NONBLOCK flag is given. If CLOCAL is set, the line behaves as if DCD is always asserted. The software carrier flag is usually turned on for local devices, and is off for lines with modems.

In my code I open and explicitly set CLOCAL

    qDebug ( devPath );
    file = open(devPath.ascii(), O_RDONLY | O_NONBLOCK);
    if ( file != -1 ) {
        // serial port needs to be setup
        struct termios term;
        bool ok = tcgetattr ( file, &term ) == 0;
        cfmakeraw ( &term );
        term.c_iflag |= CLOCAL;
        cfsetispeed( &term, B4800 );
        ok &= tcsetattr ( file, TCSAFLUSH, &term ) == 0;
      ....

Sadly after you 'open' a device non-blocking you cannot change it back to blocking. The simplist solution is to use a 'poll' before you 'read' to wait for new data to arrive.

        // read byte from non blocking file
        pollfd pfd;
        pfd.fd = file;
        pfd.events = POLLIN;
        int ok = poll( &pfd, 1, -1 );
        if ( ok < 0 ) {
             // poll failed
        }
        char buf[1];
        ssize_t ch = read( file, buf, 1 );
        if ( ch < 1 ) {
             // read error

3. A third alternative, which I have not tried, is to use "stty" to set the serial device CLOCAL before your application starts.

Hopefully this help clear up the issue. I would recommend setting this bugs status to INVALID. Possibly the perceived regression on the USB serial drivers could be resolved in that they default to being CLOCAL which is what I assume was the case before. But another change to the Kernal semantics would probably just confuse the issue!