it-swarm.com.de

Wie finde ich alle seriellen Geräte (ttyS, ttyUSB, ..) unter Linux, ohne sie zu öffnen?

Wie kann eine Liste aller verfügbaren seriellen Schnittstellen/Geräte auf einem Linux-System erstellt werden?

Mit anderen Worten, wenn ich in /dev/ über alle Geräte iteriere, wie erkenne ich dann, welche seriellen Ports klassisch sind, dh welche normalerweise Baudraten und RTS/CTS Flusskontrolle unterstützen?

Die Lösung würde in C codiert.

Ich frage, weil ich eine Drittanbieter-Bibliothek verwende, die dies eindeutig falsch macht: Sie scheint nur über /dev/ttyS* zu iterieren. Das Problem ist, dass es zum Beispiel serielle Schnittstellen über USB gibt (die von USB-RS232-Adaptern bereitgestellt werden) und diese unter/dev/ttyUSB * aufgelistet sind. Und wenn ich das Serial-HOWTO unter Linux.org lese, komme ich auf die Idee, dass es mit der Zeit auch andere Namensräume geben wird.

Ich muss also den offiziellen Weg finden, um serielle Geräte zu erkennen. Das Problem ist, dass anscheinend keiner dokumentiert ist oder ich ihn nicht finde.

Ich stelle mir vor, eine Möglichkeit wäre, alle Dateien von /dev/tty* aus zu öffnen und eine bestimmte ioctl() aufzurufen, die nur auf seriellen Geräten verfügbar ist. Wäre das eine gute Lösung?

Aktualisieren

hrickards schlug vor, in der Quelle nach "setserial" zu suchen. Sein Code macht genau das, was ich mir vorgestellt habe:

Zunächst öffnet es ein Gerät mit:

fd = open (path, O_RDWR | O_NONBLOCK)

Dann ruft es auf:

ioctl (fd, TIOCGSERIAL, &serinfo)

Wenn dieser Aufruf keinen Fehler zurückgibt, handelt es sich anscheinend um ein serielles Gerät.

Ich habe ähnlichen Code in Serial Programming/termios gefunden, der vorschlug, auch die Option O_NOCTTY hinzuzufügen.

Bei diesem Ansatz gibt es jedoch ein Problem:

Als ich diesen Code unter BSD Unix (Mac OS X) getestet habe, hat er auch funktioniert.Allerdings, serielle Geräte, die über Bluetooth bereitgestellt werden, veranlassen das System (den Treiber), eine Verbindung mit dem Bluetooth-Gerät herzustellen. Dies dauert eine Weile, bis ein Timeout-Fehler auftritt. Dies wird nur durch das Öffnen des Geräts verursacht. Und ich kann mir vorstellen, dass ähnliche Dinge auch unter Linux passieren können - idealerweise sollte ich das Gerät nicht öffnen müssen, um seinen Typ herauszufinden. Ich frage mich, ob es auch eine Möglichkeit gibt, ioctl-Funktionen ohne Öffnen aufzurufen, oder ein Gerät so zu öffnen, dass keine Verbindungen hergestellt werden.

Was soll ich machen?

95

Das /sys-Dateisystem sollte viele Informationen für Ihre Suche enthalten. Mein System (2.6.32-40-generisch # 87-Ubuntu) schlägt vor:

/sys/class/tty

Hier finden Sie Beschreibungen aller dem System bekannten TTY-Geräte. Ein abgespecktes Beispiel:

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

Folgend einer dieser Verbindungen:

# 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

Hier enthält die Datei dev diese Informationen:

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

Dies ist der Haupt-/Nebenknoten. Diese können im Verzeichnis /dev durchsucht werden, um benutzerfreundliche Namen zu erhalten:

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

Das /sys/class/tty-Verzeichnis enthält alle TTY-Geräte, Sie möchten jedoch diese lästigen virtuellen Terminals und Pseudo-Terminals ausschließen. Ich schlage vor, Sie untersuchen nur diejenigen, die einen device/driver-Eintrag haben:

# 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/
66
A.H.

In aktuellen Kernels (seit wann nicht mehr sicher) können Sie den Inhalt von/dev/serial auflisten, um eine Liste der seriellen Anschlüsse Ihres Systems zu erhalten. Sie sind eigentlich Symlinks, die auf den richtigen/dev/-Knoten zeigen:

[email protected]:~$ 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/
[email protected]:~$ 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
[email protected]:~$ 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

Dies ist ein USB-Serial-Adapter, wie Sie sehen können. Beachten Sie, dass das Verzeichnis/dev/serial/nicht vorhanden ist, wenn sich keine seriellen Anschlüsse im System befinden. Hoffe das hilft :).

23
flu0

Ich mache so etwas wie den folgenden Code. Es funktioniert für USB-Geräte und auch für die dummen seriellen 8250-Geräte, von denen wir alle 30 haben - aber nur wenige funktionieren wirklich.

Grundsätzlich verwende ich das Konzept aus früheren Antworten. Zählen Sie zuerst alle tty-Geräte in/sys/class/tty/auf. Geräte, die kein/device-Unterverzeichnis enthalten, werden weggefiltert./sys/class/tty/console ist ein solches Gerät. Dann werden die Geräte, die tatsächlich ein Gerät enthalten, als gültige serielle Schnittstelle akzeptiert, abhängig vom Ziel des Treibersymbols.

$ 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

und für 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

Alle Treiber, die von serial8250 gesteuert werden, müssen Sonden sein, die das zuvor erwähnte ioctl verwenden. 

        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

Nur der Port, der einen gültigen Gerätetyp meldet, ist gültig.

Die vollständige Quelle zum Auflisten der Serialports sieht folgendermaßen aus. Ergänzungen sind willkommen.

#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;   
}
12
Søren Holm

Ich glaube, ich habe die Antwort in meiner Kernel-Quellendokumentation gefunden: /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 

Hier ist ein Link zu dieser Datei: http://git.kernel.org/?p=linux/kernel/git/next/linux-next.git;a=blob_plain;f=Documentation/filesystems/ proc.txt; hb = e8883f8057c0f7c9950fa9f20568f37bfa62f34a

11
mk2

Ich fand

dmesg | grep tty

die Arbeit erledigen.

5
RealHuman75

setserial mit der Option -g scheint zu tun, was Sie möchten, und die C-Quelle ist unter http://www.koders.com/c/fid39344DABD14604E70DF1B8FEA7D920A94AF78BF8.aspx verfügbar.

3
hrickards

Ich habe kein serielles USB-Gerät, aber es muss möglich sein, die realen Ports mithilfe der HAL-Bibliotheken direkt zu finden:

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

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

Der veröffentlichte Python-dbus-Code oder dieses SH-Skript listet die Bluetooth/dev/rfcomm * -Geräte auf und ist daher nicht die beste Lösung.

Beachten Sie, dass auf anderen Unix-Plattformen die seriellen Schnittstellen nicht ttyS genannt werden. Selbst unter Linux können Sie bei einigen seriellen Karten die Geräte benennen. Ein Muster in den Namen der seriellen Geräte ist falsch. 

2
kelk1

Die Verwendung von/proc/tty/drivers zeigt nur an, welche tty-Treiber geladen sind. Wenn Sie nach einer Liste der seriellen Ports Auschecken/dev/serial suchen, gibt es zwei Unterverzeichnisse: by-id und 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

Vielen Dank für diesen Beitrag: https://superuser.com/questions/131044/how-do-i-know-which-dev-ttys-is-my-serial-port

2
blarf

Ich habe kein serielles Gerät hier, um es zu testen, aber wenn Sie Python und Dbus haben, können Sie es selbst ausprobieren.

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

Wenn dies fehlschlägt, können Sie in hwmanager_i.GetAllDevicesWithProperties() suchen, um zu sehen, ob der eben geratene Fähigkeitsname "serial" einen anderen Namen hat.

HTH

2
baol

Mein Ansatz über Gruppe dialout , um jedes tty mit Benutzer 'dialout' ls -l /dev/tty* | grep 'dialout' zu erhalten, um nur den Ordner ls -l /dev/tty* | grep 'dialout' | rev | cut -d " " -f1 | rev zu erhalten.

leichtes Abhören der tty-Ausgabe, z. wenn arduino seriell out: head --lines 1 < /dev/ttyUSB0

hören Sie jede tty out nur für eine Zeile: for i in $(ls -l /dev/tty* | grep 'dialout' | rev | cut -d " " -f1 | rev); do head --lines 1 < $i; done

Ich mag die Herangehensweise, indem ich nach Treibern suche: ll /sys/class/tty/*/device/driver

Sie können jetzt den tty-Namen auswählen: ls /sys/class/tty/*/device/driver | grep 'driver' | cut -d "/" -f 5

0
McPeppr

Die Bibliothek für den seriellen Kommunikationsmanager verfügt über viele APIs und Funktionen, die auf die gewünschte Aufgabe ausgerichtet sind. Wenn das Gerät ein USB-UART ist, kann seine VID/PID verwendet werden. Wenn das Gerät BT-SPP ist, können plattformspezifische APIs verwendet werden. Schauen Sie sich dieses Projekt für die serielle Schnittstellenprogrammierung an: https://github.com/RishiGupta12/serial-communication-manager

0
samuel05051980