如何在Linux上find所有串口设备(ttyS,ttyUSB,..)而不打开它们?
在Linux系统上获取所有可用串行端口/设备列表的正确方法是什么?
换句话说,当我遍历/dev/
所有设备时,如何以经典的方式告诉哪些设备是串行端口,即那些通常支持波特率和RTS / CTSstream量控制的设备?
解决scheme将用C编码
我问,因为我正在使用一个明显错误的第三方库:它似乎只遍历/dev/ttyS*
。 问题是,例如,通过USB(由USB-RS232适配器提供)的串行端口,并列在/ dev / ttyUSB *下。 在Linux.org上阅读Serial-HOWTO ,随着时间的推移 ,我发现还会有其他的命名空间。
所以我需要find官方的方式来检测串行设备。 问题是,似乎没有logging,或者我找不到它。
我想一种方法是打开/dev/tty*
所有文件,然后调用它们的特定ioctl()
,它们只能在串行设备上使用。 那会是一个很好的解决scheme吗?
更新
hrickardsbuild议查看“setserial”的来源。 它的代码完全符合我的想法:
首先,它打开一个设备:
fd = open (path, O_RDWR | O_NONBLOCK)
然后它调用:
ioctl (fd, TIOCGSERIAL, &serinfo)
如果这个调用没有返回错误,那么显然它是一个串行设备。
我在Serial Programming / termios中发现了类似的代码,这个代码还build议添加O_NOCTTY
选项。
但是,这种方法有一个问题:
当我在BSD Unix(也就是Mac OS X)上testing这个代码时,它也起作用了。 但是 ,通过蓝牙提供的串行设备会导致系统(驱动程序)尝试连接到蓝牙设备,这需要一段时间,然后才会返回超时错误。 这是由于打开设备造成的。 我可以想象在Linux上也会发生类似的事情 – 理想情况下,我不需要打开设备来确定它的types。 我想知道是否还有一种方法可以在不打开的情况下调用ioctl
函数,或者打开一个不会导致连接的设备?
我该怎么办?
/sys
文件系统应该包含你的任务的很多信息。 我的系统(2.6.32-40-generic#87-Ubuntu)build议:
/sys/class/tty
其中给出了系统已知的所有TTY设备的描述。 一个修剪下来的例子:
# 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/
以下其中一个链接:
# 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
这里的dev
文件包含这些信息:
# cat /sys/class/tty/ttyUSB0/dev 188:0
这是主要/次要节点。 可以在/dev
目录中search以获得用户友好的名称:
# ll -R /dev |grep "188, *0" crw-rw---- 1 root dialout 188, 0 2012-03-28 20:44 ttyUSB0
/sys/class/tty
目录包含了所有的TTY设备,但你可能想要排除那些讨厌的虚拟terminal和伪terminal。 我build议你只检查那些有一个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/
在最近的内核(不知道从什么时候开始),你可以列出/ dev / serial的内容来获取系统上的串行端口列表。 它们实际上是指向正确的/ 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
这是一个USB串行适配器,你可以看到。 请注意,当系统上没有串行端口时,/ dev / serial /目录不存在。 希望这可以帮助 :)。
我想我在我的内核源文件中find了答案:/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
这是一个链接到这个文件: http : //git.kernel.org/?p=linux/kernel/git/next/linux-next.git; a= blob_plain;f=Documentation/filesystems/proc.txt;hb = e8883f8057c0f7c9950fa9f20568f37bfa62f34a
我正在做类似下面的代码。 它适用于USB设备,也是我们都有的30多个笨笨的serial8250,但是只有其中的几个可以工作。
基本上我使用以前的答案的概念。 首先枚举/ sys / class / tty /中的所有tty设备。 不包含/ device子目录的设备将被过滤掉。 / sys / class / tty / console就是这样一个设备。 然后,根据驱动程序符号链接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
和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
所有由serial8250驱动的驱动程序必须使用前面提到的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
只有端口报告有效的设备types是有效的。
枚举serialports的完整源代码如下所示。 增加是受欢迎的。
#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; }
使用-g选项的setserial似乎可以做你想做的事情,而C源代码可以在http://www.koders.com/c/fid39344DABD14604E70DF1B8FEA7D920A94AF78BF8.aspx上find 。
我没有串口设备来testing它,但是如果你有python和dbus,你可以自己尝试。
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")
如果失败,你可以在hwmanager_i.GetAllDevicesWithProperties()
里面search,看看我刚刚猜到的能力名称“serial”是否有不同的名字。
HTH
我没有USB串行设备,但是必须有一种方法直接使用HAL库来查找真正的端口:
==================================================================== #! /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 ====================================================================
发布的python-dbus代码和这个sh脚本都列出了蓝牙/ dev / rfcomm *设备,所以这不是最好的解决scheme。
请注意,在其他Unix平台上,串行端口不被命名为ttyS? 甚至在Linux中,一些串行卡允许您命名设备。 假设串行设备名称中的模式是错误的。
使用/ proc / tty / drivers只能指出哪个tty驱动程序被加载。 如果您正在查找串行端口列表,请查看/ dev / serial,它将有两个子目录:by-id和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
感谢这个职位: https : //superuser.com/questions/131044/how-do-i-know-which-dev-ttys-is-my-serial-port
我发现
dmesg | grep tty
做这个工作。
串行通信pipe理器库有许多针对您所需任务的API和function。 如果设备是USB-UART,则可以使用VID / PID。 如果设备是BT-SPP,则可以使用平台特定的API。 看看这个项目的串口编程github.com/RishiGupta12/serial-communication-manager
我的方法是通过组拨出来获取每个用户拨打' ls -l /dev/tty* | grep 'dialout'
ls -l /dev/tty* | grep 'dialout'
只能得到它的文件夹ls -l /dev/tty* | grep 'dialout' | rev | cut -d " " -f1 | rev
ls -l /dev/tty* | grep 'dialout' | rev | cut -d " " -f1 | rev
轻松听tty输出,例如,当arduino串行输出: head --lines 1 < /dev/ttyUSB0
听取每一个tty只有一行: for i in $(ls -l /dev/tty* | grep 'dialout' | rev | cut -d " " -f1 | rev); do head --lines 1 < $i; done
for i in $(ls -l /dev/tty* | grep 'dialout' | rev | cut -d " " -f1 | rev); do head --lines 1 < $i; done
我真的很喜欢通过寻找驱动程序的方法: ll /sys/class/tty/*/device/driver
你现在可以selecttty-Name: ls /sys/class/tty/*/device/driver | grep 'driver' | cut -d "/" -f 5
ls /sys/class/tty/*/device/driver | grep 'driver' | cut -d "/" -f 5