summaryrefslogtreecommitdiff
path: root/tools/hcitool.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/hcitool.c')
-rw-r--r--tools/hcitool.c3007
1 files changed, 3007 insertions, 0 deletions
diff --git a/tools/hcitool.c b/tools/hcitool.c
new file mode 100644
index 0000000..a117449
--- /dev/null
+++ b/tools/hcitool.c
@@ -0,0 +1,3007 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "textfile.h"
+#include "oui.h"
+
+/* Unofficial value, might still change */
+#define LE_LINK 0x03
+
+#define FLAGS_AD_TYPE 0x01
+#define FLAGS_LIMITED_MODE_BIT 0x01
+#define FLAGS_GENERAL_MODE_BIT 0x02
+
+#define for_each_opt(opt, long, short) while ((opt=getopt_long(argc, argv, short ? short:"+", long, NULL)) != -1)
+
+static void usage(void);
+
+static int dev_info(int s, int dev_id, long arg)
+{
+ struct hci_dev_info di = { dev_id: dev_id };
+ char addr[18];
+
+ if (ioctl(s, HCIGETDEVINFO, (void *) &di))
+ return 0;
+
+ ba2str(&di.bdaddr, addr);
+ printf("\t%s\t%s\n", di.name, addr);
+ return 0;
+}
+
+static void helper_arg(int min_num_arg, int max_num_arg, int *argc,
+ char ***argv, const char *usage)
+{
+ *argc -= optind;
+ /* too many arguments, but when "max_num_arg < min_num_arg" then no
+ limiting (prefer "max_num_arg=-1" to gen infinity)
+ */
+ if ( (*argc > max_num_arg) && (max_num_arg >= min_num_arg ) ) {
+ fprintf(stderr, "%s: too many arguments (maximal: %i)\n",
+ *argv[0], max_num_arg);
+ printf("%s", usage);
+ exit(1);
+ }
+
+ /* print usage */
+ if (*argc < min_num_arg) {
+ fprintf(stderr, "%s: too few arguments (minimal: %i)\n",
+ *argv[0], min_num_arg);
+ printf("%s", usage);
+ exit(0);
+ }
+
+ *argv += optind;
+}
+
+static char *type2str(uint8_t type)
+{
+ switch (type) {
+ case SCO_LINK:
+ return "SCO";
+ case ACL_LINK:
+ return "ACL";
+ case ESCO_LINK:
+ return "eSCO";
+ case LE_LINK:
+ return "LE";
+ default:
+ return "Unknown";
+ }
+}
+
+static int conn_list(int s, int dev_id, long arg)
+{
+ struct hci_conn_list_req *cl;
+ struct hci_conn_info *ci;
+ int id = arg;
+ int i;
+
+ if (id != -1 && dev_id != id)
+ return 0;
+
+ if (!(cl = malloc(10 * sizeof(*ci) + sizeof(*cl)))) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+ cl->dev_id = dev_id;
+ cl->conn_num = 10;
+ ci = cl->conn_info;
+
+ if (ioctl(s, HCIGETCONNLIST, (void *) cl)) {
+ perror("Can't get connection list");
+ exit(1);
+ }
+
+ for (i = 0; i < cl->conn_num; i++, ci++) {
+ char addr[18];
+ char *str;
+ ba2str(&ci->bdaddr, addr);
+ str = hci_lmtostr(ci->link_mode);
+ printf("\t%s %s %s handle %d state %d lm %s\n",
+ ci->out ? "<" : ">", type2str(ci->type),
+ addr, ci->handle, ci->state, str);
+ bt_free(str);
+ }
+
+ free(cl);
+ return 0;
+}
+
+static int find_conn(int s, int dev_id, long arg)
+{
+ struct hci_conn_list_req *cl;
+ struct hci_conn_info *ci;
+ int i;
+
+ if (!(cl = malloc(10 * sizeof(*ci) + sizeof(*cl)))) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+ cl->dev_id = dev_id;
+ cl->conn_num = 10;
+ ci = cl->conn_info;
+
+ if (ioctl(s, HCIGETCONNLIST, (void *) cl)) {
+ perror("Can't get connection list");
+ exit(1);
+ }
+
+ for (i = 0; i < cl->conn_num; i++, ci++)
+ if (!bacmp((bdaddr_t *) arg, &ci->bdaddr)) {
+ free(cl);
+ return 1;
+ }
+
+ free(cl);
+ return 0;
+}
+
+static void hex_dump(char *pref, int width, unsigned char *buf, int len)
+{
+ register int i,n;
+
+ for (i = 0, n = 1; i < len; i++, n++) {
+ if (n == 1)
+ printf("%s", pref);
+ printf("%2.2X ", buf[i]);
+ if (n == width) {
+ printf("\n");
+ n = 0;
+ }
+ }
+ if (i && n!=1)
+ 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 */ {
+ static char cls_str[48]; cls_str[0] = 0;
+
+ switch (minor & 48) {
+ case 16:
+ strncpy(cls_str, "Keyboard", sizeof(cls_str));
+ break;
+ case 32:
+ strncpy(cls_str, "Pointing device", sizeof(cls_str));
+ break;
+ case 48:
+ strncpy(cls_str, "Combo keyboard/pointing device", sizeof(cls_str));
+ break;
+ }
+ if ((minor & 15) && (strlen(cls_str) > 0))
+ strcat(cls_str, "/");
+
+ switch (minor & 15) {
+ case 0:
+ break;
+ case 1:
+ strncat(cls_str, "Joystick", sizeof(cls_str) - strlen(cls_str));
+ break;
+ case 2:
+ strncat(cls_str, "Gamepad", sizeof(cls_str) - strlen(cls_str));
+ break;
+ case 3:
+ strncat(cls_str, "Remote control", sizeof(cls_str) - strlen(cls_str));
+ break;
+ case 4:
+ strncat(cls_str, "Sensing device", sizeof(cls_str) - strlen(cls_str));
+ break;
+ case 5:
+ strncat(cls_str, "Digitizer tablet", sizeof(cls_str) - strlen(cls_str));
+ break;
+ case 6:
+ strncat(cls_str, "Card reader", sizeof(cls_str) - strlen(cls_str));
+ break;
+ default:
+ strncat(cls_str, "(reserved)", sizeof(cls_str) - strlen(cls_str));
+ break;
+ }
+ if (strlen(cls_str) > 0)
+ return cls_str;
+ }
+ 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 7: /* wearable */
+ switch (minor) {
+ case 1:
+ return "Wrist Watch";
+ case 2:
+ return "Pager";
+ case 3:
+ return "Jacket";
+ case 4:
+ return "Helmet";
+ case 5:
+ return "Glasses";
+ }
+ break;
+ case 8: /* toy */
+ switch (minor) {
+ case 1:
+ return "Robot";
+ case 2:
+ return "Vehicle";
+ case 3:
+ return "Doll / Action Figure";
+ case 4:
+ return "Controller";
+ case 5:
+ return "Game";
+ }
+ 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 char *get_device_name(const bdaddr_t *local, const bdaddr_t *peer)
+{
+ char filename[PATH_MAX + 1], addr[18];
+
+ ba2str(local, addr);
+ create_name(filename, PATH_MAX, STORAGEDIR, addr, "names");
+
+ ba2str(peer, addr);
+ return textfile_get(filename, addr);
+}
+
+/* Display local devices */
+
+static struct option dev_options[] = {
+ { "help", 0, 0, 'h' },
+ {0, 0, 0, 0 }
+};
+
+static const char *dev_help =
+ "Usage:\n"
+ "\tdev\n";
+
+static void cmd_dev(int dev_id, int argc, char **argv)
+{
+ int opt;
+
+ for_each_opt(opt, dev_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", dev_help);
+ return;
+ }
+ }
+ helper_arg(0, 0, &argc, &argv, dev_help);
+
+ printf("Devices:\n");
+
+ hci_for_each_dev(HCI_UP, dev_info, 0);
+}
+
+/* Inquiry */
+
+static struct option inq_options[] = {
+ { "help", 0, 0, 'h' },
+ { "length", 1, 0, 'l' },
+ { "numrsp", 1, 0, 'n' },
+ { "iac", 1, 0, 'i' },
+ { "flush", 0, 0, 'f' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *inq_help =
+ "Usage:\n"
+ "\tinq [--length=N] maximum inquiry duration in 1.28 s units\n"
+ "\t [--numrsp=N] specify maximum number of inquiry responses\n"
+ "\t [--iac=lap] specify the inquiry access code\n"
+ "\t [--flush] flush the inquiry cache\n";
+
+static void cmd_inq(int dev_id, int argc, char **argv)
+{
+ inquiry_info *info = NULL;
+ uint8_t lap[3] = { 0x33, 0x8b, 0x9e };
+ int num_rsp, length, flags;
+ char addr[18];
+ int i, l, opt;
+
+ length = 8; /* ~10 seconds */
+ num_rsp = 0;
+ flags = 0;
+
+ for_each_opt(opt, inq_options, NULL) {
+ switch (opt) {
+ case 'l':
+ length = atoi(optarg);
+ break;
+
+ case 'n':
+ num_rsp = atoi(optarg);
+ break;
+
+ case 'i':
+ l = strtoul(optarg, 0, 16);
+ if (!strcasecmp(optarg, "giac")) {
+ l = 0x9e8b33;
+ } else if (!strcasecmp(optarg, "liac")) {
+ l = 0x9e8b00;
+ } if (l < 0x9e8b00 || l > 0x9e8b3f) {
+ printf("Invalid access code 0x%x\n", l);
+ exit(1);
+ }
+ lap[0] = (l & 0xff);
+ lap[1] = (l >> 8) & 0xff;
+ lap[2] = (l >> 16) & 0xff;
+ break;
+
+ case 'f':
+ flags |= IREQ_CACHE_FLUSH;
+ break;
+
+ default:
+ printf("%s", inq_help);
+ return;
+ }
+ }
+ helper_arg(0, 0, &argc, &argv, inq_help);
+
+ printf("Inquiring ...\n");
+
+ num_rsp = hci_inquiry(dev_id, length, num_rsp, lap, &info, flags);
+ if (num_rsp < 0) {
+ perror("Inquiry failed.");
+ exit(1);
+ }
+
+ for (i = 0; i < num_rsp; i++) {
+ ba2str(&(info+i)->bdaddr, addr);
+ printf("\t%s\tclock offset: 0x%4.4x\tclass: 0x%2.2x%2.2x%2.2x\n",
+ addr, btohs((info+i)->clock_offset),
+ (info+i)->dev_class[2],
+ (info+i)->dev_class[1],
+ (info+i)->dev_class[0]);
+ }
+
+ bt_free(info);
+}
+
+/* Device scanning */
+
+static struct option scan_options[] = {
+ { "help", 0, 0, 'h' },
+ { "length", 1, 0, 'l' },
+ { "numrsp", 1, 0, 'n' },
+ { "iac", 1, 0, 'i' },
+ { "flush", 0, 0, 'f' },
+ { "refresh", 0, 0, 'r' },
+ { "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 const char *scan_help =
+ "Usage:\n"
+ "\tscan [--length=N] [--numrsp=N] [--iac=lap] [--flush] [--class] [--info] [--oui] [--refresh]\n";
+
+static void cmd_scan(int dev_id, int argc, char **argv)
+{
+ inquiry_info *info = NULL;
+ uint8_t lap[3] = { 0x33, 0x8b, 0x9e };
+ int num_rsp, length, flags;
+ uint8_t cls[3], features[8];
+ char addr[18], name[249], oui[9], *comp, *tmp;
+ struct hci_version version;
+ struct hci_dev_info di;
+ struct hci_conn_info_req *cr;
+ int refresh = 0, extcls = 0, extinf = 0, extoui = 0;
+ int i, n, l, opt, dd, cc, nc;
+
+ length = 8; /* ~10 seconds */
+ num_rsp = 0;
+ flags = 0;
+
+ for_each_opt(opt, scan_options, NULL) {
+ switch (opt) {
+ case 'l':
+ length = atoi(optarg);
+ break;
+
+ case 'n':
+ num_rsp = atoi(optarg);
+ break;
+
+ case 'i':
+ l = strtoul(optarg, 0, 16);
+ if (!strcasecmp(optarg, "giac")) {
+ l = 0x9e8b33;
+ } else if (!strcasecmp(optarg, "liac")) {
+ l = 0x9e8b00;
+ } else if (l < 0x9e8b00 || l > 0x9e8b3f) {
+ printf("Invalid access code 0x%x\n", l);
+ exit(1);
+ }
+ lap[0] = (l & 0xff);
+ lap[1] = (l >> 8) & 0xff;
+ lap[2] = (l >> 16) & 0xff;
+ break;
+
+ case 'f':
+ flags |= IREQ_CACHE_FLUSH;
+ break;
+
+ case 'r':
+ refresh = 1;
+ 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("%s", scan_help);
+ return;
+ }
+ }
+ helper_arg(0, 0, &argc, &argv, scan_help);
+
+ 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("Scanning ...\n");
+ num_rsp = hci_inquiry(dev_id, length, num_rsp, lap, &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);
+ }
+
+ if (extcls || extinf || extoui)
+ printf("\n");
+
+ for (i = 0; i < num_rsp; i++) {
+ uint16_t handle = 0;
+
+ if (!refresh) {
+ memset(name, 0, sizeof(name));
+ tmp = get_device_name(&di.bdaddr, &(info+i)->bdaddr);
+ if (tmp) {
+ strncpy(name, tmp, 249);
+ free(tmp);
+ nc = 1;
+ } else
+ nc = 0;
+ } else
+ nc = 0;
+
+ if (!extcls && !extinf && !extoui) {
+ ba2str(&(info+i)->bdaddr, addr);
+
+ if (nc) {
+ printf("\t%s\t%s\n", addr, name);
+ continue;
+ }
+
+ 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");
+
+ for (n = 0; n < 248 && name[n]; n++) {
+ if ((unsigned char) name[i] < 32 || name[i] == 127)
+ name[i] = '.';
+ }
+
+ name[248] = '\0';
+
+ 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;
+ }
+ }
+ }
+
+ if (handle > 0 || !nc) {
+ 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) {
+ if (!nc)
+ strcpy(name, "n/a");
+ } else {
+ for (n = 0; n < 248 && name[n]; n++) {
+ if ((unsigned char) name[i] < 32 || name[i] == 127)
+ name[i] = '.';
+ }
+
+ name[248] = '\0';
+ nc = 0;
+ }
+ }
+
+ if (strlen(name) > 0)
+ printf("Device name:\t%s%s\n", name, nc ? " [cached]" : "");
+
+ if (extcls) {
+ memcpy(cls, (info+i)->dev_class, 3);
+ printf("Device class:\t");
+ if ((cls[1] & 0x1f) > sizeof(major_classes) / sizeof(char *))
+ 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) {
+ char *ver = lmp_vertostr(version.lmp_ver);
+ printf("Manufacturer:\t%s (%d)\n",
+ bt_compidtostr(version.manufacturer),
+ version.manufacturer);
+ printf("LMP version:\t%s (0x%x) [subver 0x%x]\n",
+ ver ? ver : "n/a",
+ version.lmp_ver, version.lmp_subver);
+ if (ver)
+ bt_free(ver);
+ }
+
+ if (hci_read_remote_features(dd, handle, features, 20000) == 0) {
+ char *tmp = lmp_featurestostr(features, "\t\t", 63);
+ 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", tmp);
+ bt_free(tmp);
+ }
+
+ if (cc) {
+ usleep(10000);
+ hci_disconnect(dd, handle, HCI_OE_USER_ENDED_CONNECTION, 10000);
+ }
+ }
+
+ printf("\n");
+ }
+
+ bt_free(info);
+
+ hci_close_dev(dd);
+}
+
+/* Remote name */
+
+static struct option name_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *name_help =
+ "Usage:\n"
+ "\tname <bdaddr>\n";
+
+static void cmd_name(int dev_id, int argc, char **argv)
+{
+ bdaddr_t bdaddr;
+ char name[248];
+ int opt, dd;
+
+ for_each_opt(opt, name_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", name_help);
+ return;
+ }
+ }
+ helper_arg(1, 1, &argc, &argv, name_help);
+
+ str2ba(argv[0], &bdaddr);
+
+ if (dev_id < 0) {
+ dev_id = hci_get_route(&bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Device is not available.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ if (hci_read_remote_name(dd, &bdaddr, sizeof(name), name, 25000) == 0)
+ printf("%s\n", name);
+
+ hci_close_dev(dd);
+}
+
+/* Info about remote device */
+
+static struct option info_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *info_help =
+ "Usage:\n"
+ "\tinfo <bdaddr>\n";
+
+static void cmd_info(int dev_id, int argc, char **argv)
+{
+ bdaddr_t bdaddr;
+ uint16_t handle;
+ uint8_t features[8], max_page = 0;
+ char name[249], oui[9], *comp, *tmp;
+ struct hci_version version;
+ struct hci_dev_info di;
+ struct hci_conn_info_req *cr;
+ int i, opt, dd, cc = 0;
+
+ for_each_opt(opt, info_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", info_help);
+ return;
+ }
+ }
+ helper_arg(1, 1, &argc, &argv, info_help);
+
+ str2ba(argv[0], &bdaddr);
+
+ if (dev_id < 0)
+ dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+
+ if (dev_id < 0)
+ dev_id = hci_get_route(&bdaddr);
+
+ if (dev_id < 0) {
+ fprintf(stderr, "Device is not available or not connected.\n");
+ exit(1);
+ }
+
+ if (hci_devinfo(dev_id, &di) < 0) {
+ perror("Can't get device info");
+ exit(1);
+ }
+
+ printf("Requesting information ...\n");
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr) {
+ perror("Can't get connection info");
+ close(dd);
+ exit(1);
+ }
+
+ bacpy(&cr->bdaddr, &bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ if (hci_create_connection(dd, &bdaddr,
+ htobs(di.pkt_type & ACL_PTYPE_MASK),
+ 0, 0x01, &handle, 25000) < 0) {
+ perror("Can't create connection");
+ close(dd);
+ exit(1);
+ }
+ sleep(1);
+ cc = 1;
+ } else
+ handle = htobs(cr->conn_info->handle);
+
+ printf("\tBD Address: %s\n", argv[0]);
+
+ ba2oui(&bdaddr, oui);
+ comp = ouitocomp(oui);
+ if (comp) {
+ printf("\tOUI Company: %s (%s)\n", comp, oui);
+ free(comp);
+ }
+
+ if (hci_read_remote_name(dd, &bdaddr, sizeof(name), name, 25000) == 0)
+ printf("\tDevice Name: %s\n", name);
+
+ if (hci_read_remote_version(dd, handle, &version, 20000) == 0) {
+ char *ver = lmp_vertostr(version.lmp_ver);
+ printf("\tLMP Version: %s (0x%x) LMP Subversion: 0x%x\n"
+ "\tManufacturer: %s (%d)\n",
+ ver ? ver : "n/a",
+ version.lmp_ver,
+ version.lmp_subver,
+ bt_compidtostr(version.manufacturer),
+ version.manufacturer);
+ if (ver)
+ bt_free(ver);
+ }
+
+ memset(features, 0, sizeof(features));
+ hci_read_remote_features(dd, handle, features, 20000);
+
+ if ((di.features[7] & LMP_EXT_FEAT) && (features[7] & LMP_EXT_FEAT))
+ hci_read_remote_ext_features(dd, handle, 0, &max_page,
+ features, 20000);
+
+ printf("\tFeatures%s: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
+ "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n",
+ (max_page > 0) ? " page 0" : "",
+ features[0], features[1], features[2], features[3],
+ features[4], features[5], features[6], features[7]);
+
+ tmp = lmp_featurestostr(features, "\t\t", 63);
+ printf("%s\n", tmp);
+ bt_free(tmp);
+
+ for (i = 1; i <= max_page; i++) {
+ if (hci_read_remote_ext_features(dd, handle, i, NULL,
+ features, 20000) < 0)
+ continue;
+
+ printf("\tFeatures page %d: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
+ "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", i,
+ features[0], features[1], features[2], features[3],
+ features[4], features[5], features[6], features[7]);
+ }
+
+ if (cc) {
+ usleep(10000);
+ hci_disconnect(dd, handle, HCI_OE_USER_ENDED_CONNECTION, 10000);
+ }
+
+ hci_close_dev(dd);
+}
+
+/* Start periodic inquiry */
+
+static struct option spinq_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *spinq_help =
+ "Usage:\n"
+ "\tspinq\n";
+
+static void cmd_spinq(int dev_id, int argc, char **argv)
+{
+ uint8_t lap[3] = { 0x33, 0x8b, 0x9e };
+ struct hci_request rq;
+ periodic_inquiry_cp cp;
+ int opt, dd;
+
+ for_each_opt(opt, spinq_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", spinq_help);
+ return;
+ }
+ }
+ helper_arg(0, 0, &argc, &argv, spinq_help);
+
+ if (dev_id < 0)
+ dev_id = hci_get_route(NULL);
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("Device open failed");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(&cp, 0, sizeof(cp));
+ memcpy(cp.lap, lap, 3);
+ cp.max_period = htobs(16);
+ cp.min_period = htobs(10);
+ cp.length = 8;
+ cp.num_rsp = 0;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_LINK_CTL;
+ rq.ocf = OCF_PERIODIC_INQUIRY;
+ rq.cparam = &cp;
+ rq.clen = PERIODIC_INQUIRY_CP_SIZE;
+
+ if (hci_send_req(dd, &rq, 100) < 0) {
+ perror("Periodic inquiry failed");
+ exit(EXIT_FAILURE);
+ }
+
+ hci_close_dev(dd);
+}
+
+/* Exit periodic inquiry */
+
+static struct option epinq_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *epinq_help =
+ "Usage:\n"
+ "\tepinq\n";
+
+static void cmd_epinq(int dev_id, int argc, char **argv)
+{
+ int opt, dd;
+
+ for_each_opt(opt, epinq_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", epinq_help);
+ return;
+ }
+ }
+ helper_arg(0, 0, &argc, &argv, epinq_help);
+
+ if (dev_id < 0)
+ dev_id = hci_get_route(NULL);
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("Device open failed");
+ exit(EXIT_FAILURE);
+ }
+
+ if (hci_send_cmd(dd, OGF_LINK_CTL,
+ OCF_EXIT_PERIODIC_INQUIRY, 0, NULL) < 0) {
+ perror("Exit periodic inquiry failed");
+ exit(EXIT_FAILURE);
+ }
+
+ hci_close_dev(dd);
+}
+
+/* Send arbitrary HCI commands */
+
+static struct option cmd_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *cmd_help =
+ "Usage:\n"
+ "\tcmd <ogf> <ocf> [parameters]\n"
+ "Example:\n"
+ "\tcmd 0x03 0x0013 0x41 0x42 0x43 0x44\n";
+
+static void cmd_cmd(int dev_id, int argc, char **argv)
+{
+ unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr = buf;
+ struct hci_filter flt;
+ hci_event_hdr *hdr;
+ int i, opt, len, dd;
+ uint16_t ocf;
+ uint8_t ogf;
+
+ for_each_opt(opt, cmd_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", cmd_help);
+ return;
+ }
+ }
+ helper_arg(2, -1, &argc, &argv, cmd_help);
+
+ if (dev_id < 0)
+ dev_id = hci_get_route(NULL);
+
+ errno = 0;
+ ogf = strtol(argv[0], NULL, 16);
+ ocf = strtol(argv[1], NULL, 16);
+ if (errno == ERANGE || (ogf > 0x3f) || (ocf > 0x3ff)) {
+ printf("%s", cmd_help);
+ return;
+ }
+
+ for (i = 2, len = 0; i < argc && len < (int) sizeof(buf); i++, len++)
+ *ptr++ = (uint8_t) strtol(argv[i], NULL, 16);
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("Device open failed");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Setup filter */
+ hci_filter_clear(&flt);
+ hci_filter_set_ptype(HCI_EVENT_PKT, &flt);
+ hci_filter_all_events(&flt);
+ if (setsockopt(dd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
+ perror("HCI filter setup failed");
+ exit(EXIT_FAILURE);
+ }
+
+ printf("< HCI Command: ogf 0x%02x, ocf 0x%04x, plen %d\n", ogf, ocf, len);
+ hex_dump(" ", 20, buf, len); fflush(stdout);
+
+ if (hci_send_cmd(dd, ogf, ocf, len, buf) < 0) {
+ perror("Send failed");
+ exit(EXIT_FAILURE);
+ }
+
+ len = read(dd, buf, sizeof(buf));
+ if (len < 0) {
+ perror("Read failed");
+ exit(EXIT_FAILURE);
+ }
+
+ hdr = (void *)(buf + 1);
+ ptr = buf + (1 + HCI_EVENT_HDR_SIZE);
+ len -= (1 + HCI_EVENT_HDR_SIZE);
+
+ printf("> HCI Event: 0x%02x plen %d\n", hdr->evt, hdr->plen);
+ hex_dump(" ", 20, ptr, len); fflush(stdout);
+
+ hci_close_dev(dd);
+}
+
+/* Display active connections */
+
+static struct option con_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *con_help =
+ "Usage:\n"
+ "\tcon\n";
+
+static void cmd_con(int dev_id, int argc, char **argv)
+{
+ int opt;
+
+ for_each_opt(opt, con_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", con_help);
+ return;
+ }
+ }
+ helper_arg(0, 0, &argc, &argv, con_help);
+
+ printf("Connections:\n");
+
+ hci_for_each_dev(HCI_UP, conn_list, dev_id);
+}
+
+/* Create connection */
+
+static struct option cc_options[] = {
+ { "help", 0, 0, 'h' },
+ { "role", 1, 0, 'r' },
+ { "ptype", 1, 0, 'p' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *cc_help =
+ "Usage:\n"
+ "\tcc [--role=m|s] [--ptype=pkt_types] <bdaddr>\n"
+ "Example:\n"
+ "\tcc --ptype=dm1,dh3,dh5 01:02:03:04:05:06\n"
+ "\tcc --role=m 01:02:03:04:05:06\n";
+
+static void cmd_cc(int dev_id, int argc, char **argv)
+{
+ bdaddr_t bdaddr;
+ uint16_t handle;
+ uint8_t role;
+ unsigned int ptype;
+ int dd, opt;
+
+ role = 0x01;
+ ptype = HCI_DM1 | HCI_DM3 | HCI_DM5 | HCI_DH1 | HCI_DH3 | HCI_DH5;
+
+ for_each_opt(opt, cc_options, NULL) {
+ switch (opt) {
+ case 'p':
+ hci_strtoptype(optarg, &ptype);
+ break;
+
+ case 'r':
+ role = optarg[0] == 'm' ? 0 : 1;
+ break;
+
+ default:
+ printf("%s", cc_help);
+ return;
+ }
+ }
+ helper_arg(1, 1, &argc, &argv, cc_help);
+
+ str2ba(argv[0], &bdaddr);
+
+ if (dev_id < 0) {
+ dev_id = hci_get_route(&bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Device is not available.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ if (hci_create_connection(dd, &bdaddr, htobs(ptype),
+ htobs(0x0000), role, &handle, 25000) < 0)
+ perror("Can't create connection");
+
+ hci_close_dev(dd);
+}
+
+/* Close connection */
+
+static struct option dc_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *dc_help =
+ "Usage:\n"
+ "\tdc <bdaddr> [reason]\n";
+
+static void cmd_dc(int dev_id, int argc, char **argv)
+{
+ struct hci_conn_info_req *cr;
+ bdaddr_t bdaddr;
+ uint8_t reason;
+ int opt, dd;
+
+ for_each_opt(opt, dc_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", dc_help);
+ return;
+ }
+ }
+ helper_arg(1, 2, &argc, &argv, dc_help);
+
+ str2ba(argv[0], &bdaddr);
+ reason = (argc > 1) ? atoi(argv[1]) : HCI_OE_USER_ENDED_CONNECTION;
+
+ if (dev_id < 0) {
+ dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Not connected.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+
+ bacpy(&cr->bdaddr, &bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ perror("Get connection info failed");
+ exit(1);
+ }
+
+ if (hci_disconnect(dd, htobs(cr->conn_info->handle),
+ reason, 10000) < 0)
+ perror("Disconnect failed");
+
+ free(cr);
+
+ hci_close_dev(dd);
+}
+
+/* Role switch */
+
+static struct option sr_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *sr_help =
+ "Usage:\n"
+ "\tsr <bdaddr> <role>\n";
+
+static void cmd_sr(int dev_id, int argc, char **argv)
+{
+ bdaddr_t bdaddr;
+ uint8_t role;
+ int opt, dd;
+
+ for_each_opt(opt, sr_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", sr_help);
+ return;
+ }
+ }
+ helper_arg(2, 2, &argc, &argv, sr_help);
+
+ str2ba(argv[0], &bdaddr);
+ switch (argv[1][0]) {
+ case 'm':
+ role = 0;
+ break;
+ case 's':
+ role = 1;
+ break;
+ default:
+ role = atoi(argv[1]);
+ break;
+ }
+
+ if (dev_id < 0) {
+ dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Not connected.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ if (hci_switch_role(dd, &bdaddr, role, 10000) < 0) {
+ perror("Switch role request failed");
+ exit(1);
+ }
+
+ hci_close_dev(dd);
+}
+
+/* Read RSSI */
+
+static struct option rssi_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *rssi_help =
+ "Usage:\n"
+ "\trssi <bdaddr>\n";
+
+static void cmd_rssi(int dev_id, int argc, char **argv)
+{
+ struct hci_conn_info_req *cr;
+ bdaddr_t bdaddr;
+ int8_t rssi;
+ int opt, dd;
+
+ for_each_opt(opt, rssi_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", rssi_help);
+ return;
+ }
+ }
+ helper_arg(1, 1, &argc, &argv, rssi_help);
+
+ str2ba(argv[0], &bdaddr);
+
+ if (dev_id < 0) {
+ dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Not connected.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+
+ bacpy(&cr->bdaddr, &bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ perror("Get connection info failed");
+ exit(1);
+ }
+
+ if (hci_read_rssi(dd, htobs(cr->conn_info->handle), &rssi, 1000) < 0) {
+ perror("Read RSSI failed");
+ exit(1);
+ }
+
+ printf("RSSI return value: %d\n", rssi);
+
+ free(cr);
+
+ hci_close_dev(dd);
+}
+
+/* Get link quality */
+
+static struct option lq_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *lq_help =
+ "Usage:\n"
+ "\tlq <bdaddr>\n";
+
+static void cmd_lq(int dev_id, int argc, char **argv)
+{
+ struct hci_conn_info_req *cr;
+ bdaddr_t bdaddr;
+ uint8_t lq;
+ int opt, dd;
+
+ for_each_opt(opt, lq_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", lq_help);
+ return;
+ }
+ }
+ helper_arg(1, 1, &argc, &argv, lq_help);
+
+ str2ba(argv[0], &bdaddr);
+
+ if (dev_id < 0) {
+ dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Not connected.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+
+ bacpy(&cr->bdaddr, &bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ perror("Get connection info failed");
+ exit(1);
+ }
+
+ if (hci_read_link_quality(dd, htobs(cr->conn_info->handle), &lq, 1000) < 0) {
+ perror("HCI read_link_quality request failed");
+ exit(1);
+ }
+
+ printf("Link quality: %d\n", lq);
+
+ free(cr);
+
+ hci_close_dev(dd);
+}
+
+/* Get transmit power level */
+
+static struct option tpl_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *tpl_help =
+ "Usage:\n"
+ "\ttpl <bdaddr> [type]\n";
+
+static void cmd_tpl(int dev_id, int argc, char **argv)
+{
+ struct hci_conn_info_req *cr;
+ bdaddr_t bdaddr;
+ uint8_t type;
+ int8_t level;
+ int opt, dd;
+
+ for_each_opt(opt, tpl_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", tpl_help);
+ return;
+ }
+ }
+ helper_arg(1, 2, &argc, &argv, tpl_help);
+
+ str2ba(argv[0], &bdaddr);
+ type = (argc > 1) ? atoi(argv[1]) : 0;
+
+ if (dev_id < 0) {
+ dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Not connected.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+
+ bacpy(&cr->bdaddr, &bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ perror("Get connection info failed");
+ exit(1);
+ }
+
+ if (hci_read_transmit_power_level(dd, htobs(cr->conn_info->handle), type, &level, 1000) < 0) {
+ perror("HCI read transmit power level request failed");
+ exit(1);
+ }
+
+ printf("%s transmit power level: %d\n",
+ (type == 0) ? "Current" : "Maximum", level);
+
+ free(cr);
+
+ hci_close_dev(dd);
+}
+
+/* Get AFH channel map */
+
+static struct option afh_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *afh_help =
+ "Usage:\n"
+ "\tafh <bdaddr>\n";
+
+static void cmd_afh(int dev_id, int argc, char **argv)
+{
+ struct hci_conn_info_req *cr;
+ bdaddr_t bdaddr;
+ uint16_t handle;
+ uint8_t mode, map[10];
+ int opt, dd;
+
+ for_each_opt(opt, afh_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", afh_help);
+ return;
+ }
+ }
+ helper_arg(1, 1, &argc, &argv, afh_help);
+
+ str2ba(argv[0], &bdaddr);
+
+ if (dev_id < 0) {
+ dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Not connected.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+
+ bacpy(&cr->bdaddr, &bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ perror("Get connection info failed");
+ exit(1);
+ }
+
+ handle = htobs(cr->conn_info->handle);
+
+ if (hci_read_afh_map(dd, handle, &mode, map, 1000) < 0) {
+ perror("HCI read AFH map request failed");
+ exit(1);
+ }
+
+ if (mode == 0x01) {
+ int i;
+ printf("AFH map: 0x");
+ for (i = 0; i < 10; i++)
+ printf("%02x", map[i]);
+ printf("\n");
+ } else
+ printf("AFH disabled\n");
+
+ free(cr);
+
+ hci_close_dev(dd);
+}
+
+/* Set connection packet type */
+
+static struct option cpt_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *cpt_help =
+ "Usage:\n"
+ "\tcpt <bdaddr> <packet_types>\n";
+
+static void cmd_cpt(int dev_id, int argc, char **argv)
+{
+ struct hci_conn_info_req *cr;
+ struct hci_request rq;
+ set_conn_ptype_cp cp;
+ evt_conn_ptype_changed rp;
+ bdaddr_t bdaddr;
+ unsigned int ptype;
+ int dd, opt;
+
+ for_each_opt(opt, cpt_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", cpt_help);
+ return;
+ }
+ }
+ helper_arg(2, 2, &argc, &argv, cpt_help);
+
+ str2ba(argv[0], &bdaddr);
+ hci_strtoptype(argv[1], &ptype);
+
+ if (dev_id < 0) {
+ dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Not connected.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+
+ bacpy(&cr->bdaddr, &bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ perror("Get connection info failed");
+ exit(1);
+ }
+
+ cp.handle = htobs(cr->conn_info->handle);
+ cp.pkt_type = ptype;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_LINK_CTL;
+ rq.ocf = OCF_SET_CONN_PTYPE;
+ rq.cparam = &cp;
+ rq.clen = SET_CONN_PTYPE_CP_SIZE;
+ rq.rparam = &rp;
+ rq.rlen = EVT_CONN_PTYPE_CHANGED_SIZE;
+ rq.event = EVT_CONN_PTYPE_CHANGED;
+
+ if (hci_send_req(dd, &rq, 100) < 0) {
+ perror("Packet type change failed");
+ exit(1);
+ }
+
+ free(cr);
+
+ hci_close_dev(dd);
+}
+
+/* Get/Set link policy settings */
+
+static struct option lp_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *lp_help =
+ "Usage:\n"
+ "\tlp <bdaddr> [link policy]\n";
+
+static void cmd_lp(int dev_id, int argc, char **argv)
+{
+ struct hci_conn_info_req *cr;
+ bdaddr_t bdaddr;
+ uint16_t policy;
+ int opt, dd;
+
+ for_each_opt(opt, lp_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", lp_help);
+ return;
+ }
+ }
+ helper_arg(1, 2, &argc, &argv, lp_help);
+
+ str2ba(argv[0], &bdaddr);
+
+ if (dev_id < 0) {
+ dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Not connected.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+
+ bacpy(&cr->bdaddr, &bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ perror("Get connection info failed");
+ exit(1);
+ }
+
+ if (argc == 1) {
+ char *str;
+ if (hci_read_link_policy(dd, htobs(cr->conn_info->handle),
+ &policy, 1000) < 0) {
+ perror("HCI read_link_policy_settings request failed");
+ exit(1);
+ }
+
+ policy = btohs(policy);
+ str = hci_lptostr(policy);
+ if (str) {
+ printf("Link policy settings: %s\n", str);
+ bt_free(str);
+ } else {
+ fprintf(stderr, "Invalig settings\n");
+ exit(1);
+ }
+ } else {
+ unsigned int val;
+ if (hci_strtolp(argv[1], &val) < 0) {
+ fprintf(stderr, "Invalig arguments\n");
+ exit(1);
+ }
+ policy = val;
+
+ if (hci_write_link_policy(dd, htobs(cr->conn_info->handle),
+ htobs(policy), 1000) < 0) {
+ perror("HCI write_link_policy_settings request failed");
+ exit(1);
+ }
+ }
+
+ free(cr);
+
+ hci_close_dev(dd);
+}
+
+/* Get/Set link supervision timeout */
+
+static struct option lst_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *lst_help =
+ "Usage:\n"
+ "\tlst <bdaddr> [new value in slots]\n";
+
+static void cmd_lst(int dev_id, int argc, char **argv)
+{
+ struct hci_conn_info_req *cr;
+ bdaddr_t bdaddr;
+ uint16_t timeout;
+ int opt, dd;
+
+ for_each_opt(opt, lst_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", lst_help);
+ return;
+ }
+ }
+ helper_arg(1, 2, &argc, &argv, lst_help);
+
+ str2ba(argv[0], &bdaddr);
+
+ if (dev_id < 0) {
+ dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Not connected.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+
+ bacpy(&cr->bdaddr, &bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ perror("Get connection info failed");
+ exit(1);
+ }
+
+ if (argc == 1) {
+ if (hci_read_link_supervision_timeout(dd, htobs(cr->conn_info->handle),
+ &timeout, 1000) < 0) {
+ perror("HCI read_link_supervision_timeout request failed");
+ exit(1);
+ }
+
+ timeout = btohs(timeout);
+
+ if (timeout)
+ printf("Link supervision timeout: %u slots (%.2f msec)\n",
+ timeout, (float) timeout * 0.625);
+ else
+ printf("Link supervision timeout never expires\n");
+ } else {
+ timeout = strtol(argv[1], NULL, 10);
+
+ if (hci_write_link_supervision_timeout(dd, htobs(cr->conn_info->handle),
+ htobs(timeout), 1000) < 0) {
+ perror("HCI write_link_supervision_timeout request failed");
+ exit(1);
+ }
+ }
+
+ free(cr);
+
+ hci_close_dev(dd);
+}
+
+/* Request authentication */
+
+static struct option auth_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *auth_help =
+ "Usage:\n"
+ "\tauth <bdaddr>\n";
+
+static void cmd_auth(int dev_id, int argc, char **argv)
+{
+ struct hci_conn_info_req *cr;
+ bdaddr_t bdaddr;
+ int opt, dd;
+
+ for_each_opt(opt, auth_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", auth_help);
+ return;
+ }
+ }
+ helper_arg(1, 1, &argc, &argv, auth_help);
+
+ str2ba(argv[0], &bdaddr);
+
+ if (dev_id < 0) {
+ dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Not connected.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+
+ bacpy(&cr->bdaddr, &bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ perror("Get connection info failed");
+ exit(1);
+ }
+
+ if (hci_authenticate_link(dd, htobs(cr->conn_info->handle), 25000) < 0) {
+ perror("HCI authentication request failed");
+ exit(1);
+ }
+
+ free(cr);
+
+ hci_close_dev(dd);
+}
+
+/* Activate encryption */
+
+static struct option enc_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *enc_help =
+ "Usage:\n"
+ "\tenc <bdaddr> [encrypt enable]\n";
+
+static void cmd_enc(int dev_id, int argc, char **argv)
+{
+ struct hci_conn_info_req *cr;
+ bdaddr_t bdaddr;
+ uint8_t encrypt;
+ int opt, dd;
+
+ for_each_opt(opt, enc_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", enc_help);
+ return;
+ }
+ }
+ helper_arg(1, 2, &argc, &argv, enc_help);
+
+ str2ba(argv[0], &bdaddr);
+
+ if (dev_id < 0) {
+ dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Not connected.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+
+ bacpy(&cr->bdaddr, &bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ perror("Get connection info failed");
+ exit(1);
+ }
+
+ encrypt = (argc > 1) ? atoi(argv[1]) : 1;
+
+ if (hci_encrypt_link(dd, htobs(cr->conn_info->handle), encrypt, 25000) < 0) {
+ perror("HCI set encryption request failed");
+ exit(1);
+ }
+
+ free(cr);
+
+ hci_close_dev(dd);
+}
+
+/* Change connection link key */
+
+static struct option key_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *key_help =
+ "Usage:\n"
+ "\tkey <bdaddr>\n";
+
+static void cmd_key(int dev_id, int argc, char **argv)
+{
+ struct hci_conn_info_req *cr;
+ bdaddr_t bdaddr;
+ int opt, dd;
+
+ for_each_opt(opt, key_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", key_help);
+ return;
+ }
+ }
+ helper_arg(1, 1, &argc, &argv, key_help);
+
+ str2ba(argv[0], &bdaddr);
+
+ if (dev_id < 0) {
+ dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Not connected.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+
+ bacpy(&cr->bdaddr, &bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ perror("Get connection info failed");
+ exit(1);
+ }
+
+ if (hci_change_link_key(dd, htobs(cr->conn_info->handle), 25000) < 0) {
+ perror("Changing link key failed");
+ exit(1);
+ }
+
+ free(cr);
+
+ hci_close_dev(dd);
+}
+
+/* Read clock offset */
+
+static struct option clkoff_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *clkoff_help =
+ "Usage:\n"
+ "\tclkoff <bdaddr>\n";
+
+static void cmd_clkoff(int dev_id, int argc, char **argv)
+{
+ struct hci_conn_info_req *cr;
+ bdaddr_t bdaddr;
+ uint16_t offset;
+ int opt, dd;
+
+ for_each_opt(opt, clkoff_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", clkoff_help);
+ return;
+ }
+ }
+ helper_arg(1, 1, &argc, &argv, clkoff_help);
+
+ str2ba(argv[0], &bdaddr);
+
+ if (dev_id < 0) {
+ dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Not connected.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+
+ bacpy(&cr->bdaddr, &bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ perror("Get connection info failed");
+ exit(1);
+ }
+
+ if (hci_read_clock_offset(dd, htobs(cr->conn_info->handle), &offset, 1000) < 0) {
+ perror("Reading clock offset failed");
+ exit(1);
+ }
+
+ printf("Clock offset: 0x%4.4x\n", btohs(offset));
+
+ free(cr);
+
+ hci_close_dev(dd);
+}
+
+/* Read clock */
+
+static struct option clock_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *clock_help =
+ "Usage:\n"
+ "\tclock [bdaddr] [which clock]\n";
+
+static void cmd_clock(int dev_id, int argc, char **argv)
+{
+ struct hci_conn_info_req *cr;
+ bdaddr_t bdaddr;
+ uint8_t which;
+ uint32_t handle, clock;
+ uint16_t accuracy;
+ int opt, dd;
+
+ for_each_opt(opt, clock_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", clock_help);
+ return;
+ }
+ }
+ helper_arg(0, 2, &argc, &argv, clock_help);
+
+ if (argc > 0)
+ str2ba(argv[0], &bdaddr);
+ else
+ bacpy(&bdaddr, BDADDR_ANY);
+
+ if (dev_id < 0 && !bacmp(&bdaddr, BDADDR_ANY))
+ dev_id = hci_get_route(NULL);
+
+ if (dev_id < 0) {
+ dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Not connected.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ if (bacmp(&bdaddr, BDADDR_ANY)) {
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+
+ bacpy(&cr->bdaddr, &bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ perror("Get connection info failed");
+ free(cr);
+ exit(1);
+ }
+
+ handle = htobs(cr->conn_info->handle);
+ which = (argc > 1) ? atoi(argv[1]) : 0x01;
+
+ free(cr);
+ } else {
+ handle = 0x00;
+ which = 0x00;
+ }
+
+ if (hci_read_clock(dd, handle, which, &clock, &accuracy, 1000) < 0) {
+ perror("Reading clock failed");
+ exit(1);
+ }
+
+ accuracy = btohs(accuracy);
+
+ printf("Clock: 0x%4.4x\n", btohl(clock));
+ printf("Accuracy: %.2f msec\n", (float) accuracy * 0.3125);
+
+ hci_close_dev(dd);
+}
+
+static int read_flags(uint8_t *flags, const uint8_t *data, size_t size)
+{
+ unsigned int offset;
+
+ if (!flags || !data)
+ return -EINVAL;
+
+ offset = 0;
+ while (offset < size) {
+ uint8_t len = data[offset];
+ uint8_t type = data[offset + 1];
+
+ /* Check if it is the end of the significant part */
+ if (len == 0)
+ break;
+
+ if (type == FLAGS_AD_TYPE) {
+ *flags = data[offset + 2];
+ return 0;
+ }
+
+ offset += 1 + len;
+ }
+
+ return -ENOENT;
+}
+
+static int check_report_filter(uint8_t procedure, le_advertising_info *info)
+{
+ uint8_t flags;
+
+ /* If no discovery procedure is set, all reports are treat as valid */
+ if (procedure == 0)
+ return 1;
+
+ /* Read flags AD type value from the advertising report if it exists */
+ if (read_flags(&flags, info->data, info->length))
+ return 0;
+
+ switch (procedure) {
+ case 'l': /* Limited Discovery Procedure */
+ if (flags & FLAGS_LIMITED_MODE_BIT)
+ return 1;
+ break;
+ case 'g': /* General Discovery Procedure */
+ if (flags & (FLAGS_LIMITED_MODE_BIT | FLAGS_GENERAL_MODE_BIT))
+ return 1;
+ break;
+ default:
+ fprintf(stderr, "Unknown discovery procedure\n");
+ }
+
+ return 0;
+}
+
+static int print_advertising_devices(int dd, uint8_t filter_type)
+{
+ unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr;
+ struct hci_filter nf, of;
+ socklen_t olen;
+ hci_event_hdr *hdr;
+ int num, len;
+
+ olen = sizeof(of);
+ if (getsockopt(dd, SOL_HCI, HCI_FILTER, &of, &olen) < 0) {
+ printf("Could not get socket options\n");
+ return -1;
+ }
+
+ hci_filter_clear(&nf);
+ hci_filter_set_ptype(HCI_EVENT_PKT, &nf);
+ hci_filter_set_event(EVT_LE_META_EVENT, &nf);
+
+ if (setsockopt(dd, SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0) {
+ printf("Could not set socket options\n");
+ return -1;
+ }
+
+ /* Wait for 10 report events */
+ num = 10;
+ while (num--) {
+ evt_le_meta_event *meta;
+ le_advertising_info *info;
+ char addr[18];
+
+ while ((len = read(dd, buf, sizeof(buf))) < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ goto done;
+ }
+
+ hdr = (void *) (buf + 1);
+ ptr = buf + (1 + HCI_EVENT_HDR_SIZE);
+ len -= (1 + HCI_EVENT_HDR_SIZE);
+
+ meta = (void *) ptr;
+
+ if (meta->subevent != 0x02)
+ goto done;
+
+ /* Ignoring multiple reports */
+ info = (le_advertising_info *) (meta->data + 1);
+ if (check_report_filter(filter_type, info)) {
+ ba2str(&info->bdaddr, addr);
+ printf("%s\n", addr);
+ }
+ }
+
+done:
+ setsockopt(dd, SOL_HCI, HCI_FILTER, &of, sizeof(of));
+
+ if (len < 0)
+ return -1;
+
+ return 0;
+}
+
+static struct option lescan_options[] = {
+ { "help", 0, 0, 'h' },
+ { "privacy", 0, 0, 'p' },
+ { "passive", 0, 0, 'P' },
+ { "discovery", 1, 0, 'd' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *lescan_help =
+ "Usage:\n"
+ "\tlescan [--privacy] enable privacy\n"
+ "\tlescan [--passive] set scan type passive (default active)\n"
+ "\tlescan [--discovery=g|l] enable general or limited discovery"
+ "procedure\n";
+
+static void cmd_lescan(int dev_id, int argc, char **argv)
+{
+ int err, opt, dd;
+ uint8_t own_type = 0x00;
+ uint8_t scan_type = 0x01;
+ uint8_t filter_type = 0;
+ uint16_t interval = htobs(0x0010);
+ uint16_t window = htobs(0x0010);
+
+ for_each_opt(opt, lescan_options, NULL) {
+ switch (opt) {
+ case 'p':
+ own_type = 0x01; /* Random */
+ break;
+ case 'P':
+ scan_type = 0x00; /* Passive */
+ break;
+ case 'd':
+ filter_type = optarg[0];
+ if (filter_type != 'g' && filter_type != 'l') {
+ fprintf(stderr, "Unknown discovery procedure\n");
+ exit(1);
+ }
+
+ interval = htobs(0x0012);
+ window = htobs(0x0012);
+ break;
+ default:
+ printf("%s", lescan_help);
+ return;
+ }
+ }
+ helper_arg(0, 1, &argc, &argv, lescan_help);
+
+ if (dev_id < 0)
+ dev_id = hci_get_route(NULL);
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("Could not open device");
+ exit(1);
+ }
+
+ err = hci_le_set_scan_parameters(dd, scan_type, interval, window,
+ own_type, 0x00, 1000);
+ if (err < 0) {
+ perror("Set scan parameters failed");
+ exit(1);
+ }
+
+ err = hci_le_set_scan_enable(dd, 0x01, 0x00, 1000);
+ if (err < 0) {
+ perror("Enable scan failed");
+ exit(1);
+ }
+
+ printf("LE Scan ...\n");
+
+ err = print_advertising_devices(dd, filter_type);
+ if (err < 0) {
+ perror("Could not receive advertising events");
+ exit(1);
+ }
+
+ err = hci_le_set_scan_enable(dd, 0x00, 0x00, 1000);
+ if (err < 0) {
+ perror("Disable scan failed");
+ exit(1);
+ }
+
+ hci_close_dev(dd);
+}
+
+static struct option lecc_options[] = {
+ { "help", 0, 0, 'h' },
+ { "random", 0, 0, 'r' },
+ { "whitelist", 0, 0, 'w' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *lecc_help =
+ "Usage:\n"
+ "\tlecc [--random] <bdaddr>\n"
+ "\tlecc --whitelist\n";
+
+static void cmd_lecc(int dev_id, int argc, char **argv)
+{
+ int err, opt, dd;
+ bdaddr_t bdaddr;
+ uint16_t interval, latency, max_ce_length, max_interval, min_ce_length;
+ uint16_t min_interval, supervision_timeout, window, handle;
+ uint8_t initiator_filter, own_bdaddr_type, peer_bdaddr_type;
+
+ peer_bdaddr_type = LE_PUBLIC_ADDRESS;
+ initiator_filter = 0; /* Use peer address */
+
+ for_each_opt(opt, lecc_options, NULL) {
+ switch (opt) {
+ case 'r':
+ peer_bdaddr_type = LE_RANDOM_ADDRESS;
+ break;
+ case 'w':
+ initiator_filter = 0x01; /* Use white list */
+ break;
+ default:
+ printf("%s", lecc_help);
+ return;
+ }
+ }
+ helper_arg(0, 1, &argc, &argv, lecc_help);
+
+ if (dev_id < 0)
+ dev_id = hci_get_route(NULL);
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("Could not open device");
+ exit(1);
+ }
+
+ memset(&bdaddr, 0, sizeof(bdaddr_t));
+ if (argv[0])
+ str2ba(argv[0], &bdaddr);
+
+ interval = htobs(0x0004);
+ window = htobs(0x0004);
+ own_bdaddr_type = 0x00;
+ min_interval = htobs(0x000F);
+ max_interval = htobs(0x000F);
+ latency = htobs(0x0000);
+ supervision_timeout = htobs(0x0C80);
+ min_ce_length = htobs(0x0001);
+ max_ce_length = htobs(0x0001);
+
+ err = hci_le_create_conn(dd, interval, window, initiator_filter,
+ peer_bdaddr_type, bdaddr, own_bdaddr_type, min_interval,
+ max_interval, latency, supervision_timeout,
+ min_ce_length, max_ce_length, &handle, 25000);
+ if (err < 0) {
+ perror("Could not create connection");
+ exit(1);
+ }
+
+ printf("Connection handle %d\n", handle);
+
+ hci_close_dev(dd);
+}
+
+static struct option lewladd_options[] = {
+ { "help", 0, 0, 'h' },
+ { "random", 0, 0, 'r' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *lewladd_help =
+ "Usage:\n"
+ "\tlewladd [--random] <bdaddr>\n";
+
+static void cmd_lewladd(int dev_id, int argc, char **argv)
+{
+ int err, opt, dd;
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type = LE_PUBLIC_ADDRESS;
+
+ for_each_opt(opt, lewladd_options, NULL) {
+ switch (opt) {
+ case 'r':
+ bdaddr_type = LE_RANDOM_ADDRESS;
+ break;
+ default:
+ printf("%s", lewladd_help);
+ return;
+ }
+ }
+
+ helper_arg(1, 1, &argc, &argv, lewladd_help);
+
+ if (dev_id < 0)
+ dev_id = hci_get_route(NULL);
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("Could not open device");
+ exit(1);
+ }
+
+ str2ba(argv[0], &bdaddr);
+
+ err = hci_le_add_white_list(dd, &bdaddr, bdaddr_type, 1000);
+ hci_close_dev(dd);
+
+ if (err < 0) {
+ err = errno;
+ fprintf(stderr, "Can't add to white list: %s(%d)\n",
+ strerror(err), err);
+ exit(1);
+ }
+}
+
+static struct option lewlrm_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *lewlrm_help =
+ "Usage:\n"
+ "\tlewlrm <bdaddr>\n";
+
+static void cmd_lewlrm(int dev_id, int argc, char **argv)
+{
+ int err, opt, dd;
+ bdaddr_t bdaddr;
+
+ for_each_opt(opt, lewlrm_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", lewlrm_help);
+ return;
+ }
+ }
+
+ helper_arg(1, 1, &argc, &argv, lewlrm_help);
+
+ if (dev_id < 0)
+ dev_id = hci_get_route(NULL);
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("Could not open device");
+ exit(1);
+ }
+
+ str2ba(argv[0], &bdaddr);
+
+ err = hci_le_rm_white_list(dd, &bdaddr, LE_PUBLIC_ADDRESS, 1000);
+ hci_close_dev(dd);
+
+ if (err < 0) {
+ err = errno;
+ fprintf(stderr, "Can't remove from white list: %s(%d)\n",
+ strerror(err), err);
+ exit(1);
+ }
+}
+
+static struct option lewlsz_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *lewlsz_help =
+ "Usage:\n"
+ "\tlewlsz\n";
+
+static void cmd_lewlsz(int dev_id, int argc, char **argv)
+{
+ int err, dd, opt;
+ uint8_t size;
+
+ for_each_opt(opt, lewlsz_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", lewlsz_help);
+ return;
+ }
+ }
+
+ helper_arg(0, 0, &argc, &argv, lewlsz_help);
+
+ if (dev_id < 0)
+ dev_id = hci_get_route(NULL);
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("Could not open device");
+ exit(1);
+ }
+
+ err = hci_le_read_white_list_size(dd, &size, 1000);
+ hci_close_dev(dd);
+
+ if (err < 0) {
+ err = errno;
+ fprintf(stderr, "Can't read white list size: %s(%d)\n",
+ strerror(err), err);
+ exit(1);
+ }
+
+ printf("White list size: %d\n", size);
+}
+
+static struct option lewlclr_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *lewlclr_help =
+ "Usage:\n"
+ "\tlewlclr\n";
+
+static void cmd_lewlclr(int dev_id, int argc, char **argv)
+{
+ int err, dd, opt;
+
+ for_each_opt(opt, lewlclr_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", lewlclr_help);
+ return;
+ }
+ }
+
+ helper_arg(0, 0, &argc, &argv, lewlclr_help);
+
+ if (dev_id < 0)
+ dev_id = hci_get_route(NULL);
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("Could not open device");
+ exit(1);
+ }
+
+ err = hci_le_clear_white_list(dd, 1000);
+ hci_close_dev(dd);
+
+ if (err < 0) {
+ err = errno;
+ fprintf(stderr, "Can't clear white list: %s(%d)\n",
+ strerror(err), err);
+ exit(1);
+ }
+}
+
+static struct option ledc_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *ledc_help =
+ "Usage:\n"
+ "\tledc <handle> [reason]\n";
+
+static void cmd_ledc(int dev_id, int argc, char **argv)
+{
+ int err, opt, dd;
+ uint16_t handle;
+ uint8_t reason;
+
+ for_each_opt(opt, ledc_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", ledc_help);
+ return;
+ }
+ }
+ helper_arg(1, 2, &argc, &argv, ledc_help);
+
+ if (dev_id < 0)
+ dev_id = hci_get_route(NULL);
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("Could not open device");
+ exit(1);
+ }
+
+ handle = atoi(argv[0]);
+
+ reason = (argc > 1) ? atoi(argv[1]) : HCI_OE_USER_ENDED_CONNECTION;
+
+ err = hci_disconnect(dd, handle, reason, 10000);
+ if (err < 0) {
+ perror("Could not disconnect");
+ exit(1);
+ }
+
+ hci_close_dev(dd);
+}
+
+static struct option lecup_options[] = {
+ { "help", 0, 0, 'h' },
+ { "handle", 1, 0, 'H' },
+ { "min", 1, 0, 'm' },
+ { "max", 1, 0, 'M' },
+ { "latency", 1, 0, 'l' },
+ { "timeout", 1, 0, 't' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *lecup_help =
+ "Usage:\n"
+ "\tlecup <handle> <min> <max> <latency> <timeout>\n"
+ "\tOptions:\n"
+ "\t -H, --handle <0xXXXX> LE connection handle\n"
+ "\t -m, --min <interval> Range: 0x0006 to 0x0C80\n"
+ "\t -M, --max <interval> Range: 0x0006 to 0x0C80\n"
+ "\t -l, --latency <range> Slave latency. Range: 0x0000 to 0x03E8\n"
+ "\t -t, --timeout <time> N * 10ms. Range: 0x000A to 0x0C80\n"
+ "\n\t min/max range: 7.5ms to 4s. Multiply factor: 1.25ms"
+ "\n\t timeout range: 100ms to 32.0s. Larger than max interval\n";
+
+static void cmd_lecup(int dev_id, int argc, char **argv)
+{
+ uint16_t handle = 0, min, max, latency, timeout;
+ int opt, dd, base;
+
+ /* Aleatory valid values */
+ min = 0x0C8;
+ max = 0x0960;
+ latency = 0x0007;
+ timeout = 0x0C80;
+
+ for_each_opt(opt, lecup_options, NULL) {
+ if (optarg && strncasecmp("0x", optarg, 2) == 0)
+ base = 16;
+ else
+ base = 10;
+
+ switch (opt) {
+ case 'H':
+ handle = strtoul(optarg, NULL, base);
+ break;
+ case 'm':
+ min = strtoul(optarg, NULL, base);
+ break;
+ case 'M':
+ max = strtoul(optarg, NULL, base);
+ break;
+ case 'l':
+ latency = strtoul(optarg, NULL, base);
+ break;
+ case 't':
+ timeout = strtoul(optarg, NULL, base);
+ break;
+ default:
+ printf("%s", lecup_help);
+ return;
+ }
+ }
+
+ if (handle == 0) {
+ printf("%s", lecup_help);
+ return;
+ }
+
+ if (dev_id < 0)
+ dev_id = hci_get_route(NULL);
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ fprintf(stderr, "HCI device open failed\n");
+ exit(1);
+ }
+
+ if (hci_le_conn_update(dd, htobs(handle), htobs(min), htobs(max),
+ htobs(latency), htobs(timeout), 5000) < 0) {
+ int err = errno;
+ fprintf(stderr, "Could not change connection params: %s(%d)\n",
+ strerror(err), err);
+ }
+
+ hci_close_dev(dd);
+}
+
+static struct {
+ char *cmd;
+ void (*func)(int dev_id, int argc, char **argv);
+ char *doc;
+} command[] = {
+ { "dev", cmd_dev, "Display local devices" },
+ { "inq", cmd_inq, "Inquire remote devices" },
+ { "scan", cmd_scan, "Scan for remote devices" },
+ { "name", cmd_name, "Get name from remote device" },
+ { "info", cmd_info, "Get information from remote device" },
+ { "spinq", cmd_spinq, "Start periodic inquiry" },
+ { "epinq", cmd_epinq, "Exit periodic inquiry" },
+ { "cmd", cmd_cmd, "Submit arbitrary HCI commands" },
+ { "con", cmd_con, "Display active connections" },
+ { "cc", cmd_cc, "Create connection to remote device" },
+ { "dc", cmd_dc, "Disconnect from remote device" },
+ { "sr", cmd_sr, "Switch master/slave role" },
+ { "cpt", cmd_cpt, "Change connection packet type" },
+ { "rssi", cmd_rssi, "Display connection RSSI" },
+ { "lq", cmd_lq, "Display link quality" },
+ { "tpl", cmd_tpl, "Display transmit power level" },
+ { "afh", cmd_afh, "Display AFH channel map" },
+ { "lp", cmd_lp, "Set/display link policy settings" },
+ { "lst", cmd_lst, "Set/display link supervision timeout" },
+ { "auth", cmd_auth, "Request authentication" },
+ { "enc", cmd_enc, "Set connection encryption" },
+ { "key", cmd_key, "Change connection link key" },
+ { "clkoff", cmd_clkoff, "Read clock offset" },
+ { "clock", cmd_clock, "Read local or remote clock" },
+ { "lescan", cmd_lescan, "Start LE scan" },
+ { "lewladd", cmd_lewladd, "Add device to LE White List" },
+ { "lewlrm", cmd_lewlrm, "Remove device from LE White List" },
+ { "lewlsz", cmd_lewlsz, "Read size of LE White List" },
+ { "lewlclr", cmd_lewlclr, "Clear LE White list" },
+ { "lecc", cmd_lecc, "Create a LE Connection" },
+ { "ledc", cmd_ledc, "Disconnect a LE Connection" },
+ { "lecup", cmd_lecup, "LE Connection Update" },
+ { NULL, NULL, 0 }
+};
+
+static void usage(void)
+{
+ int i;
+
+ printf("hcitool - HCI Tool ver %s\n", VERSION);
+ printf("Usage:\n"
+ "\thcitool [options] <command> [command parameters]\n");
+ printf("Options:\n"
+ "\t--help\tDisplay help\n"
+ "\t-i dev\tHCI device\n");
+ printf("Commands:\n");
+ for (i = 0; command[i].cmd; i++)
+ printf("\t%-4s\t%s\n", command[i].cmd,
+ command[i].doc);
+ printf("\n"
+ "For more information on the usage of each command use:\n"
+ "\thcitool <command> --help\n" );
+}
+
+static struct option main_options[] = {
+ { "help", 0, 0, 'h' },
+ { "device", 1, 0, 'i' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ int opt, i, dev_id = -1;
+ bdaddr_t ba;
+
+ while ((opt=getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) {
+ switch (opt) {
+ case 'i':
+ dev_id = hci_devid(optarg);
+ if (dev_id < 0) {
+ perror("Invalid device");
+ exit(1);
+ }
+ break;
+
+ case 'h':
+ default:
+ usage();
+ exit(0);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (argc < 1) {
+ usage();
+ exit(0);
+ }
+
+ if (dev_id != -1 && hci_devba(dev_id, &ba) < 0) {
+ perror("Device is not available");
+ exit(1);
+ }
+
+ for (i = 0; command[i].cmd; i++) {
+ if (strncmp(command[i].cmd,
+ argv[0], strlen(command[i].cmd)))
+ continue;
+
+ command[i].func(dev_id, argc, argv);
+ break;
+ }
+
+ if (command[i].cmd == 0) {
+ fprintf(stderr, "Unknown command - \"%s\"\n", *argv);
+ exit(1);
+ }
+
+ return 0;
+}