2、蓝牙协议栈基础:HCI层、L2CAP层、SDP层、GAP层核心功能解析
各位好,欢迎来到第二章。上一章我们聊了蓝牙的整体架构,今天咱们把协议栈里最核心的四个层次掰开揉碎讲清楚。HCI、L2CAP、SDP、GAP,这四个缩写你肯定见过,但它们在驱动开发中到底扮演什么角色?我踩过的坑,今天一并告诉你。
2.1 HCI层:主机与控制器的“翻译官”
HCI,全称Host Controller Interface。说白了,它就是主机(比如你的手机AP)和蓝牙控制器(比如芯片里的射频部分)之间的通信管道。没有它,上层协议根本指挥不动硬件。
核心职责:HCI层定义了命令、事件、数据三种数据包的格式。主机发命令,控制器回事件,音频或数据就走ACL或SCO通道。
我个人习惯把HCI比作一个“串口协议”,只不过它更复杂。你想想看,主机要告诉控制器“开始扫描”,就得封装一个HCI命令包,通过UART、USB或SDIO发下去。控制器收到后,再回一个“命令完成”事件。
避坑指南:我曾经在一个项目中,发现扫描总是超时。查了两天,最后发现是HCI命令包的参数长度写错了。HCI命令的OpCode和参数长度必须严格对齐,多一个字节或少一个字节,控制器直接不理你。
常见的HCI命令有哪些?我列个表给你看:
| 命令类别 | 示例命令 | 作用 |
|---|---|---|
| 链路控制 | HCI_Inquiry | 发起查询(扫描) |
| 链路策略 | HCI_Set_Connection_Parameters | 调整连接间隔、延迟等 |
| 主机与控制器 | HCI_Reset | 复位控制器 |
| 信息获取 | HCI_Read_Local_Version_Information | 读取芯片版本号 |
嗯,这里要注意:HCI层不关心数据内容,它只负责“传话”。真正的数据组装,是L2CAP的事。
2.2 L2CAP层:数据拆包与复用
L2CAP,逻辑链路控制与适配协议。这层是蓝牙协议栈的“快递分拣中心”。
为什么需要它?因为底层(HCI)传输的数据包有大小限制。比如经典蓝牙的ACL包,最大只能发几百字节。但上层应用可能一次要发几KB的数据。L2CAP就负责把大包拆成小包,到对端再拼回去。
核心功能:分段重组(SAR)、协议复用(通过CID区分不同上层协议)、服务质量(QoS)控制。
我记得有一次调试一个音频传输项目,声音断断续续。抓包一看,发现L2CAP的MTU(最大传输单元)协商出了问题。两端MTU不匹配,导致数据频繁重传。后来我强制在连接时协商一个合理的MTU值,问题就解决了。
L2CAP的CID(通道标识符)很有意思。它有点像TCP的端口号。比如:
- 0x0001:信令通道(用于建立连接)
- 0x0003:SDP通道
- 0x0004:RFCOMM通道
- 0x0040-0xFFFF:动态分配的通道
你想想看,如果没有L2CAP,上层协议(如SDP、RFCOMM)就得直接跟HCI打交道,那代码得多乱?L2CAP把复杂性封装了,上层只管发数据,不用管底层怎么传。
注意:在BLE中,L2CAP的MTU默认只有23字节。如果你要传大数据,必须在连接后发起MTU更新请求。我见过不少新手,直接发超过23字节的数据,结果对端收不到,还以为是硬件坏了。
2.3 SDP层:蓝牙世界的“黄页”
SDP,服务发现协议。它的作用很简单:让蓝牙设备知道对方能干什么。
比如你的手机想连一个蓝牙耳机,它得先问:“你支持A2DP吗?支持HFP吗?”耳机通过SDP回复:“我支持A2DP,不支持HFP。”手机就知道该用哪个Profile了。
SDP的核心数据结构是服务记录(Service Record)。每条记录包含:
- 服务类ID(如0x110B表示A2DP Sink)
- 协议描述符(如L2CAP PSM=0x0019)
- 服务名称、提供者等文本信息
个人经验:我曾经调试一个车载蓝牙模块,手机死活搜不到它的A2DP服务。后来用抓包工具一看,发现SDP响应里服务类ID写错了。车载端把A2DP Source写成了A2DP Sink。手机一看:“哦,你是个音箱,那我就不连了。”改过来就好了。
SDP的查询过程其实挺耗时的。经典蓝牙里,每次连接都要做一次完整的SDP查询,大概需要几百毫秒。所以有些优化方案会缓存SDP结果,避免重复查询。
2.4 GAP层:连接的门卫
GAP,通用访问配置文件。它定义了蓝牙设备如何发现彼此、如何建立连接、以及连接后的安全策略。
说白了,GAP就是蓝牙世界的“社交礼仪”。它规定了:
- 设备角色:Broadcaster、Observer、Peripheral、Central(BLE)
- 发现模式:可发现、有限可发现、不可发现
- 连接模式:可连接、不可连接
- 安全模式:无安全、仅认证、认证+加密
我记得在做一个IoT传感器项目时,设备老是连不上手机。查了半天,发现GAP层把“可发现模式”设成了“不可发现”。手机扫描当然看不到它。嗯,这种低级错误,犯过一次就再也不会忘了。
GAP安全模式速查表:
| 安全模式 | 说明 | 典型场景 |
|---|---|---|
| 模式1 | 无安全(不加密) | 调试、广播 |
| 模式2 | 服务级安全(按服务加密) | 经典蓝牙配对 |
| 模式3 | 链路级安全(连接即加密) | BLE 4.2+ |
| 模式4 | 基于MITM保护的安全 | 支付、门锁 |
GAP还定义了广播数据格式。BLE设备通过广播包告诉别人“我是谁、我能干什么”。广播包最大31字节,怎么塞进设备名、服务UUID、Tx Power等信息,是个技术活。我一般会优先放最重要的服务UUID,设备名太长就截断。
2.5 四层协作:一个完整的连接流程
光讲理论没意思,咱们走一遍实际流程。假设手机连一个BLE心率计:
- GAP层:心率计广播“我是心率计,服务UUID是0x180D”。手机扫描到广播,发起连接。
- HCI层:手机通过HCI命令“Create Connection”告诉控制器建立链路。控制器回“Connection Complete”事件。
- L2CAP层:连接建立后,L2CAP自动协商MTU。手机和心率计确认双方都能支持247字节的MTU。
- SDP层:手机通过L2CAP的SDP通道查询心率计的服务记录,找到心率测量特征值的句柄。
- 之后,手机就可以通过GATT(基于L2CAP)读写心率数据了。
你看,每一层各司其职,缺一不可。如果HCI层丢包,L2CAP会重传;如果SDP查询失败,手机就不知道该怎么操作心率计。
最后提醒:很多驱动问题都出在层与层的接口上。比如HCI事件处理不及时,会导致L2CAP超时;L2CAP的MTU协商失败,会导致SDP查询卡住。调试时,建议从底层往上查,先确认HCI通信正常,再看L2CAP,最后查SDP和GAP。
好了,这一章就到这里。下一章我们会深入HCI的细节,讲讲命令和事件的处理机制。到时候我会分享一个我调试HCI流控的实战案例,保证让你有收获。