hciconfig代码分析及应用

一、代码分析

hciconfig的main()函数主体如下,可分为两部分进行解析,一个常用选项参数分析,一个指令分析。

int main(int argc, char *argv[])
{
    int opt, ctl, i, cmd = 0;
/**************************************************************************************/
//第一部分 常用选项参数分析
/**************************************************************************************/
    while ((opt = getopt_long(argc, argv, "ah", main_options, NULL)) != -1) {
        switch (opt) {
        case 'a':
            all = 1;
            break;

        case 'h':
        default:
            usage();
            exit(0);
        }
    }

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

    /* Open HCI socket  */
    if ((ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)) < 0) {
        perror("Can't open HCI socket.");
        exit(1);
    }

    if (argc < 1) {
        print_dev_list(ctl, 0);
        exit(0);
    }

/**************************************************************************************/
//第二部分 指令分析
/**************************************************************************************/
    di.dev_id = atoi(argv[0] + 3);
    argc--; argv++;

    if (ioctl(ctl, HCIGETDEVINFO, (void *) &di)) {
        perror("Can't get device info");
        exit(1);
    }

    while (argc > 0) {
        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);
            cmd = 1;
            break;
        }

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

        argc--; argv++;
    }

    if (!cmd)
        print_dev_info(ctl, &di);

    close(ctl);
    return 0;
}

1、常用选项参数分析

通过getopt_long()函数解析两个选项,一个-a(–all),一个-h(–help)。

static struct option main_options[] = {
    { "help",   0, 0, 'h' },
    { "all",    0, 0, 'a' },
    { 0, 0, 0, 0 }
};

(1)-a(–all)选项

先看hciconfig -a执行的输出结果打印,通过打印信息分析程序。
a

程序流程上,如果是-a选项,首先经过解析,执行到第10行执行解析all = 1,将all参数标志位置1,然后调用socket()函数打开hci 通信,然后执行31行的print_dev_list(ctl, 0)函数,然后结束程序,从这里看,hciconfig -a输出的打印是从print_dev_list(ctl, 0)函数输出的。
print_dev_list()函数

static void print_dev_list(int ctl, int flags)
{
    struct hci_dev_list_req *dl;
    struct hci_dev_req *dr;
    int i;
    // #define HCI_MAX_DEV 16
    //分配内存,假设hci设备最大次数16个
    if (!(dl = malloc(HCI_MAX_DEV * sizeof(struct hci_dev_req) +
        sizeof(uint16_t)))) {
        perror("Can't allocate memory");
        exit(1);
    }
    dl->dev_num = HCI_MAX_DEV;
    dr = dl->dev_req;
    //通过ioctl()函数获取hci设备列表
    if (ioctl(ctl, HCIGETDEVLIST, (void *) dl) < 0) {
        perror("Can't get device list");
        free(dl);
        exit(1);
    }
    //遍历获取到的hci设备列表,并获取每个hci设备的信息
    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);/*传第hci信息结构di*/ /*struct hci_dev_info *di*/
    }

    free(dl);
}

代码分析,print_dev_list()函数主要实现是分配hci设备内存,获取hci设备列表,并获取每个hci设备的信息,其中调用print_dev_info()函数,还没看到跟hciconfig -a输出打印相关的代码,继续看print_dev_info()函数。

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);

    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);

    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);
    }

    printf("\n");
}

从print_dev_info()函数分析已经看到跟hciconfig -a输出内容的代码,继续深入print_dev_info()函数里的各个函数,也是各种解析,对比输出内容一目了然。
这里第18行有个all参数,就是前面提到的标志,如果没有这个标志就不解析一部分内容,所以-a选项的作用在此。
所以单纯输入hciconfig也是能输出一部分内容的。
b

重要总结
从整体流程来看,一个hci设备用一个hci_dev_info结构体就能描述出它的所有信息。

struct hci_dev_info {
    uint16_t dev_id;
    char     name[8];

    bdaddr_t bdaddr;

    uint32_t flags;
    uint8_t  type;

    uint8_t  features[8];

    uint32_t pkt_type;
    uint32_t link_policy;
    uint32_t link_mode;

    uint16_t acl_mtu;
    uint16_t acl_pkts;
    uint16_t sco_mtu;
    uint16_t sco_pkts;

    struct   hci_dev_stats stat;
};

hciconfig -a输出信息分析

hci0:   Type: BR/EDR  Bus: UART//蓝牙的接口类型,这个是UART的,还有USB、PCI…………  
        BD Address: 00:16:53:96:22:53  ACL MTU: 1021:8  SCO MTU: 120:10//蓝牙地址  
        UP RUNNING PSCAN//命令状态  
        RX bytes:2846 acl:0 sco:0 events:67 errors:0  
        TX bytes:2034 acl:0 sco:0 commands:80 errors:0  
        Features: 0xff 0xff 0x8d 0xfe 0x9b 0xbf 0x79 0x83  
        Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3//支持数据包  
        Link policy: RSWITCH HOLD SNIFF PARK  
        Link mode: SLAVE ACCEPT//连接的类型,是主设备、从设备  
        Name: 'sp8825c1'//蓝牙名称  
        Class: 0x5a020c//蓝牙的类型  
        Service Classes: Networking, Capturing, Object Transfer, Telephony//支持的类型  
        Device Class: Phone, Smart phone  
        HCI Version: 2.1 (0x4)  Revision: 0x1250//HCI版本  
        LMP Version: 2.1 (0x4)  Subversion: 0x1250//LMP版本  
        Manufacturer: Ericsson Technology Licensing (0)//作者 

(2)-h(–help)选项

-h选项输出打印如下
c
从程序上看,如果是-h选项,执行完usage()函数就会退出。

static void usage(void)
{
    int i;

    printf("hciconfig - HCI device configuration utility\n");
    printf("Usage:\n"
        "\thciconfig\n"
        "\thciconfig [-a] hciX [command ...]\n");
    printf("Commands:\n");
    for (i = 0; command[i].cmd; i++)
        printf("\t%-10s %-8s\t%s\n", command[i].cmd,
        command[i].opt ? command[i].opt : " ",
        command[i].doc);
}

分析usage()函数主要是打印command命令

static struct {
    char *cmd;
    void (*func)(int ctl, int hdev, char *opt);
    char *opt;
    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" },
    { "rstat",  cmd_rstat,  0,      "Reset statistic counters" },
    { "auth",   cmd_auth,   0,      "Enable Authentication" },
    { "noauth", cmd_auth,   0,      "Disable Authentication" },
    { "encrypt",    cmd_encrypt,    0,      "Enable Encryption" },
    { "noencrypt",  cmd_encrypt,    0,      "Disable Encryption" },
    { "piscan", cmd_scan,   0,      "Enable Page and Inquiry scan" },
    { "noscan", cmd_scan,   0,      "Disable scan" },
    { "iscan",  cmd_scan,   0,      "Enable Inquiry scan" },
    { "pscan",  cmd_scan,   0,      "Enable Page scan" },
    { "ptype",  cmd_ptype,  "[type]",   "Get/Set default packet type" },
    { "lm",     cmd_lm,     "[mode]",   "Get/Set default link mode"   },
    { "lp",     cmd_lp,     "[policy]", "Get/Set default link policy" },
    { "name",   cmd_name,   "[name]",   "Get/Set local name" },
    { "class",  cmd_class,  "[class]",  "Get/Set class of device" },
    { "voice",  cmd_voice,  "[voice]",  "Get/Set voice setting" },
    { "iac",    cmd_iac,    "[iac]",    "Get/Set inquiry access code" },
    { "inqtpl", cmd_inq_tpl,    "[level]",  "Get/Set inquiry transmit power level" },
    { "inqmode",    cmd_inq_mode,   "[mode]",   "Get/Set inquiry mode" },
    { "inqdata",    cmd_inq_data,   "[data]",   "Get/Set inquiry data" },
    { "inqtype",    cmd_inq_type,   "[type]",   "Get/Set inquiry scan type" },
    { "inqparms",   cmd_inq_parms,  "[win:int]",    "Get/Set inquiry scan window and interval" },
    { "pageparms",  cmd_page_parms, "[win:int]",    "Get/Set page scan window and interval" },
    { "pageto", cmd_page_to,    "[to]",     "Get/Set page timeout" },
    { "afhmode",    cmd_afh_mode,   "[mode]",   "Get/Set AFH mode" },
    { "sspmode",    cmd_ssp_mode,   "[mode]",   "Get/Set Simple Pairing Mode" },
    { "aclmtu", cmd_aclmtu, "<mtu:pkt>",    "Set ACL MTU and number of packets" },
    { "scomtu", cmd_scomtu, "<mtu:pkt>",    "Set SCO MTU and number of packets" },
    { "delkey", cmd_delkey, "<bdaddr>", "Delete link key from the device" },
    { "oobdata",    cmd_oob_data,   0,      "Get local OOB data" },
    { "commands",   cmd_commands,   0,      "Display supported commands" },
    { "features",   cmd_features,   0,      "Display device features" },
    { "version",    cmd_version,    0,      "Display version information" },
    { "revision",   cmd_revision,   0,      "Display revision information" },
    { "block",  cmd_block,  "<bdaddr>", "Add a device to the blacklist" },
    { "unblock",    cmd_unblock,    "<bdaddr>", "Remove a device from the blacklist" },
    { "lerandaddr", cmd_le_addr,    "<bdaddr>", "Set LE Random Address" },
    { "leadv",  cmd_le_adv, "[type]",   "Enable LE advertising"
        "\n\t\t\t0 - Connectable undirected advertising (default)"
        "\n\t\t\t3 - Non connectable undirected advertising"},
    { "noleadv",    cmd_no_le_adv,  0,      "Disable LE advertising" },
    { "lestates",   cmd_le_states,  0,      "Display the supported LE states" },
    { NULL, NULL, 0 }
};

2、指令参数分析

第38行代码 di.dev_id = atoi(argv[0] + 3);解析hci设备号,比如hci0,hci1, hci2…
接下来是通过循环对比字符串,然后调用commond结构体数组的func,即command[i].func(ctl, di.dev_id, *argv);

二、常用操作

#查看全部的蓝牙设备信息
hciconfig -a
#开启蓝牙设备
hciconfig hci0 up
#关闭蓝牙设备
hciconfig hci0 down
#重置蓝牙设备
hciconfig hci0 reset

参考链接
https://blog.csdn.net/wangzhen209/article/details/50248813

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值