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?
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?
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/
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 :).
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;
}
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
Ich fand
dmesg | grep tty
die Arbeit erledigen.
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.
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.
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
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
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
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