蓝牙技术发展至今,已形成经典蓝牙(BR/EDR)和低功耗蓝牙(BLE)两大核心分支。经典蓝牙侧重高带宽数据传输,BLE 侧重超低功耗和快速连接。BlueZ 作为 Linux 官方蓝牙协议栈,通过精妙的双模式兼容架构,实现了两种协议的统一管理与差异化处理。
目录
[四、功耗管理差异:从 Sniff 到睡眠模式](#四、功耗管理差异:从 Sniff 到睡眠模式)
[五、拓扑结构差异:从 piconet 到 mesh](#五、拓扑结构差异:从 piconet 到 mesh)
[六、BlueZ 双模式兼容架构深度解析](#六、BlueZ 双模式兼容架构深度解析)
本文基于 BlueZ 5.x 完整源码,从协议架构、广播机制、连接模型、功耗、拓扑、协议栈分支六个维度,深度剖析 BR/EDR 与 BLE 的核心差异,并拆解 BlueZ 双模式兼容设计的源码实现。
一、协议架构差异:从物理层到应用层
1.1 核心架构对比
|--------|-------------------------------------------------|-----------------------------------|
| 维度 | BR / EDR | BLE |
| 物理层调制 | GFSK (1Mbps), π/4-DQPSK (2Mbps), 8-DPSK (3Mbps) | GFSK (1Mbps) |
| 跳频信道数 | 79个信道 | 40个信道 (3个广播, 37个数据) |
| 最大数据速率 | 3Mbps | 1Mbps (BLE 4.0), 2Mbps (BLE 4.2+) |
| 主要用途 | 音频传输、文件传输、HID | IoT 传感器、可穿戴设备、信标 |
1.2 BlueZ 中的架构抽象
BlueZ 通过统一的 HCI 层向上提供服务,但在 L2CAP 及以上层实现了明显的分支隔离:
cpp
// src/shared/att.c - ATT 层只服务于 BLE
#define ATT_MIN_PDU_LEN 1
#define ATT_TIMEOUT_INTERVAL 30000 /* BLE 特有超时 */
struct bt_att_chan {
struct bt_att *att;
int fd;
struct io *io;
uint8_t type;
int sec_level;
uint16_t mtu; /* LE MTU 协商 */
};
关键设计:ATT/GATT 是 BLE 独有的协议层,经典蓝牙使用 SDP 进行服务发现,而 BLE 使用 GATT。
二、广播机制差异:从查询扫描到扩展广播
2.1 BR / EDR 扫描机制
经典蓝牙使用 Inquiry Scan 机制:
cpp
// src/adapter.c - BR/EDR 扫描参数配置
struct btd_br_defaults {
uint16_t page_scan_type; /* 分页扫描类型 */
uint16_t page_scan_interval; /* 分页扫描间隔 */
uint16_t page_scan_win; /* 分页扫描窗口 */
uint16_t scan_type; /* 查询扫描类型 */
uint16_t scan_interval; /* 查询扫描间隔 */
uint16_t scan_win; /* 查询扫描窗口 */
uint16_t link_supervision_timeout; /* 链路监督超时 */
};
2.2 BLE 广播机制
BLE 广播更为复杂,支持多种广播类型:
cpp
// src/advertising.c - BLE 广播管理器
#define LE_ADVERTISING_MGR_IFACE "org.bluez.LEAdvertisingManager1"
#define LE_ADVERTISEMENT_IFACE "org.bluez.LEAdvertisement1"
#define AD_TYPE_BROADCAST 0 /* 不可连接广播 (Beacon) */
#define AD_TYPE_PERIPHERAL 1 /* 可连接广播 */
struct btd_adv_manager {
struct btd_adapter *adapter;
struct queue *clients;
uint8_t max_adv_len; /* 最大广播长度 */
uint8_t max_scan_rsp_len; /* 最大扫描响应长度 */
uint8_t max_ads; /* 最大广播实例数 */
int8_t min_tx_power;
int8_t max_tx_power;
};
2.3 BlueZ 扫描类型分支
cpp
// src/adapter.c - 扫描类型宏定义
#define SCAN_TYPE_BREDR (1 << BDADDR_BREDR)
#define SCAN_TYPE_LE ((1 << BDADDR_LE_PUBLIC) | (1 << BDADDR_LE_RANDOM))
#define SCAN_TYPE_DUAL (SCAN_TYPE_BREDR | SCAN_TYPE_LE)
static uint8_t get_scan_type(struct btd_adapter *adapter)
{
uint8_t type;
// 根据当前设置判断是否支持 BR/EDR
if (adapter->current_settings & MGMT_SETTING_BREDR)
type = SCAN_TYPE_BREDR;
else
type = 0;
// 根据当前设置判断是否支持 LE
if (adapter->current_settings & MGMT_SETTING_LE)
type |= SCAN_TYPE_LE;
return type;
}
源码 解读:get_scan_type() 通过检查 MGMT_SETTING_BREDR 和 MGMT_SETTING_LE标志位,动态决定扫描类型。双模式适配器同时设置两个标志位。
三、连接模型差异:从主从到角色互换
3.1 BR / EDR 连接模型
经典蓝牙采用固定的 Master-Slave 模型:
cpp
// src/adapter.c - BR/EDR 连接建立
cp.addr.type = BDADDR_BREDR;
if (dst_type == BDADDR_BREDR) {
bt_io_connect(io, connect_cb, data, NULL,
BT_IO_OPT_SOURCE_TYPE, BDADDR_BREDR,
BT_IO_OPT_DEST_TYPE, BDADDR_BREDR,
BT_IO_OPT_DEST_BDADDR, &dst,
BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
BT_IO_OPT_INVALID);
}
3.2 BLE 连接模型
BLE 采用 Central-Peripheral 模型,支持角色互换:
cpp
// src/device.c - BLE 连接建立后绑定 ATT
if (data->dst_type != BDADDR_BREDR){
g_io_channel_set_close_on_unref(io, FALSE);
device_attach_att(device, io); /* BLE 特有:绑定 ATT 通道 */
}
device_discover_services(device);
关键差异:BLE 连接建立后立即绑定 ATT 通道,经典蓝牙则需要先进行 SDP 服务发现。
3.3 连接参数差异
cpp
// src/btd.h - BLE 连接参数
struct btd_le_defaults {
uint16_t min_conn_interval; /* 最小连接间隔 (7.5ms - 4s) */
uint16_t max_conn_interval; /* 最大连接间隔 */
uint16_t conn_latency; /* 连接延迟 */
uint16_t conn_lsto; /* 连接监督超时 */
uint16_t autoconnect_timeout; /* 自动连接超时 */
};
四、功耗管理差异:从 Sniff 到睡眠模式
4.1 BR/EDR 功耗管理
经典蓝牙使用 Sniff Mode 降低功耗:
cpp
// src/btd.h - BR/EDR Sniff 参数
struct btd_br_defaults {
uint16_t min_sniff_interval; /* 最小 Sniff 间隔 */
uint16_t max_sniff_interval; /* 最大 Sniff 间隔 */
};
4.2 BLE 功耗管理
BLE 设计之初就以低功耗为核心目标,支持多种省电模式:
cpp
// src/btd.h - BLE 扫描参数(影响功耗)
struct btd_le_defaults {
uint16_t scan_interval_autoconnect; /* 自动连接扫描间隔 */
uint16_t scan_win_autoconnect; /* 自动连接扫描窗口 */
uint16_t scan_interval_suspend; /* 挂起扫描间隔 */
uint16_t scan_win_suspend; /* 挂起扫描窗口 */
uint16_t scan_interval_discovery; /* 发现扫描间隔 */
uint16_t scan_win_discovery; /* 发现扫描窗口 */
};
功耗对比:
|--------|----------|----------|----------|
| 模式 | 待机功耗 | 连接功耗 | 广播功耗 |
| BR/EDR | ~10mA | ~30mA | ~40mA |
| BLE | ~0.5μA | ~1-2mA | ~1mA |
五、拓扑结构差异:从 piconet 到 mesh
5.1 BR/EDR 拓扑
经典蓝牙使用 Piconet 结构,一个主设备最多连接 7 个从设备:
cpp
// src/adapter.c - BR/EDR 连接限制
#define MAX_CONNECTIONS 7 /* 经典蓝牙最大连接数 */
5.2 BLE 拓扑
BLE 支持更灵活的拓扑结构:
-
Point-to-Point: 一对一连接
-
Broadcast: 广播模式(Beacon)
-
Mesh: BLE Mesh 网络
cpp
// src/advertising.c - BLE 广播类型区分
#define AD_TYPE_BROADCAST 0 /* 广播模式,不可连接 */
#define AD_TYPE_PERIPHERAL 1 /* 外设模式,可连接 */
六、BlueZ 双模式兼容架构深度解析
6.1 模式初始化分支
cpp
// src/adapter.c - 双模式初始化核心逻辑
switch (btd_opts.mode) {
case BT_MODE_DUAL:
// 双模式:同时启用 BR/EDR 和 LE
if (missing_settings & MGMT_SETTING_SSP)
set_mode(adapter, MGMT_OP_SET_SSP, 0x01);
if (missing_settings & MGMT_SETTING_LE)
set_mode(adapter, MGMT_OP_SET_LE, 0x01);
if (missing_settings & MGMT_SETTING_BREDR)
set_mode(adapter, MGMT_OP_SET_BREDR, 0x01);
break;
case BT_MODE_BREDR:
// 仅 BR/EDR:禁用 LE
if (!(adapter->supported_settings & MGMT_SETTING_BREDR)) {
btd_error(adapter->dev_id, "Ignoring adapter without BR/EDR support");
goto failed;
}
if (missing_settings & MGMT_SETTING_BREDR)
set_mode(adapter, MGMT_OP_SET_BREDR, 0x01);
if (adapter->current_settings & MGMT_SETTING_LE)
set_mode(adapter, MGMT_OP_SET_LE, 0x00); /* 禁用 LE */
break;
case BT_MODE_LE:
// 仅 LE:禁用 BR/EDR
if (!(adapter->supported_settings & MGMT_SETTING_LE)) {
btd_error(adapter->dev_id, "Ignoring adapter without LE support");
goto failed;
}
if (missing_settings & MGMT_SETTING_LE)
set_mode(adapter, MGMT_OP_SET_LE, 0x01);
if (adapter->current_settings & MGMT_SETTING_BREDR)
set_mode(adapter, MGMT_OP_SET_BREDR, 0x00); /* 禁用 BR/EDR */
break;
}
设计要点:BlueZ 通过 bt_mode_t 枚举控制适配器模式,初始化时根据配置启用或禁用特定协议栈。
6.2 设备状态双轨管理
cpp
// src/device.c - 设备状态管理
struct bearer_state {
bool connected;
bool paired;
bool bonded;
bool connecting;
bool disconnecting;
bool pairing;
};
struct btd_device {
struct btd_adapter *adapter;
char *path;
bdaddr_t bdaddr;
uint8_t bdaddr_type;
bool bredr; /* 是否支持 BR/EDR */
bool le; /* 是否支持 LE */
struct bearer_state bredr_state; /* BR/EDR 状态 */
struct bearer_state le_state; /* LE 状态 */
// ...
};
static struct bearer_state *get_state(struct btd_device *dev,
uint8_t bdaddr_type)
{
if (bdaddr_type == BDADDR_BREDR)
return &dev->bredr_state; /* 返回 BR/EDR 状态 */
else
return &dev->le_state; /* 返回 LE 状态 */
}
核心设计:每个设备维护独立的 BR/EDR 和 LE 状态,支持双模式设备同时建立两种连接。
6.3 服务发现分支
cpp
// src/device.c - 服务发现分支逻辑
resolve_services:
DBG("Resolving services for %s", dev->path);
// 根据地址类型选择服务发现方式
if (bdaddr_type == BDADDR_BREDR)
err = device_browse_sdp(dev, msg); /* BR/EDR 使用 SDP */
else
err = device_browse_gatt(dev, msg); /* LE 使用 GATT */
if (err < 0) {
return btd_error_failed(msg, bdaddr_type == BDADDR_BREDR ?
ERR_BREDR_CONN_SDP_SEARCH : ERR_LE_CONN_GATT_BROWSE);
}
源码解读:服务发现是双模式差异最大的环节之一。BR/EDR 使用传统的 SDP 协议,BLE 使用 GATT 协议进行属性发现。
6.4 扫描发现分支
cpp
// src/adapter.c - 扫描设备处理
if (bdaddr_type != BDADDR_BREDR) {
// BLE 设备特殊处理
if (!btd_device_is_connected(dev) &&
g_slist_find(adapter->connect_list, dev)) {
adapter->connect_le = dev;
stop_passive_scanning(adapter); /* 停止扫描以便连接 */
}
}
七、协议栈分支:代码层面的隔离设计
7.1 协议栈架构图

7.2 L2CAP 层差异
cpp
// lib/l2cap.h - L2CAP PSM 定义
#define L2CAP_PSM_SDP 0x0001 /* BR/EDR SDP */
#define L2CAP_PSM_RFCOMM 0x0003 /* BR/EDR RFCOMM */
#define L2CAP_PSM_TCS_BIN 0x0005 /* BR/EDR TCS */
#define L2CAP_PSM_TCS_BIN_CORDLESS 0x0007
#define L2CAP_PSM_BNEP 0x000F /* BR/EDR BNEP */
#define L2CAP_PSM_HIDP 0x0011 /* BR/EDR HID */
// BLE 使用动态 PSM (0x0040-0xFFFF)
#define L2CAP_PSM_LE_FIRST 0x0040
#define L2CAP_PSM_LE_LAST 0xFFFF
7.3 连接参数配置分支
cpp
// src/main.c - 模式配置解析
static int get_mode(const char *str)
{
if (strcmp(str, "dual") == 0)
return BT_MODE_DUAL;
else if (strcmp(str, "bredr") == 0)
return BT_MODE_BREDR;
else if (strcmp(str, "le") == 0)
return BT_MODE_LE;
error("Unknown controller mode \"%s\"", str);
return BT_MODE_DUAL;
}
// src/main.c - 模式依赖检查
if (btd_opts.mode == BT_MODE_LE) {
// LE 模式:禁用 BR/EDR 相关功能
/* ... */
}
if (btd_opts.mode != BT_MODE_LE) {
// 非 LE 模式:启用 BR/EDR 相关功能
/* ... */
}
八、双模式开发适配要点
8.1 地址类型处理
cpp
// 正确处理不同地址类型
switch (bdaddr_type) {
case BDADDR_BREDR:
// 经典蓝牙地址处理
break;
case BDADDR_LE_PUBLIC:
// BLE 公共地址处理
break;
case BDADDR_LE_RANDOM:
// BLE 随机地址处理
// 注意:随机地址可能是静态的、私有可解析的或私有不可解析的
break;
}
8.2 连接回调区分
cpp
// 连接回调中区分 BR/EDR 和 LE
static void connect_cb(GIOChannel *io, GError *err, gpointer data)
{
struct connect_data *conn_data = data;
if (conn_data->dst_type == BDADDR_BREDR) {
// BR/EDR 连接处理
device_browse_sdp(device, NULL);
} else {
// LE 连接处理
device_attach_att(device, io);
device_browse_gatt(device, NULL);
}
}
8.3 服务发现策略
cpp
// 根据设备类型选择服务发现方式
if (device->bredr) {
// 经典蓝牙设备:使用 SDP
device_browse_sdp(device, msg);
}
if (device->le) {
// BLE 设备:使用 GATT
device_browse_gatt(device, msg);
}
九、踩坑经验总结
9.1 常见问题与解决方案
|---------------|------------|--------------------------------------|
| 问题 | 原因 | 解决方案 |
| 双模式设备无法同时连接 | 适配器模式配置错误 | 确保 btd_opts.mode = BT_MODE_DUAL |
| BLE 连接后无法发现服务 | 未绑定 ATT 通道 | 调用 device_attach_att() |
| 扫描不到 BLE 设备 | 扫描类型未包含 LE | 设置 SCAN_TYPE_LE 或 SCAN_TYPE_DUAL |
| BR/EDR 连接超时 | SDP 查询耗时过长 | 优化 SDP 查询参数 |
| 功耗过高 | BLE 扫描间隔过短 | 调整 scan_interval_discovery |
9.2 调试技巧
cpp
# 查看适配器支持的模式
bluetoothctl show
# 设置适配器模式
bluetoothctl set-adapter-property Mode dual
bluetoothctl set-adapter-property Mode bredr
bluetoothctl set-adapter-property Mode le
# 查看设备信息(包含地址类型)
bluetoothctl info <device>
# 启用调试日志
bluetoothd -d
9.3 配置文件示例
cpp
# /etc/bluetooth/main.conf
[General]
Mode = dual # 双模式
Privacy = device
Name = MyBluetoothDevice
[BR/EDR]
PageScanType = 0
PageScanInterval = 10
PageScanWindow = 10
[LE]
MinInterval = 20
MaxInterval = 40
Latency = 0
Timeout = 400
十、总结
BlueZ 双模式兼容架构通过以下核心设计实现了 BR/EDR 与 BLE 的统一管理:
模式控制层:通过 bt_mode_t 枚举和 MGMT_OP_SET_BREDR/LE 操作控制协议栈启用
状态隔离层:每个设备维护独立的 bredr_state 和 le_state
协议分支层:服务发现(SDP/GATT)、L2CAP PSM、连接参数等差异化处理
扫描统一层:通过 SCAN_TYPE_DUAL 实现双模式同时扫描
理解这些设计不仅有助于深入掌握 BlueZ 源码,更能指导实际开发中正确处理双模式设备的适配问题。在嵌入式场景中,根据实际需求选择合适的模式(BT_MODE_DUAL/BT_MODE_BREDR/BT_MODE_LE),可以有效优化系统资源占用和功耗表现。
附录:核心函数索引
|------------------------|---------------|---------------|
| 函数 | 文件 | 功能 |
| get_scan_type() | src/adapter.c | 根据适配器设置返回扫描类型 |
| get_state() | src/device.c | 根据地址类型返回设备状态 |
| device_browse_sdp() | src/device.c | BR/EDR 服务发现 |
| device_browse_gatt() | src/device.c | BLE 服务发现 |
| device_attach_att() | src/device.c | 绑定 BLE ATT 通道 |
| set_mode() | src/adapter.c | 设置适配器模式 |
| get_mode() | src/main.c | 解析模式配置 |