2、蓝牙协议栈架构:Host与Controller分层、HCI接口详解、L2CAP、SDP、GATT等核心协议

好,咱们今天聊聊蓝牙协议栈的架构。说实话,很多刚入行的朋友一上来就被这堆分层搞懵了。我当年也一样,第一次看蓝牙Spec的时候,心里直犯嘀咕:这玩意儿怎么这么复杂?

其实你把它拆开看,就没那么吓人了。蓝牙协议栈的核心思想,就是分层。说白了,就是把不同功能拆成独立的模块,各干各的活,互不干扰。

2.1 Host与Controller:一对好搭档

蓝牙协议栈最顶层的一个分界线,就是HostController。这两个家伙分工非常明确:

  • Controller:负责底层的射频、基带、链路管理。它是个“实干家”,只管收发数据包,不关心数据是什么内容。
  • Host:负责上层的逻辑、协议、应用。它是个“指挥官”,告诉Controller该干什么,怎么干。

为什么这么分?我个人的理解是:为了灵活性。你想想看,一个蓝牙芯片可能用在耳机、手机、汽车上,它们的上层应用千差万别。如果所有逻辑都揉在一起,那每次改个功能都得动底层硬件,多麻烦。

我在项目中遇到过一种情况:某款芯片的Controller是现成的,但Host需要我们自己写。这时候分层的好处就体现出来了——我们只需要按照HCI接口的规范来写,底层怎么跑根本不用管。

关键点:Host和Controller之间通过HCI(Host Controller Interface)通信。HCI就是它们之间的“翻译官”。

2.2 HCI接口详解:通信的桥梁

HCI,全称Host Controller Interface。它定义了Host和Controller之间怎么说话。嗯,这里要注意,HCI既可以是物理接口(比如UART、USB、SDIO),也可以是逻辑接口(比如在同一个芯片内部通过内存共享)。

HCI的报文类型主要有三种:

报文类型 方向 作用
Command Host → Controller Host下发指令,比如“扫描设备”、“建立连接”
Event Controller → Host Controller上报事件,比如“扫描到设备”、“连接断开”
Data 双向 传输实际的数据包,比如音频流、文件

我曾经踩过一个坑:在调试UART接口的HCI时,发现数据总是丢包。查了半天,原来是波特率设置不对,导致Controller发送Event时Host来不及接收。后来我加了个流控,问题就解决了。所以,HCI的物理层稳定性非常重要,尤其是用UART的时候。

小技巧:调试HCI时,可以用逻辑分析仪抓一下UART的波形,看看有没有丢字节或者乱序。我习惯在Host端加一个HCI日志打印,把每个Command和Event都记录下来,这样排查问题会快很多。

2.3 L2CAP:数据的分包与重组

L2CAP,全称Logical Link Control and Adaptation Protocol。它的名字挺长,但功能很简单:把上层的数据切成小块,交给Controller发送;收到数据后,再拼回去。

为什么要切?因为Controller一次能发送的数据包大小有限。比如BLE的LL层,一个数据包最多只能传27个字节(后来扩展到251个字节)。如果上层要发一个1KB的文件,L2CAP就得把它切成几十个小包。

L2CAP还负责多路复用。什么意思呢?就是多个上层协议(比如SDP、GATT、RFCOMM)可以共用同一条L2CAP通道。L2CAP通过Channel ID(CID)来区分不同的数据流。

我记得有一次,客户反馈说蓝牙传输大文件时总是失败。我一看日志,发现L2CAP的MTU(最大传输单元)协商出了问题。Host和Controller的MTU不一致,导致数据包被截断。后来我强制把MTU设成一致,问题就解决了。

注意:L2CAP的MTU协商一定要做对。如果Host说我能收1000字节,但Controller只能发500字节,那就会出问题。我建议在初始化阶段,双方都明确声明自己的MTU能力。

2.4 SDP:服务发现协议

SDP,全称Service Discovery Protocol。它的作用就是让蓝牙设备互相知道对方能干什么

比如你的手机连上一个蓝牙耳机,手机怎么知道这个耳机支持A2DP(音频播放)还是HFP(通话)?就是通过SDP来查询的。

SDP的工作流程很简单:

  1. 设备A发起SDP查询,问“你有什么服务?”
  2. 设备B返回一个服务列表,里面包含服务UUID、属性等信息。
  3. 设备A根据这些信息,决定要不要连接。

我在项目中遇到过一种情况:某款耳机连上手机后,手机死活不显示“媒体音频”选项。查了半天,发现是耳机的SDP记录里漏了A2DP服务的UUID。加上之后,一切正常。所以,SDP记录一定要写全,少一个UUID,上层应用就可能认不出来。

关键点:SDP只负责“发现”,不负责“连接”。它告诉你对方有什么服务,但具体怎么用,那是上层协议的事。

2.5 GATT:属性协议

GATT,全称Generic Attribute Profile。它是BLE(低功耗蓝牙)的核心协议。说白了,GATT定义了一种客户端-服务器的通信模型。

在GATT中,数据被组织成属性(Attribute)。每个属性都有一个UUID、一组权限、以及一个值。比如一个温度传感器,它的温度值就是一个属性,UUID是0x2A6E,权限是“只读”。

GATT的通信模式有两种:

  • 通知(Notification):服务器主动给客户端发数据,客户端不需要确认。适合实时性要求高的场景,比如心率监测。
  • 指示(Indication):服务器给客户端发数据,客户端必须确认。适合可靠性要求高的场景,比如固件升级。

我记得有一次做智能手环项目,心率数据总是丢包。后来发现是用了Notification,但客户端处理不过来。改成Indication之后,虽然速度慢了点,但数据一个都不丢。所以,选Notification还是Indication,要看你的业务场景

小技巧:调试GATT时,可以用手机上的nRF Connect或者LightBlue工具,直接查看设备的属性列表。这样能快速确认你的GATT服务是不是按预期暴露的。

2.6 总结一下

好了,咱们把蓝牙协议栈的核心分层捋了一遍:

  • Host与Controller:分工明确,通过HCI通信。
  • HCI:通信的桥梁,注意物理层稳定性。
  • L2CAP:数据的分包与重组,MTU协商是关键。
  • SDP:服务发现,记录要写全。
  • GATT:属性协议,选对通信模式。

说实话,这些协议看起来多,但每个都有自己的职责。你只要理解了它们各自是干什么的,整个协议栈的架构就清晰了。下一章,咱们会深入讲讲HCI的驱动实现,到时候会涉及具体的代码。嗯,敬请期待。