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);
}
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 9. Programming on Unix-systems: Xenix, HP-UX, BSD, and Linux