# 소프트웨어 개발자를 위한 USB: 사용자 공간 USB 드라이버 작성 입문

> Clean Markdown view of GeekNews topic #28356. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=28356](https://news.hada.io/topic?id=28356)
- GeekNews Markdown: [https://news.hada.io/topic/28356.md](https://news.hada.io/topic/28356.md)
- Type: GN+
- Author: [xguru](https://news.hada.io/@xguru)
- Published: 2026-04-10T01:04:06+09:00
- Updated: 2026-04-10T01:04:06+09:00
- Original source: [werwolv.net](https://werwolv.net/posts/usb_for_sw_devs/)
- Points: 24
- Comments: 1

## Topic Body

- USB 드라이버 개발은 커널 수준의 작업으로 여겨지지만, 실제로는 **소켓 프로그래밍과 유사한 난이도**로 사용자 공간에서도 구현 가능함
- **libusb**를 사용하면 커널 코드를 작성하지 않고도 장치 열거, 제어 전송, 데이터 송수신을 모두 수행할 수 있음
- USB 통신은 **Control, Bulk, Interrupt, Isochronous** 네 가지 전송형과 **IN/OUT 방향**으로 구성되며, 각 엔드포인트는 단방향 통로로 동작함
- Android 기기의 **Fastboot 프로토콜**을 예시로, Bulk 엔드포인트를 통해 명령과 응답을 주고받는 과정을 코드로 시연함
- 사용자 공간에서도 **완전한 USB 드라이버를 구현할 수 있으며**, 모든 USB 프로토콜은 동일한 기본 구조를 공유함

---

### 소개
- USB 장치용 드라이버는 커널 코드를 다뤄야 한다는 인식 때문에 어렵게 느껴지지만, 실제로는 **소켓을 사용하는 애플리케이션 수준의 복잡도**임
- 하드웨어 경험이 많지 않은 개발자도 **사용자 공간에서 USB를 다루는 방법**을 익힐 수 있음
- USB의 세부 동작을 다루는 자료가 존재하지만 초보자에게는 접근이 어려움
- USB 사용에는 **임베디드 시스템 수준의 지식이 필요하지 않으며**, 네트워크 소켓처럼 접근 가능함

### USB 장치
- 예제로 **부트로더 모드의 Android 스마트폰**을 사용
  - 쉽게 구할 수 있고, 프로토콜이 단순하며, OS에 기본 드라이버가 없어 실험에 적합함
- 부트로더 모드 진입은 기기마다 다르며, 일반적으로 전원 버튼과 볼륨 버튼 조합으로 가능함

### 장치 수동 열거
- **열거(Enumeration)** 는 호스트가 장치 정보를 요청해 자신을 식별하는 과정으로, 장치 연결 시 자동 수행됨
- 표준 장치는 **USB 클래스**를 기반으로 드라이버를 자동 로드하고, **벤더 전용 장치**는 `VID`(Vendor ID)와 `PID`(Product ID)를 사용함
- Linux에서는 `lsusb` 명령으로 장치 정보를 확인 가능
  - 예시: `ID 18d1:4ee0 Google Inc. Nexus/Pixel Device (fastboot)`
  - `18d1`은 Google의 **VID**, `4ee0`은 Nexus/Pixel 부트로더의 **PID**
- `lsusb -t` 명령으로 **클래스와 드라이버 상태**를 확인 가능
  - `Class=Vendor Specific Class`, `Driver=[none]`으로 표시되어 OS가 드라이버를 로드하지 않음
- Windows에서는 **Device Manager**나 *USB Device Tree Viewer*로 동일한 정보 확인 가능

### libusb로 장치 열거
- **libusb** 라이브러리를 사용하면 커널 코드를 작성하지 않고 사용자 공간에서 USB 장치와 통신 가능
- `libusb_hotplug_register_callback()`으로 특정 `VID:PID` 조합의 장치가 연결될 때 콜백을 실행하도록 설정
- 프로그램 실행 후 장치 연결 시 `"Device plugged in!"` 메시지 출력
- Linux에서는 기본적으로 작동하며, 필요 시 `libusb_detach_kernel_driver()`로 커널 드라이버를 분리 가능
- Windows에서는 `Winusb.sys` 드라이버가 필요하며, 없을 경우 **Zadig** 도구로 수동 교체 가능

### 장치와의 통신
- USB 장치와의 첫 통신은 **Control 엔드포인트(주소 0x00)** 를 통해 수행
- `libusb_control_transfer()`로 **표준 요청(GET_STATUS)** 을 전송해 장치 상태를 읽음
  - 예시 응답: `01 00` → 첫 바이트는 **Self-Powered**, 두 번째는 **Remote Wakeup 미지원**
- 이후 **GET_DESCRIPTOR** 요청으로 장치 디스크립터를 가져올 수 있음
  - 반환된 데이터에는 `idVendor`, `idProduct`, `bDeviceClass` 등 장치 정보가 포함됨
- `lsusb -v` 명령으로 모든 디스크립터(장치, 구성, 인터페이스, 엔드포인트 등)를 상세히 확인 가능
  - 예시: `Android Fastboot` 인터페이스에 **Bulk IN(0x81)**, **Bulk OUT(0x02)** 엔드포인트 존재

### 엔드포인트
- 엔드포인트는 **네트워크 포트와 유사한 개념**으로, 장치가 데이터를 송수신하는 통로
- 디스크립터에 각 엔드포인트의 종류와 방향이 정의되어 있음
- ## Control 전송형
  - 모든 장치에 하나 존재하며 주소는 항상 `0x00`
  - 초기 설정 및 장치 정보 요청에 사용
  - 인터페이스에 속하지 않고 장치 자체의 일부로 존재
- ## Bulk 전송형
  - **대용량 비실시간 데이터 전송**에 사용
  - 예: Mass Storage, CDC-ACM(시리얼), RNDIS(이더넷)
  - 대역폭은 높지만 우선순위는 낮음
- ## Interrupt 전송형
  - **소량의 저지연 데이터 전송**에 사용
  - 키보드, 마우스 등에서 버튼 입력을 빠르게 폴링
  - 실제 하드웨어 인터럽트는 아니며, 호스트가 주기적으로 요청함
- ## Isochronous 전송형
  - **시간 민감한 대용량 데이터**(오디오, 비디오 스트리밍)에 사용
  - 지연이 발생하면 품질 저하가 즉시 드러남
  - libusb에서는 비동기 방식으로 처리
- ## IN / OUT 방향
  - USB는 **호스트 중심 구조**로, 장치는 요청을 받기 전에는 데이터를 전송하지 않음
  - `IN`: 호스트가 데이터를 받는 방향
  - `OUT`: 호스트가 데이터를 보내는 방향
  - 엔드포인트 주소의 최상위 비트(MSB)가 `1`이면 IN, `0`이면 OUT
  - 최대 127개의 사용자 정의 엔드포인트 사용 가능 (`0x00`은 Control 전용)
  - 엔드포인트는 **단방향**이며, Fastboot 인터페이스처럼 IN/OUT 쌍으로 구성됨

### Fastboot 프로토콜
- **Fastboot**는 Android 부트로더 통신 프로토콜로, 명령 문자열을 보내고 4바이트 상태 코드와 데이터를 받는 구조
  - 예:
    - `Host: "getvar:version"` → `Client: "OKAY0.4"`
    - `Host: "getvar:nonexistant"` → `Client: "OKAY"`
- libusb를 이용해 Fastboot 명령을 전송하는 코드 예시
  - 인터페이스 0을 `libusb_claim_interface()`로 확보
  - `"getvar:version"` 명령을 **Bulk OUT(0x02)** 엔드포인트로 전송
  - **Bulk IN(0x81)** 엔드포인트로 응답 수신
  - 출력 예시:
    ```
    Request: getvar:version
    Response: OKAY0.4
    ```
  - `OKAY`는 성공 상태, `0.4`는 Fastboot 버전

### 마무리
- 커널 코드를 작성하지 않고 **사용자 공간에서 완전한 USB 드라이버를 구현** 가능
- 모든 USB 드라이버는 동일한 기본 원리를 따르며, 프로토콜만 다름
- 복잡한 프로토콜(MTP 등)도 기본 구조는 동일하며, **소켓 통신과 유사한 개념**으로 접근 가능함

## Comments



### Comment 55002

- Author: neo
- Created: 2026-04-10T01:04:07+09:00
- Points: 1

###### [Hacker News 의견들](https://news.ycombinator.com/item?id=47695012) 
- 마침 타이밍이 완벽했음. 곧 **MOTU MIDI Express XT**를 지역 Guitar Center에서 받을 예정임  
  중고 장비라 법적으로 일정 기간 보류해야 해서 기다리는 중임. 문제는 이 장비가 **표준 MIDI-over-USB**가 아니라 독자 프로토콜을 써서, Linux나 OpenBSD, Haiku 같은 내 시스템에서는 USB로 바로 쓸 수 없다는 점임  
  당장은 단순히 신스 모듈과 컨트롤러 간 라우팅만 필요하니 괜찮지만, PC 쪽에서도 작동하게 만들면 좋겠음  
  [기존 Linux 드라이버](https://github.com/vampirefrog/motu)가 있긴 한데, 안정성도 불확실하고 XT 지원 여부도 애매함. 커널 패닉 문제는 해결됐다지만 이슈가 남아 있음  
  그래서 **LibUSB 기반 사용자 공간 드라이버**를 직접 만들어보려 함. MIDI 포트를 노출하고 라우팅 툴링을 추가하면 꽤 유용할 듯함  
  - Guitar Center의 대기 기간은 단순히 도난 여부 확인 때문만은 아님. 법적으로 **전당포(pawn shop)** 처럼 일정 기간 판매 금지 의무가 있어서, 원 소유자가 되찾을 수 있는 기간이 지나야 판매 가능함  
  - 나도 같은 장비를 써서 그 드라이버를 **AUR**에 패키징했음. 바이너리 블롭은 작동 안 했지만, 단순 MIDI 라우터로 쓰기엔 충분함  

- Go 언어로 이런 걸 해보고 싶다면, cgo 없이 USB 접근이 가능한 [go-usb](https://github.com/kevmo314/go-usb) 라이브러리를 만들어둠  
  나는 이걸로 **UVC 장치**를 다루는 [go-uvc](https://github.com/kevmo314/go-uvc)도 개발했음  
  - Rust에서는 [nusb](https://github.com/kevinmehall/nusb)를 추천함  

- 나도 최근 **Macbook M3**에서 usbip 시스템을 비슷한 방식으로 구현 중임  
  다만 최신 macOS에서는 제한이 있음. 시스템이 인식하는 USB 장치에 대해선 **libusb 기반 사용자 공간 드라이버**를 빌드할 수 없고, 보안 기능을 수동으로 꺼야만 가능함  
  - 드라이버 오버라이드는 한 계층만 조정하면 되므로 완화 가능함  

- 이 접근법은 결국 USB 드라이버가 애플리케이션 코드 역할도 하는 셈임. 즉, **드라이버라기보다 라이브러리+프로그램**에 가까움  
  예를 들어 USB-Ethernet 장치를 OS의 네트워크 어댑터로 연결하려면 어떻게 해야 할지 궁금함  
  - 표준화된 장치는 보통 **USB/CDC/ECM**이나 **RNDIS**를 써서 자동 인식됨. 사용자 공간 접근은 오히려 **비표준 장치**에 유용함. Windows에서는 드라이버 서명 없이 libusb로 이식성 있게 구현 가능함  
  - Linux에서는 **tun/tap 장치**를 만들어 사용자 공간에서 커널과 통신하거나, 다른 서브시스템도 사용자 공간에서 돌려야 함  

- 이 글을 **몇 년 전**에 읽었더라면 노트북 기능을 리버스 엔지니어링할 때 훨씬 쉬웠을 것임. 특히 **키보드 LED 제어 프로그램**은 지금도 내가 가장 좋아하는 프로젝트 중 하나임  

- 정말 유용한 입문서였음. **저수준 하드웨어 API**를 다루는 건 어렵지만 보람 있음. 현대 OS의 추상화 계층 덕분에 쉬워졌지만, 그 아래를 이해하는 건 여전히 중요함  

- C++ 코드가 이상해 보였음. 화살표 문자를 직접 입력할 수 있는 키보드는 본 적이 없음  
  - 그건 **프로그래밍 폰트의 합자(ligature)** 임. 복사하면 실제로는 `->`로 보임. 최신 C++의 **trailing return type** 문법임  
  - 일부 개발자는 합자 폰트를 선호함. 두 문자를 하나의 글리프로 합쳐줌  
  - **Compose 키**를 설정하면 어떤 키보드로도 “→” 입력 가능함  
  - 결국 그냥 `"->"`임. 폰트가 그걸 화살표로 렌더링할 뿐임  

- USB 장치가 **DMA**를 지원하는지 궁금했음. 호스트를 통해서만 가능한지, 아니면 장치가 직접 메모리에 접근하는지도 알고 싶음  
  - USB 장치는 PCIe나 FireWire처럼 호스트 메모리에 직접 접근하지 않음. 대신 **XHCI 컨트롤러**가 DMA를 수행하고, 대부분의 장치 컨트롤러는 자체 RAM과 USB 간 DMA를 지원함  
  - 모든 전송은 **호스트가 주도**함. 장치가 먼저 데이터를 보내는 것처럼 보여도 실제로는 호스트가 요청함. 직접 DMA는 보안상 큰 위험 요소가 됨  

- 예전에 간단한 USB 장치를 만들려 했는데, **디스크립터(descriptor)** 작성법에 대한 정보가 거의 없었음. 대부분 “비슷한 장치 찾아서 복사하고 수정해보라”는 식이었음. USB가 정말 훌륭한 표준인지 의문이었음  
  - 나도 디스크립터가 신비로웠는데, 결국 **고정된 바이너리 구조체**라는 걸 깨달음. 각 USB 클래스가 명시한 필드와 엔드포인트만 맞추면 인식됨  
  - USB는 괜찮지만, 전기적 측면에서 USB 1/2는 **진정한 차동 신호가 아님**  
  - 튜토리얼 자료는 거의 없지만, 대기업 표준치고는 꽤 합리적임. 다만 **선택지가 너무 많아** 관련 스펙을 많이 읽어야 함  

- “USB 장치 드라이버를 직접 작성하라”는 요청을 받는다면, 그 장치를 돌려주고 **가상 COM 포트**로 처리할 수 없는지 먼저 확인하겠음
