Chapter 02

HCI: 호스트와 컨트롤러 사이의 바이트

휴대폰의 블루투스는 두 칩, 혹은 한 칩 안의 두 세계다. 운영체제 쪽 호스트와 무선 쪽 컨트롤러(1권 1장). 둘 사이에 오가는 모든 것이 HCI 패킷이고, 안드로이드의 btsnoop 로그나 리눅스의 btmon이 보여 주는 것도 이것이다. 이 장은 그 바이트를 읽는 법을 익힌다. 사양 6장 암호화 샘플(이 책 6장)에 실린 실제 HCI 바이트를 해독기로 하나씩 풀어 본다.

패킷 다섯 가지와 표시 바이트

HCI 패킷은 다섯 가지다. 호스트가 보내는 명령, 컨트롤러가 보내는 이벤트, 양방향의 ACL 데이터(L2CAP이 실린다), 클래식 음성용 동기 데이터, LE 오디오용 ISO 데이터. 모든 값은 작은 바이트부터(리틀 엔디언)다. UART 하나로 다섯 가지를 섞어 보내려면 패킷 앞에 1바이트 표시 바이트를 붙인다. 흔히 “H4”라 부르는 방식이고, btsnoop 로그도 이 표시 바이트를 기록한다.

패킷표시머리말최대 길이
명령0x01opcode 2바이트(OGF 6비트 ‖ OCF 10비트) + 길이 1바이트매개변수 255바이트
ACL 데이터0x02핸들 12비트 + PB 2비트 + BC 2비트, 길이 2바이트컨트롤러 버퍼 크기(LE는 최소 27)
동기 데이터0x03핸들 12비트 + 상태 비트, 길이 1바이트—
이벤트0x04이벤트 코드 1바이트 + 길이 1바이트매개변수 255바이트
ISO 데이터0x05핸들 12비트 + PB 2비트 + TS 1비트, 길이 14비트ISO 버퍼 크기

명령의 opcode는 위 6비트 OGF(명령 묶음)와 아래 10비트 OCF로 나뉜다. LE 명령은 모두 OGF 0x08이라 opcode가 0x20xx이고, 컨트롤러·기저대역 명령(OGF 0x03)은 0x0Cxx, 정보 명령(OGF 0x04)은 0x10xx다. 예를 들어 HCI_Reset = 0x0C03, HCI_LE_Encrypt = 0x2017. LE 이벤트는 모두 코드 0x3E(LE Meta) 하나에 첫 바이트 서브이벤트 코드로 구별한다.

사장과 비서의 메모지

호스트는 사장, 컨트롤러는 무전기를 쥔 비서다. 사장은 정해진 양식의 지시 메모(명령)를 건네고, 비서는 “받았습니다”(Command Status)나 “다 했습니다, 결과는 이렇습니다”(Command Complete)로 답하며, 무전으로 일어난 일은 따로 보고 메모(이벤트)로 올린다. 보낼 편지(ACL 데이터)는 비서의 책상 서랍 칸 수만큼만 맡길 수 있고, 비서가 “두 칸 비었습니다”(Number Of Completed Packets)라고 알려야 더 맡긴다.

UART 표시 바이트(Table 2.1): [Vol 4] Part A §2 · 명령 패킷과 OGF·OCF: [Vol 4] Part E §5.4.1 · ACL: §5.4.2 · 이벤트: §5.4.4 · ISO: §5.4.5 · 리틀 엔디언: §5.2

해독기: 사양 샘플의 HCI 바이트

사양 [Vol 6] Part C §1은 암호화 시작 과정을 HCI 바이트까지 적어 두었다. 아래 해독기의 기본 입력은 그 바이트에 H4 표시와 머리말을 붙인 것이다. 센트럴 호스트가 LE_Enable_Encryption을 보내고, 페리페럴 컨트롤러가 LTK를 요청하고, 페리페럴 호스트가 LTK를 돌려주고, HCI_LE_Encrypt로 세션 키를 확인한 뒤, ACL 데이터를 보낸다. 자기 로그의 바이트를 붙여 넣어도 된다(공백·줄바꿈 무시, # 뒤는 주석).

WORKBENCH

HCI(H4) 바이트 해독기

패킷 수—
사양 주석과 비교—
해독기는 표시 바이트 → 머리말의 길이 → 다음 패킷 순서로 자른다. 매개변수를 아는 명령·이벤트(이 장과 6장에 나오는 것)는 칸별로 풀고, 모르는 것은 이름과 바이트만 보인다. 해볼 것: ① 첫 패킷의 opcode 19 20 → 0x2019 → OGF 0x08, OCF 0x019. ② LTK 매개변수가 리틀 엔디언이라 사양의 “MSO to LSO” 값과 순서가 거꾸로다. ③ “잘라 보기”를 누르면 마지막 패킷의 길이가 모자라 해독이 멈춘다. UART에서 바이트 하나를 잃으면 그 뒤 전부가 어긋나는 이유다.

샘플 HCI 바이트와 필드 주석: [Vol 6] Part C §1·§1.1 · 명령: LE_Enable_Encryption [Vol 4] Part E §7.8.24 · LE_Long_Term_Key_Request_Reply §7.8.25 · LE_Encrypt §7.8.22 · 이벤트: LE_Long_Term_Key_Request §7.7.65.5 · Command_Complete §7.7.14

크레딧: 컨트롤러가 받을 수 있는 만큼만

컨트롤러의 메모리는 작다. 그래서 HCI는 두 가지 크레딧으로 넘치지 않게 한다.

SIMULATOR

버퍼 크레딧과 처리량

연결 이벤트당 실제 전송—
무선이 놀고 있는 비율—
호스트가 크레딧을 기다린 비율—
그래프: 연결 이벤트마다 컨트롤러가 보낸 패킷(막대)과 그때 호스트가 가진 크레딧(선). 호스트는 보낼 데이터가 늘 있다고 가정한다. 모델: 이벤트 시작 전 호스트가 크레딧만큼 버퍼를 채우고, 이벤트 동안 최대 n개를 보내며, 비운 버퍼는 정해진 주기의 Completed Packets 이벤트 때 한꺼번에 돌려받는다. 해볼 것: ① 버퍼 4, 이벤트당 4, 보고 1이면 매 이벤트 4개. ② 보고를 3 이벤트마다로 늦추면 버퍼가 적을 때 처리량이 3분의 1 가까이로 떨어진다. ③ 같은 상황에서 버퍼를 12로 늘리면 회복된다. 컨트롤러 보고 주기와 버퍼 수는 칩마다 다르다(사양은 “주기적으로”만 요구).
예측해 보기

호스트가 연결 하나에 크레딧을 모두 써 버렸는데 그 상대가 멀어져 패킷이 계속 재전송된다. 다른 연결(예: 키보드)의 데이터는?

LE ACL 버퍼는 연결마다 따로인가, 함께 쓰는가?

Total_Num_LE_ACL_Data_Packets는 모든 LE 연결이 함께 쓰는 개수다. 그래서 호스트 스택은 연결마다 크레딧을 나눠 주는 정책을 따로 둔다(한 연결이 전부 차지하지 못하게). 연결이 끊기면 그 연결이 쥐고 있던 버퍼는 모두 반환된 것으로 본다.

명령 흐름 제어: [Vol 4] Part E §4.4 · 데이터 흐름 제어·LE_Read_Buffer_Size를 먼저: §4.1·§4.1.1·§7.8.2 · Number_Of_Completed_Packets: §7.7.19 · 연결 끊김 때 버퍼: §4.3

이벤트 마스크, 그리고 비트가 모자랄 때

컨트롤러가 모든 이벤트를 다 올리면 호스트가 모르는 이벤트까지 쏟아진다. 그래서 호스트는 Set_Event_Mask(일반 이벤트)와 LE_Set_Event_Mask(LE 서브이벤트)로 받을 것만 고른다. 비트 하나가 이벤트 하나다. 같은 식으로 컨트롤러는 Read_Local_Supported_Commands로 자기가 아는 명령을 비트로 알려 준다.

문제는 비트 수가 고정이라는 것이다. LE 이벤트 마스크는 8바이트(64개), 지원 명령은 64바이트(512개)였는데, 기능이 늘며 거의 다 찼다. 6.3의 “Running Out of Bits”는 같은 일을 하는 [v2] 명령을 새 opcode로 만들어 길이를 늘렸다. LE_Set_Event_Mask [v2](0x20A4)는 255바이트, Read_Local_Supported_Commands [v2](0x1010)는 251바이트다. 한 명령에 여러 버전이 있으면 컨트롤러는 지원하고 켜져 있는 가장 새 버전의 이벤트를 쓴다.

명령[v1][v2] (6.3)
LE_Set_Event_Mask0x2001, 8옥텟0x20A4, 255옥텟
Read_Local_Supported_Commands0x1002, 64옥텟0x1010, 251옥텟

흔한 초기화 순서

사양이 순서를 하나로 정하지는 않지만, 공개 스택들은 대체로 이렇게 시작한다. HCI_Reset → Read_Local_Version_Information(코어 버전) → Read_Local_Supported_Commands → LE_Read_Local_Supported_Features → Set_Event_Mask·LE_Set_Event_Mask → LE_Read_Buffer_Size(데이터를 보내기 전 필수) → Read_BD_ADDR. 해독기의 “흔한 초기화 순서”가 이 흐름의 바이트다. 리셋 직후 명령을 하나씩만 보내는 것도 위의 명령 크레딧 규칙 때문이다.

Set_Event_Mask: [Vol 4] Part E §7.3.1 · LE_Set_Event_Mask [v1]·[v2]: §7.8.1 · Read_Local_Supported_Commands [v1]·[v2]: §7.4.2 · 버전 있는 이벤트: §4.8 · 6.3 새 기능: [Vol 1] Part C §17.1 · 버전 정보: [Vol 4] Part E §7.4.1

핵심 정리

  1. HCI 패킷 다섯 가지: 명령 01, ACL 02, 동기 03, 이벤트 04, ISO 05(UART 표시 바이트). 값은 모두 리틀 엔디언.
  2. opcode = OGF 6비트 ‖ OCF 10비트. LE 명령은 0x20xx, LE 이벤트는 0x3E + 서브이벤트 코드.
  3. 명령 크레딧(Num_HCI_Command_Packets), 데이터 크레딧(LE_Read_Buffer_Size → Number_Of_Completed_Packets). 크레딧 보고가 늦으면 무선이 비어도 호스트가 멈춘다.
  4. LE ACL 버퍼는 연결들이 함께 쓴다. 한 연결이 크레딧을 붙잡으면 다른 연결도 막힌다.
  5. 6.3 Running Out of Bits: [v2] 명령으로 LE 이벤트 마스크 8 → 255옥텟, 지원 명령 64 → 251옥텟.

확인 퀴즈

opcode 0x0C03의 OGF와 OCF는?

0x0C03 = 0000 1100 0000 0011. 위 6비트 000011 = 0x03(컨트롤러·기저대역), 아래 10비트 = 0x003(Reset).

H4 바이트열이 “04 0E 04 01 03 0C 00”이다. 무엇인가?

04 = 이벤트, 0E = Command Complete, 길이 4, Num_HCI_Command_Packets 1, opcode 03 0C(0x0C03, 리틀 엔디언), 상태 00.

호스트가 LE 데이터를 보내기 전에 반드시 해야 하는 일은?

[Vol 4] Part E §7.8.2: “shall be issued by the Host before it sends any data to an LE Controller.”

LE_Set_Event_Mask [v2]의 마스크 길이는?

[v1]은 8옥텟, 6.3의 [v2]는 255옥텟. 지원 명령 비트는 [v2]에서 251옥텟.