USB 서브시스템 심화
USB 서브시스템을 프로토콜 계층, 컨트롤러 드라이버, 사용자 공간 인터페이스까지 통합 관점으로 심층 분석합니다. USB Core와 URB 흐름, EHCI/XHCI host controller 동작, enumeration과 descriptor 파싱, class driver 작성 패턴, Gadget/UDC 기반 디바이스 에뮬레이션, Type-C/PD 및 USB4/Thunderbolt 연동, autosuspend/runtime PM 정책, usbmon·tracepoint·wireshark를 활용한 패킷 디버깅까지 실전 제품 개발과 장애 분석에 필요한 내용을 폭넓게 다룹니다.
핵심 요약
- 초기화 순서 — 탐색, 바인딩, 자원 등록 순서를 점검합니다.
- 제어/데이터 분리 — 빠른 경로와 설정 경로를 분리 설계합니다.
- IRQ/작업 분할 — 즉시 처리와 지연 처리를 구분합니다.
- 안전 한계 — 전원/열/타이밍 임계값을 함께 관리합니다.
- 운영 복구 — 오류 시 재초기화와 롤백 경로를 준비합니다.
단계별 이해
- 장치 수명주기 확인
probe부터 remove까지 흐름을 점검합니다. - 비동기 경로 설계
IRQ, 워크큐, 타이머 역할을 분리합니다. - 자원 정합성 검증
DMA/클록/전원 참조를 교차 확인합니다. - 현장 조건 테스트
연결 끊김/복구/부하 상황을 재현합니다.
USB 개요
USB (Universal Serial Bus)는 1996년 Intel, Compaq, Microsoft 등이 공동으로 제안한 범용 직렬 버스 규격입니다. 키보드, 마우스부터 외장 스토리지, 네트워크 어댑터, 비디오 캡처 장치까지 거의 모든 주변기기의 사실상 표준 인터페이스로 자리 잡았습니다. Linux 커널은 USB 서브시스템을 통해 호스트 컨트롤러 관리, 디바이스 열거, 드라이버 바인딩, 전원 관리를 통합적으로 처리합니다.
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, 디바이스 열거, 대역폭 관리 | usb_device, usb_bus |
| USB Host Controller | 하드웨어 트랜잭션 스케줄링, 데이터 전송 | xHCI, EHCI, OHCI, UHCI |
| USB Bus Interface | 전기/물리 시그널링, 패킷 인코딩 | 차동 시그널링, 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));
전송 타입
USB는 4가지 전송 타입을 정의하며, 각각 다른 특성과 용도를 가집니다:
| 전송 타입 | 특성 | 대역폭 보장 | 오류 재전송 | 용도 |
|---|---|---|---|---|
| Control | 양방향, 셋업+데이터+상태 단계 | 예약됨 (10~20%) | 예 | 디바이스 설정, 디스크립터 읽기, 벤더 요청 |
| Bulk | 대용량, 잔여 대역폭 사용 | 없음 (잔여 사용) | 예 | Mass Storage, 프린터, 네트워크 데이터 |
| Interrupt | 소량, 폴링 주기 보장 | 예약됨 | 예 | 키보드, 마우스, 조이스틱, HID |
| Isochronous | 실시간, 일정 대역폭 | 예약됨 | 없음 | 오디오, 비디오, 웹캠 |
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
Linux USB Core
Linux USB Core(drivers/usb/core/)는 호스트 컨트롤러와 디바이스 드라이버 사이의 중간 계층으로, 디바이스 열거, URB 관리, 전원 관리, 드라이버 매칭을 담당합니다.
핵심 자료구조
| 구조체 | 헤더 | 역할 |
|---|---|---|
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 = 미사용) */
/* ... */
};
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");
USB ID 매칭 매크로
| 매크로 | 매칭 기준 |
|---|---|
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() 콜백이 호출되기 전에 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; /* 콜백 컨텍스트 */
/* ... */
};
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() | 참조 카운트 기반 해제 |
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);
비동기 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;
}
동기 전송 래퍼
간단한 전송에는 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 파이프 |
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);
/* ... */
};
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 레지스터 맵
xHCI 호스트 컨트롤러는 PCI BAR0에 매핑된 MMIO 레지스터 공간을 4개 영역으로 구분합니다. 각 영역은 오프셋 기반으로 접근하며, Capability 레지스터에서 각 영역의 시작 오프셋을 읽습니다:
| 영역 | 오프셋 결정 | 주요 레지스터 | 용도 |
|---|---|---|---|
| Capability | BAR0 + 0x00 | CAPLENGTH, HCSPARAMS1/2/3, HCCPARAMS1/2 | 컨트롤러 성능/한계 정보 (읽기 전용) |
| 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() 로 관리
*/
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을 동적으로 구성할 수 있습니다. 커널 모듈 재컴파일 없이 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의 로직을 유저스페이스에서 구현할 수 있게 하는 파일시스템입니다. 커널 드라이버 없이 사용자 정의 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으로 송신 */
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) |
| 오버헤드 | 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 패킷 캡슐화 | 가능 | ~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)는 인텔이 표준화한 펌웨어 기반 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 세션을 유지합니다.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 | 선택적 | 트리 토폴로지 |
| 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 | 물리 링크 관리, 레인 본딩 | 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) |
토폴로지와 라우팅
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를 통한 직접 메모리 접근을 허용하므로, 보안이 중요합니다:
| 보안 레벨 | 설명 | 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) 상태 전환은 디바이스의 핫플러그, 절전, 에러 복구를 관리합니다. 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는 디바이스가 일정 시간 유휴 상태이면 자동으로 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 디바이스 목록 및 디스크립터 덤프 | usbutils |
usbmon | 커널 레벨 USB 트래픽 모니터링 | 커널 내장 (CONFIG_USB_MON) |
Wireshark | GUI USB 프로토콜 분석 | wireshark |
usbhid-dump | HID Report Descriptor 덤프 | usbutils |
usb-devices | 텍스트 기반 디바이스 정보 | usbutils |
usbip | USB over IP (원격 디바이스 공유) | usbip |
커널 소스 구조
| 경로 | 설명 |
|---|---|
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 트래픽 모니터링) |
관련 문서
USB와 관련된 다른 주제를 더 깊이 이해하고 싶다면 다음 문서를 참고하세요.