HCI(Host Controller Interface)是蓝牙协议栈中主机(Host)与控制器(Controller)之间的标准接口,是整个蓝牙通信的基石。本文基于蓝牙核心规范与 BlueZ 5.x 全套源码,从协议标准出发,逐层对应到 BlueZ 的具体实现,讲清协议字段如何映射为 C 结构体、指令流程如何封装为函数调用、事件上报如何分发到业务逻辑。
目录
[一、HCI 协议核心框架](#一、HCI 协议核心框架)
[二、BlueZ Lib 层:HCI 协议的原生封装](#二、BlueZ Lib 层:HCI 协议的原生封装)
[三、BlueZ Shared 层:HCI 的面向对象封装](#三、BlueZ Shared 层:HCI 的面向对象封装)
[四、Management Interface:HCI 的更高层抽象](#四、Management Interface:HCI 的更高层抽象)
[五、HCI 协议字段与 BlueZ 源码完整映射表](#五、HCI 协议字段与 BlueZ 源码完整映射表)
一、 HCI 协议核心框架
1.1 HCI 分层定位
HCI 位于蓝牙协议栈的中间层,是主机侧与控制器侧的边界:
暂时无法在飞书文档外展示此内容
1.2 四大报文类型
HCI 协议定义了四种基本报文类型,每种报文有独立的头部结构:
|----------------|---------|-------------------|-------------|----------|
| 报文类型 | 类型值 | 方向 | 作用 | 头部大小 |
| Command(命令) | 0x01 | Host → Controller | 主机下发控制指令 | 3 字节 |
| ACL Data(异步数据) | 0x02 | Host ↔ Controller | 异步面向连接的数据传输 | 4 字节 |
| SCO Data(同步数据) | 0x03 | Host ↔ Controller | 同步面向连接的语音数据 | 3 字节 |
| Event(事件) | 0x04 | Host ← Controller | 控制器上报状态/结果 | 2 字节 |
BlueZ 源码 中的类型定义:
cpp
/* HCI Packet types */
#define HCI_COMMAND_PKT 0x01
#define HCI_ACLDATA_PKT 0x02
#define HCI_SCODATA_PKT 0x03
#define HCI_EVENT_PKT 0x04
#define HCI_ISODATA_PKT 0x05
#define HCI_VENDOR_PKT 0xff
1.3 命令(Command)报文结构
命令报文由主机发往控制器,用于控制蓝牙行为:
cpp
0 16 24 24+N
+--------+--------+--------+
| Opcode | Plen | Param |
| (16bit)| (8bit) | |
+--------+--------+--------+
OGF OCF
(6b) (10b)
-
Opcode (16位) :命令操作码,由 OGF(6位 opcode group field)和 OCF(10位 opcode command field)组成Plen(8位):参数长度,指示参数部分的字节数
-
Param(变长):命令参数
BlueZ 源码 中的命令头结构体:
cpp
typedef struct {
uint16_t opcode; /* OCF & OGF */
uint8_t plen;
} __attribute__ ((packed)) hci_command_hdr;
#define HCI_COMMAND_HDR_SIZE 3
Opcode 打包/解包宏:
cpp
/* Command opcode pack/unpack */
#define cmd_opcode_pack(ogf, ocf) (uint16_t)((ocf & 0x03ff)|(ogf << 10))
#define cmd_opcode_ogf(op) (op >> 10)
#define cmd_opcode_ocf(op) (op & 0x03ff)
1.4 事件(Event)报文结构
事件报文由控制器发往主机,用于上报命令执行结果和异步事件:
cpp
0 8 16 16+N
+--------+--------+--------+
| Event | Plen | Param |
| (8bit) | (8bit) | |
+--------+--------+--------+
-
Event(8位):事件码,标识事件类型
-
Plen(8位):参数长度
-
Param(变长):事件参数
BlueZ 源码 中的事件头结构体:
cpp
typedef struct {
uint8_t evt;
uint8_t plen;
} __attribute__ ((packed)) hci_event_hdr;
#define HCI_EVENT_HDR_SIZE 2
1.5 ACL Data 报文结构
ACL(Asynchronous Connection-Oriented)数据用于异步数据传输:
cpp
0 12 14 16 32 32+N
+--------+--+----+--------+--------+
| Handle |PB| BC | Data | Data |
| (12bit)| | | Len | Payload|
+--------+--+----+--------+--------+
-
Handle(12位):连接句柄,标识具体的 ACL 连接
-
PB (2位):Packet Boundary Flag,报文边界标志
-
BC(2位):Broadcast Flag,广播标志
-
Data Len(16位):数据载荷长度
BlueZ 源码 中的 ACL 头结构体:
cpp
typedef struct {
uint16_t handle; /* Handle & Flags(PB, BC) */
uint16_t dlen;
} __attribute__ ((packed)) hci_acl_hdr;
#define HCI_ACL_HDR_SIZE 4
Handle/Flags 打包/解包宏:
cpp
/* ACL handle and flags pack/unpack */
#define acl_handle_pack(h, f) (uint16_t)((h & 0x0fff)|(f << 12))
#define acl_handle(h) (h & 0x0fff)
#define acl_flags(h) (h >> 12)
1.6 SCO Data 报文结构
SCO(Synchronous Connection-Oriented)数据用于同步语音传输:
cpp
typedef struct {
uint16_t handle;
uint8_t dlen;
} __attribute__ ((packed)) hci_sco_hdr;
#define HCI_SCO_HDR_SIZE 3
1.7 命令-事件交互模型
HCI 命令与事件之间遵循标准的交互模型:

两个关键事件:
-
Command Status (0x0F):命令已接收,正在执行
-
Command Complete (0x0E):命令执行完成,携带返回参数
二、BlueZ Lib 层:HCI 协议的原生封装
2.1 Lib 层定位
BlueZ 的 lib/ 目录提供了 HCI 协议的最基础封装,是直接面向内核 HCI Socket 的 API 层。这一层最贴近 HCI 协议本身,几乎是协议的 C 语言直译。
2.2 HCI Socket 创建与绑定
BlueZ 通过标准的 Linux Socket 接口与内核 HCI 子系统通信:
cpp
int hci_open_dev(int dev_id)
{
struct sockaddr_hci a;
int dd, err;
if (dev_id < 0) {
errno = ENODEV;
return -1;
}
/* Create HCI socket */
dd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI);
if (dd < 0)
return dd;
/* Bind socket to the HCI device */
memset(&a, 0, sizeof(a));
a.hci_family = AF_BLUETOOTH;
a.hci_dev = dev_id;
if (bind(dd, (struct sockaddr *) &a, sizeof(a)) < 0)
goto failed;
return dd;
failed:
err = errno;
close(dd);
errno = err;
return -1;
}
协议对照:
|--------------|-----------------------------------------------|---------------|
| HCI 协议概念 | BlueZ Lib API | 作用 |
| HCI 传输通道 | socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI) | 创建 HCI Socket |
| HCI 设备绑定 | bind(hci_dev = dev_id) | 绑定到具体蓝牙控制器 |
| HCI 原始报文 | 通过 writev / read 收发 | 直接读写 HCI 报文 |
2.3 命令发送:hci_send_cmd
hci_send_cmd() 是最基础的命令发送函数,直接对应 HCI Command 报文的组装和发送:
cpp
int hci_send_cmd(int dd, uint16_t ogf, uint16_t ocf, uint8_t plen, void *param)
{
uint8_t type = HCI_COMMAND_PKT;
hci_command_hdr hc;
struct iovec iv[3];
int ivn;
hc.opcode = htobs(cmd_opcode_pack(ogf, ocf));
hc.plen = plen;
iv[0].iov_base = &type;
iv[0].iov_len = 1;
iv[1].iov_base = &hc;
iv[1].iov_len = HCI_COMMAND_HDR_SIZE;
ivn = 2;
if (plen) {
iv[2].iov_base = param;
iv[2].iov_len = plen;
ivn = 3;
}
while (writev(dd, iv, ivn) < 0) {
if (errno == EAGAIN || errno == EINTR)
continue;
return -1;
}
return 0;
}
协议字段映射:
|--------------|-------------|--------------------------------|
| HCI 报文字段 | 源码变量 | 填充逻辑 |
| Type (1B) | type | 固定为 HCI_COMMAND_PKT (0x01) |
| Opcode (2B) | hc.opcode | cmd_opcode_pack(ogf, ocf) 打包 |
| Plen (1B) | hc.plen | 直接传入参数长度 |
| Param (N) | param | 调用者传入的参数结构体 |
2.4 同步请求-响应:hci_send_req
实际开发中更常用的是 hci_send_req(),它封装了"发命令+等事件"的完整流程:
cpp
int hci_send_req(int dd, struct hci_request *r, int to)
{
unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr;
uint16_t opcode = htobs(cmd_opcode_pack(r->ogf, r->ocf));
struct hci_filter nf, of;
socklen_t olen;
hci_event_hdr *hdr;
int err, try;
// 1. 保存旧过滤器,设置新过滤器
olen = sizeof(of);
if (getsockopt(dd, SOL_HCI, HCI_FILTER, &of, &olen) < 0)
return -1;
hci_filter_clear(&nf);
hci_filter_set_ptype(HCI_EVENT_PKT, &nf);
hci_filter_set_event(EVT_CMD_STATUS, &nf);
hci_filter_set_event(EVT_CMD_COMPLETE, &nf);
hci_filter_set_event(EVT_LE_META_EVENT, &nf);
hci_filter_set_event(r->event, &nf);
hci_filter_set_opcode(opcode, &nf);
if (setsockopt(dd, SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0)
return -1;
// 2. 发送命令
if (hci_send_cmd(dd, r->ogf, r->ocf, r->clen, r->cparam) < 0)
goto failed;
// 3. 等待响应事件
try = 10;
while (try--) {
evt_cmd_complete *cc;
evt_cmd_status *cs;
...
len = read(dd, buf, sizeof(buf));
...
hdr = (void *) buf;
ptr = buf + 1 + HCI_EVENT_HDR_SIZE;
switch (hdr->evt) {
case EVT_CMD_COMPLETE:
cc = (void *) ptr;
if (cmd_opcode_pack(r->ogf, r->ocf) != btohs(cc->opcode))
continue;
// 复制返回参数
memcpy(r->rparam, ptr + sizeof(*cc),
min((int) (hdr->plen - sizeof(*cc)), r->rlen));
goto done;
...
}
}
done:
// 4. 恢复过滤器
setsockopt(dd, SOL_HCI, HCI_FILTER, &of, sizeof(of));
...
}
关键设计点:HCI Filter 机制
hci_filter 是内核提供的报文过滤机制,避免用户态收到不关心的事件:
cpp
struct hci_filter {
uint32_t type_mask; // 报文类型掩码
uint32_t event_mask[2]; // 事件码掩码(64位)
uint16_t opcode; // 命令操作码过滤
};
这对应 HCI 协议中主机侧的事件过滤机制------主机需要筛选自己关心的事件,避免被无关事件淹没。
2.5 常见命令封装示例
以创建连接为例,看协议参数如何对应到 C 结构体:
HCI 协议:Create_Connection 命令 (OGF=0x01, OCF=0x0005)
|---------------------------|--------|----------|
| 参数 | 长度 | 说明 |
| BD_ADDR | 6B | 目标蓝牙地址 |
| Packet_Type | 2B | 支持的包类型 |
| Page_Scan_Repetition_Mode | 1B | 页面扫描重复模式 |
| Page_Scan_Period_Mode | 1B | 页面扫描周期模式 |
| Clock_Offset | 2B | 时钟偏移 |
| Role_Switch | 1B | 角色切换 |
BlueZ 源码对应:
cpp
#define OGF_LINK_CTL 0x01
#define OCF_CREATE_CONN 0x0005
typedef struct {
bdaddr_t bdaddr;
uint16_t pkt_type;
uint8_t pscan_rep_mode;
uint8_t pscan_per_mode;
uint16_t clock_offset;
uint8_t role_switch;
} __attribute__ ((packed)) create_conn_cp;
#define CREATE_CONN_CP_SIZE 13
三、BlueZ Shared 层:HCI 的面向对象封装
3.1 Shared 层定位
src/shared/ 目录是 BlueZ 内部的工具库,对 HCI 进行了更高级的面向对象封装。核心是 struct bt_hci,它封装了命令队列、事件分发、流控等复杂逻辑。
3.2 bt_hci 核心结构体
cpp
struct bt_hci {
int ref_count;
struct io *io; // IO 封装
bool is_stream; // 是否为流模式
bool writer_active; // 写处理是否激活
uint8_t num_cmds; // 可用命令槽位数(流控)
unsigned int next_cmd_id; // 下一个命令 ID
unsigned int next_evt_id; // 下一个事件 ID
struct queue *cmd_queue; // 待发送命令队列
struct queue *rsp_queue; // 等待响应的命令队列
struct queue *evt_list; // 事件监听器列表
};
对应 HCI 协议概念:
|------------------------------|---------------|--------------|
| HCI 协议概念 | bt_hci 成员 | 作用 |
| 命令流控 (Num_Completed_Packets) | num_cmds | 跟踪控制器可接收的命令数 |
| 命令排队 | cmd_queue | 命令发送队列 |
| 命令-响应匹配 | rsp_queue | 等待响应的命令 |
| 事件订阅 | evt_list | 事件回调注册列表 |
3.3 命令发送流程
命令发送函数:
cpp
static void send_command(struct bt_hci *hci, uint16_t opcode,
void *data, uint8_t size)
{
uint8_t type = BT_H4_CMD_PKT;
struct bt_hci_cmd_hdr hdr;
struct iovec iov[3];
int iovcnt;
if (hci->num_cmds < 1) // 流控:检查是否还有命令槽位
return;
hdr.opcode = cpu_to_le16(opcode);
hdr.plen = size;
iov[0].iov_base = &type;
iov[0].iov_len = 1;
iov[1].iov_base = &hdr;
iov[1].iov_len = sizeof(hdr);
if (size > 0) {
iov[2].iov_base = data;
iov[2].iov_len = size;
iovcnt = 3;
} else
iovcnt = 2;
if (io_send(hci->io, iov, iovcnt) < 0)
return;
hci->num_cmds--; // 发送后减少可用槽位
}
流控机制详解:
HCI 协议中,控制器通过 Command Complete/Command Status 事件中的 Num_HCI_Command_Packets 字段告知主机还能发送多少条命令。这对应到 bt_hci->num_cmds:
-
初始值为 1(假设控制器至少能处理 1 条命令)
-
每发送一条命令,
num_cmds-- -
收到 Command Complete/Status 事件,
num_cmds = event->ncmd -
当
num_cmds < 1时,新命令进入cmd_queue等待
3.4 事件处理与分发
事件处理入口:
cpp
static void process_event(struct bt_hci *hci, const void *data, size_t size)
{
const struct bt_hci_evt_hdr *hdr = data;
const struct bt_hci_evt_cmd_complete *cc;
const struct bt_hci_evt_cmd_status *cs;
if (size < sizeof(struct bt_hci_evt_hdr))
return;
data += sizeof(struct bt_hci_evt_hdr);
size -= sizeof(struct bt_hci_evt_hdr);
if (hdr->plen != size) // 长度校验
return;
switch (hdr->evt) {
case BT_HCI_EVT_CMD_COMPLETE:
if (size < sizeof(*cc))
return;
cc = data;
hci->num_cmds = cc->ncmd; // 更新流控计数
process_response(hci, le16_to_cpu(cc->opcode),
data + sizeof(*cc),
size - sizeof(*cc)); // 匹配并调用命令回调
break;
case BT_HCI_EVT_CMD_STATUS:
if (size < sizeof(*cs))
return;
cs = data;
hci->num_cmds = cs->ncmd; // 更新流控计数
process_response(hci, le16_to_cpu(cs->opcode), &cs->status, 1);
break;
default:
// 普通事件:分发给所有注册的监听器
queue_foreach(hci->evt_list, process_notify, (void *) hdr);
break;
}
}
事件分发机制对应关系:
|------------------|---------------|-------------------------------------------|
| HCI 事件类型 | 处理方式 | 源码逻辑 |
| Command Complete | 匹配响应队列,调用命令回调 | process_response() + opcode 匹配 |
| Command Status | 同上,但仅返回状态码 | process_response() + &cs->status |
| 其他异步事件 | 广播给所有注册的监听器 | queue_foreach(evt_list, process_notify) |
3.5 命令响应匹配
cpp
static void process_response(struct bt_hci *hci, uint16_t opcode,
const void *data, size_t size)
{
struct cmd *cmd;
if (opcode == BT_HCI_CMD_NOP) {
wakeup_writer(hci);
return;
}
// 从响应队列中找到匹配 opcode 的命令
cmd = queue_remove_if(hci->rsp_queue, match_cmd_opcode,
UINT_TO_PTR(opcode));
if (!cmd)
return;
bt_hci_ref(hci);
// 调用命令回调
if (cmd->callback)
cmd->callback(data, size, cmd->user_data);
cmd_free(cmd);
wakeup_writer(hci); // 尝试发送下一条命令
bt_hci_unref(hci);
}
四、Management Interface:HCI 的更高层抽象
4.1 MGMT 层的定位
Management Interface(MGMT)是 Linux 内核在 HCI 之上提供的更高层控制接口。BlueZ 5.x 的蓝牙守护进程 bluetoothd 主要通过 MGMT 接口与内核交互,而不是直接使用原始 HCI 命令。
为什么需要 MGMT?
原始 HCI 命令粒度太细(如设置扫描模式需要多条命令),且需要处理复杂的状态管理。MGMT 将常用操作封装为高级命令,由内核统一管理状态。
4.2 MGMT 报文结构
MGMT 报文同样基于 HCI Socket,但使用独立的 Control Channel:
cpp
+----------+----------+----------+----------+
| Opcode | Index | Length | Data |
| (16bit) | (16bit) | (16bit) | (Length) |
+----------+----------+----------+----------+
-
Opcode:MGMT 命令/事件码
-
Index:适配器索引(hci0=0, hci1=1...)
-
Length:数据长度
-
Data:命令参数 / 事件参数
BlueZ 源码中的 MGMT 头:
cpp
struct mgmt_hdr {
uint16_t opcode;
uint16_t index;
uint16_t len;
} __packed;
#define MGMT_HDR_SIZE 6
4.3 MGMT Socket 创建
cpp
struct mgmt *mgmt_new_default(void)
{
struct mgmt *mgmt;
union {
struct sockaddr common;
struct sockaddr_hci hci;
} addr;
int fd;
fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,
BTPROTO_HCI);
...
addr.hci.hci_family = AF_BLUETOOTH;
addr.hci.hci_dev = HCI_DEV_NONE; // 不绑定具体设备
addr.hci.hci_channel = HCI_CHANNEL_CONTROL; // Control 通道
bind(fd, &addr.common, sizeof(addr.hci));
...
mgmt = mgmt_new(fd);
...
}
HCI Channel 类型对比:
|----------------|-----------------------|----------------------|
| Channel 类型 | 宏定义 | 用途 |
| Raw | HCI_CHANNEL_RAW | 原始 HCI 报文收发 |
| User | HCI_CHANNEL_USER | 用户态驱动(User Channel) |
| Control | HCI_CHANNEL_CONTROL | Management Interface |
| Monitor | HCI_CHANNEL_MONITOR | 监控抓包 |
4.4 MGMT 命令发送
cpp
unsigned int mgmt_send_timeout(struct mgmt *mgmt, uint16_t opcode,
uint16_t index, uint16_t length,
const void *param, mgmt_request_func_t callback,
void *user_data, mgmt_destroy_func_t destroy,
int timeout)
{
struct mgmt_request *request;
request = create_request(mgmt, opcode, index, length, param,
callback, user_data, destroy, timeout);
if (!request)
return 0;
if (mgmt->next_request_id < 1)
mgmt->next_request_id = 1;
request->id = mgmt->next_request_id++;
// 加入请求队列
if (!queue_push_tail(mgmt->request_queue, request)) {
free(request->buf);
free(request);
return 0;
}
wakeup_writer(mgmt); // 唤醒写处理
return request->id;
}
4.5 MGMT 事件读取与分发
cpp
static bool can_read_data(struct io *io, void *user_data)
{
struct mgmt *mgmt = user_data;
struct mgmt_hdr *hdr;
struct mgmt_ev_cmd_complete *cc;
struct mgmt_ev_cmd_status *cs;
ssize_t bytes_read;
uint16_t opcode, event, index, length;
bytes_read = read(mgmt->fd, mgmt->buf, mgmt->len);
...
hdr = mgmt->buf;
event = btohs(hdr->opcode);
index = btohs(hdr->index);
length = btohs(hdr->len);
mgmt_ref(mgmt);
switch (event) {
case MGMT_EV_CMD_COMPLETE:
cc = mgmt->buf + MGMT_HDR_SIZE;
opcode = btohs(cc->opcode);
// 匹配等待中的命令并调用回调
...
break;
case MGMT_EV_CMD_STATUS:
cs = mgmt->buf + MGMT_HDR_SIZE;
opcode = btohs(cs->opcode);
// 匹配等待中的命令并调用状态回调
...
break;
default:
// 普通事件:分发给注册的监听器
...
break;
}
...
}
MGMT 与 HCI 的层级关系:

五、HCI 协议字段与 BlueZ 源码完整映射表
5.1 命令体系映射
|----------------------------|---------------------|--------------------|-------------------------|
| HCI 协议概念 | BlueZ Lib 层 | BlueZ Shared 层 | 说明 |
| OGF (Opcode Group Field) | cmd_opcode_ogf() | - | 命令组码提取 |
| OCF (Opcode Command Field) | cmd_opcode_ocf() | - | 命令码提取 |
| Opcode 打包 | cmd_opcode_pack() | - | OGF+OCF 组合 |
| Command Header | hci_command_hdr | bt_hci_cmd_hdr | 命令头部结构体 |
| 命令发送 | hci_send_cmd() | bt_hci_send() | 发送 HCI 命令 |
| 同步请求 | hci_send_req() | - | 发命令+等响应 |
| 命令流控 | - | bt_hci.num_cmds | Num_HCI_Command_Packets |
5.2 事件体系映射
|------------------------|----------------------|---------------------------|---------|
| HCI 协议概念 | BlueZ Lib 层 | BlueZ Shared 层 | 说明 |
| Event Code | hci_event_hdr.evt | bt_hci_evt_hdr.evt | 事件码 |
| Event Parameter Length | hci_event_hdr.plen | bt_hci_evt_hdr.plen | 事件参数长度 |
| Command Complete Event | EVT_CMD_COMPLETE | BT_HCI_EVT_CMD_COMPLETE | 命令完成事件 |
| Command Status Event | EVT_CMD_STATUS | BT_HCI_EVT_CMD_STATUS | 命令状态事件 |
| 事件过滤 | hci_filter | - | 内核态过滤 |
| 事件回调注册 | - | bt_hci_register() | 用户态事件分发 |
5.3 数据报文映射
|-----------------|--------------------------|-----------|
| HCI 协议概念 | BlueZ Lib 层 | 说明 |
| ACL Handle | hci_acl_hdr.handle | 连接句柄(12位) |
| PB Flag | acl_flags() >> 0 & 0x3 | 报文边界标志 |
| BC Flag | acl_flags() >> 2 & 0x3 | 广播标志 |
| ACL Data Length | hci_acl_hdr.dlen | ACL 数据长度 |
| SCO Handle | hci_sco_hdr.handle | SCO 连接句柄 |
| SCO Data Length | hci_sco_hdr.dlen | SCO 数据长度 |
六、理论协议与实际源码的差异
6.1 差异一:HCI Socket 传输层
协议标准: HCI 规范定义了多种传输层(USB、UART、SDIO、BCSP等),每种传输层有不同的报文封装方式。
BlueZ 实际实现: 用户态看到的是统一的 HCI Socket 接口,传输层差异由内核驱动屏蔽。用户态只需要处理 HCI 报文类型前缀(1字节)+ HCI PDU。
cpp
// HCI Socket 的报文格式是:Type(1B) + HCI PDU
// Type 对应 HCI_COMMAND_PKT / HCI_ACLDATA_PKT / HCI_SCODATA_PKT / HCI_EVENT_PKT
6.2 差异二:Management Interface 层
协议标准: HCI 规范只定义了 Host ↔ Controller 的接口。
BlueZ 实际实现: 在内核中增加了 MGMT 层,对 HCI 命令进行了聚合和抽象。用户态 bluetoothd主要通过 MGMT 而不是直接 HCI 控制适配器。
典型例子: 设置适配器可发现
|---------|---------------------------------------------------------|
| 方式 | 操作 |
| 原始 HCI | 需要写入 Scan Enable、Page Scan Type、Inquiry Scan Type 等多条命令 |
| MGMT 接口 | 一条 MGMT_OP_SET_DISCOVERABLE 命令搞定 |
6.3 差异三:事件过滤机制
协议标准: HCI 规范定义了 Set_Event_Mask 命令,用于在控制器侧过滤事件。
BlueZ 实际实现: 除了控制器侧过滤,内核还通过 HCI_FILTER socket 选项提供了第二道过滤,用户态可以进一步筛选自己关心的事件。
6.4 差异四:命令流控
协议标准: 严格的命令-事件模型,每条命令必须等 Command Complete 或 Command Status 才能发下一条。
BlueZ 实际实现:
-
libhci的hci_send_req()是同步阻塞的,严格遵守一条命令等响应 -
shared/bt_hci是异步队列化的,根据num_cmds动态调整并发数 -
MGMT 接口有自己的请求队列,与 HCI 流控独立
七、开发踩坑要点
坑点一:HCI Filter 未设置导致收不到事件
现象: read() 一直阻塞或读到非预期的事件。
原因: 未设置 HCI_FILTER,收到的是全部 HCI 事件。
正确做法:
cpp
struct hci_filter nf;
hci_filter_clear(&nf);
hci_filter_set_ptype(HCI_EVENT_PKT, &nf);
hci_filter_set_event(EVT_CMD_COMPLETE, &nf);
hci_filter_set_event(EVT_CMD_STATUS, &nf);
setsockopt(dd, SOL_HCI, HCI_FILTER, &nf, sizeof(nf));
坑点二:Opened 大小端处理错误
现象: 发送的命令 opcode 不对,控制器返回 Unknown Command。
原因: HCI 协议使用小端序(Little-Endian),忘记用 htobs() / btohs()转换。
cpp
// 错误:直接赋值
hc.opcode = cmd_opcode_pack(ogf, ocf);
// 正确:主机字节序 → 小端序
hc.opcode = htobs(cmd_opcode_pack(ogf, ocf));
坑点三:用 raw socket 时与内核冲突
现象: 自己发的 HCI 命令和内核操作相互干扰。
原因: HCI_CHANNEL_RAW 与内核共享控制器,内核也在发命令。
解决方案:
-
需要完全控制控制器时,使用
HCI_CHANNEL_USER(User Channel) -
只需要监听事件时,使用
HCI_CHANNEL_MONITOR -
正常应用开发,通过 DBus 调用
bluetoothd,不要直接操作 HCI
坑点四:命令超时处理缺失
现象: 程序卡死在 hci_send_req()。
原因: 控制器异常导致 Command Complete 永远不来。
解决方案:
cpp
// 设置合理的超时时间
int err = hci_send_req(dd, &rq, 1000); // 1秒超时
if (err < 0 && errno == ETIMEDOUT) {
// 超时处理
}
坑点五:结构体对齐问题
现象: 解析 HCI 报文时数据错位。
原因: 编译器结构体对齐填充。
BlueZ 的解决方案: 所有 HCI 结构体都加了 attribute ((packed)):
cpp
typedef struct {
uint8_t status;
uint16_t handle;
uint8_t link_type;
...
} __attribute__ ((packed)) evt_conn_complete; // 关键:packed 属性
八、完整源码层级架构总结

九、总结
HCI 协议是蓝牙通信的基石,而 BlueZ 对 HCI 的封装体现了典型的分层设计思想:
-
Lib 层:最贴近协议,几乎是 HCI 报文的 C 语言直译,适合底层开发
-
Shared 层:面向对象封装,加入队列、流控、事件分发,适合异步框架
-
MGMT 层:内核级抽象,聚合多条 HCI 命令为高级操作,守护进程首选
-
DBus 层:最高层抽象,应用开发直接调用,无需关心 HCI 细节
理解这一层级关系,才能在遇到问题时精准定位:是 HCI 协议本身的问题?是内核驱动的问题?还是 BlueZ 用户态封装的问题?