리눅스 커널 USB 가젯(Gadget) 드라이버 프레임워크 분석

리눅스 시스템에서 USB 장치 모드(Peripheral Mode)를 구현하기 위한 핵심 요소는 USB 가젯(Gadget) 서브시스템입니다. 관련 소스 코드는 주로 커널 내의 drivers/usb/gadget/ 경로에서 관리됩니다.

USB 장치 논리 계층 구조

USB 장치는 호스트와의 통신을 위해 복잡한 계층 구조를 가집니다. 일반적인 구조는 루트(Root)에서 포트(Port), 장치(Device), 그리고 최종적으로 여러 개의 엔드포인트(EP)로 이어집니다. 장치 기술자(Descriptor)의 관점에서 본 구조는 다음과 같습니다.

Device Descriptor
└── Configuration 0
    ├── Interface 0 (e.g., HID 인터페이스)
    │   ├── HID Descriptor
    │   └── Endpoint 1 (IN)
    └── Interface 1 (e.g., Data 인터페이스)
        ├── Endpoint 2 (OUT)
        └── Endpoint 3 (IN)
USB 컨트롤러는 물리적인 대역폭과 엔드포인트 개수에 제한이 있으므로, 설계 시 장치가 사용하는 엔드포인트 총합이 컨트롤러의 사양을 초과하지 않도록 주의해야 합니다. 이를 초과할 경우 열거(Enumeration) 단계에서 실패할 수 있습니다.

가젯 드라이버의 주요 계층

가젯 프레임워크는 유연한 구성을 위해 계층화되어 있습니다.
  • usb/gadget/configfs.c: 유저 스페이스에서 USB 기능을 동적으로 구성하기 위한 ConfigFS 인터페이스.
  • usb/gadget/functions.c: 개별 USB 기능들의 공통 로직 관리.
  • usb/gadget/function/f_*.c: RNDIS, UVC, Storage 등 구체적인 USB 클래스 구현체.

RNDIS 가젯 구현 예시

안드로이드 기기에서 흔히 사용하는 USB 테더링(USB 가상 랜카드)은 RNDIS 기능을 통해 구현됩니다. f_rndis.c에서 드라이버 등록은 다음과 같은 매크로를 통해 이루어집니다.

static struct usb_function_instance *custom_rndis_alloc_inst(void)
{
    struct f_rndis_opts *opts;
    // 인스턴스 초기화 로직
    return &opts->func_inst;
}

static struct usb_function *custom_rndis_alloc_func(struct usb_function_instance *fi)
{
    struct f_rndis *rndis_dev;
    // 기능 할당 및 엔드포인트 설정 로직
    return &rndis_dev->func;
}

DECLARE_USB_FUNCTION_INIT(rndis_mod, custom_rndis_alloc_inst, custom_rndis_alloc_func);
이 매크로는 드라이버 로드 시 usb_function_register를 호출하며, 두 가지 핵심 콜백을 등록합니다.
  1. alloc_inst: USB 인스턴스 초기화 및 ConfigFS 속성 설정.
  2. alloc_func: 엔드포인트 기술자 처리 및 실제 기능 할당.

UVC(USB Video Class) 가젯 아키텍처

UVC 가젯은 f_uvc.c(어댑터 계층)와 uvc_v4l2.c(V4L2 인터페이스 계층)로 나뉩니다. 이 구조는 USB로 들어오는 비디오 데이터를 커널의 V4L2 프레임워크와 연결합니다. f_uvc.cvdev 프라이빗 데이터를 통해 가상 V4L2 장치를 제어하며, V4L2 API를 사용하여 표준 비디오 장치로 등록됩니다.

// V4L2 장치 등록 예시
struct video_device *v_dev = &uvc->vdev;
v_dev->fops = &uvc_v4l2_fops;
video_set_drvdata(v_dev, uvc);
ret = video_register_device(v_dev, VFL_TYPE_VIDEO, -1);
구현 시 uvc_v4l2_fopsuvc_v4l2_ioctl_ops를 통해 호스트의 요청(해상도 변경, 스트리밍 제어 등)을 처리합니다.

엔드포인트 및 대역폭 점검

가젯 기기를 개발할 때 대역폭 소모량과 엔드포인트 할당 상태를 확인하는 것이 중요합니다. usbmon 기반의 usbtop 도구를 사용하거나 디버그 파일 시스템을 조회할 수 있습니다.

# 현재 연결된 장치 및 엔드포인트 상태 확인
cat /sys/kernel/debug/usb/devices
출력 정보 중 B 항목은 할당된 대역폭을 나타내며, Int(인터럽트) 및 Iso(등시성) 전송 엔드포인트 개수를 유심히 살펴봐야 합니다. 컨트롤러마다 주기적 엔드포인트(Periodic Endpoints) 제한이 다르기 때문입니다. 또한, USB 호스트가 SET_INTERFACE 명령을 보내면 드라이버의 set_alt 함수가 호출됩니다. 예를 들어 UAC2(Audio Class) 드라이버에서는 afunc_set_alt 함수를 통해 샘플링 레이트를 변경하거나 재생/녹음 상태를 전환하는 로직을 수행합니다.

태그: linux-kernel usb-gadget driver-development v4l2 rndis

6월 7일 00:24에 게시됨