Bluetooth 커널 서브시스템 심화
Linux Bluetooth는 net/bluetooth/ 코어와 BlueZ 사용자 공간 도구가 결합되어 동작합니다. 이 문서는 커널 내부 데이터 경로, 드라이버 계층, 보안, 성능, 디버깅 관점에서 Bluetooth Classic과 BLE를 함께 정리합니다. Bluetooth Core Specification 5.4 기준으로 프로토콜 스택 전반, Mesh, LE Audio까지 포괄합니다.
핵심 요약
- HCI -- 호스트와 컨트롤러 사이 표준 명령/이벤트 인터페이스
- L2CAP -- 채널 기반 다중화, 재조립, 흐름 제어
- 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 |
인터럽트/벌크/아이소크러너스 엔드포인트 활용, 가장 범용적 | PC/노트북 내장 어댑터, USB 동글 |
| UART (H4) | hci_uart |
단순 직렬 프로토콜, 패킷 타입 바이트로 구분 | 임베디드 SoC, Raspberry Pi |
| UART (H5/3-Wire) | hci_uart |
슬립 모드, CRC, 재전송 지원으로 안정성 향상 | 전력 관리가 중요한 IoT 장치 |
| SDIO | btsdio |
SD 버스 기반 전송, 주로 WiFi+BT 콤보 칩에서 사용 | 모바일/태블릿 콤보 모듈 |
| SPI | 벤더별 구현 | 저전력, 단순 인터페이스 | 웨어러블, 센서 노드 |
BlueZ 커널 모듈 구조
리눅스 커널의 Bluetooth 구현은 net/bluetooth/ 디렉토리에 모듈화되어 있습니다.
각 모듈은 독립적으로 빌드 가능하며 CONFIG_BT_* 옵션으로 제어됩니다.
| 커널 모듈 | 소스 위치 | 역할 |
|---|---|---|
bluetooth | net/bluetooth/ | 코어: AF_BLUETOOTH 소켓, 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는 네 가지 패킷 타입을 정의합니다. 각 패킷은 첫 번째 바이트(패킷 인디케이터)로 타입을 구분하며, 이후 타입별 헤더와 페이로드가 따릅니다.
| 패킷 타입 | 인디케이터 | 방향 | 헤더 크기 | 용도 |
|---|---|---|---|---|
| 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 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() 콜백으로 전송합니다.
컨트롤러 응답은 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 명령 전송 시 |
L2CAP (Logical Link Control and Adaptation Protocol)
채널 멀티플렉싱
L2CAP는 하나의 ACL 링크 위에 여러 논리 채널을 다중화합니다. 각 채널은 CID(Channel Identifier)로 구분되며, 고정 CID(0x0001~0x003F)와 동적 CID(0x0040~)로 나뉩니다. 동적 채널은 PSM(Protocol/Service Multiplexer) 값으로 상위 프로토콜을 식별합니다.
| CID | 용도 | 프로토콜 |
|---|---|---|
0x0001 | L2CAP 시그널링 (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 |
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 | 순서 보장, 재전송 없음 (실시간 우선) | 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 직렬 포트를 에뮬레이션하는 프로토콜입니다. 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 |
| 연결 지연 | 100 ms 이상 | 수 ms~수십 ms |
| 전력 소모 | 상대적으로 높음 | 매우 낮음 (코인 셀 배터리 수 년) |
| 토폴로지 | 포인트 투 포인트 (피코넷) | 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을 그룹화하는 최상위 컨테이너. 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 | 비암호화 데이터의 서명 검증 | /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 노드의 메시지를 임시 저장하고 폴링 시 전달 | 메모리와 상시 전원 필요 |
| 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 | 실시간 오디오 (대역폭 보장) |
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 | 펌웨어 로드, DDC(Device Dynamic Configuration), TLV 파싱 |
btbcm | Broadcom | btbcm.c | 펌웨어(.hcd) 로드, 패치 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 프로브 흐름
/* 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 열기
일반적인 문제와 해결
| 증상 | 가능한 원인 | 진단 방법 | 해결 방안 |
|---|---|---|---|
| 페어링 실패 | 키 캐시 불일치 | 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 데몬 |