HID 리포트 디스크립터의 바이트 단위 분석, Usage Page 구성, 부트 프로토콜과 리포트 프로토콜의 차이, 그리고 리딩펜의 OEM 커스터마이즈 규칙을 설명합니다.

게시일: 8월 16, 2024 - 38 조회수

리딩펜 HID 리포트 디스크립터와 Usage Page: OID 탭을 위한 바이트 단위 맵

대상 독자: 임베디드 펌웨어 엔지니어, OS 드라이버 통합 엔지니어, OEM 제품 매니저로서, 리딩펜 HID 리포트 디스크립터의 각 바이트가 실제로 무엇을 의미하는지, Usage Page가 왜 일반 데스크톱 페이지와 벤더 정의 페이지로 분할되어 있는지, 그리고 현장에 이미 배포된 230개 이상의 유통사급 드라이버를 손상시키지 않으면서 맵을 확장하는 방법을 이해해야 하는 분들.

HID 리포트 디스크립터는 리딩펜 펌웨어에서 가장 중요한 단 80~200 바이트입니다. 이는 펌웨어가 탑재된 MCU와 향후 연결될 모든 호스트 운영체제(Windows 10/11, macOS 12/13/14, ChromeOS, Android 9~14, iPadOS 15~17, Linux Kernel 5.15+) 간의 계약입니다. 디스크립터에 오류가 발생하면 펜이 잘못된 문자를 입력하거나 OID 탭을 무시하고, 더 심각한 경우 오디오가 잘못된 싱크로 전송될 수 있습니다. 당사가 출하한 47,000대 이상의 제품군에서 디스크립터 회귀는 OCR 엔진을 제외한 다른 모든 펌웨어 서브시스템보다 더 많은 디버깅 시간을 소모시켰습니다.

본 글에서는 TC-057의 표준 디스크립터를 바이트 단위로 살펴보고, 각 필드가 왜 이러한 값으로 설정되었는지 설명하며, ODM 브랜드가 맵을 안전하게 확장할 수 있는 부분과 확장할 수 없는 부분을 제시합니다.

1. HID 계약의 세 가지 계층

HID 리포트 디스크립터는 세 가지 논리적 계층으로 파싱됩니다.

계층 세분성 목적 길이
항목 Short(1바이트) 또는 Long(3바이트) 태그 + 크기 + 데이터 가변
로컬 항목별로 정의됨 Usage, Usage Minimum/Maximum, 지정자(Designator) 가변
메인 입력/출력/피처 배열을 감쌈 Collection, End Collection, Input, Output, Feature 가변

파서는 상태 기반(Stateful)입니다. Collection은 새로운 스코프를 시작하며, 그 스코프 내의 Input/Output/Feature 항목들은 하나의 리포트를 구성합니다. 디스크립터는 왼쪽에서 오른쪽으로 순차적으로 처리되며, 모든 바이트는 올바른 형식이어야 합니다. 단 하나의 잘못된 바이트만 있어도 호스트는 전체 디스크립터를 거부합니다.

당사 Q6/Q8 디스크립터는 92바이트이며, F8 디스크립터는 76바이트입니다. T2/Y6(헤드폰 재생용 컨슈머 제어 완전 지원)은 168바이트입니다. 디스크립터 길이는 열거 시점에 HID 디스크립터에 인코딩되며 호스트는 이를 캐싱합니다. 향후 펌웨어 개정을 통해 디스크립터를 변경하는 경우에도 호스트가 캐시된 버전을 계속 사용할 수 있으므로, 펌웨어 업데이트 시에는 항상 USB 버스 리셋을 강제 수행합니다(TC-075 참조).

2. 리딩펜이 사용하는 아이템 태그

HID 사양은 태그 테이블을 정의하며, 리딩펜은 이 중 12개만 사용합니다.

태그 니모닉 역할 Q6/Q8 사용 여부
0x05 Usage Page 페이지를 전환 예(3회 사용)
0x09 Usage 하나의 Usage를 선택합니다 예(4회 사용)
0x15 Logical Minimum 입력/출력 범위 설정 예(3회 사용)
0x25 Logical Maximum 입력/출력 범위 설정 예(3회 사용)
0x75 Report Size 요소당 비트 수 예(3회 사용)
0x95 Report Count 요소(Element) 수 예(3회 사용)
0xA1 Collection 스코프 열기 예(2회 사용)
0x81 Input 입력 리포트 필드를 정의합니다 예 (3회)
0x91 Output 출력 리포트 필드를 정의합니다 예 (1회)
0x19 Usage Minimum 범위 시작 예 (1회)
0x29 Usage Maximum 범위 종료 예 (1회)
0xC0 End Collection 범위를 닫습니다 예 (2회)

우리는 의도적으로 이 12개 태그의 하위 집합 내에서만 작업합니다. HID 사양은 더 많은 태그를 허용하지만, 태그를 추가할수록 OS별 특이 동작이 발생할 가능성이 커집니다. 예를 들어 Padding (0x5A) 태그는 기술적으로는 합법적이지만, Linux 커널의 hid-core.c는 suspend/resume 중 일부 Padding 시퀀스를 거부합니다.

3. 세 가지 Usage Page

Q6/Q8 디스크립터는 세 가지 Usage Page를 사용합니다:

Usage Page 16진수(Hex) 용도
Generic Desktop 0x01 키보드, 마우스, 시스템 컨트롤
Consumer 0x0C 볼륨, 재생/일시정지, 트랙 이동
Vendor Defined 0xFF00 OID 이벤트 페이로드 (커스텀 브랜드 확장)

Generic Desktop 페이지는 출고하는 키보드 컬렉션입니다. 1바이트 수정 키(8비트, 1바이트 리포트 크기, 8 리포트 개수)와 1바이트 예약 상수(8비트, 1 리포트 크기, 1 개수)를 선언합니다. 이 둘이 합쳐져 부트 키보드 리포트 헤더를 구성합니다.

Consumer 페이지는 헤드폰 재생용으로 출고하는 볼륨 업 / 볼륨 다운 / 재생-일시정지 컨트롤입니다. 총 3비트입니다.

Vendor Defined 페이지 0xFF00은 OID 이벤트 페이로드입니다: 리포트당 16바이트, Report ID 없음, Output 리포트에 위치하여 필요한 경우 호스트가 펜에 16바이트 컨텍스트를 푸시할 수도 있습니다.

4. Q6/Q8 펌웨어 3.3.1의 바이트 맵

아래는 Q6/Q8 플랫폼의 표준 디스크립터를 바이트 인덱스로 나타낸 것입니다:

Byte 16진수(Hex) 의미 이유
0 0x05 Usage Page 태그(Global) 파서에게 페이지 전환을 알림
1 0x01 Page 0x01 (Generic Desktop) 키보드 범위를 열기
2 0x09 Usage 태그(Local) 하나의 Usage 선택
3 0x06 키보드 부트 프로토콜에 필수
4 0xA1 컬렉션 태그 (Main) 애플리케이션 컬렉션을 엽니다
5 0x01 애플리케이션 애플리케이션은 표준 래퍼입니다
6 0x05 Usage Page 태그
7 0x07 키보드 페이지 수정 키는 여기에 있습니다
8 0x19 Usage Minimum
9 0xE0 Left Control = 224 수정 키 범위 224–231
10 0x29 Usage Maximum
11 0xE7 Right GUI = 231 수정 키 범위를 닫습니다
12 0x15 Logical Minimum
13 0x00 0 Boolean false
14 0x25 Logical Maximum
15 0x01 1 Boolean true
16 0x75 Report Size
17 0x01 요소당 1비트 Modifier는 비트필드입니다
18 0x95 Report Count
19 0x08 8개의 요소 수정 키당 1비트
20 0x81 Input 태그(Main)
21 0x02 데이터, 변수, 절대값 표준 부팅 키보드 수정자
22 0x95 리포트 개수
23 0x01 1개 요소 예약 바이트
24 0x75 리포트 크기
25 0x08 8비트 예약된 1바이트
26 0x81 Input 태그
27 0x03 상수, 변수, 절대값 OS에서 기대하는 예약 바이트
28 0x05 Usage Page 태그
29 0x0C Consumer 페이지 미디어 키용
30 0x09 Usage
31 0xE9 볼륨 업
32 0x09 Usage
33 0xEA 볼륨 다운
34 0x09 Usage
35 0xCD 재생/일시정지
36 0x15 논리 최소값
37 0x00 0
38 0x25 논리 최대값
39 0x01 1
40 0x75 Report Size
41 0x01 1비트
42 0x95 Report Count
43 0x03 3개 요소(Elements) 3개 미디어 비트
44 0x81 Input 태그
45 0x02 Data, Variable, Absolute
46 0xC0 End Collection 키보드 앱을 닫습니다
47 0x06 Usage Page 태그(확장) 2바이트 Usage Page
48 0xFF, 0x00 Page 0xFF00 Vendor defined
49 0x09 Usage
50 0x01 OID Event 커스텀 Usage
51 0xA1 Collection
52 0x01 Application
53 0x15 Logical Minimum
54 0x00 0
55 0x26 Logical Maximum(확장) 2바이트 Logical Max
56 0xFF, 0x00 255
57 0x75 Report Size
58 0x08 8비트
59 0x95 Report Count
60 0x10 16개 요소(Elements) 16바이트 OID 페이로드
61 0x09 Usage
62 0x02 OID Payload
63 0x91 Output 태그
64 0x02 Data, Variable, Absolute
65 0xC0 End Collection Vendor Collection을 닫습니다

Q6/Q8의 나머지 바이트(66~91)는 바이트 정렬을 위한 패딩과 바이트 ID용 Feature Report입니다.

5. Boot 프로토콜 vs Report 프로토콜

리딩펜은 Protocol Mode 특성(UUID 0x2A4E on BLE, bInterfaceProtocol byte on USB)을 제공합니다. 두 가지 프로토콜이 유효합니다:

프로토콜(Protocol) Report 레이아웃 사용 시점
Boot 8바이트 고정(modifier + reserved + 6 keycodes) BIOS, 복구, 초기 부팅
Report Descriptor-defined 일반 OS 사용 시

Q6/Q8 펌웨어 3.3.1에서는 기본값이 Report Protocol입니다. 호스트는 Protocol Mode 특성에 0x00을 기록하여 Boot Protocol을 요청할 수 있으며, 펌웨어는 16바이트 OID Report를 8바이트 Boot 포맷으로 축소하여 처리합니다. 데이터 손실은 없으며, OID Page ID와 코드는 단순히 키코드 바이트에 접혀 들어갑니다.

12,000대 규모의 학교 프로그램에서는 표준 HID 드라이버 스택이 1.4초 만에 로드되고 완전한 Report Protocol 파서는 4.8초가 걸리는 잠금된 환경의 Windows 이미지를 학교 노트북이 사용하므로, 기본적으로 Boot Protocol을 활성화할 것을 권장했습니다. 수업 시작 시 30명의 학생이 동시에 콜드부팅하는 상황에서 지연 시간 절감은 의미 있는 차이를 만듭니다.

6. Vendor-Defined Page 0xFF00: 예약한 이유

우리는 2022년에 USB-IF Vendor 목록에서 0xFF00을 예약했습니다. 이 예약은 해당 Page에서 0x01부터 0xFFFF까지의 모든 Usage ID를 정의할 수 있는 법적 권한을 부여하며, 당사의 펜과 상호 운용하려는 모든 제3자는 당사와 Usage ID 범위를 협의해야 합니다. ODM 고객은 자사 브랜드 ID에 32개 Usage의 하위 범위를 할당받으며, 그 중 처음 8개는 당사의 내부 사용용으로 예약되어 브랜드에는 24개의 Usage가 남습니다.

동일한 Q6/Q8 펌웨어가 230개 이상의 유통업체에 출하될 때 각 앱이 다른 유통업체의 펜과 실수로 통신하지 않도록 하는 것이 이러한 방식입니다.

7. OID Report 레이아웃

16바이트 OID 페이로드 Report는 다음과 같이 구성됩니다:

Byte 필드(Field) 인코딩(Encoding) 범위(Range)
0–3 Page ID uint32 LE 0 to 4,294,967,295
4–7 OID Code uint32 LE 0 to 4,294,967,295
8 언어 uint8 0–255 (언어 팩 참조)
9–10 시퀀스 uint16 LE 0–65,535
11–12 체류 시간(ms) uint16 LE 0–65,535
13 플래그 uint8 비트마스크 bit 0 = repeat, bit 1 = audio busy
14–15 예약 uint16 LE 0

플래그 바이트는 고객 브랜드가 재정의할 수 있는 유일한 항목입니다. 비트 2–7은 브랜드별 플래그(예: "테스트 모드", "진단", "공장 현장 QA")용으로 할당됩니다. 비트 0–1은 당사 펌웨어용으로 예약되어 있습니다.

8. Feature Report (Brand ID)

디스크립터 끝에 있는 4바이트 Feature Report가 브랜드 ID를 보유합니다. Feature Report는 호스트에서 읽기 전용이며, 공장 프로비저닝 중에 설정됩니다. 이는 두 가지 목적을 수행합니다:

  1. 컴패니언 앱은 브랜드 ID를 읽어 어느 콘텐츠 카탈로그를 로드할지 결정합니다.
  2. QA 스테이션은 브랜드 ID를 읽어 펌웨어 로드 무결성을 검증합니다.
0x09, 0x03,        // Usage (Brand ID)
0x15, 0x00,        // Logical Minimum 0
0x26, 0xFF, 0x00,  // Logical Maximum 255
0x75, 0x08,        // Report Size 8
0x95, 0x04,        // Report Count 4
0xB1, 0x02,        // Feature (Data, Variable, Absolute)

첫 번째 바이트는 브랜드 인덱스(현재 38개의 브랜드 인덱스가 할당됨), 두 번째는 지역 코드(1=EU, 2=US, 3=APAC, 4=MENA, 5=LATAM, 6=SSA), 세 번째는 콘텐츠 리비전(0–255), 네 번째는 처음 세 바이트에 대한 CRC입니다.

9. 디스크립터가 GATT에 매핑되는 방식

펜이 BLE에 있을 때, 동일한 디스크립터 바이트는 특성 0x2A4B(Report Descriptor)에서 제공됩니다. 리포트 자체는 알림을 통해 특성 0x2A4D에서 전송됩니다. 프로토콜 모드는 0x2A4E에 있습니다. 브랜드 ID Feature Report는 동일 HID 서비스의 벤더 정의 특성 0x9F01(읽기 전용)에 노출됩니다.

USB HID Item GATT Characteristic UUID 속성
Report Descriptor 0x2A4B Read
Report 0x2A4D Notify, Write, WriteWithoutResponse
Protocol Mode 0x2A4E Read, Write
Brand ID 0x9F01 (custom) Read
HID Information 0x2A4A Read

이것은 TC-060에 문서화된 GATT 테이블입니다.

10. 모든 디스크립터 빌드에서 실행하는 검증 스위트

디스크립터가 출하되기 전에 다음을 통과해야 합니다:

검사 도구 합격 기준
파싱 적합성 USB-IF HID 디스크립터 뷰어 오류 0건, 경고 0건
부트 프로토콜 부트 가능성 USBlyzer 부트 리포트를 성공적으로 수정
OS 열거 Windows 10/11 장치 관리자 HID 키보드 + HID 벤더 디바이스로 목록 표시
iPadOS 페어링 iPad 9세대, iPadOS 17 페어링 및 디스크립터 읽기 성공
ChromeOS 열거(Enumeration) Lenovo Chromebook 14 입력 디바이스로 목록 표시
macOS 열거(Enumeration) MacBook Air M1 시스템 정보에 3개 인터페이스 모두 표시
Android 열거(Enumeration) Pixel 7a, Android 14 InputDeviceManager reports keyboard + vendor

이 중 하나라도 실패하면 디스크립터는 거부되고 펌웨어 빌드는 보류됩니다. 2023년 이후 86개의 펌웨어 릴리스 가운데 6개가 이 게이트에서 거부되어 롤백되었습니다. 가장 흔한 원인은 확장 길이 인코딩이 잘못된 Usage Page 태그로, macOS 검증에서는 실패하지만 Windows에서는 통과합니다.

11. 고객이 자주 하는 실수

ODM 고객이 자체 디스크립터 초안을 가져올 때, 거부 사례의 약 80%는 다음 세 가지 실수에서 비롯됩니다.

  1. Modifier 뒤의 예약 바이트를 누락하는 경우. macOS는 부트 키보드 리포트를 정확히 9바이트(8 modifier + 1 예약 + 6 키코드) 기대합니다. 고객이 이를 8바이트로 압축하면 Mac의 부트 프로토콜이 동작하지 않습니다.
  2. 사용 Logical Maximum 0x00FF을(를) 확장 태그 없이 사용하는 경우.단축(Short) 형식0x25 0xFF은(는) 최대 127 값까지는 동작하지만, 255까지의 값을 위해서는 확장 태그가 필수입니다. 고객이 이를 잊으면 디스크립터가 Linux에서 조용히 잘립니다.
  3. Output 컬렉션 내부에 Input 리포트를 선언하는 경우. HID 파서는 Output 전용 컬렉션을 엄격히 강제하지 않지만 일부 OS 드라이버는 그렇게 합니다. 저희는 항상 OID 컬렉션에서 Output을 선언하여 대칭성을 명시적으로 표현합니다.

12. 성능 예산

호스트 측에서는 열거 시점에 디스크립터가 한 번 파싱됩니다. 디바이스 측에서는 모든 리포트 어셈블리가 디스크립터 맵에 접근합니다. Q6/Q8 펌웨어 3.3.1의 내부 벤치마크 기준:

Operation Cycles 64 MHz에서의 시간
Usage Page 조회 8 125 ns
Usage ID 조회 12 188 ns
16바이트 OID 리포트 어셈블 240 3.75 µs
USB EP1 IN으로 리포트 전송 320 5.0 µs
BLE 0x2A4D로 리포트 전송 1,840 28.75 µs
탭당 총합 (USB) 580 9.1 µs
탭당 총합 (BLE) 2,100 32.8 µs

9.1 µs USB 수치가 6.4 ms 엔드 투 엔드 레이턴시를 가능하게 하는 핵심입니다. BLE 수치는 32 ms BLE 레이턴시의 가장 큰 구성 요소입니다.

Descriptor Build Time (ms)

13. 더 큰 그림

HID 리포트 디스크립터를 공개적으로 공개하지 않는 펜을 평가 중이라면, 그것이 가장 먼저 요청해야 할 사항입니다. 디스크립터 공유를 거부하는 공급업체는 비준수 사항을 숨기고 있거나, 귀사 측에서 드라이버 작업이 필요한 비 HID 스택을 기반으로 구축하고 있는 경우입니다. Q6/Q8 펌웨어 3.3.1의 디스크립터는 92바이트이며, 자격을 갖춘 ODM 고객에게 요청 시 NDA 체결 하에 공개하고 있습니다.

15. 관련 자료


견적 요청 — 빠른 공장 회신을 받으세요

Frequently Asked Questions

Usage Page 0xFF00를 자체 페이지 번호로 교체할 수 있나요?
가능하지만, USB-IF에 벤더 Usage Page를 등록해야 하며 이때 일회성 비용 2,000달러와 6주의 리드 타임이 발생합니다. 당사는 이미 0xFF00을 등록하여 출고하고 있습니다. 0xFF00을 사용하는 브랜드는 기존 230개 이상의 유통사 앱이 사용하는 규약을 그대로 활용할 수 있습니다.
Q6/Q8에서는 디스크립터가 92바이트이고 F8에서는 76바이트인 이유는 무엇인가요?
Q6/Q8에는 Brand ID용 4바이트 Feature 리포트와 OID 페이로드용 12바이트 Output 컬렉션이 추가되어 디스크립터가 92바이트입니다. F8는 BLE 전용이며 Brand ID Feature 리포트 없이 출고되므로 디스크립터가 76바이트입니다.
펌웨어 버전마다 디스크립터가 변경되나요?
아니요, 디스크립터는 플랫폼 수명 동안 고정됩니다. 2023년 이후 86회의 펌웨어 릴리스 동안 Q6/Q8 디스크립터는 변경되지 않았습니다. 새로운 필드가 필요한 경우 새 플랫폼(예: T2/Y6)을 출시합니다.
호스트에 오래된 디스크립터가 캐시되면 어떻게 되나요?
Windows에서는 캐시가 레지스트리의 HIDClass 아래에 저장되며 USB 버스 리셋으로 무효화됩니다. 펌웨어 업데이트 시 2초 동안 USB 서스펜드/리스 사이클을 강제 실행하여 호스트 캐시를 초기화합니다.
브랜드 앱에서 OID 페이로드를 16바이트 이상으로 확장할 수 있나요?
규약을 깨지 않는 한 불가능합니다. 16바이트 레이아웃은 고정되어 있습니다. 더 큰 페이로드가 필요한 경우 Report ID 2를 사용하는 두 번째 벤더 컬렉션에 32바이트 확장 페이로드를 담아 제공하지만, 앱 SDK 업그레이드가 필요하며 프리미엄 브랜드 등급에만 제공됩니다.
장바구니
이 사이트에서 쿠키를 허용하시면 더 나은 경험을 하실 수 있습니다. 쿠키 정책
Chat on WhatsApp