USB 서브시스템
USB 서브시스템을 프로토콜 계층, 컨트롤러 드라이버, 사용자 공간(User Space) 인터페이스까지 통합 관점으로 심층 분석합니다. USB Core와 URB 흐름, EHCI/XHCI host controller 동작, enumeration과 descriptor 파싱, class driver 작성 패턴, Gadget/UDC 기반 디바이스 에뮬레이션, Type-C/PD 및 USB4/Thunderbolt 연동, autosuspend/runtime PM 정책, usbmon·tracepoint·wireshark를 활용한 패킷(Packet) 디버깅(Debugging)까지 실전 제품 개발과 장애 분석에 필요한 내용을 폭넓게 다룹니다.
핵심 요약
- USB Enumeration — 장치 연결 시 허브가 포트 변화를 감지하고, 커널이 장치 디스크립터를 읽어 주소를 할당하고 드라이버를 바인딩하는 전체 과정입니다.
- Transfer Types — Control(설정/상태), Bulk(대용량 데이터), Interrupt(주기적 소량), Isochronous(실시간 스트리밍) 네 가지 전송 타입이 대역폭과 지연 특성이 다릅니다.
- URB (USB Request Block) — USB I/O의 기본 단위로, 드라이버가
usb_submit_urb()로 제출하고 완료 콜백에서 결과를 수신하는 비동기 요청 구조체입니다. - Host Controller (xHCI/EHCI) — USB 호스트 컨트롤러 하드웨어를 추상화하는 HCD(Host Controller Driver)로, xHCI는 USB 3.x를 지원하며 EHCI는 USB 2.0 전용입니다.
- usb_driver / usb_device —
usb_register()로 등록하며,id_table의 Vendor/Product ID 매칭으로probe()가 호출되는 USB 디바이스 모델입니다. - USB Class Drivers — HID(키보드/마우스), Mass Storage(USB 메모리), CDC-ACM(시리얼) 등 USB 클래스 규격별 공통 드라이버가 상위 계층을 구성합니다.
- USB Gadget — 리눅스 장치가 USB 디바이스(Device) 역할을 할 때 사용하는 프레임워크로, ConfigFS 기반으로 기능(Function)을 동적 구성합니다.
- OTG (On-The-Go) — 하나의 포트가 호스트와 디바이스 역할을 동적으로 전환할 수 있는 프로토콜로, ID 핀 상태에 따라 역할이 결정됩니다.
단계별 이해
- USB 열거(Enumeration) 과정 파악 — 장치 연결 → 포트 리셋 → 디스크립터 읽기 → 주소 할당 → 설정 선택 → 드라이버 바인딩 순서를 이해합니다.
lsusb -v로 장치 디스크립터, 설정 디스크립터, 인터페이스 디스크립터 계층을 확인하고,usbmon으로 실제 열거 패킷을 캡처합니다. - URB 기반 I/O 흐름 추적 — 드라이버가 URB를 할당하고 제출하면 HCD가 하드웨어에 스케줄링하고, 완료 시 콜백이 호출되는 비동기 흐름을 따라갑니다.
usb_alloc_urb()→usb_fill_bulk_urb()→usb_submit_urb()→ 완료 콜백 패턴이 USB 드라이버의 기본 I/O 구조입니다. - 전송 타입별 특성과 엔드포인트 이해 — Control/Bulk/Interrupt/Isochronous 각 타입의 대역폭 보장, 재전송 정책, 지연 특성 차이를 파악합니다.
엔드포인트 디스크립터의
bmAttributes와wMaxPacketSize가 전송 타입과 최대 패킷 크기를 결정합니다. - Host Controller Driver 구조 파악 — xHCI의 링 버퍼(Command/Transfer/Event Ring) 기반 스케줄링 구조를 이해합니다.
/sys/bus/usb/devices/에서 USB 장치 트리를 확인하고,/sys/kernel/debug/usb/에서 HCD 상태를 점검합니다. - USB Gadget과 ConfigFS 활용 — 임베디드 장치에서 리눅스를 USB 디바이스로 동작시키는 gadget 프레임워크의 ConfigFS 기반 구성을 실습합니다.
/sys/kernel/config/usb_gadget/에서 기능(Function)을 추가하고 UDC(USB Device Controller)에 바인딩하는 절차를 확인합니다.
USB 개요
USB (Universal Serial Bus)는 1996년 Intel, Compaq, Microsoft 등이 공동으로 제안한 범용 직렬 버스 규격입니다. 키보드, 마우스부터 외장 스토리지, 네트워크 어댑터, 비디오 캡처 장치까지 거의 모든 주변기기의 사실상 표준 인터페이스로 자리 잡았습니다. Linux 커널은 USB 서브시스템을 통해 호스트 컨트롤러 관리, 디바이스 열거, 드라이버 바인딩, 전원 관리(Power Management)를 통합적으로 처리합니다.
USB 버전 발전
| 규격 | 연도 | 최대 속도 | 인코딩 | 커넥터 |
|---|---|---|---|---|
| USB 1.0 | 1996 | 1.5 Mbps (Low Speed) | NRZI | Type-A/B |
| USB 1.1 | 1998 | 12 Mbps (Full Speed) | NRZI | Type-A/B |
| USB 2.0 | 2000 | 480 Mbps (High Speed) | NRZI | Type-A/B, Mini, Micro |
| USB 3.0 | 2008 | 5 Gbps (SuperSpeed) | 8b/10b | Type-A (청색), Type-B SS, Micro-B SS |
| USB 3.1 | 2013 | 10 Gbps (SuperSpeed+) | 128b/132b | Type-C |
| USB 3.2 | 2017 | 20 Gbps (2x2 레인) | 128b/132b | Type-C 전용 |
| USB4 | 2019 | 40 Gbps (Gen 3x2) | PAM3 (64b/66b) | Type-C 전용 |
| USB4 v2.0 | 2022 | 80 Gbps (Gen 4) | PAM3 | Type-C 전용 |
drivers/thunderbolt/)가 USB4 호스트 컨트롤러도 함께 관리합니다.프로토콜 계층 구조
USB 통신은 다음과 같은 계층으로 구성됩니다:
| 계층 | 역할 | 주요 요소 |
|---|---|---|
| Client Software | 클래스/벤더 드라이버, 유저스페이스 앱 | usb_driver, libusb |
| USB System Software | USB Core, 디바이스 열거, 대역폭(Bandwidth) 관리 | usb_device, usb_bus |
| USB Host Controller | 하드웨어 트랜잭션(Transaction) 스케줄링, 데이터 전송 | xHCI, EHCI, OHCI, UHCI |
| USB Bus Interface | 전기/물리 시그널(Signal)링, 패킷 인코딩 | 차동 시그널링, NRZI/8b10b |
USB 프로토콜 기초
디스크립터 계층
USB 디바이스는 자신의 기능을 디스크립터(Descriptor) 계층 구조로 기술합니다. 호스트는 열거(Enumeration) 과정에서 이 디스크립터를 읽어 적절한 드라이버를 바인딩합니다:
| 디스크립터 | bDescriptorType | 핵심 필드 | 설명 |
|---|---|---|---|
| Device | 0x01 | idVendor, idProduct, bDeviceClass | 디바이스 전체 정보, VID/PID로 드라이버 매칭 |
| Configuration | 0x02 | bNumInterfaces, bMaxPower | 전원 특성, 인터페이스 구성 |
| Interface | 0x04 | bInterfaceClass/SubClass/Protocol | 기능 단위, 클래스 드라이버 매칭 기준 |
| Endpoint | 0x05 | bEndpointAddress, bmAttributes, wMaxPacketSize | 데이터 파이프 정의, 전송 타입/방향/크기 |
/* 커널 내부: USB Device Descriptor 구조체 */
/* include/uapi/linux/usb/ch9.h */
struct usb_device_descriptor {
__u8 bLength; /* 18 */
__u8 bDescriptorType; /* USB_DT_DEVICE (0x01) */
__le16 bcdUSB; /* USB 규격 버전 (BCD: 0x0200 = USB 2.0) */
__u8 bDeviceClass; /* 디바이스 클래스 (0x00이면 인터페이스 레벨) */
__u8 bDeviceSubClass;
__u8 bDeviceProtocol;
__u8 bMaxPacketSize0; /* EP0 최대 패킷 크기 (8/16/32/64) */
__le16 idVendor; /* Vendor ID */
__le16 idProduct; /* Product ID */
__le16 bcdDevice; /* 디바이스 릴리즈 번호 */
__u8 iManufacturer; /* 제조사 문자열 인덱스 */
__u8 iProduct; /* 제품명 문자열 인덱스 */
__u8 iSerialNumber; /* 시리얼 번호 문자열 인덱스 */
__u8 bNumConfigurations; /* Configuration 수 */
} __attribute__ ((packed));
코드 설명
include/uapi/linux/usb/ch9.h에 정의된 usb_device_descriptor는 USB 디바이스의 최상위 디스크립터로, 호스트가 열거(Enumeration) 과정에서 가장 먼저 읽는 18바이트 구조체입니다.
- bcdUSB디바이스가 준수하는 USB 규격 버전을 BCD(Binary Coded Decimal)로 나타냅니다. 예를 들어
0x0200은 USB 2.0입니다. - bDeviceClass디바이스 전체의 클래스 코드입니다.
0x00이면 인터페이스 레벨에서 클래스를 지정하며, 복합(Composite) 디바이스에서 흔히 사용됩니다. - bMaxPacketSize0Endpoint 0(Control)의 최대 패킷 크기입니다. 속도별로 8(Low Speed), 64(Full/High Speed) 등 고정 값이 사용됩니다.
- idVendor / idProductUSB-IF가 할당하는 벤더 ID와 제품 ID로, 드라이버 매칭의 기본 키입니다.
usb_device_id테이블에서 이 값으로 바인딩 대상을 결정합니다. - bNumConfigurations디바이스가 지원하는 Configuration 수입니다. 대부분의 디바이스는 1개만 가지지만, 전원 모드에 따라 여러 Configuration을 제공하는 경우도 있습니다.
- __attribute__((packed))구조체 패딩을 방지하여 USB 와이어 포맷과 메모리 레이아웃이 정확히 일치하도록 합니다.
전송 타입
USB는 4가지 전송 타입을 정의하며, 각각 다른 특성과 용도를 가집니다:
| 전송 타입 | 특성 | 대역폭 보장 | 오류 재전송(Retransmission) | 용도 |
|---|---|---|---|---|
| Control | 양방향, 셋업+데이터+상태 단계 | 예약됨 (10~20%) | 예 | 디바이스 설정, 디스크립터 읽기, 벤더 요청 |
| Bulk | 대용량, 잔여 대역폭 사용 | 없음 (잔여 사용) | 예 | Mass Storage, 프린터, 네트워크 데이터 |
| Interrupt | 소량, 폴링(Polling) 주기 보장 | 예약됨 | 예 | 키보드, 마우스, 조이스틱, HID |
| Isochronous | 실시간(Real-time), 일정 대역폭 | 예약됨 | 없음 | 오디오, 비디오, 웹캠 |
0x81은 IN (디바이스 → 호스트) Endpoint 1, 0x02는 OUT (호스트 → 디바이스) Endpoint 2입니다. Endpoint 0(EP0)은 항상 양방향 Control 전송에 사용됩니다.열거 (Enumeration) 과정
USB 디바이스가 연결되면 호스트는 다음 순서로 디바이스를 인식합니다:
- 연결 감지 — Hub가 포트 상태 변경을 인터럽트로 호스트에 통보
- 포트 리셋 — 호스트가 해당 포트에 USB Reset 시그널 전송 (SE0, 10~50ms)
- 속도 감지 — D+/D- 풀업 저항 위치로 Low/Full/High Speed 결정
- 기본 주소 할당 — 디바이스는 주소 0으로 응답, 호스트가 SET_ADDRESS(1~127) 전송
- 디스크립터 읽기 — GET_DESCRIPTOR로 Device, Configuration, Interface, Endpoint 정보 수집
- Configuration 선택 — SET_CONFIGURATION으로 활성 Configuration 설정
- 드라이버 바인딩 — 커널이 VID/PID 또는 클래스 코드 기반으로 적합한 드라이버를 probe
USB 디바이스 열거 상세 흐름
USB 디바이스가 물리적으로 연결되면 커널은 Hub 드라이버를 기점으로 복잡한 열거(Enumeration) 과정을 수행합니다. 이 과정은 전기적 감지, 속도 협상, 주소 할당, 디스크립터 수집, Configuration 선택, 드라이버 바인딩까지 여러 단계로 구성됩니다.
USB 3.x 열거의 차이점
USB 3.x 디바이스는 USB 2.0과 열거 흐름이 유사하지만 몇 가지 중요한 차이가 있습니다:
| 항목 | USB 2.0 | USB 3.x |
|---|---|---|
| 속도 감지 | D+/D- 풀업 저항 | LTSSM(Link Training and Status State Machine) |
| 리셋 방식 | SE0 시그널 (10~50ms) | Warm/Hot Reset + LFPS 핸드셰이크 |
| EP0 크기 | 8/16/32/64 (디스크립터에서 확인) | 항상 512 바이트 (고정) |
| 주소 할당 | SET_ADDRESS 제어 전송 | xHCI Address Device 명령 |
| BOS Descriptor | 없음 (선택적) | 필수 — USB 3.x 성능/SS 디스크립터 포함 |
| 링크 전력 | Suspend/Resume만 | U0/U1/U2/U3 다단계 LPM |
/* USB 3.x BOS(Binary device Object Store) Descriptor */
/* GET_DESCRIPTOR(BOS)로 읽으며, 디바이스의 확장 성능 정보 포함 */
/*
* BOS Descriptor 하위 구조:
* USB 2.0 Extension — LPM(L1) 지원 여부
* SuperSpeed Cap — 지원 속도, U1/U2 탈출 지연 시간
* SuperSpeedPlus Cap — Gen 2x2 등 확장 속도 지원
* Container ID — 디바이스 고유 식별자 (UUID)
* Platform Cap — WebUSB, MS OS 2.0 등 플랫폼 확장
*
* 커널 처리: usb_get_bos_descriptor() → usb_device.bos에 저장
* U1/U2 지연 값으로 LPM 정책 결정
*/
Linux USB Core
Linux USB Core(drivers/usb/core/)는 호스트 컨트롤러와 디바이스 드라이버 사이의 중간 계층으로, 디바이스 열거, URB 관리, 전원 관리, 드라이버 매칭을 담당합니다.
핵심 자료구조
| 구조체(Struct) | 헤더 | 역할 |
|---|---|---|
struct usb_bus | <linux/usb.h> | USB 버스 인스턴스, HCD와 1:1 대응 |
struct usb_device | <linux/usb.h> | USB 디바이스 인스턴스 (디스크립터, 상태, 파이프 정보) |
struct usb_interface | <linux/usb.h> | 인터페이스 단위 — 드라이버가 바인딩하는 대상 |
struct usb_host_config | <linux/usb.h> | Configuration Descriptor 파싱 결과 |
struct usb_host_interface | <linux/usb.h> | Interface Descriptor + Endpoint 배열 |
struct usb_host_endpoint | <linux/usb.h> | Endpoint Descriptor + URB 큐 |
/* struct usb_device 주요 필드 (simplified) */
/* include/linux/usb.h */
struct usb_device {
int devnum; /* 버스 내 디바이스 주소 (1~127) */
char devpath[16]; /* sysfs 경로 문자열 */
enum usb_device_state state; /* 연결 상태 */
enum usb_device_speed speed; /* Low/Full/High/Super/Super+ */
struct usb_device_descriptor descriptor; /* Device Descriptor 사본 */
struct usb_host_config *config; /* 모든 Configuration 배열 */
struct usb_host_config *actconfig; /* 현재 활성 Configuration */
struct usb_host_endpoint ep0; /* Endpoint 0 (Control) */
struct usb_host_endpoint *ep_in[16]; /* IN Endpoint 배열 */
struct usb_host_endpoint *ep_out[16]; /* OUT Endpoint 배열 */
struct usb_device *parent; /* 부모 Hub (Root Hub는 NULL) */
struct usb_bus *bus; /* 소속 버스 */
struct device dev; /* 범용 디바이스 모델 */
int maxchild; /* Hub인 경우 하위 포트 수 */
unsigned can_submit:1; /* URB 제출 가능 여부 */
/* ... */
};
/* struct usb_interface — 드라이버가 bind하는 단위 */
struct usb_interface {
struct usb_host_interface *altsetting; /* Alternate Setting 배열 */
struct usb_host_interface *cur_altsetting; /* 현재 활성 설정 */
unsigned num_altsetting; /* Alternate Setting 개수 */
struct usb_interface_cache *intf_cache;
struct device dev; /* 디바이스 모델 (드라이버 바인딩 대상) */
int minor; /* USB minor 번호 (-1 = 미사용) */
/* ... */
};
코드 설명
include/linux/usb.h에 정의된 두 핵심 자료구조입니다. usb_device는 물리적 USB 디바이스 전체를, usb_interface는 드라이버가 실제로 바인딩하는 논리 단위를 나타냅니다.
- devnum버스 내에서 고유한 디바이스 주소(1~127)입니다.
SET_ADDRESS요청으로 열거 시 할당됩니다. - speed
enum usb_device_speed로 Low/Full/High/Super/Super+ 속도를 구분합니다. HCD가 포트 리셋 후 결정합니다. - actconfig현재 활성화된 Configuration을 가리킵니다.
usb_set_configuration()호출 시 설정됩니다. - ep0, ep_in[], ep_out[]Endpoint 0는 모든 디바이스에 존재하는 양방향 Control 엔드포인트이며,
ep_in/ep_out배열(각 16개)이 방향별 엔드포인트를 관리합니다. - parent / busUSB 디바이스 트리 구조를 형성합니다. Root Hub는
parent가 NULL이고,bus는 소속 호스트 컨트롤러에 1:1 대응합니다. - usb_interface.altsetting하나의 인터페이스가 여러 Alternate Setting을 가질 수 있으며,
usb_set_interface()로 전환합니다. 대역폭 요구가 다른 모드(예: 비디오 해상도)를 지원하는 데 사용됩니다. - usb_interface.devLinux 디바이스 모델의
struct device를 내장하여,usb_driver의probe()/disconnect()가 이 인터페이스 단위로 호출됩니다.
usb_device 전체가 아니라 usb_interface에 바인딩됩니다. 하나의 USB 디바이스가 여러 인터페이스를 가질 수 있고, 각 인터페이스에 서로 다른 드라이버가 바인딩될 수 있습니다 (예: CDC-ACM은 Control + Data 두 개의 인터페이스를 가집니다).USB 스택 내부 구조
USB Core의 자료구조는 계층적으로 연결됩니다. usb_bus는 호스트 컨트롤러에 1:1 대응하고, 그 아래에 usb_device가 트리 형태로 구성됩니다. 각 usb_device는 하나 이상의 usb_interface를 가지며, 인터페이스에는 데이터 전송을 위한 usb_host_endpoint가 연결됩니다.
디바이스 열거 내부 흐름
디바이스 연결 시 USB Core 내부에서 수행되는 열거(Enumeration) 과정의 핵심 함수 호출 순서입니다. Hub 드라이버의 hub_event()가 포트 변경 감지를 시작으로 전체 열거를 주도합니다:
/* drivers/usb/core/hub.c — 디바이스 열거 내부 흐름 */
/* 1. Hub 인터럽트 URB가 포트 상태 변경 감지 */
hub_irq()
→ kick_hub_wq() /* Hub 이벤트 워크큐 스케줄 */
/* 2. 워크큐에서 hub_event() 실행 */
hub_event()
→ port_event() /* 각 포트의 상태 변경 처리 */
→ hub_port_connect_change()
→ hub_port_init() /* 포트 리셋 + 속도 감지 */
→ hub_set_address() /* SET_ADDRESS(1~127) */
→ usb_get_device_descriptor() /* 64바이트 Device Descriptor */
→ usb_new_device() /* 디바이스 등록 시작 */
/* 3. usb_new_device() 상세 */
usb_new_device(struct usb_device *udev)
{
usb_enumerate_device(udev);
→ usb_get_configuration() /* 모든 Configuration Descriptor 읽기 */
→ usb_parse_configuration() /* Interface/Endpoint 파싱 */
→ usb_get_string() /* 제조사/제품명 문자열 */
usb_choose_configuration(udev); /* 최적 Configuration 자동 선택 */
usb_set_configuration(udev, config_num);
device_add(&udev->dev); /* 디바이스 모델 등록 → probe 트리거 */
}
디스크립터 파싱과 Configuration 선택
usb_parse_configuration()은 호스트가 GET_DESCRIPTOR로 수신한 원시 바이트 스트림을 파싱하여 커널 자료구조로 변환합니다. Configuration Descriptor를 기준으로 하위 Interface/Endpoint를 순차적으로 파싱합니다:
/* drivers/usb/core/config.c — Configuration 파싱 핵심 로직 */
/* usb_parse_configuration()이 처리하는 디스크립터 순서:
* Configuration Descriptor (bDescriptorType=0x02)
* └─ Interface Association Descriptor (IAD, 0x0B) — 복합 디바이스용
* └─ Interface Descriptor (0x04)
* └─ Class-Specific Descriptor (클래스별)
* └─ Endpoint Descriptor (0x05)
* └─ SS Endpoint Companion (0x30) — USB 3.x
* └─ SSP Isoc Endpoint Companion (0x31) — USB 3.1+
*/
/* Configuration 자동 선택 기준 (usb_choose_configuration) */
/*
* 1. bNumInterfaces가 가장 많은 Configuration 우선
* 2. 첫 Interface의 bInterfaceClass가 벤더 전용(0xFF)이 아닌 것 우선
* 3. 위 조건이 같으면 첫 번째 Configuration 선택
* 4. USB 허브는 항상 Configuration 1 선택
*
* 드라이버가 직접 선택하려면:
* usb_driver_set_configuration(udev, config_value);
*/
usb_set_interface(udev, ifnum, alt)로 전환합니다.USB 드라이버 모델
드라이버 골격
#include <linux/module.h>
#include <linux/usb.h>
#define MY_VENDOR_ID 0x1234
#define MY_PRODUCT_ID 0x5678
struct my_usb_dev {
struct usb_device *udev; /* USB 디바이스 */
struct usb_interface *intf; /* 바인딩된 인터페이스 */
unsigned char *bulk_in_buf; /* Bulk IN 수신 버퍼 */
size_t bulk_in_size;
__u8 bulk_in_ep; /* Bulk IN endpoint 주소 */
__u8 bulk_out_ep; /* Bulk OUT endpoint 주소 */
};
static int my_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *ep;
struct my_usb_dev *dev;
int i;
iface_desc = intf->cur_altsetting;
/* private 데이터 할당 */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
dev->udev = usb_get_dev(udev);
dev->intf = intf;
/* Endpoint 탐색 */
for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
ep = &iface_desc->endpoint[i].desc;
if (usb_endpoint_is_bulk_in(ep)) {
dev->bulk_in_size = usb_endpoint_maxp(ep);
dev->bulk_in_ep = ep->bEndpointAddress;
dev->bulk_in_buf = kmalloc(dev->bulk_in_size,
GFP_KERNEL);
}
if (usb_endpoint_is_bulk_out(ep))
dev->bulk_out_ep = ep->bEndpointAddress;
}
usb_set_intfdata(intf, dev);
dev_info(&intf->dev, "USB device probed: %04x:%04x\\n",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct));
return 0;
}
static void my_usb_disconnect(struct usb_interface *intf)
{
struct my_usb_dev *dev = usb_get_intfdata(intf);
usb_set_intfdata(intf, NULL);
usb_put_dev(dev->udev);
kfree(dev->bulk_in_buf);
kfree(dev);
dev_info(&intf->dev, "USB device disconnected\\n");
}
/* USB Device ID 테이블 — 드라이버가 지원하는 디바이스 목록 */
static const struct usb_device_id my_usb_ids[] = {
{ USB_DEVICE(MY_VENDOR_ID, MY_PRODUCT_ID) },
{ } /* 종료 마커 */
};
MODULE_DEVICE_TABLE(usb, my_usb_ids);
static struct usb_driver my_usb_driver = {
.name = "my_usb",
.id_table = my_usb_ids,
.probe = my_usb_probe,
.disconnect = my_usb_disconnect,
};
module_usb_driver(my_usb_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Example USB driver");
코드 설명
drivers/usb/core/driver.c 기반의 USB 드라이버 등록/해제와 probe/disconnect 콜백 전체 패턴을 보여주는 최소 예제입니다.
- struct my_usb_dev드라이버 전용 컨텍스트 구조체입니다.
usb_device포인터, 바인딩된 인터페이스, Bulk 엔드포인트 정보와 수신 버퍼를 보관합니다. - my_usb_probe()USB Core가 ID 테이블 매칭에 성공하면 호출합니다.
interface_to_usbdev()로 상위usb_device를 얻고,cur_altsetting의 엔드포인트 디스크립터를 순회하여 Bulk IN/OUT 주소를 저장합니다. - usb_get_dev() / usb_put_dev()
usb_device의 참조 카운트를 증감합니다.probe()에서 증가,disconnect()에서 감소하여 디바이스 해제 시점을 안전하게 관리합니다. - usb_set_intfdata()인터페이스에 드라이버 전용 데이터를 연결합니다. 이후
usb_get_intfdata()로 콜백에서 꺼내 씁니다. - usb_device_id / USB_DEVICE()드라이버가 지원하는 디바이스 목록입니다.
MODULE_DEVICE_TABLE()은 모듈 로딩 시스템(modalias)이 자동 바인딩할 수 있도록 별도 섹션에 ID를 기록합니다. - module_usb_driver()
usb_register()/usb_deregister()를 래핑하는 편의 매크로로,module_init/module_exit보일러플레이트를 자동 생성합니다.
USB ID 매칭 매크로(Macro)
| 매크로 | 매칭 기준 |
|---|---|
USB_DEVICE(vendor, product) | Vendor + Product ID |
USB_DEVICE_VER(vendor, product, lo, hi) | + bcdDevice 범위 |
USB_DEVICE_INTERFACE_CLASS(vend, prod, cl) | + Interface Class |
USB_DEVICE_INTERFACE_PROTOCOL(vend, prod, pr) | + Interface Protocol |
USB_INTERFACE_INFO(cl, sc, pr) | Interface Class/SubClass/Protocol |
USB_DEVICE_INFO(cl, sc, pr) | Device Class/SubClass/Protocol |
드라이버 라이프사이클
드라이버 매칭/바인딩 상세
USB Core의 usb_device_match()는 등록된 usb_driver의 id_table과 디바이스의 디스크립터 정보를 비교하여 적합한 드라이버를 찾습니다. 매칭이 성공하면 usb_probe_interface()를 거쳐 드라이버의 probe()가 호출됩니다.
매칭 플래그(match_flags)는 usb_device_id 구조체의 어떤 필드를 비교할지 결정합니다. USB_DEVICE() 매크로는 Vendor/Product ID만 비교하고, USB_INTERFACE_INFO()는 클래스/서브클래스/프로토콜을 비교합니다. 여러 플래그를 OR 조합하면 더 세밀한 매칭이 가능합니다.
분리(Disconnect) 처리 순서가 중요합니다. 드라이버의 disconnect() 콜백(Callback)이 호출되기 전에 usb_unbind_interface()가 인터페이스를 비활성화하며, disconnect() 내에서 반드시 진행 중인 URB를 usb_kill_urb()로 취소하고, 할당된 리소스를 해제해야 합니다. pre_reset()/post_reset() 콜백은 usb_reset_device() 호출 시 디바이스 리셋 전후에 드라이버 상태를 저장/복원할 기회를 제공합니다.
/* disconnect 및 pre_reset/post_reset 콜백 패턴 */
static void my_usb_disconnect(struct usb_interface *intf)
{
struct my_usb_dev *dev = usb_get_intfdata(intf);
/* 1. 인터페이스 데이터 클리어 (재진입 방지) */
usb_set_intfdata(intf, NULL);
/* 2. 진행 중인 URB 모두 취소 (동기적으로 대기) */
usb_kill_urb(dev->int_urb);
usb_kill_urb(dev->bulk_urb);
/* 3. 유저스페이스 인터페이스 해제 */
usb_deregister_dev(intf, &my_class);
/* 4. 리소스 해제 */
usb_free_urb(dev->int_urb);
usb_put_dev(dev->udev);
kfree(dev);
}
/* pre_reset: 디바이스 리셋 직전 호출 */
static int my_pre_reset(struct usb_interface *intf)
{
struct my_usb_dev *dev = usb_get_intfdata(intf);
/* URB 중지, 상태 저장 */
usb_kill_urb(dev->bulk_urb);
return 0;
}
/* post_reset: 디바이스 리셋 완료 후 호출 */
static int my_post_reset(struct usb_interface *intf)
{
struct my_usb_dev *dev = usb_get_intfdata(intf);
/* 엔드포인트 재확인, URB 재제출 */
usb_submit_urb(dev->bulk_urb, GFP_NOIO);
return 0;
}
URB (USB Request Block)
URB (USB Request Block)은 USB 드라이버가 데이터를 전송하기 위해 USB Core에 제출하는 요청 단위입니다. 네트워크의 sk_buff, 블록 I/O의 bio에 해당하는 USB 서브시스템의 핵심 자료구조입니다.
struct urb 주요 필드
/* include/linux/usb.h */
struct urb {
struct list_head urb_list; /* HCD 내부 리스트 */
struct usb_device *dev; /* 대상 디바이스 */
unsigned int pipe; /* 파이프 (endpoint + 방향 + 타입) */
int status; /* 완료 상태 (0=성공) */
unsigned int transfer_flags; /* URB 플래그 */
void *transfer_buffer; /* 데이터 버퍼 (kmalloc) */
dma_addr_t transfer_dma; /* DMA 주소 (선택) */
u32 transfer_buffer_length; /* 버퍼 크기 */
u32 actual_length; /* 실제 전송된 바이트 수 */
unsigned char *setup_packet; /* Control 전송용 SETUP 패킷 */
int interval; /* Interrupt/Isoc 폴링 간격 */
int number_of_packets; /* Isoc 패킷 수 */
struct usb_iso_packet_descriptor iso_frame_desc[]; /* Isoc 프레임 */
usb_complete_t complete; /* 완료 콜백 함수 */
void *context; /* 콜백 컨텍스트 */
/* ... */
};
코드 설명
include/linux/usb.h에 정의된 struct urb은 USB 데이터 전송의 핵심 요청 단위입니다. 네트워크의 sk_buff, 블록 I/O의 bio에 대응하는 USB 서브시스템의 I/O 기술자입니다.
- pipe엔드포인트 번호, 방향(IN/OUT), 전송 타입(Control/Bulk/Interrupt/Isochronous)을 하나의
unsigned int로 인코딩합니다.usb_sndbulkpipe()등의 매크로로 생성합니다. - status완료 콜백에서 전송 결과를 나타냅니다. 0이면 성공,
-ENOENT/-ECONNRESET은 드라이버에 의한 정상 취소,-ESHUTDOWN은 디바이스 분리를 의미합니다. - transfer_buffer / transfer_dmaCPU 주소와 DMA 주소를 각각 보관합니다.
URB_NO_TRANSFER_DMA_MAP플래그 설정 시 HCD가 DMA 매핑을 건너뜁니다. - actual_length실제 전송된 바이트 수입니다. Short 패킷 수신 시
transfer_buffer_length보다 작을 수 있습니다. - setup_packetControl 전송 전용 8바이트 SETUP 데이터를 가리킵니다. Bulk/Interrupt/Isochronous 전송에서는 NULL입니다.
- intervalInterrupt/Isochronous 전송의 폴링 간격입니다. Full Speed는 ms, High Speed 이상은 마이크로프레임(125us) 단위입니다.
- complete / context전송 완료 시 인터럽트 컨텍스트에서 호출되는 콜백과 그 인자입니다.
drivers/usb/core/urb.c의usb_giveback_urb()가 이를 호출합니다.
URB 라이프사이클
| 단계 | 함수 | 설명 |
|---|---|---|
| 1 | usb_alloc_urb() | URB 할당 |
| 2 | usb_fill_*_urb() | 전송 타입별 필드 초기화 |
| 3 | usb_submit_urb() | USB Core/HCD 큐에 제출 |
| 4 | HCD 처리 | 하드웨어 전송 수행 |
| 5 | complete() | 완료 콜백 실행 (인터럽트 컨텍스트) |
| 6 | usb_free_urb() | 참조 카운트(Reference Count) 기반 해제 |
URB 초기화 헬퍼
/* Bulk 전송 URB 초기화 */
static void usb_fill_bulk_urb(
struct urb *urb,
struct usb_device *dev,
unsigned int pipe, /* usb_sndbulkpipe() / usb_rcvbulkpipe() */
void *transfer_buffer,
int buffer_length,
usb_complete_t complete_fn,
void *context);
/* Interrupt 전송 URB 초기화 */
static void usb_fill_int_urb(
struct urb *urb,
struct usb_device *dev,
unsigned int pipe,
void *transfer_buffer,
int buffer_length,
usb_complete_t complete_fn,
void *context,
int interval); /* 폴링 간격 (ms 또는 microframes) */
/* Control 전송 URB 초기화 */
static void usb_fill_control_urb(
struct urb *urb,
struct usb_device *dev,
unsigned int pipe,
unsigned char *setup_packet, /* 8바이트 SETUP 데이터 */
void *transfer_buffer,
int buffer_length,
usb_complete_t complete_fn,
void *context);
코드 설명
include/linux/usb.h에 정의된 URB 초기화 헬퍼 함수들입니다. 전송 타입(Bulk/Interrupt/Control)별로 struct urb의 필드를 올바르게 채워주는 인라인 함수이며, Isochronous 전송은 별도로 수동 초기화해야 합니다.
- usb_fill_bulk_urb()Bulk 전송용입니다.
pipe에usb_sndbulkpipe()(송신) 또는usb_rcvbulkpipe()(수신)로 생성한 파이프를 전달합니다. 대용량 데이터(Mass Storage, 네트워크)에 사용됩니다. - usb_fill_int_urb()Interrupt 전송용입니다. 마지막 인자
interval이 폴링 주기를 지정합니다. HID(키보드/마우스) 등 소량 주기적 데이터에 사용됩니다. - usb_fill_control_urb()Control 전송용입니다.
setup_packet인자로 8바이트 SETUP 데이터(bmRequestType, bRequest, wValue, wIndex, wLength)를 전달합니다. 디바이스 설정 및 벤더 요청에 사용됩니다. - complete_fn / context세 함수 모두 완료 콜백과 콜백 컨텍스트를 받습니다. 콜백은 인터럽트 컨텍스트에서 실행되므로 sleep이 불가능하고,
GFP_ATOMIC만 사용할 수 있습니다.
비동기 Bulk 전송 예제
static void my_bulk_complete(struct urb *urb)
{
struct my_usb_dev *dev = urb->context;
if (urb->status) {
if (urb->status == -ENOENT ||
urb->status == -ECONNRESET ||
urb->status == -ESHUTDOWN) {
/* URB 취소됨 — 정상적인 종료 */
return;
}
dev_err(&dev->intf->dev,
"bulk read failed: %d\\n", urb->status);
return;
}
/* 수신 데이터 처리 */
dev_info(&dev->intf->dev,
"received %d bytes\\n", urb->actual_length);
/* 연속 수신을 위해 URB 재제출 */
usb_submit_urb(urb, GFP_ATOMIC);
}
static int my_start_bulk_read(struct my_usb_dev *dev)
{
struct urb *urb;
int ret;
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb)
return -ENOMEM;
usb_fill_bulk_urb(urb, dev->udev,
usb_rcvbulkpipe(dev->udev, dev->bulk_in_ep),
dev->bulk_in_buf,
dev->bulk_in_size,
my_bulk_complete,
dev);
ret = usb_submit_urb(urb, GFP_KERNEL);
if (ret) {
dev_err(&dev->intf->dev,
"failed to submit URB: %d\\n", ret);
usb_free_urb(urb);
}
return ret;
}
코드 설명
비동기 Bulk 수신의 전형적인 패턴입니다. drivers/usb/core/urb.c의 usb_submit_urb()로 URB를 HCD에 제출하고, 완료 콜백에서 데이터를 처리한 뒤 재제출하는 연속 수신 루프를 구성합니다.
- my_bulk_complete()URB 완료 콜백입니다. 인터럽트 컨텍스트에서 호출되므로 sleep 불가이며,
urb->context로 드라이버 데이터에 접근합니다. - -ENOENT / -ECONNRESET / -ESHUTDOWN세 에러 코드는 각각
usb_kill_urb(),usb_unlink_urb(), 디바이스 분리에 의한 정상적인 URB 취소를 나타냅니다. 이 경우 재제출 없이 즉시 반환해야 합니다. - usb_submit_urb(urb, GFP_ATOMIC)콜백 내에서 재제출 시 반드시
GFP_ATOMIC을 사용합니다. 이 패턴으로 연속 수신을 구현하며, 중단 시 재제출을 하지 않으면 자연스럽게 멈춥니다. - usb_alloc_urb(0, GFP_KERNEL)첫 번째 인자 0은 Isochronous 패킷 수입니다. Bulk 전송은 0을 전달합니다. 프로세스 컨텍스트이므로
GFP_KERNEL을 사용합니다. - usb_fill_bulk_urb()
usb_rcvbulkpipe()로 수신 방향 파이프를 생성하고, 사전 할당된 버퍼와 완료 콜백을 설정합니다. - usb_free_urb()제출 실패 시 즉시 해제합니다. 제출 성공 후에는 완료 콜백이 호출된 뒤에만 해제해야 합니다.
동기 전송 래퍼
간단한 전송에는 URB를 직접 관리하지 않고 동기 래퍼를 사용할 수 있습니다:
/* 동기 Bulk 전송 */
int actual_len;
int ret = usb_bulk_msg(dev->udev,
usb_rcvbulkpipe(dev->udev, dev->bulk_in_ep),
dev->bulk_in_buf,
dev->bulk_in_size,
&actual_len,
5000); /* 타임아웃 (ms) */
/* 동기 Control 전송 */
ret = usb_control_msg(dev->udev,
usb_sndctrlpipe(dev->udev, 0),
0x09, /* bRequest: SET_CONFIGURATION */
USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
1, /* wValue */
0, /* wIndex */
NULL, /* data */
0, /* size */
5000); /* 타임아웃 (ms) */
/* 동기 Interrupt 전송 */
ret = usb_interrupt_msg(dev->udev,
usb_rcvintpipe(dev->udev, dev->int_in_ep),
dev->int_buf, dev->int_buf_size,
&actual_len, 5000);
usb_bulk_msg(), usb_control_msg() 같은 동기 API는 내부에서 wait_for_completion()을 호출하므로 인터럽트 컨텍스트나 URB 완료 콜백 내에서 사용하면 안 됩니다. 이런 경우에는 비동기 URB를 사용해야 합니다.파이프(Pipe) 매크로
| 매크로 | 용도 |
|---|---|
usb_sndctrlpipe(dev, ep) | Control OUT 파이프 |
usb_rcvctrlpipe(dev, ep) | Control IN 파이프 |
usb_sndbulkpipe(dev, ep) | Bulk OUT 파이프 |
usb_rcvbulkpipe(dev, ep) | Bulk IN 파이프 |
usb_sndintpipe(dev, ep) | Interrupt OUT 파이프 |
usb_rcvintpipe(dev, ep) | Interrupt IN 파이프 |
usb_sndisocpipe(dev, ep) | Isochronous OUT 파이프 |
usb_rcvisocpipe(dev, ep) | Isochronous IN 파이프 |
URB 완료 콜백과 오류 처리
URB의 완료 콜백(complete)은 USB 드라이버의 핵심 비동기 처리 메커니즘입니다. 콜백은 인터럽트 컨텍스트(하드웨어 IRQ 또는 softirq)에서 호출되므로, 수면(sleep) 불가능한 환경에서 실행됩니다. URB 상태 코드를 정확히 처리하는 것이 안정적인 USB 드라이버의 핵심입니다.
URB 상태 코드
| 상태 코드 | 값 | 의미 | 드라이버 동작 |
|---|---|---|---|
0 | 0 | 성공 | 데이터 처리 후 URB 재제출 |
-ENOENT | -2 | usb_kill_urb()로 취소됨 | 정리 중 — 재제출 금지 |
-ECONNRESET | -104 | usb_unlink_urb()로 해제됨 | 정리 중 — 재제출 금지 |
-ESHUTDOWN | -108 | 디바이스/HCD 제거됨 | 모든 I/O 중단, 자원 해제 |
-EPIPE | -32 | Endpoint Stall (HALT) | usb_clear_halt() 호출 후 재시도 |
-EOVERFLOW | -75 | 수신 데이터가 버퍼(Buffer) 초과 | 버퍼 크기 확인, 오류 보고 |
-ETIMEDOUT | -110 | 전송 타임아웃 | 디바이스 리셋 검토 |
-EPROTO | -71 | 프로토콜 오류 (비트 스터핑 등) | 하드웨어 문제 — 재시도/리셋 |
-EILSEQ | -84 | CRC 오류 (데이터 무결성(Integrity) 실패) | 케이블/커넥터 확인, 재시도 |
-EREMOTEIO | -121 | Short packet (요청보다 적게 수신) | URB_SHORT_NOT_OK 플래그 시 오류 |
-ENODEV | -19 | 디바이스 분리됨 | 모든 I/O 중단, 자원 해제 |
/* URB 완료 콜백 견고한 구현 패턴 */
static void robust_urb_complete(struct urb *urb)
{
struct my_usb_dev *dev = urb->context;
int status = urb->status;
switch (status) {
case 0:
/* 성공 — 데이터 처리 */
process_data(dev, urb->transfer_buffer, urb->actual_length);
break;
case -ENOENT: /* usb_kill_urb() */
case -ECONNRESET: /* usb_unlink_urb() */
case -ESHUTDOWN: /* HCD/디바이스 제거 */
/* 정리 중 — 재제출하지 않음 */
return;
case -EPIPE:
/* Endpoint Stall — 워크큐에서 clear_halt 처리 */
schedule_work(&dev->stall_work);
return;
case -EOVERFLOW:
dev_err(&dev->intf->dev, "buffer overflow\\n");
return;
default:
/* 기타 오류 — 제한적 재시도 */
if (++dev->error_count < 3) {
dev_warn(&dev->intf->dev,
"URB error %d, retry %d\\n",
status, dev->error_count);
} else {
dev_err(&dev->intf->dev,
"URB error %d, giving up\\n", status);
return;
}
break;
}
/* URB 재제출 (인터럽트 컨텍스트이므로 GFP_ATOMIC) */
status = usb_submit_urb(urb, GFP_ATOMIC);
if (status)
dev_err(&dev->intf->dev,
"resubmit URB failed: %d\\n", status);
}
URB 취소와 동기화
| 함수 | 동작 | 컨텍스트 | 사용 시점 |
|---|---|---|---|
usb_kill_urb() | URB 취소 + 콜백 완료 대기 (동기) | 프로세스 (sleep OK) | disconnect(), remove() |
usb_unlink_urb() | URB 취소 요청 (비동기) | 어디서든 (atomic OK) | 콜백 내, 빠른 취소 |
usb_poison_urb() | URB 취소 + 향후 재제출 영구 차단 | 프로세스 (sleep OK) | 디바이스 분리 최종 단계 |
usb_kill_anchored_urbs() | 앵커된 모든 URB 일괄 취소 | 프로세스 (sleep OK) | 대량 URB 정리 |
/* USB Anchor를 이용한 URB 일괄 관리 */
struct usb_anchor my_anchor;
/* 초기화 */
init_usb_anchor(&my_anchor);
/* URB 제출 시 앵커에 등록 */
usb_anchor_urb(urb, &my_anchor);
ret = usb_submit_urb(urb, GFP_KERNEL);
if (ret)
usb_unanchor_urb(urb);
/* disconnect 시 모든 URB 일괄 취소 */
usb_kill_anchored_urbs(&my_anchor);
/* 또는 타임아웃 기반 대기 */
if (!usb_wait_anchor_empty_timeout(&my_anchor, 1000))
usb_kill_anchored_urbs(&my_anchor);
Host Controller Driver (HCD)
HCD(Host Controller Driver)는 USB 호스트 컨트롤러 하드웨어를 직접 제어하는 최하위 계층 드라이버입니다. USB Core가 제출한 URB를 하드웨어 트랜잭션으로 변환하여 실행합니다.
호스트 컨트롤러 종류
| HCD | 규격 | USB 속도 | 특징 | 커널 드라이버 |
|---|---|---|---|---|
| UHCI | Intel | USB 1.x (LS/FS) | 소프트웨어 스케줄링, CPU 부하 높음 | uhci-hcd |
| OHCI | Compaq/Microsoft/NSC | USB 1.x (LS/FS) | 하드웨어 스케줄링, 임베디드 주력 | ohci-hcd |
| EHCI | Intel | USB 2.0 (HS) | Companion Controller(UHCI/OHCI)와 쌍 | ehci-hcd |
| xHCI | Intel | USB 1.x ~ 3.x | 모든 속도 통합, 스트림 지원 | xhci-hcd |
struct hc_driver
/* include/linux/usb/hcd.h */
struct hc_driver {
const char *description;
const char *product_desc;
size_t hcd_priv_size; /* HCD private 데이터 크기 */
/* HCD 생명주기 */
int (*reset)(struct usb_hcd *hcd);
int (*start)(struct usb_hcd *hcd);
void (*stop)(struct usb_hcd *hcd);
void (*shutdown)(struct usb_hcd *hcd);
/* URB 관리 */
int (*urb_enqueue)(struct usb_hcd *hcd,
struct urb *urb, gfp_t mem_flags);
int (*urb_dequeue)(struct usb_hcd *hcd,
struct urb *urb, int status);
/* Root Hub 조작 */
int (*hub_status_data)(struct usb_hcd *hcd,
char *buf);
int (*hub_control)(struct usb_hcd *hcd,
u16 typeReq, u16 wValue,
u16 wIndex, char *buf,
u16 wLength);
/* Endpoint 관리 */
void (*endpoint_reset)(struct usb_hcd *hcd,
struct usb_host_endpoint *ep);
void (*endpoint_disable)(struct usb_hcd *hcd,
struct usb_host_endpoint *ep);
/* ... */
};
코드 설명
include/linux/usb/hcd.h에 정의된 struct hc_driver는 호스트 컨트롤러 드라이버(HCD)가 USB Core에 제공하는 오퍼레이션 테이블입니다. xHCI(drivers/usb/host/xhci.c), EHCI, OHCI 등 각 HCD가 이 콜백들을 구현합니다.
- reset() / start() / stop()HCD 생명주기 콜백입니다.
reset()은 하드웨어 초기화,start()는 Root Hub 활성화,stop()은 컨트롤러 중지를 수행합니다.drivers/usb/core/hcd.c의usb_add_hcd()가 이 순서로 호출합니다. - urb_enqueue()USB Core가
usb_submit_urb()호출 시 최종적으로 도달하는 콜백입니다. HCD는 URB를 하드웨어 전송 큐(xHCI의 경우 Transfer Ring)에 삽입합니다. - urb_dequeue()
usb_kill_urb()/usb_unlink_urb()호출 시 진행 중인 URB를 하드웨어 큐에서 제거합니다. - hub_status_data() / hub_control()가상 Root Hub 에뮬레이션 콜백입니다. 물리적 Root Hub가 별도 USB 디바이스로 존재하지 않으므로, HCD가 포트 상태 변경 감지와 허브 제어 요청을 소프트웨어로 처리합니다.
- endpoint_reset() / endpoint_disable()엔드포인트 리셋(STALL 클리어) 및 비활성화 시 HCD 하드웨어 상태를 정리합니다. xHCI의 경우 Endpoint Context를 갱신합니다.
- hcd_priv_sizeHCD별 전용 데이터 크기입니다.
usb_create_hcd()가struct usb_hcd뒤에 이 크기만큼 추가 메모리를 할당합니다.
xHCI 내부 구조
xHCI는 링 버퍼(Ring) 기반의 명령/전송 메커니즘을 사용합니다:
| 링 타입 | 방향 | 용도 |
|---|---|---|
| Command Ring | SW → HW | Slot 활성화/비활성화, Endpoint 구성, 주소 할당 |
| Event Ring | HW → SW | 명령 완료, 전송 완료, 포트 상태 변경 알림 |
| Transfer Ring | SW → HW | 데이터 전송 요청 (Endpoint별 1개) |
xHCI Transfer Ring / Event Ring 상세
xHCI의 Transfer Ring과 Event Ring은 호스트 메모리(DMA 가능 영역)에 TRB(Transfer Request Block) 배열로 구성됩니다. 각 TRB는 16바이트 크기이며, Enqueue Pointer(소프트웨어 쓰기 위치)와 Dequeue Pointer(하드웨어 읽기 위치)로 링을 관리합니다.
xHCI 레지스터(Register) 맵
xHCI 호스트 컨트롤러는 PCI BAR0에 매핑(Mapping)된 MMIO 레지스터 공간을 4개 영역으로 구분합니다. 각 영역은 오프셋(Offset) 기반으로 접근하며, Capability 레지스터에서 각 영역의 시작 오프셋을 읽습니다:
| 영역 | 오프셋 결정 | 주요 레지스터 | 용도 |
|---|---|---|---|
| Capability | BAR0 + 0x00 | CAPLENGTH, HCSPARAMS1/2/3, HCCPARAMS1/2 | 컨트롤러 성능/한계 정보 (읽기 전용(Read-Only)) |
| Operational | BAR0 + CAPLENGTH | USBCMD, USBSTS, DNCTRL, CRCR, DCBAAP, CONFIG | 컨트롤러 제어, DCBAA/Command Ring 설정 |
| Runtime | BAR0 + RTSOFF | IMAN, IMOD, ERSTSZ, ERSTBA, ERDP (Interrupter별) | 인터럽트 관리, Event Ring 설정 |
| Doorbell | BAR0 + DBOFF | DB[0] (Command), DB[1..MAX_SLOTS] (EP별) | xHC에 새 작업 알림 |
/* drivers/usb/host/xhci.h — xHCI 레지스터 접근 (간략) */
/* Capability 레지스터에서 오프셋 추출 */
u32 cap_length = readl(&xhci->cap_regs->hc_capbase) & 0xFF;
u32 rts_offset = readl(&xhci->cap_regs->run_regs_off) & ~0x1F;
u32 db_offset = readl(&xhci->cap_regs->db_off) & ~0x3;
/* Operational 레지스터 */
xhci->op_regs = (void *)xhci->cap_regs + cap_length;
writel(CMD_RUN, &xhci->op_regs->command); /* 컨트롤러 시작 */
/* Doorbell 레지스터로 xHC에 새 TRB 알림 */
xhci->dba = (void *)xhci->cap_regs + db_offset;
writel(ep_index, &xhci->dba->doorbell[slot_id]); /* EP별 doorbell */
/* xHCI 대역폭 스케줄링 개요 */
/*
* xHCI는 USB 2.0(Microframe 기반)과 USB 3.x(Service Interval 기반)
* 의 대역폭을 각각 독립적으로 스케줄링합니다.
*
* Periodic (Interrupt/Isochronous) 엔드포인트 추가 시:
* 1. Add Endpoint Command → xHC가 대역폭 계산
* 2. 부족하면 Bandwidth Error (Completion Code 34) 반환
* 3. 성공하면 Configure Endpoint Command로 확정
*
* USB 3.x Streams (Bulk 전용):
* - 하나의 Bulk EP에 여러 Stream ID → 각각 독립 Transfer Ring
* - UAS(USB Attached SCSI)에서 명령 큐잉에 활용
* - xhci_alloc_streams() / xhci_free_streams() 로 관리
*/
HCD 내부 비교: xHCI vs EHCI vs OHCI
각 HCD는 URB를 하드웨어 트랜잭션으로 변환하는 방식이 근본적으로 다릅니다. 스케줄링 메커니즘, 메모리 구조, DMA 패턴을 비교하면 USB 성능 차이를 이해할 수 있습니다.
| 특성 | OHCI | EHCI | xHCI |
|---|---|---|---|
| 스케줄링 | 하드웨어 (ED/TD 리스트) | 하드웨어 (QH/qTD) | 하드웨어 (Ring Buffer + TRB) |
| 데이터 구조 | Endpoint Descriptor(ED) + Transfer Descriptor(TD) | Queue Head(QH) + Queue Transfer Descriptor(qTD) | Device Context + Transfer Ring(TRB) |
| USB 속도 | Low/Full Speed만 | High Speed (LS/FS는 Companion 위임) | 전 속도 통합 (LS/FS/HS/SS/SS+) |
| Companion 필요 | 아니오 | 예 (OHCI/UHCI가 LS/FS 처리) | 아니오 (통합) |
| 스트림 지원 | 없음 | 없음 | 있음 (Bulk EP당 최대 65535 스트림) |
| MSI-X 지원 | 없음 | 제한적 | 있음 (Interrupter별 이벤트 링) |
| 64-bit DMA | 아니오 | 제한적 (EHCI 64-bit 확장) | 네이티브 64-bit |
| Endpoint 대역폭 | 하드웨어 자동 | 주기적 스케줄링 테이블 | EP Context의 Max ESIT Payload |
/* HCD별 urb_enqueue 내부 흐름 비교 */
/* OHCI: ohci_urb_enqueue() */
/*
* 1. ED(Endpoint Descriptor) 찾기/생성
* 2. TD(Transfer Descriptor) 체인 할당 (데이터 크기에 따라 N개)
* 3. TD를 ED의 큐에 연결
* 4. ED를 적절한 스케줄 리스트에 삽입
* - Control/Bulk: 비동기 리스트
* - Interrupt: HCCA의 주기적 프레임 리스트
* - Isochronous: 프레임 리스트 직접 삽입
* 5. HW가 프레임마다 리스트를 순회하며 처리
*/
/* EHCI: ehci_urb_enqueue() */
/*
* 1. QH(Queue Head) 찾기/생성
* 2. qTD 체인 할당 (최대 5개 버퍼 포인터/qTD)
* 3. qTD를 QH의 오버레이에 연결
* 4. Async 스케줄: QH를 순환 리스트에 삽입
* Periodic 스케줄: 프레임 리스트 슬롯에 연결
* 5. LS/FS 디바이스: Split Transaction (CSPLIT/SSPLIT)
*/
/* xHCI: xhci_urb_enqueue() */
/*
* 1. Slot/EP에 대응하는 Transfer Ring 찾기
* 2. TRB 체인 구성 (Normal/Setup/Data/Status TRB)
* 3. TRB를 Transfer Ring의 Enqueue Pointer에 기록
* 4. Doorbell Register 쓰기 → xHC에 새 작업 알림
* 5. 완료 시 Event Ring에 Transfer Event TRB 생성
* 6. ISR에서 Event Ring 폴링 → URB 완료 콜백 호출
*/
USB Gadget Framework
USB Gadget Framework는 Linux 시스템을 USB 디바이스 (Function)로 동작하게 하는 프레임워크입니다. 임베디드 시스템, SBC(Single Board Computer), 스마트폰 등에서 호스트에 연결될 때 다양한 USB 기능(Mass Storage, Ethernet, Serial 등)을 제공합니다.
Gadget 아키텍처
| 계층 | 역할 | 예시 |
|---|---|---|
| Gadget Function Driver | USB 클래스 기능 구현 | f_mass_storage, f_ecm, f_acm, f_hid |
| Composite Framework | 여러 Function을 하나의 Configuration으로 합성 | usb_composite_driver |
| UDC (USB Device Controller) | 디바이스 컨트롤러 하드웨어 제어 | dwc3, musb, cdns3, fotg210 |
ConfigFS Gadget
ConfigFS를 통해 유저스페이스에서 USB Gadget을 동적으로 구성할 수 있습니다. 커널 모듈(Kernel Module) 재컴파일 없이 USB 디바이스 기능을 조합합니다:
# ConfigFS USB Gadget 생성 예제: CDC ECM (Ethernet) + ACM (Serial)
# 1. Gadget 디렉토리 생성
cd /sys/kernel/config/usb_gadget
mkdir my_gadget && cd my_gadget
# 2. 디바이스 디스크립터 설정
echo 0x1d6b > idVendor # Linux Foundation
echo 0x0104 > idProduct # Multifunction Composite Gadget
echo 0x0100 > bcdDevice
echo 0x0200 > bcdUSB
# 3. 영문 문자열 설정
mkdir strings/0x409
echo "0123456789" > strings/0x409/serialnumber
echo "Linux" > strings/0x409/manufacturer
echo "My Gadget" > strings/0x409/product
# 4. Configuration 생성
mkdir configs/c.1
mkdir configs/c.1/strings/0x409
echo "ECM + ACM" > configs/c.1/strings/0x409/configuration
echo 250 > configs/c.1/MaxPower # 500mA (USB 2.0 단위: 2mA)
# 5. Function 인스턴스 생성
mkdir functions/ecm.usb0 # CDC ECM (Ethernet)
mkdir functions/acm.GS0 # CDC ACM (Serial)
# 6. Function을 Configuration에 링크
ln -s functions/ecm.usb0 configs/c.1/
ln -s functions/acm.GS0 configs/c.1/
# 7. UDC에 바인딩 (활성화)
ls /sys/class/udc # 사용 가능한 UDC 확인
echo "fe980000.usb" > UDC # UDC 이름 기록 → 호스트에 연결됨
# 비활성화
echo "" > UDC
FunctionFS
FunctionFS는 USB Gadget Function의 로직을 유저스페이스에서 구현할 수 있게 하는 파일시스템(Filesystem)입니다. 커널 드라이버 없이 사용자 정의 USB 프로토콜을 구현할 수 있습니다:
/* 유저스페이스 FunctionFS 사용 예제 (간략) */
#include <linux/usb/functionfs.h>
/* 1. FunctionFS 마운트 */
/* mount -t functionfs my_func /dev/ffs-my_func */
/* 2. ep0에 디스크립터 기록 */
int ep0_fd = open("/dev/ffs-my_func/ep0", O_RDWR);
write(ep0_fd, &descriptors, sizeof(descriptors));
write(ep0_fd, &strings, sizeof(strings));
/* 3. Endpoint 파일 열기 */
int ep1_fd = open("/dev/ffs-my_func/ep1", O_RDWR); /* Bulk IN */
int ep2_fd = open("/dev/ffs-my_func/ep2", O_RDWR); /* Bulk OUT */
/* 4. 데이터 송수신 */
read(ep2_fd, buf, sizeof(buf)); /* Bulk OUT에서 수신 */
write(ep1_fd, buf, len); /* Bulk IN으로 송신 */
Composite Gadget 실전 구현
Composite Framework는 여러 USB Function을 하나의 USB 디바이스로 합성하는 프레임워크입니다. 예를 들어 하나의 임베디드 보드가 동시에 Ethernet(ECM), Serial(ACM), Mass Storage 기능을 제공할 수 있습니다. 커널 모듈 방식과 ConfigFS 방식 두 가지로 구현할 수 있습니다.
커널 모듈 방식 Composite Gadget
/* 커널 모듈 기반 Composite Gadget 골격 */
#include <linux/module.h>
#include <linux/usb/composite.h>
/* 디바이스 디스크립터 */
static struct usb_device_descriptor device_desc = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = cpu_to_le16(0x0200),
.bDeviceClass = USB_CLASS_MISC, /* IAD 사용 시 */
.bDeviceSubClass = 0x02, /* Common Class */
.bDeviceProtocol = 0x01, /* IAD */
.idVendor = cpu_to_le16(0x1d6b),
.idProduct = cpu_to_le16(0x0104),
};
/* Configuration 바인드 — Function 인스턴스 추가 */
static int my_config_bind(struct usb_configuration *c)
{
struct usb_function *f_ecm, *f_acm;
f_ecm = usb_get_function(fi_ecm); /* ECM 인스턴스 획득 */
usb_add_function(c, f_ecm); /* Configuration에 추가 */
f_acm = usb_get_function(fi_acm); /* ACM 인스턴스 획득 */
usb_add_function(c, f_acm);
return 0;
}
static struct usb_configuration my_config = {
.label = "My Composite Config",
.bConfigurationValue = 1,
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
.MaxPower = 250, /* 500mA (USB 2.0 단위: 2mA) */
};
/* Composite 드라이버 바인드 */
static int my_composite_bind(struct usb_composite_dev *cdev)
{
/* Function Instance 할당 */
fi_ecm = usb_get_function_instance("ecm");
fi_acm = usb_get_function_instance("acm");
/* Configuration 추가 */
usb_add_config(cdev, &my_config, my_config_bind);
return 0;
}
static struct usb_composite_driver my_composite_driver = {
.name = "my_gadget",
.dev = &device_desc,
.strings = my_strings,
.max_speed = USB_SPEED_SUPER,
.bind = my_composite_bind,
.unbind = my_composite_unbind,
};
module_usb_composite_driver(my_composite_driver);
코드 설명
USB Gadget/UDC 프레임워크(drivers/usb/gadget/composite.c)를 사용하여 임베디드 디바이스를 USB 주변기기로 동작시키는 Composite Gadget 골격입니다. 호스트측 드라이버와 반대 방향으로, 디바이스측 USB Function을 정의합니다.
- usb_device_descriptorGadget이 호스트에 보고할 디바이스 디스크립터입니다.
USB_CLASS_MISC+ SubClass 0x02 + Protocol 0x01은 IAD(Interface Association Descriptor) 사용을 나타내며, 복합 디바이스의 표준 조합입니다. - usb_get_function_instance()이름으로 등록된 Gadget Function(예: "ecm", "acm")의 인스턴스를 할당합니다. 커널에 내장된
f_ecm,f_acm등의 모듈이 이 이름으로 등록되어 있습니다. - usb_add_function()Configuration에 Function을 추가합니다. 하나의 Configuration에 여러 Function(ECM + ACM 등)을 추가하면 복합 USB 디바이스가 됩니다.
- usb_configuration.MaxPowerUSB 2.0 단위로 2mA이므로, 250은 500mA를 의미합니다. 호스트의 전원 공급 능력에 맞춰 설정해야 합니다.
- usb_composite_driverComposite 프레임워크의 최상위 드라이버 구조체입니다.
max_speed로 지원 가능한 최대 속도를 선언하고,bind/unbind에서 Function Instance와 Configuration을 관리합니다. - module_usb_composite_driver()
usb_composite_probe()/usb_composite_unregister()를 래핑하는 편의 매크로로, UDC(USB Device Controller)에 Gadget 드라이버를 등록/해제합니다.
주요 내장 Gadget Function
| Function 이름 | ConfigFS 이름 | 용도 | 호스트측 드라이버 |
|---|---|---|---|
f_ecm | ecm.<name> | CDC ECM Ethernet | cdc_ether |
f_ncm | ncm.<name> | CDC NCM 고성능 Ethernet | cdc_ncm |
f_rndis | rndis.<name> | RNDIS Ethernet (Windows 호환) | rndis_host |
f_acm | acm.<name> | CDC ACM 가상 시리얼 | cdc-acm |
f_mass_storage | mass_storage.<name> | USB Mass Storage | usb-storage |
f_hid | hid.<name> | HID (키보드/마우스 에뮬레이션) | usbhid |
f_uvc | uvc.<name> | UVC 웹캠 에뮬레이션 | uvcvideo |
f_midi | midi.<name> | USB MIDI | snd-usb-audio |
f_printer | printer.<name> | USB 프린터 | usblp |
f_fs (FunctionFS) | ffs.<name> | 유저스페이스 구현 Function | 벤더 드라이버/libusb |
UDC (USB Device Controller) 드라이버
| UDC 드라이버 | 하드웨어 | USB 속도 | 주요 사용처 |
|---|---|---|---|
dwc3 | DesignWare USB3 DRD | SS/HS/FS | ARM SoC (Qualcomm, Samsung, TI 등) |
dwc2 | DesignWare USB2 OTG | HS/FS | Raspberry Pi, STM32, Allwinner |
musb | Mentor USB OTG | HS/FS | TI OMAP/AM3x, Allwinner |
cdns3 | Cadence USB3 DRD | SS/HS/FS | TI J7, Intel Elkhart Lake |
chipidea | ChipIdea USB2 OTG | HS/FS | NXP i.MX, Qualcomm |
fotg210 | Faraday FOTG210 | HS/FS | Gemini, Cortina Access |
dummy_hcd | 가상 HCD/UDC | SS/HS/FS | 개발/테스트용 |
USB 클래스 드라이버
USB 클래스 드라이버는 USB-IF가 정의한 표준 디바이스 클래스를 구현합니다. VID/PID에 무관하게 클래스 코드만으로 드라이버가 매칭됩니다.
HID (Human Interface Device)
| 속성 | 값 |
|---|---|
| Interface Class | 0x03 |
| 전송 타입 | Interrupt (IN/OUT) |
| 커널 드라이버 | usbhid (drivers/hid/usbhid/) |
| 디바이스 예시 | 키보드, 마우스, 게임패드, 터치스크린 |
/* HID Report Descriptor 파싱 흐름 */
/* drivers/hid/usbhid/hid-core.c */
usbhid_probe()
→ hid_parse() /* HID Report Descriptor 파싱 */
→ hid_hw_start() /* Input 디바이스 등록 */
→ usb_fill_int_urb() /* Interrupt IN URB 설정 */
→ usb_submit_urb() /* 입력 이벤트 수신 시작 */
Mass Storage
| 속성 | 값 |
|---|---|
| Interface Class | 0x08 |
| SubClass | 0x06 (SCSI transparent) |
| Protocol | 0x50 (Bulk-Only Transport, BOT) |
| 전송 타입 | Bulk (IN + OUT) |
| 커널 드라이버 | usb-storage, uas (UASP) |
uas 드라이버(drivers/usb/storage/uas.c)가 담당합니다.CDC (Communications Device Class)
| 서브클래스 | 용도 | 커널 드라이버 | 호스트측 인터페이스 |
|---|---|---|---|
| ACM (Abstract Control Model) | 가상 시리얼 포트 | cdc-acm | /dev/ttyACM* |
| ECM (Ethernet Control Model) | Ethernet 어댑터 | cdc_ether | eth* / usb* |
| NCM (Network Control Model) | 고성능 Ethernet (USB 3.0+) | cdc_ncm | eth* / usb* |
| EEM (Ethernet Emulation Model) | 가상 Ethernet (Control IF 없음) | cdc_eem | usb* |
| MBIM | 모바일 광대역 (LTE/5G) | cdc_mbim | wwan* |
Audio Class
| 속성 | 값 |
|---|---|
| Interface Class | 0x01 |
| 전송 타입 | Isochronous (오디오 스트림), Interrupt (MIDI) |
| 커널 드라이버 | snd-usb-audio (sound/usb/) |
| 디바이스 예시 | USB 헤드셋, DAC, 마이크, MIDI 컨트롤러 |
주요 USB 클래스 코드
| Class | 이름 | 예시 |
|---|---|---|
| 0x01 | Audio | USB 사운드 카드, 마이크 |
| 0x02 | CDC (Communications) | 모뎀, Ethernet, 시리얼 |
| 0x03 | HID | 키보드, 마우스, 게임패드 |
| 0x06 | Still Image | 디지털 카메라 (PTP) |
| 0x07 | Printer | USB 프린터 |
| 0x08 | Mass Storage | USB 메모리, 외장 하드 |
| 0x09 | Hub | USB 허브 |
| 0x0A | CDC-Data | CDC 데이터 인터페이스 |
| 0x0E | Video | 웹캠, 비디오 캡처 |
| 0xE0 | Wireless Controller | Bluetooth 어댑터 |
| 0xEF | Miscellaneous | IAD(Interface Association) |
| 0xFE | Application Specific | DFU (Device Firmware Upgrade) |
| 0xFF | Vendor Specific | 벤더 전용 프로토콜 |
HID Report Descriptor 파싱 흐름
HID(Human Interface Device) 드라이버는 디바이스가 제공하는 Report Descriptor를 파싱하여 입력/출력 필드를 자동으로 매핑합니다. Report Descriptor는 바이트 스트림으로 인코딩된 계층적 구조이며, Global/Local/Main 아이템으로 구성됩니다.
/* HID Report Descriptor 바이트 예제: USB 키보드 (간략) */
/*
* 0x05, 0x01, // Usage Page (Generic Desktop)
* 0x09, 0x06, // Usage (Keyboard)
* 0xA1, 0x01, // Collection (Application)
* 0x05, 0x07, // Usage Page (Key Codes)
* 0x19, 0xE0, // Usage Minimum (Left Control)
* 0x29, 0xE7, // Usage Maximum (Right GUI)
* 0x15, 0x00, // Logical Minimum (0)
* 0x25, 0x01, // Logical Maximum (1)
* 0x75, 0x01, // Report Size (1 bit)
* 0x95, 0x08, // Report Count (8 modifiers)
* 0x81, 0x02, // Input (Data, Variable, Absolute) → 8 modifier bits
* 0x95, 0x06, // Report Count (6 keys)
* 0x75, 0x08, // Report Size (8 bits each)
* 0x19, 0x00, // Usage Minimum (0)
* 0x29, 0xFF, // Usage Maximum (255)
* 0x81, 0x00, // Input (Data, Array) → 6 key codes
* 0xC0 // End Collection
*
* → 총 8바이트 Report: [Modifiers(1B)] [Reserved(1B)] [Key1..Key6(6B)]
* → usbhid-dump으로 실제 디바이스의 Report Descriptor 확인 가능
*/
UAS (USB Attached SCSI) 프로토콜
UAS는 USB 3.0 이상에서 Mass Storage의 BOT(Bulk-Only Transport)를 대체하는 고성능 프로토콜입니다. xHCI의 Streams 기능을 활용하여 하나의 Bulk Endpoint에 여러 독립 스트림을 할당하고, 명령 큐잉과 비순차 완료를 지원합니다:
| 특성 | BOT (Bulk-Only Transport) | UAS (USB Attached SCSI) |
|---|---|---|
| 명령 큐잉 | 불가 (한 번에 1개 명령) | 가능 (최대 32+ 큐 깊이) |
| 완료 순서 | 순차 (FIFO) | 비순차 (Out-of-Order) |
| Endpoint 수 | 2개 (Bulk IN + OUT) | 4개 (Command/Status/Data IN/OUT) |
| xHCI Streams | 미사용 | 활용 (Stream ID = Tag) |
| 오버헤드(Overhead) | CBW/CSW 31+13 바이트 | IU 헤더 8~16 바이트 |
| 성능 향상 | 기준 | ~20~40% IOPS 향상 (랜덤 I/O 기준) |
CDC 변형 비교: ACM/ECM/NCM/MBIM
CDC(Communications Device Class)는 다양한 하위 모델을 정의합니다. 각 변형은 다른 프레이밍과 집적(aggregation) 방식을 사용하여 네트워크 성능과 호환성에 큰 차이가 있습니다:
| CDC 변형 | SubClass | 데이터 프레이밍 | 패킷 집적 | 최대 성능 | 주요 용도 |
|---|---|---|---|---|---|
| ACM | 0x02 | 시리얼 스트림 | 없음 | ~10 Mbps | AT 모뎀, 디버그 콘솔 |
| ECM | 0x06 | Ethernet 프레임 (1:1) | 없음 | ~200 Mbps | USB 2.0 Ethernet 어댑터 |
| NCM | 0x0D | NTB (Network Transfer Block) | 다중 프레임 집적 | ~900 Mbps | USB 3.0+ 고성능 NIC |
| MBIM | 0x0E | NCM 기반 + 제어 채널 | 다중 프레임 집적 | ~1 Gbps | LTE/5G 모뎀 |
| EEM | 0x0C | EEM 패킷 캡슐화(Encapsulation) | 가능 | ~200 Mbps | Control IF 불필요 장치 |
/* NCM vs ECM 성능 차이의 핵심: NTB (Network Transfer Block) */
/*
* ECM: USB 전송 1회 = Ethernet 프레임 1개
* → USB 전송 오버헤드가 패킷마다 발생
*
* NCM: USB 전송 1회 = NTB 1개 = 다수의 Ethernet 프레임 포함
* NTB 구조:
* ┌─────────────┬──────────────┬──────────────┬────────┐
* │ NTH (12B) │ NDP (가변) │ Datagram 1 │ Dgram 2│
* │ dwSignature │ wNdpIndex │ (Eth Frame) │ ... │
* │ wBlockLen │ Pointer Tbl │ │ │
* └─────────────┴──────────────┴──────────────┴────────┘
* NTH: NTB Header, NDP: NTB Datagram Pointer
* 최대 NTB 크기: 16KB (USB 2.0) ~ 65535B (USB 3.x)
*
* 결과: NCM은 USB 전송 1회로 수십 개 패킷을 보내
* 프로토콜 오버헤드를 크게 줄입니다.
*/
USB Type-C와 Power Delivery
USB Type-C는 단순한 커넥터 규격을 넘어 전원 공급(PD), 대체 모드(Alt Mode), 역할 전환(DRP) 등 복잡한 기능을 제공합니다. Linux 커널은 Type-C 서브시스템을 통해 이를 관리합니다.
Type-C 서브시스템
| 구성 요소 | 역할 | 커널 경로 |
|---|---|---|
| typec core | Type-C 포트/파트너/케이블 모델링 | drivers/usb/typec/ |
| UCSI | USB Type-C Connector System Software Interface | drivers/usb/typec/ucsi/ |
| TCPM | Type-C Port Manager (PD 상태 머신) | drivers/usb/typec/tcpm/ |
| tcpci | Type-C Port Controller Interface 드라이버 | drivers/usb/typec/tcpm/tcpci.c |
| Alt Mode drivers | DisplayPort, TBT3 등 대체 모드 | drivers/usb/typec/altmodes/ |
PD (Power Delivery) 협상
USB PD는 Type-C CC(Configuration Channel) 라인을 통해 전원 협상을 수행합니다:
| PD 버전 | 최대 전력 | 전압 범위 |
|---|---|---|
| USB 2.0/3.0 기본 | 4.5W (5V/900mA) | 5V 고정 |
| USB Type-C 기본 | 15W (5V/3A) | 5V 고정 |
| PD 2.0/3.0 | 100W (20V/5A) | 5V, 9V, 15V, 20V |
| PD 3.1 (EPR) | 240W (48V/5A) | 최대 48V (Extended Power Range) |
# sysfs에서 Type-C 정보 확인
ls /sys/class/typec/
# 포트 정보
cat /sys/class/typec/port0/data_role # [host] device
cat /sys/class/typec/port0/power_role # [source] sink
cat /sys/class/typec/port0/preferred_role # sink / source / none
cat /sys/class/typec/port0/port_type # dual / source / sink
# 연결된 파트너 정보
cat /sys/class/typec/port0-partner/type # UFP / DFP
cat /sys/class/typec/port0-partner/usb_power_delivery/
ls /sys/class/typec/port0-partner/identity/ # VDM ID 정보
# 역할 전환 (DRP 포트)
echo "device" > /sys/class/typec/port0/data_role
echo "sink" > /sys/class/typec/port0/power_role
Type-C CC 라인 감지와 방향 결정
USB Type-C 커넥터는 CC1/CC2(Configuration Channel) 핀을 통해 연결 감지, 방향 결정, 역할 협상을 수행합니다. Source 측은 Rp(풀업), Sink 측은 Rd(풀다운) 저항을 연결하며, CC 라인의 전압 레벨로 연결 상태와 전류 공급 능력을 판단합니다.
PD 협상 상태 머신
USB PD(Power Delivery)의 전원 협상은 CC 라인을 통한 메시지 교환으로 이루어집니다. TCPM(Type-C Port Manager)이 PD 상태 머신을 구현하며, Source와 Sink 간 PDO(Power Data Object) 교환을 통해 최적의 전원 계약을 체결합니다.
UCSI 명령/알림 모델
UCSI(USB Type-C Connector System Software Interface)는 인텔이 표준화한 펌웨어(Firmware) 기반 Type-C 관리 인터페이스입니다. 운영체제(OPM)가 플랫폼 정책 관리자(PPM)에게 명령을 보내고, PPM이 CC 감지와 PD 협상을 자체 처리한 뒤 알림으로 결과를 보고합니다:
/* drivers/usb/typec/ucsi/ucsi.c — UCSI 명령 인터페이스 */
/* UCSI 주요 명령 (Command) */
enum ucsi_command {
UCSI_PPM_RESET = 0x01, /* PPM 초기화 */
UCSI_CANCEL = 0x02, /* 진행 중인 명령 취소 */
UCSI_CONNECTOR_RESET = 0x03, /* 특정 커넥터 리셋 */
UCSI_ACK_CC_CI = 0x04, /* 알림 확인 응답 */
UCSI_SET_NOTIFICATION_ENABLE = 0x05, /* 알림 활성화 */
UCSI_GET_CAPABILITY = 0x06, /* PPM 성능 조회 */
UCSI_GET_CONNECTOR_CAPABILITY = 0x07,
UCSI_SET_UOM = 0x08, /* USB Operation Mode 설정 */
UCSI_SET_PDR = 0x0B, /* Power Direction Role 설정 */
UCSI_GET_PDOS = 0x10, /* PDO 목록 조회 */
UCSI_GET_CONNECTOR_STATUS = 0x12, /* 커넥터 상태 조회 */
};
typec connector class API
커널의 typec 클래스는 Type-C 포트, 파트너, 케이블을 sysfs에 등록하고 유저스페이스에 노출합니다. TCPM이나 UCSI 드라이버가 이 API를 호출하여 상태를 업데이트합니다:
/* drivers/usb/typec/class.c — Connector Class API (간략) */
/* 1. 포트 등록 */
struct typec_port *port = typec_register_port(dev, &cap);
/* cap: typec_capability 구조체 — 역할, 전압, Alt Mode 등 */
/* 2. 파트너 연결 시 */
struct typec_partner *partner = typec_register_partner(port, &desc);
/* desc: 파트너 정보 — UFP/DFP, USB PD 버전, Identity */
/* 3. 상태 업데이트 */
typec_set_data_role(port, TYPEC_HOST); /* DFP */
typec_set_pwr_role(port, TYPEC_SOURCE); /* Source */
typec_set_orientation(port, TYPEC_ORIENTATION_NORMAL);
/* 4. PD 계약 등록 */
struct usb_power_delivery *pd;
pd = usb_power_delivery_register(dev, &pd_desc);
typec_port_set_usb_power_delivery(port, pd);
/* 5. 분리 시 */
typec_unregister_partner(partner);
typec_unregister_port(port);
SRC_EPR_KEEP_ALIVE 상태를 통해 EPR 세션을 유지합니다.TCPM/TCPC 상태 머신 상세
TCPM(Type-C Port Manager)은 Linux 커널의 USB Type-C PD 상태 머신 구현체입니다. drivers/usb/typec/tcpm/tcpm.c에 약 7000줄 규모로 구현되어 있으며, CC 감지, PD 협상, 역할 전환, VDM(Vendor Defined Message), Alt Mode 진입까지 Type-C의 모든 동적 동작을 관리합니다.
TCPM 주요 상태
| 상태 그룹 | 주요 상태 | 설명 |
|---|---|---|
| 연결 감지 | TOGGLING, SRC_UNATTACHED, SNK_UNATTACHED | CC 라인 모니터링, DRP 토글 |
| 연결 확립 | SRC_ATTACHED, SNK_ATTACHED, ACC_UNATTACHED | CC 라인 확정, VBUS 감지/공급 |
| PD 탐색 | SRC_STARTUP, SNK_STARTUP, SRC_DISCOVERY | PD 통신 개시, Source_Cap 교환 |
| PD 협상 | SRC_NEGOTIATE, SNK_NEGOTIATE, SNK_TRANSITION_SINK | PDO 선택, 전원 계약 체결 |
| 정상 동작 | SRC_READY, SNK_READY | 전원 계약 완료, VDM/Alt Mode 가능 |
| 역할 전환 | PR_SWAP_*, DR_SWAP_*, VCONN_SWAP_* | Power Role, Data Role, VCONN Swap |
| 오류/복구 | ERROR_RECOVERY, HARD_RESET_*, SOFT_RESET_* | PD 통신 오류 복구 |
/* drivers/usb/typec/tcpm/tcpm.c — TCPC 콜백 인터페이스 */
struct tcpc_dev {
/* CC 라인 제어 */
int (*set_cc)(struct tcpc_dev *dev, enum typec_cc_status cc);
int (*get_cc)(struct tcpc_dev *dev,
enum typec_cc_status *cc1, enum typec_cc_status *cc2);
/* VBUS 제어 */
int (*get_vbus)(struct tcpc_dev *dev);
int (*set_vbus)(struct tcpc_dev *dev, bool on, bool charge);
/* PD 메시지 송수신 */
int (*set_pd_rx)(struct tcpc_dev *dev, bool on);
int (*pd_transmit)(struct tcpc_dev *dev,
enum tcpm_transmit_type type,
const struct pd_message *msg,
unsigned int negotiated_rev);
/* 역할/극성 설정 */
int (*set_polarity)(struct tcpc_dev *dev,
enum typec_cc_polarity polarity);
int (*set_roles)(struct tcpc_dev *dev, bool attached,
enum typec_role role, enum typec_data_role data);
/* VCONN 제어 */
int (*set_vconn)(struct tcpc_dev *dev, bool on);
/* ... */
};
Thunderbolt / USB4
Thunderbolt은 Intel이 개발한 고속 인터커넥트 기술로, PCIe와 DisplayPort를 단일 케이블로 터널링합니다. USB4는 Thunderbolt 3 프로토콜을 USB-IF가 공개 표준으로 채택한 것이며, Linux 커널에서는 동일한 drivers/thunderbolt/ 드라이버가 Thunderbolt 1~4와 USB4를 모두 관리합니다.
세대별 비교
| 세대 | 연도 | 최대 대역폭 | 프로토콜 | 커넥터 | PCIe 터널링 | 데이지 체인 |
|---|---|---|---|---|---|---|
| Thunderbolt 1 | 2011 | 2×10 Gbps | PCIe 2.0 + DP | Mini DisplayPort | PCIe 2.0 ×4 | 최대 6대 |
| Thunderbolt 2 | 2013 | 20 Gbps (채널 결합) | PCIe 2.0 + DP 1.2 | Mini DisplayPort | PCIe 2.0 ×4 | 최대 6대 |
| Thunderbolt 3 | 2015 | 40 Gbps | PCIe 3.0 + DP 1.2/1.4 | USB Type-C | PCIe 3.0 ×4 | 최대 6대 |
| USB4 1.0 | 2019 | 40 Gbps (Gen 3×2) | PCIe + DP 터널링 | USB Type-C | 선택적 | 트리 토폴로지(Topology) |
| Thunderbolt 4 | 2020 | 40 Gbps | USB4 + Intel 인증 | USB Type-C | PCIe 3.0 ×4 (32 Gbps 필수) | 허브를 통한 트리 |
| USB4 v2.0 | 2022 | 80 Gbps (Gen 4) | PCIe + DP 2.1 터널링 | USB Type-C | 선택적 | 트리 토폴로지 |
| Thunderbolt 5 | 2024 | 80/120 Gbps (PAM-3) | USB4 v2.0 + 대역폭 공유 | USB Type-C | PCIe 4.0 ×4 (64 Gbps) | 트리 토폴로지 |
아키텍처: 터널링과 어댑터
Thunderbolt/USB4의 핵심 개념은 터널링입니다. 단일 물리 링크 위에 여러 프로토콜을 동시에 멀티플렉싱합니다:
라우터(Router)는 Thunderbolt/USB4 토폴로지의 핵심 구성 요소입니다. 각 장치(호스트, 허브, 도크)에 하나 이상의 라우터가 있으며, 라우터에는 여러 어댑터(Adapter)가 연결됩니다:
| 어댑터 유형 | 역할 | 커널 표현 |
|---|---|---|
| Lane Adapter | 물리 링크 관리, 레인 본딩(Bonding) | struct tb_port (type=1) |
| PCIe Down/Up Adapter | PCIe 터널의 송/수신 엔드포인트 | struct tb_port (type=PCIe) |
| DP IN/OUT Adapter | DisplayPort 터널 엔드포인트 | struct tb_port (type=DP) |
| USB3 Down/Up Adapter | USB 3.x 터널 엔드포인트 | struct tb_port (type=USB3) |
| Protocol Adapter (DMA) | 호스트 메모리 DMA, 네트워킹 | struct tb_port (type=DMA) |
토폴로지와 라우팅(Routing)
Linux 커널 Thunderbolt 드라이버
커널의 Thunderbolt 드라이버(drivers/thunderbolt/)는 Thunderbolt 1~4와 USB4를 통합 관리합니다:
| 파일 | 역할 |
|---|---|
tb.c | 코어 로직, 연결 관리자(Connection Manager) 구현 |
switch.c | 라우터(스위치) 초기화, 열거(enumeration) |
tunnel.c | PCIe/DP/USB3/DMA 터널 생성 및 관리 |
path.c | 터널 경로(path) 설정, 홉 구성 |
nhi.c | Native Host Interface — 호스트 컨트롤러 PCIe 드라이버 |
ctl.c | 컨트롤 채널 (라우터 간 통신, 설정 공간 읽기/쓰기) |
icm.c | Intel Connection Manager (펌웨어 기반 CM) |
usb4.c | USB4 전용 레지스터/오퍼레이션 |
usb4_port.c | USB4 포트 관리, 링크 재훈련 |
xdomain.c | 크로스 도메인(peer-to-peer) 디스커버리, 서비스 프로토콜 |
eeprom.c | 디바이스 ROM (DROM) 파싱 — 장치 ID 정보 |
dma_port.c | DMA 포트 기반 NVM(Non-Volatile Memory) 펌웨어 업데이트 |
retimer.c | USB4 리타이머 펌웨어 업데이트 |
clx.c | CL0s/CL1/CL2 링크 절전 상태 관리 |
연결 관리자 (Connection Manager)
Thunderbolt/USB4에서 연결 관리자(CM)는 터널 생성, 대역폭 할당, 보안 정책을 결정하는 핵심 컴포넌트입니다. Linux에는 두 가지 CM이 있습니다:
| CM 유형 | 설명 | 사용 조건 |
|---|---|---|
| ICM (Intel CM) | 인텔 펌웨어 기반 CM. 커널은 펌웨어에 메시지를 보내 터널 생성을 요청 | Thunderbolt 1~3 (Alpine/Titan Ridge) |
| Software CM | 커널이 직접 라우터 설정 공간을 제어하여 터널 생성 | USB4 호스트, Ice Lake+, Apple Silicon |
/* drivers/thunderbolt/tb.c — Software CM의 핵심 구조 */
static const struct tb_cm_ops tb_cm_ops = {
.start = tb_start,
.stop = tb_stop,
.suspend_noirq = tb_suspend_noirq,
.resume_noirq = tb_resume_noirq,
.handle_event = tb_handle_event,
.disapprove_switch = tb_disconnect_and_release_dp,
.approve_switch = tb_tunnel_pci,
.approve_xdomain_paths = tb_approve_xdomain_paths,
.runtime_suspend = tb_runtime_suspend,
.runtime_resume = tb_runtime_resume,
};
/* Software CM 디바이스 연결 시 터널 생성 흐름 */
/*
* 1. NHI가 Hotplug 이벤트 수신 (인터럽트)
* 2. tb_handle_event() 호출 → TB_CFG_PKG_EVENT
* 3. 새 라우터(switch) 열거: tb_scan_port()
* 4. 보안 레벨 확인 후 승인
* 5. 터널 생성: tb_tunnel_pci(), tb_tunnel_dp(), tb_tunnel_usb3()
* 6. 대역폭 분배: tb_reclaim_usb3_bandwidth()
* 7. PCIe 핫플러그로 downstream 디바이스 열거
*/
핵심 자료구조
/* include/linux/thunderbolt.h */
/* struct tb — Thunderbolt 도메인(버스) */
struct tb {
struct device dev;
struct mutex lock; /* 도메인 잠금 */
struct tb_nhi *nhi; /* Native Host Interface */
struct tb_ctl *ctl; /* 컨트롤 채널 */
struct tb_switch *root_switch; /* 호스트 라우터 */
const struct tb_cm_ops *cm_ops; /* CM 콜백 */
int index; /* 도메인 번호 */
enum tb_security_level security_level;
};
/* struct tb_switch — 라우터(스위치) */
struct tb_switch {
struct device dev;
struct tb *tb; /* 소속 도메인 */
u64 route; /* route string */
u16 vendor; /* 벤더 ID */
u16 device; /* 디바이스 ID */
struct tb_port *ports; /* 어댑터(포트) 배열 */
unsigned int config_space_len; /* 설정 공간 크기 */
bool is_unplugged; /* 제거됨 플래그 */
u8 link_speed; /* Gbps */
u8 link_width; /* 1 또는 2 (레인 수) */
enum tb_clx clx; /* CL 절전 상태 */
unsigned int generation; /* TBT 세대 */
};
/* struct tb_port — 어댑터(포트) */
struct tb_port {
struct tb_switch *sw; /* 소속 라우터 */
u8 port; /* 포트 번호 */
enum tb_port_type type; /* Lane/PCIe/DP/USB3/DMA */
struct tb_port *remote; /* 연결된 원격 포트 */
struct tb_port *dual_link_port; /* 듀얼 레인 파트너 */
unsigned int total_credits; /* 대역폭 크레딧 */
};
/* struct tb_tunnel — 터널 (PCIe, DP, USB3, DMA) */
struct tb_tunnel {
struct tb *tb;
struct tb_port *src_port; /* 소스 어댑터 */
struct tb_port *dst_port; /* 목적지 어댑터 */
struct tb_path **paths; /* 경로 배열 */
unsigned int npaths;
enum tb_tunnel_type type; /* PCIe/DP/USB3/DMA */
int allocated_up; /* 할당된 업스트림 Mbps */
int allocated_down; /* 할당된 다운스트림 Mbps */
};
터널 생성 과정
/* drivers/thunderbolt/tunnel.c — PCIe 터널 생성 */
struct tb_tunnel *tb_tunnel_alloc_pci(
struct tb *tb,
struct tb_port *up, /* PCIe Up Adapter (디바이스 측) */
struct tb_port *down /* PCIe Down Adapter (호스트 측) */
)
{
struct tb_tunnel *tunnel;
struct tb_path *path;
tunnel = tb_tunnel_alloc(tb, 2, TB_TUNNEL_PCI);
/* path[0]: downstream (호스트→디바이스) */
path = tb_path_alloc(tb, down, TB_PCI_HOPID, up, TB_PCI_HOPID);
tunnel->paths[0] = path;
/* path[1]: upstream (디바이스→호스트) */
path = tb_path_alloc(tb, up, TB_PCI_HOPID, down, TB_PCI_HOPID);
tunnel->paths[1] = path;
return tunnel;
}
/* 터널 활성화 시 각 홉의 라우터 설정 공간에 path를 기록 */
int tb_tunnel_activate(struct tb_tunnel *tunnel)
{
for (int i = 0; i < tunnel->npaths; i++) {
tb_path_activate(tunnel->paths[i]);
/* 각 홉의 라우터에 in_port, in_hop, out_port, out_hop 설정 */
}
return 0;
}
대역폭 관리
USB4/Thunderbolt 4 이상에서는 터널 간 대역폭을 동적으로 분배합니다. DP 터널이 필요로 하는 대역폭에 따라 PCIe와 USB3 터널의 할당량이 조정됩니다:
/* drivers/thunderbolt/tunnel.c — 대역폭 재분배 */
/* USB3 터널의 대역폭을 DP 터널을 위해 양보 */
static int tb_reclaim_usb3_bandwidth(struct tb *tb,
struct tb_port *port)
{
/* 1. 현재 USB3 터널의 실제 사용량 확인 */
/* 2. 미사용 대역폭을 DP 터널에 재할당 */
/* 3. USB3 터널의 보장 최소 대역폭(1 Gbps) 유지 */
}
/*
* 대역폭 그룹 (Bandwidth Group):
* - USB4 v2.0에서 도입된 QoS 메커니즘
* - 같은 그룹의 터널은 대역폭을 공유
* - DP 터널은 높은 우선순위, PCIe는 Best-Effort
*
* 40 Gbps 링크 대역폭 분배 예시:
* DP (4K@60): ~17 Gbps (보장)
* USB3: ~5 Gbps (보장 최소, 최대 10 Gbps)
* PCIe: 나머지 (Best-Effort, 최대 ~32 Gbps)
*/
보안 레벨
Thunderbolt의 PCIe 터널링은 DMA를 통한 직접 메모리 접근(DMA)을 허용하므로, 보안이 중요합니다:
| 보안 레벨 | 설명 | sysfs 값 |
|---|---|---|
| none | 모든 디바이스 자동 연결 (보안 없음) | none |
| user | 사용자 승인 후 연결 (기본값) | user |
| secure | 디바이스 키 기반 인증 후 연결 | secure |
| dponly | DisplayPort 터널만 허용 (PCIe 차단) | dponly |
| usbonly | USB 터널만 허용 (PCIe/DP 차단) | usbonly |
| nopcie | USB4: PCIe 터널 없음 (USB3 + DP만) | nopcie |
# 도메인 보안 레벨 확인
cat /sys/bus/thunderbolt/devices/domain0/security
# 디바이스 승인 (user 모드)
echo 1 > /sys/bus/thunderbolt/devices/0-1/authorized
# 디바이스 키 기반 인증 (secure 모드)
# 1. 처음 연결 시 challenge 키 저장
cat /sys/bus/thunderbolt/devices/0-1/key
# 2. 재연결 시 저장된 키로 인증
echo "<saved-key>" > /sys/bus/thunderbolt/devices/0-1/key
echo 2 > /sys/bus/thunderbolt/devices/0-1/authorized
iommu=pt 대신 strict 모드(CONFIG_IOMMU_DEFAULT_DMA_STRICT=y 또는 iommu.strict=1)를 우선 검토하고, 플랫폼 정책에 맞게 조합을 선택해야 합니다. 최신 커널은 CONFIG_THUNDERBOLT_DMA_PROTECTION 지원 여부도 함께 확인하세요.
보안 레벨 상세 및 인증 흐름
Thunderbolt의 보안 레벨은 PCIe DMA 터널링으로 인한 메모리 직접 접근 위험을 관리합니다. 각 레벨은 디바이스 연결 시 요구하는 인증 수준이 다르며, IOMMU 보호와 함께 다층 방어를 구성합니다.
NHI 드라이버와 CM 상태 전환
NHI(Native Host Interface)는 Thunderbolt 호스트 컨트롤러의 PCI 디바이스 드라이버입니다. PCI BAR0에 매핑된 레지스터를 통해 송수신 링 버퍼를 관리하고, MSI-X 인터럽트로 이벤트를 수신합니다:
/* drivers/thunderbolt/nhi.c — NHI PCI 드라이버 구조 */
struct tb_nhi {
struct pci_dev *pdev;
void __iomem *iobase; /* PCI BAR0 매핑 */
struct tb_ring **tx_rings; /* 송신 링 배열 */
struct tb_ring **rx_rings; /* 수신 링 배열 */
int hop_count; /* 사용 가능한 HopID 수 */
int msix_ida; /* MSI-X 벡터 관리 */
};
/* NHI 링 구조: DMA 기반 송수신 버퍼 */
struct tb_ring {
struct tb_ring_desc *descriptors; /* DMA 디스크립터 배열 */
dma_addr_t descriptors_dma;
unsigned int head; /* SW 쓰기 포인터 */
unsigned int tail; /* HW 읽기 포인터 */
unsigned int size; /* 링 크기 */
int hop; /* HopID */
void (*callback)(struct tb_ring *, struct ring_frame *, bool);
};
Connection Manager(CM) 상태 전환은 디바이스의 핫플러그(Hotplug), 절전, 에러 복구를 관리합니다. Software CM의 주요 상태 전환은 다음과 같습니다:
/* Software CM 상태 전환 흐름 */
/*
* [IDLE] ──hotplug──→ [SCANNING]
* │ │
* │ ├── tb_scan_port()
* │ ├── tb_switch_alloc() → tb_switch_add()
* │ ├── 보안 레벨 확인
* │ └── tb_tunnel_pci() / tb_tunnel_dp() / tb_tunnel_usb3()
* │ │
* │ ↓
* │ [ACTIVE] ──unplug──→ [REMOVING]
* │ │ │
* │ │ ├── tb_tunnel_deactivate()
* │ │ ├── tb_switch_remove()
* │ │ └── 대역폭 재계산
* │ │ │
* ←──────────────────────────←────────────────────────┘
*
* Suspend 시: tb_suspend_noirq() → 터널 상태 저장
* Resume 시: tb_resume_noirq() → 라우터 재열거 + 터널 복원
*
* 터널 대역폭 QoS:
* - DP 터널: 해상도/주사율 기반 보장 대역폭 계산
* - USB3 터널: 최소 1 Gbps 보장, 유휴 시 DP/PCIe에 양보
* - PCIe 터널: Best-Effort, 잔여 대역폭 사용
* - tb_reclaim_usb3_bandwidth()로 동적 재분배
*/
sysfs 인터페이스
# Thunderbolt/USB4 도메인 정보
ls /sys/bus/thunderbolt/devices/
# domain0 — 도메인 (버스)
# 0-0 — 호스트 라우터 (route 0)
# 0-1 — 첫 번째 연결 디바이스 (route 1)
# 0-301 — 두 번째 홉 디바이스 (route 0x301)
# 라우터(스위치) 속성
cat /sys/bus/thunderbolt/devices/0-1/device_name # 디바이스 이름
cat /sys/bus/thunderbolt/devices/0-1/vendor_name # 벤더 이름
cat /sys/bus/thunderbolt/devices/0-1/generation # TBT 세대 (1-4) 또는 USB4
cat /sys/bus/thunderbolt/devices/0-1/authorized # 승인 상태
cat /sys/bus/thunderbolt/devices/0-1/link_speed # 링크 속도 (Gbps)
cat /sys/bus/thunderbolt/devices/0-1/link_width # 레인 수 (1 또는 2)
cat /sys/bus/thunderbolt/devices/0-1/rx_speed # USB4: 수신 속도
cat /sys/bus/thunderbolt/devices/0-1/rx_lanes # USB4: 수신 레인 수
cat /sys/bus/thunderbolt/devices/0-1/tx_speed # USB4: 송신 속도
cat /sys/bus/thunderbolt/devices/0-1/tx_lanes # USB4: 송신 레인 수
cat /sys/bus/thunderbolt/devices/0-1/nvm_version # 펌웨어 버전
# NVM(펌웨어) 업데이트
# 1. 펌웨어 이미지를 nvm_non_active_store에 기록
dd if=firmware.bin of=/sys/bus/thunderbolt/devices/0-1/nvm_non_active_store
# 2. 인증(플래시) 시작
echo 1 > /sys/bus/thunderbolt/devices/0-1/nvm_authenticate
# 3. 상태 확인 (0 = 성공)
cat /sys/bus/thunderbolt/devices/0-1/nvm_authenticate
Thunderbolt 네트워킹
Thunderbolt은 두 호스트 간 고속 IP 네트워킹을 지원합니다. 커널의 thunderbolt-net 드라이버가 DMA 터널을 통해 가상 이더넷 인터페이스를 생성합니다:
# thunderbolt-net 커널 모듈 로드
modprobe thunderbolt_net
# Thunderbolt 케이블로 두 호스트 연결 시 자동으로 네트워크 인터페이스 생성
ip link show thunderbolt0 # 또는 thunderbolt1, ...
# IP 주소 설정 (양쪽 호스트)
# Host A:
ip addr add 172.16.0.1/24 dev thunderbolt0
ip link set thunderbolt0 up
# Host B:
ip addr add 172.16.0.2/24 dev thunderbolt0
ip link set thunderbolt0 up
# 대역폭 테스트 (Thunderbolt 3: ~22 Gbps 실측)
iperf3 -s # Host A: 서버
iperf3 -c 172.16.0.1 -t 30 # Host B: 클라이언트
drivers/thunderbolt/xdomain.c와 drivers/net/thunderbolt/에 구현되어 있습니다.
PCIe 터널링과 eGPU
Thunderbolt PCIe 터널을 통해 외부 GPU(eGPU), NVMe 스토리지, 네트워크 카드 등을 연결할 수 있습니다:
# Thunderbolt을 통해 연결된 PCIe 디바이스 확인
lspci -vt | grep -A2 "Thunderbolt"
# -[0000:00]-+-00.0 Intel ... Host Bridge
# +-0d.2 Intel ... Thunderbolt 4 NHI
# \-0d.0-[01-04]--+-00.0 Intel ... USB4 Root Port
# \-[02-04]--+-00.0 eGPU (NVIDIA/AMD)
# eGPU PCIe 대역폭 확인
lspci -vvs 02:00.0 | grep -i "lnksta"
# LnkSta: Speed 8GT/s (ok), Width x4 (ok)
# → PCIe 3.0 x4 = ~32 Gbps (Thunderbolt 3/4 최대)
# eGPU 핫플러그 이벤트 모니터링
udevadm monitor --subsystem-match=thunderbolt
udevadm monitor --subsystem-match=pci
# 안전한 eGPU 제거
# 1. GPU를 사용하는 프로세스 종료
# 2. PCIe 디바이스 제거
echo 1 > /sys/bus/pci/devices/0000:02:00.0/remove
# 3. Thunderbolt 디바이스 분리
DisplayPort 터널링
Thunderbolt/USB4는 DisplayPort 신호를 터널링하여 외부 모니터를 연결합니다:
| 연결 방식 | 설명 | 대역폭 |
|---|---|---|
| DP Alt Mode | Type-C 핀을 DP 신호로 직접 사용 (터널링 아님) | DP 1.4: 32.4 Gbps |
| DP Tunnel (TBT3/USB4) | Thunderbolt 링크 위에 DP 패킷 터널링 | 링크 대역폭에서 동적 할당 |
| DP 2.1 Tunnel (USB4 v2) | UHBR 레이트 터널링 | 최대 80 Gbps |
# 연결된 DP 터널 확인 (debugfs)
cat /sys/kernel/debug/thunderbolt/0-0/tunnels
# DP IN: port 3 → port 12 (0-1), allocated_bw: 17280 Mbps
# PCIe: port 1 → port 9 (0-1), allocated_bw: 22000 Mbps
# USB3: port 2 → port 10 (0-1), allocated_bw: 10000 Mbps
# DRM/KMS에서 Thunderbolt 연결 모니터 확인
cat /sys/class/drm/card0-DP-*/status # connected
cat /sys/class/drm/card0-DP-*/edid | edid-decode
전원 관리
| 절전 상태 | 설명 | 복구 시간 |
|---|---|---|
| CL0s | 링크 유휴 시 저전력 (PCIe L0s와 유사) | ~수 μs |
| CL1 | 더 깊은 링크 절전 (PCIe L1과 유사) | ~수십 μs |
| CL2 | USB4: 가장 깊은 절전, PHY 전원 차단 | ~수 ms |
| RTD3 | Runtime D3 — 컨트롤러 전원 완전 차단 | ~수백 ms |
# Thunderbolt 런타임 PM 상태 확인
cat /sys/bus/thunderbolt/devices/0-0/power/runtime_status
# active / suspended
# RTD3 활성화 확인 (기본 활성화)
cat /sys/bus/thunderbolt/devices/domain0/boot_acl
# CLx 상태 확인
cat /sys/bus/thunderbolt/devices/0-1/clx # cl0s, cl1, cl2 또는 disabled
# 전원 관리 관련 커널 메시지
dmesg | grep -i "thunderbolt.*power\|thunderbolt.*CLx\|thunderbolt.*RTD3"
디버깅
# 커널 로그에서 Thunderbolt/USB4 메시지 확인
dmesg | grep -i thunderbolt
# thunderbolt 0-1: new device found, vendor=0x8086 device=0x1234
# thunderbolt 0-1: Thunderbolt 4 device
# 동적 디버그 활성화 (상세 로깅)
echo "module thunderbolt +p" > /sys/kernel/debug/dynamic_debug/control
# debugfs 정보
ls /sys/kernel/debug/thunderbolt/
# 0-0/ — 호스트 라우터 디버그 정보
# 0-1/ — 연결 디바이스 디버그 정보
# 라우터 설정 공간 덤프
cat /sys/kernel/debug/thunderbolt/0-1/regs
# 트레이스포인트
echo 1 > /sys/kernel/debug/tracing/events/thunderbolt/enable
cat /sys/kernel/debug/tracing/trace_pipe
# tb_cfg_read/write: 라우터 설정 공간 접근 추적
# tb_tx/rx: 컨트롤 패킷 송수신 추적
# boltctl — 유저스페이스 관리 도구 (bolt 프로젝트)
boltctl list # 연결된 디바이스 목록
boltctl info 0-1 # 디바이스 상세 정보
boltctl authorize 0-1 # 디바이스 승인
boltctl enroll 0-1 # 디바이스 등록 (자동 승인)
boltctl forget 0-1 # 등록 해제
boltctl monitor # 실시간 이벤트 모니터링
Thunderbolt/USB4 Kconfig 옵션
| 옵션 | 설명 |
|---|---|
CONFIG_USB4 | Thunderbolt/USB4 서브시스템 활성화 (이전명: CONFIG_THUNDERBOLT) |
CONFIG_USB4_DEBUGFS_WRITE | debugfs를 통한 라우터 설정 공간 쓰기 허용 (디버깅용) |
CONFIG_USB4_DMA_TEST | DMA 트래픽 테스트 드라이버 (개발/테스트용) |
CONFIG_USB4_KUNIT_TEST | Thunderbolt KUnit 테스트 |
CONFIG_THUNDERBOLT_NET | Thunderbolt 네트워킹 (호스트 간 IP) |
CONFIG_THUNDERBOLT_DMA_PROTECTION | 부팅 시 IOMMU 기반 DMA 보호 자동 적용 |
CONFIG_HOTPLUG_PCI_PCIE | PCIe 핫플러그 (Thunderbolt PCIe 터널에 필요) |
CONFIG_ACPI_HOTPLUG_MEMORY | ACPI 기반 Thunderbolt 독 메모리 핫플러그 |
usbfs와 libusb
usbfs는 유저스페이스에서 USB 디바이스에 직접 접근할 수 있게 하는 파일시스템입니다. 커널 드라이버 없이도 /dev/bus/usb/를 통해 Control, Bulk, Interrupt, Isochronous 전송을 수행할 수 있습니다.
usbfs 접근 구조
| 경로 | 의미 |
|---|---|
/dev/bus/usb/001/001 | Bus 1의 Root Hub (Device 1) |
/dev/bus/usb/001/002 | Bus 1의 연결 디바이스 |
/dev/bus/usb/002/001 | Bus 2의 Root Hub 또는 디바이스 |
각 디바이스 파일은 캐릭터 디바이스이며, ioctl()로 다음 트랜잭션을 수행합니다: USBDEVFS_CONTROL, USBDEVFS_BULK, USBDEVFS_SUBMITURB, USBDEVFS_REAPURB, USBDEVFS_CLAIMINTERFACE, USBDEVFS_RELEASEINTERFACE, USBDEVFS_RESETEP, USBDEVFS_RESET.
libusb 사용 예제
libusb는 usbfs 위에 구축된 크로스 플랫폼 유저스페이스 USB 라이브러리입니다:
#include <libusb-1.0/libusb.h>
#include <stdio.h>
#define VENDOR_ID 0x1234
#define PRODUCT_ID 0x5678
#define EP_BULK_IN 0x81
#define EP_BULK_OUT 0x02
int main(void)
{
libusb_device_handle *handle;
int ret, transferred;
unsigned char buf[64];
/* 1. 라이브러리 초기화 */
libusb_init(NULL);
/* 2. 디바이스 열기 */
handle = libusb_open_device_with_vid_pid(
NULL, VENDOR_ID, PRODUCT_ID);
if (!handle) {
fprintf(stderr, "Device not found\\n");
return 1;
}
/* 3. 커널 드라이버 분리 (필요 시) */
if (libusb_kernel_driver_active(handle, 0) == 1)
libusb_detach_kernel_driver(handle, 0);
/* 4. 인터페이스 점유 */
libusb_claim_interface(handle, 0);
/* 5. Bulk OUT 전송 (호스트 → 디바이스) */
unsigned char tx_data[] = {0x01, 0x02, 0x03};
ret = libusb_bulk_transfer(handle, EP_BULK_OUT,
tx_data, sizeof(tx_data), &transferred, 5000);
printf("Sent %d bytes\\n", transferred);
/* 6. Bulk IN 전송 (디바이스 → 호스트) */
ret = libusb_bulk_transfer(handle, EP_BULK_IN,
buf, sizeof(buf), &transferred, 5000);
printf("Received %d bytes\\n", transferred);
/* 7. 정리 */
libusb_release_interface(handle, 0);
libusb_close(handle);
libusb_exit(NULL);
return 0;
}
SUBSYSTEM=="usb", ATTR{idVendor}=="1234", ATTR{idProduct}=="5678", MODE="0666"
USB 전원 관리
USB 전원 관리는 사용하지 않는 디바이스를 저전력 상태(Suspend)로 전환하여 전력 소모를 줄입니다. Linux 커널은 USB 디바이스 레벨과 인터페이스 레벨 모두에서 전원 관리를 지원합니다.
Autosuspend
Autosuspend는 디바이스가 일정 시간 유휴 상태(Idle State)이면 자동으로 Suspend하는 메커니즘입니다:
# autosuspend 설정 (sysfs)
# 디바이스별 경로: /sys/bus/usb/devices/<busnum>-<port>/power/
# autosuspend 활성화
echo "auto" > /sys/bus/usb/devices/1-2/power/control
# autosuspend 비활성화 (항상 활성)
echo "on" > /sys/bus/usb/devices/1-2/power/control
# autosuspend 대기 시간 (초) — 0이면 즉시 suspend
echo 2 > /sys/bus/usb/devices/1-2/power/autosuspend_delay_ms
# 현재 전원 상태 확인
cat /sys/bus/usb/devices/1-2/power/runtime_status
# active / suspended / suspending
# USB autosuspend 전역 기본값 (커널 파라미터)
# usbcore.autosuspend=2 (2초 후 자동 suspend)
# usbcore.autosuspend=-1 (autosuspend 비활성화)
Remote Wakeup
Remote Wakeup은 Suspend된 디바이스가 이벤트 발생 시 호스트를 깨울 수 있는 기능입니다 (예: 마우스 움직임, 키보드 입력):
/* 드라이버에서 autosuspend / remote wakeup 지원 */
static int my_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf);
/* ... 드라이버 초기화 ... */
/* Remote Wakeup 허용 */
device_set_wakeup_enable(&udev->dev, 1);
/* autosuspend 활성화 */
usb_enable_autosuspend(udev);
return 0;
}
/* Suspend 콜백 */
static int my_usb_suspend(struct usb_interface *intf,
pm_message_t message)
{
struct my_usb_dev *dev = usb_get_intfdata(intf);
/* 진행 중인 URB 취소 */
usb_kill_urb(dev->bulk_urb);
return 0;
}
/* Resume 콜백 */
static int my_usb_resume(struct usb_interface *intf)
{
struct my_usb_dev *dev = usb_get_intfdata(intf);
/* URB 재제출 */
usb_submit_urb(dev->bulk_urb, GFP_NOIO);
return 0;
}
static struct usb_driver my_usb_driver = {
.name = "my_usb",
.id_table = my_usb_ids,
.probe = my_usb_probe,
.disconnect = my_usb_disconnect,
.suspend = my_usb_suspend,
.resume = my_usb_resume,
.supports_autosuspend = 1, /* autosuspend 지원 선언 */
};
Selective Suspend
| 용어 | 설명 |
|---|---|
| Global Suspend | 전체 USB 버스 Suspend (시스템 Sleep 시) |
| Selective Suspend | 개별 디바이스만 Suspend (런타임 PM) |
| L1 (LPM) | USB 2.0 Link Power Management — 빠른 진입/탈출 |
| U1/U2/U3 | USB 3.x Link States — U1/U2(저전력), U3(Suspend) |
echo "on" > power/control로 autosuspend를 비활성화하거나, 커널의 USB quirks 테이블에 등록합니다.USB 3.x Link Power State 전환
USB 3.x는 링크 레벨에서 4가지 전력 상태(U0~U3)를 정의합니다. U1/U2는 LPM(Link Power Management)을 통해 유휴 시 자동 전환되며, U3는 전통적인 Suspend 상태입니다. 상태 전환은 링크 양쪽이 LPM 토큰(LGO_U1/LGO_U2)을 교환하여 합의합니다.
Selective Suspend 메커니즘 상세
USB의 Selective Suspend는 시스템이 활성(S0) 상태에서 개별 디바이스만 Suspend하는 런타임 PM 메커니즘입니다. USB 2.0 LPM(L1 상태)과 USB 3.x U1/U2는 하드웨어가 자동으로 전환하는 반면, Selective Suspend(U3/L2)는 소프트웨어가 명시적으로 제어합니다:
| 메커니즘 | 적용 버전 | 진입 방법 | 복구 시간 | 절전 효과 |
|---|---|---|---|---|
| USB 2.0 LPM (L1) | USB 2.0 Addendum | HW 자동 (Extended Token) | ~50~300 μs | 중간 (PHY 유지) |
| USB 3.x U1 | USB 3.0+ | HW 자동 (LGO_U1 토큰) | ~수 μs | 낮음 (클록 유지) |
| USB 3.x U2 | USB 3.0+ | HW 자동 (LGO_U2 토큰) | ~수십 μs~ms | 중간 (클록 정지) |
| Selective Suspend (U3/L2) | USB 1.1+ | SW 명시적 (SET_FEATURE) | ~수 ms~20ms | 높음 (PHY 전원 차단) |
BESL(Best Effort Service Latency)은 USB 2.0 LPM에서 디바이스가 L1 상태에서 복구하는 데 필요한 시간을 호스트에 알리는 메커니즘입니다. BESL 값이 클수록 깊은 절전이 가능하지만 복구 시간이 길어집니다. xHCI 드라이버는 Endpoint의 BESL 값을 확인하여 LPM 정책을 조정합니다.
Remote Wakeup은 Suspend 상태의 디바이스가 호스트를 깨울 수 있는 기능입니다. USB 2.0에서는 D+/D- 라인에 Resume 신호를 보내고, USB 3.x에서는 LFPS(Low Frequency Periodic Signaling)를 사용합니다. 드라이버는 device_set_wakeup_enable()으로 Remote Wakeup을 허용하고, 호스트는 SET_FEATURE(DEVICE_REMOTE_WAKEUP)로 디바이스에 통보합니다. Suspend 중 디바이스 이벤트(키보드 입력, 마우스 이동 등) 발생 시 디바이스가 스스로 Resume 신호를 생성하여 호스트를 깨웁니다.
디버깅
lsusb
# 연결된 USB 디바이스 목록
lsusb
# 트리 구조 (토폴로지)
lsusb -t
# 특정 디바이스 상세 정보 (모든 디스크립터 덤프)
lsusb -v -d 1234:5678
# 특정 버스/디바이스
lsusb -v -s 001:003
# 출력 예시 (lsusb -t)
# /: Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 10000M
# |__ Port 1: Dev 2, If 0, Class=Mass Storage, Driver=uas, 5000M
# /: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/12p, 480M
# |__ Port 3: Dev 5, If 0, Class=HID, Driver=usbhid, 12M
# |__ Port 3: Dev 5, If 1, Class=HID, Driver=usbhid, 12M
# |__ Port 9: Dev 3, If 0, Class=Wireless, Driver=btusb, 12M
usbmon
usbmon은 USB 트래픽을 모니터링하는 커널 기능입니다. /sys/kernel/debug/usb/usbmon/을 통해 접근합니다:
# usbmon 모듈 로드
modprobe usbmon
# debugfs 마운트 확인
mount -t debugfs none /sys/kernel/debug 2>/dev/null
# 텍스트 형식 모니터링 (Bus 1)
cat /sys/kernel/debug/usb/usbmon/1t
# 모든 버스 모니터링
cat /sys/kernel/debug/usb/usbmon/0t
# 바이너리 형식 (Wireshark용)
cat /sys/kernel/debug/usb/usbmon/1u > capture.bin
# usbmon 텍스트 출력 형식:
# URB_TAG TIMESTAMP EVENT_TYPE ADDRESS:EP STATUS LENGTH DATA
# d5ea8c00 3575914555 S Bi:1:003:1 -115 31 = 55534253 ...
# S = Submit, C = Complete
# Bi = Bulk IN, Bo = Bulk OUT, Ci = Control IN
Wireshark를 이용한 USB 분석
# Wireshark에서 USB 캡처 (usbmon 인터페이스 선택)
# 또는 tcpdump로 캡처 파일 생성
tcpdump -i usbmon1 -w usb_capture.pcap
# Wireshark에서 열기
wireshark usb_capture.pcap
# Wireshark 필터 예시:
# usb.transfer_type == URB_BULK
# usb.device_address == 3
# usb.endpoint_address == 0x81
# usb.idVendor == 0x1234
sysfs USB 정보
커널 디버깅
# USB 관련 커널 메시지
dmesg | grep -i usb
# Dynamic Debug 활성화
echo 'module usbcore +p' > /sys/kernel/debug/dynamic_debug/control
echo 'module xhci_hcd +p' > /sys/kernel/debug/dynamic_debug/control
echo 'module usbhid +p' > /sys/kernel/debug/dynamic_debug/control
# USB 디바이스 수동 리셋
echo 0 > /sys/bus/usb/devices/1-2/authorized
echo 1 > /sys/bus/usb/devices/1-2/authorized
# USB 바인딩/언바인딩
echo "1-2:1.0" > /sys/bus/usb/drivers/usbhid/unbind
echo "1-2:1.0" > /sys/bus/usb/drivers/usbhid/bind
# quirks 설정 (모듈 파라미터)
# usbcore.quirks=VID:PID:flags
# 예: usbcore.quirks=0x1234:0x5678:gn (RESET_RESUME + NO_SET_INTF)
디버깅 도구 요약
| 도구 | 용도 | 설치 |
|---|---|---|
lsusb | USB 디바이스 목록 및 디스크립터 덤프(Dump) | usbutils |
usbmon | 커널 레벨 USB 트래픽 모니터링 | 커널 내장 (CONFIG_USB_MON) |
Wireshark | GUI USB 프로토콜 분석 | wireshark |
usbhid-dump | HID Report Descriptor 덤프 | usbutils |
usb-devices | 텍스트 기반 디바이스 정보 | usbutils |
usbip | USB over IP (원격 디바이스 공유) | usbip |
USB 보안
USB는 설계 초기부터 신뢰 기반 프로토콜이었으나, BadUSB, DMA 공격, 악성 디바이스 등 다양한 보안 위협이 알려지면서 커널 레벨의 방어 메커니즘이 중요해졌습니다.
USB 디바이스 인증 (USB Authentication)
USB-IF는 USB 3.2 규격에 USB Authentication 프로토콜을 추가했습니다. 공개키 기반(RSA/ECDSA) 인증으로 디바이스의 정당성을 검증합니다. 현재 Linux 커널에는 완전한 구현이 없지만, Thunderbolt의 보안 레벨(secure 모드)이 유사한 기능을 제공합니다.
커널 USB 인가(Authorization) 메커니즘
# USB 디바이스 인가 제어 (커널 내장 메커니즘)
# 버스 전체 기본 인가 정책
# 0: 새 디바이스 기본 비인가 (수동 승인 필요)
# 1: 새 디바이스 자동 인가 (기본값)
echo 0 > /sys/bus/usb/devices/usb1/authorized_default
# 개별 디바이스 인가/비인가
echo 0 > /sys/bus/usb/devices/1-2/authorized # 비인가 (드라이버 언바인드)
echo 1 > /sys/bus/usb/devices/1-2/authorized # 인가 (드라이버 바인딩)
# 인터페이스 레벨 인가
echo 0 > /sys/bus/usb/devices/1-2:1.0/authorized
USBGuard
USBGuard는 USB 디바이스 접근을 정책 기반으로 제어하는 유저스페이스 데몬입니다. 커널의 authorized sysfs 인터페이스를 활용하여 화이트리스트/블랙리스트 정책을 적용합니다.
# USBGuard 기본 사용법
usbguard list-devices # 연결된 디바이스 목록
usbguard allow-device <id> # 디바이스 허용
usbguard block-device <id> # 디바이스 차단
usbguard generate-policy > rules.conf # 현재 상태 기반 정책 생성
# 정책 규칙 예시 (/etc/usbguard/rules.conf)
# 키보드/마우스 허용
allow with-interface equals { 03:01:01 03:01:02 }
# 특정 VID/PID 허용
allow id 1d6b:0002
# Mass Storage 차단
reject with-interface equals { 08:*:* }
BadUSB 방어
| 공격 유형 | 설명 | 방어 방법 |
|---|---|---|
| HID 위장 | USB 메모리가 키보드로 가장하여 명령 주입 | USBGuard 인터페이스 정책, authorized_default=0 |
| DMA 공격 | FireWire/Thunderbolt DMA로 메모리 직접 접근 | IOMMU 활성화, Thunderbolt 보안 레벨 |
| 펌웨어 변조 | USB 컨트롤러 펌웨어 재프로그래밍 | 서명 검증(Signature Verification), USB Authentication (미래) |
| Juice Jacking | 충전 포트를 통한 데이터 탈취 | USBGuard, 데이터 라인 차단 케이블 |
| 디스크립터 퍼징 | 악의적 디스크립터로 커널 파서 취약점(Vulnerability) 공격 | 커널 퍼징 테스트, syzkaller, 디스크립터 검증 강화 |
CONFIG_SECURITY_LOCKDOWN_LSM을 활성화하면 Secure Boot 환경에서 /dev/mem, iopl(), ACPI 테이블 덮어쓰기 등을 차단하여 USB 기반 DMA 공격의 영향을 줄일 수 있습니다.USB 가상화(Virtualization)
USB 디바이스를 가상 머신에서 사용하거나 네트워크를 통해 원격 공유하려면 USB 가상화 기술이 필요합니다. Linux 커널은 VHCI(Virtual Host Controller Interface)와 USB/IP를 제공합니다.
USB/IP (USB over IP)
USB/IP는 USB 디바이스를 네트워크를 통해 원격 공유하는 커널 모듈입니다. 서버 측에서 물리 USB 디바이스를 내보내고, 클라이언트 측에서 가상 HCD(VHCI)를 통해 로컬 디바이스처럼 사용합니다.
# USB/IP 서버 측 (디바이스를 가진 머신)
modprobe usbip-host
usbipd -D # 데몬 시작
usbip list -l # 로컬 디바이스 목록
usbip bind -b 1-2 # 디바이스 내보내기
# USB/IP 클라이언트 측 (디바이스를 사용할 머신)
modprobe vhci-hcd # VHCI 로드
usbip list -r server_ip # 서버의 내보낸 디바이스 목록
usbip attach -r server_ip -b 1-2 # 디바이스 연결
# 연결 해제
usbip detach -p 0 # 포트 0 분리
# 클라이언트에서 디바이스가 로컬처럼 보임
lsusb # VHCI에 연결된 원격 디바이스 표시
가상 머신 USB Pass-through
| 방식 | 메커니즘 | 성능 | 호환성 | 보안 |
|---|---|---|---|---|
| VFIO USB | 호스트 USB 디바이스를 VM에 직접 할당 | 네이티브 | 높음 | IOMMU 그룹 격리(Isolation) |
| QEMU USB Pass-through | QEMU가 호스트 usbfs를 통해 VM에 전달 | 중간 | 높음 | QEMU 프로세스 권한 |
| virtio-usb (실험적) | virtio 기반 반가상화(Paravirtualization) USB HCD | 높음 | 제한적 | virtio 채널 격리 |
| USB/IP (VHCI) | 네트워크 기반 USB 포워딩 | 낮음 (네트워크 지연) | 범용 | 암호화(Encryption) 필요 (SSH 터널) |
# QEMU USB 패스스루 예제
# 방법 1: 호스트 USB 디바이스 직접 할당 (VID/PID 기반)
qemu-system-x86_64 \
-usb \
-device usb-host,vendorid=0x1234,productid=0x5678
# 방법 2: 호스트 버스/디바이스 번호 기반
qemu-system-x86_64 \
-usb \
-device usb-host,hostbus=1,hostaddr=3
# 방법 3: VFIO USB 그룹 패스스루 (IOMMU 필요)
# USB 컨트롤러 전체를 VM에 할당
echo "0000:00:14.0" > /sys/bus/pci/devices/0000:00:14.0/driver/unbind
echo "0000:00:14.0" > /sys/bus/pci/drivers/vfio-pci/bind
qemu-system-x86_64 \
-device vfio-pci,host=0000:00:14.0
dummy_hcd를 이용한 USB 테스트
dummy_hcd는 물리 USB 하드웨어 없이 가상 HCD/UDC 쌍을 생성하여 USB Gadget 드라이버를 테스트할 수 있게 합니다:
# dummy_hcd 로드 — 가상 Host + Device 컨트롤러 쌍 생성
modprobe dummy_hcd
# UDC 확인
ls /sys/class/udc/ # dummy_udc.0 표시
# ConfigFS로 Gadget 연결
echo "dummy_udc.0" > /sys/kernel/config/usb_gadget/my_gadget/UDC
# 호스트 측에서 가상 디바이스가 열거됨
lsusb # dummy_hcd에 연결된 Gadget 표시
# 용도: Gadget Function 개발/테스트, CI/CD 파이프라인, 퍼징
USB 성능 최적화
USB 성능은 전송 타입, URB 크기, DMA 설정, 스케줄링 정책에 따라 크게 달라집니다. 최대 처리량(Throughput)을 달성하려면 프로토콜 오버헤드를 최소화하고 하드웨어 기능을 최대한 활용해야 합니다.
Bulk 스트림
USB 3.0 이상에서는 하나의 Bulk Endpoint에 여러 Stream을 할당할 수 있습니다. 각 Stream은 독립적인 Transfer Ring을 가지므로, 명령 큐잉과 비순차 완료가 가능합니다. UAS(USB Attached SCSI)가 대표적인 활용 사례입니다.
/* Bulk Streams API */
/* 스트림 할당 */
int num_streams = usb_alloc_streams(
intf, /* usb_interface */
eps, /* Endpoint 배열 */
num_eps, /* Endpoint 수 */
num_streams_requested, /* 요청 스트림 수 */
GFP_KERNEL);
/* 반환: 실제 할당된 스트림 수 (요청보다 적을 수 있음) */
/* URB에 Stream ID 설정 */
urb->stream_id = stream_id; /* 1 ~ num_streams */
usb_submit_urb(urb, GFP_KERNEL);
/* 스트림 해제 */
usb_free_streams(intf, eps, num_eps, GFP_KERNEL);
DMA와 Scatter-Gather
| 최적화 기법 | 설명 | API |
|---|---|---|
| DMA 매핑 | URB의 transfer_dma 필드에 DMA 주소 직접 설정 | URB_NO_TRANSFER_DMA_MAP 플래그 |
| Scatter-Gather | 비연속 메모리를 단일 USB 전송으로 모음 | usb_sg_init() / usb_sg_wait() |
| Zero-copy | 커널-유저 간 데이터 복사 회피 | usb_alloc_coherent() + mmap |
| URB 배치 | 여러 URB를 동시 제출하여 파이프라인(Pipeline) 효과 | 다수 URB 동시 usb_submit_urb() |
/* Scatter-Gather 전송 예제 */
struct usb_sg_request sg_req;
struct scatterlist sg[4];
int ret;
/* scatterlist 설정 (비연속 버퍼들) */
sg_init_table(sg, 4);
sg_set_buf(&sg[0], buf1, len1);
sg_set_buf(&sg[1], buf2, len2);
sg_set_buf(&sg[2], buf3, len3);
sg_set_buf(&sg[3], buf4, len4);
/* SG 요청 초기화 */
ret = usb_sg_init(&sg_req, udev,
usb_sndbulkpipe(udev, ep),
0, /* 전송 플래그 */
sg, 4, /* scatterlist, 엔트리 수 */
total_len, /* 총 바이트 수 */
GFP_KERNEL);
/* 동기 전송 실행 */
usb_sg_wait(&sg_req);
if (sg_req.status)
dev_err(&intf->dev, "SG transfer failed: %d\\n",
sg_req.status);
성능 튜닝 가이드
| 항목 | 권장 설정 | 효과 |
|---|---|---|
| URB 크기 | Bulk: 16~64KB (USB 2.0), 64~256KB (USB 3.x) | 프로토콜 오버헤드 감소 |
| URB 동시 제출 | 2~8개 URB 파이프라인 | HW 유휴 시간 제거 |
| Bulk Streams | UAS: 32개 스트림 | 명령 큐잉, 비순차 완료 |
| NCM vs ECM | CDC NCM 사용 (USB 3.x 네트워킹) | 패킷 집적으로 ~4x 처리량 |
| LPM 비활성화 | 지연 민감 응용에서 U1/U2 비활성화 | 레이턴시 감소 (처리량 교환) |
| autosuspend 조정 | 고빈도 I/O 시 autosuspend_delay 증가 | Resume 오버헤드 감소 |
| IOMMU 모드 | 성능 우선: iommu=pt, 보안 우선: strict | DMA 매핑 오버헤드 감소/격리 |
USB 트레이싱과 ftrace
USB 서브시스템은 ftrace의 tracepoint를 활용하여 URB 제출/완료, xHCI 이벤트, Gadget 동작 등을 추적할 수 있습니다. usbmon보다 세밀한 커널 내부 동작을 분석할 때 유용합니다.
USB tracepoint 목록
# 사용 가능한 USB tracepoint 확인
ls /sys/kernel/debug/tracing/events/xhci-hcd/
# xhci_address_ctx, xhci_cmd_completion, xhci_dbg_address,
# xhci_handle_event, xhci_urb_enqueue, xhci_urb_dequeue, ...
ls /sys/kernel/debug/tracing/events/gadget/
# usb_gadget_connect, usb_gadget_disconnect,
# usb_ep_queue, usb_ep_dequeue, usb_gadget_giveback_request, ...
# xHCI URB 흐름 추적
echo 1 > /sys/kernel/debug/tracing/events/xhci-hcd/xhci_urb_enqueue/enable
echo 1 > /sys/kernel/debug/tracing/events/xhci-hcd/xhci_urb_dequeue/enable
echo 1 > /sys/kernel/debug/tracing/events/xhci-hcd/xhci_cmd_completion/enable
# 트레이스 출력 확인
cat /sys/kernel/debug/tracing/trace_pipe
# Gadget 동작 추적
echo 1 > /sys/kernel/debug/tracing/events/gadget/enable
# trace-cmd를 이용한 USB 트레이싱
trace-cmd record -e xhci-hcd -e gadget sleep 5
trace-cmd report
perf를 이용한 USB 성능 분석
# USB IRQ 처리 시간 분석
perf record -g -a -e irq:irq_handler_entry -e irq:irq_handler_exit sleep 5
perf report
# xHCI 이벤트 핸들러 프로파일링
perf top -g -p $(pgrep -f "kworker.*xhci")
# USB Storage I/O 레이턴시 분석
perf trace -e 'usb:*' -p $(pgrep usb-storage) -- sleep 10
# BPF 기반 USB 레이턴시 히스토그램 (bpftrace)
bpftrace -e '
tracepoint:xhci-hcd:xhci_urb_enqueue { @start[arg0] = nsecs; }
tracepoint:xhci-hcd:xhci_urb_dequeue /@start[arg0]/ {
@us = hist((nsecs - @start[arg0]) / 1000);
delete(@start[arg0]);
}'
커널 6.x USB 변경사항
커널 6.x 시리즈에서는 USB 서브시스템에 다양한 개선과 새로운 기능이 추가되었습니다.
| 커널 버전 | 변경사항 | 영향 |
|---|---|---|
| 6.0 | USB4 v2.0 (80 Gbps) 초기 지원, xHCI 드라이버 개선 | Thunderbolt 5 준비 |
| 6.1 | Type-C retimer 펌웨어 업데이트, UCSI 개선 | USB4 리타이머 관리 |
| 6.2 | USB4 CL(Link) 절전 상태 개선, DP 터널 대역폭 관리 | 전력 소모 감소 |
| 6.3 | xHCI PME 처리 개선, USB3 LPM BESL 지원 확대 | Suspend/Resume 안정성 |
| 6.4 | Type-C USB Power Delivery 3.1 EPR(240W) 지원 | 고전력 충전 지원 |
| 6.5 | USB4 v2.0 대역폭 할당 개선, Thunderbolt 대역폭 그룹 | 멀티 터널 성능 최적화 |
| 6.6 | xHCI interrupter 개선 (오디오 클래스용), NCM 다중 큐 | USB 오디오 레이턴시 감소 |
| 6.7 | USB4 DisplayPort 대역폭 협상 개선, TCPM PD 3.1 안정화 | DP Alt Mode 화질 개선 |
| 6.8 | gadget: USB 요청 완료 워크큐 최적화, dwc3 성능 개선 | Gadget 처리량 향상 |
| 6.9 | xHCI 세그먼트 할당 최적화, USB4 CLx 절전 개선 | 메모리 사용량 감소 |
| 6.10 | typec: 커넥터 클래스 리팩터링, PD 개정 3.2 준비 | Type-C 드라이버 유지보수성 |
| 6.11 | USB4 v2.0 80 Gbps 대칭 터널링, DisplayPort 2.1 터널 | 차세대 대역폭 활용 |
| 6.12+ | xHCI interrupter 다중화 (USB Audio offload 기반), USB PD 3.2 | 오디오 오프로드, 고급 전원 관리 |
폐기 예정 및 제거된 기능
| 기능 | 상태 | 대안 |
|---|---|---|
UHCI HCD | 유지보수 모드 (신규 하드웨어 없음) | xHCI가 LS/FS 통합 지원 |
usbfs procfs (/proc/bus/usb/) | 제거됨 | /dev/bus/usb/ (devtmpfs) |
g_multi 등 레거시 Gadget | 폐기 예정 | ConfigFS 기반 Gadget 구성 |
CONFIG_USB_OTG (구형) | 리팩터링 중 | Type-C DRD(Dual-Role Device) 모델 |
커널 소스 구조
| 경로 | 설명 |
|---|---|
drivers/usb/core/ | USB Core (열거, URB, Hub, 드라이버 매칭) |
drivers/usb/host/ | HCD 드라이버 (xhci, ehci, ohci, uhci) |
drivers/usb/gadget/ | USB Gadget Framework (UDC, composite, function) |
drivers/usb/storage/ | USB Mass Storage 드라이버 (usb-storage, uas) |
drivers/usb/class/ | USB 클래스 드라이버 (cdc-acm, usblp) |
drivers/usb/serial/ | USB-Serial 어댑터 드라이버 |
drivers/usb/typec/ | Type-C 서브시스템 (TCPM, UCSI, Alt Mode) |
drivers/hid/usbhid/ | USB HID 드라이버 |
sound/usb/ | USB Audio 드라이버 |
drivers/net/usb/ | USB 네트워크 드라이버 (cdc_ether, cdc_ncm, rndis) |
drivers/media/usb/ | USB 미디어 드라이버 (UVC 웹캠 등) |
include/linux/usb.h | USB Core API 헤더 |
include/linux/usb/hcd.h | HCD API 헤더 |
include/linux/usb/gadget.h | Gadget API 헤더 |
include/linux/usb/ch9.h | USB Chapter 9 상수/구조체 (uapi) |
drivers/thunderbolt/ | Thunderbolt/USB4 코어 (CM, 터널, 라우터, NHI) |
drivers/net/thunderbolt/ | Thunderbolt 네트워킹 드라이버 |
include/linux/thunderbolt.h | Thunderbolt/USB4 API 헤더 |
주요 Kconfig 옵션
| 옵션 | 설명 |
|---|---|
CONFIG_USB | USB 서브시스템 활성화 |
CONFIG_USB_XHCI_HCD | xHCI 호스트 컨트롤러 드라이버 |
CONFIG_USB_EHCI_HCD | EHCI 호스트 컨트롤러 드라이버 |
CONFIG_USB_OHCI_HCD | OHCI 호스트 컨트롤러 드라이버 |
CONFIG_USB_STORAGE | USB Mass Storage 드라이버 |
CONFIG_USB_UAS | USB Attached SCSI (UASP) 드라이버 |
CONFIG_USB_HID | USB HID 드라이버 |
CONFIG_USB_ACM | CDC ACM (가상 시리얼) 드라이버 |
CONFIG_USB_NET_CDCETHER | CDC Ethernet 드라이버 |
CONFIG_USB_NET_CDC_NCM | CDC NCM 네트워크 드라이버 |
CONFIG_SND_USB_AUDIO | USB Audio 드라이버 |
CONFIG_USB_GADGET | USB Gadget Framework |
CONFIG_USB_CONFIGFS | ConfigFS 기반 USB Gadget 구성 |
CONFIG_USB_FUNCTIONFS | FunctionFS (유저스페이스 Gadget) |
CONFIG_TYPEC | USB Type-C 서브시스템 |
CONFIG_TYPEC_TCPM | Type-C Port Manager |
CONFIG_TYPEC_UCSI | UCSI Type-C 인터페이스 |
CONFIG_USB4 | Thunderbolt/USB4 서브시스템 |
CONFIG_THUNDERBOLT_NET | Thunderbolt 네트워킹 (호스트 간 IP) |
CONFIG_USB_MON | usbmon (USB 트래픽 모니터링) |
참고자료
커널 공식 문서
- Linux USB API — Linux USB API 가이드
- Writing USB Device Drivers — USB 디바이스 드라이버 작성 가이드
- USB Request Block (URB) — URB 구조와 사용법
- USB3 Debug Port — USB3 디버그 포트 활용
- USB Gadget API — USB Gadget 프레임워크
- USB support overview — USB 지원 개요
커널 소스 코드
drivers/usb/core/urb.c— URB 제출 및 관리drivers/usb/core/hub.c— USB 허브 드라이버 (열거 로직)drivers/usb/core/driver.c— USB 드라이버 매칭include/linux/usb.h— usb_device, usb_driver 정의include/linux/usb/ch9.h— USB 표준 디스크립터drivers/usb/host/xhci.c— xHCI 호스트 컨트롤러 드라이버drivers/usb/gadget/udc/— UDC 드라이버drivers/usb/dwc3/— DesignWare USB3 컨트롤러drivers/usb/typec/— USB Type-C 서브시스템
규격 문서
- USB-IF 공식 규격 문서 — USB 2.0/3.x/4 규격 및 클래스 규격
외부 자료
- Linux Device Drivers, 3rd Edition — LDD3 온라인 전문
- LDD3 Chapter 13: USB Drivers — USB 드라이버 챕터 (PDF)
- USB and sysfs — USB sysfs 인터페이스 해설
- USB Type-C in the kernel — 커널의 USB Type-C 지원
관련 문서
USB와 관련된 다른 주제를 더 깊이 이해하고 싶다면 다음 문서를 참고하세요.