How to find all serial devices (ttyS, ttyUSB, ..) on Linux without opening them? How to find all serial devices (ttyS, ttyUSB, ..) on Linux without opening them? linux linux

How to find all serial devices (ttyS, ttyUSB, ..) on Linux without opening them?


The /sys filesystem should contain plenty information for your quest. My system (2.6.32-40-generic #87-Ubuntu) suggests:

/sys/class/tty

Which gives you descriptions of all TTY devices known to the system. A trimmed down example:

# ll /sys/class/tty/ttyUSB*lrwxrwxrwx 1 root root 0 2012-03-28 20:43 /sys/class/tty/ttyUSB0 -> ../../devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.4/2-1.4:1.0/ttyUSB0/tty/ttyUSB0/lrwxrwxrwx 1 root root 0 2012-03-28 20:44 /sys/class/tty/ttyUSB1 -> ../../devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.0/ttyUSB1/tty/ttyUSB1/

Following one of these links:

# ll /sys/class/tty/ttyUSB0/insgesamt 0drwxr-xr-x 3 root root    0 2012-03-28 20:43 ./drwxr-xr-x 3 root root    0 2012-03-28 20:43 ../-r--r--r-- 1 root root 4096 2012-03-28 20:49 devlrwxrwxrwx 1 root root    0 2012-03-28 20:43 device -> ../../../ttyUSB0/drwxr-xr-x 2 root root    0 2012-03-28 20:49 power/lrwxrwxrwx 1 root root    0 2012-03-28 20:43 subsystem -> ../../../../../../../../../../class/tty/-rw-r--r-- 1 root root 4096 2012-03-28 20:43 uevent

Here the dev file contains this information:

# cat /sys/class/tty/ttyUSB0/dev188:0

This is the major/minor node. These can be searched in the /dev directory to get user-friendly names:

# ll -R /dev |grep "188, *0"crw-rw----   1 root dialout 188,   0 2012-03-28 20:44 ttyUSB0

The /sys/class/tty dir contains all TTY devices but you might want to exclude those pesky virtual terminals and pseudo terminals. I suggest you examine only those which have a device/driver entry:

# ll /sys/class/tty/*/device/driverlrwxrwxrwx 1 root root 0 2012-03-28 19:07 /sys/class/tty/ttyS0/device/driver -> ../../../bus/pnp/drivers/serial/lrwxrwxrwx 1 root root 0 2012-03-28 19:07 /sys/class/tty/ttyS1/device/driver -> ../../../bus/pnp/drivers/serial/lrwxrwxrwx 1 root root 0 2012-03-28 19:07 /sys/class/tty/ttyS2/device/driver -> ../../../bus/platform/drivers/serial8250/lrwxrwxrwx 1 root root 0 2012-03-28 19:07 /sys/class/tty/ttyS3/device/driver -> ../../../bus/platform/drivers/serial8250/lrwxrwxrwx 1 root root 0 2012-03-28 20:43 /sys/class/tty/ttyUSB0/device/driver -> ../../../../../../../../bus/usb-serial/drivers/ftdi_sio/lrwxrwxrwx 1 root root 0 2012-03-28 21:15 /sys/class/tty/ttyUSB1/device/driver -> ../../../../../../../../bus/usb-serial/drivers/ftdi_sio/


In recent kernels (not sure since when) you can list the contents of /dev/serial to get a list of the serial ports on your system. They are actually symlinks pointing to the correct /dev/ node:

flu0@laptop:~$ ls /dev/serial/total 0drwxr-xr-x 2 root root 60 2011-07-20 17:12 by-id/drwxr-xr-x 2 root root 60 2011-07-20 17:12 by-path/flu0@laptop:~$ ls /dev/serial/by-id/total 0lrwxrwxrwx 1 root root 13 2011-07-20 17:12 usb-Prolific_Technology_Inc._USB-Serial_Controller-if00-port0 -> ../../ttyUSB0flu0@laptop:~$ ls /dev/serial/by-path/total 0lrwxrwxrwx 1 root root 13 2011-07-20 17:12 pci-0000:00:0b.0-usb-0:3:1.0-port0 -> ../../ttyUSB0

This is a USB-Serial adapter, as you can see. Note that when there are no serial ports on the system, the /dev/serial/ directory does not exists. Hope this helps :).


I'm doing something like the following code. It works for USB-devices and also the stupid serial8250-devuices that we all have 30 of - but only a couple of them realy works.

Basically I use concept from previous answers. First enumerate all tty-devices in /sys/class/tty/. Devices that does not contain a /device subdir is filtered away. /sys/class/tty/console is such a device. Then the devices actually containing a devices in then accepted as valid serial-port depending on the target of the driver-symlink fx.

$ ls -al /sys/class/tty/ttyUSB0//device/driverlrwxrwxrwx 1 root root 0 sep  6 21:28 /sys/class/tty/ttyUSB0//device/driver -> ../../../bus/platform/drivers/usbserial

and for ttyS0

$ ls -al /sys/class/tty/ttyS0//device/driverlrwxrwxrwx 1 root root 0 sep  6 21:28 /sys/class/tty/ttyS0//device/driver -> ../../../bus/platform/drivers/serial8250

All drivers driven by serial8250 must be probes using the previously mentioned ioctl.

        if (ioctl(fd, TIOCGSERIAL, &serinfo)==0) {            // If device type is no PORT_UNKNOWN we accept the port            if (serinfo.type != PORT_UNKNOWN)                the_port_is_valid

Only port reporting a valid device-type is valid.

The complete source for enumerating the serialports looks like this. Additions are welcome.

#include <stdlib.h>#include <dirent.h>#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>#include <string.h>#include <fcntl.h>#include <termios.h>#include <sys/ioctl.h>#include <linux/serial.h>#include <iostream>#include <list>using namespace std;static string get_driver(const string& tty) {    struct stat st;    string devicedir = tty;    // Append '/device' to the tty-path    devicedir += "/device";    // Stat the devicedir and handle it if it is a symlink    if (lstat(devicedir.c_str(), &st)==0 && S_ISLNK(st.st_mode)) {        char buffer[1024];        memset(buffer, 0, sizeof(buffer));        // Append '/driver' and return basename of the target        devicedir += "/driver";        if (readlink(devicedir.c_str(), buffer, sizeof(buffer)) > 0)            return basename(buffer);    }    return "";}static void register_comport( list<string>& comList, list<string>& comList8250, const string& dir) {    // Get the driver the device is using    string driver = get_driver(dir);    // Skip devices without a driver    if (driver.size() > 0) {        string devfile = string("/dev/") + basename(dir.c_str());        // Put serial8250-devices in a seperate list        if (driver == "serial8250") {            comList8250.push_back(devfile);        } else            comList.push_back(devfile);     }}static void probe_serial8250_comports(list<string>& comList, list<string> comList8250) {    struct serial_struct serinfo;    list<string>::iterator it = comList8250.begin();    // Iterate over all serial8250-devices    while (it != comList8250.end()) {        // Try to open the device        int fd = open((*it).c_str(), O_RDWR | O_NONBLOCK | O_NOCTTY);        if (fd >= 0) {            // Get serial_info            if (ioctl(fd, TIOCGSERIAL, &serinfo)==0) {                // If device type is no PORT_UNKNOWN we accept the port                if (serinfo.type != PORT_UNKNOWN)                    comList.push_back(*it);            }            close(fd);        }        it ++;    }}list<string> getComList() {    int n;    struct dirent **namelist;    list<string> comList;    list<string> comList8250;    const char* sysdir = "/sys/class/tty/";    // Scan through /sys/class/tty - it contains all tty-devices in the system    n = scandir(sysdir, &namelist, NULL, NULL);    if (n < 0)        perror("scandir");    else {        while (n--) {            if (strcmp(namelist[n]->d_name,"..") && strcmp(namelist[n]->d_name,".")) {                // Construct full absolute file path                string devicedir = sysdir;                devicedir += namelist[n]->d_name;                // Register the device                register_comport(comList, comList8250, devicedir);            }            free(namelist[n]);        }        free(namelist);    }    // Only non-serial8250 has been added to comList without any further testing    // serial8250-devices must be probe to check for validity    probe_serial8250_comports(comList, comList8250);    // Return the lsit of detected comports    return comList;}int main() {    list<string> l = getComList();    list<string>::iterator it = l.begin();    while (it != l.end()) {        cout << *it << endl;        it++;    }    return 0;   }