蓝牙协议栈 (双模蓝牙)
AC792N SDK(V3)内置的杰理双模蓝牙协议栈,基于 BTStack 架构实现经典蓝牙(BR/EDR)与低功耗蓝牙(BLE/LE)两条协议路径的统一管理,向上以事件回调的方式与用户 App 交互,向下通过 HCI 接口驱动单芯片内的控制器(Controller)。
Purpose and Scope
本文档说明 AC792N SDK 中双模蓝牙协议栈的整体架构、目录布局、HCI 层接口、事件驱动模型、双连接管理机制、Profile 支持(A2DP/AVCTP、ATT/GATT/SM、LE Audio ISO)以及连接失败错误码语义,帮助开发者在 App 模式中正确注册事件处理函数并处理连接生命周期。
本页不展开以下内容,相关能力由对应页面说明:
- 协议栈对外暴露的常用接口函数清单 → 参见《常用蓝牙库接口函数》
- 音频编解码细节(SBC/AAC 等)→ 参见 A2DP 音频编解码相关页面
- LE Audio 广播(Auracast)的独立 API → 参见 Auracast Sink/Delegator 相关页面
- 蓝牙连接管理 App(回连、提示音、低电关机等策略)→ 参见蓝牙 App 相关页面
Overview
蓝牙协议栈是 AC792N 芯片上所有蓝牙业务(音乐播放、通话、配对、回连、BLE 透传、LE Audio 等)的底层支撑。SDK 将协议栈以预编译库的形式随头文件发布,用户代码通过 sdk/include_lib/btstack/ 下的头文件与协议栈交互,编译时通过 btstack_lib.ld 系列链接脚本将协议栈库(text/data/bss 段)链接进固件。
协议栈采用事件驱动的异步模型:
- 用户 App 注册一个事件处理函数(如
bt_connction_status_event_handler); - 协议栈把连接状态、HCI 事件、配对请求等以
struct bt_event的形式投递给处理函数; - 处理函数根据
event字段(协议栈状态)与value字段(子事件/错误码)分发到对应的业务逻辑。
双模能力体现在两条并行的协议路径上:
- 经典路径(BR/EDR):HCI 层 → L2CAP → A2DP/AVRCP(AVCTP)等 Profile,用于音乐播放与通话;
- 低功耗路径(LE):HCI LE 命令 → ATT/GATT/SM,用于 BLE 服务与配对;同时支持 ISO(LE Audio)通道与 Auracast 广播接收。
两条路径共享同一个 HCI 传输层与同一个事件回调入口,这使得 SDK 可以在一颗芯片上同时维护两个经典连接(BT_STATUS_FIRST_CONNECTED / BT_STATUS_SECOND_CONNECTED)以及若干 LE 链路。
Architecture
下图展示双模协议栈的分层架构与数据流向(节点名称均对应实际头文件/模块):
flowchart TD
subgraph sg_App["应用层 (App)"]
App["用户 App / 蓝牙 App 模式"]
Handler["bt_connction_status_event_handler"]
end
subgraph sg_Stack["双模协议栈 (BTStack)"]
HCI["HCI 层<br/>(Command/Event/ACL/SCO/ISO)"]
L2CAP["L2CAP"]
Classic["经典 Profile<br/>A2DP / AVRCP(AVCTP)"]
LE["LE 协议族<br/>ATT / GATT / SM"]
ISO["LE Audio ISO<br/>Auracast Sink/Delegator"]
end
subgraph sg_Controller["控制器与存储 (Controller)"]
RF["BR/EDR + LE 射频控制器"]
VM["VM 存储<br/>(LinkKey / 配对信息)"]
end
App -->|"注册回调"| Handler
Handler -->|"bt_event 事件"| HCI
Handler -->|"BT_STATUS_* 状态"| App
HCI <--> L2CAP
L2CAP <--> Classic
L2CAP <--> LE
LE <--> ISO
HCI <--> RF
Classic -->|"配对信息读写"| VM
LE -->|"配对信息读写"| VM
架构说明:
- HCI 层(
bluetooth.h中的包类型/OGF/事件定义):协议栈与控制器之间的统一接口,承载命令、事件、ACL、SCO、ISO 五类数据包; - L2CAP:经典与 LE 共用的逻辑链路复用层,向上承载各类 Profile 与 ATT;
- 经典 Profile:
a2dp_media_codec.h提供 A2DP 媒体编解码接口,avctp_user.h提供 AVRCP/AVCTP 控制通道接口; - LE 协议族:
le/att.h、le/gatt.h、le/sm.h、le/ble_api.h等头文件提供 BLE 的 Attribute、GATT 服务与安全管理(配对/加密); - LE Audio ISO:
bluetooth.h中定义了 ISO SDU/PDU 长度与间隔常量,配合le/auracast_sink_api.h、le/auracast_delegator_api.h支持 LE Audio 广播接收与转发; - VM 存储:配对 LinkKey、回连地址等持久化数据保存在 VM 区,协议栈事件
HCI_EVENT_VENDOR_NO_RECONN_ADDR正是“上电回连时从 VM 读不到地址”的杰理自定义事件。
关于事件如何从 HCI 一路投递到用户 App 的完整时序,参见下文「核心流程」一节。
协议栈文件布局
协议栈头文件统一位于 sdk/include_lib/btstack/,其组织方式反映了协议栈的分层结构:
| 文件/目录 | 职责 |
|---|---|
bluetooth.h | 总入口:聚合 LE 头文件与公共头文件,定义 HCI 包类型、OGF 命令组、HCI 事件码、ISO 常量 |
btstack_event.h | 公共事件定义(含杰理自定义事件,如 BTSTACK_EVENT_HCI_CONNECTIONS_DELETE) |
btstack_typedef.h | 协议栈基础类型定义 |
btstack_task.h | 协议栈任务/调度接口 |
bt_profile_config.h | Profile 配置入口(A2DP/AVRCP 等使能与参数配置) |
a2dp_media_codec.h | A2DP 媒体流编解码接口 |
avctp_user.h | AVRCP/AVCTP 控制通道用户接口 |
le/ | LE 协议族:ble_data_types.h、ble_api.h、le_user.h、att.h、gatt.h、sm.h 及 auracast_sink_api.h、auracast_delegator_api.h |
btstack_lib.ld / btstack_lib_text.ld / btstack_lib_data.ld / btstack_lib_bss.ld | 协议栈预编译库的链接脚本(代码段/数据段/BSS 段) |
bluetooth.h 作为唯一总入口,通过条件式聚合把 LE 与公共模块组织在一起:
//LE
#include "le/ble_data_types.h"
#include "le/ble_api.h"
#include "le/le_user.h"
#include "le/att.h"
#include "le/gatt.h"
#include "le/sm.h"
...
//Common
#include "btstack_event.h"
Source: bluetooth.h
设计意图:bluetooth.h 采用“伞形头文件”模式,用户 App 只需包含一个头文件即可获得双模协议栈的全部接口声明,避免在业务代码中维护复杂的包含关系;同时预编译库配合独立链接脚本(btstack_lib.ld 系列)发布,SDK 只暴露接口、不暴露实现,便于协议栈独立迭代升级。
HCI 层:协议栈与控制器之间的统一接口
HCI 数据包类型
HCI 层承载五类数据包,定义于 bluetooth.h:
| 宏 | 值 | 用途 |
|---|---|---|
HCI_COMMAND_DATA_PACKET | 0x01 | 主机 → 控制器命令 |
HCI_ACL_DATA_PACKET | 0x02 | 经典 ACL 数据 |
HCI_SCO_DATA_PACKET | 0x03 | 同步面向连接(SCO)语音数据 |
HCI_EVENT_PACKET | 0x04 | 控制器 → 主机事件 |
HCI_ISO_DATA_PACKET | 0x05 | LE Audio ISO 数据(定义于 ISO 常量区) |
#define HCI_COMMAND_DATA_PACKET 0x01
#define HCI_ACL_DATA_PACKET 0x02
#define HCI_SCO_DATA_PACKET 0x03
#define HCI_EVENT_PACKET 0x04
Source: bluetooth.h
OGF 命令组
HCI 命令按 OGF(Opcode Group Field)分组,bluetooth.h 定义了全部标准命令组以及杰理厂商命令组:
| 宏 | 值 | 命令组 |
|---|---|---|
OGF_LINK_CONTROL | 0x01 | 链路控制(查询、连接、配对) |
OGF_LINK_POLICY | 0x02 | 链路策略 |
OGF_CONTROLLER_BASEBAND | 0x03 | 控制器基带 |
OGF_INFORMATIONAL_PARAMETERS | 0x04 | 信息参数(本地版本/支持特性) |
OGF_STATUS_PARAMETERS | 0x05 | 状态参数 |
OGF_TESTING | 0x06 | 测试 |
OGF_LE_CONTROLLER | 0x08 | LE 控制器命令 |
OGF_VENDOR_LE_CONTROLLER | 0x3E | 杰理厂商 LE 命令 |
OGF_VENDOR | 0x3F | 杰理厂商命令 |
Source: bluetooth.h
ISO(LE Audio)常量
协议栈为 LE Audio 预留了 ISO 通道参数,表明 SDK 对 LE Audio 的支持已内建到 HCI 层:
#define ISO_SDU_DTAT_LENGTH 0x032c //812byte
#define ISO_PDU_DTAT_LENGTH 251 //251byte
#define ISO_PDU_INTERVAL_M_S 0x4e20 //20ms
#define ISO_PDU_INTERVAL_S_M 0x4e20 //20ms
#define HCI_ISO_DATA_PACKET 0x05
Source: bluetooth.h
设计意图:SDU 812 字节、PDU 251 字节、20ms 间隔的组合对应 LE Audio 单声道 LC3 低延迟配置,为 Auracast 接收(auracast_sink_api.h)与转发(auracast_delegator_api.h)提供统一的数据通路参数。
事件驱动模型与回调机制
事件结构体 struct bt_event
协议栈向用户投递事件的统一载体定义如下:
struct bt_event {
u8 event; // 事件类型
u8 args[7]; // 事件参数,如带蓝牙地址等
u32 value; // 事件值,如HCI_EVENT_CONNECTION_COMPLETE事件的子事件
};
Source: event.rst.txt
三个字段的分工:
event:事件主类型,取值来自BT_STATUS_*或HCI_EVENT_*;args[7]:最多 7 字节的附加参数,常见为蓝牙地址(BD_ADDR)等;value:32 位事件值,承载子事件——典型场景是HCI_EVENT_CONNECTION_COMPLETE事件的子事件(连接错误码,即ERROR_CODE_*)。
协议栈状态事件(BT_STATUS_*)
在蓝牙 App 模式及需要处理连接生命周期的其他 App 中,用户注册的处理函数会收到以下协议栈级事件:
| 事件 | 说明 |
|---|---|
BT_STATUS_INIT_OK | 蓝牙协议栈初始化完成 |
BT_STATUS_FIRST_CONNECTED | 首个设备连接成功 |
BT_STATUS_SECOND_CONNECTED | 第二个设备连接成功 |
BT_STATUS_FIRST_DISCONNECT | 首个连接设备断开 |
BT_STATUS_SECOND_DISCONNECT | 第二个连接设备断开 |
BT_STATUS_CONN_A2DP_CH | A2DP 连接 |
BT_STATUS_DISCON_A2DP_CH | A2DP 断开 |
Source: event.rst.txt
HCI 事件
协议栈会把需要用户感知的 HCI 事件透传上来,常见的有:
| HCI 事件 | 说明 |
|---|---|
HCI_EVENT_VENDOR_META | 杰理厂商事件容器 |
HCI_EVENT_INQUIRY_COMPLETE | 蓝牙搜索完成 |
HCI_EVENT_USER_CONFIRMATION_REQUEST | 蓝牙连接确认(配对确认) |
HCI_EVENT_USER_PASSKEY_REQUEST | 配对码请求 |
HCI_EVENT_USER_PRESSKEY_NOTIFICATION | 配对码通知 |
HCI_EVENT_PIN_CODE_REQUEST | 连接 PIN Code 请求 |
HCI_EVENT_VENDOR_NO_RECONN_ADDR | 杰理自定义:上电回连时读不到 VM 中的回连地址 |
HCI_EVENT_DISCONNECTION_COMPLETE | 蓝牙断连完成 |
BTSTACK_EVENT_HCI_CONNECTIONS_DELETE | 杰理自定义:断开后协议栈资源释放完成 |
HCI_EVENT_CONNECTION_COMPLETE | 连接完成事件(其子事件即 ERROR_CODE_*) |
Source: event.rst.txt
设计意图:事件语义分层是这套协议栈的核心设计——BT_STATUS_* 是协议栈加工后的“业务状态”(双连接、A2DP 通道),直接驱动 UI/业务;HCI_EVENT_* 是原始控制器事件,用于配对交互、回连、资源释放等需要细粒度控制的场景;ERROR_CODE_* 则把连接失败归因到具体原因,供上层决定“重试、重新配对还是放弃”。HCI_EVENT_VENDOR_NO_RECONN_ADDR 与 BTSTACK_EVENT_HCI_CONNECTIONS_DELETE 两个杰理自定义事件则暴露了回连链路与资源释放的边界,这是国产 SDK 为低成本单芯片优化而做的典型扩展。
双模连接管理:双连接与回连机制
AC792N 协议栈在经典蓝牙路径上支持双设备连接,这是 BT_STATUS_FIRST_* 与 BT_STATUS_SECOND_* 成对事件存在的原因:协议栈内部维护两个独立的连接槽位,每个槽位有独立的连接句柄、A2DP 通道与断开状态机。
典型生命周期如下:
- 上电初始化:协议栈完成初始化后上报
BT_STATUS_INIT_OK; - 回连:App 根据 VM 中保存的回连地址发起连接。若 VM 中读不到地址,协议栈会上报
HCI_EVENT_VENDOR_NO_RECONN_ADDR,提示上层放弃回连、进入可发现/可配对状态; - 首个连接建立:
HCI_EVENT_CONNECTION_COMPLETE(子事件ERROR_CODE_SUCCESS)→ 业务收到BT_STATUS_FIRST_CONNECTED; - 第二个连接建立:同样流程上报
BT_STATUS_SECOND_CONNECTED;若资源不足,控制器会以ERROR_CODE_SYNCHRONOUS_CONNECTION_LIMIT_TO_A_DEVICE_EXCEEDED拒绝; - 断开:
HCI_EVENT_DISCONNECTION_COMPLETE之后,协议栈继续释放 L2CAP 资源,资源释放完成再上报杰理自定义事件BTSTACK_EVENT_HCI_CONNECTIONS_DELETE——只有收到该事件,连接槽位才真正可复用。
回连场景中协议栈还会遇到一种经典竞态:断电后立即开机回连时,对端(手机)可能还认为旧 ACL 连接存在,此时连接请求会得到 ERROR_CODE_ACL_CONNECTION_ALREADY_EXISTS 或 ERROR_CODE_CONNECTION_REJECTED_DUE_TO_UNACCEPTABLE_BD_ADDR(本机地址全 0 或异常时),上层需要据此调整回连策略。
Profile 支持:经典与 LE 两条路径
经典路径:A2DP 与 AVRCP
a2dp_media_codec.h:A2DP 媒体流的编解码接口,负责音频数据在协议栈与音频框架之间的搬运;avctp_user.h:AVRCP/AVCTP 控制通道接口,承载播放/暂停、上一曲/下一曲、音量等远程控制命令;bt_profile_config.h:Profile 级配置入口,控制 A2DP/AVRCP 等功能的使能、参数与初始化行为。
A2DP 通道的状态变化通过 BT_STATUS_CONN_A2DP_CH / BT_STATUS_DISCON_A2DP_CH 上报,业务层据此刷新音乐播放状态(如提示音、播放器暂停/恢复)。
LE 路径:ATT / GATT / SM
le/att.h:Attribute Protocol,GATT 的底层传输;le/gatt.h:GATT 层,提供服务发现、特征读写、通知/指示等 API;le/sm.h:Security Manager,负责 BLE 配对、加密与密钥分发(对应 HCI 层的HCI_EVENT_USER_CONFIRMATION_REQUEST、HCI_EVENT_USER_PASSKEY_REQUEST等配对事件);le/ble_api.h、le/le_user.h、le/ble_data_types.h:BLE 能力聚合与数据类型定义。
LE Audio:ISO 与 Auracast
bluetooth.h 中 ISO 常量(SDU/PDU 长度、20ms 间隔)为 LE Audio 定义了数据通路;le/auracast_sink_api.h 与 le/auracast_delegator_api.h 分别提供 Auracast 广播接收与转发(Delegator)能力,使设备既可以收听公共广播,也可以作为转发节点扩展广播覆盖。
Core Flow:连接建立与事件投递时序
下图以“上电 → 初始化 → 回连 → 首个连接成功 → A2DP 建立”为主线,展示一次完整的双模连接生命周期:
sequenceDiagram
participant App as 用户 App
participant Stack as 双模协议栈
participant Ctl as 控制器
participant VM as VM 存储
App->>Stack: 协议栈初始化
Stack->>Ctl: HCI 命令(Reset/读本地版本)
Ctl-->>Stack: HCI_EVENT_COMMAND_COMPLETE
Stack-->>App: BT_STATUS_INIT_OK
App->>Stack: 发起回连
Stack->>VM: 读取回连地址/LinkKey
alt VM 中无回连地址
VM-->>Stack: 无地址
Stack-->>App: HCI_EVENT_VENDOR_NO_RECONN_ADDR
else VM 中有回连地址
VM-->>Stack: LinkKey
Stack->>Ctl: HCI Create Connection(经典)
Ctl-->>Stack: HCI_EVENT_CONNECTION_COMPLETE
alt 子事件 ERROR_CODE_SUCCESS
Stack-->>App: BT_STATUS_FIRST_CONNECTED
App->>Stack: 建立 A2DP 流
Stack-->>App: BT_STATUS_CONN_A2DP_CH
else 子事件 ERROR_CODE_*
Stack-->>App: 错误码(Page Timeout/Key Missing等)
end
end
时序要点:
- 初始化成功标志是
BT_STATUS_INIT_OK,在此之前用户不应发起连接类操作; - 回连与配对是两条路径:有 LinkKey 走回连(
HCI Create Connection),无地址/无 Key 则等待配对交互(PIN Code / Passkey / 确认请求类事件); - 连接成功以 HCI 事件为权威,业务状态(
BT_STATUS_FIRST_CONNECTED)在 HCI 成功之后由协议栈合成上报; - A2DP 是连接之上的独立状态,连接成功 ≠ 音乐通道就绪,业务层还需等待
BT_STATUS_CONN_A2DP_CH。
错误码的详细语义与处理建议见下文「失败模式与边界场景」一节。
Usage Examples
示例 1:注册协议栈事件处理函数
所有蓝牙业务(连接、配对、A2DP)都从这一个回调入口开始。SDK 文档给出的标准原型如下:
/*-----------------------------------------------------------------------------
@brief 蓝牙协议栈处理函数
@param bt : 事件结构体
@return 默认0
@note
@demo
------------------------------------------------------------------------------*/
static int bt_connction_status_event_handler(struct bt_event *bt)
Source: event.rst.txt
在 App 中实现该函数后,需要把它注册到协议栈(注册入口由 btstack_task.h 提供,具体注册函数见《常用蓝牙库接口函数》页面)。处理函数内部通常按 bt->event 与 bt->value 双重分发,例如:
static int bt_connction_status_event_handler(struct bt_event *bt)
{
switch (bt->event) {
case BT_STATUS_INIT_OK:
/* 协议栈就绪,可开启回连或可发现模式 */
break;
case HCI_EVENT_CONNECTION_COMPLETE:
/* bt->value 携带 ERROR_CODE_* 子事件 */
switch (bt->value) {
case ERROR_CODE_SUCCESS:
/* 连接成功 */
break;
case ERROR_CODE_PAGE_TIMEOUT:
/* 回连超时退出 */
break;
case ERROR_CODE_PIN_OR_KEY_MISSING:
/* LinkKey 丢失,需重新配对 */
break;
}
break;
case BTSTACK_EVENT_HCI_CONNECTIONS_DELETE:
/* 协议栈资源释放完成,槽位可复用 */
break;
}
return 0;
}
说明:上述 switch 分支为根据官方事件语义整理的推荐写法示意;
bt_event字段定义与事件语义见 event.rst.txt。
示例 2:事件结构体字段使用
协议栈投递的事件载荷统一为 struct bt_event,其中 value 字段专门用于承载连接完成事件的子事件(错误码):
struct bt_event {
u8 event; // 事件类型
u8 args[7]; // 事件参数,如带蓝牙地址等
u32 value; // 事件值,如HCI_EVENT_CONNECTION_COMPLETE事件的子事件
};
Source: event.rst.txt
示例 3:HCI 数据包类型与命令组常量
需要直接构造 HCI 命令或解析 HCI 事件时,使用 bluetooth.h 中的标准常量:
#define HCI_COMMAND_DATA_PACKET 0x01
#define HCI_ACL_DATA_PACKET 0x02
#define HCI_SCO_DATA_PACKET 0x03
#define HCI_EVENT_PACKET 0x04
Source: bluetooth.h
Configuration Options
协议栈的配置分三类入口,均在 sdk/include_lib/btstack/ 下:
| 配置项/入口 | 类型 | 说明 |
|---|---|---|
bt_profile_config.h | 头文件宏配置 | Profile 级使能与参数(A2DP/AVRCP 等),具体宏在文件中定义 |
btstack_lib.ld 系列 | 链接脚本 | 协议栈库 text/data/bss 段的内存布局,一般无需修改 |
ISO_SDU_DTAT_LENGTH (0x032c) | 编译期常量 | ISO SDU 长度 812 字节 |
ISO_PDU_DTAT_LENGTH (251) | 编译期常量 | ISO PDU 长度 251 字节 |
ISO_PDU_INTERVAL_M_S / ISO_PDU_INTERVAL_S_M (0x4e20) | 编译期常量 | ISO PDU 间隔 20ms(主/从方向) |
ISO 常量定义见 bluetooth.h。
设计意图:把 ISO 参数做成编译期常量,是因为 SDU/PDU/间隔三者必须与对端 LC3 编码参数严格匹配;固件侧固定这些值可避免运行时参数错误导致的链路不稳定。Profile 配置收敛到 bt_profile_config.h,则是为了让双模功能裁剪(如仅 BLE、仅经典)通过编译期配置完成,减少代码体积。
API Reference
事件处理回调
static int bt_connction_status_event_handler(struct bt_event *bt)
蓝牙协议栈事件处理函数,是协议栈向用户 App 投递事件的入口。
参数:
bt(struct bt_event *):事件结构体指针,包含event(事件类型)、args[7](附加参数,如蓝牙地址)、value(事件值/子事件)。
返回: int,默认返回 0。
Source: event.rst.txt
事件结构体
struct bt_event
| 字段 | 类型 | 说明 |
|---|---|---|
event | u8 | 事件类型(BT_STATUS_* / HCI_EVENT_*) |
args[7] | u8 | 事件参数,如带蓝牙地址等 |
value | u32 | 事件值,如 HCI_EVENT_CONNECTION_COMPLETE 事件的子事件 |
Source: event.rst.txt
关键常量汇总
HCI 数据包类型(bluetooth.h):HCI_COMMAND_DATA_PACKET(0x01)、HCI_ACL_DATA_PACKET(0x02)、HCI_SCO_DATA_PACKET(0x03)、HCI_EVENT_PACKET(0x04)、HCI_ISO_DATA_PACKET(0x05)。
OGF 命令组(bluetooth.h):OGF_LINK_CONTROL(0x01)、OGF_LINK_POLICY(0x02)、OGF_CONTROLLER_BASEBAND(0x03)、OGF_INFORMATIONAL_PARAMETERS(0x04)、OGF_STATUS_PARAMETERS(0x05)、OGF_TESTING(0x06)、OGF_LE_CONTROLLER(0x08)、OGF_VENDOR_LE_CONTROLLER(0x3E)、OGF_VENDOR(0x3F)。
主要 HCI 事件(bluetooth.h,节选):HCI_EVENT_INQUIRY_COMPLETE(0x01)、HCI_EVENT_INQUIRY_RESULT(0x02)、HCI_EVENT_CONNECTION_COMPLETE(0x03)、HCI_EVENT_CONNECTION_REQUEST(0x04)、HCI_EVENT_DISCONNECTION_COMPLETE(0x05)、HCI_EVENT_AUTHENTICATION_COMPLETE(0x06)、HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE(0x07)、HCI_EVENT_ENCRYPTION_CHANGE(0x08)、HCI_EVENT_COMMAND_COMPLETE(0x0E)、HCI_EVENT_COMMAND_STATUS(0x0F)、HCI_EVENT_HARDWARE_ERROR(0x10)、HCI_EVENT_ROLE_CHANGE(0x12)、HCI_EVENT_NUMBER_OF_COMPLETED_PACKETS(0x13)、HCI_EVENT_MODE_CHANGE(0x14)、HCI_EVENT_PIN_CODE_REQUEST(0x16)、HCI_EVENT_LINK_KEY_REQUEST(0x17)、HCI_EVENT_LINK_KEY_NOTIFICATION(0x18)、HCI_EVENT_DATA_BUFFER_OVERFLOW(0x1A)、HCI_EVENT_INQUIRY_RESULT_WITH_RSSI(0x22)。
Source: bluetooth.h
失败模式与边界场景
协议栈把连接失败的原因通过 HCI_EVENT_CONNECTION_COMPLETE 的子事件(bt->value)暴露给上层,官方文档给出了完整语义。这是本 SDK 调试回连问题最重要的查错表:
| 错误码(子事件) | 含义与处理建议 |
|---|---|
ERROR_CODE_SUCCESS | 连接成功 |
ERROR_CODE_PIN_OR_KEY_MISSING | 连接过程中 LinkKey 丢失(手机删除了 LinkKey),需触发重新配对 |
ERROR_CODE_SYNCHRONOUS_CONNECTION_LIMIT_TO_A_DEVICE_EXCEEDED | 连接个数超出资源允许,第二个连接请求被拒 |
ERROR_CODE_CONNECTION_REJECTED_DUE_TO_LIMITED_RESOURCES | 回连目标资源不足(部分手机连接一台设备后即以此拒绝),或本机资源不足 |
ERROR_CODE_CONNECTION_REJECTED_DUE_TO_UNACCEPTABLE_BD_ADDR | 对端只允许指定地址连接;或本机地址全 0/异常 |
ERROR_CODE_CONNECTION_ACCEPT_TIMEOUT_EXCEEDED | 连接耗时过长,对端主动放弃 |
ERROR_CODE_REMOTE_USER_TERMINATED_CONNECTION | 最常见的断开消息(对端主动断开) |
ERROR_CODE_CONNECTION_TERMINATED_BY_LOCAL_HOST | 正常断开,本地 L2CAP 资源释放完成后上报 |
ERROR_CODE_AUTHENTICATION_FAILURE | 鉴权失败:LinkKey 丢失,回连时会出现一次 |
ERROR_CODE_PAGE_TIMEOUT | 回连超时退出消息 |
ERROR_CODE_CONNECTION_TIMEOUT | 连接过程中 LinkKey 错误 |
ERROR_CODE_ACL_CONNECTION_ALREADY_EXISTS | 回连时对端尚未释放本机地址(常见于断电立即开机回连) |
Source: event.rst.txt
错误码驱动的决策流程可抽象为:
flowchart TD
Start(["连接请求 / 回连"]) --> Evt{"HCI_EVENT_CONNECTION_COMPLETE"}
Evt -->|"ERROR_CODE_SUCCESS"| Ok["BT_STATUS_*_CONNECTED 连接成功"]
Evt -->|"ERROR_CODE_PAGE_TIMEOUT"| Timeout["回连超时退出"]
Evt -->|"ERROR_CODE_PIN_OR_KEY_MISSING /<br/>ERROR_CODE_AUTHENTICATION_FAILURE"| RePair["重新配对"]
Evt -->|"ERROR_CODE_CONNECTION_REJECTED_DUE_TO_LIMITED_RESOURCES"| Limit["资源不足,稍后重试"]
Evt -->|"ERROR_CODE_ACL_CONNECTION_ALREADY_EXISTS"| Already["对端地址未释放,延迟重试"]
Evt -->|"ERROR_CODE_CONNECTION_REJECTED_DUE_TO_UNACCEPTABLE_BD_ADDR"| Addr["检查本机地址是否为全0/异常"]
Ok --> End(["处理完成"])
Timeout --> End
RePair --> End
Limit --> End
Already --> End
Addr --> End
边界场景要点:
- 断电开机立即回连:对端 ACL 未释放 →
ERROR_CODE_ACL_CONNECTION_ALREADY_EXISTS,应退避重试而非立即放弃; - LinkKey 丢失:回连会出现
ERROR_CODE_PIN_OR_KEY_MISSING或ERROR_CODE_AUTHENTICATION_FAILURE,随后应进入配对流程; - 断开资源释放是异步的:
HCI_EVENT_DISCONNECTION_COMPLETE之后还需等待BTSTACK_EVENT_HCI_CONNECTIONS_DELETE,在此之前槽位不可复用,重复连接会触发资源类错误; - 地址异常:
ERROR_CODE_CONNECTION_REJECTED_DUE_TO_UNACCEPTABLE_BD_ADDR出现时优先排查本机 BD_ADDR(全 0/未写入 VM)。
并发与一致性
协议栈采用单线程事件循环 + 回调模型:所有 HCI 事件与 BT_STATUS_* 状态都在协议栈上下文中按序投递,用户回调内不应做阻塞操作(如等待 I/O 或长时间自旋),否则会拖慢协议栈事件处理、放大超时类错误。双连接槽位(FIRST/SECOND)由协议栈串行管理,业务侧只需按事件顺序维护状态即可,无需额外加锁;但回调中访问共享资源(如 UI、音频状态)时仍需按 App 自身线程模型加锁,因为回调上下文与 App 其他任务上下文是并发的。
性能与运维考虑
- ISO 参数固定:LE Audio 通道使用 20ms 间隔、812 字节 SDU,音频数据按该节奏调度,上层搬移音频数据应与之对齐,避免缓冲溢出;
- 回连节奏:
ERROR_CODE_PAGE_TIMEOUT意味着一次回连尝试耗时已超时,App 应控制重试次数与间隔,避免反复回连消耗射频与功耗; - 资源上限:经典双连接 + LE 多链路共享同一控制器资源,超限时控制器以
ERROR_CODE_SYNCHRONOUS_CONNECTION_LIMIT_TO_A_DEVICE_EXCEEDED拒绝,业务侧需做连接优先级管理; - 内存布局:协议栈库通过
btstack_lib_text/data/bss.ld链接脚本放置,修改链接脚本会影响协议栈静态内存(如 ACL 缓冲区、连接槽位)的可用性,非必要不调整。
扩展点
- 事件处理函数:
bt_connction_status_event_handler(struct bt_event *bt)是协议栈对上层唯一的标准化扩展入口,所有自定义业务(回连策略、提示音、UI 状态机)都挂接在此回调上; - 杰理自定义事件:
HCI_EVENT_VENDOR_NO_RECONN_ADDR、BTSTACK_EVENT_HCI_CONNECTIONS_DELETE是协议栈为 SDK 场景扩展的事件,上层可据此实现更精准的回连与资源管理; - Profile 配置裁剪:通过
bt_profile_config.h可按产品需求裁剪双模能力(仅经典 / 仅 BLE / 双模 + LE Audio); - LE Audio / Auracast:
auracast_sink_api.h与auracast_delegator_api.h提供独立 API,可在现有双模协议栈之上叠加广播接收与转发业务,无需改动核心连接逻辑。
Related Links
- 协议栈事件说明(完整事件表):event.rst.txt
- 常用蓝牙库接口函数:常用蓝牙库接口函数.rst.txt
- 协议栈总入口头文件:bluetooth.h
- 协议栈事件定义:btstack_event.h
- 协议栈任务/调度接口:btstack_task.h
- Profile 配置:bt_profile_config.h
- A2DP 媒体编解码:a2dp_media_codec.h
- AVRCP/AVCTP 用户接口:avctp_user.h
- LE 协议族目录:le/
- 相关页面:《常用蓝牙库接口函数》《蓝牙连接管理 App》