summaryrefslogtreecommitdiffstats
path: root/tools/hcitool.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/hcitool.c')
-rw-r--r--tools/hcitool.c368
1 files changed, 368 insertions, 0 deletions
diff --git a/tools/hcitool.c b/tools/hcitool.c
new file mode 100644
index 00000000..4d6380fb
--- /dev/null
+++ b/tools/hcitool.c
@@ -0,0 +1,368 @@
+/*
+ BlueZ - Bluetooth protocol stack for Linux
+ Copyright (C) 2000-2001 Qualcomm Incorporated
+
+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 as
+ published by the Free Software Foundation;
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM,
+ OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER
+ RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
+ USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS,
+ TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * $Id$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <termios.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <asm/types.h>
+
+#include <bluetooth.h>
+#include <hci.h>
+#include <hci_lib.h>
+
+extern int optind,opterr,optopt;
+extern char *optarg;
+
+static int ctl;
+
+static int for_each_dev(int flag, int(*func)(int d, long arg), long arg)
+{
+ struct hci_dev_list_req *dl;
+ struct hci_dev_req *dr;
+ int i;
+
+ dl = malloc(HCI_MAX_DEV * sizeof(struct hci_dev_req) + sizeof(uint16_t));
+ if (!dl) {
+ perror("Can't allocate memory");
+ return -1;
+ }
+ dl->dev_num = HCI_MAX_DEV;
+ dr = dl->dev_req;
+
+ if (ioctl(ctl, HCIGETDEVLIST, (void*)dl)) {
+ perror("Can't get device list");
+ return -1;
+ }
+
+ if (!dl->dev_num)
+ return -1;
+
+ for (i=0; i < dl->dev_num; i++, dr++) {
+ if (dr->dev_opt & (1<<flag)) {
+ if (!func || func(dr->dev_id, arg))
+ return dr->dev_id;
+ }
+ }
+ return -1;
+}
+
+static int other_bdaddr(int dev_id, long arg)
+{
+ struct hci_dev_info di = {dev_id: dev_id};
+ if (ioctl(ctl, HCIGETDEVINFO, (void*) &di))
+ return 0;
+ return bacmp((bdaddr_t *)arg, &di.bdaddr);
+}
+
+static int get_route(bdaddr_t *bdaddr)
+{
+ if (bdaddr)
+ return for_each_dev(HCI_UP, other_bdaddr, (long) bdaddr);
+ else
+ return for_each_dev(HCI_UP, NULL, 0);
+}
+
+static int dev_info(int dev_id, long arg)
+{
+ struct hci_dev_info di = {dev_id: dev_id};
+ bdaddr_t bdaddr;
+ if (ioctl(ctl, HCIGETDEVINFO, (void*) &di))
+ return 0;
+
+ baswap(&bdaddr, &di.bdaddr);
+ printf("\t%s\t%s\n", di.name, batostr(&bdaddr));
+ return 0;
+}
+
+static int conn_list(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(ctl, HCIGETCONNLIST, (void*)cl)) {
+ perror("Can't get connection list");
+ exit(1);
+ }
+
+ for (i=0; i < cl->conn_num; i++, ci++) {
+ bdaddr_t bdaddr;
+ baswap(&bdaddr, &ci->bdaddr);
+ printf("\t%s %s %s handle %d state %d lm %s\n",
+ ci->out ? "<" : ">",
+ ci->type == ACL_LINK ? "ACL" : "SCO",
+ batostr(&bdaddr), ci->handle,
+ ci->state,
+ hci_lmtostr(ci->link_mode));
+ }
+ return 0;
+}
+
+static int find_conn(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(ctl, 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))
+ return 1;
+ return 0;
+}
+
+static void cmd_dev(int dev_id, char **opt, int nopt)
+{
+ printf("Devices:\n");
+ for_each_dev(HCI_UP, dev_info, 0);
+}
+
+static void cmd_inq(int dev_id, char **opt, int nopt)
+{
+ inquiry_info *info;
+ int i, num_rsp = 0, length, flags;
+ bdaddr_t bdaddr;
+
+ if (dev_id < 0)
+ dev_id = get_route(NULL);
+
+ if (nopt >= 1)
+ length = atoi(opt[0]);
+ else
+ length = 10; /* 10 seconds */
+
+ flags = 0;
+ if (nopt >= 2)
+ flags |= !strncasecmp("f", opt[1], 1) ? IREQ_CACHE_FLUSH : 0;
+
+ printf("Inquiring ...\n");
+ info = hci_inquiry(dev_id, length, &num_rsp, NULL, flags);
+
+ if (!info) {
+ perror("Inquiry failed.");
+ exit(1);
+ }
+
+ for (i = 0; i < num_rsp; i++) {
+ baswap(&bdaddr, &(info+i)->bdaddr);
+ printf("\t%s\tclock offset: 0x%4.4x\tclass: 0x%2.2x%2.2x%2.2x\n",
+ batostr(&bdaddr), (info+i)->clock_offset,
+ (info+i)->dev_class[2],
+ (info+i)->dev_class[1],
+ (info+i)->dev_class[0]);
+ }
+ free(info);
+}
+
+static void cmd_con(int dev_id, char **opt, int nopt)
+{
+ printf("Connections:\n");
+ if (dev_id < 0)
+ for_each_dev(HCI_UP, conn_list, 0);
+ else
+ conn_list(dev_id, 0);
+}
+
+static void cmd_cc(int dev_id, char **opt, int nopt)
+{
+ bdaddr_t bdaddr;
+ int dd, ptype, role;
+
+ if (nopt < 1)
+ return;
+
+ baswap(&bdaddr, strtoba(opt[0]));
+
+ if (dev_id < 0) {
+ dev_id = 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 (nopt >= 2)
+ hci_strtoptype(opt[1], &ptype);
+ else
+ ptype = HCI_DM1 | HCI_DM3 | HCI_DM5 | HCI_DH1 | HCI_DH3 | HCI_DH5;
+
+ if (nopt >= 3)
+ role = !strncasecmp("m", opt[2], 1) ? 0 : 1;
+ else
+ role = 0;
+
+ hci_create_connection(dd, &bdaddr, ptype, role, 1000);
+
+ hci_close_dev(dd);
+}
+
+static void cmd_dc(int dev_id, char **opt, int nopt)
+{
+ struct hci_conn_info_req *cr;
+ bdaddr_t bdaddr;
+ int dd;
+
+ if (nopt < 1)
+ return;
+
+ baswap(&bdaddr, strtoba(*opt));
+
+ if (dev_id < 0) {
+ dev_id = 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)
+ return;
+
+ bacpy(&cr->bdaddr, &bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ perror("Get connection info failed");
+ exit(1);
+ }
+
+ hci_disconnect(dd, cr->conn_info->handle, 0, 100);
+
+ close(dd);
+ free(cr);
+}
+
+struct {
+ char *cmd;
+ void (*func)(int dev_id, char **opt, int nopt);
+ char *opt;
+ char *doc;
+} command[] = {
+ { "dev", cmd_dev, 0, "Display local devices" },
+ { "inq", cmd_inq, "[lenght] [flush]", "Inquire remote devices" },
+ { "con", cmd_con, 0, "Display active connections" },
+ { "cc", cmd_cc, "<bdaddr> [pkt type] [role]", "Create connection to remote device" },
+ { "dc", cmd_dc, "<bdaddr>", "Disconnect from remote device" },
+ { NULL, NULL, 0}
+};
+
+static void usage(void)
+{
+ int i;
+
+ printf("hcitool - HCI Tool\n");
+ printf("Usage:\n"
+ "\thcitool [-i hciX] [command]\n");
+ printf("Commands:\n");
+ for (i=0; command[i].cmd; i++)
+ printf("\t%-4s %-20s\t%s\n", command[i].cmd,
+ command[i].opt ? command[i].opt : " ",
+ command[i].doc);
+}
+
+int main(int argc, char *argv[], char *env[])
+{
+ int opt, i, dev_id = -1;
+ char *dev;
+
+ while ((opt=getopt(argc, argv, "i:h")) != EOF) {
+ switch(opt) {
+ case 'i':
+ dev = strdup(optarg);
+ dev_id = atoi(dev + 3);
+ break;
+
+ case 'h':
+ default:
+ usage();
+ exit(0);
+ }
+ }
+
+ if (argc - optind < 1) {
+ usage();
+ exit(0);
+ }
+
+ /* Open HCI socket */
+ if ((ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)) < 0) {
+ perror("Can't open HCI socket.");
+ exit(1);
+ }
+
+ for (i=0; command[i].cmd; i++) {
+ if (strncmp(command[i].cmd, argv[optind], 3))
+ continue;
+ optind++;
+ command[i].func(dev_id, argv + optind, argc - optind);
+ break;
+ }
+
+ close(ctl);
+ return 0;
+}