嵌入式产品里蓝牙常和 WiFi 共用一颗 combo 芯片。现象却很碎:能扫到手机、连不上;A2DP 有声无控;BLE 广播正常但 GATT 读失败。根因往往不在「蓝牙协议本身」,而在 Linux BlueZ 分层 :硬件/固件 → HCI 驱动 → 内核协议(L2CAP/RFCOMM/SCO)→ 用户态 bluetoothd / D-Bus。
本文把 嵌入式 Linux 蓝牙栈 从芯片到应用串清楚,覆盖经典蓝牙与 BLE、HCI 命令事件、套接字类型、mgmt、音频与排障,读完能对照 btmon 与内核路径定位问题落在哪一层。
源码锚点
| 路径 | 作用 |
|---|---|
include/net/bluetooth/hci_core.h |
struct hci_dev、hci_alloc_dev、hci_register_dev |
include/net/bluetooth/hci.h |
HCI 命令/事件、quirk |
include/net/bluetooth/bluetooth.h |
bt_sock、协议注册 |
include/net/bluetooth/l2cap.h |
L2CAP 信道 |
include/net/bluetooth/rfcomm.h |
串口仿真 RFCOMM |
include/net/bluetooth/sco.h |
同步语音链路 |
include/net/bluetooth/mgmt.h |
用户态管理接口(mgmt) |
net/bluetooth/ |
BlueZ 内核:hci_core、l2cap、smp 等 |
include/linux/rfkill.h |
射频开关(与 WiFi 共用) |
控制器注册(概念):
c
/* include/net/bluetooth/hci_core.h */
struct hci_dev *hci_alloc_dev(void);
int hci_register_dev(struct hci_dev *hdev);
驱动在 probe 里分配 hci_dev,填 open/close/send 等总线回调,设必要 quirk (hci.h),再 hci_register_dev。之后内核出现 hci0。
分层总览:别把 HCI 和「蓝牙应用」混为一谈
#mermaid-svg-2jahqZryaOf8u6II{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-2jahqZryaOf8u6II .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2jahqZryaOf8u6II .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2jahqZryaOf8u6II .error-icon{fill:#552222;}#mermaid-svg-2jahqZryaOf8u6II .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2jahqZryaOf8u6II .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2jahqZryaOf8u6II .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2jahqZryaOf8u6II .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2jahqZryaOf8u6II .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2jahqZryaOf8u6II .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2jahqZryaOf8u6II .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2jahqZryaOf8u6II .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2jahqZryaOf8u6II .marker.cross{stroke:#333333;}#mermaid-svg-2jahqZryaOf8u6II svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2jahqZryaOf8u6II p{margin:0;}#mermaid-svg-2jahqZryaOf8u6II .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-2jahqZryaOf8u6II .cluster-label text{fill:#333;}#mermaid-svg-2jahqZryaOf8u6II .cluster-label span{color:#333;}#mermaid-svg-2jahqZryaOf8u6II .cluster-label span p{background-color:transparent;}#mermaid-svg-2jahqZryaOf8u6II .label text,#mermaid-svg-2jahqZryaOf8u6II span{fill:#333;color:#333;}#mermaid-svg-2jahqZryaOf8u6II .node rect,#mermaid-svg-2jahqZryaOf8u6II .node circle,#mermaid-svg-2jahqZryaOf8u6II .node ellipse,#mermaid-svg-2jahqZryaOf8u6II .node polygon,#mermaid-svg-2jahqZryaOf8u6II .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2jahqZryaOf8u6II .rough-node .label text,#mermaid-svg-2jahqZryaOf8u6II .node .label text,#mermaid-svg-2jahqZryaOf8u6II .image-shape .label,#mermaid-svg-2jahqZryaOf8u6II .icon-shape .label{text-anchor:middle;}#mermaid-svg-2jahqZryaOf8u6II .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-2jahqZryaOf8u6II .rough-node .label,#mermaid-svg-2jahqZryaOf8u6II .node .label,#mermaid-svg-2jahqZryaOf8u6II .image-shape .label,#mermaid-svg-2jahqZryaOf8u6II .icon-shape .label{text-align:center;}#mermaid-svg-2jahqZryaOf8u6II .node.clickable{cursor:pointer;}#mermaid-svg-2jahqZryaOf8u6II .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-2jahqZryaOf8u6II .arrowheadPath{fill:#333333;}#mermaid-svg-2jahqZryaOf8u6II .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2jahqZryaOf8u6II .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2jahqZryaOf8u6II .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2jahqZryaOf8u6II .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-2jahqZryaOf8u6II .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2jahqZryaOf8u6II .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-2jahqZryaOf8u6II .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2jahqZryaOf8u6II .cluster text{fill:#333;}#mermaid-svg-2jahqZryaOf8u6II .cluster span{color:#333;}#mermaid-svg-2jahqZryaOf8u6II div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-2jahqZryaOf8u6II .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-2jahqZryaOf8u6II rect.text{fill:none;stroke-width:0;}#mermaid-svg-2jahqZryaOf8u6II .icon-shape,#mermaid-svg-2jahqZryaOf8u6II .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2jahqZryaOf8u6II .icon-shape p,#mermaid-svg-2jahqZryaOf8u6II .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-2jahqZryaOf8u6II .icon-shape .label rect,#mermaid-svg-2jahqZryaOf8u6II .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2jahqZryaOf8u6II .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-2jahqZryaOf8u6II .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-2jahqZryaOf8u6II :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 硬件侧
内核 BlueZ
用户态
bluetoothd / BlueZ
btmgmt / bluetoothctl
应用: GATT / A2DP / SPP
PulseAudio / PipeWire 音频
mgmt / HCI socket
HCI 核心 hci_core
L2CAP / SMP
RFCOMM / SCO / BNEP
HCI 驱动 USB/UART/SDIO
固件
蓝牙控制器芯片
HCI(Host Controller Interface) :主机(Linux)与控制器(芯片+固件)之间的标准命令/事件/ACL/SCO 数据通道。
Linux 侧「蓝牙驱动」多数是 把某总线收发适配成 HCI,真正 802.15 风格跳频在控制器固件里。
调用链
控制器上线
#mermaid-svg-RCWaPIq6uIvgpTqi{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-RCWaPIq6uIvgpTqi .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RCWaPIq6uIvgpTqi .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RCWaPIq6uIvgpTqi .error-icon{fill:#552222;}#mermaid-svg-RCWaPIq6uIvgpTqi .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RCWaPIq6uIvgpTqi .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RCWaPIq6uIvgpTqi .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RCWaPIq6uIvgpTqi .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RCWaPIq6uIvgpTqi .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RCWaPIq6uIvgpTqi .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RCWaPIq6uIvgpTqi .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RCWaPIq6uIvgpTqi .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RCWaPIq6uIvgpTqi .marker.cross{stroke:#333333;}#mermaid-svg-RCWaPIq6uIvgpTqi svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RCWaPIq6uIvgpTqi p{margin:0;}#mermaid-svg-RCWaPIq6uIvgpTqi .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-RCWaPIq6uIvgpTqi .cluster-label text{fill:#333;}#mermaid-svg-RCWaPIq6uIvgpTqi .cluster-label span{color:#333;}#mermaid-svg-RCWaPIq6uIvgpTqi .cluster-label span p{background-color:transparent;}#mermaid-svg-RCWaPIq6uIvgpTqi .label text,#mermaid-svg-RCWaPIq6uIvgpTqi span{fill:#333;color:#333;}#mermaid-svg-RCWaPIq6uIvgpTqi .node rect,#mermaid-svg-RCWaPIq6uIvgpTqi .node circle,#mermaid-svg-RCWaPIq6uIvgpTqi .node ellipse,#mermaid-svg-RCWaPIq6uIvgpTqi .node polygon,#mermaid-svg-RCWaPIq6uIvgpTqi .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RCWaPIq6uIvgpTqi .rough-node .label text,#mermaid-svg-RCWaPIq6uIvgpTqi .node .label text,#mermaid-svg-RCWaPIq6uIvgpTqi .image-shape .label,#mermaid-svg-RCWaPIq6uIvgpTqi .icon-shape .label{text-anchor:middle;}#mermaid-svg-RCWaPIq6uIvgpTqi .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-RCWaPIq6uIvgpTqi .rough-node .label,#mermaid-svg-RCWaPIq6uIvgpTqi .node .label,#mermaid-svg-RCWaPIq6uIvgpTqi .image-shape .label,#mermaid-svg-RCWaPIq6uIvgpTqi .icon-shape .label{text-align:center;}#mermaid-svg-RCWaPIq6uIvgpTqi .node.clickable{cursor:pointer;}#mermaid-svg-RCWaPIq6uIvgpTqi .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-RCWaPIq6uIvgpTqi .arrowheadPath{fill:#333333;}#mermaid-svg-RCWaPIq6uIvgpTqi .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-RCWaPIq6uIvgpTqi .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-RCWaPIq6uIvgpTqi .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RCWaPIq6uIvgpTqi .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-RCWaPIq6uIvgpTqi .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RCWaPIq6uIvgpTqi .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-RCWaPIq6uIvgpTqi .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-RCWaPIq6uIvgpTqi .cluster text{fill:#333;}#mermaid-svg-RCWaPIq6uIvgpTqi .cluster span{color:#333;}#mermaid-svg-RCWaPIq6uIvgpTqi div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-RCWaPIq6uIvgpTqi .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-RCWaPIq6uIvgpTqi rect.text{fill:none;stroke-width:0;}#mermaid-svg-RCWaPIq6uIvgpTqi .icon-shape,#mermaid-svg-RCWaPIq6uIvgpTqi .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RCWaPIq6uIvgpTqi .icon-shape p,#mermaid-svg-RCWaPIq6uIvgpTqi .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-RCWaPIq6uIvgpTqi .icon-shape .label rect,#mermaid-svg-RCWaPIq6uIvgpTqi .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RCWaPIq6uIvgpTqi .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-RCWaPIq6uIvgpTqi .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-RCWaPIq6uIvgpTqi :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 总线 probe: USB/UART/SDIO
hci_alloc_dev
设置 bus 回调 open/close/send
设置 quirk 如需
hci_register_dev
创建 hciX / 初始化命令
bluetoothd 通过 mgmt 管理
经典连接(示意:SPP / RFCOMM)
text
应用 connect RFCOMM
→ 内核 RFCOMM → L2CAP → HCI ACL
→ 驱动 send → 控制器
反向:控制器事件/ACL → hci_recv_frame → 协议栈 → socket
BLE GATT(示意)
text
用户态/bluetoothd
→ ATT over L2CAP (LE credit / fixed CID)
→ HCI LE ACL
→ 控制器链路层
安全:SMP 配对密钥;内核 smp 与用户态策略协作
重点知识
1. hci_dev:控制器在内核的实体
struct hci_dev(hci_core.h)保存:
- 名称、总线类型、厂商特征
- 命令队列、连接哈希、统计
hci_dev_stats - 回调:向控制器发帧、打开关闭传输
用户态看到的 hci0 对应一个已注册的 hci_dev。
hciconfig(旧)/ btmgmt info / bluetoothctl list 都围绕它。
2. HCI 三种流量
| 类型 | 作用 |
|---|---|
| Command / Event | 主机下命令、控制器回事件(复位、查特性、建链) |
| ACL | 异步数据(L2CAP 承载) |
| SCO / eSCO | 同步语音(通话、部分 headset) |
调试神器:btmon 抓 HCI 层,相当于蓝牙的 tcpdump。关联失败先看是命令超时、认证失败还是 ACL 断开。
3. 传输驱动(嵌入式最常见)
| 总线 | 典型场景 | 注意 |
|---|---|---|
| USB | USB 蓝牙 dongle、部分模组 | 电流、固件、reset |
| UART / H4 / H5 / BCSP | 模组接 SoC UART | 波特率、流控、串口 DMA |
| SDIO | WiFi/BT combo | 与 WiFi 共存、共享晶振/天线、固件组合 |
UART 蓝牙经典坑:波特率与固件约定不一致 → hci_register 后初始化命令超时。
Combo 芯片:WiFi 驱动与 BT 驱动加载顺序、共享管脚、rfkill 要一起看。
4. 协议栈:L2CAP 以上
#mermaid-svg-GyQDHk6QBo6Xv1Mq{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-GyQDHk6QBo6Xv1Mq .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .error-icon{fill:#552222;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .marker.cross{stroke:#333333;}#mermaid-svg-GyQDHk6QBo6Xv1Mq svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-GyQDHk6QBo6Xv1Mq p{margin:0;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .cluster-label text{fill:#333;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .cluster-label span{color:#333;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .cluster-label span p{background-color:transparent;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .label text,#mermaid-svg-GyQDHk6QBo6Xv1Mq span{fill:#333;color:#333;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .node rect,#mermaid-svg-GyQDHk6QBo6Xv1Mq .node circle,#mermaid-svg-GyQDHk6QBo6Xv1Mq .node ellipse,#mermaid-svg-GyQDHk6QBo6Xv1Mq .node polygon,#mermaid-svg-GyQDHk6QBo6Xv1Mq .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .rough-node .label text,#mermaid-svg-GyQDHk6QBo6Xv1Mq .node .label text,#mermaid-svg-GyQDHk6QBo6Xv1Mq .image-shape .label,#mermaid-svg-GyQDHk6QBo6Xv1Mq .icon-shape .label{text-anchor:middle;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .rough-node .label,#mermaid-svg-GyQDHk6QBo6Xv1Mq .node .label,#mermaid-svg-GyQDHk6QBo6Xv1Mq .image-shape .label,#mermaid-svg-GyQDHk6QBo6Xv1Mq .icon-shape .label{text-align:center;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .node.clickable{cursor:pointer;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .arrowheadPath{fill:#333333;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-GyQDHk6QBo6Xv1Mq .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GyQDHk6QBo6Xv1Mq .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-GyQDHk6QBo6Xv1Mq .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .cluster text{fill:#333;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .cluster span{color:#333;}#mermaid-svg-GyQDHk6QBo6Xv1Mq div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-GyQDHk6QBo6Xv1Mq rect.text{fill:none;stroke-width:0;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .icon-shape,#mermaid-svg-GyQDHk6QBo6Xv1Mq .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .icon-shape p,#mermaid-svg-GyQDHk6QBo6Xv1Mq .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .icon-shape .label rect,#mermaid-svg-GyQDHk6QBo6Xv1Mq .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GyQDHk6QBo6Xv1Mq .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-GyQDHk6QBo6Xv1Mq .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-GyQDHk6QBo6Xv1Mq :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} HCI
L2CAP
ATT/GATT BLE
RFCOMM
BNEP 个人网
SMP 配对
串口应用
SCO
语音
- L2CAP:信道复用、分片重组;BLE 上承载 ATT。
- RFCOMM:仿串口,Android SPP、很多工控透传。
- BNEP:蓝牙个人区域网(较少用于现代手机互联)。
- SCO :语音;音频音乐更多走 A2DP(用户态配置 + 内核支持)。
- SMP:LE 安全配对。
套接字:AF_BLUETOOTH + BTPROTO_L2CAP / RFCOMM / HCI / SCO 等(见 bluetooth.h 注册路径)。
5. 用户态 BlueZ
现代发行版:
bluetoothd:守护进程,经 D-Bus 暴露适配器/设备/GATT API。bluetoothctl:交互式控制。btmgmt:更贴近内核 mgmt 命令。
嵌入式裁剪根文件系统时缺 bluetoothd 或 D-Bus 策略,会出现「内核有 hci0,用户态扫不到」。
6. 经典蓝牙 vs BLE
| 经典 BR/EDR | BLE | |
|---|---|---|
| 功耗 | 较高 | 低 |
| 典型 | 音频、SPP、HID 旧设备 | 传感器、Beacon、GATT |
| 主机关注 | RFCOMM/A2DP/SCO | ATT/GATT、广播、连接间隔 |
同一控制器可双模;能力由 HCI Read Local Supported Features / LE features 事件报告。
7. 配对、绑定与权限
配对产生密钥,绑定后免重复输入。失败常见:
- 双方 IO 能力不匹配(NoInputNoOutput vs DisplayYesNo)
- Secure Connections / MITM 策略过严
- 旧设备不支持 LE Secure Connections
内核 SMP + 用户态 agent(bluetoothctl agent)共同完成。产线常用「预绑定」或固定 PIN(安全性差,仅工控)。
8. 音频路径(嵌入式易踩坑)
- HFP/HSP:通话,偏 SCO。
- A2DP:音乐;编解码 SBC/AAC/aptX 取决于用户态与对端。
- 与 PulseAudio / PipeWire / ALSA 集成;内核只提供链路,策略与编解码在用户态。
只有 hci0 没有音频框架插件 → 「能连耳机无声音」。
9. rfkill 与共存
rfkill list 中 Bluetooth 被 soft/hard block 会导致适配器 down。
WiFi/BT 共存:天线隔离、固件共存算法、同模组 SDIO 时序;WiFi 高吞吐时 BT 音频卡顿不一定是 BlueZ bug。
10. 调试与观测
bash
dmesg | grep -iE 'bluetooth|hci|firmware|tty.*bt'
hciconfig -a 2>/dev/null; btmgmt info
rfkill list
bluetoothctl show; bluetoothctl devices
# 抓 HCI(需权限)
btmon
# 内核配置:CONFIG_BT、CONFIG_BT_HCIUART、CONFIG_BT_HCIBTUSB 等
| 现象 | 方向 |
|---|---|
| 无 hci0 | 总线 probe、固件、UART 波特率、设备树 |
| hci0 在但 up 失败 | rfkill、初始化命令超时、quirk |
| 扫不到设备 | 天线、区域、扫描参数、被 block |
| 能配对不能连配置文件 | SDP/GATT 用户态、权限、UUID |
| BLE 连接秒断 | 连接间隔、监督超时、供电、干扰 |
| 音频无声 | Pulse/PipeWire、编解码、profile |
Checklist
- 能区分:HCI 驱动、内核协议栈、bluetoothd 用户态三层职责
- 知道
hci_alloc_dev→hci_register_dev后才有hci0 - 会用
btmon看命令超时还是链路断开 - 清楚 RFCOMM/L2CAP/SCO 各自典型用途
- UART 蓝牙查过波特率与固件;combo 查过与 WiFi 共存/rfkill
- 根文件系统确认 D-Bus + bluetoothd(若用 BlueZ 标准栈)
- 音频问题先查用户态 profile,再查 SCO/A2DP 链路
成稿:
articles/csdn-merged/嵌入式Linux-蓝牙框架完整篇.md