BLE 控制器、链路层与 HCI 传输
AW30N BLE SDK 中负责蓝牙低功耗(BLE)协议栈底层的完整实现面:HCI(Host Controller Interface)传输抽象层、BLE 链路层(Link Layer)控制器 API,以及它们与上层 Host 之间的命令/事件/数据通路。
Purpose and Scope
本文档面向需要理解或扩展 AW30N BLE 协议栈底层的工程师,系统性地说明三部分内容:
- HCI 传输层(
hci_transport.h):UART(H4/H5/eHCILL)与 USB 等物理传输通道的抽象接口,用于连接外部蓝牙 Host(如 PC、手机 SoC 上的 Host 协议栈)。 - 链路层控制器 API(
ble/hci_ll.h):Vendor Host 直接调用 Controller 的 LL 部分 API,覆盖广播、扫描、连接、加密、BLE 5.x 扩展广播、周期性广播以及 LE Audio 的 CIS/BIG/ISO 通路。 - 底层数据包与事件结构:ACL/ISO 数据包格式、广播报告事件、LL 事件枚举与回调注册机制。
以下内容属于其他目录页的边界,本文不展开:经典蓝牙(BR/EDR)的 hci_lmp.h 接口、GAP/GATT 等 Host 层 Profile、应用层业务(如音频链路 audio_link)。HCI 传输层虽是 Host 与 Controller 的公共边界,本文仅以 BLE 侧视角说明其接口与用法。
Overview
BLE 协议栈在逻辑上分为 Host(主机)与 Controller(控制器)两大部分,二者之间通过 HCI(Host Controller Interface)划分:
- Controller:包含物理层(PHY)、链路层(LL),负责射频收发、事件调度、加密、连接状态机等实时性要求极高的任务。
- Host:包含 L2CAP、SM、GAP、GATT 等逻辑层,负责协议语义与上层应用交互。
- HCI:定义命令(Command)、事件(Event)、ACL 数据、ISO 数据与 SCO 数据五类分组,是 Host 控制 Controller 的唯一标准接口。
AW30N 是单芯片方案,因此 SDK 提供两条路径同时支持两种部署形态:
- 片内 Vendor Host 直连:应用代码通过
hci_ll.h暴露的ll_hci_*API 直接驱动 Controller 的链路层,省去标准 HCI 命令解析开销。这也是 SDK 默认、推荐的方式。 - 外部 Host 经传输层连接:通过
hci_transport.h的 UART(H4/H5)或 USB 传输实例,把本芯片当作纯 Controller 使用,由外部 Host 协议栈通过 HCI 分组控制。
hci_ll.h 的头部注释明确说明了这一点:"提供Vendor Host 直接调用Controller API LL Part",即把 HCI 层最核心的 LL 命令子集以 C 函数形式直接暴露给片内 Host。
// hci_ll.h 头部:明确本头文件的定位
// Filename : hci_ll.h
// Description : 提供Vendor Host 直接调用Controller API LL Part
Source: hci_ll.h
Architecture
下图展示 AW30N 中 BLE 控制器、链路层与 HCI 传输的整体架构及两条 Host 通路:
flowchart TD
subgraph sg_Host["Host 层(片上 / 外部)"]
App["应用 / GAP / GATT 等 Host 逻辑"]
VHost["Vendor Host(片上直连)"]
ExtHost["外部 Host(PC / 手机 SoC)"]
end
subgraph sg_LL_API["链路层 API 面(ble/hci_ll.h)"]
LLHostAPI["ll_hci_* Host 侧 API<br/>adv / scan / conn / security"]
LLCtrlAPI["ll_hci_cmd_handler / ll_event_handler<br/>Controller 侧接口"]
LLBle5["BLE 5.x / ISO 扩展 API<br/>ext adv / periodic adv / CIS / BIG"]
end
subgraph sg_HCI_Transport["HCI 传输抽象层(hci_transport.h)"]
H4["hci_transport_h4_instance"]
H5["hci_transport_h5_instance<br/>BCSP / 自动休眠"]
UART["btstack_uart_block_*<br/>POSIX / FreeRTOS / embedded"]
USB["hci_transport_usb_instance"]
end
subgraph sg_Controller["Controller(PHY + Link Layer)"]
LL["Link Layer 状态机"]
BB["基带 / 事件调度<br/>bb_le_timer_* / bb_le_clk_get_time_us"]
RF["射频 PHY"]
end
App -->|"ll_hci_* 直连调用"| VHost
VHost --> LLHostAPI
LLHostAPI --> LLCtrlAPI
LLCtrlAPI --> LL
LLBle5 --> LLCtrlAPI
LL --> BB
BB --> RF
ExtHost -->|"HCI 分组(H4/H5/USB)"| H4
ExtHost --> H5
ExtHost --> USB
H4 --> UART
H5 --> UART
UART -->|"字节流"| LLCtrlAPI
USB -->|"USB 传输"| LLCtrlAPI
LL -.->|"事件 / ACL / ISO 数据"| LLHostAPI
架构要点说明:
- 两条 Host 通路互斥或并存:片上应用通常走
ll_hci_*直连路径(左侧),外部 Host 则通过 HCI 传输层(右侧)以标准 HCI 分组与 Controller 交互。两类接口最终都汇聚到 Controller 侧的统一入口ll_hci_cmd_handler(int *cmd)与事件出口ll_event_handler(int *msg)。 - 传输层是可插拔的:
hci_transport_t是一个函数指针结构体,H4、H5、USB 分别是其不同实现;btstack_uart_block_t进一步抽象了底层 UART 驱动(POSIX/FreeRTOS/嵌入式),使同一套 HCI 逻辑可跨平台运行。 - 链路层 API 面按能力分层:基础 LE 能力(广播/扫描/连接/加密)为
ll_hci_*直接函数;BLE 5.x 与 LE Audio(CIS/BIG/ISO)因为参数为变长结构,统一采用(u8 *data, u32 size)形式的二进制参数接口。 - 时间关键路径下沉到 BB 层:链路层依赖
bb_le_timer_set/get/reset/register/del与bb_le_clk_get_time_us获取微秒级时钟,保证连接事件与广播事件的时间精度。
Source: hci_ll.h、hci_transport.h
HCI 传输层:hci_transport.h 详解
hci_transport.h 定义了两个层次的抽象:UART 驱动接口(btstack_uart_block_t)与 HCI 传输实例接口(hci_transport_t)。这种分层借鉴了 BTstack 的设计:传输实例负责 HCI 分组级语义(帧头、分片、流控),UART 驱动只负责字节级收发,二者通过函数指针注入,可在编译期或运行期组合。
UART 配置与休眠模式
传输启动前需要给出 UART 参数。btstack_uart_config_t 包含波特率、流控开关与设备名三个字段,是所有 UART 实现的统一配置入口:
typedef struct {
uint32_t baudrate;
int flowcontrol;
const char *device_name;
} btstack_uart_config_t;
Source: hci_transport.h
休眠模式枚举定义了三种 UART 省电策略,是低功耗设计的关键:
| 枚举值 | 含义 | 适用协议 |
|---|---|---|
BTSTACK_UART_SLEEP_OFF | UART 常开,无省电 | 默认 |
BTSTACK_UART_SLEEP_RTS_HIGH_WAKE_ON_CTS_PULSE | RTS 拉高,CTS 脉冲唤醒 | eHCILL(TI H4 扩展) |
BTSTACK_UART_SLEEP_RTS_LOW_WAKE_ON_RX_EDGE | RTS 拉低,RX 边沿唤醒 | H5 及不支持 CTS 脉冲唤醒的 eHCILL |
typedef enum {
// UART active, sleep off
BTSTACK_UART_SLEEP_OFF = 0,
// used for eHCILL
BTSTACK_UART_SLEEP_RTS_HIGH_WAKE_ON_CTS_PULSE,
// used for H5 and for eHCILL without support for wake on CTS pulse
BTSTACK_UART_SLEEP_RTS_LOW_WAKE_ON_RX_EDGE,
} btstack_uart_sleep_mode_t;
Source: hci_transport.h
设计意图:BLE 外设(如 TWS 耳机、穿戴设备)对功耗极其敏感,HCI UART 在空闲时必须能关闭时钟。eHCILL 用 CTS 脉冲做确定性唤醒(字节不会丢),H5 则退化为 RX 边沿唤醒(唤醒瞬间的字节可能丢失,需要 H5 重传机制兜底)。
UART 驱动接口:btstack_uart_block_t
btstack_uart_block_t 是纯函数指针结构体,相当于一个"块式"(block-oriented)UART 驱动契约:
typedef struct {
int (*init)(const btstack_uart_config_t *uart_config);
int (*open)(void);
int (*close)(void);
void (*set_block_received)(void (*block_handler)(void));
void (*set_block_sent)(void (*block_handler)(void));
int (*set_baudrate)(uint32_t baudrate);
int (*set_parity)(int parity);
int (*set_flowcontrol)(int flowcontrol);
void (*receive_block)(uint8_t *buffer, uint16_t len);
void (*send_block)(const uint8_t *buffer, uint16_t length);
int (*get_supported_sleep_modes)(void);
void (*set_sleep)(btstack_uart_sleep_mode_t sleep_mode);
void (*set_wakeup_handler)(void (*wakeup_handler)(void));
} btstack_uart_block_t;
Source: hci_transport.h
关键语义:
receive_block/send_block是非阻塞的块传输:调用方给出缓冲区和长度,完成或出错通过set_block_received/set_block_sent注册的回调通知。这使得 HCI 传输层可以运行在中断驱动、无轮询的异步模型下。set_sleep/set_wakeup_handler是低功耗扩展点:set_wakeup_handler注册的回调会在 CTS 脉冲或 RX 数据到来时被调用,用于通知 HCI 传输层"Controller 请求唤醒"(见头文件第 116-119 行注释)。- SDK 提供四个公共实现工厂:
btstack_uart_block_posix_instance()、btstack_uart_block_windows_instance()、btstack_uart_block_embedded_instance()、btstack_uart_block_freertos_instance(),分别面向 PC 调试、嵌入式裸机与 FreeRTOS 环境。
HCI 传输实例接口:hci_transport_t
hci_transport_t 封装了 HCI 分组级能力:注册分组回调、发送分组、查询是否可立即发送等:
typedef struct {
const char *name;
void (*init)(const void *transport_config);
int (*open)(void);
int (*close)(void);
void (*register_packet_handler)(void (*handler)(int packet_type, const u8 *packet, int size));
int (*can_send_packet_now)(uint8_t packet_type);
int (*send_packet)(int packet_type, const u8 *packet, int size);
int (*set_baudrate)(uint32_t baudrate);
void (*reset_link)(void); // H5/BCSP 链路复位
void (*set_sco_config)(uint16_t voice_setting, int num_connections); // USB SCO 配置
} hci_transport_t;
Source: hci_transport.h
设计要点:
register_packet_handler是 HCI 数据上行入口,回调参数packet_type区分命令/事件/ACL/SCO/ISO 分组。can_send_packet_now支持"异步传输层"(无缓冲、中断驱动)——发送前先查询是否可发,避免阻塞。reset_link是 H5(BCSP)特有扩展,用于复位 H5 链路层状态机;set_sco_config是 USB 传输的扩展,用于配置 SCO 连接数。这两个扩展以可选函数指针形式存在,体现了接口设计的"最小公共接口 + 按需扩展"思路。
传输配置与实例工厂
配置类型用 type 字段区分 UART 与 USB 两种传输,便于在统一入口上做类型分派:
typedef enum {
HCI_TRANSPORT_CONFIG_UART,
HCI_TRANSPORT_CONFIG_USB
} hci_transport_config_type_t;
typedef struct {
hci_transport_config_type_t type; // == HCI_TRANSPORT_CONFIG_UART
uint32_t baudrate_init; // initial baud rate
uint32_t baudrate_main; // = 0: same as initial baudrate
int flowcontrol;
const char *device_name;
} hci_transport_config_uart_t;
Source: hci_transport.h
baudrate_init 与 baudrate_main 分离的用意:H4/H5 协议支持"波特率切换"——先用低速波特率完成 HCI 命令交换,再通过命令把双方切到高速波特率,baudrate_main 为 0 时表示与初始波特率相同。
实例工厂函数一览:
| 工厂函数 | 用途 |
|---|---|
hci_transport_h4_instance(uart_driver) | H4 协议(单字节分组指示 + CRC 可选),需传入 btstack_uart_block_t |
hci_transport_h5_instance(uart_driver) | H5 协议(面向字节流的三线 UART,含滑动窗口重传),需传入 btstack_uart_block_t |
hci_transport_usb_instance() | USB 传输实例 |
hci_transport_uart_instance() | 通用 UART 传输 |
hci_transport_h4_controller_instance() | Controller 侧 H4 实例 |
hci_transport_h4_host_instance() | Host 侧 H4 实例 |
配套的 H5 控制函数:hci_transport_h5_set_auto_sleep(inactivity_timeout_ms) 设置空闲休眠超时(0 为关闭),hci_transport_h5_enable_bcsp_mode() 开启 BCSP 模式(启用事件奇偶校验),hci_transport_usb_set_path(len, port_numbers) 按 USB 端口路径指定设备。
链路层 API:hci_ll.h 详解
hci_ll.h 是片上 Host 与链路层之间的"零拷贝"接口面。与标准 HCI 不同,这里没有命令解析器与命令完成事件的往返,而是把 LL 功能直接映射为 C 函数调用,降低延迟与 RAM 开销。文件内注释把 API 明确划分为 Host part 与 Controller part 两大块,外加 BLE 5.x 扩展与厂商(vendor)扩展。
Host 侧 API:广播 / 扫描 / 连接 / 加密
这部分函数由片内 Host 直接调用,是应用层最常接触的 LL 入口:
//Adjust Host part API
void ll_hci_init(void);
void ll_hci_reset(void);
void ll_hci_destory(void);
void ll_hci_set_event_mask(const u8 *mask);
void ll_hci_set_name(const char *name);
void ll_hci_adv_set_params(uint16_t adv_int_min, uint16_t adv_int_max, uint8_t adv_type,
uint8_t direct_address_type, uint8_t *direct_address,
uint8_t channel_map, uint8_t filter_policy);
void ll_hci_adv_set_data(uint8_t advertising_data_length, uint8_t *advertising_data);
void ll_hci_adv_scan_response_set_data(uint8_t scan_response_data_length, uint8_t *scan_response_data);
int ll_hci_adv_enable(bool enable);
void ll_hci_scan_set_params(uint8_t scan_type, uint16_t scan_interval, uint16_t scan_window);
int ll_hci_scan_enable(bool enable, u8 filter_duplicates);
int ll_hci_create_conn(u8 *conn_param, u8 *addr_param);
int ll_hci_create_conn_ext(void *param);
int ll_hci_create_conn_cancel(void);
Source: hci_ll.h
接口设计规律:
- 生命周期:
ll_hci_init→ 配置(广播/扫描参数)→ 使能(*_enable)→ 使用 →ll_hci_reset/ll_hci_destory。init与reset分离,便于运行中恢复出厂链路状态而不释放资源。 - 参数语义对齐 HCI 规范:
ll_hci_adv_set_params的参数顺序与 HCILE Set Advertising Parameters命令一致(间隔、类型、直连地址、信道图、过滤策略),从标准 Host 移植代码成本低。 - 返回 int 表示成否:使能类接口返回 0 成功、非 0 失败,失败原因通常映射到 Controller 错误码(如
CONNECTION_TERMINATED_BY_LOCAL_HOST 0x16)。
连接与加密相关的函数覆盖了 LE 安全的核心流程:
int ll_hci_encryption(u8 *key, u8 *plaintext_data); // AES-128 加密原语
int ll_hci_get_le_rand(void); // 获取随机数
int ll_hci_start_encryption(u16 handle, u32 rand_low, u32 rand_high, u16 peer_ediv, u8 *ltk);
int ll_hci_long_term_key_request_reply(u16 handle, u8 *ltk); // 应答 LTK 请求
int ll_hci_long_term_key_request_nagative_reply(u16 handle); // 拒绝 LTK 请求
int ll_hci_connection_update(u16 handle, u16 conn_interval_min, u16 conn_interval_max,
u16 conn_latency, u16 supervision_timeout,
u16 minimum_ce_length, u16 maximum_ce_length);
u16 ll_hci_get_acl_data_len(void);
u16 ll_hci_get_acl_total_num(void);
void ll_hci_set_random_address(u8 *addr);
int ll_hci_disconnect(u16 handle, u8 reason);
int ll_hci_read_local_p256_pb_key(void);
int ll_hci_generate_dhkey(const u8 *data, u32 size);
Source: hci_ll.h
其中 ll_hci_start_encryption 对应 HCI LE Start Encryption(传入 EDIV 与 Rand 的低/高 32 位),ll_hci_connection_update 对应 LE Connection Update,ll_hci_generate_dhkey 对应 SMP 的 P-256 DH 密钥生成——这些是 LE Secure Connections 的底层支撑。
Controller 侧 API:命令入口与事件出口
Controller 侧接口是双通路汇聚点:无论命令来自片内 Host 直连还是外部 HCI 传输,最终都进入 ll_hci_cmd_handler 解析执行;事件与数据则通过 ll_event_handler 向上分发。
//Adjust Controller part API
void ll_hci_cmd_handler(int *cmd); // HCI 命令统一入口(含标准命令与 vendor 命令)
void ll_event_handler(int *msg); // LL 事件向上分发
void ll_hci_private_free_dma_rx(u8 *rx_head); // 释放 DMA RX 缓冲
void ll_hci_set_data_length(u16 conn_handle, u16 tx_octets, u16 tx_time); // LE Data Length
hci_ll_param_t *ll_hci_param_config_get(void); // 读取地址类型/过滤策略位域配置
void hci_ll_get_device_address(uint8_t *addr_type, u8 *addr);
void ll_hci_set_host_channel_classification(u8 *channel_map); // 主机信道分类
Source: hci_ll.h
hci_ll_param_t 是一个 8 位位域结构,集中了四种地址/过滤策略配置,避免为每个策略单开接口:
typedef struct {
u8 Own_Address_Type: 2;
u8 Adv_Filter_Policy: 2;
u8 Scan_Filter_Policy: 2;
u8 initiator_filter_policy: 2;
} hci_ll_param_t;
Source: hci_ll.h
ll_hci_private_free_dma_rx 的存在说明 RX 路径使用 DMA 直接写入内存,上层处理完数据后需显式归还缓冲——这是典型的零拷贝设计,也是内存管理上必须配对使用的约束(使用后不释放会导致 RX 缓冲耗尽)。
BLE 5.x 扩展:扩展广播 / 周期性广播 / PHY
BLE 5 的命令参数复杂且长度可变,因此 API 风格统一为"二进制参数 + 长度",由调用方按 hci_ll.h 中定义的打包结构填充:
// ble5
void ll_hci_set_ext_adv_params(u8 *data, u32 size);
void ll_hci_set_ext_adv_data(u8 *data, u32 size);
void ll_hci_set_ext_adv_enable(u8 *data, u32 size);
void ll_hci_set_phy(u16 conn_handle, u8 all_phys, u8 tx_phy, u8 rx_phy, u16 phy_options);
void ll_hci_set_ext_scan_params(u8 *data, u32 size);
void ll_hci_set_ext_scan_enable(u8 *data, u32 size);
void ll_hci_ext_create_conn(u8 *data, u32 size);
void ll_hci_set_periodic_adv_params(u8 *data, u32 size);
void ll_hci_set_periodic_adv_data(u8 *data, u32 size);
void ll_hci_set_periodic_adv_enable(u8 *data, u32 size);
void ll_hci_periodic_adv_creat_sync(u8 *data, u32 size);
void ll_hci_periodic_adv_terminate_sync(u8 *data, u32 size);
void ll_hci_periodic_adv_create_sync_cancel(void);
Source: hci_ll.h
对应的参数结构体(均为 _GNU_PACKED_ 紧凑布局,直接对应 HCI 命令参数区)包括:
le_set_ext_adv_param_t:扩展广播参数(Advertising_Handle、Event_Properties、主/次信道 PHY、Advertising_SID 等 16 个字段)。le_set_ext_adv_data_t:扩展广播数据,Advertising_Data[31]定长数组承载最长 31 字节的载荷。le_set_ext_adv_en_t:扩展广播使能(Enable、Number_of_Sets、Duration、Max_Extended_Advertising_Events)。__ext_scan_param/__ext_scan_enable:扩展扫描参数(可含多 PHY 的scan_phy_param[]变长数组)与使能(Filter_Duplicates、Duration、Period)。le_evt_type_t:扩展广播事件类型的位域联合体,用 16 bit 位域描述 Connectable/Scannable/Directed/Scan_response/Legacy/Data_status,并可通过event_type字段整体读写。
/*! \brief LE Extended Advertising report event. */
typedef union {
struct {
uint16_t Connectable_advertising : 1,
Scannable_advertising : 1,
Directed_advertising : 1,
Scan_response : 1,
Legacy_adv_PDUs_used : 1,
Data_status : 2,
All_other_bits : 9;
};
uint16_t event_type;
} _GNU_PACKED_ le_evt_type_t;
Source: hci_ll.h
LE Audio(ISO)扩展:CIS / CIG / BIG
hci_ll.h 为 LE Audio(LE 2M/CODED PHY 上的 ISO 数据)提供了完整的数据结构与命令接口,覆盖 CIG/CIS(连接等时流)与 BIG/BIS(广播等时流)两条通路:
void ll_hci_set_cig_params(uint8_t *data, size_t size);
void ll_hci_create_cis(uint8_t *data, size_t size);
void ll_hci_remove_cig(uint8_t *data, size_t size);
void ll_hci_accept_cis_req(uint8_t *data, size_t size);
void ll_hci_create_big(uint8_t *data, size_t size);
void ll_hci_big_create_sync(uint8_t *data, size_t size);
void ll_hci_big_terminate_sync(uint8_t *data, size_t size);
void ll_hci_read_iso_tx_sync(uint8_t *data, size_t size);
void ll_hci_setup_iso_data_path(uint8_t *data, size_t size);
Source: hci_ll.h
关键数据结构:
le_set_cig_param_t:CIG 参数(SDU_Interval、Packing、Framing、Max_Transport_Latency、CIS_Count 及变长param[]数组,每个 CIS 含 PHY/RTN/Max_SDU)。le_create_cis_t:创建 CIS,param[]为 CIS 连接句柄与 ACL 连接句柄对。le_create_big_t:创建 BIG,含 Broadcast_Code[16] 加密密钥、RTN、PHY、Packing/Framing 等。le_big_create_sync_t/le_big_terminate_sync_t:接收端同步/终止 BIG。hci_iso_data_packets_t/hci_iso_hdr_t:ISO 数据分组头,位域包含 Connection_Handle(12)、PB_Flag(2)、TS_Flag(1)、ISO_Data_Load_Length(14),以及时间戳、Packet_Sequence_Num、ISO_SDU_Length、Packet_Status_Flag(2)(0b00 有效 / 0b01 可能错误 / 0b10 数据丢失)。
hci_iso_hdr_t 对 pb_flag 的注释给出了分片语义(0b00 首片、0b01 续片、0b10 完整 SDU、0b11 末片),packet_status_flag 则让接收方知道 SDU 是否完整——这对音频数据是"丢包不重传、但必须告知解码器"场景的关键设计。
LL 事件与回调注册机制
链路层向上报事件有两种通道:一是 ll_event_handler(int *msg) 的 LL 内部事件(枚举 LL_EVENT_SUPERVISION_TIMEOUT、LL_EVENT_RX、LL_EVENT_ACL_TX_POST),二是面向 Host 的回调注册接口:
enum {
LL_EVENT_SUPERVISION_TIMEOUT,
LL_EVENT_RX,
LL_EVENT_ACL_TX_POST,
};
void hci_add_event_handler(void *callback_handler);
void hci_remove_event_handler(void *callback_handler);
void ll_hci_event_callback_register(void (*callback)(uint8_t packet_type, uint8_t *packet, uint16_t size));
void hci_iso_receive_callback_register(void *callback);
void ll_conn_rx_acl_callback_register(void (*callback)(uint8_t *packet, size_t size));
void ll_big_tx_align_callback_register(uint8_t big_handle, const void *callback);
void ll_cig_tx_align_callback_register(uint8_t cig_id, const void *callback);
回调语义分析:
hci_add_event_handler/hci_remove_event_handler是通用事件分发器,支持注册/注销多个处理器,用于 HCI 事件(如连接完成、加密变化)。ll_hci_event_callback_register是分组级回调(packet_type + packet + size),与 HCI 传输层的register_packet_handler签名一致,说明片内 Host 与外部 Host 看到的是同一类事件流。ll_conn_rx_acl_callback_register直接暴露 ACL 接收数据包;ll_big_tx_align_callback_register/ll_cig_tx_align_callback_register用于 CIG/BIG 发送对齐——LE Audio 要求音频数据按等时间隔(ISO_Interval)对齐发送,回调在时间点到达时触发应用填充音频帧。- 事件头中的
hci_iso_receive_callback_register注册 ISO 数据接收回调,配合ll_iso_unpack_hdr解包 ISO 头:
uint8_t ll_iso_unpack_hdr(const uint8_t *sdu, hci_iso_hdr_t *hdr);
基带定时器与时钟接口
链路层的时间关键操作依赖 BB 层提供的微秒级定时器,这些接口也被暴露给上层用于调试与同步:
typedef void (*timeout_callback_t)(void *priv);
void bb_le_timer_set(uint8_t idx, uint32_t usec, timeout_callback_t callback, void *priv);
u32 bb_le_clk_get_time_us(void);
uint8_t bb_le_timer_get(void);
void bb_le_timer_reset(uint8_t idx, uint32_t usec);
void bb_le_timer_register(uint8_t idx, timeout_callback_t callback, void *priv);
void bb_le_timer_del(uint8_t idx);
Source: hci_ll.h
bb_le_clk_get_time_us 返回链路层 1/12 微秒精度的系统时钟(ll_get_ctrler_clk 的输出参数 us_1per12 印证了这一点),定时器以索引(idx)管理、支持 set/reset/register/del 完整生命周期。事件调度(连接事件、广播事件、CIG/BIG 对齐)都建立在它之上。
核心流程
流程一:片内 Vendor Host 直连链路层(广播示例)
以"开启广播"为例,展示从应用调用到射频发射的完整控制流:
sequenceDiagram
participant App as 应用层
participant LL as ll_hci_* API(hci_ll.h)
participant Cmd as ll_hci_cmd_handler
participant LLState as Link Layer 状态机
participant BB as 基带(bb_le_timer_*)
participant RF as 射频 PHY
App->>LL: ll_hci_adv_set_params(间隔, 类型, 信道图, 过滤策略)
LL->>Cmd: 参数打包为 LL 命令
Cmd->>LLState: 配置广播参数
App->>LL: ll_hci_adv_set_data(len, advertising_data)
LL->>Cmd: 设置广播数据
App->>LL: ll_hci_adv_enable(true)
LL->>Cmd: 使能广播
Cmd->>LLState: 进入 Advertising 状态
LLState->>BB: 注册广播事件定时器(bb_le_timer_set)
BB->>RF: 按 adv_interval 触发 PDU 发送
RF-->>LLState: TX 完成
LLState->>Cmd: 上报事件(ll_event_handler / 回调)
Cmd-->>App: hci_add_event_handler 注册的回调
流程要点: 参数设置是同步的(void 返回,直接写入链路层状态),只有使能类操作返回 int 表达结果;射频发送由 BB 定时器驱动,与 CPU 主流程异步——这也是为什么上层必须通过回调而非轮询获取发送结果。
流程二:外部 Host 经 HCI UART 传输(H4)控制 Controller
当 AW30N 作为纯 Controller 被外部 Host 驱动时,HCI 分组流经传输层:
sequenceDiagram
participant Host as 外部 Host
participant T as hci_transport_h4_instance
participant U as btstack_uart_block_*(UART 驱动)
participant Cmd as ll_hci_cmd_handler
participant Evt as ll_event_handler / 回调
Host->>T: send_packet(HCI_CMD, cmd, size)
T->>T: 添加 H4 分组指示字节(0x01 命令)
T->>U: send_block(buffer, len)
U->>Cmd: UART 中断接收 → DMA → 命令缓冲
Cmd->>Cmd: 解析并执行 LL 命令
Cmd->>Evt: 生成 Command Complete / LE Meta 事件
Evt->>T: register_packet_handler 分发事件分组
T->>U: send_block(事件分组)
U-->>Host: 事件经 UART 返回
流程要点: 外部 Host 路径与片内直连路径共享 ll_hci_cmd_handler,区别仅在命令来源(UART 分组 vs 直接函数调用)。can_send_packet_now 在发送前查询 UART 是否可写,避免在无缓冲传输层上阻塞。
流程三:LE Audio 发送对齐(CIG/BIG)
ISO 数据对时序敏感,发送对齐回调是设计核心:
sequenceDiagram
participant App as 音频应用
participant Cb as ll_cig_tx_align_callback_register 回调
participant LL as 链路层(ISO 调度)
participant BB as 基带时钟
App->>Cb: 注册 CIG 发送对齐回调
BB->>LL: ISO_Interval 时间点到达(bb_le_clk_get_time_us 校准)
LL->>Cb: 触发对齐回调
Cb->>App: 填充音频 SDU 到 ISO 缓冲
App->>LL: 提交 ISO SDU(hci_iso_hdr_t 描述)
LL->>BB: 按 CIG 参数(NSE/BN/PHY)发送
BB-->>App: 发送完成(packet_status_flag 反馈状态)
使用示例
示例一:初始化并配置广播
以下代码展示片内 Host 使用 hci_ll.h API 的典型调用序列(参数结构取自头文件声明):
// 生命周期初始化
ll_hci_init();
ll_hci_set_event_mask(mask);
ll_hci_set_name("AW30N_DEV");
// 配置广播:间隔 100ms~100ms,ADV_IND,全信道
ll_hci_adv_set_params(160, 160, 0 /*ADV_IND*/,
0 /*public*/, NULL,
0x07 /*37/38/39 信道*/, 0 /*过滤策略*/);
// 设置广播数据(31 字节以内)
uint8_t adv_data[] = { 0x02, 0x01, 0x06, 0x03, 0x03, 0x12, 0x18, 0x00 };
ll_hci_adv_set_data(sizeof(adv_data), adv_data);
ll_hci_adv_scan_response_set_data(0, NULL);
// 使能广播
int ret = ll_hci_adv_enable(true);
Source: hci_ll.h
示例二:建立连接与加密
连接建立与 LTK 应答的典型序列:
// 主动连接:conn_param 为连接参数,addr_param 为目标地址
int ret = ll_hci_create_conn(conn_param, addr_param);
// 收到对端加密请求后,用 LTK 应答
int ok = ll_hci_long_term_key_request_reply(handle, ltk);
// 或者拒绝
int no = ll_hci_long_term_key_request_nagative_reply(handle);
// 发起连接参数更新
ll_hci_connection_update(handle, 6, 6, 0, 500, 0xFFFF, 0xFFFF);
// 断开连接(原因码见 Controller Error Codes,如 0x16)
ll_hci_disconnect(handle, CONNECTION_TERMINATED_BY_LOCAL_HOST);
Source: hci_ll.h
示例三:HCI 传输层初始化(外部 Host / H4 + FreeRTOS UART)
传输层组合示例——用 FreeRTOS UART 驱动构造 H4 传输实例:
// 1. 选择 UART 驱动实现(FreeRTOS / POSIX / embedded / windows)
const btstack_uart_block_t *uart_driver = btstack_uart_block_freertos_instance();
// 2. 构造 H4 传输实例
const hci_transport_t *transport = hci_transport_h4_instance(uart_driver);
// 3. UART 配置:初始波特率 115200,主波特率 1000000,使能流控
hci_transport_config_uart_t config = {
.type = HCI_TRANSPORT_CONFIG_UART,
.baudrate_init = 115200,
.baudrate_main = 1000000,
.flowcontrol = 1,
.device_name = "bt_uart",
};
// 4. 初始化并注册 HCI 分组回调
transport->init(&config);
transport->register_packet_handler(hci_packet_handler);
transport->open();
// 5. 发送 HCI 命令分组(packet_type 为 HCI_COMMAND_PACKET)
transport->send_packet(HCI_COMMAND_PACKET, cmd_buf, cmd_len);
Source: hci_transport.h、hci_transport.h
配置选项
以下配置项来自 HCI 传输层与链路层 API 面,均以结构体字段或函数参数形式暴露:
| 配置项 | 类型 | 默认/取值 | 说明 |
|---|---|---|---|
btstack_uart_config_t.baudrate | uint32_t | 由调用方指定 | UART 初始波特率 |
btstack_uart_config_t.flowcontrol | int | 由调用方指定 | 硬件流控开关(RTS/CTS) |
btstack_uart_config_t.device_name | const char* | 由调用方指定 | 设备节点名(如 "bt_uart") |
hci_transport_config_uart_t.baudrate_init | uint32_t | 115200 常见 | 初始波特率,用于 HCI 握手 |
hci_transport_config_uart_t.baudrate_main | uint32_t | 0(=init) | 切换后的主波特率 |
btstack_uart_sleep_mode_t | enum | SLEEP_OFF | UART 休眠模式:OFF / eHCILL(CTS 脉冲) / H5(RX 边沿) |
hci_transport_h5_set_auto_sleep | uint16_t | 0(关闭) | H5 空闲休眠超时(ms) |
hci_transport_h5_enable_bcsp_mode | void | 关闭 | 启用 BCSP 模式(事件奇偶校验) |
ll_hci_adv_set_params 参数 | 多类型 | 调用方指定 | 广播间隔/类型/信道图/过滤策略 |
ll_hci_adv_set_data 长度 | uint8_t | ≤31 | 广播数据长度 |
ll_hci_set_data_length | uint16_t | 调用方指定 | LE Data Length 的 tx_octets/tx_time |
ll_set_scan_priority | uint8_t | 调用方指定 | 扫描优先级 |
hci_ll_param_t 位域 | 4×2bit | 调用方指定 | Own 地址类型、广播/扫描/发起过滤策略 |
API 参考
以下为 hci_ll.h 与 hci_transport.h 中最重要的公开接口签名。完整列表以头文件为准,此处按功能分组给出核心条目。
生命周期与初始化
| 函数 | 说明 |
|---|---|
void ll_hci_init(void) | 初始化链路层 Host 接口,必须先于其他 ll_hci_* 调用 |
void ll_hci_reset(void) | 复位链路层状态(连接、广播、扫描全部终止) |
void ll_hci_destory(void) | 销毁链路层 Host 接口,释放资源 |
void ll_hci_set_event_mask(const u8 *mask) | 设置事件掩码,控制哪些 HCI 事件上报 |
void ll_hci_set_name(const char *name) | 设置设备名(用于 Read Local Name) |
广播与扫描
| 函数 | 说明 |
|---|---|
void ll_hci_adv_set_params(uint16_t adv_int_min, uint16_t adv_int_max, uint8_t adv_type, uint8_t direct_address_type, uint8_t *direct_address, uint8_t channel_map, uint8_t filter_policy) | 配置广播参数;间隔单位为 0.625ms |
void ll_hci_adv_set_data(uint8_t advertising_data_length, uint8_t *advertising_data) | 设置广播数据(≤31 字节) |
void ll_hci_adv_scan_response_set_data(uint8_t len, uint8_t *data) | 设置扫描响应数据 |
int ll_hci_adv_enable(bool enable) | 使能/关闭广播;返回 0 成功 |
void ll_hci_scan_set_params(uint8_t scan_type, uint16_t scan_interval, uint16_t scan_window) | 配置扫描参数 |
int ll_hci_scan_enable(bool enable, u8 filter_duplicates) | 使能/关闭扫描,可同时开启重复过滤 |
void ll_hci_set_random_address(u8 *addr) | 设置随机地址(用于隐私) |
连接与安全
| 函数 | 说明 |
|---|---|
int ll_hci_create_conn(u8 *conn_param, u8 *addr_param) | 发起 LE 连接 |
int ll_hci_create_conn_ext(void *param) | 发起扩展连接(BLE 5) |
int ll_hci_create_conn_cancel(void) | 取消未完成的连接请求 |
int ll_hci_disconnect(u16 handle, u8 reason) | 断开连接,reason 为 HCI 错误码 |
int ll_hci_start_encryption(u16 handle, u32 rand_low, u32 rand_high, u16 peer_ediv, u8 *ltk) | 启动链路加密 |
int ll_hci_long_term_key_request_reply(u16 handle, u8 *ltk) | 应答 LTK 请求(接受) |
int ll_hci_long_term_key_request_nagative_reply(u16 handle) | 应答 LTK 请求(拒绝) |
int ll_hci_connection_update(u16 handle, u16 interval_min, u16 interval_max, u16 latency, u16 timeout, u16 ce_min, u16 ce_max) | 更新连接参数 |
int ll_hci_read_local_p256_pb_key(void) / int ll_hci_generate_dhkey(const u8 *data, u32 size) | LE Secure Connections:读本地 P-256 公钥 / 计算 DH 密钥 |
事件与回调
| 函数 | 说明 |
|---|---|
void hci_add_event_handler(void *callback_handler) | 注册 HCI 事件处理器(可多个) |
void hci_remove_event_handler(void *callback_handler) | 注销事件处理器 |
void ll_hci_event_callback_register(void (*cb)(uint8_t packet_type, uint8_t *packet, uint16_t size)) | 注册分组级事件回调 |
void ll_conn_rx_acl_callback_register(void (*cb)(uint8_t *packet, size_t size)) | 注册 ACL 接收回调 |
void hci_iso_receive_callback_register(void *callback) | 注册 ISO 数据接收回调 |
void ll_cig_tx_align_callback_register(uint8_t cig_id, const void *callback) | 注册 CIG 发送对齐回调 |
void ll_big_tx_align_callback_register(uint8_t big_handle, const void *callback) | 注册 BIG 发送对齐回调 |
HCI 传输层
| 函数 | 说明 |
|---|---|
const hci_transport_t *hci_transport_h4_instance(const btstack_uart_block_t *uart_driver) | 构造 H4 传输实例 |
const hci_transport_t *hci_transport_h5_instance(const btstack_uart_block_t *uart_driver) | 构造 H5 传输实例 |
const hci_transport_t *hci_transport_usb_instance(void) | 构造 USB 传输实例 |
const hci_transport_t *hci_transport_h4_controller_instance(void) | Controller 侧 H4 实例 |
const hci_transport_t *hci_transport_h4_host_instance(void) | Host 侧 H4 实例 |
void hci_transport_h5_set_auto_sleep(uint16_t inactivity_timeout_ms) | 设置 H5 自动休眠超时(0 关闭) |
void hci_transport_h5_enable_bcsp_mode(void) | 启用 BCSP 模式 |
void hci_transport_usb_set_path(int len, uint8_t *port_numbers) | 按 USB 端口路径选择设备 |
const btstack_uart_block_t *btstack_uart_block_freertos_instance(void) 等 4 个 | 获取平台 UART 驱动实例 |
传输接口方法(hci_transport_t 函数指针):
init(const void *transport_config):初始化传输,transport_config为hci_transport_config_uart_t或 USB 配置。open()/close():打开/关闭传输连接。register_packet_handler(handler):注册 HCI 分组回调(packet_type + packet + size)。can_send_packet_now(packet_type):查询是否可立即发送指定类型分组。send_packet(packet_type, packet, size):发送分组;返回 0 成功,非 0 失败(如缓冲区满)。set_baudrate(baudrate):运行期切换波特率(H4/H5 波特率切换用)。reset_link():H5/BCSP 链路复位。set_sco_config(voice_setting, num_connections):USB 传输配置 SCO。
故障模式、边界情况与并发
连接超时与断开
- 链路层通过
LL_EVENT_SUPERVISION_TIMEOUT事件上报监督超时(连接参数中的supervision_timeout到期且未收到任何有效包)。上层应通过ll_hci_disconnect或直接处理该事件完成状态清理。 - 主动断开原因码
CONNECTION_TERMINATED_BY_LOCAL_HOST (0x16)在hci_ll.h中以宏定义暴露,表明 SDK 内部按 HCI 规范错误码体系组织断开原因。
DMA 缓冲生命周期
- RX 数据经 DMA 直接写入内存(
ll_hci_private_free_dma_rx(u8 *rx_head)的存在证明此设计)。上层处理完 RX 数据后必须调用该函数归还缓冲,否则 DMA RX 缓冲池耗尽将导致接收停止。这是零拷贝设计伴随的显式内存管理约束。
回调注册的并发语义
hci_add_event_handler/hci_remove_event_handler支持多处理器,但头文件未声明线程安全语义。在 FreeRTOS/裸机场景下,事件回调运行在链路层上下文(中断或高优先级任务),回调内不得执行阻塞操作(如等待互斥锁、长时间打印),否则将直接拉长链路层调度周期,造成连接事件漂移。set_block_received/set_block_sent的回调同样是中断上下文,处理必须轻量;真正耗时的解析应推迟到任务上下文。
休眠模式的字节丢失风险
BTSTACK_UART_SLEEP_RTS_LOW_WAKE_ON_RX_EDGE(H5)模式下,唤醒瞬间 RX 上的字节可能丢失,依赖 H5 滑动窗口重传机制恢复;而 eHCILL 的 CTS 脉冲唤醒是确定性的。选择休眠模式时必须与所用协议(H4 无重传 vs H5 有重传)匹配,否则会引入不可恢复的帧错误。
参数边界
ll_hci_adv_set_data的广播数据长度以 uint8_t 传递,标准广播通道 PDU 上限 31 字节;扩展广播数据(le_set_ext_adv_data_t)为 31 字节定长数组,而le_set_prd_adv_data_t注释标注"0 to 252"字节——不同接口的容量上限不同,超限填充会造成 PDU 截断或参数错误。ll_hci_create_conn失败返回非 0 后,必须调用ll_hci_create_conn_cancel或等待连接超时清理内部状态机,不能直接重发。
性能与运行考虑
零拷贝与 DMA
- 链路层 RX 使用 DMA 直写(
ll_hci_private_free_dma_rx),TX 方向数据包结构均为_GNU_PACKED_紧凑布局,与 HCI 规范逐字节对应,避免了结构体填充导致的协议偏差。
时间精度
bb_le_clk_get_time_us()与ll_get_ctrler_clk(hdl, &us_1per12, &ref_clk_us, &evt)提供微秒级时间基准,连接事件、广播事件、CIG/BIG 发送对齐全部依赖它。任何上层对链路层时间的占用(长临界区、关中断)都会直接影响射频调度精度。
带宽估算工具
hci_ll.h尾部提供三个带宽估算函数,用于在启动连接/广播前评估 PHY 与载荷长度下的空中时间开销:
uint32_t ll_aux_ind_packet_bandwidth(uint8_t phy, uint16_t payload_len, uint8_t encrypt);
uint32_t ll_ext_ind_packet_bandwidth(uint8_t phy, uint16_t payload_len, uint8_t encrypt);
uint32_t ll_padv_packet_bandwidth(uint8_t phy, uint16_t payload_len, uint8_t encrypt);
Source: hci_ll.h
- 配合
ll_vendor_latency_hold_cnt(conn_handle, hold_cnt)、ll_vendor_open_latency/ll_vendor_close_latency等厂商延迟控制接口,可以在功耗与吞吐之间做运行期权衡。
功耗控制
- UART 休眠模式(eHCILL/H5)、H5 自动休眠超时、
ll_set_scan_priority扫描优先级、ll_hci_set_data_length数据长度限制等,共同构成低功耗调优的手段。默认全关(SLEEP_OFF、auto_sleep=0),需要功耗优化时按链路状态逐步开启。
扩展点
- 平台 UART 驱动:实现
btstack_uart_block_t的全部函数指针即可接入新平台(POSIX/Windows/embedded/FreeRTOS 之外的自定义驱动),H4/H5 传输逻辑无需改动。 - 事件分发:
hci_add_event_handler支持多处理器注册,可在不侵入链路层的情况下挂接调试探针、日志或协议分析器。 - 音频对齐回调:
ll_cig_tx_align_callback_register/ll_big_tx_align_callback_register让应用在 ISO 时间点填充音频数据,可扩展实现不同的编解码缓冲策略(如双缓冲、动态水位)。 - Vendor 命令:
ll_set_vendor_param(uint8_t *vendor_param, size_t size)提供厂商私有参数注入通道,SDK 的专有特性(如ll_set_private_access_addr_pair_channel、ll_vendor_set_code_type、rf_mdm_con_ble_sync_wordTWS 同步)都通过这类接口扩展。 - MAC 管理:
le_controller_set_mac(void *addr)可运行期覆盖设备地址,用于产线烧录或多地址场景。
Related Links
- 经典蓝牙 HCI/LMP 接口(hci_lmp.h) —— 经典蓝牙侧的对应接口,本文未展开
- hci_transport.h 完整头文件
- hci_ll.h 完整头文件
- 补丁包中同一接口的版本差异:
补丁包/v1.2.0升级至v1.3.0蓝牙补丁/include_lib/bt_controller_include/ble/hci_ll.h、补丁包/v1.3.4升级至v1.4.0蓝牙相关补丁/.../hci_ll.h可供对比版本演进