Bluetooth 커널 서브시스템
Linux Bluetooth는 net/bluetooth/ 코어와 BlueZ 사용자 공간(User Space) 도구가 결합되어 동작합니다. 이 문서는 커널 내부 데이터 경로, 드라이버 계층, 보안, 성능, 디버깅(Debugging) 관점에서 Bluetooth Classic과 BLE를 함께 정리합니다. Bluetooth Core Specification 5.4 기준으로 프로토콜 스택 전반, Mesh, LE Audio까지 포괄합니다.
핵심 요약
- HCI -- 호스트와 컨트롤러 사이 표준 명령/이벤트 인터페이스
- L2CAP -- 채널 기반 다중화(Multiplexing), 재조립, 흐름 제어(Flow Control)
- SMP -- BLE 페어링/키 분배 보안 계층
- GATT/GAP -- BLE 서비스 검색과 데이터 교환의 핵심 프로토콜
- mgmt -- BlueZ가 커널 Bluetooth 스택을 제어하는 관리 인터페이스
- btmon -- HCI, mgmt 트래픽을 동시에 보는 핵심 디버깅 도구
단계별 이해
- 컨트롤러 등록
btusb또는hci_uart드라이버가 HCI 장치를 등록합니다. - 어댑터 활성화
BlueZ가 mgmt 명령으로 스캔/광고/연결 모드를 설정합니다. - 채널 협상
L2CAP가 CID 채널을 열고 MTU/보안 파라미터를 협상합니다. - 상위 서비스 사용
RFCOMM, ATT/GATT, SCO 오디오가 각각 목적에 맞게 데이터 경로를 사용합니다.
Bluetooth 프로토콜 스택 아키텍처
Bluetooth Core Specification 구조
Bluetooth Core Specification은 Host와 Controller를 명확히 분리합니다. Controller는 무선 송수신(Radio), 베이스밴드(Baseband), 링크 매니저(Link Manager)를 담당하며, Host는 L2CAP, SDP, RFCOMM, GATT 등 상위 프로토콜을 처리합니다. 이 둘 사이의 표준 인터페이스가 HCI(Host Controller Interface)입니다.
HCI Transport 계층
HCI 패킷은 물리적으로 다양한 전송 수단을 통해 이동합니다. 리눅스 커널은 다음 HCI Transport를 지원합니다:
| Transport | 커널 드라이버 | 특징 | 주요 사용 환경 |
|---|---|---|---|
| USB | btusb |
인터럽트(Interrupt)/벌크/아이소크러너스 엔드포인트 활용, 가장 범용적 | PC/노트북 내장 어댑터, USB 동글 |
| UART (H4) | hci_uart |
단순 직렬 프로토콜, 패킷 타입 바이트로 구분 | 임베디드 SoC, Raspberry Pi |
| UART (H5/3-Wire) | hci_uart |
슬립(Sleep) 모드, CRC, 재전송(Retransmission) 지원으로 안정성 향상 | 전력 관리가 중요한 IoT 장치 |
| SDIO | btsdio |
SD 버스(Bus) 기반 전송, 주로 WiFi+BT 콤보 칩에서 사용 | 모바일/태블릿 콤보 모듈 |
| SPI | 벤더별 구현 | 저전력, 단순 인터페이스 | 웨어러블, 센서 노드 |
BlueZ 커널 모듈(Kernel Module) 구조
리눅스 커널의 Bluetooth 구현은 net/bluetooth/ 디렉토리에 모듈화되어 있습니다.
각 모듈은 독립적으로 빌드 가능하며 CONFIG_BT_* 옵션으로 제어됩니다.
| 커널 모듈 | 소스 위치 | 역할 |
|---|---|---|
bluetooth | net/bluetooth/ | 코어: AF_BLUETOOTH 소켓(Socket), HCI 코어, SMP |
bnep | net/bluetooth/bnep/ | Bluetooth Network Encapsulation Protocol |
cmtp | net/bluetooth/cmtp/ | CAPI Message Transport Protocol |
hidp | net/bluetooth/hidp/ | Human Interface Device Protocol |
rfcomm | net/bluetooth/rfcomm/ | RS-232 에뮬레이션 프로토콜 |
btusb | drivers/bluetooth/btusb.c | USB HCI 드라이버 |
hci_uart | drivers/bluetooth/hci_uart.h | UART HCI 드라이버 프레임워크 |
HCI (Host Controller Interface)
HCI 패킷 형식
HCI는 네 가지 패킷 타입을 정의합니다. 각 패킷은 첫 번째 바이트(패킷 인디케이터)로 타입을 구분하며, 이후 타입별 헤더와 페이로드(Payload)가 따릅니다.
| 패킷 타입 | 인디케이터 | 방향 | 헤더 크기 | 용도 |
|---|---|---|---|---|
| Command | 0x01 | Host -> Controller | 3 바이트 (OpCode + Param Length) | 컨트롤러 제어 명령 |
| ACL Data | 0x02 | 양방향 | 4 바이트 (Handle + Flags + Length) | 비동기 데이터 전송 |
| SCO Data | 0x03 | 양방향 | 3 바이트 (Handle + Length) | 동기 음성 데이터 |
| Event | 0x04 | Controller -> Host | 2 바이트 (Event Code + Param Length) | 상태/응답 이벤트 |
| ISO Data | 0x05 | 양방향 | 4 바이트 (Handle + Flags + Length) | LE Audio ISO 채널 데이터 |
hci_dev 구조체(Struct)
struct hci_dev는 커널에서 하나의 Bluetooth 어댑터를 나타내는 핵심 자료구조입니다.
net/bluetooth/hci_core.c에서 관리하며, 드라이버가 hci_alloc_dev()로 할당하고
hci_register_dev()로 등록합니다.
/* include/net/bluetooth/hci_core.h (주요 필드 발췌) */
struct hci_dev {
struct list_head list; /* 전역 hci_dev_list 링크 */
struct mutex lock;
char name[8]; /* "hci0", "hci1" ... */
__u16 id;
__u8 bus; /* HCI_USB, HCI_UART, HCI_SDIO ... */
__u8 dev_type; /* HCI_PRIMARY, HCI_AMP */
unsigned long flags; /* HCI_UP, HCI_RUNNING, HCI_PAIRABLE ... */
__u8 dev_flags;
__u16 manufacturer; /* 벤더 식별자 */
__u16 lmp_subver;
/* 드라이버 콜백 */
int (*open)(struct hci_dev *hdev);
int (*close)(struct hci_dev *hdev);
int (*send)(struct hci_dev *hdev, struct sk_buff *skb);
int (*setup)(struct hci_dev *hdev);
int (*set_bdaddr)(struct hci_dev *hdev, const bdaddr_t *bdaddr);
/* ACL/SCO 연결 목록 */
struct list_head conn_hash;
struct list_head adv_instances;
/* 명령/이벤트 큐 */
struct sk_buff_head cmd_q;
struct sk_buff_head raw_q;
struct sk_buff *sent_cmd;
struct workqueue_struct *workqueue;
struct workqueue_struct *req_workqueue;
};
HCI 코어 흐름과 상태 머신
HCI 명령은 hci_send_cmd()를 통해 cmd_q에 대기열에 추가된 뒤,
hci_cmd_work() 워커가 하나씩 꺼내 드라이버의 send() 콜백(Callback)으로 전송합니다.
컨트롤러 응답은 Command Complete/Command Status 이벤트로 돌아오며,
hci_event_packet()에서 처리됩니다.
/* net/bluetooth/hci_core.c - RX 경로 흐름 요약 */
int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
switch (hci_skb_pkt_type(skb)) {
case HCI_EVENT_PKT:
return hci_event_packet(hdev, skb);
case HCI_ACLDATA_PKT:
return hci_acldata_packet(hdev, skb);
case HCI_SCODATA_PKT:
return hci_scodata_packet(hdev, skb);
case HCI_ISODATA_PKT:
return hci_isodata_packet(hdev, skb);
}
return -EINVAL;
}
HCI 장치의 상태 전이는 hdev->flags 비트 필드로 관리됩니다.
주요 상태 전이는 다음과 같습니다:
| 상태 플래그 | 의미 | 전이 조건 |
|---|---|---|
HCI_INIT | 초기화 진행 중 | hci_dev_open() 호출 시 설정 |
HCI_UP | 장치 활성 상태 | 초기화 완료 후 설정 |
HCI_RUNNING | 드라이버 동작 중 | open() 콜백 성공 시 |
HCI_DISCOVERABLE | 검색 가능 모드 | mgmt Set Discoverable 명령 |
HCI_PAIRABLE | 페어링 허용 모드 | mgmt Set Pairable 명령 |
HCI_INQUIRY | 장치 검색(Inquiry) 진행 중 | Inquiry 명령 전송 시 |
Bluetooth Classic 연결 수립 흐름
Bluetooth Classic(BR/EDR)에서 두 장치가 연결을 수립하는 과정은 Inquiry, Paging, 연결 설정, 인증의 4단계로 진행됩니다. 각 단계에서 HCI 명령과 이벤트가 호스트와 컨트롤러 사이를 오가며, 최종적으로 ACL 링크가 생성됩니다.
L2CAP (Logical Link Control and Adaptation Protocol)
채널 멀티플렉싱
L2CAP는 하나의 ACL 링크 위에 여러 논리 채널을 다중화합니다. 각 채널은 CID(Channel Identifier)로 구분되며, 고정 CID(0x0001~0x003F)와 동적 CID(0x0040~)로 나뉩니다. 동적 채널은 PSM(Protocol/Service Multiplexer) 값으로 상위 프로토콜을 식별합니다.
| CID | 용도 | 프로토콜 |
|---|---|---|
0x0001 | L2CAP 시그널(Signal)링 (BR/EDR) | 연결 요청/응답, 설정 협상 |
0x0002 | Connectionless | 그룹 브로드캐스트 |
0x0003 | AMP Manager | AMP 컨트롤러 관리 |
0x0004 | ATT (LE 고정 채널) | GATT Attribute Protocol |
0x0005 | LE 시그널링 | LE 연결 파라미터 업데이트 |
0x0006 | SMP (LE 고정 채널) | Security Manager Protocol |
0x0007 | SMP (BR/EDR) | Cross-transport 키 분배 |
PSM (Protocol/Service Multiplexer)
| PSM 값 | 프로토콜 | 설명 |
|---|---|---|
0x0001 | SDP | Service Discovery Protocol |
0x0003 | RFCOMM | RS-232 에뮬레이션 |
0x000F | BNEP | Bluetooth Network Encapsulation |
0x0011 | HID Control | HID 장치 제어 채널 |
0x0013 | HID Interrupt | HID 장치 데이터 채널 |
0x0017 | AVCTP | Audio/Video Control Transport |
0x0019 | AVDTP | Audio/Video Distribution Transport |
다음 다이어그램은 하나의 ACL 링크 위에서 여러 L2CAP 채널이 CID와 PSM을 통해 다중화되는 구조를 보여줍니다. 고정 채널(ATT, SMP, 시그널링)과 동적 채널(RFCOMM, A2DP 등)이 동시에 공존하며, 각 채널은 독립적인 MTU와 전송 모드를 가집니다.
MTU 협상과 모드
L2CAP 채널 설정 시 양쪽이 MTU(Maximum Transmission Unit)를 협상합니다. 기본 MTU는 BR/EDR에서 672 바이트, LE에서 23 바이트입니다. 실제 대부분의 구현은 더 큰 MTU를 요청합니다.
L2CAP는 여러 전송 모드를 지원합니다:
| 모드 | 약어 | 특징 | 사용 사례 |
|---|---|---|---|
| Basic Mode | - | 흐름 제어/재전송 없음, 단순 | 기본 데이터 전송 |
| Enhanced Retransmission Mode | ERTM | I-frame 시퀀스 번호, 선택적 재전송, 흐름 제어 | OBEX, HID (신뢰성 필요) |
| Streaming Mode | SM | 순서 보장(Ordering), 재전송 없음 (실시간(Real-time) 우선) | A2DP 오디오 스트리밍 |
| LE Credit-based Flow Control | LE CoC | 크레딧 기반 흐름 제어, LE 전용 | LE 데이터 채널 (대용량 전송) |
| Enhanced Credit-based Flow Control | ECBFC | 다중 채널 동시 설정, Bluetooth 5.2+ | LE Audio, 고성능 LE 전송 |
/* net/bluetooth/l2cap_core.c - L2CAP 연결 요청 처리 핵심 흐름 */
static struct l2cap_chan *l2cap_connect(
struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd,
u8 *data, u8 cmd_len)
{
struct l2cap_conn_req *req = (struct l2cap_conn_req *)data;
__le16 psm = req->psm;
__le16 scid = req->scid;
/* PSM으로 상위 프로토콜 핸들러를 검색 */
struct l2cap_chan *pchan = l2cap_global_chan_by_psm(
0, psm, &conn->hcon->src,
&conn->hcon->dst, ACL_LINK);
/* 채널 설정: MTU, 모드, 보안 레벨 협상 진행 */
...
}
RFCOMM
RS-232 에뮬레이션
RFCOMM은 L2CAP 위에서 RS-232 직렬 포트(Serial Port)를 에뮬레이션하는 프로토콜입니다. GSM 07.10 멀티플렉서 프로토콜을 기반으로 하며, 하나의 L2CAP 채널(PSM 0x0003) 위에 최대 30개의 DLC(Data Link Connection)를 다중화할 수 있습니다. 각 DLC는 서버 채널 번호(1~30)로 식별됩니다.
rfcomm_tty 드라이버
커널의 rfcomm 모듈은 TTY 인터페이스(/dev/rfcomm0 등)를 제공합니다.
이를 통해 기존 직렬 포트 프로그램이 수정 없이 Bluetooth 통신에 사용될 수 있습니다.
/* net/bluetooth/rfcomm/tty.c - RFCOMM TTY 드라이버 등록 */
static const struct tty_operations rfcomm_ops = {
.open = rfcomm_tty_open,
.close = rfcomm_tty_close,
.write = rfcomm_tty_write,
.write_room = rfcomm_tty_write_room,
.set_termios = rfcomm_tty_set_termios,
.tiocmget = rfcomm_tty_tiocmget,
.tiocmset = rfcomm_tty_tiocmset,
};
/* RFCOMM TTY 생성 명령 예시 */
/* rfcomm bind /dev/rfcomm0 AA:BB:CC:DD:EE:FF 1 */
| RFCOMM 서비스 | 서버 채널 | 용도 |
|---|---|---|
| SPP (Serial Port Profile) | 보통 1 | 범용 직렬 통신 |
| DUN (Dial-up Networking) | 보통 1~2 | 모뎀 에뮬레이션 |
| OBEX Push | 보통 9~12 | 파일 전송 |
| HFP (Hands-Free Profile) | SDP로 검색 | 차량 핸즈프리 통화 |
BLE (Bluetooth Low Energy)
BLE vs Classic 비교
Bluetooth 4.0에서 도입된 BLE는 Classic BR/EDR과는 완전히 다른 PHY와 프로토콜 스택을 사용합니다. 동일한 2.4 GHz ISM 대역을 사용하지만, 채널 수, 변조 방식, 전력 소모, 데이터 전송 패턴이 크게 다릅니다.
| 항목 | Classic (BR/EDR) | BLE (LE) |
|---|---|---|
| 채널 수 | 79 (1 MHz 간격) | 40 (2 MHz 간격, 3개 광고 채널) |
| 최대 데이터 속도 | BR: 1 Mbps, EDR: 3 Mbps | LE 1M: 1 Mbps, LE 2M: 2 Mbps |
| 연결 지연(Latency) | 100 ms 이상 | 수 ms~수십 ms |
| 전력 소모 | 상대적으로 높음 | 매우 낮음 (코인 셀 배터리 수 년) |
| 토폴로지(Topology) | 포인트 투 포인트 (피코넷) | Star, Mesh, Broadcast |
| 주요 용도 | 오디오, 파일 전송, HID | 센서, 비콘, 웨어러블, IoT |
| 보안 | SSP (Secure Simple Pairing) | SMP (Security Manager Protocol) |
| 서비스 검색 | SDP | GATT |
Advertising과 Scanning
BLE 장치 검색의 핵심은 Advertising과 Scanning입니다. 광고자(Advertiser)는 37, 38, 39번 채널에서 주기적으로 광고 패킷을 브로드캐스트하고, 스캐너(Scanner)는 이 채널을 순회하며 패킷을 수집합니다.
| 광고 타입 | HCI 명칭 | 특징 |
|---|---|---|
| Connectable Undirected | ADV_IND | 모든 장치가 연결 요청 가능, 가장 일반적 |
| Connectable Directed | ADV_DIRECT_IND | 특정 장치만 연결 가능, 빠른 재연결 |
| Non-Connectable Undirected | ADV_NONCONN_IND | 비콘용, 연결 불가, 브로드캐스트 전용 |
| Scannable Undirected | ADV_SCAN_IND | Scan Request로 추가 데이터 요청 가능 |
Connection Interval과 LE PHY
BLE 연결이 수립되면 Central과 Peripheral은 Connection Interval(7.5 ms ~ 4 s)에 따라 주기적으로 데이터를 교환합니다. Slave Latency는 Peripheral이 건너뛸 수 있는 연결 이벤트 수로, 전력 절감에 핵심적입니다.
| LE PHY | 도입 버전 | 데이터 속도 | 특징 |
|---|---|---|---|
| LE 1M | BT 4.0 | 1 Mbps | 기본 PHY, 호환성 최대 |
| LE 2M | BT 5.0 | 2 Mbps | 전송 시간 단축으로 전력 절감, 도달 거리 약간 감소 |
| LE Coded (S=2) | BT 5.0 | 500 kbps | FEC 코딩으로 도달 거리 2배 확장 |
| LE Coded (S=8) | BT 5.0 | 125 kbps | FEC 코딩으로 도달 거리 4배 확장 |
Extended Advertising (Bluetooth 5.0+)
기존 Legacy Advertising은 31 바이트 페이로드 제한이 있었습니다. Bluetooth 5.0의 Extended Advertising은 보조 채널(Secondary Advertising Channel)을 활용하여 최대 255 바이트(체인 시 더 큰 데이터)의 광고 데이터를 전송할 수 있습니다. 또한 Advertising Sets를 통해 여러 광고 인스턴스를 동시에 운영할 수 있습니다.
GATT/GAP
GAP (Generic Access Profile)
GAP는 Bluetooth 장치의 검색 가능성, 연결 모드, 보안 요구사항을 정의합니다. BLE에서 GAP 역할은 다음 네 가지로 나뉩니다:
| GAP 역할 | 설명 | 리눅스 커널 관련 |
|---|---|---|
| Broadcaster | 광고만 전송, 연결 불가 | 비콘, 센서 브로드캐스트 |
| Observer | 스캔만 수행, 연결 불가 | 패시브 수집기 |
| Peripheral | 광고 전송 + 연결 수락 (Slave) | 센서, 웨어러블 |
| Central | 스캔 + 연결 개시 (Master) | 스마트폰, 게이트웨이 |
GATT (Generic Attribute Profile)
GATT는 BLE 데이터 교환의 핵심 프레임워크입니다.
ATT(Attribute Protocol) 위에서 동작하며, 데이터를 Service -> Characteristic -> Descriptor
계층으로 구조화합니다. L2CAP 고정 CID 0x0004를 사용합니다.
GATT 데이터 계층
| 계층 | UUID 예시 | 설명 |
|---|---|---|
| Service | 0x180D (Heart Rate) |
관련 Characteristic을 그룹화하는 최상위 컨테이너(Container). Primary/Secondary 구분 |
| Characteristic | 0x2A37 (Heart Rate Measurement) |
실제 데이터 값을 담는 단위. Properties(Read/Write/Notify/Indicate) 정의 |
| Descriptor | 0x2902 (CCCD) |
Characteristic의 메타데이터. CCCD는 Notification/Indication 활성화에 필수 |
ATT 프로토콜 주요 연산
| ATT PDU | OpCode | 방향 | 설명 |
|---|---|---|---|
| Read Request | 0x0A | Client -> Server | 핸들 기반 값 읽기 |
| Read Response | 0x0B | Server -> Client | 읽기 응답 |
| Write Request | 0x12 | Client -> Server | 값 쓰기 (응답 필요) |
| Write Response | 0x13 | Server -> Client | 쓰기 확인 |
| Write Command | 0x52 | Client -> Server | 값 쓰기 (응답 없음) |
| Notification | 0x1B | Server -> Client | 서버 주도 값 전달 (확인 없음) |
| Indication | 0x1D | Server -> Client | 서버 주도 값 전달 (확인 필요) |
| Find By Type Value | 0x06 | Client -> Server | UUID로 서비스 검색 |
net/bluetooth/l2cap_core.c의 ATT 채널이 GATT 패킷을 처리하며,
실제 GATT 프로파일 로직 대부분은 BlueZ bluetoothd에서 구현됩니다.
SMP (Security Manager Protocol)
페어링 절차 개요
SMP는 BLE 연결의 보안을 담당하며, L2CAP 고정 CID 0x0006에서 동작합니다.
페어링 과정은 크게 세 단계로 진행됩니다:
- Phase 1 - Pairing Feature Exchange: 양쪽 장치가 IO Capability, 인증 요구사항, 지원 기능을 교환합니다.
- Phase 2 - Authentication (STK/LTK 생성): 선택된 페어링 방식에 따라 키를 생성합니다. Legacy Pairing은 STK(Short Term Key), LE Secure Connections는 LTK를 직접 생성합니다.
- Phase 3 - Key Distribution: LTK, IRK(Identity Resolving Key), CSRK(Connection Signature Resolving Key)를 교환합니다.
페어링 방식 비교
| 방식 | IO Capability 조합 | MITM 방어 | 사용자 경험 |
|---|---|---|---|
| Just Works | NoInput/NoOutput 포함 시 | 없음 | 사용자 입력 없이 즉시 페어링 |
| Passkey Entry | Display+Keyboard 조합 | 있음 | 한쪽이 표시한 6자리 숫자를 다른 쪽이 입력 |
| Numeric Comparison | 양쪽 Display+YesNo (SC 전용) | 있음 | 양쪽에 동일한 6자리 숫자를 표시, 사용자가 확인 |
| OOB (Out of Band) | OOB 데이터 사용 가능 시 | OOB 채널에 따라 다름 | NFC 탭 등 외부 채널로 인증 데이터 교환 |
LE Secure Connections
Bluetooth 4.2에서 도입된 LE Secure Connections는 ECDH(Elliptic Curve Diffie-Hellman) P-256 키 교환을 사용합니다.
Legacy Pairing의 STK 방식보다 훨씬 강력한 보안을 제공하며, 패시브 도청에 대한 방어력이 있습니다.
커널에서는 net/bluetooth/smp.c의 sc_send_public_key()에서
ECDH 공개키를 교환합니다.
키 분배
| 키 종류 | 약어 | 용도 | 저장 위치 |
|---|---|---|---|
| Long Term Key | LTK | 연결 암호화 (재연결 시 사용) | /var/lib/bluetooth/ |
| Identity Resolving Key | IRK | 랜덤 주소에서 실제 장치 식별 | /var/lib/bluetooth/ |
| Connection Signature Resolving Key | CSRK | 비암호화 데이터의 서명 검증(Signature Verification) | /var/lib/bluetooth/ |
| Identity Address | - | 장치의 고정 BD_ADDR | IRK와 함께 저장 |
/* net/bluetooth/smp.c - SMP 페어링 요청 처리 */
static u8 smp_cmd_pairing_req(struct l2cap_conn *conn,
struct sk_buff *skb)
{
struct smp_cmd_pairing *req = (void *)skb->data;
struct smp_chan *smp;
/* IO Capability와 인증 요구사항 확인 */
smp->preq[0] = SMP_CMD_PAIRING_REQ;
memcpy(&smp->preq[1], req, sizeof(*req));
/* SC 지원 여부에 따라 Legacy/SC 분기 */
if (req->auth_req & SMP_AUTH_SC)
set_bit(SMP_FLAG_SC, &smp->flags);
/* 페어링 방식 결정: IO Capability 매트릭스 참조 */
smp->method = get_auth_method(smp, req->io_capability,
rsp->io_capability);
...
}
SMP_AUTH_MITM 플래그가 설정되어 있는지 확인하세요.
A2DP와 LE Audio
A2DP 오디오 프로파일
A2DP(Advanced Audio Distribution Profile)는 Classic Bluetooth에서 고품질 스테레오 오디오 스트리밍에 사용됩니다. AVDTP(Audio/Video Distribution Transport Protocol) 위에서 동작하며, L2CAP의 Streaming Mode를 사용하여 재전송 없이 실시간 전송을 우선합니다.
A2DP 코덱 비교
| 코덱 | 필수/선택 | 비트레이트 | 지연 | 비고 |
|---|---|---|---|---|
| SBC | 필수 (기본) | 198~345 kbps | 높음 (~100-150 ms) | 모든 A2DP 장치 지원, 품질 보통 |
| AAC | 선택 | 최대 256 kbps (CBR) | 중간 | Apple 생태계 선호, 라이선스 필요 |
| aptX | 선택 (Qualcomm) | 352 kbps | 낮음 (~40 ms) | Qualcomm 칩셋 필요 |
| aptX HD | 선택 (Qualcomm) | 576 kbps | 중간 (~80 ms) | 24-bit/48 kHz 고해상도 |
| LDAC | 선택 (Sony) | 최대 990 kbps | 중간 | 24-bit/96 kHz, Android 8+ 기본 지원 |
pipewire-media-session 또는 wireplumber가
BlueZ의 D-Bus MediaEndpoint API를 통해 코덱을 협상합니다.
LE Audio와 ISO Channels
Bluetooth 5.2에서 도입된 LE Audio는 BLE 기반의 차세대 오디오 표준입니다. 기존 A2DP의 Classic Bluetooth 의존성을 제거하고, 저전력과 새로운 기능을 제공합니다.
| 항목 | A2DP (Classic) | LE Audio |
|---|---|---|
| 전송 방식 | L2CAP Streaming Mode | ISO Channels (CIS/BIS) |
| 필수 코덱 | SBC | LC3 |
| 멀티 스트림 | 하나의 싱크만 | 여러 싱크 동시 전송 가능 |
| 브로드캐스트 | 불가 | Auracast (BIS) |
| 보청기 지원 | 제한적 | ASHA/HAP 프로파일 지원 |
| 전력 소모 | 상대적으로 높음 | BLE 기반으로 저전력 |
LC3 코덱과 Broadcast Audio
LC3(Low Complexity Communication Codec)는 LE Audio의 필수 코덱으로, SBC보다 절반의 비트레이트에서 동등 이상의 품질을 제공합니다. Broadcast Audio (Auracast)는 BIS(Broadcast ISO Stream)를 통해 하나의 소스에서 다수의 수신기로 오디오를 동시 전송하는 기능입니다.
커널에서 ISO 채널은 HCI ISO Data 패킷(타입 0x05)을 통해 전송되며,
net/bluetooth/iso.c에서 소켓 인터페이스를 제공합니다.
/* net/bluetooth/iso.c - ISO 소켓 생성 */
static int iso_sock_create(struct net *net,
struct socket *sock, int protocol,
int kern)
{
struct sock *sk;
sock->ops = &iso_sock_ops;
sk = iso_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern);
if (!sk)
return -ENOMEM;
iso_sock_init(sk, NULL);
return 0;
}
Bluetooth Mesh
Mesh 네트워크 토폴로지
Bluetooth Mesh는 BLE Advertising/Scanning 기반의 관리형 플러딩(Managed Flooding) 메시 네트워크입니다. 기존 BLE의 포인트 투 포인트 토폴로지를 넘어 수백~수천 노드의 대규모 네트워크를 구성할 수 있습니다. 메시지는 TTL(Time To Live) 기반으로 네트워크 전체에 전파됩니다.
노드 역할
| 역할 | 설명 | 특징 |
|---|---|---|
| Relay 노드 | 수신한 메시지를 재전송하여 도달 범위를 확장 | 항상 스캔/광고 상태, 전력 소모 높음 |
| Proxy 노드 | GATT Bearer를 통해 BLE 연결 기반 장치와 Mesh 네트워크를 연결 | 스마트폰 등 Mesh 미지원 장치의 게이트웨이 |
| Friend 노드 | Low Power 노드의 메시지를 임시 저장하고 폴링(Polling) 시 전달 | 메모리와 상시 전원 필요 |
| Low Power 노드 | 대부분 슬립 상태, Friend 노드에 폴링하여 메시지 수신 | 배터리 구동 센서에 최적 |
Provisioning
Provisioning은 새로운 장치를 Mesh 네트워크에 추가하는 과정입니다. Provisioner가 인증 후 NetKey, IV Index, Unicast Address를 할당합니다. 이 과정은 PB-ADV(Advertising Bearer) 또는 PB-GATT(GATT Bearer)를 통해 수행됩니다.
메시지 전달 과정
- Application Layer: 모델(Model)이 메시지를 생성하고 AppKey로 암호화
- Upper Transport: 접근 메시지(Access Message)를 세그먼트화하고 Application/Device Key로 암호화/인증
- Lower Transport: 세그먼트 조립/분해, SAR(Segmentation and Reassembly) 처리
- Network Layer: NetKey로 암호화/인증, TTL 관리, 릴레이 결정
- Bearer Layer: BLE Advertising 패킷으로 변환하여 전송
bluetooth-meshd 데몬을 통해 Mesh 기능을 지원합니다.
커널은 BLE Advertising/Scanning 기반 전송만 담당하고,
Mesh 프로토콜 스택(프로비저닝, 암호화, 릴레이 로직)은 사용자 공간에서 처리합니다.
BlueZ 아키텍처
bluetoothd 데몬
bluetoothd는 BlueZ의 핵심 데몬으로, D-Bus를 통해 응용 프로그램에
Bluetooth 기능을 노출합니다. GATT 프로파일, 에이전트 기반 페어링,
장치 관리 등 대부분의 상위 로직을 담당합니다.
D-Bus API 구조
| D-Bus 인터페이스 | 객체 경로 | 역할 |
|---|---|---|
org.bluez.Adapter1 | /org/bluez/hci0 | 어댑터 관리 (스캔, 전원, 검색 모드) |
org.bluez.Device1 | /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX | 장치 관리 (연결, 페어링, 프로퍼티) |
org.bluez.GattService1 | .../service00XX | GATT 서비스 표현 |
org.bluez.GattCharacteristic1 | .../char00XX | GATT Characteristic 표현 |
org.bluez.AgentManager1 | /org/bluez | 페어링 에이전트 등록/관리 |
org.bluez.ProfileManager1 | /org/bluez | 커스텀 프로파일 등록 |
org.bluez.MediaEndpoint1 | 에이전트가 등록 | A2DP 코덱 엔드포인트 |
mgmt API (커널-유저 인터페이스)
mgmt API는 bluetoothd가 커널 Bluetooth 스택을 제어하는 전용 인터페이스입니다.
기존의 hciconfig/hcitool이 사용하던 raw HCI 소켓 방식을 대체합니다.
AF_BLUETOOTH 소켓의 BTPROTO_HCI에서 HCI_CHANNEL_CONTROL로 접근합니다.
/* mgmt 소켓 생성 (사용자 공간) */
int fd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC,
BTPROTO_HCI);
struct sockaddr_hci addr = {
.hci_family = AF_BLUETOOTH,
.hci_dev = HCI_DEV_NONE,
.hci_channel = HCI_CHANNEL_CONTROL, /* mgmt 채널 */
};
bind(fd, (struct sockaddr *)&addr, sizeof(addr));
주요 mgmt 명령
| 명령 | OpCode | 설명 |
|---|---|---|
| Read Management Version | 0x0001 | mgmt 프로토콜 버전 확인 |
| Set Powered | 0x0005 | 어댑터 전원 켜기/끄기 |
| Set Discoverable | 0x0006 | 검색 가능 모드 설정 |
| Start Discovery | 0x0023 | 장치 검색 시작 |
| Pair Device | 0x0019 | 페어링 개시 |
| Add Advertising | 0x003E | LE 광고 인스턴스 추가 |
# btmgmt로 mgmt 명령 직접 실행
btmgmt info # 어댑터 정보 확인
btmgmt power on # 전원 켜기
btmgmt find # 장치 검색
btmgmt advertising on # LE 광고 활성화
btmgmt bondable on # 페어링 허용
커널 드라이버
btusb
btusb는 가장 널리 사용되는 Bluetooth HCI 드라이버입니다.
USB 인터페이스를 통해 HCI 패킷을 송수신하며, 다양한 벤더의 USB Bluetooth 어댑터를 지원합니다.
/* drivers/bluetooth/btusb.c - 핵심 구조 */
static int btusb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct btusb_data *data;
struct hci_dev *hdev;
data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL);
hdev = hci_alloc_dev();
hdev->bus = HCI_USB;
hdev->open = btusb_open; /* URB 제출 시작 */
hdev->close = btusb_close; /* URB 취소 */
hdev->send = btusb_send_frame; /* TX: 패킷 타입별 엔드포인트 선택 */
hdev->flush = btusb_flush;
/* 벤더별 초기화 콜백 설정 */
if (id->driver_info & BTUSB_INTEL_NEW)
hdev->setup = btintel_setup_combined;
else if (id->driver_info & BTUSB_REALTEK)
hdev->setup = btrtl_setup_realtek;
else if (id->driver_info & BTUSB_MEDIATEK)
hdev->setup = btmtk_usb_setup;
return hci_register_dev(hdev);
}
USB 엔드포인트 사용
| 엔드포인트 타입 | 방향 | HCI 패킷 타입 | 특징 |
|---|---|---|---|
| Control | TX (Host->Controller) | Command | Setup 패킷으로 전송 |
| Interrupt IN | RX (Controller->Host) | Event | 주기적 폴링 |
| Bulk OUT | TX | ACL Data | 비동기 대량 전송 |
| Bulk IN | RX | ACL Data | 비동기 대량 수신 |
| Isochronous | 양방향 | SCO/ISO Data | 실시간 오디오 (대역폭(Bandwidth) 보장) |
hci_uart (UART HCI 드라이버)
hci_uart는 UART 기반 Bluetooth 칩과 통신하는 프레임워크 드라이버입니다.
여러 프로토콜 변형을 플러그인 방식으로 지원합니다:
| 프로토콜 | 소스 파일 | 특징 |
|---|---|---|
| H:4 (H4) | hci_h4.c | 가장 단순, 패킷 타입 바이트만 추가. 에러 복구 없음 |
| H:5 (3-Wire) | hci_h5.c | SLIP 인코딩, CRC-16, 재전송, 슬립 모드 지원 |
| BCSP | hci_bcsp.c | BlueCore Serial Protocol (CSR/Qualcomm) |
| LL | hci_ll.c | TI WiLink 칩 전용, 슬립/웨이크업 최적화 |
| QCA | hci_qca.c | Qualcomm 칩 전용, IBS(In-Band Sleep) 지원 |
| Marvell | hci_mrvl.c | Marvell/NXP 칩 전용 |
| AG6XX | hci_ag6xx.c | Intel AG6XX 시리즈 전용 |
벤더별 초기화 드라이버
| 드라이버 | 벤더 | 소스 파일 | 주요 기능 |
|---|---|---|---|
btintel | Intel | btintel.c | 펌웨어(Firmware) 로드, DDC(Device Dynamic Configuration), TLV 파싱 |
btbcm | Broadcom | btbcm.c | 펌웨어(.hcd) 로드, 패치(Patch) RAM 기록 |
btrtl | Realtek | btrtl.c | 펌웨어/설정 로드, ROM 버전 파싱 |
btmtk | MediaTek | btmtk.c | 펌웨어 로드, 칩 리셋 시퀀스 |
btqca | Qualcomm | btqca.c | NVM/RAMPATCH 로드, EDL(Enhanced Data Length) 프로토콜 |
/lib/firmware/에 위치하며, 드라이버의 setup() 콜백에서
request_firmware()를 통해 로드됩니다.
펌웨어가 없으면 dmesg에 "firmware file not found" 오류가 출력되며
어댑터가 동작하지 않습니다.
구성 요소와 계층
커널 Bluetooth는 크게 컨트롤러 I/O 계층(HCI 드라이버), 프로토콜 계층(L2CAP/RFCOMM/SCO), 소켓 계층(AF_BLUETOOTH), 정책/관리 계층(mgmt)으로 나뉩니다. 아래 다이어그램은 패킷이 하드웨어에서 사용자 공간까지 이동하는 전체 경로를 보여줍니다.
AF_BLUETOOTH 소켓 패밀리
리눅스 커널은 AF_BLUETOOTH 주소 패밀리를 통해 사용자 공간에 Bluetooth 소켓 인터페이스를 제공합니다.
프로토콜 번호에 따라 다양한 소켓 타입을 사용할 수 있습니다:
| 프로토콜 | 상수 | 소켓 타입 | 용도 |
|---|---|---|---|
| L2CAP | BTPROTO_L2CAP | SOCK_SEQPACKET / SOCK_STREAM | L2CAP 채널 직접 접근 |
| HCI | BTPROTO_HCI | SOCK_RAW | HCI 패킷 직접 송수신, mgmt 접근 |
| RFCOMM | BTPROTO_RFCOMM | SOCK_STREAM | RFCOMM 직렬 통신 |
| SCO | BTPROTO_SCO | SOCK_SEQPACKET | 동기 음성 데이터 |
| ISO | BTPROTO_ISO | SOCK_SEQPACKET | LE Audio ISO 채널 |
보안 모델: SSP와 SMP
Classic은 SSP(Secure Simple Pairing), BLE는 SMP(Security Manager Protocol)를 중심으로 링크 키/LTK를 교환합니다. 보안 레벨은 페어링 방식(Just Works, Passkey, Numeric Comparison)에 따라 달라집니다.
보안 레벨 정의
| 레벨 | 이름 | 암호화 | 인증 | 대상 |
|---|---|---|---|---|
| 1 | No Security | 없음 | 없음 | BLE/Classic |
| 2 | Unauthenticated Encryption | 있음 | 없음 (Just Works) | BLE/Classic |
| 3 | Authenticated Encryption | 있음 | 있음 (MITM 방어) | BLE/Classic |
| 4 | Authenticated LE SC + 128-bit | 있음 (AES-CCM) | 있음 (P-256 ECDH) | BLE 4.2+ |
페어링 방식별 특징
| 모드 | 대상 | 특징 | 주의점 |
|---|---|---|---|
| Just Works | BLE/Classic | 사용자 입력 없이 빠름 | MITM 방어 약함 |
| Passkey | BLE/Classic | 숫자 입력 기반 검증 | UI 동기화 필요 |
| Numeric Comparison | BLE 4.2+ | MITM 방어 강함 | 양쪽 표시/확인 인터페이스 필요 |
| OOB | BLE/Classic | 외부 채널(NFC 등) 활용 | OOB 채널 가용 여부에 의존 |
Cross-Transport Key Derivation
Bluetooth 4.2+에서는 한 번의 페어링으로 Classic과 BLE 양쪽 키를 동시에 생성할 수 있습니다.
이를 Cross-Transport Key Derivation(CTKD)이라 합니다.
Classic에서 페어링한 키를 BLE LTK로 변환하거나, 그 반대도 가능합니다.
이 과정은 L2CAP CID 0x0007(BR/EDR SMP 채널)을 통해 수행됩니다.
드라이버 모델: btusb와 hci_uart
btusb는 USB 인터럽트/벌크 엔드포인트를 통해 HCI 패킷을 수집하고, hci_uart는 H4/H5 프로토콜로 UART 컨트롤러와 통신합니다.
btusb 프로브(Probe) 흐름
/* drivers/bluetooth/btusb.c 프로브 패턴 요약 */
static int btusb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct hci_dev *hdev = hci_alloc_dev();
if (!hdev)
return -ENOMEM;
hdev->bus = HCI_USB;
hdev->open = btusb_open;
hdev->close = btusb_close;
hdev->send = btusb_send_frame;
/* btusb_send_frame()은 패킷 타입에 따라
Control(Command), Bulk(ACL), Isoc(SCO) 엔드포인트를 선택 */
return hci_register_dev(hdev);
}
hci_uart 초기화 흐름
/* drivers/bluetooth/hci_ldisc.c - Line Discipline 기반 초기화 */
static int hci_uart_tty_open(struct tty_struct *tty)
{
struct hci_uart *hu;
hu = kzalloc(sizeof(struct hci_uart), GFP_KERNEL);
hu->tty = tty;
tty->disc_data = hu;
/* 이후 HCIUARTSETPROTO ioctl로 프로토콜(H4/H5/QCA 등) 설정
hdev = hci_alloc_dev() -> hci_register_dev() */
return 0;
}
/* serdev 기반 초기화 (최신 커널 권장) */
static int hci_uart_probe(struct serdev_device *serdev)
{
/* Device Tree 또는 ACPI 매칭으로 자동 프로브
별도의 userspace 설정(hciattach) 불필요 */
}
hciattach 유틸리티를 사용했으나,
최신 커널은 Device Tree/ACPI 기반의 serdev 프레임워크를 권장합니다.
serdev를 사용하면 별도의 유저 공간 설정 없이 부팅 시 자동으로 HCI 장치가 등록됩니다.
성능 튜닝 포인트
연결 파라미터 최적화
- 스캔 윈도우/인터벌 조정으로 연결 지연과 전력 사용량 균형 맞추기
- LE Connection Interval과 Slave Latency 튜닝으로 배터리 수명 최적화
- 오디오 경로에서 SCO/eSCO 패킷 손실이 증가하면 USB autosuspend 설정 점검
- 고밀도 환경에서는 AFH(Adaptive Frequency Hopping) 동작과 채널 혼잡도를 함께 관찰
LE Connection Parameter 가이드라인
| 사용 사례 | Connection Interval | Slave Latency | Supervision Timeout |
|---|---|---|---|
| 고속 데이터 전송 (OTA 업데이트) | 7.5~15 ms | 0 | 2~6 s |
| 주기적 센서 데이터 | 100~500 ms | 0~4 | 4~10 s |
| 초저전력 비콘 연결 | 1~4 s | 4~10 | 10~30 s |
| HID (키보드/마우스) | 7.5~15 ms | 0~4 | 2~6 s |
| 오디오 (LE Audio) | 7.5~10 ms | 0 | 2 s |
USB autosuspend 설정
# Bluetooth USB 어댑터의 autosuspend 비활성화 (오디오 끊김 방지)
echo -1 > /sys/bus/usb/devices/1-3/power/autosuspend_delay_ms
# 또는 udev 규칙으로 영구 설정
# /etc/udev/rules.d/99-bluetooth-power.rules
ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="8087", ATTR{idProduct}=="0a2b", \
ATTR{power/autosuspend_delay_ms}="-1"
디버깅과 성능 분석
btmon: 핵심 디버깅 도구
btmon은 HCI와 mgmt 트래픽을 실시간으로 모니터링하는 BlueZ의 핵심 도구입니다.
커널의 HCI 모니터 채널(HCI_CHANNEL_MONITOR)을 통해 패킷을 수집합니다.
# 실시간 HCI + mgmt 트래픽 모니터링
sudo btmon
# 파일로 저장 (BT Snoop 형식, Wireshark에서 열기 가능)
sudo btmon --write /tmp/bluetooth.log
# 특정 어댑터만 모니터링
sudo btmon --index 0
# 타임스탬프와 함께 출력
sudo btmon --date
bluetoothctl 디버깅
# 어댑터 상태 확인
bluetoothctl show
# 장치 검색
bluetoothctl scan on
# 연결된 장치 목록
bluetoothctl devices Connected
# 장치 정보 상세 확인
bluetoothctl info AA:BB:CC:DD:EE:FF
# GATT 서비스 탐색
bluetoothctl menu gatt
bluetoothctl list-attributes
debugfs 인터페이스
커널은 /sys/kernel/debug/bluetooth/에 디버깅 정보를 노출합니다.
# HCI 장치별 디버그 정보
ls /sys/kernel/debug/bluetooth/hci0/
# 주요 파일:
# features - 지원 기능 비트맵
# manufacturer - 벤더 정보
# hci_revision - HCI 리비전
# blacklist - 장치 블랙리스트
# uuids - 등록된 서비스 UUID 목록
# conn_info_min/max_age - 연결 정보 캐시 수명
cat /sys/kernel/debug/bluetooth/hci0/features
커널 로그 분석
# Bluetooth 관련 커널 로그 필터링
dmesg | grep -i -E "bluetooth|hci|l2cap|smp|btusb|rfcomm"
# 동적 디버그 활성화 (상세 추적)
echo "module bluetooth +p" > /sys/kernel/debug/dynamic_debug/control
echo "module btusb +p" > /sys/kernel/debug/dynamic_debug/control
# BT Snoop 로그를 Wireshark로 분석
sudo btmon --write /tmp/bt_snoop.log
# Wireshark에서 File -> Open으로 /tmp/bt_snoop.log 열기
일반적인 문제와 해결
| 증상 | 가능한 원인 | 진단 방법 | 해결 방안 |
|---|---|---|---|
| 페어링 실패 | 키 캐시(Cache) 불일치 | btmon에서 SMP 오류 확인 |
양쪽에서 페어링 정보 삭제 후 재시도 |
| 연결 끊김 반복 | Supervision Timeout, RF 간섭 | btmon에서 Disconnection 이벤트 reason 확인 |
Connection Interval/Timeout 조정, 환경 점검 |
| 오디오 끊김 (A2DP) | USB autosuspend, Wi-Fi 간섭 | btmon에서 ACL 패킷 손실 확인 |
autosuspend 비활성화, Wi-Fi 채널 분리 |
| 어댑터 미인식 | 펌웨어 미설치, 드라이버 미로드 | dmesg에서 firmware 오류 확인 |
linux-firmware 패키지 설치 |
| BLE 스캔 실패 | LE 기능 비활성화, rfkill | rfkill list, btmgmt info |
rfkill unblock bluetooth, LE 활성화 |
/var/lib/bluetooth/ 디렉토리에서 해당 장치 폴더를 직접 삭제할 수도 있습니다.커널 설정
| 옵션 | 설명 | 권장 |
|---|---|---|
CONFIG_BT | Bluetooth 코어 서브시스템 | y |
CONFIG_BT_BREDR | Classic BR/EDR 지원 | y |
CONFIG_BT_LE | BLE(Low Energy) 지원 | y |
CONFIG_BT_RFCOMM | RFCOMM 프로토콜 | m |
CONFIG_BT_RFCOMM_TTY | RFCOMM TTY 지원 (/dev/rfcomm*) | y (RFCOMM 사용 시) |
CONFIG_BT_BNEP | BNEP (Bluetooth PAN) | m |
CONFIG_BT_HIDP | HIDP (HID over Bluetooth) | m |
CONFIG_BT_HCIBTUSB | USB HCI 드라이버 | m |
CONFIG_BT_HCIUART | UART HCI 드라이버 프레임워크 | m |
CONFIG_BT_HCIUART_H4 | UART H4 프로토콜 | y (UART 사용 시) |
CONFIG_BT_HCIUART_3WIRE | UART H5(3-Wire) 프로토콜 | y (3-Wire 필요 시) |
CONFIG_BT_HCIUART_QCA | Qualcomm UART 프로토콜 | y (QCA 칩셋) |
CONFIG_BT_HCISDIO | SDIO HCI 드라이버 | m (SDIO 칩셋) |
CONFIG_BT_INTEL | Intel Bluetooth 지원 | m (Intel 칩셋) |
CONFIG_BT_BCM | Broadcom Bluetooth 지원 | m (Broadcom 칩셋) |
CONFIG_BT_RTL | Realtek Bluetooth 지원 | m (Realtek 칩셋) |
CONFIG_BT_MTK | MediaTek Bluetooth 지원 | m (MediaTek 칩셋) |
CONFIG_BT_QCA | Qualcomm Bluetooth 지원 | m (Qualcomm 칩셋) |
필수 사용자 공간 패키지
| 패키지 | 제공 도구 | 역할 |
|---|---|---|
bluez | bluetoothd, bluetoothctl, btmgmt, btmon | BlueZ 핵심 데몬과 관리 도구 |
bluez-utils | hciconfig, hcitool, sdptool | 레거시 도구 (deprecated, 참조용) |
linux-firmware | - | Bluetooth 칩 펌웨어 파일 |
pipewire-pulse | - | Bluetooth 오디오 통합 (PipeWire) |
bluez-mesh | bluetooth-meshd | Bluetooth Mesh 데몬 |
HCI 패킷 구조와 커널 처리 경로
HCI Command 패킷 상세
HCI Command 패킷은 호스트에서 컨트롤러로 전송되는 제어 명령입니다.
OpCode는 OGF(Opcode Group Field, 6비트)와 OCF(Opcode Command Field, 10비트)로 구성됩니다.
커널에서는 hci_send_cmd()로 명령을 큐에 넣고, hci_cmd_work() 워커가 순차 전송합니다.
| OGF | 그룹 | 대표 OCF | 설명 |
|---|---|---|---|
0x01 | Link Control | Create Connection, Disconnect | ACL/SCO 연결 관리 |
0x02 | Link Policy | Hold Mode, Sniff Mode | 전력 모드 전환 |
0x03 | Host Controller & Baseband | Reset, Set Event Mask | 컨트롤러 기본 설정 |
0x04 | Informational | Read BD_ADDR, Read Buffer Size | 컨트롤러 정보 조회 |
0x05 | Status | Read RSSI, Read Link Quality | 상태/통계 조회 |
0x08 | LE Controller | LE Set Scan, LE Create Connection | BLE 전용 명령 |
ACL Data 패킷 구조
ACL 데이터 패킷은 비동기 데이터 전송에 사용됩니다. 헤더의 Handle 필드에는 연결 핸들(12비트)과 패킷 경계 플래그(PB Flag, 2비트), 브로드캐스트 플래그(BC Flag, 2비트)가 포함됩니다. PB 플래그로 L2CAP PDU의 첫 번째 조각인지 연속 조각인지를 구분합니다.
/* include/net/bluetooth/hci.h - ACL 헤더 구조 */
struct hci_acl_hdr {
__le16 handle; /* 12비트 handle + 2비트 PB + 2비트 BC */
__le16 dlen; /* 데이터 길이 */
} __packed;
#define ACL_START_NO_FLUSH 0x00
#define ACL_CONT 0x01
#define ACL_START 0x02
#define ACL_COMPLETE 0x03
/* PB 플래그 추출 매크로 */
#define hci_handle_pack(h, f) ((__u16) ((h) | ((f) << 12)))
#define hci_handle(h) ((h) & 0x0FFF)
#define hci_flags(h) ((h) >> 12)
btusb에서 hci_core, L2CAP까지의 RX 경로
USB Bluetooth 어댑터에서 수신된 HCI 패킷이 L2CAP 프로토콜 핸들러(Handler)에 도달하기까지의 전체 경로를 추적합니다. 이 흐름을 이해하면 패킷 손실이나 지연의 원인을 정확히 진단할 수 있습니다.
/* net/bluetooth/hci_core.c - ACL 데이터 수신 처리 */
static void hci_acldata_packet(struct hci_dev *hdev,
struct sk_buff *skb)
{
struct hci_acl_hdr *hdr = (void *)skb->data;
struct hci_conn *conn;
__u16 handle = __le16_to_cpu(hdr->handle);
__u16 flags = hci_flags(handle);
handle = hci_handle(handle);
conn = hci_conn_hash_lookup_handle(hdev, handle);
if (!conn) {
bt_dev_err(hdev, "ACL packet for unknown handle %d", handle);
kfree_skb(skb);
return;
}
skb_pull(skb, HCI_ACL_HDR_SIZE);
/* L2CAP로 전달 - PB 플래그에 따라 재조립 수행 */
l2cap_recv_acldata(conn, skb, flags);
}
TX 경로: L2CAP에서 btusb까지
송신 경로는 수신의 역순으로 진행됩니다. l2cap_do_send()가 L2CAP PDU를 생성하고,
hci_send_acl()이 ACL 프래그먼트로 분할한 뒤 hci_tx_work() 워커가
드라이버의 send() 콜백을 호출합니다. 컨트롤러의 버퍼 여유(Num_Completed_Packets 이벤트)를
추적하여 흐름 제어를 수행합니다.
/* net/bluetooth/hci_core.c - ACL TX 흐름 제어 */
static struct hci_conn *hci_low_sent(
struct hci_dev *hdev, __u8 type, int *quote)
{
/* 연결 간 공정 스케줄링: 가장 적게 보낸 연결을 선택 */
/* cnt = hdev->acl_cnt (컨트롤러 여유 버퍼 수) */
/* *quote = cnt / num_connections (연결당 할당량) */
...
}
BLE GATT 프로파일 구현과 커널 경로
ATT 패킷 처리 커널 경로
ATT 프로토콜 패킷은 L2CAP 고정 CID 0x0004를 통해 전달됩니다.
커널에서의 처리 경로는 다음과 같습니다:
l2cap_recv_acldata()에서 CID0x0004확인l2cap_recv_frame()->l2cap_data_channel()- ATT 고정 채널의
recv()콜백 호출 - 커널은 기본적인 ATT 라우팅(Routing)만 수행하고, 실제 GATT 로직은 BlueZ
bluetoothd가 처리
/* net/bluetooth/l2cap_core.c - ATT 고정 채널 등록 */
static struct l2cap_chan *l2cap_le_connect(struct l2cap_conn *conn)
{
struct l2cap_chan *chan;
/* ATT 고정 채널 (CID 0x0004) 검색 */
chan = l2cap_global_fixed_chan(conn, L2CAP_CID_ATT,
&conn->hcon->src);
if (!chan)
return NULL;
/* 채널 상태를 CONNECTED로 전환 */
l2cap_chan_add(conn, chan);
l2cap_chan_ready(chan);
return chan;
}
GATT 서비스 UUID 체계
Bluetooth SIG가 할당한 표준 16비트 UUID는 Base UUID(00000000-0000-1000-8000-00805F9B34FB)의
상위 32비트 중 16비트를 대체하여 128비트로 확장됩니다. 커스텀 서비스는 128비트 UUID를 사용합니다.
| 서비스 | 16비트 UUID | 용도 |
|---|---|---|
| Generic Access | 0x1800 | 장치명, 외형, 연결 파라미터 |
| Generic Attribute | 0x1801 | 서비스 변경 알림 |
| Heart Rate | 0x180D | 심박수 모니터링 |
| Battery Service | 0x180F | 배터리 잔량 |
| Device Information | 0x180A | 모델/시리얼/펌웨어 버전 |
| Current Time | 0x1805 | 시각 동기화 |
| Blood Pressure | 0x1810 | 혈압 모니터링 |
| Environmental Sensing | 0x181A | 온도/습도/압력 등 환경 센서 |
GATT Notification/Indication 메커니즘
GATT 서버가 클라이언트에게 데이터 변경을 능동적으로 알리는 두 가지 방법이 있습니다.
Notification은 확인 응답 없이 전송하고, Indication은 확인을 받아야 다음 전송이 가능합니다.
두 방법 모두 CCCD(Client Characteristic Configuration Descriptor, UUID 0x2902)에
클라이언트가 구독 비트를 기록해야 활성화됩니다.
/* BlueZ bluetoothd - GATT Notification 전송 예시 (D-Bus 기반) */
/* bluetoothctl에서 Characteristic에 대해: */
# select-attribute /org/bluez/hci0/dev_.../service000a/char000b
# notify on <-- CCCD에 0x0001 기록 (Notification 활성화)
# notify off <-- CCCD에 0x0000 기록 (비활성화)
Bluetooth 5.x 신기능
Bluetooth 5.0 주요 기능
| 기능 | 설명 | 커널 관련 |
|---|---|---|
| LE 2M PHY | 2 Mbps 물리 계층, 전송 시간 단축 | HCI_LE_SET_PHY 명령 |
| LE Coded PHY | FEC 코딩으로 도달 거리 4배 확장 | Long Range 모드 |
| Extended Advertising | 보조 채널로 최대 255바이트 광고 | hci_request.c |
| Periodic Advertising | 정기적 브로드캐스트, 수신자 동기화 | Periodic Sync 명령 |
| Advertising Sets | 다중 광고 인스턴스 동시 운영 | adv_instance 관리 |
Bluetooth 5.1 - Direction Finding
Bluetooth 5.1은 AoA(Angle of Arrival)와 AoD(Angle of Departure) 기술로 방향 탐지 기능을 도입했습니다. CTE(Constant Tone Extension)를 BLE 패킷에 부착하여 안테나 배열로 신호 방향을 측정합니다. 실내 위치 추적(IPS, Indoor Positioning System)의 핵심 기술입니다.
| 방식 | 원리 | 안테나 요구 | 사용 사례 |
|---|---|---|---|
| AoA | 수신 측 다중 안테나로 도래각 측정 | 수신기: 배열 안테나 | 에셋 트래킹, 실내 내비게이션 |
| AoD | 송신 측 다중 안테나로 출발각 전달 | 비콘: 배열 안테나 | 실내 측위 비콘 |
Bluetooth 5.2 - LE Audio / EATT / Power Control
Bluetooth 5.2는 LE Audio의 기반 기술을 도입한 핵심 버전입니다:
- ISO Channels (CIS/BIS): 등시성 데이터 전송. CIS는 연결 기반, BIS는 브로드캐스트 기반
- LC3 코덱: SBC 대비 동등 품질에서 50% 낮은 비트레이트
- EATT (Enhanced ATT): L2CAP Enhanced Credit-based Flow Control로 여러 ATT 베어러 동시 사용
- LE Power Control: RSSI 기반 송신 전력 동적 조정
Bluetooth 5.3 - AdvDataInfo / Channel Classification / Encryption Key Size
- AdvDataInfo (ADI) in Periodic Advertising: 중복 수신 방지로 전력 절감
- Channel Classification Enhancement: 서브이벤트별 채널 품질 보고
- Connection Subrating: 연결 이벤트 하위 등급으로 전력 최적화
- LE Enhanced Connection Complete v2: 확장된 연결 완료 이벤트
Bluetooth 5.4 - PAwR / eSL / Encrypted Advertising Data
- PAwR (Periodic Advertising with Responses): 주기적 광고에 대한 응답 기능, ESL(Electronic Shelf Labels) 핵심 기술
- Encrypted Advertising Data: 광고 데이터 암호화로 프라이버시 강화
- LE GATT Security Levels Characteristic: GATT 서비스별 보안 수준 광고
Channel Sounding (Bluetooth 6.0)
Bluetooth 6.0에서 도입된 Channel Sounding은 센티미터 수준의 정밀 거리 측정 기술입니다. 기존 RSSI 기반 근접 감지의 한계를 극복하며, Phase-Based Ranging과 Round-Trip Time(RTT) 방식을 결합합니다. 디지털 키(자동차 잠금(Lock) 해제), 물리적 근접 검증, 실내 위치 추적에 핵심적입니다. 리눅스 커널에서의 지원은 6.x 이후 버전에서 HCI 명령/이벤트 추가로 진행 중입니다.
커널 소스 구조 (net/bluetooth/ 맵)
net/bluetooth/ 디렉토리 구조
Bluetooth 코어 프로토콜 스택의 소스 코드는 net/bluetooth/ 디렉토리에 위치합니다.
각 파일의 역할과 상호 의존 관계를 이해하면 커널 디버깅과 패치 작성이 수월해집니다.
| 파일 | 줄 수 (6.x 기준) | 역할 |
|---|---|---|
hci_core.c | ~4,000 | HCI 코어: 장치 등록, RX/TX 워크큐, 연결 관리 |
hci_event.c | ~7,000 | HCI 이벤트 디스패치(Dispatch): Command Complete, LE 이벤트 등 |
hci_request.c | ~2,500 | HCI 요청 직렬화(Serialization), 초기화 시퀀스 |
hci_conn.c | ~2,500 | ACL/LE/SCO 연결 생성/해제/관리 |
hci_sync.c | ~6,000 | 동기화된 HCI 명령 실행 (6.x 신규 인프라) |
hci_sock.c | ~2,000 | HCI 소켓: raw/mgmt/monitor 채널 |
l2cap_core.c | ~7,500 | L2CAP 코어: 채널 관리, 시그널링, 재조립 |
l2cap_sock.c | ~1,800 | L2CAP 소켓 인터페이스 |
smp.c | ~3,500 | SMP: 페어링, 키 교환, 암호화 함수 |
mgmt.c | ~9,000 | mgmt API: 장치 관리 명령 처리 |
iso.c | ~2,000 | ISO 채널 소켓 (LE Audio) |
sco.c | ~1,500 | SCO/eSCO 동기 음성 소켓 |
af_bluetooth.c | ~800 | AF_BLUETOOTH 소켓 패밀리 등록 |
lib.c | ~300 | Bluetooth 유틸리티 함수 |
drivers/bluetooth/ 디렉토리 구조
| 파일 | 역할 |
|---|---|
btusb.c | USB HCI 드라이버 (~4,500줄), 대부분의 USB 어댑터 지원 |
hci_ldisc.c | UART Line Discipline 기반 HCI 프레임워크 |
hci_serdev.c | serdev(Serial Device) 기반 HCI 프레임워크 (최신) |
hci_h4.c / hci_h5.c | H:4, H:5(3-Wire) UART 프로토콜 |
hci_qca.c | Qualcomm UART 프로토콜 (IBS 슬립 지원) |
btintel.c | Intel 벤더 초기화 (~3,000줄) |
btrtl.c | Realtek 벤더 초기화 |
btbcm.c | Broadcom 벤더 초기화 |
btmtk.c | MediaTek 벤더 초기화 |
btqca.c | Qualcomm 벤더 초기화 |
btsdio.c | SDIO HCI 드라이버 |
virtio_bt.c | VirtIO Bluetooth 드라이버 (가상화(Virtualization)) |
btmon/hcidump 디버깅
btmon 고급 사용법
btmon은 커널의 HCI_CHANNEL_MONITOR를 사용하여 모든 HCI 및 mgmt 트래픽을
사용자 공간에서 캡처합니다. Wireshark의 BT Snoop 형식과 호환되며,
원격 장비에서 캡처한 로그를 워크스테이션에서 분석할 수 있습니다.
# 상세 타임스탬프 + 특정 어댑터 모니터링
sudo btmon --index 0 --date --time
# BT Snoop 형식으로 저장 (Wireshark에서 분석용)
sudo btmon --write /tmp/bt_trace.snoop
# 기존 스눕 파일 읽기 (오프라인 분석)
btmon --read /tmp/bt_trace.snoop
# 실시간 + 파일 저장 동시 수행
sudo btmon --write /tmp/bt_trace.snoop 2>&1 | tee /tmp/bt_console.log
# Priority 기반 필터 (priority는 btmon 자체는 미지원, 대안:)
# Wireshark에서 btmon 파일을 열고 Display Filter 적용
# 예: bthci_evt.code == 0x0e (Command Complete)
hcidump (레거시 도구)
hcidump는 btmon 이전에 사용되던 레거시 캡처 도구입니다.
현재는 btmon 사용을 권장하지만, 일부 환경에서 여전히 유용합니다.
# 전체 패킷 덤프
sudo hcidump -X
# 특정 패킷 타입만 필터링
sudo hcidump -t HCI # HCI 이벤트/명령만
sudo hcidump -t L2CAP # L2CAP 패킷만
sudo hcidump -t SMP # SMP 페어링 패킷만
# pcap 형식으로 저장
sudo hcidump -w /tmp/bt.pcap
Wireshark로 Bluetooth 트래픽 분석
btmon의 BT Snoop 출력을 Wireshark에서 열면 프로토콜별 계층 분석이 가능합니다. 주요 Display Filter:
| 필터 | 대상 |
|---|---|
bthci_cmd | HCI 명령 패킷 |
bthci_evt | HCI 이벤트 패킷 |
bthci_acl | HCI ACL 데이터 |
btl2cap | L2CAP 프레임 |
btatt | ATT 프로토콜 (GATT) |
btsmp | SMP 페어링 |
btrfcomm | RFCOMM 데이터 |
btatt.opcode == 0x1b | ATT Notification만 |
bthci_evt.code == 0x3e | LE Meta Event만 |
커널 tracing으로 Bluetooth 디버깅
# ftrace로 Bluetooth 함수 추적
echo 'hci_*' > /sys/kernel/debug/tracing/set_ftrace_filter
echo 'l2cap_*' >> /sys/kernel/debug/tracing/set_ftrace_filter
echo function > /sys/kernel/debug/tracing/current_tracer
echo 1 > /sys/kernel/debug/tracing/tracing_on
# 추적 결과 확인
cat /sys/kernel/debug/tracing/trace
# Bluetooth tracepoint 이벤트
ls /sys/kernel/debug/tracing/events/bluetooth/
# hci_send_cmd, hci_recv_frame, l2cap_send, l2cap_recv 등
# 특정 tracepoint 활성화
echo 1 > /sys/kernel/debug/tracing/events/bluetooth/hci_recv_frame/enable
Bluetooth/Wi-Fi 공존 메커니즘
간섭 문제
Bluetooth(2.402~2.480 GHz)와 Wi-Fi 2.4 GHz(2.412~2.462 GHz)는 동일한 ISM 대역을 공유합니다. 특히 Wi-Fi 채널 1, 6, 11과 Bluetooth 호핑 채널이 겹칠 수 있으며, 콤보 칩(같은 안테나 공유)에서는 물리적으로 동시 전송이 불가능합니다. 이 문제를 해결하기 위해 여러 공존 메커니즘이 사용됩니다.
공존 메커니즘 비교
| 메커니즘 | 방식 | 특징 | 지원 칩셋 |
|---|---|---|---|
| AFH (Adaptive Frequency Hopping) | Bluetooth가 Wi-Fi 점유 채널을 회피 | Bluetooth 자체 기능, 모든 BT 2.0+ 지원 | 표준 Bluetooth 기능 |
| 2-wire Coex (PTA) | BT_ACTIVE/BT_STATUS 신호로 우선순위(Priority) 중재 | 하드웨어 GPIO 연결 필요, 반응 빠름 | 대부분의 콤보/분리 칩 |
| 3-wire Coex | 2-wire + BT_PRIORITY 신호 추가 | SCO 음성 등 우선순위 높은 트래픽 보호 | Intel, Qualcomm 일부 |
| TDM (Time Division Multiplexing) | 시분할로 BT/Wi-Fi 교대 사용 | 콤보 칩 내부 펌웨어에서 처리 | 대부분의 콤보 칩 |
| MWS (Mobile Wireless Standard) | LTE/Wi-Fi/BT 3자 간 공존 | HCI MWS 명령으로 커널에서 제어 가능 | 모바일 SoC |
커널에서의 공존 설정
# Intel 칩셋: btintel coex 파라미터
# dmesg에서 공존 설정 확인
dmesg | grep -i coex
# iwlwifi + btusb 콤보에서 공존 상태 확인
cat /sys/kernel/debug/iwlwifi/*/bt_coex
# Qualcomm 칩: IBS(In-Band Sleep)와 공존
# Device Tree에서 공존 핀 설정
# bt-coex-pin = <GPIO_NUM>;
# Wi-Fi 채널을 5 GHz로 전환하여 간섭 회피 (가장 효과적)
iw dev wlan0 set channel 36
커널 버전별 Bluetooth 변경 이력
| 커널 버전 | 주요 변경 사항 |
|---|---|
3.0 | Bluetooth 4.0 LE 기본 지원 추가 |
3.5 | mgmt API 도입 (hciconfig/hcitool 대체 시작) |
3.12 | LE Dual Mode, Privacy 1.1 지원 |
3.16 | LE Secure Connections (Bluetooth 4.2) 지원 |
3.19 | Privacy 1.2 (RPA Resolution Offloading) |
4.2 | Extended Advertising 예비 코드, LE Data Length Extension |
4.8 | LE PHY 2M/Coded 지원 (Bluetooth 5.0) |
4.20 | Extended Advertising / Periodic Advertising 지원 |
5.0 | Mesh 지원 기반, LE Extended Scanning |
5.4 | Advertising Sets 다중 인스턴스, hci_sync.c 기반 리팩토링 시작 |
5.12 | MSFT Vendor Extension (모니터 광고), mgmt Quality Report |
5.13 | hci_sync.c 도입: 비동기 HCI 요청을 동기화된 워크큐로 대체 |
5.15 | ISO 소켓(BTPROTO_ISO), CIS/BIS 기본 지원 (LE Audio) |
5.19 | LC3 코덱 ID 등록, ISO Test 모드 |
6.0 | LE BIG(Broadcast Isochronous Group) 생성/종료, hci_request.c 제거 시작 |
6.2 | Quality of Service 개선, ISO 소켓 안정화 |
6.3 | EATT(Enhanced ATT) 커널 측 지원 개선 |
6.5 | hci_sync.c 전면 전환 완료, hci_request.c 제거 |
6.7 | ISO Connected/Broadcast 안정화, 다중 CIS 지원 개선 |
6.8+ | PAwR(Periodic Advertising with Responses) 초기 지원 |
hci_request.c의 비동기 HCI 요청 메커니즘이
hci_sync.c의 동기화된 워크큐 기반으로 완전히 대체되었습니다.
이 리팩토링은 레이스 컨디션과 타이밍 관련 버그를 대폭 줄였으며,
새로운 Bluetooth 기능(ISO, Extended Advertising 등)의 구현을 크게 단순화했습니다.
L2CAP ERTM/Streaming 모드
ERTM (Enhanced Retransmission Mode) 상세
ERTM은 L2CAP에 TCP와 유사한 신뢰성을 제공합니다. I-Frame(Information Frame)에 시퀀스 번호 (TxSeq/ReqSeq)를 부여하고, S-Frame(Supervisory Frame)으로 흐름 제어와 재전송을 관리합니다. OBEX 파일 전송이나 AVRCP 같은 신뢰성이 중요한 프로토콜에서 사용됩니다.
ERTM 프레임 타입
| 프레임 | 유형 | 설명 |
|---|---|---|
| I-Frame | 정보 | TxSeq + ReqSeq 포함, SDU 데이터 전달 |
| S-Frame (RR) | 감독 | Receiver Ready: ACK + 수신 준비 완료 |
| S-Frame (REJ) | 감독 | Reject: 특정 시퀀스부터 재전송 요청 (Go-Back-N) |
| S-Frame (SREJ) | 감독 | Selective Reject: 특정 시퀀스만 재전송 요청 |
| S-Frame (RNR) | 감독 | Receiver Not Ready: 흐름 제어 일시 정지 |
/* net/bluetooth/l2cap_core.c - ERTM I-Frame 전송 */
static void l2cap_ertm_send(struct l2cap_chan *chan)
{
struct sk_buff *skb;
while (chan->tx_send_head &&
chan->unacked_frames < chan->remote_tx_win) {
skb = chan->tx_send_head;
/* TxSeq/ReqSeq 설정 */
l2cap_set_txseq(chan, skb);
chan->next_tx_seq = (chan->next_tx_seq + 1) %
chan->tx_win_max;
chan->unacked_frames++;
/* 재전송 타이머 시작 */
l2cap_set_retrans_timer(chan);
l2cap_do_send(chan, skb);
}
}
ERTM 타이머(Timer)
| 타이머 | 기본값 | 용도 |
|---|---|---|
| Retransmission Timer | 2000 ms | I-Frame ACK 미수신 시 재전송 트리거 |
| Monitor Timer | 12000 ms | S-Frame 폴 요청 후 응답 대기 |
| Acknowledgement Timer | 200 ms | 지연 ACK: 데이터 없을 때 단독 ACK 전송 |
Streaming Mode
Streaming Mode는 ERTM과 유사하지만 재전송이 없습니다. 순서 번호로 패킷 누락을 감지하되, 재전송 대신 상위 계층에 누락을 알립니다. A2DP 오디오 스트리밍처럼 약간의 패킷 손실보다 지연이 더 치명적인 경우에 적합합니다.
LE Credit-based Flow Control (LE CoC)
BLE에서 대용량 데이터를 전송할 때 사용하는 모드입니다. 수신 측이 크레딧(Credit)을 발급하고, 송신 측은 크레딧만큼만 SDU를 전송할 수 있습니다. 크레딧이 소진되면 수신 측이 추가 크레딧을 발급할 때까지 대기합니다. GATT의 20바이트 ATT MTU 제한을 우회하여 수백 KB 단위 데이터를 효율적으로 전송할 수 있습니다.
/* net/bluetooth/l2cap_core.c - LE CoC 크레딧 관리 */
static void l2cap_chan_le_send_credits(struct l2cap_chan *chan)
{
struct l2cap_le_credits pkt;
u16 return_credits;
/* 수신 버퍼 여유량에 기반하여 크레딧 발급 */
return_credits = chan->rx_credits_max - chan->rx_credits;
if (return_credits < chan->rx_credits_max / 2)
return;
pkt.cid = cpu_to_le16(chan->scid);
pkt.credits = cpu_to_le16(return_credits);
chan->rx_credits += return_credits;
l2cap_send_cmd(chan->conn, chan->ident,
L2CAP_LE_CREDITS, sizeof(pkt), &pkt);
}
SMP 페어링 상세
IO Capability 매트릭스
SMP 페어링 방식은 양쪽 장치의 IO Capability 조합에 의해 자동으로 결정됩니다. 다음 매트릭스는 LE Secure Connections 모드에서의 페어링 방식 선택을 보여줍니다:
| Initiator \ Responder | DisplayOnly | DisplayYesNo | KeyboardOnly | NoInputNoOutput | KeyboardDisplay |
|---|---|---|---|---|---|
| DisplayOnly | Just Works | Just Works | Passkey Entry | Just Works | Passkey Entry |
| DisplayYesNo | Just Works | Numeric Comparison | Passkey Entry | Just Works | Numeric Comparison |
| KeyboardOnly | Passkey Entry | Passkey Entry | Passkey Entry | Just Works | Passkey Entry |
| NoInputNoOutput | Just Works | Just Works | Just Works | Just Works | Just Works |
| KeyboardDisplay | Passkey Entry | Numeric Comparison | Passkey Entry | Just Works | Numeric Comparison |
OOB (Out-of-Band) 페어링
OOB 페어링은 Bluetooth 무선 채널이 아닌 외부 채널(NFC, QR 코드 등)을 통해
인증 데이터를 교환합니다. NFC 탭으로 페어링하는 경우가 대표적입니다.
커널에서는 SMP_CMD_PAIRING_REQ의 OOB 플래그와
mgmt API의 Add Remote OOB Data 명령으로 처리합니다.
/* net/bluetooth/smp.c - OOB 데이터 확인 */
static u8 smp_cmd_pairing_req(struct l2cap_conn *conn,
struct sk_buff *skb)
{
...
/* OOB 데이터 가용 여부 확인 */
if (req->oob_flag == SMP_OOB_PRESENT) {
/* 로컬에 OOB 데이터가 있으면 사용 */
if (test_bit(SMP_FLAG_LOCAL_OOB, &smp->flags))
smp->method = REQ_OOB;
}
...
}
Passkey Entry 내부 동작
Passkey Entry에서는 6자리 숫자(000000~999999)를 20비트로 변환하여 각 비트에 대해 Confirm/Random 교환을 반복합니다(LE SC 모드). 총 20라운드의 교환으로 MITM을 방어합니다. Legacy Pairing에서는 단일 라운드로 처리되어 보안이 약합니다.
Numeric Comparison
Numeric Comparison은 LE Secure Connections에서만 사용됩니다. ECDH 키 교환 후 양쪽이 동일한 6자리 숫자를 계산하여 표시하고, 사용자가 양쪽의 숫자가 같은지 확인하여 Yes/No로 응답합니다. 패시브/능동 MITM 공격 모두에 강력한 방어를 제공합니다.
Address Resolution과 Privacy
BLE Privacy 기능은 Resolvable Private Address(RPA)를 사용하여 장치 추적을 방지합니다.
RPA는 IRK(Identity Resolving Key)로 생성/검증되며, 주기적으로 변경됩니다(기본 15분).
커널의 hci_conn.c에서 RPA 해석을 수행하며,
하드웨어 오프로딩(Offloading)이 가능한 칩셋에서는 컨트롤러가 직접 해석합니다.
# Privacy 활성 상태 확인
btmgmt info
# Privacy: enabled (addr-resolution: on, rpa-timeout: 900s)
# RPA timeout 설정 (초 단위, 기본 900초 = 15분)
# /sys/kernel/debug/bluetooth/hci0/rpa_timeout
Bluetooth Mesh 프로비저닝/릴레이
프로비저닝 프로토콜 상세
Mesh 프로비저닝은 비보안 상태의 장치를 안전하게 네트워크에 추가하는 과정입니다. ECDH 키 교환과 인증을 거쳐 NetKey, AppKey, Unicast Address를 배포합니다.
- Beaconing: 비프로비저닝 장치가 Unprovisioned Device Beacon을 브로드캐스트
- Invitation: Provisioner가 Provisioning Invite PDU를 전송, 장치가 Capabilities로 응답
- Public Key Exchange: ECDH P-256 공개키 교환 (OOB 또는 In-Band)
- Authentication: Output OOB(LED 깜빡임), Input OOB(버튼 누름), Static OOB(QR 코드), No OOB 중 선택
- Confirmation: 양쪽이 Confirm/Random 값을 교환하여 인증 확인
- Distribution: Provisioner가 Provisioning Data(NetKey, IV Index, Unicast Address)를 암호화하여 전송
Mesh 보안 계층
| 키 | 범위 | 용도 |
|---|---|---|
| NetKey | 네트워크 전체 | Network Layer 암호화/인증, 네트워크 접근 제어(Access Control) |
| AppKey | 애플리케이션 범위 | Application Layer 암호화, 서비스별 접근 제어 |
| DevKey | 장치별 고유 | 프로비저너와 장치 간 설정 통신 |
Relay 동작 상세
Relay 노드는 수신한 Network PDU를 재전송하여 메시지 도달 범위를 확장합니다. 중복 전송 방지를 위해 Network Message Cache에 최근 메시지의 (SRC, SEQ) 쌍을 저장하고, 이미 처리한 메시지는 폐기합니다. TTL이 0이면 더 이상 릴레이하지 않습니다.
Mesh Relay 결정 로직은 다음과 같은 순서로 동작합니다 (bluetooth-meshd 내부).
- TTL > 1인지 확인합니다.
- SRC가 자신이 아닌지 확인합니다.
- Network Message Cache에 (SRC, SEQ) 쌍이 없는지 확인합니다.
- 조건 충족 시 TTL-1로 재전송합니다.
- 캐시에 (SRC, SEQ)를 추가합니다.
Friend/Low Power 노드 상호작용
Low Power(LP) 노드는 대부분의 시간을 슬립 상태로 보내며, Friend 노드가 LP 노드 대신 메시지를 수신/저장합니다. LP 노드는 주기적으로 깨어나 Friend Poll 메시지를 보내 대기 중인 메시지를 수신합니다.
| 파라미터 | 설명 | 전력 영향 |
|---|---|---|
| PollTimeout | LP 노드가 폴링하는 최대 간격 | 길수록 절전, 짧을수록 응답 빠름 |
| ReceiveDelay | Friend Update 수신 전 대기 시간(Latency) | 라디오 온 시간에 영향 |
| ReceiveWindow | 메시지 수신을 위한 창 크기 | 넓을수록 수신 확률 증가 |
| Friend Queue Size | Friend가 저장하는 최대 메시지 수 | 메모리 사용량에 영향 |
Directed Forwarding (Mesh Protocol 1.1)
Mesh Protocol 1.1에서 도입된 Directed Forwarding은 기존 Managed Flooding 방식의 비효율성을 개선합니다. 경로 탐색(Path Discovery)을 통해 최적 경로를 찾고, 해당 경로의 노드만 메시지를 중계하여 네트워크 트래픽을 크게 줄입니다. 대규모 Mesh 네트워크(수백 노드 이상)에서 특히 효과적입니다.
Bluetooth 에러 코드와 Disconnection Reason
HCI 에러 코드
HCI 이벤트에서 반환되는 에러 코드는 연결 실패, 끊김, 명령 거부의 원인을 진단하는 핵심 정보입니다.
btmon에서 확인 가능하며, 커널 로그(dmesg)에도 출력됩니다.
| 에러 코드 | 이름 | 의미 | 진단 포인트 |
|---|---|---|---|
0x05 | Authentication Failure | 인증 실패 (키 불일치) | 양쪽 페어링 정보 삭제 후 재시도 |
0x06 | PIN/Key Missing | 링크 키 없음 | 재페어링 필요 |
0x08 | Connection Timeout | 연결 시도 시간 초과 | RF 환경, 거리, 간섭 확인 |
0x0E | Rejected (Limited Resources) | 리소스 부족으로 거부 | 최대 연결 수 확인 |
0x13 | Remote User Terminated | 원격 장치가 의도적 연결 해제 | 정상 종료, 문제 아님 |
0x16 | Connection Terminated (Local) | 로컬 호스트가 연결 해제 | 소프트웨어 의도적 종료 |
0x22 | LMP Response Timeout | LMP 응답 시간 초과 | 컨트롤러 간 통신 문제 |
0x3B | Unacceptable Connection Parameters | 연결 파라미터 거부 | Interval/Latency/Timeout 조정 |
0x08 (Connection Timeout) 반복 시:
btmon에서 마지막 유효 패킷 시점을 확인하고,
Supervision Timeout 값과 비교하세요.
Wi-Fi 간섭이 의심되면 iw dev wlan0 station dump로
Wi-Fi 측 재전송률도 함께 확인합니다.
실전 시나리오와 트러블슈팅
시나리오 1: BLE 센서 데이터 수집 게이트웨이
리눅스 장치(Raspberry Pi 등)를 BLE Central로 사용하여 다수의 BLE 센서에서 데이터를 수집하는 산업용 게이트웨이 시나리오입니다.
# 1. 어댑터 상태 확인
btmgmt info
# supported-settings: powered connectable le
# current-settings: powered le
# 2. LE 스캔 시작 (passive, 중복 허용)
btmgmt find -l # LE only 스캔
# 3. bluetoothctl로 연결 및 GATT 서비스 탐색
bluetoothctl
# [bluetooth]# scan on
# [bluetooth]# connect AA:BB:CC:DD:EE:FF
# [bluetooth]# menu gatt
# [bluetooth]# list-attributes
# [bluetooth]# select-attribute /org/bluez/hci0/dev_.../service000a/char000b
# [bluetooth]# read
# [bluetooth]# notify on
# 4. 다수 연결 시 파라미터 튜닝
# Connection Interval을 100ms 이상으로 설정하여 다수 연결 안정화
# Supervision Timeout은 10초 이상 권장
시나리오 2: A2DP 오디오 끊김 해결
# 1. btmon으로 패킷 손실 확인
sudo btmon --write /tmp/a2dp_debug.snoop
# 2. USB autosuspend 비활성화
# Bluetooth 어댑터의 USB 경로 확인
lsusb -t | grep -i bluetooth
echo -1 > /sys/bus/usb/devices/1-3/power/autosuspend_delay_ms
# 3. Wi-Fi 간섭 확인
iw dev wlan0 station dump | grep -E "signal|tx retries"
# Wi-Fi 재전송이 높으면 5GHz로 전환
iw dev wlan0 set channel 36
# 4. A2DP 코덱 확인 (PipeWire)
pw-dump | grep -A5 bluetooth
# SBC -> AAC 또는 LDAC로 변경 시도
시나리오 3: 임베디드 BLE Peripheral 개발
# 리눅스를 BLE Peripheral (GATT Server)로 설정
# 1. 광고 활성화
btmgmt advertising on
btmgmt name "Linux-Sensor"
btmgmt connectable on
# 2. bluetoothd에 GATT 서비스 등록 (D-Bus API 사용)
# Python 예시 (bluez의 example-gatt-server.py 참조)
# dbus.service.Object로 GattService1, GattCharacteristic1 구현
# 3. 광고 데이터에 서비스 UUID 포함
# bluetoothctl: advertise on
# advertise.uuids 0x180D (Heart Rate Service)
관련 커널 데이터 구조 요약
주요 자료구조 관계
| 구조체 | 파일 | 역할 | 주요 필드 |
|---|---|---|---|
struct hci_dev |
hci_core.h |
Bluetooth 어댑터 표현 | name, flags, conn_hash, cmd_q, open/close/send 콜백 |
struct hci_conn |
hci_core.h |
ACL/LE/SCO 연결 표현 | handle, type, state, dst(BD_ADDR), l2cap_data |
struct l2cap_conn |
l2cap_core.h |
L2CAP 연결 (hci_conn에 1:1 매핑(Mapping)) | hcon, chan_list, info_state, feat_mask |
struct l2cap_chan |
l2cap_core.h |
L2CAP 채널 | scid/dcid, psm, state, mode, imtu/omtu, ops 콜백 |
struct smp_chan |
smp.c |
SMP 페어링 상태 | method, flags, preq/prsp, tk, ltk, irk, csrk |
struct bt_sock |
bluetooth.h |
Bluetooth 소켓 기본 구조 | sk, parent, flags |
struct mgmt_hdr |
mgmt.h |
mgmt API 패킷 헤더 | opcode, index, len |
struct btusb_data |
btusb.c |
btusb 드라이버 인스턴스 데이터 | hdev, udev, intf, intr/bulk/isoc URB |
/* include/net/bluetooth/hci_core.h - hci_conn 구조체 주요 필드 */
struct hci_conn {
struct list_head list;
atomic_t refcnt;
bdaddr_t dst; /* 원격 장치 주소 */
__u8 dst_type; /* 주소 타입 (Public/Random) */
__u16 handle; /* HCI 연결 핸들 */
__u16 state; /* BT_CONNECTED, BT_OPEN 등 */
__u8 type; /* ACL_LINK, LE_LINK, SCO_LINK */
__u8 sec_level; /* 현재 보안 레벨 */
__u8 pending_sec_level;
__u16 le_conn_interval;
__u16 le_conn_latency;
__u16 le_supv_timeout;
struct l2cap_conn *l2cap_data; /* L2CAP 연결 데이터 */
struct smp_chan *smp_chan; /* SMP 채널 (페어링 중) */
struct hci_dev *hdev; /* 소속 어댑터 */
void *l2cap_data;
void *sco_data;
void *iso_data;
};