diff options
-rw-r--r-- | tools/hcitool.c | 572 |
1 files changed, 269 insertions, 303 deletions
diff --git a/tools/hcitool.c b/tools/hcitool.c index 9c4dc468..9ac890fd 100644 --- a/tools/hcitool.c +++ b/tools/hcitool.c @@ -142,6 +142,140 @@ static void hex_dump(char *pref, int width, unsigned char *buf, int len) printf("\n"); } +static char *get_minor_device_name(int major, int minor) +{ + switch (major) { + case 0: /* misc */ + return ""; + case 1: /* computer */ + switch(minor) { + case 0: + return "Uncategorized"; + case 1: + return "Desktop workstation"; + case 2: + return "Server"; + case 3: + return "Laptop"; + case 4: + return "Handheld"; + case 5: + return "Palm"; + case 6: + return "Wearable"; + } + break; + case 2: /* phone */ + switch(minor) { + case 0: + return "Uncategorized"; + case 1: + return "Cellular"; + case 2: + return "Cordless"; + case 3: + return "Smart phone"; + case 4: + return "Wired modem or voice gateway"; + case 5: + return "Common ISDN Access"; + case 6: + return "Sim Card Reader"; + } + break; + case 3: /* lan access */ + if (minor == 0) + return "Uncategorized"; + switch(minor / 8) { + case 0: + return "Fully available"; + case 1: + return "1-17% utilized"; + case 2: + return "17-33% utilized"; + case 3: + return "33-50% utilized"; + case 4: + return "50-67% utilized"; + case 5: + return "67-83% utilized"; + case 6: + return "83-99% utilized"; + case 7: + return "No service available"; + } + break; + case 4: /* audio/video */ + switch(minor) { + case 0: + return "Uncategorized"; + case 1: + return "Device conforms to the Headset profile"; + case 2: + return "Hands-free"; + /* 3 is reserved */ + case 4: + return "Microphone"; + case 5: + return "Loudspeaker"; + case 6: + return "Headphones"; + case 7: + return "Portable Audio"; + case 8: + return "Car Audio"; + case 9: + return "Set-top box"; + case 10: + return "HiFi Audio Device"; + case 11: + return "VCR"; + case 12: + return "Video Camera"; + case 13: + return "Camcorder"; + case 14: + return "Video Monitor"; + case 15: + return "Video Display and Loudspeaker"; + case 16: + return "Video Conferencing"; + /* 17 is reserved */ + case 18: + return "Gaming/Toy"; + } + break; + case 5: /* peripheral */ + switch(minor) { + case 16: + return "Keyboard"; + case 32: + return "Pointing device"; + case 48: + return "Combo keyboard/pointing device"; + } + break; + case 6: /* imaging */ + if (minor & 4) + return "Display"; + if (minor & 8) + return "Camera"; + if (minor & 16) + return "Scanner"; + if (minor & 32) + return "Printer"; + break; + case 63: /* uncategorised */ + return ""; + } + return "Unknown (reserved) minor device class"; +} + +static char *major_classes[] = { + "Miscellaneous", "Computer", "Phone", "LAN Access", + "Audio/Video", "Peripheral", "Imaging", "Uncategorized" +}; + /* Display local devices */ static struct option dev_options[] = { @@ -240,20 +374,31 @@ static struct option scan_options[] = { { "length", 1, 0, 'l' }, { "numrsp", 1, 0, 'n' }, { "flush", 0, 0, 'f' }, + { "class", 0, 0, 'c' }, + { "info", 0, 0, 'i' }, + { "oui", 0, 0, 'o' }, + { "all", 0, 0, 'a' }, + { "ext", 0, 0, 'a' }, { 0, 0, 0, 0 } }; static char *scan_help = "Usage:\n" - "\tscan [--length=N] [--numrsp=N] [--flush]\n"; + "\tscan [--length=N] [--numrsp=N] [--flush] [--class] [--info] [--oui]\n"; static void cmd_scan(int dev_id, int argc, char **argv) { inquiry_info *info = NULL; int num_rsp, length, flags; - char addr[18]; - char name[249]; - int i, opt, dd; + uint8_t cls[3]; + uint16_t handle; + char addr[18], name[249], oui[9], *comp; + unsigned char features[8]; + struct hci_version version; + struct hci_dev_info di; + struct hci_conn_info_req *cr; + int extcls = 0, extinf = 0, extoui = 0; + int i, opt, dd, cc; length = 8; /* ~10 seconds */ num_rsp = 100; @@ -273,6 +418,24 @@ static void cmd_scan(int dev_id, int argc, char **argv) flags |= IREQ_CACHE_FLUSH; break; + case 'c': + extcls = 1; + break; + + case 'i': + extinf = 1; + break; + + case 'o': + extoui = 1; + break; + + case 'a': + extcls = 1; + extinf = 1; + extoui = 1; + break; + default: printf(scan_help); return; @@ -287,6 +450,11 @@ static void cmd_scan(int dev_id, int argc, char **argv) } } + if (hci_devinfo(dev_id, &di) < 0) { + perror("Can't get device info"); + exit(1); + } + printf("Scanning ...\n"); num_rsp = hci_inquiry(dev_id, length, num_rsp, NULL, &info, flags); if (num_rsp < 0) { @@ -301,15 +469,110 @@ static void cmd_scan(int dev_id, int argc, char **argv) exit(1); } + if (extcls || extinf || extoui) + printf("\n"); + for (i = 0; i < num_rsp; i++) { + if (!extcls && !extinf && !extoui) { + memset(name, 0, sizeof(name)); + if (hci_read_remote_name_with_clock_offset(dd, + &(info+i)->bdaddr, + (info+i)->pscan_rep_mode, + (info+i)->clock_offset | 0x8000, + sizeof(name), name, 100000) < 0) + strcpy(name, "n/a"); + ba2str(&(info+i)->bdaddr, addr); + printf("\t%s\t%s\n", addr, name); + continue; + } + + ba2str(&(info+i)->bdaddr, addr); + printf("BD Address:\t%s [mode %d, clkoffset 0x%4.4x]\n", addr, + (info+i)->pscan_rep_mode, btohs((info+i)->clock_offset)); + + if (extoui) { + ba2oui(&(info+i)->bdaddr, oui); + comp = ouitocomp(oui); + if (comp) { + printf("OUI company:\t%s (%s)\n", comp, oui); + free(comp); + } + } + + cc = 0; + + if (extinf) { + cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); + if (cr) { + bacpy(&cr->bdaddr, &(info+i)->bdaddr); + cr->type = ACL_LINK; + if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) { + handle = 0; + cc = 1; + } else { + handle = htobs(cr->conn_info->handle); + cc = 0; + } + free(cr); + } + + if (cc) { + if (hci_create_connection(dd, &(info+i)->bdaddr, + htobs(di.pkt_type & ACL_PTYPE_MASK), + (info+i)->clock_offset | 0x8000, + 0x01, &handle, 25000) < 0) { + handle = 0; + cc = 0; + } + } + } + memset(name, 0, sizeof(name)); if (hci_read_remote_name_with_clock_offset(dd, &(info+i)->bdaddr, (info+i)->pscan_rep_mode, (info+i)->clock_offset | 0x8000, sizeof(name), name, 100000) < 0) strcpy(name, "n/a"); - ba2str(&(info+i)->bdaddr, addr); - printf("\t%s\t%s\n", addr, name); + printf("Device name:\t%s\n", name); + + if (extcls) { + memcpy(cls, (info+i)->dev_class, 3); + printf("Device class:\t"); + if ((cls[1] & 0x1f) > sizeof(*major_classes)) + printf("Invalid"); + else + printf("%s, %s", major_classes[cls[1] & 0x1f], + get_minor_device_name(cls[1] & 0x1f, cls[0] >> 2)); + printf(" (0x%2.2x%2.2x%2.2x)\n", cls[2], cls[1], cls[0]); + } + + if (extinf && handle > 0) { + if (hci_read_remote_version(dd, handle, &version, 20000) == 0) { + printf("Manufacturer:\t%s (%d)\n", + bt_compidtostr(version.manufacturer), + version.manufacturer); + printf("LMP version:\t%s (0x%x) [subver 0x%x]\n", + lmp_vertostr(version.lmp_ver), + version.lmp_ver, version.lmp_subver); + } + + if (hci_read_remote_features(dd, handle, features, 20000) == 0) { + printf("LMP features:\t0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x" + " 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", + features[0], features[1], + features[2], features[3], + features[4], features[5], + features[6], features[7]); + printf("%s\n", lmp_featurestostr(features, "\t\t", 63)); + } + + if (cc) { + usleep(10000); + hci_disconnect(dd, handle, HCI_OE_USER_ENDED_CONNECTION, 10000); + } + } + + printf("\n"); } close(dd); @@ -491,302 +754,6 @@ static void cmd_info(int dev_id, int argc, char **argv) close(dd); } -/* Find remote devices */ - -static struct option find_options[] = { - { "help", 0, 0, 'h' }, - { "length", 1, 0, 'l' }, - { "numrsp", 1, 0, 'n' }, - { "flush", 0, 0, 'f' }, - { 0, 0, 0, 0 } -}; - -static char *find_help = - "Usage:\n" - "\tfind [--length=N] [--numrsp=N] [--flush]\n"; - -static char *get_minor_device_name(int major, int minor) -{ - switch (major) { - case 0: /* misc */ - return ""; - case 1: /* computer */ - switch(minor) { - case 0: - return "Uncategorized"; - case 1: - return "Desktop workstation"; - case 2: - return "Server"; - case 3: - return "Laptop"; - case 4: - return "Handheld"; - case 5: - return "Palm"; - case 6: - return "Wearable"; - } - break; - case 2: /* phone */ - switch(minor) { - case 0: - return "Uncategorized"; - case 1: - return "Cellular"; - case 2: - return "Cordless"; - case 3: - return "Smart phone"; - case 4: - return "Wired modem or voice gateway"; - case 5: - return "Common ISDN Access"; - case 6: - return "Sim Card Reader"; - } - break; - case 3: /* lan access */ - if (minor == 0) - return "Uncategorized"; - switch(minor / 8) { - case 0: - return "Fully available"; - case 1: - return "1-17% utilized"; - case 2: - return "17-33% utilized"; - case 3: - return "33-50% utilized"; - case 4: - return "50-67% utilized"; - case 5: - return "67-83% utilized"; - case 6: - return "83-99% utilized"; - case 7: - return "No service available"; - } - break; - case 4: /* audio/video */ - switch(minor) { - case 0: - return "Uncategorized"; - case 1: - return "Device conforms to the Headset profile"; - case 2: - return "Hands-free"; - /* 3 is reserved */ - case 4: - return "Microphone"; - case 5: - return "Loudspeaker"; - case 6: - return "Headphones"; - case 7: - return "Portable Audio"; - case 8: - return "Car Audio"; - case 9: - return "Set-top box"; - case 10: - return "HiFi Audio Device"; - case 11: - return "VCR"; - case 12: - return "Video Camera"; - case 13: - return "Camcorder"; - case 14: - return "Video Monitor"; - case 15: - return "Video Display and Loudspeaker"; - case 16: - return "Video Conferencing"; - /* 17 is reserved */ - case 18: - return "Gaming/Toy"; - } - break; - case 5: /* peripheral */ - switch(minor) { - case 16: - return "Keyboard"; - case 32: - return "Pointing device"; - case 48: - return "Combo keyboard/pointing device"; - } - break; - case 6: /* imaging */ - if (minor & 4) - return "Display"; - if (minor & 8) - return "Camera"; - if (minor & 16) - return "Scanner"; - if (minor & 32) - return "Printer"; - break; - case 63: /* uncategorised */ - return ""; - } - return "Unknown (reserved) minor device class"; -} - -static char *major_classes[] = { - "Miscellaneous", "Computer", "Phone", "LAN Access", - "Audio/Video", "Peripheral", "Imaging", "Uncategorized" -}; - -static void cmd_find(int dev_id, int argc, char **argv) -{ - inquiry_info *info = NULL; - int num_rsp, length, flags; - uint8_t cls[3]; - uint16_t handle; - char addr[18], name[249], oui[9], *comp; - unsigned char features[8]; - struct hci_version version; - struct hci_dev_info di; - struct hci_conn_info_req *cr; - int i, opt, dd, cc = 0; - - length = 8; /* ~10 seconds */ - num_rsp = 100; - flags = 0; - - for_each_opt(opt, find_options, NULL) { - switch (opt) { - case 'l': - length = atoi(optarg); - break; - - case 'n': - num_rsp = atoi(optarg); - break; - - case 'f': - flags |= IREQ_CACHE_FLUSH; - break; - - default: - printf(find_help); - return; - } - } - - if (dev_id < 0) { - dev_id = hci_get_route(NULL); - if (dev_id < 0) { - perror("Device is not available"); - exit(1); - } - } - - if (hci_devinfo(dev_id, &di) < 0) { - perror("Can't get device info"); - exit(1); - } - - printf("Searching ...\n"); - num_rsp = hci_inquiry(dev_id, length, num_rsp, NULL, &info, flags); - if (num_rsp < 0) { - perror("Inquiry failed"); - exit(1); - } - - dd = hci_open_dev(dev_id); - if (dd < 0) { - perror("HCI device open failed"); - free(info); - exit(1); - } - - printf("\n"); - - for (i = 0; i < num_rsp; i++) { - ba2str(&(info+i)->bdaddr, addr); - printf("BD Address:\t%s [mode %d, clkoffset 0x%4.4x]\n", addr, - (info+i)->pscan_rep_mode, btohs((info+i)->clock_offset)); - - ba2oui(&(info+i)->bdaddr, oui); - comp = ouitocomp(oui); - if (comp) { - printf("OUI company:\t%s (%s)\n", comp, oui); - free(comp); - } - - cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); - if (cr) { - bacpy(&cr->bdaddr, &(info+i)->bdaddr); - cr->type = ACL_LINK; - if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) { - if (hci_create_connection(dd, &(info+i)->bdaddr, - htobs(di.pkt_type & ACL_PTYPE_MASK), - (info+i)->clock_offset | 0x8000, - 0x01, &handle, 25000) < 0) - handle = 0; - else - cc = 1; - } else - handle = htobs(cr->conn_info->handle); - } else - handle = 0; - - memset(name, 0, sizeof(name)); - if (hci_read_remote_name_with_clock_offset(dd, &(info+i)->bdaddr, - (info+i)->pscan_rep_mode, - (info+i)->clock_offset | 0x8000, - sizeof(name), name, 100000) < 0) - strcpy(name, "n/a"); - printf("Device name:\t%s\n", name); - - memcpy(cls, (info+i)->dev_class, 3); - printf("Device class:\t"); - if ((cls[1] & 0x1f) > sizeof(*major_classes)) - printf("Invalid"); - else - printf("%s, %s", major_classes[cls[1] & 0x1f], - get_minor_device_name(cls[1] & 0x1f, cls[0] >> 2)); - printf(" (0x%2.2x%2.2x%2.2x)\n", cls[2], cls[1], cls[0]); - - if (handle > 0) { - if (hci_read_remote_version(dd, handle, &version, 20000) == 0) { - printf("Manufacturer:\t%s (%d)\n", - bt_compidtostr(version.manufacturer), - version.manufacturer); - printf("LMP version:\t%s (0x%x) [subver 0x%x]\n", - lmp_vertostr(version.lmp_ver), - version.lmp_ver, version.lmp_subver); - } - - if (hci_read_remote_features(dd, handle, features, 20000) == 0) { - printf("LMP features:\t0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x" - " 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", - features[0], features[1], - features[2], features[3], - features[4], features[5], - features[6], features[7]); - printf("%s\n", lmp_featurestostr(features, "\t\t", 63)); - } - - if (cc) { - usleep(10000); - hci_disconnect(dd, handle, HCI_OE_USER_ENDED_CONNECTION, 10000); - } - } - - printf("\n"); - - if (cr) - free(cr); - } - - close(dd); - free(info); -} - /* Send arbitrary HCI commands */ static struct option cmd_options[] = { @@ -1942,7 +1909,6 @@ static struct { { "scan", cmd_scan, "Scan for remote devices" }, { "name", cmd_name, "Get name from remote device" }, { "info", cmd_info, "Get information from remote device" }, - { "find", cmd_find, "Search for remote devices" }, { "cmd", cmd_cmd, "Submit arbitrary HCI commands" }, { "con", cmd_con, "Display active connections" }, { "cc", cmd_cc, "Create connection to remote device" }, |