2. 配置蓝牙地址

任务说明

本案例实现蓝牙设备地址(BD_ADDR)的设置与读取功能。BD_ADDR 是蓝牙设备的唯一 48 位标识符,分为公共地址(Public Address)和随机地址(Random Address)两类。我们将基于 Linux 蓝牙协议栈(BlueZ)提供的 HCI 接口,通过发送 HCI 命令完成地址配置,并读取当前生效地址进行验证。

实现方案

使用 BlueZ 的 HCI 用户态库(libbluetooth),通过 HCI 套接字直接与蓝牙控制器通信。核心操作包括:

  • 打开 HCI 套接字并绑定指定蓝牙设备(如 hci0)
  • 发送 HCI 命令 HCI_OP_WRITE_BD_ADDR(Opcode 0x0006)写入新地址
  • 发送 HCI 命令 HCI_OP_READ_BD_ADDR(Opcode 0x0009)读取当前地址
  • 解析返回事件,提取 BD_ADDR 并格式化输出

代码实现


// bd_addr_config.c
// 编译: gcc -o bd_addr_config bd_addr_config.c -lbluetooth
// 运行: sudo ./bd_addr_config hci0 00:11:22:33:44:55   (设置地址)
//       sudo ./bd_addr_config hci0                     (读取地址)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>

// 将 "XX:XX:XX:XX:XX:XX" 格式字符串转换为 6 字节数组
static int parse_bdaddr(const char *str, uint8_t *addr) {
    unsigned int b[6];
    if (sscanf(str, "%02x:%02x:%02x:%02x:%02x:%02x",
               &b[0], &b[1], &b[2], &b[3], &b[4], &b[5]) != 6)
        return -1;
    for (int i = 0; i < 6; i++)
        addr[i] = (uint8_t)b[i];
    return 0;
}

// 读取 BD_ADDR
static int read_bdaddr(int dd) {
    unsigned char buf[HCI_MAX_EVENT_SIZE];
    hci_command_hdr *cmd = (hci_command_hdr *)buf;
    uint16_t opcode = HCI_OP_READ_BD_ADDR;

    cmd->opcode = htobs(opcode);
    cmd->plen = 0;

    if (write(dd, buf, HCI_COMMAND_HDR_SIZE) != HCI_COMMAND_HDR_SIZE) {
        perror("write command");
        return -1;
    }

    // 等待命令完成事件
    int len = read(dd, buf, sizeof(buf));
    if (len < 0) {
        perror("read event");
        return -1;
    }

    // 解析 HCI 事件
    hci_event_hdr *evt = (hci_event_hdr *)(buf + 1); // 跳过包类型字节
    if (evt->evt != EVT_CMD_COMPLETE) {
        fprintf(stderr, "Unexpected event: 0x%02x\n", evt->evt);
        return -1;
    }

    evt_cmd_complete *cc = (evt_cmd_complete *)(buf + 1 + HCI_EVENT_HDR_SIZE);
    if (cc->opcode != htobs(opcode)) {
        fprintf(stderr, "Opcode mismatch\n");
        return -1;
    }

    uint8_t status = cc->data[0];
    if (status != 0) {
        fprintf(stderr, "HCI command failed, status=0x%02x\n", status);
        return -1;
    }

    // BD_ADDR 在 data[1] 到 data[6]
    uint8_t *bdaddr = &cc->data[1];
    printf("Current BD_ADDR: %02X:%02X:%02X:%02X:%02X:%02X\n",
           bdaddr[0], bdaddr[1], bdaddr[2],
           bdaddr[3], bdaddr[4], bdaddr[5]);
    return 0;
}

// 写入 BD_ADDR
static int write_bdaddr(int dd, uint8_t *new_addr) {
    unsigned char buf[HCI_MAX_EVENT_SIZE];
    hci_command_hdr *cmd = (hci_command_hdr *)buf;
    uint16_t opcode = HCI_OP_WRITE_BD_ADDR;

    cmd->opcode = htobs(opcode);
    cmd->plen = 6;
    memcpy(buf + HCI_COMMAND_HDR_SIZE, new_addr, 6);

    if (write(dd, buf, HCI_COMMAND_HDR_SIZE + 6) != HCI_COMMAND_HDR_SIZE + 6) {
        perror("write command");
        return -1;
    }

    // 等待命令完成事件
    int len = read(dd, buf, sizeof(buf));
    if (len < 0) {
        perror("read event");
        return -1;
    }

    hci_event_hdr *evt = (hci_event_hdr *)(buf + 1);
    if (evt->evt != EVT_CMD_COMPLETE) {
        fprintf(stderr, "Unexpected event: 0x%02x\n", evt->evt);
        return -1;
    }

    evt_cmd_complete *cc = (evt_cmd_complete *)(buf + 1 + HCI_EVENT_HDR_SIZE);
    if (cc->opcode != htobs(opcode)) {
        fprintf(stderr, "Opcode mismatch\n");
        return -1;
    }

    uint8_t status = cc->data[0];
    if (status != 0) {
        fprintf(stderr, "HCI command failed, status=0x%02x\n", status);
        return -1;
    }

    printf("BD_ADDR write successful\n");
    return 0;
}

int main(int argc, char *argv[]) {
    if (argc < 2 || argc > 3) {
        fprintf(stderr, "Usage: %s <hciX> [new_bdaddr]\n", argv[0]);
        fprintf(stderr, "  hciX       - Bluetooth device, e.g. hci0\n");
        fprintf(stderr, "  new_bdaddr - Address in XX:XX:XX:XX:XX:XX format (optional)\n");
        return 1;
    }

    // 打开 HCI 设备
    int dd = hci_open_dev(hci_devid(argv[1]));
    if (dd < 0) {
        perror("hci_open_dev");
        return 1;
    }

    int ret = 0;
    if (argc == 3) {
        // 设置模式
        uint8_t addr[6];
        if (parse_bdaddr(argv[2], addr) != 0) {
            fprintf(stderr, "Invalid BD_ADDR format: %s\n", argv[2]);
            hci_close_dev(dd);
            return 1;
        }
        ret = write_bdaddr(dd, addr);
    } else {
        // 读取模式
        ret = read_bdaddr(dd);
    }

    hci_close_dev(dd);
    return ret;
}

代码说明

程序通过 hci_open_dev() 打开指定蓝牙设备,然后根据命令行参数决定执行写入或读取操作。写入时使用 HCI_OP_WRITE_BD_ADDR 命令携带 6 字节新地址;读取时使用 HCI_OP_READ_BD_ADDR 命令。两者均通过 write() 发送命令,再 read() 等待控制器返回 EVT_CMD_COMPLETE 事件,从事件参数中提取状态码和地址数据。地址字节序遵循蓝牙规范,低字节在前。

HCI 命令交互流程

主机 (Host) 蓝牙控制器 HCI 套接字 命令构造 事件解析 命令接收 地址寄存器 事件生成 HCI 命令 (Write/Read BD_ADDR) 命令完成事件 (含状态与地址) 用户态程序 固件

编译与运行


# 安装依赖(Debian/Ubuntu)
sudo apt-get install libbluetooth-dev

# 编译
gcc -o bd_addr_config bd_addr_config.c -lbluetooth

# 读取当前地址(需要 root 权限)
sudo ./bd_addr_config hci0

# 设置新地址
sudo ./bd_addr_config hci0 00:11:22:33:44:55

验证方法

执行读取命令后,程序输出 Current BD_ADDR: XX:XX:XX:XX:XX:XX。执行设置命令后,程序输出 BD_ADDR write successful,随后再次运行读取命令,确认输出的地址与刚设置的地址一致,即表示设置成功。注意:部分蓝牙控制器(尤其是 USB 适配器)可能不支持修改 BD_ADDR,此时 HCI 命令会返回非零状态码,程序将打印错误信息。若需恢复原始地址,请使用原厂工具或重新插拔适配器。