Wat is de juiste manier om een lijst te krijgen van alle beschikbare seriële poorten/apparaten op een Linux-systeem?
Met andere woorden, als ik alle apparaten in /dev/
doorloop, hoe weet ik dan op de klassieke manier welke seriële poorten zijn, dat wil zeggen diegene die gewoonlijk baudrates ondersteunen en RTS/CTSstroomregeling?
De oplossing zou worden gecodeerd in C.
Ik vraag het omdat ik een bibliotheek van derden gebruik die dit duidelijk verkeerd doet: het lijkt alleen te herhalen over /dev/ttyS*
. Het probleem is dat er bijvoorbeeld seriële poorten zijn via USB (geleverd door USB-RS232-adapters), en die worden vermeld onder /dev/ttyUSB*. En als ik de Serial-HOWTO op Linux.orglees, krijg ik het idee dat er na verloop van tijd ook andere naamruimten zullen zijn.
Dus ik moet de officiële manier vinden om seriële apparaten te detecteren. Het probleem is dat niets gedocumenteerd lijkt te zijn, of ik kan het niet vinden.
Ik stel me voor dat een manier zou zijn om alle bestanden van /dev/tty*
te openen en een specifieke ioctl()
erop aan te roepen die alleen beschikbaar is op seriële apparaten. Zou dat echter een goede oplossing zijn?
Bijwerken
hrickardsstelde voor om naar de bron te kijken voor “setserial”.
De code doet precies wat ik in gedachten had:
Eerst opent het een apparaat met:
fd = open (path, O_RDWR | O_NONBLOCK)
Vervolgens roept het aan:
ioctl (fd, TIOCGSERIAL, &serinfo)
Als die oproep geen fout retourneert, is het blijkbaar een serieel apparaat.
Ik heb vergelijkbare code gevonden in Serial Programming/termios, die voorstelde om ook de optie O_NOCTTY
toe te voegen.
Er is echter één probleem met deze aanpak:
Toen ik deze code testte op BSD Unix (dat wil zeggen, Mac OS X), werkte deze ook. Echterseriële apparaten die worden geleverd via Bluetooth zorgen ervoor dat het systeem (stuurprogramma) probeert verbinding te maken met het Bluetooth-apparaat, wat even duurt voordat het terugkeert met een time-out fout. Dit wordt veroorzaakt door het apparaat gewoon te openen. En ik kan me voorstellen dat soortgelijke dingen ook op Linux kunnen gebeuren – idealiter zou ik het apparaat niet hoeven te openen om het type te achterhalen. Ik vraag me af of er ook een manier is om ioctl
-functies aan te roepen zonder een open, of een apparaat te openen op een manier dat er geen verbindingen worden gemaakt?
Wat moet ik doen?
Antwoord 1, autoriteit 100%
Het /sys
bestandssysteem zou voldoende informatie moeten bevatten voor je zoektocht. Mijn systeem (2.6.32-40-generiek #87-Ubuntu) suggereert:
/sys/class/tty
Hiermee krijgt u beschrijvingen van alle TTY-apparaten die bij het systeem bekend zijn. Een ingekort voorbeeld:
# 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/
Een van deze links volgen:
# 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 bevat het bestand dev
deze informatie:
# cat /sys/class/tty/ttyUSB0/dev
188:0
Dit is het hoofd-/kleinknooppunt. Deze kunnen worden doorzocht in de /dev
directory om gebruiksvriendelijke namen te krijgen:
# ll -R /dev |grep "188, *0"
crw-rw---- 1 root dialout 188, 0 2012-03-28 20:44 ttyUSB0
De /sys/class/tty
map bevat alle TTY-apparaten, maar misschien wilt u die vervelende virtuele terminals en pseudo-terminals uitsluiten. Ik raad u aan alleen die te onderzoeken die een device/driver
-invoer hebben:
# 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/
Antwoord 2, autoriteit 36%
In recente kernels (niet zeker sinds wanneer) kun je de inhoud van /dev/serial weergeven om een lijst te krijgen van de seriële poorten op je systeem. Het zijn eigenlijk symbolische links die naar de juiste /dev/ node verwijzen:
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
Dit is een USB-seriële adapter, zoals u kunt zien. Merk op dat wanneer er geen seriële poorten op het systeem zijn, de /dev/serial/ directory niet bestaat. Ik hoop dat dit helpt :).
Antwoord 3, autoriteit 17%
Ik doe zoiets als de volgende code. Het werkt voor USB-apparaten en ook voor de stomme serial8250-apparaten waarvan we er allemaal 30 hebben – maar slechts een paar werken echt.
In principe gebruik ik het concept uit eerdere antwoorden. Noem eerst alle tty-apparaten in /sys/class/tty/. Apparaten die geen /device-submap bevatten, worden weggefilterd. /sys/class/tty/console is zo’n apparaat. Vervolgens worden de apparaten die daadwerkelijk een apparaat bevatten, geaccepteerd als geldige seriële poort, afhankelijk van het doel van de 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
en voor 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 stuurprogramma’s die door serial8250 worden aangestuurd, moeten probes zijn die de eerder genoemde ioctl gebruiken.
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
Alleen poorten die een geldig apparaattype rapporteren, zijn geldig.
De volledige bron voor het opsommen van de seriële poorten ziet er als volgt uit. Aanvullingen zijn welkom.
#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;
}
Antwoord 4, autoriteit 16%
Ik heb gevonden
dmesg | grep tty
het werk doen.
Antwoord 5, autoriteit 15%
Ik denk dat ik het antwoord heb gevonden in mijn kernelbrondocumentatie:
/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 is een link naar dit bestand:
http://git.kernel.org/?P=LINUX/kernel/git/NEXT/LINUX-NEXT.GIT;A=Blob_PlainImiMSF=Documentation/Filesystems/Proc.txt;HB=E8883F8057C0F7C9950FA9F20568F37BFA62F34A
6, Autoriteit 2%
De bibliotheek voor seriële communicatiebeheer heeft veel API’s en functies die zijn gericht op de taak die u wilt. Als het apparaat een USB-UART is, kan zijn VID/PID worden gebruikt. Als het apparaat BT-SPP is, kunnen platformspecifieke API’s worden gebruikt. Bekijk dit project voor het programmeren van seriële poorten: https://github.com/RishiGupta12/serial -communicatiemanager
Antwoord 7
ja, ik weet het, ik ben te laat (zoals altijd). Hier is mijn stukje code (gebaseerd op het antwoord van mk2). Misschien helpt dit iemand:
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;
}