Jak znaleźć wszystkie urządzenia szeregowe (ttyS, ttyUSB,) w Linuksie bez ich otwierania?

Jaki jest właściwy sposób na uzyskanie listy wszystkich dostępnych portów/urządzeń szeregowych w systemie Linux?

Innymi słowy, kiedy iteruję wszystkie urządzenia w /dev/, Jak powiedzieć, które z nich są portami szeregowymi w klasyczny sposób, czyli te, które zwykle obsługują szybkość transmisji i RTS/CTS kontrola przepływu?

Rozwiązanie byłoby zakodowane w C.

Pytam, ponieważ używam biblioteki innej firmy, która robi to wyraźnie źle: wydaje się, że tylko iteruje nad /dev/ttyS*. Problem polega na tym, że istnieją na przykład porty szeregowe przez USB (dostarczane przez Adaptery USB-RS232), a te są wymienione w /dev/ttyUSB*. I czytając Serial-HOWTO w Linux.org , domyślam się, że w miarę upływu czasu będą też inne przestrzenie nazw.

Więc muszę znaleźć oficjalny sposób na wykrycie urządzeń szeregowych. Problem w tym, że żadna nie jest udokumentowana, albo nie mogę jej znaleźć.

Wyobrażam sobie, że jednym ze sposobów byłoby otwarcie wszystkich plików z /dev/tty* i wywołanie specyficzne ioctl() na nich, które są dostępne tylko na urządzeniach szeregowych. Czy to byłoby dobre rozwiązanie?

Update

hrickards zasugerował, aby spojrzeć na źródło dla "setserial". Jego kod robi dokładnie to, co miałem na myśli:

Najpierw otwiera urządzenie z:

fd = open (path, O_RDWR | O_NONBLOCK)

Następnie wywołuje:

ioctl (fd, TIOCGSERIAL, &serinfo)

Jeśli to wywołanie nie zwróci żadnego błędu, to najwyraźniej jest to urządzenie szeregowe.

Znalazłem podobny kod w Serial Programowanie / termios, co sugerowało dodanie opcji O_NOCTTY.

Jest jednak jeden problem z tym podejściem:

Kiedy testowałem ten kod na BSD Unix (czyli Mac OS X), działał również. jednakże, urządzenia szeregowe dostarczane przez Bluetooth powodują, że system (sterownik) próbuje połączyć się z urządzeniem Bluetooth, co trwa chwilę, zanim powróci z błędem limitu czasu. Jest to spowodowane przez samo otwarcie urządzenia. I mogę sobie wyobrazić, że podobne rzeczy mogą się dziać również na Linuksie - najlepiej, aby nie trzeba było otwierać urządzenia, aby dowiedzieć się, jaki jest jego typ. Zastanawiam się, czy jest też sposób na wywołanie funkcji ioctl bez open, czy też otwarcie urządzenia w sposób, który nie powoduje nawiązywania połączeń?

Co powinienem zrobić?

Author: Thomas Tempelmann, 2010-03-27

12 answers

System plików /sys powinien zawierać wiele informacji dla Twojego zadania. Mój system (2.6.32-40-generic #87-Ubuntu) sugeruje:

/sys/class/tty

Który daje opisy wszystkich urządzeń TTY znanych systemowi. Przykład przycięty:

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

Po jednym z tych linków:

# 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

Tutaj Plik dev zawiera te informacje:

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

To jest węzeł główny/podrzędny. Można je przeszukiwać w katalogu /dev, aby uzyskać przyjazną dla użytkownika nazwy:

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

Katalog /sys/class/tty zawiera wszystkie urządzenia TTY, ale możesz chcieć wykluczyć te brzydkie wirtualne terminale i pseudo terminale. Proponuję zbadać tylko te, które mają device/driver wpis:

# 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/
 83
Author: A.H.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-28 19:26:39

W ostatnich kernelach (Nie wiem od kiedy) możesz wypisać zawartość /dev / serial, aby uzyskać listę portów szeregowych w Twoim systemie. W rzeczywistości są to dowiązania symboliczne wskazujące na poprawny/ dev / node:

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

To jest adapter USB-Serial, jak widać. Zauważ, że gdy w systemie nie ma portów szeregowych, katalog /dev/serial / nie istnieje. Mam nadzieję, że to pomoże :).

 29
Author: flu0,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-20 21:17:34

Robię coś takiego jak poniższy kod. Działa na urządzenia USB, a także głupie serial8250-devuices, że wszyscy mamy 30 - ale tylko kilka z nich naprawdę działa.

Zasadniczo używam koncepcji z poprzednich odpowiedzi. Najpierw wylicz wszystkie urządzenia tty w /sys / class/tty/. Urządzenia, które nie zawierają subdir /device, są odfiltrowane. /sys / class / TTY / console jest takim urządzeniem. Następnie urządzenia faktycznie zawierające urządzenia w Następnie akceptowane jako ważny port szeregowy w zależności od cel sterownika-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

I dla 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

Wszystkie sterowniki sterowane serial8250 muszą być sondami używającymi wcześniej wspomnianego 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

Tylko port raportujący poprawny typ urządzenia jest poprawny.

Pełne źródło do wyliczenia serialports wygląda tak. Dodatki są mile widziane.

#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;   
}
 13
Author: Søren Holm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-06 20:11:37

Myślę, że znalazłem odpowiedź w dokumentacji źródła jądra: / 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 

Oto link do tego pliku: http://git.kernel.org/?p=linux/kernel/git/next/linux-next.git;a=blob_plain;f=Documentation/filesystems/proc.txt;hb=e8883f8057c0f7c9950fa9f20568f37bfa62f34a

 12
Author: mk2,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-15 19:17:22

Znalazłem

dmesg | grep tty
Wykonuję swoją pracę.
 10
Author: RealHuman75,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-09 15:24:55

Setserial z opcją-g wydaje się robić to, co chcesz, a Źródło C jest dostępne pod adresem http://www.koders.com/c/fid39344DABD14604E70DF1B8FEA7D920A94AF78BF8.aspx .

 4
Author: hrickards,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-27 17:05:30

Nie mam tutaj urządzenia szeregowego, aby go przetestować, ale jeśli masz Pythona i dbus, możesz spróbować sam.

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

Jeśli się nie powiedzie, możesz przeszukać wnętrze hwmanager_i.GetAllDevicesWithProperties(), aby sprawdzić, czy nazwa funkcji "serial", którą właśnie odgadłem, ma inną nazwę.

HTH

 3
Author: baol,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-31 18:41:12

Nie mam urządzenia szeregowego USB, ale musi być sposób, aby znaleźć prawdziwe porty za pomocą bibliotek HAL bezpośrednio:

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

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

Opublikowany kod Pythona-dbus ani ten skrypt sh nie wymienia urządzeń bluetooth/dev / rfcomm*, więc nie jest to najlepsze rozwiązanie.

Zauważ, że na innych platformach uniksowych porty szeregowe nie są nazywane ttyS? i nawet w Linuksie niektóre karty seryjne pozwalają nazwać urządzenia. Zakładając, że wzór w nazwach urządzeń szeregowych jest błędny.

 2
Author: kelk1,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-31 02:01:35

Użycie /proc/tty / drivers wskazuje tylko, które sterowniki tty są załadowane. Jeśli szukasz listy portów szeregowych, sprawdź /dev / serial, będzie ona miała dwa podkatalogi: by-id i 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

Thanks to this post: https://superuser.com/questions/131044/how-do-i-know-which-dev-ttys-is-my-serial-port

 2
Author: blarf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-20 10:04:24

Tak, wiem, jestem za późno (jak zawsze). Oto mój fragment kodu (na podstawie odpowiedzi mk2). Może to komuś pomoże:

std::vector<std::string> find_serial_ports()
{
 std::vector<std::string> ports;
    std::filesystem::path kdr_path{"/proc/tty/drivers"};
    if (std::filesystem::exists(kdr_path))
    {
        std::ifstream ifile(kdr_path.generic_string());
        std::string line;
        std::vector<std::string> prefixes;
        while (std::getline(ifile, line))
        {
            std::vector<std::string> items;
            auto it = line.find_first_not_of(' ');
            while (it != std::string::npos)
            {

                auto it2 = line.substr(it).find_first_of(' ');
                if (it2 == std::string::npos)
                {
                    items.push_back(line.substr(it));
                    break;
                }
                it2 += it;
                items.push_back(line.substr(it, it2 - it));
                it = it2 + line.substr(it2).find_first_not_of(' ');
            }
            if (items.size() >= 5)
            {
                if (items[4] == "serial" && items[0].find("serial") != std::string::npos)
                {
                    prefixes.emplace_back(items[1]);
                }
            }
        }
        ifile.close();
        for (auto& p: std::filesystem::directory_iterator("/dev"))
        {
            for (const auto& pf : prefixes)
            {
                auto dev_path = p.path().generic_string();
                if (dev_path.size() >= pf.size() && std::equal(dev_path.begin(), dev_path.begin() + pf.size(), pf.begin()))
                {
                    ports.emplace_back(dev_path);
                }
            }
        }
    }
    return ports;
}
 1
Author: cdannebe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-05-15 14:00:11

Moje podejście poprzez grupę dialout aby uzyskać każdy tty z użytkownikiem 'dialout' ls -l /dev/tty* | grep 'dialout' aby uzyskać tylko jego folder ls -l /dev/tty* | grep 'dialout' | rev | cut -d " " -f1 | rev

Łatwe słuchanie wyjścia tty np. po wyjściu szeregowym arduino: head --lines 1 < /dev/ttyUSB0

Słuchaj każdego tty tylko dla jednej linijki: for i in $(ls -l /dev/tty* | grep 'dialout' | rev | cut -d " " -f1 | rev); do head --lines 1 < $i; done

Bardzo podoba mi się podejście poprzez szukanie kierowców: ll /sys/class/tty/*/device/driver

Możesz wybrać nazwę tty teraz: ls /sys/class/tty/*/device/driver | grep 'driver' | cut -d "/" -f 5

 0
Author: McPeppr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-21 15:08:58

Biblioteka Menedżera komunikacji szeregowej ma wiele interfejsów API i funkcji dostosowanych do żądanego zadania. Jeśli urządzenie jest USB-UART, można użyć VID / PID. Jeśli urządzenie jest BT-SPP, można użyć specyficznych dla platformy interfejsów API. Spójrz na ten projekt do programowania portów szeregowych: https://github.com/RishiGupta12/serial-communication-manager

 0
Author: samuel05051980,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-25 15:59:05