【BlueZ 】netlink 在 BlueZ 中的应用:用户态与内核态的配置消息传递

Linux 内核与用户态的通信机制中,Netlink 是最经典的异步消息传递方案之一。它以套接字为载体,支持多播、请求-响应、事件通知等多种交互模式。BlueZ 作为 Linux 蓝牙协议栈,虽然直接使用 PF_BLUETOOTH 协议族的 HCI Socket 实现 MGMT (Management) 接口,但其设计思想完全借鉴了 Netlink 的精髓:统一的消息头、异步的命令/事件模型、可扩展的 TLV 编解码。本文基于 BlueZ 5.87 源码与 Linux 内核蓝牙子系统,全程源码落地,深度拆解 MGMT 接口(类 Netlink 设计)在 BlueZ 中的完整实现。


目录

[一、Netlink 通信机制原理](#一、Netlink 通信机制原理)

[二、MGMT 接口架构与核心结构体](#二、MGMT 接口架构与核心结构体)

[三、MGMT 套接字创建与初始化](#三、MGMT 套接字创建与初始化)

[四、MGMT 消息发送流程](#四、MGMT 消息发送流程)

[五、MGMT 消息接收流程](#五、MGMT 消息接收流程)

[六、MGMT 事件注册与注销](#六、MGMT 事件注册与注销)

七、业务层使用示例

[八、MGMT vs HCI Socket vs D-Bus 对比](#八、MGMT vs HCI Socket vs D-Bus 对比)

九、开发适配要点与调试技巧

十、核心函数速查表

十一、总结


1.1 Netlink 基础概念

Netlink 是 Linux 内核提供的一种套接字通信机制,用于内核与用户态进程之间的异步消息传递。其核心特点:

1.2 Netlink 消息格式

1.3 BlueZ 对 Netlink 设计的借鉴

BlueZ 并未直接使用 AF_NETLINK 协议族,而是使用了 PF_BLUETOOTH 协议族下的 HCI_CHANNEL_CONTROL (3) 通道来实现 MGMT 接口。其设计完全借鉴了 Netlink 的核心思想:

二、MGMT 接口架构与核心结构体

2.1 MGMT 接口整体架构

2.2 核心数据结构

mgmt 主结构

cpp 复制代码
// src/shared/mgmt.c --- 核心管理结构

struct mgmt {
    int ref_count;              // 引用计数
    int fd;                     // HCI Socket 文件描述符
    bool close_on_unref;        // 引用归零时是否关闭 fd
    struct io *io;              // IO 封装(封装 fd 操作)
    bool writer_active;         // 写操作是否活跃
    
    // 三个队列
    struct queue *request_queue;   // 待发送请求队列
    struct queue *reply_queue;     // 待发送回复队列(优先级高)
    struct queue *pending_list;    // 已发送等待响应的请求列表
    struct queue *notify_list;     // 事件通知注册列表
    
    unsigned int next_request_id;  // 下一个请求 ID
    unsigned int next_notify_id;   // 下一个通知 ID
    
    // 通知处理状态
    bool need_notify_cleanup;      // 是否需要清理已移除的通知
    bool in_notify;               // 是否正在处理通知
    
    // 缓冲区
    void *buf;                    // 接收缓冲区
    uint16_t len;                 // 缓冲区长度
    uint16_t mtu;                 // 最大传输单元
    
    // 调试回调
    mgmt_debug_func_t debug_callback;
    mgmt_destroy_func_t debug_destroy;
    void *debug_data;
};

mgmt_request 请求结构

cpp 复制代码
// src/shared/mgmt.c --- 请求结构

struct mgmt_request {
    struct mgmt *mgmt;          // 所属 mgmt 实例
    unsigned int id;            // 请求 ID(用于匹配响应)
    uint16_t opcode;            // 操作码(如 MGMT_OP_SET_POWERED)
    uint16_t index;             // 适配器索引(如 hci0=0)
    void *buf;                  // 请求数据缓冲区
    uint16_t len;               // 数据长度
    mgmt_request_func_t callback;  // 响应回调函数
    mgmt_destroy_func_t destroy;   // 用户数据销毁函数
    void *user_data;            // 用户数据
    int timeout;                // 超时时间(秒)
    unsigned int timeout_id;    // 超时定时器 ID
};

mgmt_notify 通知结构

cpp 复制代码
// src/shared/mgmt.c --- 通知结构

struct mgmt_notify {
    unsigned int id;            // 通知 ID
    uint16_t event;             // 事件码(如 MGMT_EV_INDEX_ADDED)
    uint16_t index;             // 适配器索引(MGMT_INDEX_NONE=0xFFFF 表示所有)
    bool removed;               // 是否已标记移除
    mgmt_notify_func_t callback;  // 事件回调函数
    mgmt_destroy_func_t destroy;   // 用户数据销毁函数
    void *user_data;            // 用户数据
};

mgmt_hdr 消息头

cpp 复制代码
// lib/mgmt.h --- 消息头定义

#define MGMT_INDEX_NONE     0xFFFF

struct mgmt_hdr {
    uint16_t opcode;    // 操作码(命令码或事件码)
    uint16_t index;     // 适配器索引
    uint16_t len;       // 数据负载长度
} __packed;

#define MGMT_HDR_SIZE       6

mgmt_tlv TLV 编解码结构

cpp 复制代码
// lib/mgmt.h --- TLV 结构(借鉴 Netlink NLA 设计)

struct mgmt_tlv {
    uint16_t type;      // 类型
    uint8_t  length;    // 数据长度
    uint8_t  value[];   // 数据内容(柔性数组)
} __packed;

2.3 MGMT 命令与事件体系

核心命令码(部分)

cpp 复制代码
// lib/mgmt.h --- 命令码定义

#define MGMT_OP_READ_VERSION        0x0001  // 读取 MGMT 版本
#define MGMT_OP_READ_COMMANDS       0x0002  // 读取支持的命令
#define MGMT_OP_READ_INDEX_LIST     0x0003  // 读取适配器列表
#define MGMT_OP_READ_INFO           0x0004  // 读取适配器信息
#define MGMT_OP_SET_POWERED         0x0005  // 设置电源状态
#define MGMT_OP_SET_DISCOVERABLE    0x0006  // 设置可发现状态
#define MGMT_OP_SET_CONNECTABLE     0x0007  // 设置可连接状态
#define MGMT_OP_SET_BONDABLE        0x0009  // 设置可配对状态
#define MGMT_OP_SET_LOCAL_NAME      0x000F  // 设置本地名称
#define MGMT_OP_ADD_UUID            0x0010  // 添加 UUID
#define MGMT_OP_REMOVE_UUID         0x0011  // 移除 UUID
#define MGMT_OP_START_DISCOVERY     0x0023  // 开始发现
#define MGMT_OP_STOP_DISCOVERY      0x0024  // 停止发现
#define MGMT_OP_ADD_ADVERTISING     0x003E  // 添加广播
#define MGMT_OP_REMOVE_ADVERTISING  0x003F  // 移除广播
#define MGMT_OP_READ_EXT_INDEX_LIST 0x003C  // 读取扩展适配器列表

核心事件码(部分)

cpp 复制代码
// lib/mgmt.h --- 事件码定义

#define MGMT_EV_CMD_COMPLETE            0x0001  // 命令完成
#define MGMT_EV_CMD_STATUS              0x0002  // 命令状态
#define MGMT_EV_CONTROLLER_ERROR        0x0003  // 控制器错误
#define MGMT_EV_INDEX_ADDED             0x0004  // 适配器添加
#define MGMT_EV_INDEX_REMOVED           0x0005  // 适配器移除
#define MGMT_EV_NEW_SETTINGS            0x0006  // 新设置
#define MGMT_EV_DEVICE_CONNECTED        0x000B  // 设备已连接
#define MGMT_EV_DEVICE_DISCONNECTED     0x000C  // 设备已断开
#define MGMT_EV_DEVICE_FOUND            0x0012  // 设备发现
#define MGMT_EV_DISCOVERING             0x0013  // 发现状态变更
#define MGMT_EV_ADVERTISING_ADDED      0x0023  // 广播已添加
#define MGMT_EV_ADVERTISING_REMOVED    0x0024  // 广播已移除

三、MGMT 套接字创建与初始化

3.1 mgmt_new_default() --- 默认套接字创建

cpp 复制代码
// src/shared/mgmt.c --- 创建默认 MGMT 连接

struct mgmt *mgmt_new_default(void)
{
    struct mgmt *mgmt;
    union {
        struct sockaddr common;
        struct sockaddr_hci hci;   // HCI 专用地址结构
    } addr;
    int fd;

    // 1. 创建 HCI Socket
    //    PF_BLUETOOTH: 蓝牙协议族
    //    SOCK_RAW: 原始套接字(直接访问 HCI 层)
    //    BTPROTO_HCI: HCI 协议
    fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                BTPROTO_HCI);
    if (fd < 0)
        return NULL;

    // 2. 绑定地址
    memset(&addr, 0, sizeof(addr));
    addr.hci.hci_family = AF_BLUETOOTH;
    addr.hci.hci_dev = HCI_DEV_NONE;           // 不指定具体设备
    addr.hci.hci_channel = HCI_CHANNEL_CONTROL; // 关键!使用控制通道

    // 3. 绑定到控制通道
    if (bind(fd, &addr.common, sizeof(addr.hci)) < 0) {
        close(fd);
        return NULL;
    }

    // 4. 创建 mgmt 实例
    mgmt = mgmt_new(fd);
    if (!mgmt) {
        close(fd);
        return NULL;
    }

    // 5. 设置关闭标志
    mgmt->close_on_unref = true;

    return mgmt;
}

关键点解析:

|-------------------------------------|-----------------|---------------------------------------------|
| 步骤 | 操作 | 说明 |
| socket() | 创建原始 HCI Socket | 类似 Netlink 的 socket(AF_NETLINK, SOCK_RAW) |
| hci_channel = HCI_CHANNEL_CONTROL | 指定控制通道 | 关键:这是 MGMT 接口的专属通道 |
| bind() | 绑定到内核 HCI Core | 类似 Netlink 的 bind() 绑定到内核 |

3.2 mgmt_new() --- 实例初始化

cpp 复制代码
// src/shared/mgmt.c --- mgmt 实例创建

struct mgmt *mgmt_new(int fd)
{
    struct mgmt *mgmt;

    if (fd < 0)
        return NULL;

    // 1. 分配并清零结构
    mgmt = new0(struct mgmt, 1);
    mgmt->fd = fd;
    mgmt->close_on_unref = false;

    // 2. 分配接收缓冲区
    mgmt->len = 512;
    mgmt->buf = malloc(mgmt->len);
    if (!mgmt->buf) {
        free(mgmt);
        return NULL;
    }

    // 3. 创建 IO 封装
    //    io_new() 封装 fd,提供统一的 read/write 接口
    mgmt->io = io_new(fd);
    if (!mgmt->io) {
        free(mgmt->buf);
        free(mgmt);
        return NULL;
    }

    // 4. 创建队列
    mgmt->request_queue = queue_new();   // 请求队列
    mgmt->reply_queue = queue_new();     // 回复队列(优先级高)
    mgmt->pending_list = queue_new();    // 等待响应列表
    mgmt->notify_list = queue_new();     // 通知列表

    // 5. 注册读回调
    //    can_read_data() 处理内核返回的消息
    if (!io_set_read_handler(mgmt->io, can_read_data, mgmt, NULL)) {
        // 失败清理
        queue_destroy(mgmt->notify_list, NULL);
        queue_destroy(mgmt->pending_list, NULL);
        queue_destroy(mgmt->reply_queue, NULL);
        queue_destroy(mgmt->request_queue, NULL);
        io_destroy(mgmt->io);
        free(mgmt->buf);
        free(mgmt);
        return NULL;
    }

    mgmt->writer_active = false;

    // 6. 设置 MTU
    mgmt_set_mtu(mgmt);

    return mgmt_ref(mgmt);
}

3.3 mgmt_set_mtu() --- MTU 设置

cpp 复制代码
// src/shared/mgmt.c --- 动态调整 MTU

static void mgmt_set_mtu(struct mgmt *mgmt)
{
    socklen_t len = 0;

    // 1. 尝试获取当前 MTU
    //    SOL_BLUETOOTH / BT_SNDMTU 是蓝牙特有的 socket 选项
    if (getsockopt(mgmt->fd, SOL_BLUETOOTH, BT_SNDMTU, 
                &mgmt->mtu, &len) < 0) {
        // 2. 如果不支持,使用默认 HCI ACL 大小
        mgmt->mtu = HCI_MAX_ACL_SIZE;  // 通常为 1024 字节
        return;
    }

    // 3. 尝试增大 MTU(部分命令可能超过 1024 字节)
    if (mgmt->mtu < UINT16_MAX) {
        uint16_t mtu = UINT16_MAX;

        if (!setsockopt(mgmt->fd, SOL_BLUETOOTH, BT_SNDMTU, 
                    &mtu, sizeof(mtu)))
            mgmt->mtu = mtu;
    }
}

四、MGMT 消息发送流程

4.1 请求发送流程

cpp 复制代码
用户态调用 mgmt_send()
        │
        ▼
mgmt_send_timeout() --- 添加超时支持
        │
        ▼
create_request() --- 构造请求包
        │
        ▼
queue_push_tail(request_queue) --- 加入发送队列
        │
        ▼
wakeup_writer() --- 唤醒写操作
        │
        ▼
can_write_data() --- 从队列取请求
        │
        ▼
send_request() --- 发送到内核
        │
        ▼
io_send() → write(fd, ...) --- 实际写入
        │
        ▼
内核 HCI Core 接收处理

4.2 create_request() --- 请求构造

cpp 复制代码
// src/shared/mgmt.c --- 构造请求包

static struct mgmt_request *create_request(struct mgmt *mgmt, uint16_t opcode,
                uint16_t index, uint16_t length,
                const void *param, mgmt_request_func_t callback,
                void *user_data, mgmt_destroy_func_t destroy,
                int timeout)
{
    struct mgmt_request *request;
    struct mgmt_hdr *hdr;

    // 1. 参数校验
    if (!opcode)
        return NULL;

    if (length > 0 && !param)
        return NULL;

    // 2. 检查 MTU 限制
    if (length > mgmt->mtu) {
        printf("length %u > %u mgmt->mtu", length, mgmt->mtu);
        return NULL;
    }

    // 3. 分配请求结构
    request = new0(struct mgmt_request, 1);
    request->len = length + MGMT_HDR_SIZE;  // 数据长度 + 头部长度
    request->buf = malloc(request->len);
    if (!request->buf) {
        free(request);
        return NULL;
    }

    // 4. 拷贝参数数据
    if (length > 0)
        memcpy(request->buf + MGMT_HDR_SIZE, param, length);

    // 5. 构造消息头(小端序转换)
    hdr = request->buf;
    hdr->opcode = htobs(opcode);    // 操作码
    hdr->index = htobs(index);      // 适配器索引
    hdr->len = htobs(length);      // 数据长度

    // 6. 设置请求属性
    request->mgmt = mgmt;
    request->opcode = opcode;
    request->index = index;
    request->callback = callback;
    request->destroy = destroy;
    request->user_data = user_data;
    request->timeout = timeout;

    return request;
}

4.3 mgmt_send_timeout() --- 带超时发送

cpp 复制代码
// src/shared/mgmt.c --- 带超时的请求发送

unsigned int mgmt_send_timeout(struct mgmt *mgmt, uint16_t opcode,
                uint16_t index, uint16_t length,
                const void *param, mgmt_request_func_t callback,
                void *user_data, mgmt_destroy_func_t destroy,
                int timeout)
{
    struct mgmt_request *request;

    if (!mgmt)
        return 0;

    // 1. 构造请求
    request = create_request(mgmt, opcode, index, length, param,
                    callback, user_data, destroy, timeout);
    if (!request)
        return 0;

    // 2. 分配唯一请求 ID
    if (mgmt->next_request_id < 1)
        mgmt->next_request_id = 1;

    request->id = mgmt->next_request_id++;

    // 3. 加入发送队列
    if (!queue_push_tail(mgmt->request_queue, request)) {
        free(request->buf);
        free(request);
        return 0;
    }

    // 4. 唤醒写操作
    wakeup_writer(mgmt);

    return request->id;
}

4.4 send_request() --- 实际发送

cpp 复制代码
// src/shared/mgmt.c --- 发送请求到内核

static bool send_request(struct mgmt *mgmt, struct mgmt_request *request)
{
    struct iovec iov;
    ssize_t ret;

    // 1. 构造 IO 向量
    iov.iov_base = request->buf;
    iov.iov_len = request->len;

    // 2. 调用 IO 层发送(实际调用 write(fd, ...))
    ret = io_send(mgmt->io, &iov, 1);
    if (ret < 0) {
        util_debug(mgmt->debug_callback, mgmt->debug_data,
                "write failed: %s", strerror(-ret));
        // 3. 发送失败,回调通知
        if (request->callback)
            request->callback(MGMT_STATUS_FAILED, 0, NULL,
                            request->user_data);
        destroy_request(request);
        return false;
    }

    // 4. 设置超时定时器
    if (request->timeout)
        request->timeout_id = timeout_add_seconds(request->timeout,
                            request_timeout,
                            request,
                            NULL);

    // 5. 调试输出
    util_debug(mgmt->debug_callback, mgmt->debug_data,
            "[0x%04x] command 0x%04x",
            request->index, request->opcode);

    util_hexdump('<', request->buf, ret, mgmt->debug_callback,
                        mgmt->debug_data);

    // 6. 加入等待响应列表
    queue_push_tail(mgmt->pending_list, request);

    return true;
}

4.5 can_write_data() --- 写操作调度

cpp 复制代码
// src/shared/mgmt.c --- 写操作回调(由 IO 层触发)

static bool can_write_data(struct io *io, void *user_data)
{
    struct mgmt *mgmt = user_data;
    struct mgmt_request *request;
    bool can_write;

    // 1. 优先处理回复队列(高优先级)
    request = queue_pop_head(mgmt->reply_queue);
    if (!request) {
        // 回复队列为空,检查请求队列
        if (!queue_isempty(mgmt->pending_list))
            return false;  // 还有未完成的请求,不能发送新请求

        request = queue_pop_head(mgmt->request_queue);
        if (!request)
            return false;  // 队列为空,不需要写

        can_write = false;
    } else {
        // 2. 回复可以排队发送(多个回复)
        can_write = !queue_isempty(mgmt->reply_queue);
    }

    // 3. 发送请求
    if (!send_request(mgmt, request))
        return true;  // 发送失败,请求已被销毁

    return can_write;  // 是否还有更多数据待发送
}

队列优先级设计:

五、MGMT 消息接收流程

5.1 can_read_data() --- 消息接收入口

cpp 复制代码
// src/shared/mgmt.c --- 消息接收处理

static bool can_read_data(struct io *io, void *user_data)
{
    struct mgmt *mgmt = user_data;
    struct mgmt_hdr *hdr;
    struct mgmt_ev_cmd_complete *cc;
    struct mgmt_ev_cmd_status *cs;
    ssize_t bytes_read;
    uint16_t opcode, event, index, length;

    // 1. 从 Socket 读取数据
    bytes_read = read(mgmt->fd, mgmt->buf, mgmt->len);
    if (bytes_read < 0)
        return false;  // 读取失败,停止接收

    // 2. 调试输出
    util_hexdump('>', mgmt->buf, bytes_read,
                mgmt->debug_callback, mgmt->debug_data);

    // 3. 检查最小长度(MGMT_HDR_SIZE = 6)
    if (bytes_read < MGMT_HDR_SIZE)
        return true;  // 数据不完整,继续读取

    // 4. 解析消息头
    hdr = mgmt->buf;
    event = btohs(hdr->opcode);   // 注意:这里读的是事件/命令码
    index = btohs(hdr->index);
    length = btohs(hdr->len);

    // 5. 检查数据完整性
    if (bytes_read < length + MGMT_HDR_SIZE)
        return true;  // 数据不完整,继续读取

    // 6. 增加引用计数(防止处理中被释放)
    mgmt_ref(mgmt);

    // 7. 根据事件类型分发处理
    switch (event) {
    case MGMT_EV_CMD_COMPLETE:
        // 命令完成事件
        cc = mgmt->buf + MGMT_HDR_SIZE;
        opcode = btohs(cc->opcode);

        util_debug(mgmt->debug_callback, mgmt->debug_data,
                "[0x%04x] command 0x%04x complete: 0x%02x",
                        index, opcode, cc->status);

        // 7a. 完成对应的请求
        request_complete(mgmt, cc->status, opcode, index, 
                        length - 3, mgmt->buf + MGMT_HDR_SIZE + 3);
        break;

    case MGMT_EV_CMD_STATUS:
        // 命令状态事件(提前通知)
        cs = mgmt->buf + MGMT_HDR_SIZE;
        opcode = btohs(cs->opcode);

        util_debug(mgmt->debug_callback, mgmt->debug_data,
                "[0x%04x] command 0x%02x status: 0x%02x",
                        index, opcode, cs->status);

        // 7b. 完成请求(状态类型,无数据)
        request_complete(mgmt, cs->status, opcode, index, 0, NULL);
        break;

    default:
        // 其他事件(通知类事件)
        util_debug(mgmt->debug_callback, mgmt->debug_data,
                "[0x%04x] event 0x%04x", index, event);

        // 7c. 分发给注册的通知回调
        process_notify(mgmt, event, index, length,
                        mgmt->buf + MGMT_HDR_SIZE);
        break;
    }

    // 8. 减少引用计数
    mgmt_unref(mgmt);

    return true;  // 继续读取
}

5.2 request_complete() --- 请求完成处理

cpp 复制代码
// src/shared/mgmt.c --- 请求完成

static void request_complete(struct mgmt *mgmt, uint8_t status,
                uint16_t opcode, uint16_t index,
                uint16_t length, const void *param)
{
    struct opcode_index match = { .opcode = opcode, .index = index };
    struct mgmt_request *request;

    // 1. 在等待列表中查找对应的请求
    //    优先通过 opcode + index 精确匹配
    request = queue_remove_if(mgmt->pending_list,
                    match_request_opcode_index, &match);
    if (!request) {
        util_debug(mgmt->debug_callback, mgmt->debug_data,
                "Unable to find request for opcode 0x%04x",
                opcode);

        // 2. 如果没找到,尝试仅通过 index 匹配
        request = queue_remove_if(mgmt->pending_list,
                        match_request_index,
                        UINT_TO_PTR(index));
    }

    // 3. 如果找到请求,调用回调
    if (request) {
        if (request->callback)
            request->callback(status, length, param,
                            request->user_data);

        destroy_request(request);  // 销毁请求
    }

    // 4. 唤醒写操作(可能有排队的请求需要发送)
    wakeup_writer(mgmt);
}

5.3 process_notify() --- 事件通知分发

cpp 复制代码
// src/shared/mgmt.c --- 事件通知处理

static void process_notify(struct mgmt *mgmt, uint16_t event, uint16_t index,
                uint16_t length, const void *param)
{
    struct event_index match = { .event = event, .index = index,
                    .length = length, .param = param };

    // 1. 标记正在处理通知
    mgmt->in_notify = true;

    // 2. 遍历所有注册的通知,匹配并调用
    queue_foreach(mgmt->notify_list, notify_handler, &match);

    // 3. 标记处理完成
    mgmt->in_notify = false;

    // 4. 清理已标记移除的通知(在通知处理中调用了 unregister)
    if (mgmt->need_notify_cleanup) {
        queue_remove_all(mgmt->notify_list, match_notify_removed,
                            NULL, destroy_notify);
        mgmt->need_notify_cleanup = false;
    }
}

5.4 notify_handler() --- 单个通知匹配

cpp 复制代码
// src/shared/mgmt.c --- 通知匹配与回调

static void notify_handler(void *data, void *user_data)
{
    struct mgmt_notify *notify = data;
    struct event_index *match = user_data;

    // 1. 检查是否已标记移除
    if (notify->removed)
        return;

    // 2. 检查事件码是否匹配
    if (notify->event != match->event)
        return;

    // 3. 检查索引是否匹配(MGMT_INDEX_NONE 表示匹配所有)
    if (notify->index != match->index && notify->index != MGMT_INDEX_NONE)
        return;

    // 4. 调用通知回调
    if (notify->callback)
        notify->callback(match->index, match->length, match->param,
                            notify->user_data);
}

六、MGMT 事件注册与注销

6.1 mgmt_register() --- 注册事件监听

cpp 复制代码
// src/shared/mgmt.c --- 注册事件回调

unsigned int mgmt_register(struct mgmt *mgmt, uint16_t event, uint16_t index,
                mgmt_notify_func_t callback,
                void *user_data, mgmt_destroy_func_t destroy)
{
    struct mgmt_notify *notify;

    if (!mgmt || !event)
        return 0;

    // 1. 创建通知结构
    notify = new0(struct mgmt_notify, 1);
    notify->event = event;      // 事件码(如 MGMT_EV_INDEX_ADDED)
    notify->index = index;      // 适配器索引(MGMT_INDEX_NONE=0xFFFF 表示所有)
    notify->callback = callback;
    notify->destroy = destroy;
    notify->user_data = user_data;

    // 2. 分配唯一 ID
    if (mgmt->next_notify_id < 1)
        mgmt->next_notify_id = 1;

    notify->id = mgmt->next_notify_id++;

    // 3. 加入通知列表
    if (!queue_push_tail(mgmt->notify_list, notify)) {
        free(notify);
        return 0;
    }

    return notify->id;
}

6.2 mgmt_unregister() --- 注销事件监听

cpp 复制代码
// src/shared/mgmt.c --- 注销事件回调

bool mgmt_unregister(struct mgmt *mgmt, unsigned int id)
{
    struct mgmt_notify *notify;

    if (!mgmt || !id)
        return false;

    // 1. 尝试从列表移除
    notify = queue_remove_if(mgmt->notify_list, match_notify_id,
                            UINT_TO_PTR(id));
    if (!notify)
        return false;

    // 2. 如果不在通知处理中,直接销毁
    if (!mgmt->in_notify) {
        destroy_notify(notify);
        return true;
    }

    // 3. 如果正在处理通知,标记移除(稍后清理)
    notify->removed = true;
    mgmt->need_notify_cleanup = true;

    return true;
}

6.3 通知线程安全设计

七、业务层使用示例

7.1 适配器电源管理

cpp 复制代码
// src/adapter.c --- 适配器类实现示例

// 电源开关
static int powered_up(struct btd_adapter *adapter)
{
    struct mgmt_cp_set_powered cp;

    cp.val = 0x01;  // 开启电源

    // 发送 MGMT_OP_SET_POWERED 命令
    if (mgmt_send(adapter->mgmt, MGMT_OP_SET_POWERED,
                adapter->dev_id, sizeof(cp), &cp,
                set_powered_complete, adapter, NULL) > 0)
        return 0;

    return -EIO;
}

// 电源命令完成回调
static void set_powered_complete(uint8_t status, uint16_t length,
                    const void *param, void *user_data)
{
    struct btd_adapter *adapter = user_data;

    if (status != MGMT_STATUS_SUCCESS) {
        btd_error(adapter->dev_id, "Set Powered failed: %s", 
                mgmt_errstr(status));
        return;
    }

    // 电源设置成功,启动后续初始化
    adapter->powered = true;
    // ... 继续初始化流程
}

7.2 设备发现流程

cpp 复制代码
// src/adapter.c --- 设备发现

static gboolean start_discovery(struct btd_adapter *adapter)
{
    struct mgmt_cp_start_discovery cp;

    cp.type = SCAN_TYPE_LE;  // LE 扫描

    // 1. 注册设备发现事件
    adapter->found_id = mgmt_register(adapter->mgmt,
                    MGMT_EV_DEVICE_FOUND, adapter->dev_id,
                    device_found_event, adapter, NULL);

    // 2. 发送开始发现命令
    if (mgmt_send(adapter->mgmt, MGMT_OP_START_DISCOVERY,
                adapter->dev_id, sizeof(cp), &cp,
                start_discovery_complete, adapter, NULL) > 0)
        return TRUE;

    return FALSE;
}

// 设备发现事件回调
static void device_found_event(uint16_t index, uint16_t length,
                    const void *param, void *user_data)
{
    const struct mgmt_ev_device_found *ev = param;
    struct btd_adapter *adapter = user_data;

    // 处理发现的设备
    // ev->addr: 设备地址
    // ev->rssi: 信号强度
    // ev->flags: 设备标志
    // ev->eir: 扩展信息(广播数据)
    
    // ... 将设备信息上报给 BlueZ 框架
    adapter->on_device_found(ev);
}

7.3 适配器事件监听

cpp 复制代码
// src/adapter.c --- 适配器初始化时注册事件

static int adapter_init(struct btd_adapter *adapter)
{
    // 1. 创建 MGMT 连接
    adapter->mgmt = mgmt_new_default();
    if (!adapter->mgmt)
        return -EIO;

    // 2. 注册索引添加事件(所有适配器)
    adapter->idx_added_id = mgmt_register(adapter->mgmt,
                    MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE,
                    index_added_event, NULL, NULL);

    // 3. 注册索引移除事件
    adapter->idx_removed_id = mgmt_register(adapter->mgmt,
                    MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE,
                    index_removed_event, NULL, NULL);

    // 4. 注册设备连接事件
    adapter->connected_id = mgmt_register(adapter->mgmt,
                    MGMT_EV_DEVICE_CONNECTED, adapter->dev_id,
                    device_connected_event, adapter, NULL);

    // 5. 注册设备断开事件
    adapter->disconnected_id = mgmt_register(adapter->mgmt,
                    MGMT_EV_DEVICE_DISCONNECTED, adapter->dev_id,
                    device_disconnected_event, adapter, NULL);

    return 0;
}

八、MGMT vs HCI Socket vs D-Bus 对比

8.1 三种通信机制对比表

|-----------|---------------------|-----------------|--------------|
| 特性 | MGMT 接口 | HCI Socket | D-Bus |
| 协议族 | PF_BLUETOOTH | PF_BLUETOOTH | AF_UNIX |
| 通道类型 | HCI_CHANNEL_CONTROL | HCI_CHANNEL_RAW | D-Bus 总线 |
| 通信方向 | 内核 ↔ 用户态 | 内核 ↔ 用户态 | 用户态进程间 |
| 数据类型 | 管理命令/事件 | HCI 数据包 | 方法/信号 |
| 消息格式 | mgmt_hdr + TLV | HCI 包头 + 数据 | D-Bus 消息体 |
| 异步支持 | 原生支持 | 原生支持 | 原生支持 |
| 请求-响应 | 支持(mgmt_send) | 需手动匹配 | 原生支持 |
| 事件通知 | 支持(mgmt_register) | 需手动解析 | 原生支持(Signal) |
| 权限要求 | CAP_NET_ADMIN | CAP_NET_RAW | 用户权限 |
| 典型用途 | 适配器管理、配置 | HCI 数据收发 | 框架间通信 |

8.2 MGMT 与 Netlink 设计模式对比

8.3 使用场景选择指南

九、开发适配要点与调试技巧

9.1 权限与配置检查清单

cpp 复制代码
MGMT 接口开发检查清单
====================

□ 1. 内核配置
    CONFIG_BT=y (蓝牙子系统)
    CONFIG_BT_HCICORE=y (HCI Core)
    CONFIG_BT_HCIUART=y (UART HCI 驱动)
    CONFIG_BT_HCIBTUSB=y (USB HCI 驱动,可选)

□ 2. 权限要求
    • 需要 CAP_NET_ADMIN 权限
    • root 用户或 sudo 执行
    • 或设置 setcap cap_net_admin+ep 到可执行文件

□ 3. 套接字创建
    • PF_BLUETOOTH / SOCK_RAW / BTPROTO_HCI
    • bind(addr.hci_channel = HCI_CHANNEL_CONTROL)
    • 检查返回值:-EPERM 表示权限不足

□ 4. 适配器就绪
    • hciconfig 检查设备状态
    • hciconfig hci0 up 启动适配器
    • dmesg 检查内核日志

□ 5. 协议版本
    • 读取 MGMT_OP_READ_VERSION 获取版本
    • 根据版本选择可用的命令/事件
    • 检查 MGMT_OP_READ_COMMANDS 获取支持的命令

9.2 常见问题排查

问题 1:socket() 调用失败

cpp 复制代码
现象: socket(PF_BLUETOOTH, ...) 返回 -EAFNOSUPPORT

排查步骤:
1. 检查内核蓝牙支持
   zcat /proc/config.gz | grep CONFIG_BT
   需要 CONFIG_BT=y 或 m

2. 加载内核模块
   modprobe bluetooth
   modprobe hci_uart

3. 检查蓝牙服务
   systemctl status bluetooth
   systemctl restart bluetooth

4. 检查系统日志
   dmesg | grep -i bluetooth
   journalctl -u bluetooth

问题 2:bind() 调用失败

cpp 复制代码
现象: bind(fd, ...) 返回 -EPERM 或 -EACCES

排查步骤:
1. 检查权限
   id  # 查看当前用户
   # 需要 CAP_NET_ADMIN 权限

2. 临时添加权限
   sudo setcap cap_net_admin+ep /path/to/your_binary

3. 使用 root 运行
   sudo ./your_program

4. 检查安全模块
   getenforce  # SELinux 状态
   # 可能需要调整 SELinux 策略

问题 3:MGMT 命令超时

cpp 复制代码
现象: mgmt_send_timeout() 返回超时状态

排查步骤:
1. 检查适配器状态
   hciconfig hci0
   # 确保适配器已启动

2. 检查 HCI 设备
   ls /sys/class/bluetooth/
   # 确认 hci0 存在

3. 查看内核日志
   dmesg | tail -50
   # 检查是否有 HCI 错误

4. 使用 btmon 抓包分析
   sudo btmon
   # 观察 MGMT 命令是否发出
   # 检查内核响应

5. 增加调试输出
   mgmt_set_debug(mgmt, debug_func, ...);
   # 查看详细的收发日志

问题 4:事件未收到

cpp 复制代码
现象: mgmt_register() 注册的回调未被触发

排查步骤:
1. 检查事件码
   # 确认事件码正确
   # 参考 lib/mgmt.h 中的定义

2. 检查适配器索引
   # 如果注册时指定了特定索引
   # 确认事件发生在该索引上
   # 或使用 MGMT_INDEX_NONE 监听所有

3. 检查套接字状态
   # 确认套接字未关闭
   # mgmt_unref() 可能导致关闭

4. 检查 IO 回调
   # can_read_data() 是否被调用
   # 检查主事件循环是否正常运行

5. 检查过滤器
   # MGMT 接口无事件过滤器
   # 但需确保未提前注销

9.3 调试工具使用

hciconfig --- 设备状态查看

cpp 复制代码
# 查看所有设备
hciconfig -a

# 查看详细信息
hciconfig hci0

# 查看支持的特性
hciconfig hci0 features
hciconfig hci0 version

# 启用/禁用设备
sudo hciconfig hci0 up
sudo hciconfig hci0 down

# 重置设备
sudo hciconfig hci0 reset

btmon --- HCI 抓包分析

cpp 复制代码
# 实时监控 HCI 数据包
sudo btmon

# 保存到文件
sudo btmon -w capture.btmon

# 读取日志文件
btmon -r capture.btmon

# 过滤特定设备
sudo btmon -i hci0

# 仅显示 MGMT 相关
sudo btmon | grep -i mgmt

strace --- 系统调用跟踪

cpp 复制代码
# 跟踪 MGMT 相关系统调用
sudo strace -e trace=socket,bind,sendto,recvfrom \
    -f ./your_program

# 详细跟踪
sudo strace -e trace=all -f -s 256 ./your_program

hcidump --- 传统 HCI 抓包

cpp 复制代码
# 抓取 HCI 数据包
sudo hcidump -t

# 过滤特定设备
sudo hcidump -t -i hci0

# 输出到文件
sudo hcidump -t -w capture.log

9.4 代码调试技巧

启用 MGMT 调试

cpp 复制代码
// 启用详细调试输出
static void debug_func(const char *str, void *user_data)
{
    printf("[MGMT DEBUG] %s\n", str);
}

// 创建 MGMT 实例后立即启用
struct mgmt *mgmt = mgmt_new_default();
mgmt_set_debug(mgmt, debug_func, NULL, NULL);

消息过滤与解析

cpp 复制代码
// 自定义消息解析
void parse_mgmt_message(const void *data, uint16_t len)
{
    const struct mgmt_hdr *hdr = data;
    uint16_t opcode = btohs(hdr->opcode);
    uint16_t index = btohs(hdr->index);
    uint16_t msg_len = btohs(hdr->len);

    printf("MGMT: opcode=0x%04x, index=%u, len=%u\n",
        opcode, index, msg_len);

    // 根据 opcode 解析
    switch (opcode) {
    case MGMT_EV_CMD_COMPLETE:
        const struct mgmt_ev_cmd_complete *cc = data + MGMT_HDR_SIZE;
        printf("  CMD COMPLETE: cmd=0x%04x, status=0x%02x\n",
            btohs(cc->opcode), cc->status);
        break;

    case MGMT_EV_DEVICE_FOUND:
        const struct mgmt_ev_device_found *ev = data + MGMT_HDR_SIZE;
        printf("  DEVICE FOUND: %s, RSSI=%d\n",
            ba2str(&ev->addr.bdaddr), ev->rssi);
        break;

    // ... 更多事件解析
    }
}

错误处理模式

cpp 复制代码
// 完整的错误处理示例
int mgmt_operation(struct mgmt *mgmt)
{
    unsigned int id;
    int ret;

    // 1. 发送请求
    id = mgmt_send_timeout(mgmt, MGMT_OP_SET_POWERED,
                        0, sizeof(cp), &cp,
                        callback, user_data, NULL, 5);
    if (!id) {
        fprintf(stderr, "Failed to send MGMT command\n");
        return -EIO;
    }

    // 2. 等待响应(在主循环中处理)
    // ...

    // 3. 检查超时
    // 超时回调中会返回 MGMT_STATUS_TIMEOUT

    // 4. 错误恢复
    // 如果失败,可能需要:
    // - 取消当前操作: mgmt_cancel(mgmt, id)
    // - 重试: 重新发送命令
    // - 重置适配器: hciconfig hci0 reset

    return 0;
}

十、核心函数速查表

10.1 套接字与实例管理

|----------------------|-------------------|--------------------------------------|
| 函数 | 源文件 | 功能 |
| mgmt_new_default() | src/shared/mgmt.c | 创建默认 MGMT 连接(使用 HCI_CHANNEL_CONTROL) |
| mgmt_new(fd) | src/shared/mgmt.c | 基于已打开的 fd 创建 MGMT 实例 |
| mgmt_ref(mgmt) | src/shared/mgmt.c | 增加引用计数 |
| mgmt_unref(mgmt) | src/shared/mgmt.c | 减少引用计数,归零时释放资源 |
| mgmt_set_debug() | src/shared/mgmt.c | 设置调试回调 |
| mgmt_get_mtu() | src/shared/mgmt.c | 获取当前 MTU |

10.2 命令发送相关

|------------------------|-------------------|----------------|
| 函数 | 源文件 | 功能 |
| mgmt_send() | src/shared/mgmt.c | 发送命令(无超时) |
| mgmt_send_timeout() | src/shared/mgmt.c | 发送命令(带超时) |
| mgmt_send_nowait() | src/shared/mgmt.c | 发送命令(不排队,立即发送) |
| mgmt_reply() | src/shared/mgmt.c | 发送回复命令(高优先级) |
| mgmt_reply_timeout() | src/shared/mgmt.c | 发送回复命令(带超时) |
| mgmt_send_tlv() | src/shared/mgmt.c | 使用 TLV 列表发送命令 |
| mgmt_cancel() | src/shared/mgmt.c | 取消指定 ID 的请求 |
| mgmt_cancel_index() | src/shared/mgmt.c | 取消指定适配器的所有请求 |
| mgmt_cancel_all() | src/shared/mgmt.c | 取消所有请求 |

10.3 事件通知相关

|---------------------------|-------------------|----------------|
| 函数 | 源文件 | 功能 |
| mgmt_register() | src/shared/mgmt.c | 注册事件监听回调 |
| mgmt_unregister() | src/shared/mgmt.c | 注销指定 ID 的事件监听 |
| mgmt_unregister_index() | src/shared/mgmt.c | 注销指定适配器的所有事件监听 |
| mgmt_unregister_all() | src/shared/mgmt.c | 注销所有事件监听 |

10.4 TLV 编解码相关

|---------------------------------|-------------------|---------------|
| 函数 | 源文件 | 功能 |
| mgmt_tlv_list_new() | src/shared/mgmt.c | 创建 TLV 列表 |
| mgmt_tlv_list_free() | src/shared/mgmt.c | 释放 TLV 列表 |
| mgmt_tlv_add() | src/shared/mgmt.c | 添加 TLV 条目 |
| mgmt_tlv_list_load_from_buf() | src/shared/mgmt.c | 从缓冲区加载 TLV 列表 |
| mgmt_tlv_list_foreach() | src/shared/mgmt.c | 遍历 TLV 列表 |

10.5 核心回调函数

|----------------------|-------------------|---------------|
| 函数 | 源文件 | 功能 |
| can_read_data() | src/shared/mgmt.c | 读回调,处理内核返回的消息 |
| can_write_data() | src/shared/mgmt.c | 写回调,从队列取请求发送 |
| request_complete() | src/shared/mgmt.c | 请求完成处理 |
| process_notify() | src/shared/mgmt.c | 事件通知分发 |
| notify_handler() | src/shared/mgmt.c | 单个通知匹配与回调 |
| request_timeout() | src/shared/mgmt.c | 请求超时处理 |

十一、总结

11.1 核心设计要点

  1. 类 Netlink 设计模式:MGMT 接口虽然基于 PF_BLUETOOTH 协议族,但完全借鉴了 Netlink 的异步消息传递思想,包括统一的消息头、请求-响应模型、事件通知机制。

  2. 三队列调度机制:

  • reply_queue:高优先级回复队列

  • request_queue:普通请求队列

  • pending_list:等待响应的请求列表

这种设计保证了命令处理的顺序性和实时性。

  1. 引用计数与延迟清理:通过 in_notify 标志和 removed 延迟标记机制,解决了通知处理过程中注销回调的线程安全问题。

  2. MTU 动态调整:通过 BT_SNDMTU socket 选项动态调整 MTU,支持大于 1024 字节的大型命令(如加载大量密钥)。

11.2 开发实践建议

  1. 权限处理:始终检查并处理权限错误,使用 CAP_NET_ADMIN 而非直接以 root 运行。

  2. 超时设置:对关键命令设置合理的超时(通常 5-30 秒),避免永久阻塞。

  3. 错误恢复:实现完整的错误恢复策略,包括重试、降级、用户通知等。

  4. 资源清理:确保在程序退出时正确调用 mgmt_unregister_all() 和 mgmt_unref(),避免资源泄漏。

  5. 异步处理:所有操作都是异步的,必须通过回调机制处理结果,不能使用同步阻塞方式。

  6. 调试日志:开发阶段始终启用 mgmt_set_debug(),记录完整的消息收发日志。

11.3 与 Netlink 的协同

在复杂的蓝牙应用中,可能需要同时使用:

  • MGMT 接口:蓝牙适配器管理(电源、可发现、配对等)

  • HCI Socket:直接 HCI 数据收发(特殊命令、监控等)

  • D-Bus:BlueZ 高层 API(GATT、Profile、Device 等)

  • Netlink:系统级网络配置(IP、路由等)

理解这些通信机制的设计哲学,对于构建健壮的蓝牙应用至关重要。


附录:MGMT 接口资源

参考文档

  • BlueZ MGMT API 文档:doc/mgmt-api.txt

  • Linux 内核蓝牙文档:Documentation/bluetooth/

  • BlueZ 源码:src/shared/mgmt.c、lib/mgmt.h

版本历史

  • Linux v3.4:MGMT 1.0 基础版本

  • Linux v3.13:MGMT 1.4 增加广播、静态地址等

  • Linux v5.5:MGMT 1.15 增加 PHY 配置

  • Linux v5.8:MGMT 1.18 增加监控、系统配置等

工具支持

  • mgmt 命令行工具:BlueZ 源码 tools/mgmt-tester.c

  • btmon:HCI 抓包工具

  • hciconfig:蓝牙设备配置工具


参考文献

  • BlueZ Source Code: src/shared/mgmt.c, src/shared/mgmt.h, lib/mgmt.h

  • BlueZ Documentation: doc/mgmt-api.txt

  • Linux Kernel: net/bluetooth/hci_core.c, net/bluetooth/hci_sock.c

  • Bluetooth Specification Version 5.3: Volume 4, Part A

  • Linux Netlink Documentation: Documentation/core-api/netlink.rst


相关推荐
xbzb1 小时前
Nginx 反向代理总 404?Location 匹配与 proxy_pass 尾斜杠避坑手册
linux·运维·nginx·虚拟主机
Julien20042 小时前
CGroups 资源控制组
linux·运维·服务器·ssh·学习方法
皓月盈江2 小时前
Linux Debian系统安装Google Chrome谷歌浏览器教程
linux·chrome·debian·谷歌浏览器
Vcaker2 小时前
Linux学习37-rook-ceph部署
linux·运维·学习
Mortalbreeze2 小时前
MySQL 基础篇(二):数据库和数据表的基本操作
linux·服务器·数据库·mysql
阳光九叶草LXGZXJ2 小时前
Linux-学习-12-JDK安装
java·linux·运维·学习
夜听莺儿鸣13 小时前
502-003_Linux 中断与异常(一):从Linux角度理解中断与异常
linux·中断与异常·linux中断
杨云龙UP13 小时前
TDengine 3.4.2.8 Community 三节点三副本生产集群部署实战(DNode/MNode/taosAdapter/Explorer)
大数据·linux·运维·数据库·tdengine·时序库
向成科技14 小时前
XC3576H工控主板|深度适配Ubuntu 26.04 LTS,释放边缘AI与工业开发新潜能
linux·人工智能·ubuntu·机器人·硬件·主板·边缘ai