blob: bebdfbd44980b56993d0de22184af2255f880229 [file] [log] [blame]
/*
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 = 8; /* ~ 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 ptype, dd;
uint16_t handle;
uint8_t 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, 0, role, &handle, 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, 0x13, 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;
}