【BlueZ】hciconfig 工具:蓝牙适配器管理实操 + 源码实现基础

hciconfig 是 BlueZ 提供的经典蓝牙适配器配置工具,直接通过 HCI 套接字与内核蓝牙子系统交互,绕过 bluetoothd 守护进程。它的定位类似于网络世界的 ifconfig------操作对象是 HCI 控制器(hci0/hci1...),能完成上电/下电、扫描模式切换、本地名称/设备类设置、LE 地址与广播配置等底层操作。本文基于 BlueZ 5.x 源码,全面整理 hciconfig 实操命令,并深度拆解其源码入口、参数解析、ioctl 系统调用、HCI 指令封装、套接字通信底层实现,最后对比 hciconfig与 bluetoothctl 的功能差异与适配场景。


目录

[一、hciconfig 全套实操命令](#一、hciconfig 全套实操命令)

二、源码总览与工程结构

三、程序入口与参数解析

[四、ioctl 通信层:控制套接字操作](#四、ioctl 通信层:控制套接字操作)

[五、HCI 指令通信层:hci_send_req 机制](#五、HCI 指令通信层:hci_send_req 机制)

六、命令实现详解:两种模式

七、设备信息打印与状态解析

[八、hciconfig vs bluetoothctl 功能差异](#八、hciconfig vs bluetoothctl 功能差异)

九、常见报错与解决方案

十、嵌入式调试技巧

[十一、核心命令 → 源码函数 → 底层机制速查](#十一、核心命令 → 源码函数 → 底层机制速查)

十二、总结


一、hciconfig 全套实操命令

1.1 基本用法

cpp 复制代码
# 查看所有适配器(简要)
hciconfig

# 查看所有适配器(详细,含 features/name/class/version)
hciconfig -a

# 查看指定适配器
hciconfig hci0
hciconfig -a hci0

# 帮助
hciconfig -h

典型输出:

cpp 复制代码
hci0:   Type: Primary  Bus: USB
        BD Address: 00:1A:7D:DA:71:13  ACL MTU: 310:10  SCO MTU: 64:8
        UP RUNNING
        RX bytes:1234 acl:0 sco:0 events:56 commands:57 errors:0
        TX bytes:4321 acl:0 sco:0 commands:57 errors:0

1.2 适配器 启停与重置

cpp 复制代码
hciconfig hci0 up        # 上电并初始化 HCI 设备
hciconfig hci0 down      # 关闭 HCI 设备
hciconfig hci0 reset     # 重置(先 down 再 up)
hciconfig hci0 rstat     # 重置统计计数器

1.3 扫描模式配置

cpp 复制代码
hciconfig hci0 piscan    # 开启 Page + Inquiry 扫描(可连接 + 可发现)
hciconfig hci0 iscan     # 仅开启 Inquiry 扫描(可发现)
hciconfig hci0 pscan     # 仅开启 Page 扫描(可连接)
hciconfig hci0 noscan    # 关闭所有扫描

1.4 认证与加密

cpp 复制代码
hciconfig hci0 auth      # 启用认证
hciconfig hci0 noauth    # 禁用认证
hciconfig hci0 encrypt   # 启用加密
hciconfig hci0 noencrypt # 禁用加密

1.5 本地名称与设备类

cpp 复制代码
# 查看本地名称
hciconfig hci0 name

# 设置本地名称(最长 248 字节)
hciconfig hci0 name "My Bluetooth Device"

# 查看设备类
hciconfig hci0 class
# 输出示例:
#   Class: 0x1c010c
#   Service Classes: Networking, Object Transfer, Audio
#   Device Class: Computer, Laptop

# 设置设备类(3 字节十六进制)
hciconfig hci0 class 0x1c010c

1.6 硬件信息查询

cpp 复制代码
hciconfig hci0 version     # HCI/LMP 版本、制造商
hciconfig hci0 features    # LMP 特性页(page 0/1/2...)
hciconfig hci0 commands    # 支持的 HCI 命令列表
hciconfig hci0 revision    # 硬件修订版本(厂商相关)
hciconfig hci0 lestates    # LE 支持的状态组合

version 输出示例:

cpp 复制代码
hci0:   Type: Primary  Bus: USB
        BD Address: 00:1A:7D:DA:71:13  ACL MTU: 310:10  SCO MTU: 64:8
        HCI Version: 5.0 (0x9)  Revision: 0x100
        LMP Version: 5.0 (0x9)  Subversion: 0x100
        Manufacturer: Cambridge Silicon Radio (10)

1.7 链路参数配置

cpp 复制代码
# 数据包类型
hciconfig hci0 ptype                    # 查看
hciconfig hci0 ptype DM1,DH1,DH3        # 设置

# 链路策略
hciconfig hci0 lp                       # 查看
hciconfig hci0 lp HOLD,SNIFF            # 设置

# 链路模式
hciconfig hci0 lm                       # 查看
hciconfig hci0 lm MASTER,ACCEPT         # 设置

# 语音设置
hciconfig hci0 voice                    # 查看
hciconfig hci0 voice 0x0060             # 设置

# IAC(Inquiry Access Code)
hciconfig hci0 iac                      # 查看
hciconfig hci0 iac 0x9e8b33             # 设置 GIAC

1.8 查询参数调优

cpp 复制代码
# 查询发射功率
hciconfig hci0 inqtpl                   # 查看
hciconfig hci0 inqtpl 10                # 设置

# 查询模式(0=标准, 1=RSSI, =EIR)
hciconfig hci0 inqmode                  # 查看
hciconfig hci0 inqmode 1                # 设置为 RSSI 模式

# 查询扫描参数(窗口:间隔)
hciconfig hci0 inqparms                 # 查看
hciconfig hci0 inqparms 18:32           # 设置

# 查询扫描类型(0=标准, 1=交错)
hciconfig hci0 inqtype                  # 查看
hciconfig hci0 inqtype 1                # 设置

# Page 扫描参数
hciconfig hci0 pageparms                # 查看
hciconfig hci0 pageparms 18:32          # 设置

# Page 超时
hciconfig hci0 pageto                   # 查看
hciconfig hci0 pageto 8192              # 设置(slots)

1.9 高级功能

cpp 复制代码
# AFH 模式(0=禁用, 1=启用)
hciconfig hci0 afhmode                  # 查看
hciconfig hci0 afhmode 1                # 启用

# Simple Pairing 模式
hciconfig hci0 sspmode                  # 查看
hciconfig hci0 sspmode 1                # 启用

# ACL/SCO MTU
hciconfig hci0 aclmtu 800:10            # 设置 ACL MTU:包数
hciconfig hci0 scomtu 64:8              # 设置 SCO MTU:包数

# OOB 数据
hciconfig hci0 oobdata                  # 读取本地 OOB 数据

# 设备黑名单
hciconfig hci0 block AA:BB:CC:DD:EE:FF  # 拉黑设备
hciconfig hci0 unblock AA:BB:CC:DD:EE:FF # 解除拉黑

# 删除链路密钥
hciconfig hci0 delkey AA:BB:CC:DD:EE:FF

1.10 LE 专项配置

cpp 复制代码
# 设置 LE 随机地址(MAC 修改)
hciconfig hci0 lerandaddr AA:BB:CC:DD:EE:FF

# 开启 LE 广播
hciconfig hci0 leadv           # 默认: 可连接非定向广播 (type 0)
hciconfig hci0 leadv 0         # ADV_IND: 可连接非定向
hciconfig hci0 leadv 3         # ADV_NONCONN_IND: 不可连接非定向

# 关闭 LE 广播
hciconfig hci0 noleadv

# 查看 LE 支持的状态
hciconfig hci0 lestates

1.11 实战速查:从零配置一台 BLE 外设

cpp 复制代码
# 1. 上电
hciconfig hci0 up

# 2. 设置随机地址(修改 MAC)
hciconfig hci0 lerandaddr AA:BB:CC:DD:EE:FF

# 3. 开启 LE 广播
hciconfig hci0 leadv 0

# 4. 验证状态
hciconfig -a hci0

# 5. 停止广播
hciconfig hci0 noleadv

# 6. 下电
hciconfig hci0 down

二、源码总览与工程结构

hciconfig 的全部源码集中在单文件 tools/hciconfig.c(约 2066 行),依赖 BlueZ 的公共库 lib/hci.c(HCI 操作封装)和 lib/hci.h(结构体与 ioctl 定义)。

cpp 复制代码
tools/hciconfig.c          # 全部源码:命令表 + 各 cmd_* 函数 + main()
    │
    ├── lib/hci.h           # ioctl 码定义、struct hci_dev_info / hci_dev_req 等
    ├── lib/hci.c           # hci_open_dev / hci_send_req / hci_read_local_version 等
    └── lib/bluetooth.h     # bdaddr_t、ba2str、str2ba 等基础定义

与 bluetoothctl 不同,hciconfig 不依赖 D-Bus,不连接 bluetoothd,直接通过 socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI) + ioctl() + hci_send_req() 与内核 HCI 子系统交互。

三、程序入口与参数解析

3.1 main() 全貌

入口位于 tools/hciconfig.c第 1999 行:

cpp 复制代码
int main(int argc, char *argv[])
{
    int opt, ctl, i, cmd = 0;

    /* 1. 解析全局选项:-a (all), -h (help) */
    while ((opt = getopt_long(argc, argv, "ah", main_options, NULL)) != -1) {
        switch (opt) {
        case 'a':
            all = 1;          /* 设置全局 all 标志,影响输出详细程度 */
            break;
        case 'h':
        default:
            usage();
            exit(0);
        }
    }

    argc -= optind;
    argv += optind;
    optind = 0;

    /* 2. 打开 HCI 控制套接字(用于 ioctl 操作) */
    if ((ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)) < 0) {
        perror("Can't open HCI socket.");
        exit(1);
    }

    /* 3. 无参数 → 列出所有设备 */
    if (argc < 1) {
        print_dev_list(ctl, 0);
        exit(0);
    }

    /* 4. 解析设备号:从 "hci0" 提取 0 */
    di.dev_id = atoi(argv[0] + 3);   /* 跳过 "hci" 前缀 */
    argc--; argv++;

    /* 5. 获取设备信息(ioctl HCIGETDEVINFO) */
    if (ioctl(ctl, HCIGETDEVINFO, (void *) &di)) {
        perror("Can't get device info");
        exit(1);
    }

    /* 6. 遍历剩余参数,在命令表中查找并执行 */
    while (argc > 0) {
        for (i = 0; command[i].cmd; i++) {
            if (strncmp(command[i].cmd, *argv, strlen(command[i].cmd)))
                continue;

            /* 有参数的命令消耗下一个 argv */
            if (command[i].opt)
                argc--, argv++;

            command[i].func(ctl, di.dev_id, *argv);
            cmd = 1;
            break;
        }

        if (command[i].cmd == 0)
            fprintf(stderr, "Warning: unknown command - \"%s\"\n", *argv);

        argc--; argv++;
    }

    /* 7. 无命令 → 打印设备详情 */
    if (!cmd)
        print_dev_info(ctl, &di);

    close(ctl);
    return 0;
}

3.2 两套套接字体系

hciconfig 的源码中存在两种套接字使用方式,对应两类操作:

|--------------------|---------------------------------------------|------------------------------|-----------------------------------|
| 方式 | 套接字 | 操作 | 典型命令 |
| 控制套接字 (ctl) | socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI) | ioctl() | up/down/scan/auth/ptype |
| HCI 设备套接字 (dd) | hci_open_dev()→ bind 到具体 hciX | hci_send_req() / hci_read_* | name/class/version/features/leadv |

cpp 复制代码
/* main() 中创建的是控制套接字 ctl,只用于 ioctl */
ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);

/* 各 cmd_* 函数中按需创建 HCI 设备套接字 dd,用于收发 HCI 命令 */
dd = hci_open_dev(hdev);   /* 内部: socket + bind(hci_dev=hdev) */

3.3 命令表数据结构

命令表是 hciconfig 的核心数据结构,采用编译期静态初始化:

cpp 复制代码
static struct {
    char *cmd;                                    /* 命令名 */
    void (*func)(int ctl, int hdev, char *opt);   /* 处理函数 */
    char *opt;                                    /* 参数提示(NULL 表示无参数) */
    char *doc;                                    /* 描述 */
} command[] = {
    { "up",       cmd_up,       0,          "Open and initialize HCI device" },
    { "down",     cmd_down,     0,          "Close HCI device" },
    { "reset",    cmd_reset,    0,          "Reset HCI device" },
    { "piscan",   cmd_scan,     0,          "Enable Page and Inquiry scan" },
    { "noscan",   cmd_scan,     0,          "Disable scan" },
    { "name",     cmd_name,     "[name]",   "Get/Set local name" },
    { "class",    cmd_class,    "[class]",  "Get/Set class of device" },
    { "version",  cmd_version,  0,          "Display version information" },
    { "features", cmd_features, 0,          "Display device features" },
    { "lerandaddr", cmd_le_addr, "<bdaddr>", "Set LE Random Address" },
    { "leadv",    cmd_le_adv,   "[type]",   "Enable LE advertising" },
    { NULL, NULL, 0 }   /* 哨兵 */
};

main() 中的查找逻辑是简单的线性匹配:

cpp 复制代码
for (i = 0; command[i].cmd; i++) {
    if (strncmp(command[i].cmd, *argv, strlen(command[i].cmd)))
        continue;   /* 不匹配,继续 */
    /* 匹配成功,执行 */
    if (command[i].opt)
        argc--, argv++;   /* 消耗参数 */
    command[i].func(ctl, di.dev_id, *argv);
    break;
}

3.4 参数解析流程图

cpp 复制代码
命令行: hciconfig -a hci0 name "MyBT"
            │
            ├─ getopt_long → all=1
            │
            ├─ socket(AF_BLUETOOTH, ...) → ctl
            │
            ├─ argv[0]="hci0" → di.dev_id = atoi("0") = 0
            ├─ ioctl(ctl, HCIGETDEVINFO, &di) → 获取设备信息
            │
            ├─ argv[1]="name" → 匹配 command[16] "name"
            │   ├─ command[16].opt = "[name]" → 非NULL → 消耗 argv[2]
            │   └─ cmd_name(ctl, 0, "MyBT")
            │
            └─ close(ctl)

四、ioctl 通信层:控制套接字操作

4.1 ioctl 码定义

所有 hciconfig 的 ioctl 码定义在 lib/hci.h:

cpp 复制代码
#define HCIDEVUP       _IOW('H', 201, int)
#define HCIDEVDOWN     _IOW('H', 202, int)
#define HCIDEVRESET    _IOW('H', 203, int)
#define HCIDEVRESTAT   _IOW('H', 204, int)
#define HCIGETDEVLIST  _IOR('H', 210, int)
#define HCIGETDEVINFO  _IOR('H', 211, int)
#define HCISETSCAN     _IOW('H', 221, int)
#define HCISETAUTH     _IOW('H', 222, int)
#define HCISETENCRYPT  _IOW('H', 223, int)
#define HCISETPTYPE    _IOW('H', 224, int)
#define HCISETLINKPOL  _IOW('H', 225, int)
#define HCISETLINKMODE _IOW('H', 226, int)
#define HCIBLOCKADDR   _IOW('H', 230, int)

这些是标准的 Linux ioctl 码,_IOW 表示写入(用户态→内核),_IOR 表示读取(内核→用户态)。'H' 是蓝牙子系统的魔数。

4.2 设备列表获取:HCIGETDEVLIST

cpp 复制代码
static void print_dev_list(int ctl, int flags)
{
    struct hci_dev_list_req *dl;
    struct hci_dev_req *dr;
    int i;

    /* 分配缓冲区:最多 HCI_MAX_DEV 个设备 */
    dl = malloc(HCI_MAX_DEV * sizeof(struct hci_dev_req) + sizeof(uint16_t));
    dl->dev_num = HCI_MAX_DEV;
    dr = dl->dev_req;

    /* ioctl 获取设备列表 */
    if (ioctl(ctl, HCIGETDEVLIST, (void *) dl) < 0) {
        perror("Can't get device list");
        exit(1);
    }

    /* 遍历每个设备,获取详细信息 */
    for (i = 0; i < dl->dev_num; i++) {
        di.dev_id = (dr + i)->dev_id;
        if (ioctl(ctl, HCIGETDEVINFO, (void *) &di) < 0)
            continue;
        print_dev_info(ctl, &di);
    }

    free(dl);
}

相关结构体:

cpp 复制代码
struct hci_dev_req {
    uint16_t dev_id;
    uint32_t dev_opt;
};

struct hci_dev_list_req {
    uint16_t dev_num;
    struct hci_dev_req dev_req[];   /* 柔性数组 */
};

4.3 设备信息获取:HCIGETDEVINFO

cpp 复制代码
/* main() 中 */
di.dev_id = atoi(argv[0] + 3);    /* "hci0" → 0 */
ioctl(ctl, HCIGETDEVINFO, (void *) &di);

struct hci_dev_info 是 hciconfig 最核心的数据结构,一次 ioctl 即可获取控制器的所有静态信息:

cpp 复制代码
struct hci_dev_info {
    uint16_t dev_id;          /* 设备 ID */
    char     name[8];         /* 设备名 "hci0" */
    bdaddr_t bdaddr;          /* BD 地址(MAC) */
    uint32_t flags;           /* 状态标志(UP/RUNNING/PSCAN/ISCAN...) */
    uint8_t  type;            /* 类型(高4位: Primary/AMP, 低4位: Bus) */
    uint8_t  features[8];     /* LMP 特性 page 0 */
    uint32_t pkt_type;        /* 支持的包类型 */
    uint32_t link_policy;     /* 链路策略 */
    uint32_t link_mode;       /* 链路模式 */
    uint16_t acl_mtu;         /* ACL MTU */
    uint16_t acl_pkts;        /* ACL 包数 */
    uint16_t sco_mtu;         /* SCO MTU */
    uint16_t sco_pkts;        /* SCO 包数 */
    struct hci_dev_stats stat;/* 收发统计 */
};

4.4 启停控制:HCIDEVUP / HCIDEVDOWN

cpp 复制代码
static void cmd_up(int ctl, int hdev, char *opt)
{
    if (ioctl(ctl, HCIDEVUP, hdev) < 0) {
        if (errno == EALREADY)    /* 已经 up,不算错误 */
            return;
        fprintf(stderr, "Can't init device hci%d: %s (%d)\n",
                hdev, strerror(errno), errno);
        exit(1);
    }
}

static void cmd_down(int ctl, int hdev, char *opt)
{
    if (ioctl(ctl, HCIDEVDOWN, hdev) < 0) {
        fprintf(stderr, "Can't down device hci%d: %s (%d)\n",
                hdev, strerror(errno), errno);
        exit(1);
    }
}

reset 实现为 down + up 组合:

cpp 复制代码
static void cmd_reset(int ctl, int hdev, char *opt)
{
    cmd_down(ctl, hdev, "down");
    cmd_up(ctl, hdev, "up");
}

4.5 扫描模式:HCISETSCAN

cpp 复制代码
static void cmd_scan(int ctl, int hdev, char *opt)
{
    struct hci_dev_req dr;

    dr.dev_id  = hdev;
    dr.dev_opt = SCAN_DISABLED;
    if (!strcmp(opt, "iscan"))
        dr.dev_opt = SCAN_INQUIRY;          /* 0x02 */
    else if (!strcmp(opt, "pscan"))
        dr.dev_opt = SCAN_PAGE;             /* 0x01 */
    else if (!strcmp(opt, "piscan"))
        dr.dev_opt = SCAN_PAGE | SCAN_INQUIRY; /* 0x03 */

    if (ioctl(ctl, HCISETSCAN, (unsigned long) &dr) < 0) {
        fprintf(stderr, "Can't set scan mode on hci%d: %s (%d)\n",
                hdev, strerror(errno), errno);
        exit(1);
    }
}

注意 cmd_scan 被四个命令复用:piscan、iscan、pscan、noscan,通过 opt 参数区分具体行为。这是命令表中 auth/noauth、encrypt/noencrypt 同样采用的复用模式。

4.6 ioctl 类命令汇总

|-----------------------------|----------------|--------------|----------------------|
| 命令 | ioctl 码 | 参数结构 | 内核处理 |
| up | HCIDEVUP | dev_id (int) | 开启 HCI 设备,执行初始化序列 |
| down | HCIDEVDOWN | dev_id (int) | 关闭 HCI 设备 |
| rstat | HCIDEVRESTAT | dev_id (int) | 清零收发统计计数器 |
| piscan/iscan/pscan/noscan | HCISETSCAN | hci_dev_req | 设置 Page/Inquiry 扫描使能 |
| auth/noauth | HCISETAUTH | hci_dev_req | 设置认证使能 |
| encrypt/noencrypt | HCISETENCRYPT | hci_dev_req | 设置加密使能 |
| ptype | HCISETPTYPE | hci_dev_req | 设置默认数据包类型 |
| lp | HCISETLINKPOL | hci_dev_req | 设置链路策略 |
| lm | HCISETLINKMODE | hci_dev_req | 设置链路模式 |
| block | HCIBLOCKADDR | bdaddr_t | 添加到拒绝列表 |
| unblock | HCIUNBLOCKADDR | bdaddr_t | 从拒绝列表移除 |

五、HCI 指令通信层:hci_send_req 机制

5.1 两层通信模型

5.2 hci_open_dev() --- 设备套接字创建

lib/hci.c:

cpp 复制代码
int hci_open_dev(int dev_id)
{
    struct sockaddr_hci a;
    int dd, err;

    if (dev_id < 0) {
        errno = ENODEV;
        return -1;
    }

    /* 创建 HCI 原始套接字 */
    dd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI);
    if (dd < 0)
        return dd;

    /* 绑定到指定 HCI 设备 */
    memset(&a, 0, sizeof(a));
    a.hci_family = AF_BLUETOOTH;
    a.hci_dev = dev_id;          /* 绑定到 hci0/hci1... */
    if (bind(dd, (struct sockaddr *) &a, sizeof(a)) < 0)
        goto failed;

    return dd;

failed:
    err = errno;
    close(dd);
    errno = err;
    return -1;
}

与 main() 中的控制套接字不同,这里 bind() 绑定了具体的 hci_dev,使得后续 read()/write() 直接操作该设备的 HCI 数据流。

5.3 hci_send_req() --- 请求-响应核心

lib/hci.c这是 hciconfig 发送 HCI 命令的核心函数:

cpp 复制代码
int hci_send_req(int dd, struct hci_request *r, int to)
{
    unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr;
    uint16_t opcode = htobs(cmd_opcode_pack(r->ogf, r->ocf));
    struct hci_filter nf, of;
    socklen_t olen;
    hci_event_hdr *hdr;
    int err, try;

    /* 1. 保存旧过滤器,设置新过滤器:只接收目标命令的响应事件 */
    olen = sizeof(of);
    getsockopt(dd, SOL_HCI, HCI_FILTER, &of, &olen);

    hci_filter_clear(&nf);
    hci_filter_set_ptype(HCI_EVENT_PKT,  &nf);
    hci_filter_set_event(EVT_CMD_STATUS, &nf);
    hci_filter_set_event(EVT_CMD_COMPLETE, &nf);
    hci_filter_set_event(EVT_LE_META_EVENT, &nf);
    hci_filter_set_event(r->event, &nf);
    hci_filter_set_opcode(opcode, &nf);
    setsockopt(dd, SOL_HCI, HCI_FILTER, &nf, sizeof(nf));

    /* 2. 发送 HCI 命令包 */
    if (hci_send_cmd(dd, r->ogf, r->ocf, r->clen, r->cparam) < 0)
        goto failed;

    /* 3. 轮询等待响应事件(最多重试 10 次) */
    try = 10;
    while (try--) {
        evt_cmd_complete *cc;
        evt_cmd_status *cs;
        int len;

        if (to) {
            struct pollfd p;
            int n;

            p.fd = dd;
            p.events = POLLIN;
            while ((n = poll(&p, 1, to)) < 0) {
                if (errno == EAGAIN || errno == EINTR)
                    continue;
                goto failed;
            }

            if (!n) {           /* 超时 */
                errno = ETIMEDOUT;
                goto failed;
            }

            to -= 10;
            if (to < 0) to = 0;
        }

        /* 读取事件包 */
        while ((len = read(dd, buf, sizeof(buf))) < 0) {
            if (errno == EAGAIN || errno == EINTR)
                continue;
            goto failed;
        }

        hdr = (void *) (buf + 1);
        ptr = buf + (1 + HCI_EVENT_HDR_SIZE);
        len -= (1 + HCI_EVENT_HDR_SIZE);

        switch (hdr->evt) {
        case EVT_CMD_STATUS:
            cs = (void *) ptr;
            if (cs->opcode != opcode)
                continue;
            if (r->event != EVT_CMD_STATUS) {
                if (cs->status) {
                    errno = EIO;
                    goto failed;
                }
                break;
            }
            /* 返回状态 */
            if (r->rparam && r->rlen)
                memcpy(r->rparam, ptr, r->rlen);
            goto done;

        case EVT_CMD_COMPLETE:
            cc = (void *) ptr;
            if (cc->opcode != opcode)
                continue;
            /* 跳过事件头,拷贝返回参数 */
            ptr += EVT_CMD_COMPLETE_SIZE;
            len -= EVT_CMD_COMPLETE_SIZE;
            if (r->rparam && r->rlen)
                memcpy(r->rparam, ptr, r->rlen);
            goto done;
        }
    }

failed:
    err = errno;
    /* 恢复旧过滤器 */
    setsockopt(dd, SOL_HCI, HCI_FILTER, &of, sizeof(of));
    errno = err;
    return -1;

done:
    /* 恢复旧过滤器 */
    setsockopt(dd, SOL_HCI, HCI_FILTER, &of, sizeof(of));
    return 0;
}

5.4 hci_request 结构

lib/hci_lib.h:

cpp 复制代码
struct hci_request {
    uint16_t ogf;       /* Opcode Group Field(命令组) */
    uint16_t ocf;       /* Opcode Command Field(命令码) */
    int      event;     /* 期望的响应事件类型 */
    void     *cparam;   /* 命令参数 */
    int      clen;      /* 命令参数长度 */
    void     *rparam;   /* 响应参数缓冲 */
    int      rlen;      /* 响应参数长度 */
};

ogf(Opcode Group Field)和 ocf(Opcode Command Field)组成 16 位 HCI Opcode,是蓝牙规范定义的命令编号。例如:

  • OGF_LE_CTL (0x08) + OCF_LE_SET_RANDOM_ADDRESS (0x0005) → 设置 LE 随机地址

  • OGF_LE_CTL (0x08) + OCF_LE_SET_ADVERTISING_PARAMETERS (0x0006) → 设置广播参数

  • OGF_HOST_CTL (0x03) + OCF_WRITE_LOCAL_NAME (0x0013) → 写本地名称

5.5 hci_send_req 工作时序

六、命令实现详解:两种模式

6.1 模式一:ioctl 直接配置(无需 HCI 命令包)

以 cmd_scan(piscan/iscan/pscan/noscan)为例,通过控制套接字的 ioctl 直接配置内核 HCI Core:

cpp 复制代码
static void cmd_scan(int ctl, int hdev, char *opt)
{
    struct hci_dev_req dr;
    dr.dev_id  = hdev;
    dr.dev_opt = SCAN_PAGE | SCAN_INQUIRY;   /* piscan */

    /* 直接 ioctl,不发 HCI 命令包 */
    ioctl(ctl, HCISETSCAN, (unsigned long) &dr);
}

此类命令的特点:内核 HCI Core 收到 ioctl 后,自行决定是否下发 HCI 命令给控制器。用户态工具只需告诉内核"我要什么",不需要关心 HCI 协议细节。

适用命令:up/down/reset/rstat/piscan/pscan/iscan/noscan/auth/noauth/encrypt/noencrypt/ptype/lp/lm/block/unblock

6.2 模式二:HCI 命令直接收发

以 cmd_le_addr(设置 LE 随机地址)为例,通过设备套接字直接发送 HCI 命令包:

cpp 复制代码
static void cmd_le_addr(int ctl, int hdev, char *opt)
{
    struct hci_request rq;
    le_set_random_address_cp cp;   /* 命令参数结构 */
    uint8_t status;                /* 响应参数 */
    int dd;

    /* 1. 打开设备套接字 */
    dd = hci_open_dev(hdev);

    /* 2. 填充命令参数 */
    memset(&cp, 0, sizeof(cp));
    str2ba(opt, &cp.bdaddr);       /* "AA:BB:CC:DD:EE:FF" → bdaddr_t */

    /* 3. 构造 hci_request */
    memset(&rq, 0, sizeof(rq));
    rq.ogf = OGF_LE_CTL;                    /* LE 控制命令组 */
    rq.ocf = OCF_LE_SET_RANDOM_ADDRESS;     /* 设置随机地址 */
    rq.cparam = &cp;                        /* 命令参数 */
    rq.clen = LE_SET_RANDOM_ADDRESS_CP_SIZE;/* 参数长度 */
    rq.rparam = &status;                    /* 响应缓冲 */
    rq.rlen = 1;                            /* 响应长度(仅 status 字节) */

    /* 4. 发送请求并等待响应 */
    ret = hci_send_req(dd, &rq, 1000);      /* 超时 1000ms */
    if (status || ret < 0) {
        fprintf(stderr, "Can't set random address for hci%d: %s (%d)\n",
                hdev, strerror(errno), errno);
    }

    /* 5. 关闭设备套接字 */
    hci_close_dev(dd);
}

6.3 LE 广播配置:多命令组合

cmd_le_adv 演示了多步 HCI 命令组合:

cpp 复制代码
static void cmd_le_adv(int ctl, int hdev, char *opt)
{
    struct hci_request rq;
    le_set_advertise_enable_cp advertise_cp;
    le_set_advertising_parameters_cp adv_params_cp;
    uint8_t status;
    int dd;

    dd = hci_open_dev(hdev);

    /* 第一步:设置广播参数 */
    memset(&adv_params_cp, 0, sizeof(adv_params_cp));
    adv_params_cp.min_interval = htobs(0x0800);  /* 1.28s */
    adv_params_cp.max_interval = htobs(0x0800);
    if (opt)
        adv_params_cp.advtype = atoi(opt);       /* 广播类型 */
    adv_params_cp.chan_map = 7;                  /* 三个广播信道全开 */

    memset(&rq, 0, sizeof(rq));
    rq.ogf = OGF_LE_CTL;
    rq.ocf = OCF_LE_SET_ADVERTISING_PARAMETERS;
    rq.cparam = &adv_params_cp;
    rq.clen = LE_SET_ADVERTISING_PARAMETERS_CP_SIZE;
    rq.rparam = &status;
    rq.rlen = 1;

    hci_send_req(dd, &rq, 1000);

    /* 第二步:使能广播 */
    memset(&advertise_cp, 0, sizeof(advertise_cp));
    advertise_cp.enable = 0x01;

    memset(&rq, 0, sizeof(rq));
    rq.ogf = OGF_LE_CTL;
    rq.ocf = OCF_LE_SET_ADVERTISE_ENABLE;
    rq.cparam = &advertise_cp;
    rq.clen = LE_SET_ADVERTISE_ENABLE_CP_SIZE;
    rq.rparam = &status;
    rq.rlen = 1;

    hci_send_req(dd, &rq, 1000);

    hci_close_dev(dd);
}

6.4 封装函数:hci_write_local_name 等

部分命令(name/class/version/features/sspmode)通过 lib/hci.c 提供的封装函数实现,本质上仍是 hci_send_req 的简化:

cpp 复制代码
/* lib/hci.c --- 封装函数内部实现 */
int hci_write_local_name(int dd, const char *name, int to)
{
    change_local_name_cp cp;
    memset(&cp, 0, sizeof(cp));
    strncpy(cp.name, name, sizeof(cp.name));

    /* 内部调用 hci_send_req */
    struct hci_request rq = {
        .ogf = OGF_HOST_CTL,
        .ocf = OCF_WRITE_LOCAL_NAME,
        .cparam = &cp,
        .clen = CHANGE_LOCAL_NAME_CP_SIZE,
        .rparam = NULL,
        .rlen = 0,
    };
    return hci_send_req(dd, &rq, to);
}

cmd_name 调用封装函数:

cpp 复制代码
static void cmd_name(int ctl, int hdev, char *opt)
{
    int dd = hci_open_dev(hdev);

    if (opt) {
        /* 设置名称 */
        hci_write_local_name(dd, opt, 2000);
    } else {
        /* 读取名称 */
        char name[249];
        hci_read_local_name(dd, sizeof(name), name, 1000);
        printf("\tName: '%s'\n", name);
    }

    hci_close_dev(dd);
}

6.5 两种模式对比

|----------|--------------------|------------------------------------|
| 特性 | ioctl 模式 | hci_send_req 模式 |
| 套接字 | 控制套接字 ctl(不绑定设备) | 设备套接字 dd(绑定具体 hciX) |
| 通信方式 | ioctl() 系统调用 | write() + read() |
| 协议层级 | 内核 HCI Core 级 | HCI 命令/事件级(直达控制器) |
| 命令封装 | struct hci_dev_req | struct hci_request + HCI Opcode |
| 响应处理 | ioctl 返回值 | 解析 EVT_CMD_COMPLETE/EVT_CMD_STATUS |
| 超时控制 | 无(同步返回) | 有(poll 超时) |
| 典型命令 | up/down/scan/auth | name/class/version/features/leadv |
| 适用场景 | 设备级控制 | 需要读控制器寄存器/特性的操作 |

七、设备信息打印与状态解析

7.1 print_dev_info() --- 完整信息输出

cpp 复制代码
static void print_dev_info(int ctl, struct hci_dev_info *di)
{
    struct hci_dev_stats *st = &di->stat;
    char *str;

    print_dev_hdr(di);

    /* 状态标志(UP/RUNNING/PSCAN/ISCAN/AUTH/ENCRYPT...) */
    str = hci_dflagstostr(di->flags);
    printf("\t%s\n", str);
    bt_free(str);

    /* 收发统计 */
    printf("\tRX bytes:%d acl:%d sco:%d events:%d errors:%d\n",
        st->byte_rx, st->acl_rx, st->sco_rx, st->evt_rx, st->err_rx);
    printf("\tTX bytes:%d acl:%d sco:%d commands:%d errors:%d\n",
        st->byte_tx, st->acl_tx, st->sco_tx, st->cmd_tx, st->err_tx);

    /* -a 详细模式:额外打印 features/ptype/lp/lm/name/class/version */
    if (all && !hci_test_bit(HCI_RAW, &di->flags)) {
        print_dev_features(di, 0);

        if (((di->type & 0x30) >> 4) == HCI_PRIMARY) {
            print_pkt_type(di);
            print_link_policy(di);
            print_link_mode(di);

            if (hci_test_bit(HCI_UP, &di->flags)) {
                cmd_name(ctl, di->dev_id, NULL);   /* 读取名称 */
                cmd_class(ctl, di->dev_id, NULL);  /* 读取设备类 */
            }
        }

        if (hci_test_bit(HCI_UP, &di->flags))
            cmd_version(ctl, di->dev_id, NULL);    /* 读取版本 */
    }
}

7.2 print_dev_hdr() --- 头部输出

cpp 复制代码
static void print_dev_hdr(struct hci_dev_info *di)
{
    static int hdr = -1;
    char addr[18];

    /* 同一设备只打印一次头部 */
    if (hdr == di->dev_id)
        return;
    hdr = di->dev_id;

    ba2str(&di->bdaddr, addr);

    printf("%s:\tType: %s  Bus: %s\n", di->name,
           hci_typetostr((di->type & 0x30) >> 4),  /* Primary / AMP */
           hci_bustostr(di->type & 0x0f));          /* USB / UART / SDIO... */

    printf("\tBD Address: %s  ACL MTU: %d:%d  SCO MTU: %d:%d\n",
           addr, di->acl_mtu, di->acl_pkts, di->sco_mtu, di->sco_pkts);
}

7.3 设备类型字段解析

cpp 复制代码
di->type 是一个 uint8_t,编码了两部分信息:

  bit 4-5: 控制器类型
    0x00 → HCI_PRIMARY(主控制器,BR/EDR + LE)
    0x10 → HCI_AMP(AMP 控制器)

  bit 0-3: 总线类型
    0x00 → HCI_VIRTUAL
    0x01 → HCI_USB
    0x02 → HCI_PCCARD
    0x03 → HCI_UART
    0x04 → HCI_RS232
    0x05 → HCI_PCI
    0x06 → HCI_SDIO

八、hciconfig vs bluetoothctl 功能差异

8.1 架构对比

8.2 功能对比表

|-------------------|---------------------------------------|-------------------------------------|
| 维度 | hciconfig | bluetoothctl |
| 通信方式 | socket + ioctl + hci_send_req | D-Bus → bluetoothd |
| 依赖 bluetoothd | 不依赖 | 强依赖 |
| 权限 | root(CAP_NET_ADMIN) | root 或 policykit 授权 |
| 操作层级 | HCI 控制器级 | Profile/Service 级 |
| 设备管理 | 仅控制器(hciX) | 控制器 + 设备 + GATT + Profile |
| 配对/连接 | 不支持 | 完整支持 |
| GATT 读写 | 不支持 | 完整支持 |
| Agent 交互 | 不支持 | 完整支持 |
| 广播配置 | 基础(leadv/noleadv) | 完整(UUID/Service/Manufacturer/Data) |
| MAC 修改 | 支持(lerandaddr) | 支持(lerandaddr) |
| 硬件调试 | 强(features/commands/version/revision) | 弱(仅 show) |
| 扫描参数 | 基础(piscan/iscan/pscan) | 丰富(UUID/RSSI/Pathloss/Transport 过滤) |
| 运行模式 | 单次执行 | 交互式 shell + 非交互 |
| 维护状态 | 已废弃(维护中,不新增功能) | 活跃开发 |

8.3 使用场景选择

九、常见报错与解决方案

9.1 套接字创建失败

cpp 复制代码
$ hciconfig
Can't open HCI socket.: Address family not supported by protocol

根因:内核未启用蓝牙支持。

解决:

cpp 复制代码
# 检查内核配置
zcat /proc/config.gz | grep CONFIG_BT
# 需要 CONFIG_BT=y 或 m

# 加载蓝牙模块
sudo modprobe bluetooth
sudo modprobe hci_uart    # UART 接口
sudo modprobe btusb       # USB 接口

9.2 设备初始化失败

cpp 复制代码
$ hciconfig hci0 up
Can't init device hci0: Operation not possible due to RF-kill (132)

根因:蓝牙被 rfkill 软开关禁用。

解决:

cpp 复制代码
# 查看 rfkill 状态
rfkill list

# 解除蓝牙软阻止
sudo rfkill unblock bluetooth

9.3 权限不足

cpp 复制代码
$ hciconfig hci0 up
Can't init device hci0: Operation not permitted (1)

根因:非 root 用户,缺少 CAP_NET_ADMIN。

解决:

cpp 复制代码
sudo hciconfig hci0 up

# 或设置 capability
sudo setcap cap_net_admin+ep /usr/bin/hciconfig

9.4 设备被占用

cpp 复制代码
$ hciconfig hci0 down
Can't down device hci0: Device or resource busy (16)

根因:bluetoothd 正在使用设备,或有活跃连接。

解决:

cpp 复制代码
# 停止 bluetoothd
sudo systemctl stop bluetooth

# 再操作
sudo hciconfig hci0 down

9.5 HCI 命令超时

cpp 复制代码
$ hciconfig hci0 version
Can't read version info hci0: Connection timed out (110)

根因:控制器未响应 HCI 命令(固件异常/USB 接触不良/电源问题)。

解决:

cpp 复制代码
# 1. 重置设备
sudo hciconfig hci0 reset

# 2. 物理重插 USB 蓝牙适配器

# 3. 检查 dmesg
dmesg | tail -50

# 4. 重新加载驱动
sudo rmmod btusb && sudo modprobe btusb

9.6 设置名称/类无效

cpp 复制代码
$ hciconfig hci0 name "Test"
# 设置成功,但 hciconfig hci0 name 显示旧名称

根因:bluetoothd 会覆盖 hciconfig 的设置(bluetoothd 管理名称和设备类)。

解决:停止 bluetoothd 后再操作,或使用 bluetoothctl 的 system-alias / advertise 命令。

十、嵌入式调试技巧

10.1 无 bluetoothd 环境下使用 hciconfig

在嵌入式设备上,常需要在没有 bluetoothd 的环境下进行蓝牙调试:

cpp 复制代码
# 1. 确保内核蓝牙模块已加载
lsmod | grep bluetooth

# 2. 加载 HCI 驱动
# UART 蓝牙模块
sudo hciattach /dev/ttyS1 texas 115200
# 或
sudo hciattach /dev/ttyS1 bcm43xx 921600

# 3. 上电
hciconfig hci0 up

# 4. 配置基本参数
hciconfig hci0 piscan
hciconfig hci0 name "Embedded-BT"
hciconfig hci0 class 0x1c010c

# 5. 验证
hciconfig -a hci0

10.2 硬件能力探测脚本

cpp 复制代码
#!/bin/bash
# hw_probe.sh --- 蓝牙硬件能力探测

DEV=${1:-hci0}

echo "=== Device Info ==="
hciconfig -a $DEV | head -10

echo "=== Version ==="
hciconfig $DEV version

echo "=== Features ==="
hciconfig $DEV features

echo "=== Supported Commands ==="
hciconfig $DEV commands

echo "=== LE States ==="
hciconfig $DEV lestates

echo "=== SSP Mode ==="
hciconfig $DEV sspmode

echo "=== Revision ==="
hciconfig $DEV revision

10.3 btmon 配合抓包

cpp 复制代码
# 启动 btmon 监控 HCI 流量
sudo btmon &

# 执行 hciconfig 命令
hciconfig hci0 leadv 0
hciconfig hci0 version

# btmon 输出示例
< HCI Command: LE Set Advertising Parameters (0x08|0x0006) plen 15
> HCI Event: Command Complete (0x0e) plen 4
  LE Set Advertising Parameters (0x08|0x0006) ncmd 1
  Status: Success (0x00)

10.4 strace 追踪系统调用

cpp 复制代码
# 追踪 ioctl 和 socket 调用
sudo strace -e trace=socket,ioctl,read,write,close \
    -f hciconfig hci0 version

# 关键输出:
# socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI) = 3    # 控制套接字
# ioctl(3, HCIGETDEVINFO, ...) = 0                     # 获取设备信息
# socket(AF_BLUETOOTH, SOCK_RAW|SOCK_CLOEXEC, BTPROTO_HCI) = 4  # 设备套接字
# bind(4, {sa_family=AF_BLUETOOTH, ...}, 6) = 0       # 绑定 hci0
# getsockopt(4, SOL_HCI, HCI_FILTER, ...) = 0         # 获取旧过滤器
# setsockopt(4, SOL_HCI, HCI_FILTER, ...) = 0         # 设置新过滤器
# write(4, "\1\4\10", 3) = 3                           # 发送 HCI 命令
# poll([{fd=4, events=POLLIN}], 1, 1000) = 1           # 等待响应
# read(4, "\4\16\4\1\1\10", 258) = 6                   # 读取事件

10.5 直接读取 /sys 信息

无需 hciconfig,直接从 sysfs 读取部分信息:

cpp 复制代码
# 设备地址
cat /sys/class/bluetooth/hci0/address

# 设备类型
cat /sys/class/bluetooth/hci0/type

# 设备名
cat /sys/class/bluetooth/hci0/name

# 电源状态
cat /sys/class/bluetooth/hci0/power/control

# 厂商信息
cat /sys/class/bluetooth/hci0/device/manufacturer
cat /sys/class/bluetooth/hci0/device/idVendor
cat /sys/class/bluetooth/hci0/device/idProduct

十一、核心命令 → 源码函数 → 底层机制速查

|-----------------------------|------------------|-----------------------------------------------------------------|-------------|
| hciconfig 命令 | 源码函数 | 底层机制 | 通信层级 |
| up | cmd_up | ioctl(ctl, HCIDEVUP, hdev) | 内核 HCI Core |
| down | cmd_down | ioctl(ctl, HCIDEVDOWN, hdev) | 内核 HCI Core |
| reset | cmd_reset | HCIDEVDOWN + HCIDEVUP | 内核 HCI Core |
| rstat | cmd_rstat | ioctl(ctl, HCIDEVRESTAT, hdev) | 内核 HCI Core |
| piscan/iscan/pscan/noscan | cmd_scan | ioctl(ctl, HCISETSCAN, &dr) | 内核 HCI Core |
| auth/noauth | cmd_auth | ioctl(ctl, HCISETAUTH, &dr) | 内核 HCI Core |
| encrypt/noencrypt | cmd_encrypt | ioctl(ctl, HCISETENCRYPT, &dr) | 内核 HCI Core |
| ptype | cmd_ptype | ioctl(ctl, HCISETPTYPE, &dr) | 内核 HCI Core |
| lp | cmd_lp | ioctl(ctl, HCISETLINKPOL, &dr) | 内核 HCI Core |
| lm | cmd_lm | ioctl(ctl, HCISETLINKMODE, &dr) | 内核 HCI Core |
| block | cmd_block | ioctl(dd, HCIBLOCKADDR, &bdaddr) | 内核 HCI Core |
| name | cmd_name | hci_write_local_name / hci_read_local_name | HCI 命令 |
| class | cmd_class | hci_write_class_of_dev / hci_read_class_of_dev | HCI 命令 |
| version | cmd_version | hci_read_local_version | HCI 命令 |
| features | cmd_features | hci_read_local_ext_features | HCI 命令 |
| commands | cmd_commands | hci_read_local_commands | HCI 命令 |
| sspmode | cmd_ssp_mode | hci_write_simple_pairing_mode | HCI 命令 |
| voice | cmd_voice | hci_write_voice_setting / hci_read_voice_setting | HCI 命令 |
| lerandaddr | cmd_le_addr | hci_send_req(OGF_LE_CTL, OCF_LE_SET_RANDOM_ADDRESS) | HCI 命令 |
| leadv | cmd_le_adv | hci_send_req(OCF_LE_SET_ADV_PARAMS) + OCF_LE_SET_ADV_ENABLE | HCI 命令 |
| noleadv | cmd_no_le_adv | hci_send_req(OCF_LE_SET_ADV_ENABLE, enable=0) | HCI 命令 |
| lestates | cmd_le_states | hci_read_le_host_supported | HCI 命令 |
| revision | cmd_revision | hci_send_req(厂商相关) | HCI 命令 |
| 无命令(默认) | print_dev_info | ioctl(ctl, HCIGETDEVINFO) | 内核 HCI Core |
| 无参数(列表) | print_dev_list | ioctl(ctl, HCIGETDEVLIST) + HCIGETDEVINFO | 内核 HCI Core |

十二、总结

12.1 核心设计要点

  1. 单文件实现:hciconfig 全部逻辑集中在 tools/hciconfig.c 一个文件中,结构清晰,约 2000 行代码。

  2. 命令表驱动:所有命令通过静态 command\[\] 数组注册,main() 线性查找匹配后调用对应 cmd_* 函数。这种模式简单、高效,适合命令数量适中的工具。

  3. 双层通信:

  • ioctl 层(控制套接字):设备级控制(up/down/scan/auth),直接配置内核 HCI Core 状态。

  • HCI 命令层(设备套接字):控制器级操作(name/class/version/features),通过 hci_send_req 收发 HCI 命令/事件包。

  1. 无 D-Bus 依赖:hciconfig 直接与内核交互,不需要 bluetoothd 守护进程,适合嵌入式环境和底层调试。

  2. 函数复用:cmd_scan 被 piscan/iscan/pscan/noscan 复用,cmd_auth 被 auth/noauth 复用,通过 opt 参数区分行为。

12.2 与 bluetoothctl 的定位差异

  • hciconfig = 蓝牙世界的 ifconfig:操作网络接口(HCI 设备)的底层工具,关注硬件级配置。

  • bluetoothctl = 蓝牙世界的网络管理器:通过 bluetoothd 管理完整的蓝牙服务栈,关注应用级交互。

两者互补而非替代:hciconfig 适合硬件调试和无守护进程环境,bluetoothctl 适合日常使用和完整蓝牙功能。

12.3 维护状态说明

hciconfig 在 BlueZ 中标记为 deprecated(不推荐使用),但并未移除,仍然维护。官方推荐使用 bluetoothctl 和 hcitool(也已 deprecated)的替代品。但在以下场景中 hciconfig 仍不可替代:

  • 无 bluetoothd 的嵌入式环境

  • 需要直接修改 MAC 地址(lerandaddr)

  • 需要查询底层硬件能力(features/commands/lestates)

  • 内核驱动开发调试


附录:核心源码文件索引

|-----------------------|---------------------------------------------------------------------|
| 文件 | 关键内容 |
| tools/hciconfig.c | 全部源码:main()、命令表、各 cmd_* 函数、print_dev_info |
| lib/hci.h | ioctl 码定义、struct hci_dev_info / hci_dev_req / hci_request |
| lib/hci.c | hci_open_dev / hci_send_req / hci_send_cmd / hci_read_local_version |
| lib/hci_lib.h | struct hci_request、hci_open_dev / hci_send_req 等函数声明 |
| lib/bluetooth.h | bdaddr_t、ba2str、str2ba 基础定义 |
| doc/hciconfig.1 | 手册页 |

参考文献

  • BlueZ Source Code: tools/hciconfig.c, lib/hci.c, lib/hci.h

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

  • Bluetooth Core Specification Version 5.3: Volume 2, Part E (HCI Commands and Events)

  • Linux ioctl Documentation: Documentation/userspace-api/ioctl/ioctl-decoding.rst


相关推荐
SKH.1 小时前
Linux驱动-系统移植
linux·服务器
根目录下的猫1 小时前
虚拟机复制过来运行时:“虚拟机使用的此版本,VMware Workstation 不支持的硬件版本。”错误解决办法,亲测可用
linux·运维·服务器·后端
yunwei371 小时前
eBPF 入门实践教程十七:编写 eBPF 程序统计随机/顺序磁盘 I/O
linux·后端·性能优化
帷幕落秋2 小时前
内存、虚拟内存与 OOM
linux·运维
dingdingfish2 小时前
TLPI 第41章 读书笔记:Fundamentals of Shared Libraries
linux·static·link·dynamic·library·tlpi
Apipi*2 小时前
30天速通Linux 第五章Linux 进程管理
linux·运维·服务器
杨云龙UP2 小时前
PostgreSQL 四种常见部署模式快速理解:单实例、流复制、repmgr、Patroni
linux·运维·数据库·postgresql·流复制·高可用ha
流浪0012 小时前
Linux系统篇46——线程(十一) 生产者消费者模型,三种关系和一个交易场所
linux·线程·并发·同步·互斥·阻塞队列·生产者消费者模型
Jason_zhao_MR3 小时前
新一代电能数据采集终端方案
linux·人工智能·嵌入式硬件·fpga开发·嵌入式