bluez/tools/hciconfig.c

1229 lines
28 KiB
C
Raw Normal View History

2002-03-09 05:12:35 +08:00
/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2000-2001 Qualcomm Incorporated
* Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
* Copyright (C) 2002-2004 Marcel Holtmann <marcel@holtmann.org>
*
*
* 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$
2002-03-09 05:12:35 +08:00
*/
2004-04-03 14:04:35 +08:00
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
2002-03-09 05:12:35 +08:00
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
2002-04-23 04:43:10 +08:00
#include <getopt.h>
2002-03-09 05:12:35 +08:00
#include <termios.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
2002-08-20 12:39:54 +08:00
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
2002-03-09 05:12:35 +08:00
#include "csr.h"
2002-03-09 05:12:35 +08:00
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)
2002-03-09 05:12:35 +08:00
{
if (!format) {
2003-11-18 19:38:31 +08:00
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",
2002-03-09 05:12:35 +08:00
di->features[0], di->features[1],
2003-11-18 19:38:31 +08:00
di->features[2], di->features[3],
di->features[4], di->features[5],
di->features[6], di->features[7] );
} else {
2003-11-18 19:38:31 +08:00
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",
2002-04-23 04:16:27 +08:00
di->features[0], di->features[1],
di->features[2], di->features[3],
2003-11-18 19:38:31 +08:00
di->features[4], di->features[5],
di->features[6], di->features[7],
lmp_featurestostr(di->features, "\t\t", 63));
}
2002-03-09 05:12:35 +08:00
}
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);
}
}
2003-11-17 23:30:14 +08:00
void cmd_iac(int ctl, int hdev, char *opt)
{
int s = hci_open_dev(hdev);
if (s < 0) {
printf("Can't open device hci%d. %s(%d)\n", hdev, strerror(errno), errno);
exit(1);
}
if (opt) {
int l = strtoul(opt, 0, 16);
uint8_t lap[3];
if (l < 0x9e8b00 || l > 0x9e8b3f) {
printf("Invalid access code 0x%x\n", l);
exit(1);
}
lap[0] = (l & 0xff);
lap[1] = (l >> 8) & 0xff;
lap[2] = (l >> 16) & 0xff;
if (hci_write_current_iac_lap(s, 1, lap, 1000) < 0) {
printf("Failed to set IAC on hci%d: %s\n", hdev, strerror(errno));
exit(1);
}
} else {
uint8_t lap[3 * MAX_IAC_LAP];
int i, j;
uint8_t n;
if (hci_read_current_iac_lap(s, &n, lap, 1000) < 0) {
printf("Failed to read IAC from hci%d: %s\n", hdev, strerror(errno));
exit(1);
}
print_dev_hdr(&di);
printf("\tIAC: ");
for (i = 0; i < n; i++) {
printf("0x");
for (j = 3; j--; )
printf("%02x", lap[j + 3*i]);
if (i < n-1)
printf(", ");
}
printf("\n");
}
close(s);
}
2002-03-09 05:12:35 +08:00
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 (!opt)
return;
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 (!opt)
return;
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);
}
}
2002-03-09 05:12:35 +08:00
void cmd_features(int ctl, int hdev, char *opt)
{
print_dev_hdr(&di);
print_dev_features(&di, 1);
2002-03-09 05:12:35 +08:00
}
void cmd_name(int ctl, int hdev, char *opt)
{
2002-08-22 18:06:55 +08:00
int s = hci_open_dev(hdev);
2002-08-22 18:06:55 +08:00
if (s < 0) {
2002-03-09 05:12:35 +08:00
printf("Can't open device hci%d. %s(%d)\n", hdev, strerror(errno), errno);
exit(1);
}
if (opt) {
2002-08-22 18:06:55 +08:00
if (hci_write_local_name(s, opt, 1000) < 0) {
2002-03-09 05:12:35 +08:00
printf("Can't change local name on hci%d. %s(%d)\n",
hdev, strerror(errno), errno);
exit(1);
}
} else {
2002-08-22 18:06:55 +08:00
char name[248];
int i;
2002-08-22 18:06:55 +08:00
if (hci_read_local_name(s, sizeof(name), name, 1000) < 0) {
2002-03-09 05:12:35 +08:00
printf("Can't read local name on hci%d. %s(%d)\n",
hdev, strerror(errno), errno);
exit(1);
}
for (i = 0; i < 248 && name[i]; i++)
if (!isprint(name[i]))
name[i] = '.';
2002-03-09 05:12:35 +08:00
print_dev_hdr(&di);
2002-08-22 18:06:55 +08:00
printf("\tName: '%s'\n", name);
2002-03-09 05:12:35 +08:00
}
}
/*
* see http://www.bluetooth.org/assigned-numbers/baseband.htm --- all
* strings are reproduced verbatim
*/
static char *get_minor_device_name(int major, int minor)
{
switch (major) {
case 0: /* misc */
return "";
case 1: /* computer */
switch(minor) {
case 0:
return "Uncategorized";
case 1:
return "Desktop workstation";
case 2:
return "Server";
case 3:
return "Laptop";
case 4:
return "Handheld";
case 5:
return "Palm";
case 6:
return "Wearable";
}
break;
case 2: /* phone */
switch(minor) {
case 0:
return "Uncategorized";
case 1:
return "Cellular";
case 2:
return "Cordless";
case 3:
return "Smart phone";
case 4:
return "Wired modem or voice gateway";
case 5:
return "Common ISDN Access";
case 6:
return "Sim Card Reader";
}
break;
case 3: /* lan access */
if (minor == 0)
return "Uncategorized";
switch(minor / 8) {
case 0:
return "Fully available";
case 1:
return "1-17% utilized";
case 2:
return "17-33% utilized";
case 3:
return "33-50% utilized";
case 4:
return "50-67% utilized";
case 5:
return "67-83% utilized";
case 6:
return "83-99% utilized";
case 7:
return "No service available";
}
break;
case 4: /* audio/video */
switch(minor) {
case 0:
return "Uncategorized";
case 1:
return "Device conforms to the Headset profile";
case 2:
return "Hands-free";
/* 3 is reserved */
case 4:
return "Microphone";
case 5:
return "Loudspeaker";
case 6:
return "Headphones";
case 7:
return "Portable Audio";
case 8:
return "Car Audio";
case 9:
return "Set-top box";
case 10:
return "HiFi Audio Device";
case 11:
return "VCR";
case 12:
return "Video Camera";
case 13:
return "Camcorder";
case 14:
return "Video Monitor";
case 15:
return "Video Display and Loudspeaker";
case 16:
return "Video Conferencing";
/* 17 is reserved */
case 18:
return "Gaming/Toy";
}
break;
case 5: /* peripheral */
switch(minor) {
case 16:
return "Keyboard";
case 32:
return "Pointing device";
case 48:
return "Combo keyboard/pointing device";
}
break;
case 6: /* imaging */
if (minor & 4)
return "Display";
if (minor & 8)
return "Camera";
if (minor & 16)
return "Scanner";
if (minor & 32)
return "Printer";
break;
case 63: /* uncategorised */
return "";
}
return "Unknown (reserved) minor device class";
}
2002-03-09 05:12:35 +08:00
void cmd_class(int ctl, int hdev, char *opt)
{
static char *services[] = { "Positioning",
"Networking",
"Rendering",
"Capturing",
"Object Transfer",
"Audio",
"Telephony",
"Information" };
static char *major_devices[] = { "Miscellaneous",
"Computer",
"Phone",
"LAN Access",
"Audio/Video",
"Peripheral",
"Imaging",
"Uncategorized" };
2002-08-22 18:06:55 +08:00
int s = hci_open_dev(hdev);
2002-03-09 05:12:35 +08:00
2002-08-22 18:06:55 +08:00
if (s < 0) {
2002-03-09 05:12:35 +08:00
printf("Can't open device hci%d. %s(%d)\n", hdev, strerror(errno), errno);
exit(1);
}
if (opt) {
2003-02-12 21:34:55 +08:00
uint32_t cod = strtoul(opt, NULL, 16);
if (hci_write_class_of_dev(s, cod, 1000) < 0) {
2002-03-09 05:12:35 +08:00
printf("Can't write local class of device on hci%d. %s(%d)\n",
hdev, strerror(errno), errno);
exit(1);
}
} else {
2002-08-22 18:06:55 +08:00
uint8_t cls[3];
if (hci_read_class_of_dev(s, cls, 1000) < 0) {
2002-03-09 05:12:35 +08:00
printf("Can't read class of device on hci%d. %s(%d)\n",
hdev, strerror(errno), errno);
exit(1);
}
print_dev_hdr(&di);
2002-08-22 18:06:55 +08:00
printf("\tClass: 0x%02x%02x%02x\n", cls[2], cls[1], cls[0]);
printf("\tService Classes: ");
2002-08-22 18:06:55 +08:00
if (cls[2]) {
int first = 1;
for (s = 0; s < sizeof(*services); s++)
2002-08-22 18:06:55 +08:00
if (cls[2] & (1 << s)) {
if (!first)
printf(", ");
printf(services[s]);
first = 0;
}
} else
printf("Unspecified");
printf("\n\tDevice Class: ");
if ((cls[1] & 0x1f) > sizeof(*major_devices))
printf("Invalid Device Class!\n");
else
2003-09-08 05:40:13 +08:00
printf("%s, %s\n", major_devices[cls[1] & 0x1f],
get_minor_device_name(cls[1] & 0x1f, cls[0] >> 2));
2002-03-09 05:12:35 +08:00
}
}
2002-12-15 22:18:03 +08:00
void cmd_voice(int ctl, int hdev, char *opt)
{
static char *icf[] = { "Linear", "u-Law", "A-Law", "Reserved" };
static char *idf[] = { "1's complement", "2's complement", "Sign-Magnitude", "Reserved" };
static char *iss[] = { "8 bit", "16 bit" };
static char *acf[] = { "CVSD", "u-Law", "A-Law", "Reserved" };
int s = hci_open_dev(hdev);
if (s < 0) {
printf("Can't open device hci%d. %s(%d)\n", hdev, strerror(errno), errno);
exit(1);
}
if (opt) {
uint16_t vs = htobs(strtoul(opt, NULL, 16));
2002-12-15 22:18:03 +08:00
if (0 > hci_write_voice_setting(s, vs, 1000)) {
printf("Can't write voice setting on hci%d. %s(%d)\n",
hdev, strerror(errno), errno);
exit(1);
}
} else {
uint16_t vs;
uint8_t ic;
if (0 > hci_read_voice_setting(s, &vs, 1000)) {
printf("Can't read voice setting on hci%d. %s(%d)\n",
hdev, strerror(errno), errno);
exit(1);
}
vs = htobs(vs);
2002-12-15 22:18:03 +08:00
ic = (vs & 0x0300) >> 8;
print_dev_hdr(&di);
printf("\tVoice setting: 0x%04x%s\n", vs,
((vs & 0x03fc) == 0x0060) ? " (Default Condition)" : "");
printf("\tInput Coding: %s\n", icf[ic]);
printf("\tInput Data Format: %s\n", idf[(vs & 0xc0) >> 6]);
if (!ic) {
printf("\tInput Sample Size: %s\n", iss[(vs & 0x20) >> 5]);
printf("\t# of bits padding at MSB: %d\n", (vs & 0x1c) >> 2);
}
printf("\tAir Coding Format: %s\n", acf[vs & 0x03]);
}
}
2002-03-09 05:12:35 +08:00
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);
2002-03-09 05:12:35 +08:00
}
2004-04-25 23:09:01 +08:00
void cmd_inq_mode(int ctl, int hdev, char *opt)
{
struct hci_request rq;
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);
}
memset(&rq, 0, sizeof(rq));
if (opt) {
uint8_t mode = atoi(opt);
2004-04-25 23:09:01 +08:00
if (hci_write_inquiry_mode(dd, mode, 1000) < 0) {
2004-04-25 23:09:01 +08:00
printf("Can't set inquiry mode on hci%d. %s(%d)\n",
hdev, strerror(errno), errno);
exit(1);
}
} else {
uint8_t mode;
2004-04-25 23:09:01 +08:00
if (hci_read_inquiry_mode(dd, &mode, 1000) < 0) {
2004-04-25 23:09:01 +08:00
printf("Can't read inquiry mode on hci%d. %s(%d)\n",
hdev, strerror(errno), errno);
exit(1);
}
print_dev_hdr(&di);
printf("\tInquiry mode: %s\n",
mode == 1 ? "Inquiry with RSSI" : "Standard Inquiry");
2004-04-25 23:09:01 +08:00
}
}
2002-03-20 02:06:30 +08:00
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) {
2002-03-23 03:08:41 +08:00
unsigned int window, interval;
2002-03-20 02:06:30 +08:00
write_inq_activity_cp cp;
if (sscanf(opt,"%4u:%4u", &window, &interval) != 2) {
2002-03-20 02:06:30 +08:00
printf("Invalid argument format\n");
exit(1);
}
2002-03-20 02:06:30 +08:00
rq.ogf = OGF_HOST_CTL;
rq.ocf = OCF_WRITE_INQ_ACTIVITY;
rq.cparam = &cp;
rq.clen = WRITE_INQ_ACTIVITY_CP_SIZE;
2002-03-23 03:08:41 +08:00
cp.window = htobs((uint16_t)window);
cp.interval = htobs((uint16_t)interval);
2002-03-20 02:06:30 +08:00
if (window < 0x12 || window > 0x1000)
printf("Warning: inquiry window out of range!\n");
2002-03-20 02:06:30 +08:00
if (interval < 0x12 || interval > 0x1000)
printf("Warning: inquiry interval out of range!\n");
2002-03-20 02:06:30 +08:00
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;
2002-03-20 02:06:30 +08:00
rq.ogf = OGF_HOST_CTL;
rq.ocf = OCF_READ_INQ_ACTIVITY;
rq.rparam = &rp;
rq.rlen = READ_INQ_ACTIVITY_RP_SIZE;
2002-03-20 02:06:30 +08:00
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);
2002-03-20 02:06:30 +08:00
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);
2002-08-22 18:06:55 +08:00
}
2002-03-20 02:06:30 +08:00
}
2002-06-25 12:15:33 +08:00
void cmd_page_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_page_activity_cp cp;
if (sscanf(opt,"%4u:%4u", &window, &interval) != 2) {
printf("Invalid argument format\n");
exit(1);
}
2002-06-25 12:15:33 +08:00
rq.ogf = OGF_HOST_CTL;
rq.ocf = OCF_WRITE_PAGE_ACTIVITY;
rq.cparam = &cp;
rq.clen = WRITE_PAGE_ACTIVITY_CP_SIZE;
2002-06-25 12:15:33 +08:00
cp.window = htobs((uint16_t)window);
cp.interval = htobs((uint16_t)interval);
2002-06-25 12:15:33 +08:00
if (window < 0x12 || window > 0x1000)
printf("Warning: page window out of range!\n");
2002-06-25 12:15:33 +08:00
if (interval < 0x12 || interval > 0x1000)
printf("Warning: page interval out of range!\n");
2002-06-25 12:15:33 +08:00
if (hci_send_req(s, &rq, 1000) < 0) {
printf("Can't set page parameters name on hci%d. %s(%d)\n",
hdev, strerror(errno), errno);
exit(1);
}
} else {
uint16_t window, interval;
read_page_activity_rp rp;
2002-06-25 12:15:33 +08:00
rq.ogf = OGF_HOST_CTL;
rq.ocf = OCF_READ_PAGE_ACTIVITY;
rq.rparam = &rp;
rq.rlen = READ_PAGE_ACTIVITY_RP_SIZE;
2002-06-25 12:15:33 +08:00
if (hci_send_req(s, &rq, 1000) < 0) {
printf("Can't read page parameters on hci%d. %s(%d)\n",
hdev, strerror(errno), errno);
exit(1);
}
if (rp.status) {
printf("Read page parameters on hci%d returned status %d\n",
hdev, rp.status);
exit(1);
}
print_dev_hdr(&di);
2002-06-25 12:15:33 +08:00
window = btohs(rp.window);
interval = btohs(rp.interval);
printf("\tPage interval: %u slots (%.2f ms), window: %u slots (%.2f ms)\n",
interval, (float)interval * 0.625, window, (float)window * 0.625);
}
2002-06-25 12:15:33 +08:00
}
void cmd_page_to(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 timeout;
write_page_timeout_cp cp;
2002-07-10 02:54:46 +08:00
if (sscanf(opt,"%5u", &timeout) != 1) {
2002-06-25 12:15:33 +08:00
printf("Invalid argument format\n");
exit(1);
}
2002-06-25 12:15:33 +08:00
rq.ogf = OGF_HOST_CTL;
rq.ocf = OCF_WRITE_PAGE_TIMEOUT;
rq.cparam = &cp;
rq.clen = WRITE_PAGE_TIMEOUT_CP_SIZE;
2002-06-25 12:15:33 +08:00
cp.timeout = htobs((uint16_t)timeout);
2002-06-25 12:15:33 +08:00
if (timeout < 0x01 || timeout > 0xFFFF)
printf("Warning: page timeout out of range!\n");
2002-06-25 12:15:33 +08:00
if (hci_send_req(s, &rq, 1000) < 0) {
printf("Can't set page timeout on hci%d. %s(%d)\n",
hdev, strerror(errno), errno);
exit(1);
}
} else {
uint16_t timeout;
read_page_timeout_rp rp;
2002-06-25 12:15:33 +08:00
rq.ogf = OGF_HOST_CTL;
rq.ocf = OCF_READ_PAGE_TIMEOUT;
rq.rparam = &rp;
rq.rlen = READ_PAGE_TIMEOUT_RP_SIZE;
2002-06-25 12:15:33 +08:00
if (hci_send_req(s, &rq, 1000) < 0) {
printf("Can't read page timeout on hci%d. %s(%d)\n",
hdev, strerror(errno), errno);
exit(1);
}
if (rp.status) {
printf("Read page timeout on hci%d returned status %d\n",
hdev, rp.status);
exit(1);
}
print_dev_hdr(&di);
timeout = btohs(rp.timeout);
printf("\tPage timeout: %u slots (%.2f ms)\n",
timeout, (float)timeout * 0.625);
}
2002-06-25 12:15:33 +08:00
}
2004-04-26 03:10:25 +08:00
void cmd_afh_mode(int ctl, int hdev, char *opt)
{
struct hci_request rq;
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);
}
memset(&rq, 0, sizeof(rq));
if (opt) {
write_afh_mode_cp cp;
cp.mode = atoi(opt);
rq.ogf = OGF_HOST_CTL;
rq.ocf = OCF_WRITE_AFH_MODE;
rq.cparam = &cp;
rq.clen = WRITE_AFH_MODE_RP_SIZE;
if (hci_send_req(dd, &rq, 1000) < 0) {
printf("Can't set AFH mode on hci%d. %s(%d)\n",
hdev, strerror(errno), errno);
exit(1);
}
} else {
read_afh_mode_rp rp;
rq.ogf = OGF_HOST_CTL;
rq.ocf = OCF_READ_AFH_MODE;
rq.rparam = &rp;
rq.rlen = READ_AFH_MODE_RP_SIZE;
if (hci_send_req(dd, &rq, 1000) < 0) {
printf("Can't read AFH mode on hci%d. %s(%d)\n",
hdev, strerror(errno), errno);
exit(1);
}
if (rp.status) {
printf("Read AFH mode on hci%d returned status %d\n",
hdev, rp.status);
exit(1);
}
print_dev_hdr(&di);
printf("\tAFH mode: %s\n", rp.mode == 1 ? "Enabled" : "Disabled");
}
}
static void print_rev_ericsson(int dd)
2002-06-25 12:36:09 +08:00
{
struct hci_request rq;
unsigned char buf[102];
memset(&rq, 0, sizeof(rq));
rq.ogf = OGF_VENDOR_CMD;
rq.ocf = 0x000f;
rq.cparam = NULL;
rq.clen = 0;
rq.rparam = &buf;
rq.rlen = sizeof(buf);
if (hci_send_req(dd, &rq, 1000) < 0) {
printf("\nCan't read revision info. %s(%d)\n", strerror(errno), errno);
return;
}
printf("\t%s\n", buf + 1);
}
static void print_rev_csr(int dd, uint16_t rev)
{
uint16_t buildid, chipver, chiprev, maxkeylen, mapsco;
if (csr_read_varid_uint16(dd, 0, CSR_VARID_BUILDID, &buildid) < 0) {
printf("\t%s\n", csr_buildidtostr(rev));
return;
}
printf("\t%s\n", csr_buildidtostr(buildid));
if (!csr_read_varid_uint16(dd, 1, CSR_VARID_CHIPVER, &chipver)) {
if (csr_read_varid_uint16(dd, 2, CSR_VARID_CHIPREV, &chiprev) < 0)
chiprev = 0;
printf("\tChip version: %s\n", csr_chipvertostr(chipver, chiprev));
}
if (!csr_read_varid_uint16(dd, 3, CSR_VARID_MAX_CRYPT_KEY_LENGTH, &maxkeylen))
printf("\tMax key size: %d bit\n", maxkeylen * 8);
if (!csr_read_pskey_uint16(dd, 4, CSR_PSKEY_HOSTIO_MAP_SCO_PCM, &mapsco))
printf("\tSCO mapping: %s\n", mapsco ? "PCM" : "HCI");
}
static void print_rev_digianswer(int dd)
{
struct hci_request rq;
unsigned char req[] = { 0x07 };
unsigned char buf[102];
memset(&rq, 0, sizeof(rq));
rq.ogf = OGF_VENDOR_CMD;
rq.ocf = 0x000e;
rq.cparam = req;
rq.clen = sizeof(req);
rq.rparam = &buf;
rq.rlen = sizeof(buf);
if (hci_send_req(dd, &rq, 1000) < 0) {
printf("\nCan't read revision info. %s(%d)\n", strerror(errno), errno);
return;
}
printf("\t%s\n", buf + 1);
}
static void print_rev_broadcom(uint16_t hci_rev, uint16_t lmp_subver)
{
printf("\tFirmware %d.%d.%03d\n", hci_rev, lmp_subver >> 8, lmp_subver & 0xff);
}
2004-07-28 00:43:40 +08:00
static void print_rev_avm(uint16_t hci_rev, uint16_t lmp_subver)
{
2004-07-28 00:43:40 +08:00
if (lmp_subver == 0x01)
printf("\tFirmware 03.%d.%d\n", hci_rev >> 8, hci_rev & 0xff);
else
printf("\tUnknown type\n");
}
static void cmd_revision(int ctl, int hdev, char *opt)
{
struct hci_version ver;
2002-06-25 12:36:09 +08:00
int dd;
dd = hci_open_dev(hdev);
if (dd < 0) {
printf("Can't open device hci%d. %s(%d)\n", hdev, strerror(errno), errno);
return;
}
if (hci_read_local_version(dd, &ver, 1000) < 0) {
printf("Can't read version info hci%d. %s(%d)\n",
hdev, strerror(errno), errno);
return;
}
print_dev_hdr(&di);
switch (ver.manufacturer) {
case 0:
case 48:
print_rev_ericsson(dd);
break;
case 10:
print_rev_csr(dd, ver.hci_rev);
2002-06-25 12:36:09 +08:00
break;
case 12:
print_rev_digianswer(dd);
break;
case 15:
print_rev_broadcom(ver.hci_rev, ver.lmp_subver);
break;
case 31:
2004-07-28 00:43:40 +08:00
print_rev_avm(ver.hci_rev, ver.lmp_subver);
break;
2002-06-25 12:36:09 +08:00
default:
2003-02-04 23:49:49 +08:00
printf("\tUnsupported manufacturer\n");
2002-06-25 12:36:09 +08:00
break;
}
return;
}
2002-03-09 05:12:35 +08:00
void print_dev_hdr(struct hci_dev_info *di)
{
static int hdr = -1;
2003-07-25 01:34:34 +08:00
char addr[18];
2002-03-09 05:12:35 +08:00
if (hdr == di->dev_id)
return;
hdr = di->dev_id;
2003-07-25 01:34:34 +08:00
ba2str(&di->bdaddr, addr);
2002-03-09 05:12:35 +08:00
printf("%s:\tType: %s\n", di->name, hci_dtypetostr(di->type) );
printf("\tBD Address: %s ACL MTU: %d:%d SCO MTU: %d:%d\n",
2003-07-25 01:34:34 +08:00
addr, di->acl_mtu, di->acl_pkts,
di->sco_mtu, di->sco_pkts);
2002-03-09 05:12:35 +08:00
}
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);
2002-03-09 05:12:35 +08:00
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);
2002-03-09 05:12:35 +08:00
if (all) {
print_dev_features(di, 0);
2002-03-09 05:12:35 +08:00
print_pkt_type(di);
print_link_policy(di);
print_link_mode(di);
2002-05-09 01:00:05 +08:00
if (hci_test_bit(HCI_UP, &di->flags)) {
cmd_name(ctl, di->dev_id, NULL);
cmd_class(ctl, di->dev_id, NULL);
cmd_version(ctl, di->dev_id, NULL);
}
2002-03-09 05:12:35 +08:00
}
2002-03-09 05:12:35 +08:00
printf("\n");
}
struct {
char *cmd;
void (*func)(int ctl, int hdev, char *opt);
char *opt;
char *doc;
} command[] = {
2002-06-26 02:31:56 +08:00
{ "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" },
2002-12-15 22:18:03 +08:00
{ "voice", cmd_voice, "[voice]", "Get/Set voice setting" },
{ "iac", cmd_iac, "[iac]", "Get/Set inquiry access code" },
2004-04-25 23:09:01 +08:00
{ "inqmode", cmd_inq_mode, "[mode]", "Get/set inquiry mode" },
2002-06-26 02:31:56 +08:00
{ "inqparms", cmd_inq_parms, "[win:int]", "Get/Set inquiry scan window and interval" },
{ "pageparms", cmd_page_parms, "[win:int]", "Get/Set page scan window and interval" },
{ "pageto", cmd_page_to, "[to]", "Get/Set page timeout" },
2004-04-26 03:10:25 +08:00
{ "afhmode", cmd_afh_mode, "[mode]", "Get/Set AFH mode" },
2002-06-26 02:31:56 +08:00
{ "aclmtu", cmd_aclmtu, "<mtu:pkt>", "Set ACL MTU and number of packets" },
{ "scomtu", cmd_scomtu, "<mtu:pkt>", "Set SCO MTU and number of packets" },
{ "features", cmd_features, 0, "Display device features" },
{ "version", cmd_version, 0, "Display version information" },
{ "revision", cmd_revision, 0, "Display revision information" },
{ NULL, NULL, 0 }
2002-03-09 05:12:35 +08:00
};
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);
}
2002-04-23 04:43:10 +08:00
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)
2002-03-09 05:12:35 +08:00
{
int opt, ctl, i, cmd=0;
2002-04-23 04:43:10 +08:00
while ((opt=getopt_long(argc, argv, "ah", main_options, NULL)) != -1) {
2002-03-09 05:12:35 +08:00
switch(opt) {
case 'a':
all = 1;
break;
2002-04-23 04:43:10 +08:00
2002-03-09 05:12:35 +08:00
case 'h':
2002-04-23 04:43:10 +08:00
default:
2002-03-09 05:12:35 +08:00
usage();
exit(0);
}
}
2002-04-23 04:43:10 +08:00
argc -= optind;
argv += optind;
optind = 0;
2002-03-09 05:12:35 +08:00
/* Open HCI socket */
if ((ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)) < 0) {
perror("Can't open HCI socket.");
exit(1);
}
2002-04-23 04:43:10 +08:00
if (argc < 1) {
2002-03-09 05:12:35 +08:00
print_dev_list(ctl, 0);
exit(0);
}
2002-04-23 04:43:10 +08:00
di.dev_id = atoi(argv[0] + 3);
argc--; argv++;
2002-03-09 05:12:35 +08:00
if (ioctl(ctl, HCIGETDEVINFO, (void*)&di)) {
perror("Can't get device info");
exit(1);
}
2002-04-23 04:43:10 +08:00
while (argc > 0) {
2002-03-09 05:12:35 +08:00
for (i=0; command[i].cmd; i++) {
2002-06-26 02:31:56 +08:00
if (strncmp(command[i].cmd, *argv, 5))
2002-03-09 05:12:35 +08:00
continue;
2002-04-23 04:43:10 +08:00
if (command[i].opt) {
argc--; argv++;
}
2002-04-23 04:43:10 +08:00
command[i].func(ctl, di.dev_id, *argv);
2002-03-09 05:12:35 +08:00
cmd = 1;
break;
}
2002-04-23 04:43:10 +08:00
argc--; argv++;
2002-03-09 05:12:35 +08:00
}
if (!cmd)
print_dev_info(ctl, &di);
close(ctl);
return 0;
}