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.
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ć?
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/
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 :).
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;
}
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
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ę.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 .
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
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.
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
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;
}
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
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
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