Quelle est la bonne manière d'obtenir une liste de tous les ports/périphériques série disponibles sur un système Linux?
En d'autres termes, lorsque j'itère tous les périphériques dans /dev/
, comment savoir quels sont les ports série de la manière classique, c'est-à-dire ceux qui prennent généralement en charge les débits en bauds et RTS/CTS contrôle de flux?
La solution serait codée en C.
Je pose la question parce que j'utilise une bibliothèque tierce qui fait clairement ce qui ne va pas: il semble que seule itération sur /dev/ttyS*
. Le problème est qu’il existe, par exemple, des ports série sur USB (fournis par les adaptateurs USB-RS232), et que ceux-ci sont répertoriés sous/dev/ttyUSB *. Et en lisant le Serial-HOWTO sur Linux.org , je me rends compte qu’il y aura également d’autres espaces de noms, le moment venu.
Je dois donc trouver le moyen officiel de détecter les périphériques série. Le problème est qu'aucun ne semble être documenté, ou je ne le trouve pas.
J'imagine qu'une solution consisterait à ouvrir tous les fichiers à partir de /dev/tty*
et à appeler une ioctl()
spécifique disponible uniquement sur les périphériques série. Serait-ce une bonne solution, cependant?
hrickards a suggéré de regarder la source de "setserial" . Son code fait exactement ce que j'avais en tête:
Tout d'abord, il ouvre un appareil avec:
fd = open (path, O_RDWR | O_NONBLOCK)
Ensuite, il appelle:
ioctl (fd, TIOCGSERIAL, &serinfo)
Si cet appel ne renvoie aucune erreur, il s'agit apparemment d'un périphérique série.
J'ai trouvé un code similaire dans Programmation série/termios, qui suggérait également d'ajouter l'option O_NOCTTY
.
Il y a un problème avec cette approche, cependant:
Lorsque j'ai testé ce code sur BSD Unix (c'est-à-dire Mac OS X), cela a également fonctionné. Cependant, les périphériques série fournis via Bluetooth obligent le système (pilote) à tenter de se connecter au périphérique Bluetooth, ce qui prend un certain temps avant qu'il ne revienne avec une erreur de délai d'attente. Cela est dû à la simple ouverture de l'appareil. Et je peux imaginer que des choses similaires peuvent aussi arriver sous Linux - idéalement, je ne devrais pas avoir besoin d'ouvrir le périphérique pour comprendre son type. Je me demande s’il existe également un moyen d’appeler des fonctions ioctl
sans ouvrir ou d’ouvrir un périphérique de manière à ne pas créer de connexions?
Que devrais-je faire?
Le système de fichiers /sys
devrait contenir de nombreuses informations pour votre quête. Mon système (2.6.32-40-generic # 87-Ubuntu) suggère:
/sys/class/tty
Ce qui vous donne une description de tous les appareils ATS connus du système. Un exemple allégé:
# 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/
Après l'un de ces liens:
# 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
Ici, le fichier dev
contient ces informations:
# cat /sys/class/tty/ttyUSB0/dev
188:0
C'est le noeud majeur/mineur. Vous pouvez effectuer une recherche dans le répertoire /dev
pour obtenir des noms conviviaux:
# ll -R /dev |grep "188, *0"
crw-rw---- 1 root dialout 188, 0 2012-03-28 20:44 ttyUSB0
Le répertoire /sys/class/tty
contient tous les périphériques ATS, mais vous souhaiterez peut-être exclure ces terminaux virtuels et pseudo-terminaux embêtants. Je vous suggère d’examiner uniquement ceux qui ont une entrée 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/
Dans les noyaux récents (pas sûr depuis quand), vous pouvez lister le contenu de/dev/serial pour obtenir une liste des ports série de votre système. Ce sont en fait des liens symboliques pointant vers le bon/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
Comme vous pouvez le constater, il s'agit d'un adaptateur USB-série. Notez que lorsqu'il n'y a pas de ports série sur le système, le répertoire/dev/serial/n'existe pas. J'espère que cela t'aides :).
Je fais quelque chose comme le code suivant. Cela fonctionne aussi bien pour les périphériques USB que pour les stupides problèmes de série 8250 que nous avons tous sur 30, mais seuls quelques-uns fonctionnent vraiment.
Fondamentalement, j'utilise le concept des réponses précédentes. Commencez par énumérer tous les périphériques tty dans/sys/class/tty /. Les périphériques ne contenant pas de sous-répertoire/device sont filtrés./sys/class/tty/console est un tel périphérique. Ensuite, les périphériques contenant réellement un périphérique sont alors acceptés comme port série valide en fonction de la cible du driver-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
et pour 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
Tous les pilotes pilotés par serial8250 doivent être des sondes utilisant l'ioctl mentionné précédemment.
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
Seul le port signalant un type de périphérique valide est valide.
La source complète pour énumérer les ports série ressemble à ceci. Les ajouts sont les bienvenus.
#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;
}
Je pense avoir trouvé la réponse dans la documentation relative à la source du noyau: /Usr/src/linux-2.6.37-rc3/Documentation/systèmes de fichiers/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
Voici un lien vers ce fichier: http://git.kernel.org/?p=linux/kernel/git/next/linux-next.git;a=blob_plain;f=Documentation/filesystems/ proc.txt; hb = e8883f8057c0f7c9950fa9f20568f37bfa62f34a
J'ai trouvé
dmesg | grep tty
faire le travail.
setserial avec l'option -g semble faire ce que vous voulez et la source C est disponible sur http://www.koders.com/c/fid39344DABD14604E70DF1B8FEA7D920A94AF78BF8.aspx .
Je n'ai pas de périphérique série USB, mais il doit exister un moyen de trouver les ports réels à l'aide des bibliothèques HAL directement:
====================================================================
#! /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
====================================================================
Le code python-dbus publié, ni ce script sh ne répertorie les périphériques bluetooth/dev/rfcomm *, ce n’est donc pas la meilleure solution.
Notez que sur les autres plates-formes Unix, les ports série ne sont pas nommés ttyS? et même sous Linux, certaines cartes série vous permettent de nommer les périphériques. En supposant qu'un motif dans les noms de périphériques série est incorrect.
L'utilisation de/proc/tty/drivers indique uniquement quels pilotes tty sont chargés. Si vous recherchez une liste des ports série, consultez/dev/serial, il y aura deux sous-répertoires: by-id et 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
Merci à ce post: https://superuser.com/questions/131044/how-do-i-know-which-dev-ttys-is-my-serial-port
Je n'ai pas de périphérique série pour le tester, mais si vous avez python et dbus, vous pouvez l'essayer vous-même.
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 cela échoue, vous pouvez chercher dans hwmanager_i.GetAllDevicesWithProperties()
pour voir si le nom de capacité "série" que je viens de deviner a un nom différent.
HTH
Mon approche via groupe dialout pour obtenir chaque tty avec le 'dialout' de l'utilisateur ls -l /dev/tty* | grep 'dialout'
ne récupérer que son dossier ls -l /dev/tty* | grep 'dialout' | rev | cut -d " " -f1 | rev
écouter facilement la sortie tty, par exemple quand arduino serial out: head --lines 1 < /dev/ttyUSB0
écoutez chaque tty pour une seule ligne: for i in $(ls -l /dev/tty* | grep 'dialout' | rev | cut -d " " -f1 | rev); do head --lines 1 < $i; done
J'aime beaucoup l'approche via la recherche de pilotes: ll /sys/class/tty/*/device/driver
Vous pouvez choisir le nom de tty maintenant: ls /sys/class/tty/*/device/driver | grep 'driver' | cut -d "/" -f 5
La bibliothèque du gestionnaire de communication série possède de nombreuses API et fonctionnalités ciblées pour la tâche souhaitée. Si le périphérique est un USB-UART, son VID/PID peut être utilisé. Si le périphérique est BT-SPP, des API spécifiques à une plate-forme peuvent être utilisées. Jetez un coup d’œil à ce projet pour la programmation par port série: https://github.com/RishiGupta12/serial-communication-manager