第三章:Linux内核蓝牙子系统:BlueZ架构概览

好,我们正式开始接触BlueZ。说实话,很多做蓝牙开发的工程师,一听到「内核蓝牙子系统」就觉得头大。我当年刚接触时也一样,面对一堆源码文件,完全不知道从哪下手。但后来我发现,只要抓住几个核心模块,整个架构其实很清晰。

3.1 BlueZ的整体架构

BlueZ是Linux官方标准的蓝牙协议栈。它分两层:内核空间和用户空间。内核空间负责底层硬件交互和协议处理,用户空间负责配置管理和上层应用。

我个人习惯把BlueZ比作一个快递系统:

  • 硬件驱动 = 快递员,负责收发包裹
  • HCI层 = 分拣中心,统一管理数据流
  • L2CAP层 = 快递单号,区分不同业务
  • 用户空间工具 = 客服系统,让你查物流、改地址

嗯,这个比喻虽然简单,但能帮你快速建立整体认知。

3.2 内核蓝牙模块:bluetooth.ko

bluetooth.ko是蓝牙子系统的核心模块。它不直接操作硬件,而是提供一套框架。所有蓝牙驱动都基于这个框架来工作。

我建议你记住这个模块的几个关键职责:

  • 管理蓝牙控制器(HCI设备)的注册与注销
  • 提供HCI命令、事件、数据的传输通道
  • 实现L2CAP、SCO等核心协议
  • 维护连接状态和链路信息

加载这个模块很简单:

sudo modprobe bluetooth

但要注意,很多嵌入式板子默认没开蓝牙内核支持。我曾经在一个项目里折腾了半天,最后发现是内核配置里没勾选CONFIG_BT。嗯,这种低级错误,犯过一次就记住了。

避坑指南: 我曾经在调试时发现bluetooth.ko加载失败,dmesg里报"Unknown symbol"。后来查出来是内核版本不匹配,驱动模块和内核源码版本不一致。记住,模块必须与当前内核版本对应

3.3 关键数据结构之一:hci_dev

hci_dev是整个蓝牙控制器的抽象。每个蓝牙硬件(无论是USB、UART还是SDIO接口)在内核里都对应一个hci_dev实例。

它的定义在include/net/bluetooth/hci_core.h里。我挑几个最重要的字段说说:

字段名 类型 作用
name char[8] 设备名,比如hci0、hci1
bus int 总线类型:USB、UART、SDIO等
dev_type int 设备类型:BR/EDR还是LE
hdev_list list_head 全局链表,把所有hci_dev串起来
conn_hash struct hci_conn_hash 管理所有连接

你想想看,一个蓝牙芯片插到系统里,内核怎么知道它?其实就是通过hci_dev。驱动初始化时,会调用hci_alloc_dev()分配一个实例,然后填充字段,最后hci_register_dev()注册进去。

我见过不少新手在写驱动时,忘了设置bus字段,结果设备注册成功了但上层协议栈不认。这种小细节,往往最坑人。

3.4 关键数据结构之二:hci_conn

hci_conn代表一条蓝牙连接。无论是ACL链路还是SCO/eSCO链路,都用这个结构体表示。

它的核心字段包括:

  • hdev:指向所属的hci_dev
  • dst:对端蓝牙地址
  • type:连接类型(ACL/SCO/LE)
  • state:连接状态(OPEN/CLOSED等)
  • link_mode:链路模式(加密、认证等标志)

为什么需要这个结构体?说白了,蓝牙通信是面向连接的。你要传数据,必须先建立连接。hci_conn就是这条连接的「身份证」。

我记得有一次调试蓝牙耳机,音频断断续续。查了半天,发现是SCO连接的state在频繁切换。嗯,后来定位到是射频干扰导致链路不稳定。这种问题,光看应用层日志根本看不出来,必须深入内核数据结构。

调试技巧: 你可以通过/sys/kernel/debug/bluetooth/hci0/conn_info查看当前所有hci_conn的状态。我经常用这个文件来快速判断连接是否正常。

3.5 关键数据结构之三:l2cap_chan

L2CAP层是蓝牙协议栈的「交通枢纽」。l2cap_chan就是L2CAP层的一个通道实例。

每个L2CAP通道对应一个逻辑连接。比如A2DP音频流、HID鼠标事件、ATT属性协议,都是通过不同的l2cap_chan来传输的。

它的关键字段:

  • conn:指向所属的hci_conn
  • psm:协议服务多路复用器,用来区分上层协议
  • cid:通道标识符,本地唯一
  • state:通道状态(OPEN/WAIT等)
  • ops:操作函数集,包括recv、send等回调

你可能会问:为什么已经有了hci_conn,还要l2cap_chan?

原因很简单:一条物理连接上可以跑多个逻辑通道。比如你的手机同时连蓝牙耳机听歌、连手表收通知,它们共用一条ACL链路,但通过不同的l2cap_chan来区分数据。

我建议你记住这个层次关系:

hci_dev (控制器)
  └── hci_conn (物理连接)
        └── l2cap_chan (逻辑通道)

这个三层结构,是理解蓝牙内核源码的基石。

3.6 三个结构体的协作关系

光知道每个结构体长什么样还不够,你得明白它们怎么配合工作。

我画个简单的数据流:

  1. 蓝牙硬件收到数据包,驱动通过hci_dev上报
  2. 内核根据包类型,找到对应的hci_conn
  3. 再根据L2CAP头部信息,路由到正确的l2cap_chan
  4. 最后通过chan的ops回调,交给上层协议处理

反过来,发送数据时:

  1. 上层协议调用l2cap_chan的send接口
  2. l2cap_chan封装L2CAP头部,交给hci_conn
  3. hci_conn封装HCI头部,通过hci_dev发送
  4. 驱动把数据写到硬件

你看,整个流程非常清晰。每个结构体各司其职,环环相扣。

核心要点:

  • hci_dev = 控制器抽象,一个蓝牙芯片一个实例
  • hci_conn = 物理连接,一条链路一个实例
  • l2cap_chan = 逻辑通道,一个服务一个实例
  • 三者通过指针互相引用,形成完整的数据通路

好了,这一章的内容就到这里。下一章我们会深入HCI层,看看数据包到底是怎么在内核里流转的。到时候我会拿一个实际的抓包例子来讲解,保证让你印象深刻。