一、代码分析
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选项,首先经过解析,执行到第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也是能输出一部分内容的。
重要总结
从整体流程来看,一个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选项输出打印如下
从程序上看,如果是-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