Linux에서 모든 직렬 장치 (ttyS, ttyUSB, ..)를 열지 않고 찾는 방법은 무엇입니까?


113

Linux 시스템에서 사용 가능한 모든 직렬 포트 / 장치 목록을 가져 오는 올바른 방법은 무엇입니까?

즉,에서 모든 장치를 반복 할 때 /dev/일반적인 방식으로 직렬 포트, 즉 일반적으로 전송 속도 및 RTS / CTS 흐름 제어를 지원하는 장치를 어떻게 알 수 있습니까?

솔루션은 C로 코딩됩니다.

나는 이것을 분명히 잘못하는 타사 라이브러리를 사용하고 있기 때문에 묻습니다 /dev/ttyS*.. 문제는 예를 들어 USB를 통한 직렬 포트 (USB-RS232 어댑터에서 제공)가 있고 해당 포트가 / dev / ttyUSB *에 나열되어 있다는 것입니다. 그리고 Linux.org에서 Serial-HOWTO를 읽으면서 시간이 다가옴 에 따라 다른 이름 공간도있을 것이라는 생각을 얻었습니다.

따라서 직렬 장치를 감지하는 공식적인 방법을 찾아야합니다. 문제는 문서화 된 것으로 보이지 않거나 찾을 수 없다는 것입니다.

한 가지 방법은 모든 파일을 열고 직렬 장치에서만 사용할 수 /dev/tty*있는 특정 파일을 호출하는 ioctl()것입니다. 그래도 좋은 해결책이 될까요?

최신 정보

hrickards 는 "setserial"에 대한 소스를 살펴볼 것을 제안했습니다. 그 코드는 내가 염두에 둔 것과 정확히 일치합니다.

먼저 다음과 같은 장치를 엽니 다.

fd = open (path, O_RDWR | O_NONBLOCK)

그런 다음 다음을 호출합니다.

ioctl (fd, TIOCGSERIAL, &serinfo)

해당 호출이 오류를 반환하지 않으면 분명히 직렬 장치입니다.

Serial Programming / termios 에서 유사한 코드를 발견 했는데 O_NOCTTY옵션을 추가 할 것을 제안했습니다 .

하지만이 접근 방식에는 한 가지 문제가 있습니다.

이 코드를 BSD Unix (즉, Mac OS X)에서 테스트했을 때도 작동했습니다. 그러나 Bluetooth를 통해 제공되는 직렬 장치는 시스템 (드라이버)이 Bluetooth 장치에 연결을 시도하게하여 시간 초과 오류와 함께 반환되기까지 시간이 걸립니다. 이것은 장치를 열기 만하면 발생합니다. 그리고 비슷한 일이 Linux에서도 발생할 수 있다고 상상할 수 있습니다. 이상적으로는 유형을 파악하기 위해 장치를 열 필요가 없습니다. ioctl열지 않고 함수 를 호출 하거나 연결을 일으키지 않는 방식으로 장치를 여는 방법도 있는지 궁금합니다 .

어떻게해야합니까?


1
익명의 누군가가 거부 된이 편집을 제안 했으므로 대신 여기에 주석으로 남겨 둡니다. TIOCMGET 대신 ioctl 호출에서 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/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/

@entalpi 찾을 수 /dev/zero있습니다. 정말로 이것이 직렬 장치라고 생각하십니까?
AH

이미 / sys / class / tty에 이름이 있으므로 / dev에서 검색하는 것은 쓸모가 없습니다 (기본적으로 udev는 / dev / DEVNAME 노드를 생성 함). 관심있는 것은 / dev에있는 그러한 장치를 가리키는 "기호"링크입니다. 이것은 찾기가 훨씬 더 어렵습니다.
xryl669

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-08-14

4
좋은 팁! 불행히도 나는 이것이 내장 직렬 포트를 표시하지 않고 USB 직렬 포트 (연결되었을 때 udev에서 볼 수 있음) 만 표시한다고 생각합니다. VMware VM (VM에서 제공하는 ttyS0 / COM1 포함)의 Ubuntu 14에서 / dev / serial에 대한 내용이 표시되지 않으며 udev 규칙 (60-persistent-serial.rules)은 udev 장치 만보고 있습니다. 나는 udev가 "내장 된"ttyS * 직렬 포트에 대해 알지 못한다고 생각합니다. 다른 답변에서와 같이 ioctl 또는 이와 유사한 것으로 테스트해야합니다.
Reed Hedges 2016 년

ls / dev / serial / ls : '/ dev / serial /'에 액세스 할 수 없음 : 해당 파일 또는 디렉토리 없음 Slackware 14.2 현재 x64
jpka

2
@jpka : 찾을 직렬 장치가없는 경우 발생합니다. 나는 위와 같이했고 작동했습니다. 그런 다음 USB에서 내 (FTDI) 직렬 장치를 뽑았고 나중에 설명했던 오류가 발생했습니다.
Warpspace

13

다음 코드와 같은 작업을하고 있습니다. 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

유효한 장치 유형을보고하는 포트만 유효합니다.

직렬 포트를 열거하는 전체 소스는 다음과 같습니다. 추가를 환영합니다.

#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는 그 자체로는 의미가없고 대상 리소스가 미래에 살아있을 것이라는 보장이 없기 때문에 잘못된 답변 으로 간주됩니다 . 링크하려는 정보의 요약을 최소한 포함 시키십시오.
j0k

Soren 덕분에 우리는 API와 그것에 대한 아이디어를 알고 있지만 Soren을 정말 잘했습니다. 다시 한 번 감사드립니다.
ind79ra

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


3

-g 옵션이있는 seterial은 원하는 작업을 수행하는 것으로 보이며 C 소스는 http://www.koders.com/c/fid39344DABD14604E70DF1B8FEA7D920A94AF78BF8.aspx 에서 사용할 수 있습니다 .


나는 코드를 보았고 장치를 열어야하기 때문에 마지막에 내 질문에서 설명하는 결함이 있습니다. 이는 이미 연결 시도로 이어질 수 있습니다. 그러나 Linux 드라이버는 블루투스 지원과 관련하여 현재 OSX 드라이버보다 똑똑 할 수 있습니다. 바로 연결을 열 수 없기 때문일까요? 누가 알아? 구체적으로 명확히하기 위해 새로운 질문을 시작해야 할 것 같습니다. 괜찮은 것으로 판명되면 여기에서도 답변을 수락 할 수 있습니다. Hmmm ...
Thomas Tempelmann

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 스크립트는 블루투스 / dev / rfcomm * 장치를 나열하므로 최상의 솔루션이 아닙니다.

다른 유닉스 플랫폼에서는 직렬 포트의 이름이 ttyS가 아닙니까? 심지어 리눅스에서도 일부 직렬 카드를 사용하면 장치의 이름을 지정할 수 있습니다. 직렬 장치 이름의 패턴이 잘못되었다고 가정합니다.


너무 나쁜 HAL은 Ubuntu에서 제거되었고 (12.04 이후), 사용하기 쉬운 몇 가지 도구가있었습니다. 위의 대체품이 있는지 아는 사람이 있습니까? 그러나 HAL이있는 버전 / distro를 사용하는 경우 이것은 멋지게 보입니다.
Reed Hedges

2

/ proc / tty / drivers를 사용하면로드 된 tty 드라이버 만 표시됩니다. 직렬 포트 목록을 찾고 있다면 / dev / serial을 확인하면 by-id와 by-path라는 두 개의 하위 디렉토리가 있습니다.

전의:

# find . -type l
./by-path/usb-0:1.1:1.0-port0
./by-id/usb-Prolific_Technology_Inc._USB-Serial_Controller-if00-port0

이 게시물 덕분에 : /superuser/131044/how-do-i-know-which-dev-ttys-is-my-serial-port


분명히 이것은 배포판에 의존합니다. 나는 (데비안 실행) 내 상자는 / dev / 시리얼을 찾을 수 없습니다
SimonC

0

그룹 다이얼 아웃 을 통한 내 접근 방식 은 사용자 '다이얼 아웃' ls -l /dev/tty* | grep 'dialout' 으로 모든 tty를 가져 와서 폴더 만 가져옵니다. ls -l /dev/tty* | grep 'dialout' | rev | cut -d " " -f1 | rev

예를 들어 arduino 직렬 출력시 tty 출력을 쉽게 듣습니다. 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

나는 드라이버를 찾는 접근 방식을 정말 좋아합니다. ll /sys/class/tty/*/device/driver

이제 tty-Name을 선택할 수 있습니다. ls /sys/class/tty/*/device/driver | grep 'driver' | cut -d "/" -f 5


0

직렬 통신 관리자 라이브러리에는 원하는 작업을 대상으로하는 많은 API와 기능이 있습니다. 장치가 USB-UART 인 경우 해당 VID / PID를 사용할 수 있습니다. 장치가 BT-SPP 인 경우 플랫폼 별 API를 사용할 수 있습니다. 직렬 포트 프로그래밍에 대한이 프로젝트를 살펴보십시오. https://github.com/RishiGupta12/serial-communication-manager


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.