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