Linux上のすべてのシリアルデバイス(ttyS、ttyUSBなど)を開かずに見つける方法は?


113

Linuxシステムで利用可能なすべてのシリアルポート/デバイスのリストを取得する適切な方法は何ですか?

つまり、ですべてのデバイスを反復処理する場合/dev/、どのデバイスが従来の方法でシリアルポートであるか、つまり、通常はボーレートとRTS / CTSフロー制御をサポートするデバイスかをどのように確認しますか?

ソリューションはCでコーディングされます。

私がこれを明らかに間違っているサードパーティのライブラリを使用しているので、私は尋ねます:反復するだけのよう/dev/ttyS*です 問題は、たとえば、USB経由のシリアルポート(USB-RS232アダプターによって提供される)があり、それらが/ dev / ttyUSB *の下にリストされていることです。そして、読みLinux.orgでシリアル-HOWTOを、私は時間が来ると、同様に他の名前空間があるだろうというアイデアを得ます。

したがって、シリアルデバイスを検出する公式の方法を見つける必要があります。問題は、文書化されていないように見えるか、見つからないことです。

1つの方法は、シリアルデバイスでのみ使用可能なすべてのファイルを開き、それらの/dev/tty*特定のファイルを呼び出すioctl()ことだと思います。しかし、それは良い解決策でしょうか?

更新

hrickardは「setserial」のソースを調べることを提案しました。そのコードは私が念頭に置いていたものとまったく同じです:

まず、次のコマンドでデバイスを開きます。

fd = open (path, O_RDWR | O_NONBLOCK)

次に、それを呼び出します:

ioctl (fd, TIOCGSERIAL, &serinfo)

その呼び出しがエラーを返さない場合、それは明らかにシリアルデバイスです。

シリアルプログラミング/ termiosで同様のコードを見つけましたO_NOCTTY。オプションを追加することも提案されています。

ただし、このアプローチには1つの問題があります。

このコードをBSD Unix(つまりMac OS X)でテストしたところ、うまくいきました。ただし、Bluetoothを介して提供されるシリアルデバイスは、システム(ドライバー)がBluetoothデバイスに接続しようとするため、タイムアウトエラーで戻るまでにしばらく時間がかかります。これは、デバイスを開くだけで発生します。Linuxでも同様のことが起こると想像できます。理想的には、デバイスを開いてそのタイプを判別する必要はないはずです。ioctlオープンせずに関数を呼び出す方法、または接続が行われないようにデバイスを開く方法もあるのでしょうか。

私は何をすべきか?


1
匿名の誰かがこの編集を提案したため拒否されました。代わりにコメントとしてここに残します。ioctl呼び出しでTIOCMGETの代わりにTIOCGSERIALフラグを使用すると、呼び出しはエラーにならないいくつかの間違ったパスでエラーを返しませんCOM(シリアル)ポートを参照してください。TIOCMGETフラグを使用すると、ioctlは、TTYとTTYUSBの両方の可能なパスでアクセスできるCOMポートでのみ機能します。
Thomas Tempelmann 2016

回答:


78

/sysファイルシステムは、あなたの探求のためにたくさんの情報が含まれている必要があります。私のシステム(2.6.32-40-generic#87-Ubuntu)は以下を提案します:

/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ディレクトリで検索して、わかりやすい名前を取得できます。

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

/sys/class/ttyDIRは、すべての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/

@entalpiが見つかります/dev/zero。これはシリアルデバイスだと思いますか?
AH

/ sys / class / ttyにすでに名前があるため、/ devでの検索は役に立ちません(デフォルトではudevは/ dev / DEVNAMEノードを作成するため)。興味があるのは、そのようなデバイスを指す/ devの「シンボリック」リンクです。これを見つけるのははるかに困難です。
xryl669 2016年

28

最近のカーネル(いつからかわからない)では、/ 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

ご覧のとおり、これはUSBシリアルアダプターです。システムにシリアルポートがない場合、/ dev / serial /ディレクトリは存在しないことに注意してください。お役に立てれば :)。


3
これは、2.5で導入されたudev(特に/lib/udev/rules.d/??-persistent-serial.rulesの設定)の機能です。
ergosys 2013

4
すばらしいヒントです。残念ながら、これは組み込みのシリアルポートではなく、USBシリアルポートのみを表示するとは思いません(接続されているときにudevによって表示されます)。VMware VM(VMから提供されたttyS0 / COM1を使用)でUbuntu 14の/ dev / serialに何も表示されず、udevルール(60-persistent-serial.rules)がudevデバイスのみを参照している-私はudevが「組み込み」のttyS *シリアルポートを見つけ出さないと思います、それらはioctlまたは他の答えと同様にテストする必要があるでしょう。
リードヘッジ

ls / dev / serial / ls: '/ dev / serial /'にアクセスできません:そのようなファイルまたはディレクトリはありませんSlackware 14.2 current x64
jpka

2
@jpka:シリアルデバイスが見つからない場合に発生します。私は上記のようにしてそれはうまくいきました。その後、USBから(FTDI)シリアルデバイスを取り外したところ、説明したエラーが発生しました。
Warpspace

13

私は次のコードのようなことをしています。それはUSBデバイスで動作し、私たち全員が30個持っている愚かなserial8250デバイスも動作しますが、実際に動作するのはそのうちの2、3のみです。

基本的に私は以前の答えからの概念を使用します。まず/ sys / class / tty /内のすべてのttyデバイスを列挙します。/ deviceサブディレクトリを含まないデバイスはフィルタリングされます。/ sys / class / tty / consoleはそのようなデバイスです。次に、実際にデバイスを含むデバイスは、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

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

有効なデバイスタイプを報告するポートのみが有効です。

シリアルポートを列挙するための完全なソースは次のようになります。追加は大歓迎です。

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

単独のリンクはそれ自体では意味がなく、ターゲットリソースが将来も有効であることが保証されていないため、貧弱な回答見なされます。少なくとも、リンクしている情報の要約を含めるようにしてください。
j0k

Sorenのおかげで、APIとそれに関するいくつかのアイデアはわかっていますが、Sorenは本当にうまくいきました。ありがとう。
ind79ra 2016年

12

私のカーネルソースドキュメントで答えを見つけたと思います:/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


はい、それはうまくいっているようです。ただし、このソリューションでは、テキストファイルを読み取って解析する必要があります。より良い方法があるかどうか、つまり、これらのコンテンツを構造化されたバイナリ形式で取得できるAPIがあるのでしょうか。
Thomas Tempelmann、2011年


3

-gオプションを指定したsetserialは希望どおりの動作をするように見え、Cソースはhttp://www.koders.com/c/fid39344DABD14604E70DF1B8FEA7D920A94AF78BF8.aspxから入手できます


私はコードを見て、デバイスを開かなければならないので、最後に私の質問で説明している欠陥があり、それはすでに接続試行につながる可能性があり、それは今度は良くありません。しかし、Bluetoothのサポートに関しては、Linuxドライバーは現在のOSXドライバーよりもスマートなのかもしれません。すぐに接続を開けないからです。知るか?多分私はそれを明確にするために新しい質問を始めるべきです。それで問題ないことが判明した場合は、ここでも回答を受け入れることができます。
うーん

3

ここにはテスト用のシリアルデバイスはありませんが、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()を検索して、推測した機能名「serial」が別の名前であるかどうかを確認できます。

HTH


2

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スクリプトもbluetooth / dev / rfcomm *デバイスをリストしているため、最適なソリューションではありません。

他のUNIXプラットフォームでは、シリアルポートの名前がttySではないことに注意してください。Linuxでも、一部のシリアルカードではデバイスに名前を付けることができます。シリアルデバイス名のパターンが間違っていると仮定します。


悪いHALがUbuntuから削除されました(12.04以降)。使いやすいツールがいくつかありました。上記の代替品があるかどうか誰か知っていますか?しかし、HALを備えたバージョン/ディストリビューションを使用している場合、これは見栄えが良いです。
リードヘッジ

2

/ proc / tty / driversの使用は、ロードされているttyドライバーのみを示します。シリアルポートのリストを探している場合は、/ dev / serialを確認してください。これには、by-idとby-pathの2つのサブディレクトリがあります。

例:

# 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


どうやらこれはディストリビューションに依存しています。私のボックス(Debianを実行)で/ dev / serialが見つかりません
SimonC

0

グループダイヤルアウトを介した私のアプローチは、ユーザー 'dialout'のすべてのttyに ls -l /dev/tty* | grep 'dialout' フォルダーのみを取得する ls -l /dev/tty* | grep 'dialout' | rev | cut -d " " -f1 | rev

tty出力を簡単に聞く(例:arduinoシリアル出力の場合): head --lines 1 < /dev/ttyUSB0

すべてのttyを1行だけ聞いてください: 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

ここでtty-Nameを選択できます。 ls /sys/class/tty/*/device/driver | grep 'driver' | cut -d "/" -f 5



0

はい、私は知っています、私は(いつものように)遅すぎます。これが私のコードです(mk2の応答に基づく)。多分これは誰かを助ける:

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

あなたのコードは、stackoverflow.com / a / 4701610/43615が参照する回答を解析するようです。もしそうなら、返信でそれについて言及していただけますか?
Thomas Tempelmann
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.