Linux'ta tüm seri aygıtları (ttyS, ttyUSB, ..) açmadan nasıl bulabilirim?


113

Bir Linux sistemindeki tüm mevcut seri bağlantı noktalarının / cihazların bir listesini almanın uygun yolu nedir?

Başka bir deyişle, içindeki tüm aygıtları yinelediğimde /dev/, hangilerinin klasik şekilde seri bağlantı noktaları olduğunu, yani genellikle baud hızlarını ve RTS / CTS akış denetimini destekleyenleri nasıl anlarım ?

Çözüm C olarak kodlanacaktır.

Soruyorum çünkü bunu açıkça yanlış yapan bir üçüncü taraf kitaplığı kullanıyorum: Görünüşe göre sadece yinelemeli /dev/ttyS*. Sorun, örneğin, USB üzerinden seri bağlantı noktalarının (USB-RS232 bağdaştırıcıları tarafından sağlanır) bulunması ve bunların / dev / ttyUSB * altında listelenmesidir. Ve Linux.org'da Seri NASIL'ı okurken, zamanla başka isim boşluklarının da olacağı fikrini alıyorum.

Bu yüzden seri cihazları tespit etmenin resmi yolunu bulmam gerekiyor. Sorun şu ki hiçbiri belgelenmemiş gibi görünüyor veya ben bulamıyorum.

Bir yolun tüm dosyaları açmak /dev/tty*ve ioctl()bunlardan yalnızca seri cihazlarda kullanılabilen belirli bir dosyayı çağırmak olduğunu hayal ediyorum . Yine de bu iyi bir çözüm olur mu?

Güncelleme

hrickards "seterial " için kaynağa bakmayı önerdi. Kodu tam olarak aklımdaki şeyi yapıyor:

İlk olarak, aşağıdakileri içeren bir cihaz açar:

fd = open (path, O_RDWR | O_NONBLOCK)

Sonra şunu çağırır:

ioctl (fd, TIOCGSERIAL, &serinfo)

Bu çağrı herhangi bir hata vermezse, görünüşe göre bir seri cihazdır.

Seri Programlama / termios'ta , O_NOCTTYseçeneği de eklemeyi öneren benzer bir kod buldum .

Yine de bu yaklaşımla ilgili bir sorun var:

Bu kodu BSD Unix (yani Mac OS X) üzerinde test ettiğimde de işe yaradı. Ancak , Bluetooth aracılığıyla sağlanan seri aygıtlar sistemin (sürücünün) Bluetooth aygıtına bağlanmaya çalışmasına neden olur ve bu aygıtın zaman aşımı hatasıyla geri dönmesi biraz zaman alır. Bunun nedeni sadece cihazın açılmasıdır. Ve Linux'ta da benzer şeylerin olabileceğini hayal edebiliyorum - ideal olarak, türünü anlamak için cihazı açmam gerekmiyor. ioctlAçık olmadan işlevleri çağırmanın veya bağlantı yapılmasına neden olmayacak şekilde bir cihazı açmanın bir yolu olup olmadığını merak ediyorum.

Ne yapmalıyım?


1
Reddedilen bu düzenlemeyi anonim biri önerdi, bu yüzden onu burada bir yorum olarak bırakıyorum: ioctl çağrısında TIOCMGET yerine TIOCGSERIAL bayrağını kullanırsanız, çağrı, bazı yanlış yollarla hata vermez COM (seri) bağlantı noktasına bakın. TIOCMGET bayrağıyla, ioctl yalnızca hem TTY hem de TTYUSB olası yollarında erişilebilen COM bağlantı noktalarıyla çalışır.
Thomas Tempelmann

Yanıtlar:


78

/sysDosya sistemi arayışı için bol bilgilerini içermesi gerekir. Sistemim (2.6.32-40-jenerik # 87-Ubuntu) şunları öneriyor:

/sys/class/tty

Bu size sistem tarafından bilinen tüm TTY cihazlarının açıklamalarını verir. Kesilmiş bir örnek:

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

Bu bağlantılardan birinin ardından:

# 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

İşte devdosya şu bilgileri içerir:

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

Bu, büyük / küçük düğümdür. /devKullanıcı dostu isimler almak için bunlar dizinde aranabilir :

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

/sys/class/ttyDir tüm TTY cihazlarını içeren ancak sanal terminaller ve sözde terminalleri bu sinir bozucu hariç tutmak isteyebilirsiniz. Yalnızca device/drivergirişi olanları incelemenizi öneririm :

# 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 Bulacaksın /dev/zero. Bunun bir seri cihaz olduğunu gerçekten düşünüyor musunuz?
AH

/ Dev içinde arama yapmak işe yaramaz, çünkü / sys / class / tty içinde isme zaten sahipsiniz (varsayılan olarak udev / dev / DEVNAME düğümünü yaratır). İlgilendiğiniz şey, / dev içindeki bu tür bir aygıta işaret eden herhangi bir "sembolik" bağlantıdır. Bunu bulmak çok daha zor.
xryl669

28

Son çekirdeklerde (ne zamandan beri emin değilim), sisteminizdeki seri bağlantı noktalarının bir listesini almak için / dev / serial içeriğini listeleyebilirsiniz. Aslında doğru / dev / düğümü işaret eden sembolik bağlardır:

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

Gördüğünüz gibi, bu bir USB Seri adaptördür. Sistemde seri bağlantı noktası olmadığında, / dev / serial / dizininin olmadığını unutmayın. Bu yardımcı olur umarım :).


3
Bu, 2.5'te tanıtılan udev'in bir işlevidir (özellikle /lib/udev/rules.d/??-persistent-serial.rules'deki yapılandırması).
ergosys

4
Harika bir ipucu! Maalesef bunun yerleşik seri portları göstereceğini sanmıyorum, sadece USB seri portları (takıldığında udev tarafından görülür). Bir VMware sanal makinesinde (VM tarafından sağlanan ttyS0 / COM1 ile) Ubuntu 14'te / dev / serial için hiçbir şey görmüyorum ve udev kuralları (60-persistent-serial.rules) yalnızca udev cihazlarına bakıyor - Udev'in "yerleşik" ttyS * seri bağlantı noktalarını bulduğunu sanmıyorum, ioctl veya diğer yanıtlarda olduğu gibi test edilmeleri gerekecek.
Reed Hedges

ls / dev / serial / ls: '/ dev / serial /' erişilemiyor: Böyle bir dosya veya dizin yok Slackware 14.2 mevcut x64
jpka

2
@jpka: Bulunacak bir seri cihaz yoksa bu olur. Yukarıdaki gibi yaptım ve işe yaradı. Daha sonra (FTDI) seri cihazımı USB'den çıkardım ve daha sonra tarif ettiğiniz hatayı ürettim.
Warpspace

13

Aşağıdaki kod gibi bir şey yapıyorum. USB cihazları ve ayrıca hepimizin 30 tanesine sahip olduğu aptal serial8250 cihazları için çalışıyor - ama sadece birkaçı gerçekten çalışıyor.

Temelde önceki cevaplardan kavramı kullanıyorum. Önce / sys / class / tty / içindeki tüm tty aygıtlarını numaralandırın. / Device alt dizini içermeyen cihazlar filtrelenir. / sys / class / tty / console böyle bir cihazdır. Daha sonra, gerçekte bir aygıt içeren aygıtlar, sürücü-sembolik bağlantı fx'in hedefine bağlı olarak geçerli seri bağlantı noktası olarak kabul edilir.

$ 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

ve ttyS0 için

$ 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 tarafından çalıştırılan tüm sürücüler, daha önce bahsedilen ioctl kullanan problar olmalıdır.

        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

Yalnızca geçerli bir cihaz türünü bildiren bağlantı noktası geçerlidir.

Seri portları numaralandırmak için tam kaynak şuna benzer. İlaveler kabul edilir.

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

Yalnız bağlantı, kendi başına anlamsız olduğu ve hedef kaynağın gelecekte hayatta kalacağı garanti edilmediği için zayıf bir cevap olarak kabul edilir . Lütfen bağlantı kurduğunuz bilgilerin en azından özetini eklemeye çalışın.
j0k

Bunun için Soren'e teşekkürler, API'leri ve bununla ilgili bazı fikirleri biliyoruz bile ama Soren'i gerçekten iyi yaptın, tekrar teşekkürler.
ind79ra

12

Cevabı çekirdek kaynak belgelerimde bulduğumu düşünüyorum: /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 

İşte bu dosyanın bağlantısı: http://git.kernel.org/?p=linux/kernel/git/next/linux-next.git;a=blob_plain;f=Documentation/filesystems/proc.txt;hb = e8883f8057c0f7c9950fa9f20568f37bfa62f34a


Evet, işe yarıyor gibi görünüyor. Ancak, bu çözüm bir metin dosyasını okumamı ve ayrıştırmamı gerektiriyor. Daha iyi bir yer olup olmadığını merak ediyorum, yani bu içerikleri yapılandırılmış bir ikili biçimde elde etmemi sağlayan bir API.
Thomas Tempelmann


3

-g seçeneği ile seterial, istediğinizi yapıyor gibi görünür ve C kaynağı http://www.koders.com/c/fid39344DABD14604E70DF1B8FEA7D920A94AF78BF8.aspx adresinde mevcuttur .


Koda baktım ve sonunda sorumda açıkladığım kusur var, çünkü cihazı açması gerekiyor, bu da zaten bir bağlantı girişimine yol açabilir - ki bu da iyi değil. Ancak o zaman, Bluetooth desteği söz konusu olduğunda Linux sürücüleri mevcut OSX sürücüsünden daha akıllıdır, çünkü hemen bir bağlantı açmazlar? Kim bilir? Belki bunu açıklığa kavuşturmak için yeni bir soru başlatmalıyım. Sorun olmadığı ortaya çıkarsa, cevabını burada da kabul edebilirim. Hmmm ...
Thomas Tempelmann

3

Burada test edecek bir seri aygıtım yok, ancak python ve dbus'a sahipseniz kendiniz deneyebilirsiniz.

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

Başarısız olursa içeride arayabilirsin hwmanager_i.GetAllDevicesWithProperties() az önce tahmin ettiğim "seri" yetenek adının farklı bir ada sahip olup olmadığını görmek için .

HTH


2

Bir USB seri aygıtım yok, ancak HAL kitaplıklarını doğrudan kullanarak gerçek bağlantı noktalarını bulmanın bir yolu olmalı:

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

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

Gönderilen python-dbus kodu veya bu sh betiği bluetooth / dev / rfcomm * aygıtlarını listeler, bu nedenle en iyi çözüm değildir.

Diğer unix platformlarında, seri bağlantı noktalarının ttyS olarak adlandırılmadığını unutmayın. ve hatta linux'ta bile, bazı seri kartlar aygıtları adlandırmanıza izin verir. Seri aygıt adlarında bir model varsaymak yanlıştır.


Ubuntu'dan çok kötü HAL kaldırıldı (12.04'ten sonra), kullanımı kolay bazı güzel araçları vardı. Yukarıdakilerin yerine geçip geçmediğini bilen var mı? Ancak HAL'e sahip bir sürüm / dağıtım kullanıyorsanız, bu güzel görünüyor.
Reed Hedges

2

/ Proc / tty / drivers kullanılması yalnızca hangi tty sürücülerinin yüklendiğini gösterir. Seri bağlantı noktalarının bir listesini / dev / serial kontrolünü arıyorsanız, iki alt dizini olacaktır: by-id ve 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

Bu gönderi için teşekkürler: /superuser/131044/how-do-i-know-which-dev-ttys-is-my-serial-port


Görünüşe göre bu dağıtım bağımlı.
Kutumda

0

Kullanıcı 'çevirme' ile her tty'yi yalnızca klasörünü almak için almak için grup çevirme yoluyla yaklaşımımls -l /dev/tty* | grep 'dialout'ls -l /dev/tty* | grep 'dialout' | rev | cut -d " " -f1 | rev

tty çıkışını kolay dinleyin, örneğin arduino seri çıktığında: head --lines 1 < /dev/ttyUSB0

her tty'yi yalnızca bir satır için dinleyin: for i in $(ls -l /dev/tty* | grep 'dialout' | rev | cut -d " " -f1 | rev); do head --lines 1 < $i; done

Sürücü arayarak yaklaşımı gerçekten seviyorum: ll /sys/class/tty/*/device/driver

Şimdi tty-Name'i seçebilirsiniz: ls /sys/class/tty/*/device/driver | grep 'driver' | cut -d "/" -f 5



0

evet, biliyorum, çok geç kaldım (her zamanki gibi). İşte benim kod parçam (mk2'nin cevabına göre). Belki bu birine yardımcı olur:

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

Görünüşe göre kodunuz stackoverflow.com/a/4701610/43615'in neyi ifade ettiğini ayrıştırıyor . Eğer öyleyse, cevabınızda bundan bahseder misiniz lütfen?
Thomas Tempelmann
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.