bluez/tools/hcitool.c
2004-02-18 23:20:50 +00:00

1433 lines
28 KiB
C

/*
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 <ctype.h>
#include <termios.h>
#include <fcntl.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
extern int optind,opterr,optopt;
extern char *optarg;
#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 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];
ba2str(&ci->bdaddr, addr);
printf("\t%s %s %s handle %d state %d lm %s\n",
ci->out ? "<" : ">",
ci->type == ACL_LINK ? "ACL" : "SCO",
addr, ci->handle, ci->state,
hci_lmtostr(ci->link_mode));
}
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))
return 1;
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");
}
/* Display local devices */
static struct option dev_options[] = {
{"help", 0,0, 'h'},
{0, 0, 0, 0}
};
static 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(dev_help);
return;
}
}
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'},
{"flush", 0,0, 'f'},
{0, 0, 0, 0}
};
static 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 [--flush] flush the inquiry cache\n";
static void cmd_inq(int dev_id, int argc, char **argv)
{
int num_rsp, length, flags;
inquiry_info *info = NULL;
char addr[18];
int i, opt;
length = 8; /* ~10 seconds */
num_rsp = 100;
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 'f':
flags |= IREQ_CACHE_FLUSH;
break;
default:
printf(inq_help);
return;
}
}
printf("Inquiring ...\n");
num_rsp = hci_inquiry(dev_id, length, num_rsp, NULL, &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, (info+i)->clock_offset,
(info+i)->dev_class[2],
(info+i)->dev_class[1],
(info+i)->dev_class[0]);
}
free(info);
}
/* Device scanning */
static struct option scan_options[] = {
{"help", 0,0, 'h'},
{"length", 1,0, 'l'},
{"numrsp", 1,0, 'n'},
{"flush", 0,0, 'f'},
{0, 0, 0, 0}
};
static char *scan_help =
"Usage:\n"
"\tscan [--length=N] [--numrsp=N] [--flush]\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[248];
int i, opt, dd;
length = 8; /* ~10 seconds */
num_rsp = 100;
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 'f':
flags |= IREQ_CACHE_FLUSH;
break;
default:
printf(scan_help);
return;
}
}
if (dev_id < 0) {
dev_id = hci_get_route(NULL);
if (dev_id < 0) {
perror("Device is not available");
exit(1);
}
}
printf("Scanning ...\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);
}
for (i = 0; i < num_rsp; i++) {
memset(name, 0, sizeof(name));
if (hci_read_remote_name(dd, &(info+i)->bdaddr, sizeof(name), name, 100000) < 0)
strcpy(name, "n/a");
ba2str(&(info+i)->bdaddr, addr);
printf("\t%s\t%s\n", addr, name);
}
close(dd);
free(info);
}
/* Remote name */
static struct option name_options[] = {
{"help", 0,0, 'h'},
{0, 0, 0, 0}
};
static 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(name_help);
return;
}
}
argc -= optind;
argv += optind;
if (argc < 1) {
printf(name_help);
return;
}
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);
close(dd);
}
/* Info about remote device */
static struct option info_options[] = {
{"help", 0,0, 'h'},
{0, 0, 0, 0}
};
static 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;
char name[248];
unsigned char features[8];
struct hci_version version;
struct hci_conn_info_req *cr;
int opt, dd, cc = 0;
for_each_opt(opt, info_options, NULL) {
switch(opt) {
default:
printf(info_help);
return;
}
}
argc -= optind;
argv += optind;
if (argc < 1) {
printf(info_help);
return;
}
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);
}
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(HCI_DM1 | HCI_DH1), 0, 0, &handle, 25000) < 0) {
perror("Can't create connection");
close(dd);
exit(1);
}
cc = 1;
} else
handle = cr->conn_info->handle;
printf("\tBD Address: %s\n", argv[0]);
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) {
printf("\tLMP Version: %s (0x%x) LMP Subversion: 0x%x\n"
"\tManufacturer: %s (%d)\n",
lmp_vertostr(version.lmp_ver), version.lmp_ver, version.lmp_subver,
bt_compidtostr(version.manufacturer), version.manufacturer);
}
if (hci_read_remote_features(dd, handle, features, 20000) == 0) {
printf("\tFeatures: 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%s\n",
features[0], features[1], features[2], features[3],
features[4], features[5], features[6], features[7],
lmp_featurestostr(features, "\t\t", 3));
}
if (cc)
hci_disconnect(dd, handle, HCI_OE_USER_ENDED_CONNECTION, 10000);
close(dd);
}
/* Send arbitrary HCI commands */
static struct option cmd_options[] = {
{"help", 0,0, 'h'},
{0, 0, 0, 0}
};
static 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)
{
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(cmd_help);
return;
}
}
argc -= optind;
argv += optind;
if (argc < 2) {
printf(cmd_help);
return;
}
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(cmd_help);
return;
}
for (i = 2, len = 0; i < argc && len < 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);
return;
}
/* Display active connections */
static struct option con_options[] = {
{"help", 0,0, 'h'},
{0, 0, 0, 0}
};
static 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(con_help);
return;
}
}
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 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;
int opt, ptype, dd;
uint16_t handle;
uint8_t role;
role = 0;
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(cc_help);
return;
}
}
argc -= optind;
argv += optind;
if (argc < 1) {
printf(cc_help);
return;
}
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), 0, role, &handle, 1000) < 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 char *dc_help =
"Usage:\n"
"\tdc <bdaddr>\n";
static void cmd_dc(int dev_id, int argc, char **argv)
{
struct hci_conn_info_req *cr;
bdaddr_t bdaddr;
int opt, dd;
for_each_opt(opt, dc_options, NULL) {
switch(opt) {
default:
printf(dc_help);
return;
}
}
argc -= optind;
argv += optind;
if (argc < 1) {
printf(dc_help);
return;
}
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)
return;
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, cr->conn_info->handle, HCI_OE_USER_ENDED_CONNECTION, 100) < 0)
perror("Disconnect failed");
close(dd);
free(cr);
}
/* Role switch */
static struct option sr_options[] = {
{"help", 0,0, 'h'},
{0, 0, 0, 0}
};
static char *sr_help =
"Usage:\n"
"\tsr <bdaddr> <role>\n";
static void cmd_sr(int dev_id, int argc, char **argv)
{
struct hci_request rq;
switch_role_cp cp;
evt_role_change rp;
int opt, dd;
for_each_opt(opt, sr_options, NULL) {
switch(opt) {
default:
printf(sr_help);
return;
}
}
argc -= optind;
argv += optind;
if (argc < 2) {
printf(sr_help);
return;
}
str2ba(argv[0], &cp.bdaddr);
switch (argv[1][0]) {
case 'm':
cp.role = 0;
break;
case 's':
cp.role = 1;
break;
default:
cp.role = atoi(argv[1]);
break;
}
if (dev_id < 0) {
dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &cp.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);
}
memset(&rq, 0, sizeof(rq));
rq.ogf = OGF_LINK_POLICY;
rq.ocf = OCF_SWITCH_ROLE;
rq.cparam = &cp;
rq.clen = SWITCH_ROLE_CP_SIZE;
rq.rparam = &rp;
rq.rlen = EVT_ROLE_CHANGE_SIZE;
rq.event = EVT_ROLE_CHANGE;
if (hci_send_req(dd, &rq, 300) < 0) {
perror("Switch role request failed");
exit(1);
}
if (rp.status) {
fprintf(stderr, "Switch role cmd failed (0x%2.2X)\n", rp.status);
exit(1);
}
close(dd);
}
/* Read RSSI */
static struct option rssi_options[] = {
{"help", 0,0, 'h'},
{0, 0, 0, 0}
};
static 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;
struct hci_request rq;
read_rssi_rp rp;
bdaddr_t bdaddr;
int opt, dd;
for_each_opt(opt, rssi_options, NULL) {
switch(opt) {
default:
printf(rssi_help);
return;
}
}
argc -= optind;
argv += optind;
if (argc < 1) {
printf(rssi_help);
return;
}
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)
return;
bacpy(&cr->bdaddr, &bdaddr);
cr->type = ACL_LINK;
if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
perror("Get connection info failed");
exit(1);
}
memset(&rq, 0, sizeof(rq));
rq.ogf = OGF_STATUS_PARAM;
rq.ocf = OCF_READ_RSSI;
rq.cparam = &cr->conn_info->handle;
rq.clen = 2;
rq.rparam = &rp;
rq.rlen = READ_RSSI_RP_SIZE;
if (hci_send_req(dd, &rq, 100) < 0) {
perror("Read RSSI failed");
exit(1);
}
if (rp.status) {
printf("Read RSSI returned (error) status 0x%2.2X\n", rp.status);
exit(1);
}
printf("RSSI return value: %d\n", rp.rssi);
close(dd);
free(cr);
}
/* Get link quality */
static struct option lq_options[] = {
{"help", 0,0, 'h'},
{0, 0, 0, 0}
};
static 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;
struct hci_request rq;
get_link_quality_rp rp;
bdaddr_t bdaddr;
int opt, dd;
for_each_opt(opt, lq_options, NULL) {
switch(opt) {
default:
printf(lq_help);
return;
}
}
argc -= optind;
argv += optind;
if (argc < 1) {
printf(lq_help);
return;
}
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)
return;
bacpy(&cr->bdaddr, &bdaddr);
cr->type = ACL_LINK;
if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
perror("Get connection info failed");
exit(1);
}
memset(&rq, 0, sizeof(rq));
rq.ogf = OGF_STATUS_PARAM;
rq.ocf = OCF_GET_LINK_QUALITY;
rq.cparam = &cr->conn_info->handle;
rq.clen = 2;
rq.rparam = &rp;
rq.rlen = GET_LINK_QUALITY_RP_SIZE;
if (hci_send_req(dd, &rq, 100) < 0) {
perror("HCI get_link_quality request failed");
exit(1);
}
if (rp.status) {
fprintf(stderr, "HCI get_link_quality cmd failed (0x%2.2X)\n",
rp.status);
exit(1);
}
printf("Link quality: %d\n", rp.link_quality);
close(dd);
free(cr);
}
/* Get transmit power level */
static struct option tpl_options[] = {
{"help", 0,0, 'h'},
{0, 0, 0, 0}
};
static 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;
struct hci_request rq;
read_transmit_power_level_cp cp;
read_transmit_power_level_rp rp;
bdaddr_t bdaddr;
int opt, dd;
for_each_opt(opt, tpl_options, NULL) {
switch(opt) {
default:
printf(tpl_help);
return;
}
}
argc -= optind;
argv += optind;
if (argc < 1) {
printf(tpl_help);
return;
}
str2ba(argv[0], &bdaddr);
cp.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)
return;
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 = cr->conn_info->handle;
memset(&rq, 0, sizeof(rq));
rq.ogf = OGF_HOST_CTL;
rq.ocf = OCF_READ_TRANSMIT_POWER_LEVEL;
rq.cparam = &cp;
rq.clen = READ_TRANSMIT_POWER_LEVEL_CP_SIZE;
rq.rparam = &rp;
rq.rlen = READ_TRANSMIT_POWER_LEVEL_RP_SIZE;
if (hci_send_req(dd, &rq, 100) < 0) {
perror("HCI read transmit power level request failed");
exit(1);
}
if (rp.status) {
fprintf(stderr, "HCI read_transmit_power_level cmd failed (0x%2.2X)\n",
rp.status);
exit(1);
}
printf("%s transmit power level: %d\n",
(cp.type == 0) ? "Current" : "Maximum", rp.level);
close(dd);
free(cr);
}
/* Set connection packet type */
static struct option cpt_options[] = {
{"help", 0,0, 'h'},
{0, 0, 0, 0}
};
static 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;
int opt, dd, ptype;
for_each_opt(opt, cpt_options, NULL) {
switch(opt) {
default:
printf(cpt_help);
return;
}
}
argc -= optind;
argv += optind;
if (argc < 2) {
printf(cpt_help);
return;
}
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)
return;
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 = 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);
}
close(dd);
free(cr);
}
/* Get/Set link supervision timeout */
static struct option lst_options[] = {
{"help", 0,0, 'h'},
{0, 0, 0, 0}
};
static 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;
struct hci_request rq;
read_link_supervision_timeout_rp rp;
write_link_supervision_timeout_cp cp;
bdaddr_t bdaddr;
int opt, dd;
for_each_opt(opt, lst_options, NULL) {
switch(opt) {
default:
printf(lst_help);
return;
}
}
argc -= optind;
argv += optind;
if (argc < 1) {
printf(lst_help);
return;
}
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)
return;
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) {
memset(&rq, 0, sizeof(rq));
rq.ogf = OGF_HOST_CTL;
rq.ocf = OCF_READ_LINK_SUPERVISION_TIMEOUT;
rq.cparam = &cr->conn_info->handle;
rq.clen = 2;
rq.rparam = &rp;
rq.rlen = READ_LINK_SUPERVISION_TIMEOUT_RP_SIZE;
if (hci_send_req(dd, &rq, 100) < 0) {
perror("HCI read_link_supervision_timeout request failed");
exit(1);
}
if (rp.status) {
fprintf(stderr, "HCI read_link_supervision_timeout failed (0x%2.2X)\n",
rp.status);
exit(1);
}
if (rp.link_sup_to)
printf("Link supervision timeout: %u slots (%.2f msec)\n",
rp.link_sup_to, (float)rp.link_sup_to * 0.625);
else
printf("Link supervision timeout never expires\n");
}
else {
cp.handle = cr->conn_info->handle;
cp.link_sup_to = strtol(argv[1], NULL, 10);
memset(&rq, 0, sizeof(rq));
rq.ogf = OGF_HOST_CTL;
rq.ocf = OCF_WRITE_LINK_SUPERVISION_TIMEOUT;
rq.cparam = &cp;
rq.clen = WRITE_LINK_SUPERVISION_TIMEOUT_CP_SIZE;
if (hci_send_req(dd, &rq, 100) < 0) {
perror("HCI write_link_supervision_timeout request failed");
exit(1);
}
}
close(dd);
free(cr);
}
/* Request authentication */
static struct option auth_options[] = {
{"help", 0,0, 'h'},
{0, 0, 0, 0}
};
static 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;
struct hci_request rq;
auth_requested_cp cp;
evt_auth_complete rp;
bdaddr_t bdaddr;
int opt, dd;
for_each_opt(opt, auth_options, NULL) {
switch(opt) {
default:
printf(auth_help);
return;
}
}
argc -= optind;
argv += optind;
if (argc < 1) {
printf(auth_help);
return;
}
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)
return;
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 = cr->conn_info->handle;
memset(&rq, 0, sizeof(rq));
rq.ogf = OGF_LINK_CTL;
rq.ocf = OCF_AUTH_REQUESTED;
rq.cparam = &cp;
rq.clen = AUTH_REQUESTED_CP_SIZE;
rq.rparam = &rp;
rq.rlen = EVT_AUTH_COMPLETE_SIZE;
rq.event = EVT_AUTH_COMPLETE;
if (hci_send_req(dd, &rq, 25000) < 0) {
perror("HCI authentication request failed");
exit(1);
}
close(dd);
free(cr);
}
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" },
{ "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" },
{ "lst", cmd_lst, "Set/display link supervision timeout" },
{ "auth", cmd_auth, "Request authentication" },
{ 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], 3))
continue;
command[i].func(dev_id, argc, argv);
break;
}
return 0;
}