¿Cómo encontrar todos los dispositivos serie (ttyS, ttyUSB, …) en Linux sin abrirlos?

¿Cuál es la forma correcta de obtener una lista de todos los puertos / dispositivos seriales disponibles en un sistema Linux?

En otras palabras, cuando itero sobre todos los dispositivos en /dev/ , ¿cómo puedo decir cuáles son los puertos seriales de la manera clásica, es decir, aquellos que generalmente soportan velocidades en baudios y control de flujo RTS / CTS ?

La solución se codificaría en C.

Lo pregunto porque estoy usando una biblioteca de terceros que hace esto claramente incorrecto: parece iterar solo sobre /dev/ttyS* . El problema es que existen, por ejemplo, puertos serie sobre USB (proporcionados por adaptadores USB-RS232), y esos se enumeran en / dev / ttyUSB *. Y al leer el Serial-HOWTO en Linux.org , tengo la idea de que también habrá otros espacios de nombres, a medida que llegue el momento.

Entonces necesito encontrar la forma oficial para detectar dispositivos seriales. El problema es que ninguno parece estar documentado, o no puedo encontrarlo.

Imagino que una forma sería abrir todos los archivos desde /dev/tty* y llamar a un ioctl() específico en ellos que solo está disponible en dispositivos seriales. ¿Sería esa una buena solución, sin embargo?

Actualizar

Hickards sugirió mirar la fuente de “setserial”. Su código hace exactamente lo que tenía en mente:

Primero, abre un dispositivo con:

 fd = open (path, O_RDWR | O_NONBLOCK) 

Entonces invoca:

 ioctl (fd, TIOCGSERIAL, &serinfo) 

Si esa llamada no devuelve ningún error, aparentemente es un dispositivo en serie.

Encontré un código similar en Serial Programming / termios , que sugería agregar también la opción O_NOCTTY .

Sin embargo, hay un problema con este enfoque:

Cuando probé este código en BSD Unix (es decir, Mac OS X), funcionó también. Sin embargo , los dispositivos en serie que se proporcionan a través de Bluetooth hacen que el sistema (controlador) intente conectarse al dispositivo Bluetooth, lo que demora un poco antes de que vuelva con un error de tiempo de espera. Esto es causado por solo abrir el dispositivo. Y puedo imaginar que cosas similares pueden suceder también en Linux, idealmente, no debería necesitar abrir el dispositivo para averiguar su tipo. Me pregunto si también hay una manera de invocar funciones ioctl sin abrir, o abrir un dispositivo de forma que no provoque conexiones.

¿Que debería hacer?

El sistema de archivos /sys debe contener mucha información para su búsqueda. Mi sistema (2.6.32-40-generic # 87-Ubuntu) sugiere:

 /sys/class/tty 

Lo que le da descripciones de todos los dispositivos TTY que conoce el sistema. Un ejemplo recortado:

 # 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/ 

Siguiendo uno de estos enlaces:

 # ll /sys/class/tty/ttyUSB0/ insgesamt 0 drwxr-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 dev lrwxrwxrwx 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 

Aquí el archivo dev contiene esta información:

 # cat /sys/class/tty/ttyUSB0/dev 188:0 

Este es el nodo mayor / menor. Se pueden buscar en el directorio /dev para obtener nombres fáciles de usar:

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

El directorio /sys/class/tty contiene todos los dispositivos TTY, pero es posible que desee excluir esos molestos terminales virtuales y pseudo terminales. Le sugiero que examine solo aquellos que tienen una entrada de device/driver :

 # ll /sys/class/tty/*/device/driver lrwxrwxrwx 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/ 

En kernels recientes (no estoy seguro desde cuándo) puede listar el contenido de / dev / serial para obtener una lista de los puertos seriales en su sistema. En realidad, son enlaces simbólicos que apuntan al / dev / nodo correcto:

 flu0@laptop:~$ ls /dev/serial/ total 0 drwxr-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 0 lrwxrwxrwx 1 root root 13 2011-07-20 17:12 usb-Prolific_Technology_Inc._USB-Serial_Controller-if00-port0 -> ../../ttyUSB0 flu0@laptop:~$ ls /dev/serial/by-path/ total 0 lrwxrwxrwx 1 root root 13 2011-07-20 17:12 pci-0000:00:0b.0-usb-0:3:1.0-port0 -> ../../ttyUSB0 

Este es un adaptador USB-Serial, como puede ver. Tenga en cuenta que cuando no hay puertos seriales en el sistema, el directorio / dev / serial / no existe. Espero que esto ayude :).

Estoy haciendo algo como el siguiente código. Funciona para dispositivos USB y también para el estúpido serial8250-devuices del que todos tenemos 30, pero solo un par de ellos realmente funciona.

Básicamente uso el concepto de respuestas anteriores. Primero enumere todos los tty-devices en / sys / class / tty /. Los dispositivos que no contienen un / subdir dispositivo se filtran. / sys / class / tty / console es un dispositivo de este tipo. A continuación, los dispositivos que en realidad contienen un dispositivo en ese momento se aceptan como puerto en serie válido según el destino del controlador-symlink fx.

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

y para ttyS0

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

Todos los controladores accionados por serial8250 deben ser sondas usando el ioctl mencionado anteriormente.

  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 

Solo el puerto que informa un tipo de dispositivo válido es válido.

La fuente completa para enumerar los serialports tiene este aspecto. Las adiciones son bienvenidas.

 #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  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& comList, list& 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& comList, list comList8250) { struct serial_struct serinfo; list::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 getComList() { int n; struct dirent **namelist; list comList; list 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 l = getComList(); list::iterator it = l.begin(); while (it != l.end()) { cout << *it << endl; it++; } return 0; } 

Creo que encontré la respuesta en la documentación fuente de mi kernel: /usr/src/linux-2.6.37-rc3/Documentation/filesystems/proc.txt

 1.7 TTY info in /proc/tty ------------------------- Information about the available and actually used tty's can be found in the directory /proc/tty.You'll find entries for drivers and line disciplines in this directory, as shown in Table 1-11. Table 1-11: Files in /proc/tty .............................................................................. File Content drivers list of drivers and their usage ldiscs registered line disciplines driver/serial usage statistic and status of single tty lines .............................................................................. To see which tty's are currently in use, you can simply look into the file /proc/tty/drivers: > cat /proc/tty/drivers pty_slave /dev/pts 136 0-255 pty:slave pty_master /dev/ptm 128 0-255 pty:master pty_slave /dev/ttyp 3 0-255 pty:slave pty_master /dev/pty 2 0-255 pty:master serial /dev/cua 5 64-67 serial:callout serial /dev/ttyS 4 64-67 serial /dev/tty0 /dev/tty0 4 0 system:vtmaster /dev/ptmx /dev/ptmx 5 2 system /dev/console /dev/console 5 1 system:console /dev/tty /dev/tty 5 0 system:/dev/tty unknown /dev/tty 4 1-63 console 

Aquí hay un enlace a este archivo: http://git.kernel.org/?p=linux/kernel/git/next/linux-next.git;a=blob_plain;f=Documentation/filesystems/proc.txt;hb = e8883f8057c0f7c9950fa9f20568f37bfa62f34a

setserial con la opción -g parece hacer lo que usted desea y la fuente C está disponible en http://www.koders.com/c/fid39344DABD14604E70DF1B8FEA7D920A94AF78BF8.aspx .

No tengo ningún dispositivo serial aquí para probarlo, pero si tienes python y dbus puedes probarlo tú mismo.

 import dbus bus = dbus.SystemBus() hwmanager = bus.get_object('org.freedesktop.Hal', '/org/freedesktop/Hal/Manager') hwmanager_i = dbus.Interface(hwmanager, 'org.freedesktop.Hal.Manager') print hwmanager_i.FindDeviceByCapability("serial") 

Si falla, puede buscar dentro de hwmanager_i.GetAllDevicesWithProperties() para ver si el nombre de la capacidad “serial” que acabo de adivinar tiene un nombre diferente.

HTH

No tengo un dispositivo serie USB, pero debe haber una forma de encontrar los puertos reales usando las bibliotecas HAL directamente:

 ==================================================================== #! /usr/bin/env bash # # Uses HAL to find existing serial hardware # for sport in $(hal-find-by-capability --capability serial) ; do hal-get-property --udi "${sport}" --key serial.device done ==================================================================== 

El código python-dbus publicado ni este script sh enumeran los dispositivos bluetooth / dev / rfcomm *, por lo que no es la mejor solución.

Tenga en cuenta que en otras plataformas Unix, los puertos serie no se llaman ttyS? e incluso en Linux, algunas tarjetas de serie le permiten nombrar los dispositivos. Asumir un patrón en los nombres de los dispositivos seriales es incorrecto.

El uso de / proc / tty / drivers solo indica qué controladores tty están cargados. Si está buscando una lista de puertos serie, eche un vistazo a / dev / serial, tendrá dos subdirectorios: by-id y by-path.

EX:

 # find . -type l ./by-path/usb-0:1.1:1.0-port0 ./by-id/usb-Prolific_Technology_Inc._USB-Serial_Controller-if00-port0 

Gracias a esta publicación: https://superuser.com/questions/131044/how-do-i-know-which-dev-ttys-is-my-serial-port

encontré

 dmesg | grep tty 

haciendo el trabajo.

La biblioteca del administrador de comunicación en serie tiene muchas API y funciones destinadas a la tarea que desea. Si el dispositivo es un USB-UART, se puede usar su VID / PID. Si el dispositivo es BT-SPP, se pueden usar API específicas de la plataforma. Eche un vistazo a este proyecto para la progtwigción de puertos serie github.com/RishiGupta12/serial-communication-manager

Mi enfoque a través del marcado de grupo para obtener cada tty con el usuario ‘dialout’ ls -l /dev/tty* | grep 'dialout' ls -l /dev/tty* | grep 'dialout' para obtener solo su carpeta ls -l /dev/tty* | grep 'dialout' | rev | cut -d " " -f1 | rev ls -l /dev/tty* | grep 'dialout' | rev | cut -d " " -f1 | rev

fácil escuchar la salida tty, por ejemplo, cuando sale la serie de head --lines 1 < /dev/ttyUSB0 : head --lines 1 < /dev/ttyUSB0

escucha cada tty solo para una línea: for i in $(ls -l /dev/tty* | grep 'dialout' | rev | cut -d " " -f1 | rev); do head --lines 1 < $i; done for i in $(ls -l /dev/tty* | grep 'dialout' | rev | cut -d " " -f1 | rev); do head --lines 1 < $i; done

Me gusta mucho el enfoque al buscar controladores: ll /sys/class/tty/*/device/driver

Puede elegir el tty-Name ahora: ls /sys/class/tty/*/device/driver | grep 'driver' | cut -d "/" -f 5 ls /sys/class/tty/*/device/driver | grep 'driver' | cut -d "/" -f 5