blob: 4b4844ffb3f502fbbaf522ef7ea36bd808aa3a50 [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 <getopt.h>
#include <termios.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <bluetooth.h>
#include <hci.h>
#include <hci_lib.h>
extern int optind,opterr,optopt;
extern char *optarg;
static struct hci_dev_info di;
static int all;
void print_dev_hdr(struct hci_dev_info *di);
void print_dev_info(int ctl, struct hci_dev_info *di);
void print_dev_list(int ctl, int flags)
{
struct hci_dev_list_req *dl;
struct hci_dev_req *dr;
int i;
if( !(dl = malloc(HCI_MAX_DEV * sizeof(struct hci_dev_req) + sizeof(uint16_t))) ) {
perror("Can't allocate memory");
exit(1);
}
dl->dev_num = HCI_MAX_DEV;
dr = dl->dev_req;
if( ioctl(ctl, HCIGETDEVLIST, (void*)dl) ) {
perror("Can't get device list");
exit(1);
}
for(i=0; i< dl->dev_num; i++) {
di.dev_id = (dr+i)->dev_id;
if( ioctl(ctl, HCIGETDEVINFO, (void*)&di) )
continue;
print_dev_info(ctl, &di);
}
}
void print_pkt_type(struct hci_dev_info *di)
{
printf("\tPacket type: %s\n", hci_ptypetostr(di->pkt_type));
}
void print_link_policy(struct hci_dev_info *di)
{
printf("\tLink policy: %s\n", hci_lptostr(di->link_policy));
}
void print_link_mode(struct hci_dev_info *di)
{
printf("\tLink mode: %s\n", hci_lmtostr(di->link_mode));
}
void print_dev_features(struct hci_dev_info *di, int format)
{
if (!format) {
printf("\tFeatures: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n",
di->features[0], di->features[1],
di->features[2], di->features[3] );
} else {
printf("\tFeatures: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n%s\n",
di->features[0], di->features[1],
di->features[2], di->features[3],
lmp_featurestostr(di->features, "\t\t", 3));
}
}
void cmd_rstat(int ctl, int hdev, char *opt)
{
/* Reset HCI device stat counters */
if( ioctl(ctl, HCIDEVRESTAT, hdev) < 0 ) {
printf("Can't reset stats counters hci%d. %s(%d)\n", hdev,
strerror(errno), errno);
exit(1);
}
}
void cmd_scan(int ctl, int hdev, char *opt)
{
struct hci_dev_req dr;
dr.dev_id = hdev;
dr.dev_opt = SCAN_DISABLED;
if( !strcmp(opt, "iscan") )
dr.dev_opt = SCAN_INQUIRY;
else if( !strcmp(opt, "pscan") )
dr.dev_opt = SCAN_PAGE;
else if( !strcmp(opt, "piscan") )
dr.dev_opt = SCAN_PAGE | SCAN_INQUIRY;
if( ioctl(ctl, HCISETSCAN, (unsigned long)&dr) < 0 ) {
printf("Can't set scan mode on hci%d. %s(%d)\n", hdev, strerror(errno), errno);
exit(1);
}
}
void cmd_auth(int ctl, int hdev, char *opt)
{
struct hci_dev_req dr;
dr.dev_id = hdev;
if( !strcmp(opt, "auth") )
dr.dev_opt = AUTH_ENABLED;
else
dr.dev_opt = AUTH_DISABLED;
if( ioctl(ctl, HCISETAUTH, (unsigned long)&dr) < 0 ) {
printf("Can't set auth on hci%d. %s(%d)\n", hdev, strerror(errno), errno);
exit(1);
}
}
void cmd_encrypt(int ctl, int hdev, char *opt)
{
struct hci_dev_req dr;
dr.dev_id = hdev;
if( !strcmp(opt, "encrypt") )
dr.dev_opt = ENCRYPT_P2P;
else
dr.dev_opt = ENCRYPT_DISABLED;
if( ioctl(ctl, HCISETENCRYPT, (unsigned long)&dr) < 0 ) {
printf("Can't set encrypt on hci%d. %s(%d)\n", hdev, strerror(errno), errno);
exit(1);
}
}
void cmd_up(int ctl, int hdev, char *opt)
{
int ret;
/* Start HCI device */
if( (ret = ioctl(ctl, HCIDEVUP, hdev)) < 0 ) {
if( errno == EALREADY )
return;
printf("Can't init device hci%d. %s(%d)\n", hdev, strerror(errno), errno);
exit(1);
}
cmd_scan(ctl, hdev, "piscan");
}
void cmd_down(int ctl, int hdev, char *opt)
{
/* Stop HCI device */
if (ioctl(ctl, HCIDEVDOWN, hdev) < 0) {
printf("Can't down device hci%d. %s(%d)\n", hdev, strerror(errno), errno);
exit(1);
}
}
void cmd_reset(int ctl, int hdev, char *opt)
{
/* Reset HCI device
if( ioctl(ctl, HCIDEVRESET, hdev) < 0 ){
printf("Reset failed hci%d. %s(%d)\n", hdev, strerror(errno), errno);
exit(1);
}
*/
cmd_down(ctl, hdev, "down");
cmd_up(ctl, hdev, "up");
}
void cmd_ptype(int ctl, int hdev, char *opt)
{
struct hci_dev_req dr;
dr.dev_id = hdev;
if (hci_strtoptype(opt, &dr.dev_opt)) {
if (ioctl(ctl, HCISETPTYPE, (unsigned long)&dr) < 0) {
printf("Can't set pkttype on hci%d. %s(%d)\n", hdev, strerror(errno), errno);
exit(1);
}
} else {
print_dev_hdr(&di);
print_pkt_type(&di);
}
}
void cmd_lp(int ctl, int hdev, char *opt)
{
struct hci_dev_req dr;
dr.dev_id = hdev;
if (hci_strtolp(opt, &dr.dev_opt)) {
if (ioctl(ctl, HCISETLINKPOL, (unsigned long)&dr) < 0) {
printf("Can't set link policy on hci%d. %s(%d)\n",
hdev, strerror(errno), errno);
exit(1);
}
} else {
print_dev_hdr(&di);
print_link_policy(&di);
}
}
void cmd_lm(int ctl, int hdev, char *opt)
{
struct hci_dev_req dr;
dr.dev_id = hdev;
if (hci_strtolm(opt, &dr.dev_opt)) {
if (ioctl(ctl, HCISETLINKMODE, (unsigned long)&dr) < 0) {
printf("Can't set default link mode on hci%d. %s(%d)\n",
hdev, strerror(errno), errno);
exit(1);
}
} else {
print_dev_hdr(&di);
print_link_mode(&di);
}
}
void cmd_aclmtu(int ctl, int hdev, char *opt)
{
struct hci_dev_req dr = { dev_id: hdev };
uint16_t mtu, mpkt;
if (sscanf(opt, "%4hu:%4hu", &mtu, &mpkt) != 2)
return;
*((uint16_t *)&dr.dev_opt + 1) = mtu;
*((uint16_t *)&dr.dev_opt + 0) = mpkt;
if (ioctl(ctl, HCISETACLMTU, (unsigned long)&dr) < 0) {
printf("Can't set ACL mtu on hci%d. %s(%d)\n",
hdev, strerror(errno), errno);
exit(1);
}
}
void cmd_scomtu(int ctl, int hdev, char *opt)
{
struct hci_dev_req dr = { dev_id: hdev };
uint16_t mtu, mpkt;
if (sscanf(opt, "%4hu:%4hu", &mtu, &mpkt) != 2)
return;
*((uint16_t *)&dr.dev_opt + 1) = mtu;
*((uint16_t *)&dr.dev_opt + 0) = mpkt;
if (ioctl(ctl, HCISETSCOMTU, (unsigned long)&dr) < 0) {
printf("Can't set SCO mtu on hci%d. %s(%d)\n",
hdev, strerror(errno), errno);
exit(1);
}
}
void cmd_features(int ctl, int hdev, char *opt)
{
print_dev_hdr(&di);
print_dev_features(&di, 1);
}
void cmd_name(int ctl, int hdev, char *opt)
{
struct hci_request rq;
int s;
if ((s = hci_open_dev(hdev)) < 0) {
printf("Can't open device hci%d. %s(%d)\n", hdev, strerror(errno), errno);
exit(1);
}
memset(&rq, 0, sizeof(rq));
if (opt) {
change_local_name_cp cp;
strcpy(cp.name, opt);
rq.ogf = OGF_HOST_CTL;
rq.ocf = OCF_CHANGE_LOCAL_NAME;
rq.cparam = &cp;
rq.clen = CHANGE_LOCAL_NAME_CP_SIZE;
if (hci_send_req(s, &rq, 1000) < 0) {
printf("Can't change local name on hci%d. %s(%d)\n",
hdev, strerror(errno), errno);
exit(1);
}
} else {
read_local_name_rp rp;
rq.ogf = OGF_HOST_CTL;
rq.ocf = OCF_READ_LOCAL_NAME;
rq.rparam = &rp;
rq.rlen = READ_LOCAL_NAME_RP_SIZE;
if (hci_send_req(s, &rq, 1000) < 0) {
printf("Can't read local name on hci%d. %s(%d)\n",
hdev, strerror(errno), errno);
exit(1);
}
if (rp.status) {
printf("Read local name on hci%d returned status %d\n", hdev, rp.status);
exit(1);
}
print_dev_hdr(&di);
printf("\tName: '%s'\n", rp.name);
}
}
void cmd_class(int ctl, int hdev, char *opt)
{
struct hci_request rq;
int s;
if ((s = hci_open_dev(hdev)) < 0) {
printf("Can't open device hci%d. %s(%d)\n", hdev, strerror(errno), errno);
exit(1);
}
memset(&rq, 0, sizeof(rq));
if (opt) {
uint32_t cod = htobl(strtoul(opt, NULL, 16));
write_class_of_dev_cp cp;
memcpy(cp.dev_class, &cod, 3);
rq.ogf = OGF_HOST_CTL;
rq.ocf = OCF_WRITE_CLASS_OF_DEV;
rq.cparam = &cp;
rq.clen = WRITE_CLASS_OF_DEV_CP_SIZE;
if (hci_send_req(s, &rq, 1000) < 0) {
printf("Can't write local class of device on hci%d. %s(%d)\n",
hdev, strerror(errno), errno);
exit(1);
}
} else {
read_class_of_dev_rp rp;
rq.ogf = OGF_HOST_CTL;
rq.ocf = OCF_READ_CLASS_OF_DEV;
rq.rparam = &rp;
rq.rlen = READ_CLASS_OF_DEV_RP_SIZE;
if (hci_send_req(s, &rq, 1000) < 0) {
printf("Can't read class of device on hci%d. %s(%d)\n",
hdev, strerror(errno), errno);
exit(1);
}
if (rp.status) {
printf("Read class of device on hci%d returned status %d\n",
hdev, rp.status);
exit(1);
}
print_dev_hdr(&di);
printf("\tClass: 0x%02x%02x%02x\n",
rp.dev_class[2], rp.dev_class[1], rp.dev_class[0]);
}
}
void cmd_version(int ctl, int hdev, char *opt)
{
struct hci_version ver;
int dd;
dd = hci_open_dev(hdev);
if (dd < 0) {
printf("Can't open device hci%d. %s(%d)\n", hdev, strerror(errno), errno);
exit(1);
}
if (hci_read_local_version(dd, &ver, 1000) < 0) {
printf("Can't read version info hci%d. %s(%d)\n",
hdev, strerror(errno), errno);
exit(1);
}
print_dev_hdr(&di);
printf( "\tHCI Ver: %s (0x%x) HCI Rev: 0x%x LMP Ver: %s (0x%x) LMP Subver: 0x%x\n"
"\tManufacturer: %s (%d)\n",
hci_vertostr(ver.hci_ver), ver.hci_ver, ver.hci_rev,
lmp_vertostr(ver.lmp_ver), ver.lmp_ver, ver.lmp_subver,
bt_compidtostr(ver.manufacturer), ver.manufacturer);
}
void cmd_inq_parms(int ctl, int hdev, char *opt)
{
struct hci_request rq;
int s;
if ((s = hci_open_dev(hdev)) < 0) {
printf("Can't open device hci%d. %s(%d)\n", hdev, strerror(errno), errno);
exit(1);
}
memset(&rq, 0, sizeof(rq));
if (opt) {
unsigned int window, interval;
write_inq_activity_cp cp;
if (sscanf(opt,"%4u:%4u", &window, &interval) != 2) {
printf("Invalid argument format\n");
exit(1);
}
rq.ogf = OGF_HOST_CTL;
rq.ocf = OCF_WRITE_INQ_ACTIVITY;
rq.cparam = &cp;
rq.clen = WRITE_INQ_ACTIVITY_CP_SIZE;
cp.window = htobs((uint16_t)window);
cp.interval = htobs((uint16_t)interval);
if (window < 0x12 || window > 0x1000)
printf("Warning: inquiry window out of range!\n");
if (interval < 0x12 || interval > 0x1000)
printf("Warning: inquiry interval out of range!\n");
if (hci_send_req(s, &rq, 1000) < 0) {
printf("Can't set inquiry parameters name on hci%d. %s(%d)\n",
hdev, strerror(errno), errno);
exit(1);
}
} else {
uint16_t window, interval;
read_inq_activity_rp rp;
rq.ogf = OGF_HOST_CTL;
rq.ocf = OCF_READ_INQ_ACTIVITY;
rq.rparam = &rp;
rq.rlen = READ_INQ_ACTIVITY_RP_SIZE;
if (hci_send_req(s, &rq, 1000) < 0) {
printf("Can't read inquiry parameters on hci%d. %s(%d)\n",
hdev, strerror(errno), errno);
exit(1);
}
if (rp.status) {
printf("Read inquiry parameters on hci%d returned status %d\n",
hdev, rp.status);
exit(1);
}
print_dev_hdr(&di);
window = btohs(rp.window);
interval = btohs(rp.interval);
printf("\tInquiry interval: %u slots (%.2f ms), window: %u slots (%.2f ms)\n",
interval, (float)interval * 0.625, window, (float)window * 0.625);
}
}
void print_dev_hdr(struct hci_dev_info *di)
{
static int hdr = -1;
bdaddr_t bdaddr;
if (hdr == di->dev_id)
return;
hdr = di->dev_id;
baswap(&bdaddr, &di->bdaddr);
printf("%s:\tType: %s\n", di->name, hci_dtypetostr(di->type) );
printf("\tBD Address: %s ACL MTU: %d:%d SCO MTU: %d:%d\n",
batostr(&bdaddr), di->acl_mtu, di->acl_pkts,
di->sco_mtu, di->sco_pkts);
}
void print_dev_info(int ctl, struct hci_dev_info *di)
{
struct hci_dev_stats *st = &di->stat;
print_dev_hdr(di);
printf("\t%s\n", hci_dflagstostr(di->flags) );
printf("\tRX bytes:%d acl:%d sco:%d events:%d errors:%d\n",
st->byte_rx, st->acl_rx, st->sco_rx, st->evt_rx, st->err_rx);
printf("\tTX bytes:%d acl:%d sco:%d commands:%d errors:%d\n",
st->byte_tx, st->acl_tx, st->sco_tx, st->cmd_tx, st->err_tx);
if (all) {
print_dev_features(di, 0);
print_pkt_type(di);
print_link_policy(di);
print_link_mode(di);
if (di->flags & (1 << HCI_UP)) {
cmd_name(ctl, di->dev_id, NULL);
cmd_class(ctl, di->dev_id, NULL);
cmd_version(ctl, di->dev_id, NULL);
}
}
printf("\n");
}
struct {
char *cmd;
void (*func)(int ctl, int hdev, char *opt);
char *opt;
char *doc;
} command[] = {
{ "up", cmd_up, 0, "Open and initialize HCI device" },
{ "down", cmd_down, 0, "Close HCI device" },
{ "reset", cmd_reset, 0, "Reset HCI device" },
{ "rstat", cmd_rstat, 0, "Reset statistic counters" },
{ "auth", cmd_auth, 0, "Enable Authentication" },
{ "noauth", cmd_auth, 0, "Disable Authentication" },
{ "encrypt",cmd_encrypt,0, "Enable Encryption" },
{ "noencrypt", cmd_encrypt, 0, "Disable Encryption" },
{ "piscan", cmd_scan, 0, "Enable Page and Inquiry scan" },
{ "noscan", cmd_scan, 0, "Disable scan" },
{ "iscan", cmd_scan, 0, "Enable Inquiry scan" },
{ "pscan", cmd_scan, 0, "Enable Page scan" },
{ "ptype", cmd_ptype, "[type]", "Get/Set default packet type" },
{ "lm", cmd_lm, "[mode]", "Get/Set default link mode" },
{ "lp", cmd_lp, "[policy]", "Get/Set default link policy" },
{ "name", cmd_name, "[name]", "Get/Set local name" },
{ "class", cmd_class, "[class]", "Get/Set class of device" },
{ "inqparms",cmd_inq_parms, "[win:int]","Get/Set inquiry scan window and interval" },
{ "aclmtu", cmd_aclmtu, "<mtu:pkt>","Set ACL MTU and number of packets" },
{ "scomtu", cmd_scomtu, "<mtu:pkt>","Set SCO MTU and number of packets" },
{ "version", cmd_version, 0, "Display version information" },
{ "features", cmd_features, 0,"Display device features" },
{ NULL, NULL, 0}
};
void usage(void)
{
int i;
printf("hciconfig - HCI device configuration utility\n");
printf("Usage:\n"
"\thciconfig\n"
"\thciconfig [-a] hciX [command]\n");
printf("Commands:\n");
for (i=0; command[i].cmd; i++)
printf("\t%-10s %-8s\t%s\n", command[i].cmd,
command[i].opt ? command[i].opt : " ",
command[i].doc);
}
static struct option main_options[] = {
{"help", 0,0, 'h'},
{"all", 0,0, 'a'},
{0, 0, 0, 0}
};
int main(int argc, char **argv, char **env)
{
int opt, ctl, i, cmd=0;
while ((opt=getopt_long(argc, argv, "ah", main_options, NULL)) != -1) {
switch(opt) {
case 'a':
all = 1;
break;
case 'h':
default:
usage();
exit(0);
}
}
argc -= optind;
argv += optind;
optind = 0;
/* Open HCI socket */
if ((ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)) < 0) {
perror("Can't open HCI socket.");
exit(1);
}
if (argc < 1) {
print_dev_list(ctl, 0);
exit(0);
}
di.dev_id = atoi(argv[0] + 3);
argc--; argv++;
if (ioctl(ctl, HCIGETDEVINFO, (void*)&di)) {
perror("Can't get device info");
exit(1);
}
while (argc > 0) {
for (i=0; command[i].cmd; i++) {
if (strncmp(command[i].cmd, *argv, 4))
continue;
if (command[i].opt) {
argc--; argv++;
}
command[i].func(ctl, di.dev_id, *argv);
cmd = 1;
break;
}
argc--; argv++;
}
if (!cmd)
print_dev_info(ctl, &di);
close(ctl);
return 0;
}