Keywords: Linux | Serial Ports | sysfs | C Programming
Abstract: This article explores methods to list all serial devices on a Linux system without opening them, addressing issues with traditional approaches like iterating over /dev/ttyS*. It focuses on using the /sys filesystem, specifically /sys/class/tty, to identify devices with serial drivers, avoiding unnecessary connections. Code examples in C demonstrate practical implementation, and alternative methods such as /dev/serial and dmesg commands are discussed.
Introduction
In Linux systems, enumerating serial devices such as ttyS and ttyUSB ports is a common task for developers working with hardware interfaces. Traditional methods, like iterating over /dev/ttyS*, are insufficient because they miss devices like USB serial adapters listed under /dev/ttyUSB*. This article addresses the proper way to detect all serial devices without opening them, which can prevent issues like unwanted connections to Bluetooth devices.
Challenges with Device Enumeration
Early approaches involved opening each device and using ioctl calls, such as TIOCGSERIAL, to check if it is a serial port. However, this method can trigger connections on certain devices, leading to timeouts or other side effects. For instance, on BSD systems, opening Bluetooth serial devices attempts to connect, causing delays. Thus, a non-intrusive method is preferred.
Leveraging the /sys Filesystem
The /sys filesystem in Linux provides a structured view of kernel devices. Specifically, the directory /sys/class/tty contains symbolic links to all TTY devices. By examining these links, we can identify serial ports without opening them. Each entry in /sys/class/tty corresponds to a TTY device, and those with a device/driver subdirectory are likely serial devices, as they are associated with serial drivers.
For example, listing /sys/class/tty/ttyUSB0/device/driver might show a link to a driver like ../../../bus/usb-serial/drivers/ftdi_sio/, indicating a USB serial adapter. Similarly, ttyS0 might link to a serial driver. This approach avoids the pitfalls of device opening.
Implementation in C
To programmatically list serial devices in C, we can traverse the /sys/class/tty directory and check for the presence of a device/driver entry. Below is a sample code snippet that demonstrates this method.
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
int main() {
DIR *dir;
struct dirent *entry;
char path[256];
dir = opendir("/sys/class/tty");
if (dir == NULL) {
perror("opendir");
return 1;
}
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
snprintf(path, sizeof(path), "/sys/class/tty/%s/device/driver", entry->d_name);
struct stat st;
if (stat(path, &st) == 0) {
printf("Serial device: %s
", entry->d_name);
}
}
closedir(dir);
return 0;
}This code opens the /sys/class/tty directory, iterates through each entry, and checks if the device/driver path exists. If it does, the device is considered a serial port. Note that this method may include some virtual terminals, so additional filtering might be needed based on the driver name.
Alternative Methods
Other approaches include using the /dev/serial directory available in recent kernels, which contains symbolic links to serial devices organized by ID and path. For example, /dev/serial/by-id/ and /dev/serial/by-path/ provide user-friendly names. Additionally, the command dmesg | grep tty can show TTY devices detected during boot, but it may not list all current devices and is not suitable for programmatic use.
From the reference articles, practical scenarios like using USB hubs with multiple serial devices at different baud rates are possible, as the serial configuration is handled per device, and the hub itself does not impose baud rate restrictions.
Conclusion
Detecting serial devices on Linux without opening them is efficiently achieved through the /sys filesystem. By leveraging /sys/class/tty and checking for driver associations, developers can avoid unnecessary device interactions. This method is robust, scalable, and aligns with modern Linux practices. For most applications, this approach provides a reliable solution, with alternatives like /dev/serial offering convenience in newer kernels.