mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2025-01-01 09:04:55 +08:00
371 lines
7.8 KiB
C
371 lines
7.8 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 <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;
|
|
}
|