I wanted to write a program, which uses a serial port for controlling 2 stepper motors. I wanted to write it such that users have the least possible trouble. Especially during the startup phase, a dialog box should show all available serial ports and let the user select the right one.
And while trying to get all available serial ports, I stumbled across 3 nasty linux bugs:
Bug 1: Always 4 serial port devices
One of the intentions of udev was that only the devices, which are physically present on the system, appear as nodes in the
/devdirectory. A big step forward compared to the situation before. Unfortunately, the serial driver always creates
/dev/ttyS[0-3]. The reason (forgot the link) is that the ports of some exotic multi I/O board aren't detected properly. So the fix was to get 1000s of users into trouble instead of just making one special module parameter for that board.
Bug 2: open() succeeds for nonexistant ports
The manual page of open(2) says, that if one tries to open a device node with no physical device is behind it, errno is set to
ENXIO. In the case of a non existing serial port, a valid filedescriptor is returned
Bug 3: tcgetattr returns
According to the glibc manual
EIOis usually used for physical read/write errors. For regular files this error means, that you should backup your data because the disk is about to die. A physical read/write error can never happen on a device which is physically nonexistant.
The second best thing would be to return
ENXIO(no such device or address). The best thing would be to return
EBADF(bad file descriptor) because the
open()call before would have returned -1 already.
tcgetattr()doesn't succeed like
open()so it can be used for the detection routine below.
The good thing is that once there is a workaround, the problem is quickly forgotten. Here's mine (returns 1 if the port is a physically existant serial device, 0 else):
Of course this won't find ports, which are currently opened by another application.
int is_serial_port(const char * device)
int fd, ret = 1;
struct termios t;
fd = open(device, O_RDWR);
if(fd < 0)
ret = 0;