2011-04-15 17:57:41

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [RFC 0/4] hcimgmt: command line tool for mgmt interface

Hi,

When the mgmt interface was first proposed Johan sent a patch
that introduced this tool, I updated it for the current MGMT API
and added a couple new commands.


--
Cheers,


Johan Hedberg (1):
Add hcimgmt command line test tool

Vinicius Costa Gomes (3):
hcimgmt: Add command to set IO Capabilities
Add command to clear the link key list via MGMT interface
hcimgmt: add support for setting pairable mode

Makefile.tools | 5 +-
tools/hcimgmt.c | 546 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 550 insertions(+), 1 deletions(-)
create mode 100644 tools/hcimgmt.c

--
1.7.4.3



2011-04-16 00:59:50

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [RFC 0/4] hcimgmt: command line tool for mgmt interface

Hi Vinicious,

> When the mgmt interface was first proposed Johan sent a patch
> that introduced this tool, I updated it for the current MGMT API
> and added a couple new commands.

overall I like to have a tool called bt in similar fashion like iw, ip
etc. And I really want the hci* prefix getting out of tools namespace.

Regards

Marcel



2011-04-15 17:57:45

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [RFC 4/4] hcimgmt: add support for setting pairable mode

---
tools/hcimgmt.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 54 insertions(+), 1 deletions(-)

diff --git a/tools/hcimgmt.c b/tools/hcimgmt.c
index 7744344..9998982 100644
--- a/tools/hcimgmt.c
+++ b/tools/hcimgmt.c
@@ -407,6 +407,58 @@ static void cmd_clearkeys(int dev_id, int argc, char **argv)
}
}

+static struct option pairable_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *pairable_help =
+ "Usage:\n"
+ "\tclearkeys <1|0>\n";
+
+static void cmd_pairable(int dev_id, int argc, char **argv)
+{
+ int opt, dd;
+ ssize_t ret;
+ struct mgmt_mode cp;
+
+ for_each_opt(opt, pairable_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", pairable_help);
+ return;
+ }
+ }
+
+ helper_arg(1, 1, &argc, &argv, pairable_help);
+
+ memset(&cp, 0, sizeof(cp));
+
+ dd = hci_open_channel(HCI_DEV_NONE, HCI_CHANNEL_CONTROL);
+ if (dd < 0) {
+ perror("HCI Control socket open failed");
+ exit(1);
+ }
+
+ if (dev_id < 0) {
+ dev_id = hci_get_route(NULL);
+ if (dev_id < 0) {
+ perror("Device is not available");
+ exit(1);
+ }
+ }
+
+ cp.val = (uint8_t) strtol(argv[0], NULL, 0);
+
+ ret = hci_mgmt_cmd(dd, dev_id, MGMT_OP_SET_PAIRABLE, &cp, sizeof(cp),
+ MGMT_EV_CMD_COMPLETE, NULL, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Command failed: %s (%zd)\n",
+ strerror(-ret), -ret);
+ exit(1);
+ }
+}
+
static struct {
char *cmd;
void (*func)(int dev_id, int argc, char **argv);
@@ -417,7 +469,8 @@ static struct {
{ "features", cmd_features, "Control API features" },
{ "info", cmd_info, "Read controller info" },
{ "setiocap", cmd_setiocap, "Set IO Capabilities" },
- { "clearkeys", cmd_clearkeys, "Clear key list" },
+ { "clearkeys", cmd_clearkeys, "Clear key list" },
+ { "pairable", cmd_pairable, "Set pairable mode" },
{ NULL, NULL, 0 }
};

--
1.7.4.3


2011-04-15 17:57:44

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [RFC 3/4] Add command to clear the link key list via MGMT interface

---
tools/hcimgmt.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 51 insertions(+), 0 deletions(-)

diff --git a/tools/hcimgmt.c b/tools/hcimgmt.c
index fc88654..7744344 100644
--- a/tools/hcimgmt.c
+++ b/tools/hcimgmt.c
@@ -357,6 +357,56 @@ static void cmd_setiocap(int dev_id, int argc, char **argv)
}
}

+static struct option clearkeys_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *clearkeys_help =
+ "Usage:\n"
+ "\tclearkeys\n";
+
+static void cmd_clearkeys(int dev_id, int argc, char **argv)
+{
+ int opt, dd;
+ ssize_t ret;
+ struct mgmt_cp_load_keys cp;
+
+ for_each_opt(opt, clearkeys_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", clearkeys_help);
+ return;
+ }
+ }
+
+ helper_arg(0, 0, &argc, &argv, setiocap_help);
+
+ memset(&cp, 0, sizeof(cp));
+
+ dd = hci_open_channel(HCI_DEV_NONE, HCI_CHANNEL_CONTROL);
+ if (dd < 0) {
+ perror("HCI Control socket open failed");
+ exit(1);
+ }
+
+ if (dev_id < 0) {
+ dev_id = hci_get_route(NULL);
+ if (dev_id < 0) {
+ perror("Device is not available");
+ exit(1);
+ }
+ }
+
+ ret = hci_mgmt_cmd(dd, dev_id, MGMT_OP_LOAD_KEYS, &cp, sizeof(cp),
+ MGMT_EV_CMD_COMPLETE, NULL, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Command failed: %s (%zd)\n",
+ strerror(-ret), -ret);
+ exit(1);
+ }
+}
+
static struct {
char *cmd;
void (*func)(int dev_id, int argc, char **argv);
@@ -367,6 +417,7 @@ static struct {
{ "features", cmd_features, "Control API features" },
{ "info", cmd_info, "Read controller info" },
{ "setiocap", cmd_setiocap, "Set IO Capabilities" },
+ { "clearkeys", cmd_clearkeys, "Clear key list" },
{ NULL, NULL, 0 }
};

--
1.7.4.3


2011-04-15 17:57:42

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [RFC 1/4] Add hcimgmt command line test tool

From: Johan Hedberg <[email protected]>

---
Makefile.tools | 5 +-
tools/hcimgmt.c | 362 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 366 insertions(+), 1 deletions(-)
create mode 100644 tools/hcimgmt.c

diff --git a/Makefile.tools b/Makefile.tools
index 364db37..98fb99c 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -5,7 +5,7 @@ conf_DATA += tools/rfcomm.conf
endif

bin_PROGRAMS += tools/rfcomm tools/l2ping \
- tools/hcitool tools/sdptool tools/ciptool
+ tools/hcitool tools/sdptool tools/ciptool tools/hcimgmt

sbin_PROGRAMS += tools/hciattach tools/hciconfig

@@ -38,6 +38,9 @@ tools_hcitool_SOURCES = tools/hcitool.c src/oui.h src/oui.c \
src/textfile.h src/textfile.c
tools_hcitool_LDADD = lib/libbluetooth.la

+tools_hcimgmt_SOURCES = tools/hcimgmt.c
+tools_hcimgmt_LDADD = lib/libbluetooth.la
+
tools_sdptool_SOURCES = tools/sdptool.c src/sdp-xml.h src/sdp-xml.c
tools_sdptool_LDADD = lib/libbluetooth.la

diff --git a/tools/hcimgmt.c b/tools/hcimgmt.c
new file mode 100644
index 0000000..99338ed
--- /dev/null
+++ b/tools/hcimgmt.c
@@ -0,0 +1,362 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ *
+ *
+ * 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 <bluetooth/mgmt.h>
+
+#define MGMT_BUF_SIZE 1024
+
+#define for_each_opt(opt, long, short) while ((opt=getopt_long(argc, argv, short ? short:"+", long, NULL)) != -1)
+
+static void usage(void);
+
+/* Display local devices */
+
+static struct option dev_options[] = {
+ { "help", 0, 0, 'h' },
+ {0, 0, 0, 0 }
+};
+
+static int hci_open_channel(int dev_id, unsigned short channel)
+{
+ struct sockaddr_hci a;
+ int dd, err;
+
+ /* Create HCI socket */
+ dd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+ if (dd < 0)
+ return -errno;
+
+ /* Bind socket to the HCI device */
+ memset(&a, 0, sizeof(a));
+ a.hci_family = AF_BLUETOOTH;
+ a.hci_dev = dev_id;
+ a.hci_channel = channel;
+ if (bind(dd, (struct sockaddr *) &a, sizeof(a)) < 0) {
+ err = -errno;
+ goto fail;
+ }
+
+ return dd;
+
+fail:
+ close(dd);
+ return err;
+}
+
+static ssize_t hci_mgmt_cmd(int dd, uint16_t index, uint16_t opcode, void *data,
+ uint16_t len, uint16_t rsp_ev, void *rsp, uint16_t rsp_len)
+{
+ ssize_t ret;
+ size_t copied, plen;
+ struct mgmt_hdr hdr = { htobs(opcode), htobs(index), htobs(len) };
+ struct mgmt_ev_cmd_complete *cc;
+ char buf[MGMT_BUF_SIZE];
+
+ memcpy(buf, &hdr, sizeof(hdr));
+ memcpy(buf + sizeof(hdr), data, len);
+
+ ret = write(dd, buf, sizeof(hdr) + len);
+ if (ret < 0)
+ return -errno;
+
+ ret = read(dd, buf, sizeof(buf));
+ if (ret < 0)
+ return -errno;
+
+ if (ret < (ssize_t) sizeof(struct mgmt_hdr)) {
+ fprintf(stderr,
+ "ret (%zd) != sizeof(struct mgmt_hdr) (%zu)\n",
+ ret, sizeof(struct mgmt_hdr));
+ return -EIO;
+ }
+
+ memcpy(&hdr, buf, sizeof(hdr));
+
+ if (btohs(hdr.len) != ret - sizeof(hdr)) {
+ fprintf(stderr, "hdr.len (%u) != ret - sizeof(hdr) (%zu)\n",
+ btohs(hdr.len), ret - sizeof(hdr));
+ return -EIO;
+ }
+
+ if (btohs(hdr.opcode) != rsp_ev) {
+ fprintf(stderr, "hdr.opcode (%u) != rsp_ev (%u)\n",
+ btohs(hdr.opcode), rsp_ev);
+ return -EIO;
+ }
+
+ cc = (void *) &buf[sizeof(hdr)];
+
+ if (btohs(cc->opcode) != opcode) {
+ fprintf(stderr, "cc.opcode (%u) != opcode (%u)\n",
+ btohs(cc->opcode), opcode);
+ return -EIO;
+ }
+
+ /* sizeof(opcode) == 2 */
+ plen = btohs(hdr.len) - 2;
+ copied = rsp_len < plen ? plen : rsp_len;
+
+ memcpy(rsp, cc->data, copied);
+
+ return copied;
+}
+
+static void print_bytes(uint8_t *bytes, size_t count)
+{
+ size_t i;
+
+ for (i = 0; i < count; i++)
+ printf("%02x ", bytes[i]);
+}
+
+static void cmd_features(int dev_id, int argc, char **argv)
+{
+ int dd;
+ ssize_t ret;
+ struct mgmt_rp_read_features rp;
+
+ dd = hci_open_channel(HCI_DEV_NONE, HCI_CHANNEL_CONTROL);
+ if (dd < 0) {
+ perror("HCI Control socket open failed");
+ exit(1);
+ }
+
+ ret = hci_mgmt_cmd(dd, MGMT_INDEX_NONE, MGMT_OP_READ_FEATURES, NULL, 0,
+ MGMT_EV_CMD_COMPLETE, &rp, sizeof(rp));
+ if (ret < 0) {
+ fprintf(stderr, "Command failed: %s (%zd)\n",
+ strerror(-ret), -ret);
+ exit(1);
+ }
+
+ printf("HCI Control API features: ");
+ print_bytes(rp.features, 8);
+ printf("\n");
+}
+
+static void cmd_version(int dev_id, int argc, char **argv)
+{
+ int dd;
+ ssize_t ret;
+ struct mgmt_rp_read_version rp;
+
+ dd = hci_open_channel(HCI_DEV_NONE, HCI_CHANNEL_CONTROL);
+ if (dd < 0) {
+ perror("HCI Control socket open failed");
+ exit(1);
+ }
+
+ ret = hci_mgmt_cmd(dd, dev_id, MGMT_OP_READ_VERSION, NULL, 0,
+ MGMT_EV_CMD_COMPLETE, &rp, sizeof(rp));
+ if (ret < 0) {
+ fprintf(stderr, "Command failed: %s (%zd)\n",
+ strerror(-ret), -ret);
+ exit(1);
+ }
+
+ printf("HCI Control API version %u.%u\n", rp.version,
+ btohs(rp.revision));
+}
+
+static const char *dev_help =
+ "Usage:\n"
+ "\tdev\n";
+
+static void cmd_dev(int dev_id, int argc, char **argv)
+{
+ int opt, dd, i;
+ ssize_t ret;
+ uint16_t *ptr;
+ struct mgmt_rp_read_index_list rp;
+
+ for_each_opt(opt, dev_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", dev_help);
+ return;
+ }
+ }
+
+ dd = hci_open_channel(HCI_DEV_NONE, HCI_CHANNEL_CONTROL);
+ if (dd < 0) {
+ perror("HCI Control socket open failed");
+ exit(1);
+ }
+
+ ret = hci_mgmt_cmd(dd, MGMT_INDEX_NONE, MGMT_OP_READ_INDEX_LIST,
+ NULL, 0, MGMT_EV_CMD_COMPLETE, &rp, sizeof(rp));
+ if (ret < 0) {
+ fprintf(stderr, "Failed\n");
+ exit(1);
+ }
+
+ ptr = rp.index;
+ for (i = 0; i < btohs(rp.num_controllers); i++, ptr += 2) {
+ uint16_t index;
+ memcpy(&index, ptr, 2);
+ printf("hci%u ", btohs(index));
+ }
+
+ printf("\n");
+}
+
+static void cmd_info(int dev_id, int argc, char **argv)
+{
+ int dd;
+ uint16_t id;
+ ssize_t ret;
+ char addr[18];
+ struct mgmt_rp_read_info rp;
+
+ dd = hci_open_channel(HCI_DEV_NONE, HCI_CHANNEL_CONTROL);
+ if (dd < 0) {
+ perror("HCI Control socket open failed");
+ exit(1);
+ }
+
+ if (dev_id < 0) {
+ dev_id = hci_get_route(NULL);
+ if (dev_id < 0) {
+ perror("Device is not available");
+ exit(1);
+ }
+ }
+
+ id = (uint16_t) dev_id;
+
+ ret = hci_mgmt_cmd(dd, id, MGMT_OP_READ_INFO, NULL, 0,
+ MGMT_EV_CMD_COMPLETE, &rp, sizeof(rp));
+ if (ret < 0) {
+ fprintf(stderr, "Command failed: %s (%zd)\n",
+ strerror(-ret), -ret);
+ exit(1);
+ }
+
+ ba2str(&rp.bdaddr, addr);
+
+ printf("hci%u type %u addr %s features ", btohs(id), rp.type, addr);
+ print_bytes(rp.features, 8);
+ printf("\n");
+}
+
+static struct {
+ char *cmd;
+ void (*func)(int dev_id, int argc, char **argv);
+ char *doc;
+} command[] = {
+ { "dev", cmd_dev, "Display local devices" },
+ { "version", cmd_version, "Display version" },
+ { "features", cmd_features, "Control API features" },
+ { "info", cmd_info, "Read controller info" },
+ { NULL, NULL, 0 }
+};
+
+static void usage(void)
+{
+ int i;
+
+ printf("hcictl - HCI Control ver %s\n", VERSION);
+ printf("Usage:\n"
+ "\thcictl [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"
+ "\thcictl <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[1].cmd)))
+ continue;
+ command[i].func(dev_id, argc, argv);
+ break;
+ }
+ return 0;
+}
--
1.7.4.3


2011-04-15 17:57:43

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [RFC 2/4] hcimgmt: Add command to set IO Capabilities

---
tools/hcimgmt.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 80 insertions(+), 0 deletions(-)

diff --git a/tools/hcimgmt.c b/tools/hcimgmt.c
index 99338ed..fc88654 100644
--- a/tools/hcimgmt.c
+++ b/tools/hcimgmt.c
@@ -46,6 +46,31 @@

#define for_each_opt(opt, long, short) while ((opt=getopt_long(argc, argv, short ? short:"+", long, NULL)) != -1)

+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 void usage(void);

/* Display local devices */
@@ -131,6 +156,9 @@ static ssize_t hci_mgmt_cmd(int dd, uint16_t index, uint16_t opcode, void *data,
return -EIO;
}

+ if (rsp == NULL)
+ return 0;
+
/* sizeof(opcode) == 2 */
plen = btohs(hdr.len) - 2;
copied = rsp_len < plen ? plen : rsp_len;
@@ -278,6 +306,57 @@ static void cmd_info(int dev_id, int argc, char **argv)
printf("\n");
}

+static struct option setiocap_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *setiocap_help =
+ "Usage:\n"
+ "\tsetiocap <value>\n";
+
+static void cmd_setiocap(int dev_id, int argc, char **argv)
+{
+ int opt, dd;
+ ssize_t ret;
+ struct mgmt_cp_set_io_capability cp;
+
+ for_each_opt(opt, setiocap_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", setiocap_help);
+ return;
+ }
+ }
+
+ helper_arg(1, 1, &argc, &argv, setiocap_help);
+
+ memset(&cp, 0, sizeof(cp));
+ cp.io_capability = (uint8_t) strtol(argv[0], NULL, 0);
+
+ dd = hci_open_channel(HCI_DEV_NONE, HCI_CHANNEL_CONTROL);
+ if (dd < 0) {
+ perror("HCI Control socket open failed");
+ exit(1);
+ }
+
+ if (dev_id < 0) {
+ dev_id = hci_get_route(NULL);
+ if (dev_id < 0) {
+ perror("Device is not available");
+ exit(1);
+ }
+ }
+
+ ret = hci_mgmt_cmd(dd, dev_id, MGMT_OP_SET_IO_CAPABILITY, &cp, sizeof(cp),
+ MGMT_EV_CMD_COMPLETE, NULL, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Command failed: %s (%zd)\n",
+ strerror(-ret), -ret);
+ exit(1);
+ }
+}
+
static struct {
char *cmd;
void (*func)(int dev_id, int argc, char **argv);
@@ -287,6 +366,7 @@ static struct {
{ "version", cmd_version, "Display version" },
{ "features", cmd_features, "Control API features" },
{ "info", cmd_info, "Read controller info" },
+ { "setiocap", cmd_setiocap, "Set IO Capabilities" },
{ NULL, NULL, 0 }
};

--
1.7.4.3