【BlueZ 】BLE 与经典蓝牙的差异:BlueZ 中双模式的源码区分设计

蓝牙技术发展至今,已形成经典蓝牙(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_LESCAN_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 的统一管理:

  1. 模式控制层:通过 bt_mode_t 枚举和 MGMT_OP_SET_BREDR/LE 操作控制协议栈启用

  2. 状态隔离层:每个设备维护独立的 bredr_state 和 le_state

  3. 协议分支层:服务发现(SDP/GATT)、L2CAP PSM、连接参数等差异化处理

  4. 扫描统一层:通过 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 | 解析模式配置 |


相关推荐
小雪崩2 小时前
嵌入式学习 day30:minilog
linux·c语言·学习
嵌入式小能手2 小时前
飞凌嵌入式ElfBoard-Shell脚本编写基础-传参-$0
linux
丰锋ff2 小时前
初识Linux操作系统
linux·运维·服务器
嵌入式小能手2 小时前
飞凌嵌入式ElfBoard-Shell脚本编写基础-特殊变量
linux·运维·服务器
吹什么轩4 小时前
Linux系统复习:权限的解析
linux·运维·服务器
liebe1*14 小时前
Module 2:Linux Fundamentals Part 1
linux·运维·服务器
imaol14 小时前
进程 ---(多任务并行)
linux·运维·服务器
mengge.cloud4 小时前
0824LAMP项目实战:部署WordPress博客平台小白教程
android·linux·运维·服务器·学习·nginx
zone_z5 小时前
07 · 等待事件与 OWI 方法:从 OSW 到 BUG 的完整破案实录
linux·数据库·oracle·ffmpeg·bug·troubleshooting