Bagaimana menemukan semua perangkat serial (ttyS, ttyUSB, ..) di Linux tanpa membukanya?


113

Apa cara yang tepat untuk mendapatkan daftar semua port serial / perangkat yang tersedia di sistem Linux?

Dengan kata lain, ketika saya mengulang semua perangkat di /dev/, bagaimana cara mengetahui mana yang merupakan port serial dengan cara klasik, yaitu, yang biasanya mendukung baud rate dan kontrol aliran RTS / CTS ?

Solusinya akan diberi kode dalam C.

Saya bertanya karena saya menggunakan pustaka pihak ketiga yang jelas melakukan kesalahan ini: Tampaknya hanya berulang /dev/ttyS*. Masalahnya adalah bahwa ada, misalnya, port serial melalui USB (disediakan oleh adaptor USB-RS232), dan itu terdaftar di bawah / dev / ttyUSB *. Dan membaca Serial-HOWTO di Linux.org , saya mendapat ide bahwa akan ada ruang nama lain juga, seiring waktu.

Jadi saya perlu menemukan cara resmi untuk mendeteksi perangkat serial. Masalahnya adalah tidak ada yang tampaknya didokumentasikan, atau saya tidak dapat menemukannya.

Saya membayangkan salah satu cara adalah membuka semua file dari /dev/tty*dan memanggil file tertentu ioctl()yang hanya tersedia di perangkat serial. Namun, apakah itu solusi yang bagus?

Memperbarui

hrickards menyarankan untuk melihat sumber untuk "setserial". Kodenya melakukan persis seperti yang saya pikirkan:

Pertama, ini membuka perangkat dengan:

fd = open (path, O_RDWR | O_NONBLOCK)

Kemudian memanggil:

ioctl (fd, TIOCGSERIAL, &serinfo)

Jika panggilan itu tidak mengembalikan kesalahan, maka itu adalah perangkat serial, tampaknya.

Saya menemukan kode serupa di Serial Programming / termios , yang menyarankan untuk juga menambahkan O_NOCTTYopsi.

Namun, ada satu masalah dengan pendekatan ini:

Ketika saya menguji kode ini di BSD Unix (yaitu, Mac OS X), itu berhasil juga. Namun , perangkat serial yang disediakan melalui Bluetooth menyebabkan sistem (driver) mencoba menyambung ke perangkat Bluetooth, yang membutuhkan beberapa saat sebelum kembali dengan kesalahan batas waktu. Ini disebabkan hanya dengan membuka perangkat. Dan saya dapat membayangkan bahwa hal serupa dapat terjadi di Linux juga - idealnya, saya tidak perlu membuka perangkat untuk mengetahui jenisnya. Saya ingin tahu apakah ada juga cara untuk menjalankan ioctlfungsi tanpa membuka, atau membuka perangkat dengan cara yang tidak menyebabkan koneksi dibuat?

Apa yang harus saya lakukan?


1
Seseorang anonim telah menyarankan edit ini, yang ditolak, jadi saya tinggalkan di sini sebagai komentar sebagai gantinya: Jika Anda menggunakan bendera TIOCGSERIAL dalam panggilan ioctl, alih-alih TIOCMGET, maka panggilan tidak mengembalikan kesalahan dengan beberapa jalur yang salah yang tidak lihat port COM (serial). Dengan flag TIOCMGET, ioctl hanya bekerja dengan port COM yang tersedia untuk diakses di jalur yang memungkinkan TTY dan TTYUSB.
Thomas Tempelmann

Jawaban:


78

Sistem /sysfile harus berisi banyak informasi untuk pencarian Anda. Sistem saya (2.6.32-40-generik # 87-Ubuntu) menyarankan:

/sys/class/tty

Yang memberi Anda deskripsi tentang semua perangkat TTY yang dikenal sistem. Contoh yang dipangkas:

# 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/

Mengikuti salah satu tautan berikut:

# 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

Di sini devfile tersebut berisi informasi ini:

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

Ini adalah simpul mayor / minor. Ini dapat dicari di /devdirektori untuk mendapatkan nama yang mudah digunakan:

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

The /sys/class/ttydir berisi semua perangkat TTY tetapi Anda mungkin ingin mengecualikan mereka terminal virtual dan terminal semu sial. Saya sarankan Anda memeriksa hanya yang memiliki device/driverentri:

# 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 Anda akan menemukan /dev/zero. Apakah menurut Anda, ini adalah perangkat serial?
H

Mencari di / dev tidak berguna, karena Anda sudah memiliki nama di / sys / class / tty (karena secara default udev membuat node / dev / DEVNAME). Yang Anda minati adalah tautan "simbolis" di / dev yang mengarah ke perangkat tersebut. Ini jauh lebih sulit ditemukan.
xryl669

28

Di kernel terbaru (tidak yakin sejak kapan) Anda dapat membuat daftar konten / dev / serial untuk mendapatkan daftar port serial di sistem Anda. Mereka sebenarnya adalah symlink yang menunjuk ke / dev / node yang benar:

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

Ini adalah adaptor USB-Serial, seperti yang Anda lihat. Perhatikan bahwa ketika tidak ada port serial pada sistem, direktori / dev / serial / tidak ada. Semoga ini membantu :).


3
Ini adalah fungsi udev (khususnya konfigurasinya di /lib/udev/rules.d/??-persistent-serial.rules), yang diperkenalkan di 2.5.
ergosys

4
Tip bagus! Sayangnya saya tidak berpikir ini akan menunjukkan port serial bawaan, hanya port serial USB (dilihat oleh udev saat terpasang). Saya tidak melihat apa pun untuk / dev / serial di Ubuntu 14 di VMware VM (dengan ttyS0 / COM1 disediakan oleh VM), dan aturan udev (60-persistent-serial.rules) hanya melihat perangkat udev - Saya rasa udev tidak mengetahui tentang port serial "built-in" ttyS *, mereka harus diuji dengan ioctl atau serupa seperti di jawaban lain.
Reed Hedges

ls / dev / serial / ls: tidak dapat mengakses '/ dev / serial /': Tidak ada file atau direktori seperti itu Slackware 14.2 x64 saat ini
jpka

2
@jpka: Itu terjadi jika tidak ada perangkat serial yang dapat ditemukan. Saya melakukan seperti di atas dan berhasil. Saya kemudian mencabut perangkat serial (FTDI) saya dari USB dan setelah itu menghasilkan kesalahan yang Anda jelaskan.
Warpspace

13

Saya melakukan sesuatu seperti kode berikut. Ia bekerja untuk USB-perangkat dan juga serial8250-devuices bodoh yang kita semua memiliki 30 - tetapi hanya beberapa dari mereka yang benar-benar berfungsi.

Pada dasarnya saya menggunakan konsep dari jawaban sebelumnya. Pertama-tama hitung semua tty-devices di / sys / class / tty /. Perangkat yang tidak berisi subdir / perangkat disaring. / sys / class / tty / console adalah perangkat semacam itu. Kemudian perangkat yang benar-benar berisi perangkat kemudian diterima sebagai port serial yang valid tergantung pada target dari 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

dan untuk 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

Semua driver yang digerakkan oleh serial8250 harus berupa probe menggunakan ioctl yang disebutkan sebelumnya.

        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

Hanya port yang melaporkan jenis perangkat yang valid yang valid.

Sumber lengkap untuk menghitung serialports terlihat seperti ini. Penambahan dipersilakan.

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

Lone link dianggap jawaban yang buruk karena tidak ada artinya dengan sendirinya dan sumber daya target tidak dijamin akan tetap hidup di masa depan. Harap coba untuk memasukkan setidaknya ringkasan informasi yang Anda tautkan.
j0k

Terima kasih kepada Soren untuk ini, bahkan kami tahu API dan beberapa ide tentang itu tetapi Anda benar-benar hebat Soren, terima kasih lagi.
ind79ra

12

Saya rasa saya menemukan jawabannya di dokumentasi sumber kernel saya: /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 

Berikut ini tautan ke file ini: http://git.kernel.org/?p=linux/kernel/git/next/linux-next.git;a=blob_plain;f=Documentation/filesystems/proc.txt;hb = e8883f8057c0f7c9950fa9f20568f37bfa62f34a


Ya, sepertinya itu berhasil. Namun, solusi ini mengharuskan saya membaca file teks dan menguraikannya. Saya ingin tahu apakah ada yang lebih baik, yaitu API yang memungkinkan saya mendapatkan konten ini dalam format biner terstruktur.
Thomas Tempelmann


3

setserial dengan opsi -g tampaknya melakukan apa yang Anda inginkan dan sumber C tersedia di http://www.koders.com/c/fid39344DABD14604E70DF1B8FEA7D920A94AF78BF8.aspx .


Saya melihat kodenya dan ada kekurangan yang saya jelaskan dalam pertanyaan saya di akhir karena harus membuka perangkat, yang mungkin sudah mengarah pada upaya koneksi - yang pada gilirannya tidak baik. Tapi kemudian, mungkin driver Linux lebih pintar daripada driver OSX saat ini dalam hal dukungan bluetooth, karena mereka tidak akan langsung membuka koneksi? Siapa tahu? Mungkin saya harus memulai pertanyaan baru untuk menjelaskannya secara spesifik. Jika ternyata tidak apa-apa, maka saya dapat menerima jawaban Anda di sini juga. Hmmm ...
Thomas Tempelmann

3

Saya tidak memiliki perangkat serial di sini untuk mengujinya, tetapi jika Anda memiliki python dan dbus, Anda dapat mencobanya sendiri.

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")

Jika gagal Anda dapat mencari di dalam hwmanager_i.GetAllDevicesWithProperties()untuk melihat apakah nama kemampuan "serial" yang saya tebak memiliki nama yang berbeda.

HTH


2

Saya tidak memiliki perangkat serial USB, tetapi harus ada cara untuk menemukan port yang sebenarnya menggunakan pustaka HAL secara langsung:

====================================================================
#! /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

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

Kode python-dbus yang diposting atau skrip sh ini mencantumkan perangkat bluetooth / dev / rfcomm *, jadi ini bukan solusi terbaik.

Perhatikan bahwa pada platform unix lainnya, port serial tidak bernama ttyS? dan bahkan di linux, beberapa kartu serial memungkinkan Anda memberi nama perangkat. Asumsi pola dalam nama perangkat serial salah.


HAL yang terlalu buruk telah dihapus dari Ubuntu (setelah 12.04), itu memiliki beberapa alat yang bagus dan mudah digunakan. Adakah yang tahu kalau ada pengganti di atas? Tetapi jika Anda menggunakan versi / distro yang memiliki HAL ini terlihat bagus.
Reed Hedges

2

Menggunakan / proc / tty / drivers hanya menunjukkan driver tty mana yang dimuat. Jika Anda mencari daftar port serial, periksa / dev / serial, ini akan memiliki dua subdirektori: by-id dan 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

Terima kasih untuk posting ini: /superuser/131044/how-do-i-know-which-dev-ttys-is-my-serial-port


Tampaknya ini bergantung pada distro. Saya tidak dapat menemukan / dev / serial di kotak saya (menjalankan Debian)
SimonC

0

Pendekatan saya melalui panggilan grup untuk mendapatkan setiap tty dengan 'dialout' pengguna ls -l /dev/tty* | grep 'dialout' hanya untuk mendapatkan foldernya ls -l /dev/tty* | grep 'dialout' | rev | cut -d " " -f1 | rev

mudah mendengarkan keluaran tty misal saat arduino keluar serial: head --lines 1 < /dev/ttyUSB0

dengarkan setiap tty out hanya untuk satu baris: for i in $(ls -l /dev/tty* | grep 'dialout' | rev | cut -d " " -f1 | rev); do head --lines 1 < $i; done

Saya sangat suka pendekatan melalui mencari driver: ll /sys/class/tty/*/device/driver

Anda dapat memilih tty-Name sekarang: ls /sys/class/tty/*/device/driver | grep 'driver' | cut -d "/" -f 5



0

ya, saya tahu, saya terlambat (seperti biasa). Ini potongan kode saya (berdasarkan balasan dari mk2). Mungkin ini membantu seseorang:

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

Tampaknya kode Anda menguraikan apa yang dirujuk oleh jawaban stackoverflow.com/a/4701610/43615 . Jika demikian, bisakah Anda menyebutkannya dalam balasan Anda?
Thomas Tempelmann
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.