Как найти все последовательные устройства (ttyS, ttyUSB, ..) в Linux, не открывая их? - PullRequest
96 голосов
/ 27 марта 2010

Как правильно получить список всех доступных последовательных портов / устройств в системе Linux?

Другими словами, когда я перебираю все устройства в /dev/, как мне сказать, какие из них являются последовательными портами классическим способом, то есть те, которые обычно поддерживают скорости передачи данных и RTS / CTS управление потоком?

Решение будет закодировано в C.

Я спрашиваю, потому что я использую стороннюю библиотеку, которая делает это явно неправильно: похоже, она перебирает только /dev/ttyS*. Проблема в том, что, например, есть последовательные порты через USB (предоставляемые адаптерами USB-RS232), и они перечислены в / dev / ttyUSB *. И, прочитав Serial-HOWTO на Linux.org , я понял, что со временем появятся и другие пространства имен.

Поэтому мне нужно найти официальный способ обнаружения последовательных устройств. Проблема в том, что ни одна из них не документирована, или я не могу ее найти.

Я думаю, что одним из способов было бы открыть все файлы из /dev/tty* и вызвать для них определенный ioctl(), который доступен только на последовательных устройствах. Это было бы хорошим решением, хотя?

Обновление

hrickards предложил поискать источник "setserial". Его код делает именно то, что я имел в виду:

Сначала открывается устройство с:

fd = open (path, O_RDWR | O_NONBLOCK)

Затем он вызывает:

ioctl (fd, TIOCGSERIAL, &serinfo)

Если этот вызов не возвращает ошибку, то это, очевидно, последовательное устройство.

Я нашел похожий код в Последовательное программирование / termios , в котором также предлагается добавить опцию O_NOCTTY.

С этим подходом есть одна проблема:

Когда я тестировал этот код в BSD Unix (то есть в Mac OS X), он также работал. Однако , последовательные устройства, которые предоставляются через Bluetooth, заставляют систему (драйвер) пытаться подключиться к устройству Bluetooth, что занимает некоторое время, прежде чем оно вернется с ошибкой тайм-аута. Это вызвано просто открытием устройства. И я могу представить, что подобные вещи могут происходить и в Linux - в идеале, мне не нужно открывать устройство, чтобы выяснить его тип. Интересно, есть ли способ вызвать функции ioctl без открытия или открыть устройство таким образом, чтобы оно не вызывало соединения?

Что мне делать?

Ответы [ 11 ]

66 голосов
/ 28 марта 2012

Файловая система /sys должна содержать много информации для вашего квеста. Моя система (2.6.32-40-generic # 87-Ubuntu) предлагает:

/sys/class/tty

Который дает вам описания всех устройств TTY, известных системе. Урезанный пример:

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

По одной из этих ссылок:

# 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

Здесь файл dev содержит эту информацию:

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

Это основной / вспомогательный узел. Их можно искать в каталоге /dev, чтобы получить понятные имена:

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

Директория /sys/class/tty содержит все устройства TTY, но вы можете исключить эти надоедливые виртуальные терминалы и псевдо-терминалы. Я предлагаю вам изучить только те, которые имеют 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/
24 голосов
/ 21 июля 2011

В последних версиях ядра (не знаю с каких пор) вы можете перечислить содержимое / dev / serial, чтобы получить список последовательных портов в вашей системе. На самом деле это символические ссылки, указывающие на правильный узел / dev /:

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

Это адаптер USB-Serial, как вы можете видеть. Обратите внимание, что если в системе нет последовательных портов, каталог / dev / serial / не существует. Надеюсь, это поможет:).

12 голосов
/ 06 сентября 2012

Я делаю что-то вроде следующего кода. Он работает для USB-устройств, а также для глупых устройств serial8250, которых у нас у всех 30, но только пара из них действительно работает.

В основном я использую концепцию из предыдущих ответов. Сначала перечислите все tty-устройства в / sys / class / tty /. Устройства, которые не содержат подкаталог / device, отфильтровываются. / sys / class / tty / console - это такое устройство. Затем устройства, фактически содержащие устройства, затем принимаются в качестве действительного последовательного порта в зависимости от цели драйвера-символической ссылки 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

и для 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

Все драйверы, управляемые serial8250, должны быть зондами, использующими ранее упомянутый 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

Действителен только порт, сообщающий о действительном типе устройства.

Полный источник для перечисления серийных портов выглядит следующим образом. Дополнения приветствуются.

#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;   
}
11 голосов
/ 15 января 2011

Я думаю, что нашел ответ в исходной документации моего ядра: /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 

Вот ссылка на этот файл: http://git.kernel.org/?p=linux/kernel/git/next/linux-next.git;a=blob_plain;f=Documentation/filesystems/proc.txt;hb=e8883f8057c0f7c9950fa9f20568f37bfa62f34a

6 голосов
/ 09 апреля 2017

Я нашел

dmesg | grep tty

делает работу.

3 голосов
/ 27 марта 2010

setserial с опцией -g, кажется, делает то, что вы хотите, а источник C доступен в http://www.koders.com/c/fid39344DABD14604E70DF1B8FEA7D920A94AF78BF8.aspx.

2 голосов
/ 01 апреля 2015

Использование / proc / tty / drivers указывает только то, какие драйверы tty загружены. Если вы ищете список последовательных портов, проверьте / dev / serial, он будет иметь два подкаталога: by-id и 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

Благодаря этому сообщению: https://superuser.com/questions/131044/how-do-i-know-which-dev-ttys-is-my-serial-port

2 голосов
/ 31 декабря 2010

У меня нет последовательного USB-устройства, но должен быть способ найти реальные порты, используя библиотеки HAL напрямую:

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

====================================================================

В опубликованном коде python-dbus или в этом сценарии sh перечисленыустройства bluetooth / dev / rfcomm *, так что это не лучшее решение.

Обратите внимание, что на других платформах Unix последовательные порты не называются ttyS?и даже в Linux некоторые последовательные карты позволяют называть устройства.Предполагается, что шаблон в именах последовательных устройств неверен.

2 голосов
/ 27 марта 2010

У меня нет здесь последовательного устройства для тестирования, но если у вас есть Python и dbus, вы можете попробовать это сами.

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")

Если произойдет сбой, вы можете выполнить поиск внутри hwmanager_i.GetAllDevicesWithProperties(), чтобы узнать, не имеет ли другое имя возможности "serial" другое имя.

НТН

0 голосов
/ 21 июня 2016

Мой подход через группу dialout , чтобы получить каждый tty с пользователем 'dialout' ls -l /dev/tty* | grep 'dialout' чтобы получить только свою папку ls -l /dev/tty* | grep 'dialout' | rev | cut -d " " -f1 | rev

легко слушать вывод tty, например когда серийный выход Arduino: head --lines 1 < /dev/ttyUSB0

прослушивание каждого tty только на одну строку: for i in $(ls -l /dev/tty* | grep 'dialout' | rev | cut -d " " -f1 | rev); do head --lines 1 < $i; done

Мне очень нравится подход через поиск водителей: ll /sys/class/tty/*/device/driver

Теперь вы можете выбрать tty-Name: ls /sys/class/tty/*/device/driver | grep 'driver' | cut -d "/" -f 5

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...