Zephyr v3.7.0 · Bluetooth LE 구조

무선 요청은 어느 경계에서 바뀌는가

애플리케이션의 Bluetooth 요청은 Host를 거쳐 HCI 명령이나 데이터가 되고, Controller는 실제 무선 링크의 시간과 전송을 맡습니다. 세 층을 한 칩에 넣을 수도, Host와 Controller를 서로 다른 칩에 둘 수도 있습니다.

본문 기준: Zephyr 3.7.0 LTS3 · 이후 유지보수 패치는 별도입니다.

이 글은 Zephyr v3.7.0의 공식 문서와 commit 36940db938a8f4a1e919496793ed439850a221c2에 고정한다. 범위는 LE Host·HCI·Controller의 구조와 이미지 배치다. GATT 서비스 설계, 프로파일별 API, pairing 절차와 무선 성능 수치는 여기서 다루지 않는다. 그림은 원본을 복제하지 않고 설명을 위해 새로 그렸다.

1. Host는 프로토콜을, Controller는 무선 시각을 맡는다

Zephyr의 Bluetooth LE Host는 애플리케이션에 API를 제공하고 GAP 역할, 연결과 상위 프로토콜의 상태를 관리한다. Controller는 Link Layer와 무선 하드웨어 쪽에서 패킷의 송수신 및 링크 절차를 수행한다. 두 층 사이의 HCI는 명령·이벤트·데이터의 형식과 교환 경계다. HCI 자체를 UART 한 가닥과 동의어로 읽으면 한 칩 안에서 Host와 Controller가 함께 동작하는 구성을 설명할 수 없다.

그림 1. API 요청이 공중파 패킷이 되기까지의 책임 경계

↔ 좁은 화면에서는 구조도를 가로로 이동해 보세요.

애플리케이션의 Bluetooth API가 Host에서 처리되고 HCI 경계의 명령·이벤트·ACL 데이터로 Controller와 교환되며 Controller가 Link Layer와 Radio를 다루는 구조
표 1. Host·HCI·Controller의 역할
층여기서 결정하는 것여기서 결정하지 않는 것3.7.0 확인 위치
애플리케이션·Host광고·스캔·연결 같은 요청과 Host 상태Radio의 슬롯별 송수신 시각subsys/bluetooth/host/
HCI 경계명령·이벤트·ACL 데이터의 교환 형식과 드라이버 전달상위 애플리케이션 정책이나 실제 전파hci_core.c, HCI 드라이버 API
Controller·RadioLink Layer 절차와 무선 패킷 처리애플리케이션의 GATT 의미subsys/bluetooth/controller/

기존 Networking 글의 net_if·net_pkt·소켓 경로와 이 글의 BLE Host/Controller 경로는 분석 대상이 다르다. 같은 net_buf 타입을 볼 수 있어도 그것만으로 IP 패킷 경로와 HCI 경계를 합쳐서는 안 된다.

근거: 공식 3.7.0 Stack Architecture, LE Host, LE Controller.

2. 한 칩의 경계와 두 칩의 케이블은 같은 개념이 아니다

단일칩 결합 이미지에는 애플리케이션, Host, Controller가 함께 들어간다. 공식 구조 문서는 이때 Host–Controller 사이가 RAM의 함수 호출과 큐로 연결될 수 있다고 설명한다. 듀얼칩에서는 Host 이미지와 Controller 전용 이미지가 서로 다른 칩에서 돌고, HCI가 UART·SPI·USB 같은 전송을 가로지를 수 있다. 그림의 UART는 가능한 한 예시이며 모든 보드의 필수 배선은 아니다.

그림 2. 결합 이미지와 분리 이미지의 배치

↔ 좁은 화면에서는 두 배치를 가로로 비교해 보세요.

위: 한 칩의 앱·Host·Controller·Radio. 아래: Host 칩과 Controller 칩이 HCI 전송 링크로 분리된 구조. HCI는 두 배치 모두의 논리 경계
표 2. 칩 배치별 역할과 HCI 경계
이미지포함하는 역할HCI 전달검증할 조건
단일칩 결합앱 + Host + Controller칩 내부의 HCI 경계대상 SoC의 Controller·HCI 장치가 실제 설정에 포함됐는가
Host 전용앱 + Host외부 Controller로 명령·데이터 전송선택된 HCI 드라이버와 실제 전송 장치가 준비됐는가
Controller 전용Link Layer + HCI 외부 인터페이스외부 Host의 명령을 받아 이벤트·데이터를 반환RAW HCI 샘플의 transport·버퍼 설정이 보드에 맞는가

Controller 전용을 “Host 기능을 끈 결합 앱”으로만 이해하지 않는다. 3.7.0의 hci_uart 샘플은 CONFIG_BT_HCI_RAW=y와 H:4 transport를 사용해 Zephyr Controller를 외부 Host에 노출한다. 이 샘플의 설정값은 샘플과 보드에 묶인 예시다. 다른 transport나 칩에 그대로 복사하는 일반 처방이 아니다.

근거: 공식 배치와 Host/Controller 빌드 유형, HCI UART 샘플 목적·하드웨어 조건, 고정 샘플 설정.

3. 광고 요청과 연결 데이터는 HCI를 다르게 지난다

광고를 시작할 때 Host는 Controller에 설정 명령을 보내고, Controller의 명령 완료·상태 이벤트를 받는다. 스캔 중 발견한 광고는 반대로 Controller의 보고 이벤트가 Host로 올라온다. 연결된 뒤 애플리케이션 데이터가 상위 프로토콜을 통해 이동하는 경우에는 HCI ACL 데이터 경로가 쓰인다. 따라서 모든 광고 패킷을 ACL 데이터라고 부르지 않는다.

그림 3. 제어 명령·결과 이벤트와 연결 데이터의 방향

↔ 좁은 화면에서는 제어 흐름과 ACL 흐름을 오른쪽으로 이동해 확인하세요.

제어: Host에서 Controller로 HCI 명령, Controller에서 Host로 완료·상태·광고 보고 이벤트. 연결 데이터: Host와 Controller 사이 ACL 데이터 양방향. Controller가 무선 링크로 전송
표 3. HCI 명령·이벤트·ACL 데이터 방향
HCI 단위대표 방향대표 상황혼동하지 않을 점
CommandHost → Controller광고·스캔·연결 절차 설정호출 자체가 Radio 전송 완료를 뜻하지 않음
EventController → HostCommand Complete/Status, 광고 보고, 연결 상태 변화명령 응답뿐 아니라 비동기 통지도 있음
ACL Data양방향연결된 링크의 상위 데이터 운반광고 제어·광고 보고를 ACL과 합치지 않음

고정 소스의 bt_buf_type은 BT_BUF_CMD, BT_BUF_EVT, 송수신 ACL 버퍼를 별도 값으로 둔다. Host의 bt_hci_cmd_send_sync()는 명령 버퍼를 송신 큐에 넣고 완료·상태 처리를 기다린다. 실제 전송은 선택된 HCI 드라이버로 내려가며, 이벤트 수신은 해당 드라이버의 callback을 통해 Host로 올라온다. 이는 흐름 설명이지 모든 callback이 동일한 스레드에서 실행된다는 주장은 아니다.

근거: HCI 버퍼 유형, Host 명령 큐, HCI 드라이버 송신, HCI 드라이버 open/send/receive 계약.

4. 빌드 옵션과 선택된 장치를 분리해 확인한다

CONFIG_BT=y는 Bluetooth 기능을 켠다. 3.7.0의 BT_HCI는 HCI 기반 스택 선택이며, BT_CTLR는 SoC native Controller 구현을 포함하는 선택이다. BT_HCI_RAW는 Controller를 RAW HCI로 외부에 노출할 때 쓰인다. 그러나 개별 prj.conf의 몇 줄만으로 실제 Host–Controller 배치가 확정되지는 않는다. 보드 devicetree와 생성된 .config도 함께 확인해야 한다.

# 3.7.0 공식 hci_uart 샘플에서 발췌한 역할 표지
CONFIG_BT=y
CONFIG_BT_HCI_RAW=y
CONFIG_BT_HCI_RAW_H4=y
CONFIG_BT_HCI_RAW_H4_ENABLE=y

# 이 네 줄만 새 보드에 붙여서 동작을 보장하지 않는다.
# 보드의 Controller, UART, HCI 선택과 최종 .config를 확인한다.

BT_HCI_RAW_H4는 RAW HCI의 H:4 transport 지원을 켠다. 이 샘플의 BT_HCI_RAW_H4_ENABLE은 H:4를 빌드 시점에 활성화한다. 앞의 옵션 하나만으로 모든 실행에서 H:4가 이미 선택됐다고 가정하지 않는다.

표 4. 설정과 실행 시 확인할 경계
확인 지점성립 조건실패할 때 보이는 경계
.config목적한 Host/Controller/RAW HCI 선택이 반영됨소스에 함수가 있어도 이미지에는 빠질 수 있음
zephyr.dts·HCI 장치선택한 HCI driver와 transport가 실제 보드와 맞음Host의 bt_enable()에서 장치 준비 실패 가능
bt_enable() 반환·완료cb=NULL이면 동기 완료를, cb!=NULL이면 호출 반환과 완료 callback의 err를 모두 확인-ENODEV·driver open 오류 또는 비동기 초기화 오류를 구분
Controller 전용 UART 샘플UART H:4와 HW flow control 등 샘플 하드웨어 조건 충족HCI 링크 문제를 상위 GATT 문제로 오진하지 않음

고정 Host 소스는 zephyr,bt-hci chosen node가 있으면 해당 device를 받아 준비 여부를 검사하고 HCI driver를 연다. 그 chosen node가 없는 빌드에는 당시의 별도 legacy driver 등록 경로도 남아 있다. 그러므로 “모든 3.7 보드는 chosen node를 반드시 사용한다”라고 일반화하지 않는다. 또한 bt_enable(cb)에서 callback을 넘겼다면 호출의 반환값 0은 초기화 전체 성공의 확인값이 아니다. 완료 callback이 받은 err까지 확인해야 한다.

근거: BT·BT_HCI·RAW HCI 선택, Controller 선택, bt_enable 장치·driver·비동기 완료 분기, bt_enable callback 계약, HCI UART 샘플 요구 조건.

초기화 완료와 저장된 Bluetooth 설정 복원도 구분한다. CONFIG_BT_SETTINGS를 사용하면 bt_enable()이 성공적으로 완료된 뒤 settings_load() 또는 settings_load_subtree("bt")로 Bluetooth 설정을 읽어야 한다. 비동기 호출은 성공 callback 이후가 이 순서의 기준이다. 초기화 전에 이미 전체 설정을 읽었다고 해서 이 단계를 건너뛸 수는 없다. 고정 API의 설정 복원 전제.

5. 문제가 생기면 세 경계의 관찰 결과를 나란히 둔다

광고가 보이지 않는다는 결과 하나로 Host, HCI 링크, Controller 중 어느 층이 멈췄는지 결정할 수 없다. 먼저 빌드가 의도한 이미지 유형인지 확인하고, Host의 bt_enable() 결과(cb!=NULL이면 완료 callback의 err 포함)와 HCI driver 준비·open 상태를 기록한다. 그다음 명령에 대한 완료·상태 이벤트가 오는지 확인하고, 스캔 보고 또는 연결 데이터가 어느 방향에서 끊기는지 좁힌다. 실제 패킷 측정은 보드와 transport별 도구가 필요하다.

표 5. 증상별 Bluetooth 진단 출발점
관찰먼저 확인그다음 분리할 것
bt_enable() 실패선택된 HCI device와 driver open 반환보드 devicetree·transport 초기화
명령을 보냈지만 다음 상태가 없음Command Complete/Status 이벤트 수신HCI transport와 Controller 처리
광고·스캔 문제Host 요청과 Controller 이벤트를 별도 기록무선 노출·환경·상대 기기
연결 후 데이터 문제양방향 ACL buffer 흐름상위 프로토콜과 Controller 링크 구분

이 진단표는 관찰 순서다. 코드만 읽어 실제 전파 성공, 타이밍, 손실률을 인증할 수 없다. 특히 듀얼칩의 HCI transport 종류와 보드 배선은 제품 구성마다 다르다.

6. 자료의 고정점과 이어 읽기

구조와 배치의 기준은 Zephyr 3.7.0 Stack Architecture다. Host의 실제 HCI 경계는 고정 hci_core.c와 HCI driver API, Controller 쪽 소스의 출발점은 controller/hci/hci.c다. 실제 Controller 전용 transport 구성은 hci_uart 예시를 한 가지 근거로 삼았다.