10. Programming on Unix-systems: Linux

Linux' serial devices behave slightly different. Setting O_NDELAY on open causes all reads quickly to time out with "Device Busy", so either a busy loop is required, waiting for the device to emit or grab some data, or we can use select(2) with a specified time-out, which has to fit the behavior of the device at the other end of the wire, as shown in the previous section.

I have also noted that on transmission, to devices echoing the data sent to them, the receive buffer fills up quickly and must therefore be checked between each character output. Basically a loop of write(one char) followed by a select(2)/read(one char) is needed. Something like the following function may be useful, it basically opens the port, emits some string, waits for a replay for a couple seconds, and then returns the reply as read:


/* Perform a simple request/reply conversation with a device 
    sitting at some serial port 
    Hardcoded to 9600/no parity/8 data/1 stop
*/ 
int serconversatedev(char *comname, char *cmd, char *reply, int replen)
{
    int j, nw, nr, cdc, hix;
    struct termios ti;
    char *rpcat; 
    char rx[2];
    fd_set rfds;
    struct timeval tv;
    int retval;
    long ts, tu; 

    hix = open(comname, O_RDWR );

    tcgetattr(hix, &ti); 
    cfmakeraw(&ti); 
    ti.c_cflag = B9600 | CS8 | CREAD | CLOCAL; 
    ti.c_cc[VMIN] = 0;
    ti.c_cc[VTIME] = 2;
    tcsetattr(hix, TCSANOW, &ti); 

    cdc = strlen(cmd);

    /* Write, then read the echoed character in turn. */ 
    for(j = 0; j<cdc; j++)
    {
        nw = write(hix, cmd+j, 1); 
        serreadtimeout(hix, 0, 200000, rx, 1, &nr);
    }

    rpcat = reply;
    *rpcat = 0; 
    j = 0; 
    for(;;)
    {
        /* Select, wait for something to be read, then read it */ 
        if(j == 0)
        {
            /* Wait 1 second for the other device to respond.
                For some devices this can be too long, so this
                may want adustments */ 
            ts = 1;
            tu = 0; 
        }
        else
        {
            /* Wait 0.2 s for inter-char and post-transmission */ 
            ts = 0;
            tu = 200000; 
        }

        *rx = 0; 
        retval = serreadtimeout(hix, ts, tu, rx, 1, &nr);

        if(retval)
        {
            *rpcat++ = *rx;
            j++;
            if(j >= replen) break; 
        }
        else
        {
            break; 
        }
    }
    *rpcat = 0; 
    close(hix); 
    return(0);
}

Getting the port to work at all. -- the LSR Safety check message

On some portables, I have seen the /dev/ttyS0 serial port fail with a message LSR Safety check going into the system log when trying to open the port. This is caused by a conflict with another device driver, called smsc_ircc2 which gets in the way. To make the regular serial port work right, this smsc_ircc2 has to be blacklisted, in one of the *-blacklist.conf files in /etc/modprobe.d.

I thus added these lines to the /etc/modprobe.d/alsa-base-blacklist.conf:

blacklist smsc_ircc2
blacklist irda
blacklist crc_ccitt

The last two seemed to depend on the smsc_ircc2, and might not have to be blacklisted.

It isn't sufficient to remove it with modprobe -r or similar -- it does seem to bollix things for the serial port on installation, and leaves it broken from then on.

To 0. Introduction

To 9. Programming on Unix-systems: Xenix, HP-UX, BSD, and Linux

To 11. The other IO lines

Table of ASCII codes

To index page