如何在 Linux 上查找所有串行设备(ttyS、ttyUSB、..)而不打开它们?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2530096/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 19:53:25  来源:igfitidea点击:

How to find all serial devices (ttyS, ttyUSB, ..) on Linux without opening them?

linuxserial-port

提问by Thomas Tempelmann

What is the proper way to get a list of all available serial ports/devices on a Linux system?

在 Linux 系统上获取所有可用串行端口/设备列表的正确方法是什么?

In other words, when I iterate over all devices in /dev/, how do I tell which ones are serial ports in the classic way, that is, those usually supporting baud rates and RTS/CTSflow control?

换句话说,当我遍历所有设备时/dev/,我如何以经典方式判断哪些是串口,即那些通常支持波特率和RTS/CTS流量控制的?

The solution would be coded in C.

解决方案将用 C 编码。

I ask because I am using a third-party library that does this clearly wrong: It appears to only iterate over /dev/ttyS*. The problem is that there are, for instance, serial ports over USB (provided by USB-RS232 adapters), and those are listed under /dev/ttyUSB*. And reading the Serial-HOWTO at Linux.org, I get the idea that there'll be other name spaces as well, as time comes.

我问是因为我使用的第三方库显然是错误的:它似乎只迭代/dev/ttyS*. 问题是,例如,有 USB 串行端口(由 USB-RS232 适配器提供),这些端口列在 /dev/ttyUSB* 下。在阅读Linux.org 上Serial-HOWTO 时,我了解到随着时间的推移,还会有其他名称空间。

So I need to find the official way to detect serial devices. The problem is that none appears to be documented, or I can't find it.

所以我需要找到检测串口设备的官方方法。问题是似乎没有记录,或者我找不到它。

I imagine one way would be to open all files from /dev/tty*and call a specific ioctl()on them that is only available on serial devices. Would that be a good solution, though?

我想一种方法是打开所有文件/dev/tty*ioctl()在它们上调用仅在串行设备上可用的特定文件。不过,这会是一个很好的解决方案吗?

Update

更新

hrickardssuggested to look at the source for "setserial". Its code does exactly what I had in mind:

hrickards建议查看“setserial”的来源。它的代码完全符合我的想法:

First, it opens a device with:

首先,它打开一个设备:

fd = open (path, O_RDWR | O_NONBLOCK)

Then it invokes:

然后它调用:

ioctl (fd, TIOCGSERIAL, &serinfo)

If that call returns no error, then it's a serial device, apparently.

如果该调用没有返回错误,那么它显然是一个串行设备。

I found similar code in Serial Programming/termios, which suggested to also add the O_NOCTTYoption.

我在Serial Programming/termios 中找到了类似的代码,它建议也添加该O_NOCTTY选项。

There is one problem with this approach, though:

但是,这种方法存在一个问题:

When I tested this code on BSD Unix (that is, Mac OS X), it worked as well. However, serial devices that are provided through Bluetooth cause the system (driver) to try to connect to the Bluetooth device, which takes a while before it'll return with a timeout error. This is caused by just opening the device. And I can imagine that similar things can happen on Linux as well - ideally, I should not need to open the device to figure out its type. I wonder if there's also a way to invoke ioctlfunctions without an open, or open a device in a way that it does not cause connections to be made?

当我在 BSD Unix(即 Mac OS X)上测试此代码时,它也能正常工作。但是,通过蓝牙提供的串行设备会导致系统(驱动程序)尝试连接到蓝牙设备,这需要一段时间才能返回超时错误。这是由打开设备引起的。我可以想象在 Linux 上也可能发生类似的事情——理想情况下,我不需要打开设备来确定它的类型。我想知道是否还有一种方法可以在ioctl不打开的情况下调用函数,或者以不导致连接的方式打开设备?

What should I do?

我该怎么办?

采纳答案by A.H.

The /sysfilesystem should contain plenty information for your quest. My system (2.6.32-40-generic #87-Ubuntu) suggests:

/sys文件系统应该包含你的追求大量的信息。我的系统 (2.6.32-40-generic #87-Ubuntu) 建议:

/sys/class/tty

Which gives you descriptions of all TTY devices known to the system. A trimmed down example:

它为您提供了系统已知的所有 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/

Following one of these links:

按照以下链接之一:

# 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

Here the devfile contains this information:

dev文件包含以下信息:

# cat /sys/class/tty/ttyUSB0/dev
188:0

This is the major/minor node. These can be searched in the /devdirectory to get user-friendly names:

这是主要/次要节点。这些可以在/dev目录中搜索以获得用户友好的名称:

# ll -R /dev |grep "188, *0"
crw-rw----   1 root dialout 188,   0 2012-03-28 20:44 ttyUSB0

The /sys/class/ttydir contains all TTY devices but you might want to exclude those pesky virtual terminals and pseudo terminals. I suggest you examine only those which have a device/driverentry:

/sys/class/tty目录包含所有 TTY 设备,但您可能希望排除那些讨厌的虚拟终端和伪终端。我建议你只检查那些有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/

回答by hrickards

setserial with the -g option appears to do what you want and the C source is available at http://www.koders.com/c/fid39344DABD14604E70DF1B8FEA7D920A94AF78BF8.aspx.

带有 -g 选项的 setserial 似乎可以满足您的需求,C 源代码可在http://www.koders.com/c/fid39344DABD14604E70DF1B8FEA7D920A94AF78BF8.aspx 获得

回答by baol

I have no serial device here to test it, but if you have python and dbus you can try it yourself.

我这里没有串口设备来测试它,但是如果你有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")

If it fails you can search inside hwmanager_i.GetAllDevicesWithProperties()to see if the capability name "serial" that I just guessed has a different name.

如果失败,您可以在内部搜索hwmanager_i.GetAllDevicesWithProperties()以查看我刚刚猜测的功能名称“串行”是否具有不同的名称。

HTH

HTH

回答by kelk1

I do not have a USB serial device, but there must be a way to find the real ports using the HAL libraries directly:

我没有 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

====================================================================

The posted python-dbus code nor this sh script lists the bluetooth /dev/rfcomm* devices, so it is not the best solution.

发布的 python-dbus 代码和这个 sh 脚本都列出了蓝牙 /dev/rfcomm* 设备,所以它不是最好的解决方案。

Note that on other unix platforms, the serial ports are not named ttyS? and even in linux, some serial cards allow you to name the devices. Assuming a pattern in the serial devices names is wrong.

请注意,在其他unix平台上,串口没有命名为ttyS?甚至在 linux 中,一些串行卡允许您命名设备。假设串行设备名称中的模式是错误的。

回答by mk2

I think I found the answer in my kernel source documentation: /usr/src/linux-2.6.37-rc3/Documentation/filesystems/proc.txt

我想我在内核源文档中找到了答案:/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 

Here is a link to this file: http://git.kernel.org/?p=linux/kernel/git/next/linux-next.git;a=blob_plain;f=Documentation/filesystems/proc.txt;hb=e8883f8057c0f7c9950fa9f20568f37bfa62f34a

这是该文件的链接:http: //git.kernel.org/?p=linux/kernel/git/next/linux-next.git;a= blob_plain;f=Documentation/filesystems/proc.txt;hb =e8883f8057c0f7c9950fa9f20568f37bfa62f34a

回答by flu0

In recent kernels (not sure since when) you can list the contents of /dev/serial to get a list of the serial ports on your system. They are actually symlinks pointing to the correct /dev/ node:

在最近的内核中(不确定从什么时候开始),您可以列出 /dev/serial 的内容以获取系统上串行端口的列表。它们实际上是指向正确 /dev/ 节点的符号链接:

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

This is a USB-Serial adapter, as you can see. Note that when there are no serial ports on the system, the /dev/serial/ directory does not exists. Hope this helps :).

如您所见,这是一个 USB 串行适配器。注意,当系统没有串口时,/dev/serial/目录不存在。希望这可以帮助 :)。

回答by S?ren Holm

I'm doing something like the following code. It works for USB-devices and also the stupid serial8250-devuices that we all have 30 of - but only a couple of them realy works.

我正在做类似以下代码的事情。它适用于 USB 设备,也适用于我们都有 30 个的愚蠢的 serial8250 设备 - 但只有其中几个真正有效。

Basically I use concept from previous answers. First enumerate all tty-devices in /sys/class/tty/. Devices that does not contain a /device subdir is filtered away. /sys/class/tty/console is such a device. Then the devices actually containing a devices in then accepted as valid serial-port depending on the target of the driver-symlink fx.

基本上我使用以前答案中的概念。首先枚举/sys/class/tty/中的所有tty-devices。不包含 /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

and for ttyS0

对于 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

All drivers driven by serial8250 must be probes using the previously mentioned ioctl.

所有由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

Only port reporting a valid device-type is valid.

只有报告有效设备类型的端口才有效。

The complete source for enumerating the serialports looks like this. Additions are welcome.

枚举串行端口的完整源代码如下所示。欢迎补充。

#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;   
}

回答by blarf

Using /proc/tty/drivers only indicates which tty drivers are loaded. If you're looking for a list of the serial ports check out /dev/serial, it will have two subdirectories: by-id and by-path.

使用 /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

Thanks to this post: https://superuser.com/questions/131044/how-do-i-know-which-dev-ttys-is-my-serial-port

感谢这篇文章:https: //superuser.com/questions/131044/how-do-i-know-which-dev-ttys-is-my-serial-port

回答by samuel05051980

The serial communication manager library has many API and features targeted for the task you want. If the device is a USB-UART its VID/PID can be used. If the device is BT-SPP than platform specific APIs can be used. Take a look at this project for serial port programming: https://github.com/RishiGupta12/serial-communication-manager

串行通信管理器库具有许多针对您所需任务的 API 和功能。如果设备是 USB-UART,则可以使用其 VID/PID。如果设备是 BT-SPP,则可以使用特定于平台的 API。看看这个串口编程的项目:https: //github.com/RishiGupta12/serial-communication-manager

回答by McPeppr

My approach via group dialoutto get every tty with user 'dialout' ls -l /dev/tty* | grep 'dialout'to only get its folder ls -l /dev/tty* | grep 'dialout' | rev | cut -d " " -f1 | rev

我的方法是通过组拨出获取用户“拨出”的每个 tty ls -l /dev/tty* | grep 'dialout'以仅获取其文件夹 ls -l /dev/tty* | grep 'dialout' | rev | cut -d " " -f1 | rev

easy listen to the tty output e.g. when arduino serial out: head --lines 1 < /dev/ttyUSB0

轻松收听 tty 输出,例如当 arduino 串行输出时: head --lines 1 < /dev/ttyUSB0

listen to every tty out for one line only: for i in $(ls -l /dev/tty* | grep 'dialout' | rev | cut -d " " -f1 | rev); do head --lines 1 < $i; done

只听一行的每个 tty: for i in $(ls -l /dev/tty* | grep 'dialout' | rev | cut -d " " -f1 | rev); do head --lines 1 < $i; done

I really like the approach via looking for drivers: ll /sys/class/tty/*/device/driver

我真的很喜欢通过寻找驱动程序的方法: ll /sys/class/tty/*/device/driver

You can pick the tty-Name now: ls /sys/class/tty/*/device/driver | grep 'driver' | cut -d "/" -f 5

您现在可以选择 tty-Name: ls /sys/class/tty/*/device/driver | grep 'driver' | cut -d "/" -f 5