From 799757ccf1d03c33c75bc597cd5ef77741dcb6a7 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 3 Jun 2011 09:17:04 +0000 Subject: Imported upstream 4.91 --- tools/avctrl.8 | 39 + tools/avctrl.c | 248 +++ tools/avinfo.c | 672 +++++++ tools/bccmd.8 | 130 ++ tools/bccmd.c | 1254 +++++++++++++ tools/ciptool.1 | 68 + tools/ciptool.c | 498 ++++++ tools/csr.c | 2853 +++++++++++++++++++++++++++++ tools/csr.h | 553 ++++++ tools/csr_3wire.c | 62 + tools/csr_bcsp.c | 255 +++ tools/csr_h4.c | 165 ++ tools/csr_hci.c | 160 ++ tools/csr_usb.c | 180 ++ tools/dfu.c | 168 ++ tools/dfu.h | 107 ++ tools/dfubabel.1 | 38 + tools/dfubabel.c | 211 +++ tools/dfutool.1 | 53 + tools/dfutool.c | 791 ++++++++ tools/hciattach.8 | 155 ++ tools/hciattach.c | 1446 +++++++++++++++ tools/hciattach.h | 56 + tools/hciattach_ath3k.c | 1049 +++++++++++ tools/hciattach_qualcomm.c | 275 +++ tools/hciattach_st.c | 278 +++ tools/hciattach_ti.c | 529 ++++++ tools/hciattach_tialt.c | 244 +++ tools/hciconfig.8 | 277 +++ tools/hciconfig.c | 2036 +++++++++++++++++++++ tools/hcieventmask.c | 130 ++ tools/hcisecfilter.c | 155 ++ tools/hcitool.1 | 209 +++ tools/hcitool.c | 3007 +++++++++++++++++++++++++++++++ tools/hid2hci.8 | 51 + tools/hid2hci.c | 375 ++++ tools/kword.c | 65 + tools/kword.h | 46 + tools/l2ping.8 | 76 + tools/l2ping.c | 324 ++++ tools/lexer.c | 1834 +++++++++++++++++++ tools/lexer.l | 120 ++ tools/parser.c | 1768 ++++++++++++++++++ tools/parser.h | 94 + tools/parser.y | 171 ++ tools/ppporc.c | 271 +++ tools/rfcomm.1 | 137 ++ tools/rfcomm.c | 849 +++++++++ tools/rfcomm.conf | 17 + tools/sdptool.1 | 130 ++ tools/sdptool.c | 4274 ++++++++++++++++++++++++++++++++++++++++++++ tools/ubcsp.c | 1180 ++++++++++++ tools/ubcsp.h | 208 +++ 53 files changed, 30341 insertions(+) create mode 100644 tools/avctrl.8 create mode 100644 tools/avctrl.c create mode 100644 tools/avinfo.c create mode 100644 tools/bccmd.8 create mode 100644 tools/bccmd.c create mode 100644 tools/ciptool.1 create mode 100644 tools/ciptool.c create mode 100644 tools/csr.c create mode 100644 tools/csr.h create mode 100644 tools/csr_3wire.c create mode 100644 tools/csr_bcsp.c create mode 100644 tools/csr_h4.c create mode 100644 tools/csr_hci.c create mode 100644 tools/csr_usb.c create mode 100644 tools/dfu.c create mode 100644 tools/dfu.h create mode 100644 tools/dfubabel.1 create mode 100644 tools/dfubabel.c create mode 100644 tools/dfutool.1 create mode 100644 tools/dfutool.c create mode 100644 tools/hciattach.8 create mode 100644 tools/hciattach.c create mode 100644 tools/hciattach.h create mode 100644 tools/hciattach_ath3k.c create mode 100644 tools/hciattach_qualcomm.c create mode 100644 tools/hciattach_st.c create mode 100644 tools/hciattach_ti.c create mode 100644 tools/hciattach_tialt.c create mode 100644 tools/hciconfig.8 create mode 100644 tools/hciconfig.c create mode 100644 tools/hcieventmask.c create mode 100644 tools/hcisecfilter.c create mode 100644 tools/hcitool.1 create mode 100644 tools/hcitool.c create mode 100644 tools/hid2hci.8 create mode 100644 tools/hid2hci.c create mode 100644 tools/kword.c create mode 100644 tools/kword.h create mode 100644 tools/l2ping.8 create mode 100644 tools/l2ping.c create mode 100644 tools/lexer.c create mode 100644 tools/lexer.l create mode 100644 tools/parser.c create mode 100644 tools/parser.h create mode 100644 tools/parser.y create mode 100644 tools/ppporc.c create mode 100644 tools/rfcomm.1 create mode 100644 tools/rfcomm.c create mode 100644 tools/rfcomm.conf create mode 100644 tools/sdptool.1 create mode 100644 tools/sdptool.c create mode 100644 tools/ubcsp.c create mode 100644 tools/ubcsp.h (limited to 'tools') diff --git a/tools/avctrl.8 b/tools/avctrl.8 new file mode 100644 index 0000000..7c3759d --- /dev/null +++ b/tools/avctrl.8 @@ -0,0 +1,39 @@ +.\" +.\" This program is free software; you can redistribute it and/or modify +.\" it under the terms of the GNU General Public License as published by +.\" the Free Software Foundation; either version 2 of the License, or +.\" (at your option) any later version. +.\" +.\" This program is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.\" GNU General Public License for more details. +.\" +.\" You should have received a copy of the GNU General Public License +.\" along with this program; if not, write to the Free Software +.\" Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +.\" +.\" +.TH AVCTRL 8 "JUNE 6, 2005" "" "" + +.SH NAME +avctrl \- Bluetooth Audio/Video control utility +.SH SYNOPSIS +.BR "avctrl +[ +.I options +] + +.SH DESCRIPTION +.B avctrl +is used to control the Audio/Video dongles. +.SH OPTIONS +.TP +.BI -h +Gives a list of possible options. +.TP +.BI -q +Don't display any messages. +.SH AUTHOR +Written by Marcel Holtmann . +.br diff --git a/tools/avctrl.c b/tools/avctrl.c new file mode 100644 index 0000000..3621a68 --- /dev/null +++ b/tools/avctrl.c @@ -0,0 +1,248 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include + +#ifdef NEED_USB_GET_BUSSES +static inline struct usb_bus *usb_get_busses(void) +{ + return usb_busses; +} +#endif + +#ifndef USB_DIR_OUT +#define USB_DIR_OUT 0x00 +#endif + +#ifndef USB_DIR_IN +#define USB_DIR_IN 0x80 +#endif + +#define HID_REQ_GET_REPORT 0x01 +#define HID_REQ_GET_IDLE 0x02 +#define HID_REQ_GET_PROTOCOL 0x03 +#define HID_REQ_SET_REPORT 0x09 +#define HID_REQ_SET_IDLE 0x0a +#define HID_REQ_SET_PROTOCOL 0x0b + +struct device_info; + +struct device_id { + uint16_t vendor; + uint16_t product; + int (*func)(struct device_info *dev, int argc, char *argv[]); +}; + +struct device_info { + struct usb_device *dev; + struct device_id *id; +}; + +#define GET_STATE 0x01 +#define GET_REMOTE_BDADDR 0x02 +#define DISCOVER 0x03 +#define SWITCH_TO_DFU 0x04 +#define READ_CODEC 0x05 + +static int dongle_csr(struct device_info *devinfo, int argc, char *argv[]) +{ + char buf[8]; + struct usb_dev_handle *udev; + int err, intf = 2; + + memset(buf, 0, sizeof(buf)); + + if (!strncasecmp(argv[0], "discover", 4)) + buf[0] = DISCOVER; + else if (!strncasecmp(argv[0], "switch", 3)) + buf[0] = SWITCH_TO_DFU; + else if (!strncasecmp(argv[0], "dfu", 3)) + buf[0] = SWITCH_TO_DFU; + else + return -EINVAL; + + udev = usb_open(devinfo->dev); + if (!udev) + return -errno; + + if (usb_claim_interface(udev, intf) < 0) { + err = -errno; + usb_close(udev); + return err; + } + + err = usb_control_msg(udev, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + HID_REQ_SET_REPORT, 0x03 << 8, intf, buf, sizeof(buf), 10000); + + if (err == 0) { + err = -1; + errno = EALREADY; + } else { + if (errno == ETIMEDOUT) + err = 0; + } + + usb_release_interface(udev, intf); + usb_close(udev); + + return err; +} + +static struct device_id device_list[] = { + { 0x0a12, 0x1004, dongle_csr }, + { -1 } +}; + +static struct device_id *match_device(uint16_t vendor, uint16_t product) +{ + int i; + + for (i = 0; device_list[i].func; i++) { + if (vendor == device_list[i].vendor && + product == device_list[i].product) + return &device_list[i]; + } + + return NULL; +} + +static int find_devices(struct device_info *devinfo, size_t size) +{ + struct usb_bus *bus; + struct usb_device *dev; + struct device_id *id; + unsigned int count = 0; + + usb_find_busses(); + usb_find_devices(); + + for (bus = usb_get_busses(); bus; bus = bus->next) + for (dev = bus->devices; dev; dev = dev->next) { + id = match_device(dev->descriptor.idVendor, + dev->descriptor.idProduct); + if (!id) + continue; + + if (count < size) { + devinfo[count].dev = dev; + devinfo[count].id = id; + count++; + } + } + + return count; +} + +static void usage(void) +{ + printf("avctrl - Bluetooth Audio/Video control utility\n\n"); + + printf("Usage:\n" + "\tavctrl [options] \n" + "\n"); + + printf("Options:\n" + "\t-h, --help Display help\n" + "\t-q, --quiet Don't display any messages\n" + "\n"); + + printf("Commands:\n" + "\tdiscover Simulate pressing the discover button\n" + "\tswitch Switch the dongle to DFU mode\n" + "\n"); +} + +static struct option main_options[] = { + { "help", 0, 0, 'h' }, + { "quiet", 0, 0, 'q' }, + { 0, 0, 0, 0 } +}; + +int main(int argc, char *argv[]) +{ + struct device_info dev[16]; + int i, opt, num, quiet = 0; + + while ((opt = getopt_long(argc, argv, "+qh", main_options, NULL)) != -1) { + switch (opt) { + case 'q': + quiet = 1; + break; + case 'h': + usage(); + exit(0); + default: + exit(0); + } + } + + argc -= optind; + argv += optind; + optind = 0; + + if (argc < 1) { + usage(); + exit(1); + } + + usb_init(); + + num = find_devices(dev, sizeof(dev) / sizeof(dev[0])); + if (num <= 0) { + if (!quiet) + fprintf(stderr, "No Audio/Video devices found\n"); + exit(1); + } + + for (i = 0; i < num; i++) { + struct device_id *id = dev[i].id; + int err; + + if (!quiet) + printf("Selecting device %04x:%04x ", + id->vendor, id->product); + fflush(stdout); + + err = id->func(&dev[i], argc, argv); + if (err < 0) { + if (!quiet) + printf("failed (%s)\n", strerror(-err)); + } else { + if (!quiet) + printf("was successful\n"); + } + } + + return 0; +} diff --git a/tools/avinfo.c b/tools/avinfo.c new file mode 100644 index 0000000..7f76c03 --- /dev/null +++ b/tools/avinfo.c @@ -0,0 +1,672 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2006-2010 Nokia Corporation + * Copyright (C) 2004-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define AVDTP_PSM 25 + +/* Commands */ +#define AVDTP_DISCOVER 0x01 +#define AVDTP_GET_CAPABILITIES 0x02 + +#define AVDTP_PKT_TYPE_SINGLE 0x00 + +#define AVDTP_MSG_TYPE_COMMAND 0x00 + +/* SEP capability categories */ +#define AVDTP_MEDIA_TRANSPORT 0x01 +#define AVDTP_REPORTING 0x02 +#define AVDTP_RECOVERY 0x03 +#define AVDTP_CONTENT_PROTECTION 0x04 +#define AVDTP_HEADER_COMPRESSION 0x05 +#define AVDTP_MULTIPLEXING 0x06 +#define AVDTP_MEDIA_CODEC 0x07 + +/* SEP types definitions */ +#define AVDTP_SEP_TYPE_SOURCE 0x00 +#define AVDTP_SEP_TYPE_SINK 0x01 + +/* Media types definitions */ +#define AVDTP_MEDIA_TYPE_AUDIO 0x00 +#define AVDTP_MEDIA_TYPE_VIDEO 0x01 +#define AVDTP_MEDIA_TYPE_MULTIMEDIA 0x02 + +#define A2DP_CODEC_SBC 0x00 +#define A2DP_CODEC_MPEG12 0x01 +#define A2DP_CODEC_MPEG24 0x02 +#define A2DP_CODEC_ATRAC 0x03 + +#define SBC_SAMPLING_FREQ_16000 (1 << 3) +#define SBC_SAMPLING_FREQ_32000 (1 << 2) +#define SBC_SAMPLING_FREQ_44100 (1 << 1) +#define SBC_SAMPLING_FREQ_48000 (1 << 0) + +#define SBC_CHANNEL_MODE_MONO (1 << 3) +#define SBC_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) +#define SBC_CHANNEL_MODE_STEREO (1 << 1) +#define SBC_CHANNEL_MODE_JOINT_STEREO (1 << 0) + +#define SBC_BLOCK_LENGTH_4 (1 << 3) +#define SBC_BLOCK_LENGTH_8 (1 << 2) +#define SBC_BLOCK_LENGTH_12 (1 << 1) +#define SBC_BLOCK_LENGTH_16 (1 << 0) + +#define SBC_SUBBANDS_4 (1 << 1) +#define SBC_SUBBANDS_8 (1 << 0) + +#define SBC_ALLOCATION_SNR (1 << 1) +#define SBC_ALLOCATION_LOUDNESS (1 << 0) + +#define MPEG_CHANNEL_MODE_MONO (1 << 3) +#define MPEG_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) +#define MPEG_CHANNEL_MODE_STEREO (1 << 1) +#define MPEG_CHANNEL_MODE_JOINT_STEREO (1 << 0) + +#define MPEG_LAYER_MP1 (1 << 2) +#define MPEG_LAYER_MP2 (1 << 1) +#define MPEG_LAYER_MP3 (1 << 0) + +#define MPEG_SAMPLING_FREQ_16000 (1 << 5) +#define MPEG_SAMPLING_FREQ_22050 (1 << 4) +#define MPEG_SAMPLING_FREQ_24000 (1 << 3) +#define MPEG_SAMPLING_FREQ_32000 (1 << 2) +#define MPEG_SAMPLING_FREQ_44100 (1 << 1) +#define MPEG_SAMPLING_FREQ_48000 (1 << 0) + +#define MPEG_BIT_RATE_VBR 0x8000 +#define MPEG_BIT_RATE_320000 0x4000 +#define MPEG_BIT_RATE_256000 0x2000 +#define MPEG_BIT_RATE_224000 0x1000 +#define MPEG_BIT_RATE_192000 0x0800 +#define MPEG_BIT_RATE_160000 0x0400 +#define MPEG_BIT_RATE_128000 0x0200 +#define MPEG_BIT_RATE_112000 0x0100 +#define MPEG_BIT_RATE_96000 0x0080 +#define MPEG_BIT_RATE_80000 0x0040 +#define MPEG_BIT_RATE_64000 0x0020 +#define MPEG_BIT_RATE_56000 0x0010 +#define MPEG_BIT_RATE_48000 0x0008 +#define MPEG_BIT_RATE_40000 0x0004 +#define MPEG_BIT_RATE_32000 0x0002 +#define MPEG_BIT_RATE_FREE 0x0001 + +struct avdtp_service_capability { + uint8_t category; + uint8_t length; + uint8_t data[0]; +} __attribute__ ((packed)); + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +struct avdtp_header { + uint8_t message_type:2; + uint8_t packet_type:2; + uint8_t transaction:4; + uint8_t signal_id:6; + uint8_t rfa0:2; +} __attribute__ ((packed)); + +struct seid_info { + uint8_t rfa0:1; + uint8_t inuse:1; + uint8_t seid:6; + uint8_t rfa2:3; + uint8_t type:1; + uint8_t media_type:4; +} __attribute__ ((packed)); + +struct seid_req { + struct avdtp_header header; + uint8_t rfa0:2; + uint8_t acp_seid:6; +} __attribute__ ((packed)); + +struct avdtp_media_codec_capability { + uint8_t rfa0:4; + uint8_t media_type:4; + uint8_t media_codec_type; + uint8_t data[0]; +} __attribute__ ((packed)); + +struct sbc_codec_cap { + struct avdtp_media_codec_capability cap; + uint8_t channel_mode:4; + uint8_t frequency:4; + uint8_t allocation_method:2; + uint8_t subbands:2; + uint8_t block_length:4; + uint8_t min_bitpool; + uint8_t max_bitpool; +} __attribute__ ((packed)); + +struct mpeg_codec_cap { + struct avdtp_media_codec_capability cap; + uint8_t channel_mode:4; + uint8_t crc:1; + uint8_t layer:3; + uint8_t frequency:6; + uint8_t mpf:1; + uint8_t rfa:1; + uint16_t bitrate; +} __attribute__ ((packed)); + +#elif __BYTE_ORDER == __BIG_ENDIAN + +struct avdtp_header { + uint8_t transaction:4; + uint8_t packet_type:2; + uint8_t message_type:2; + uint8_t rfa0:2; + uint8_t signal_id:6; +} __attribute__ ((packed)); + +struct seid_info { + uint8_t seid:6; + uint8_t inuse:1; + uint8_t rfa0:1; + uint8_t media_type:4; + uint8_t type:1; + uint8_t rfa2:3; +} __attribute__ ((packed)); + +struct seid_req { + struct avdtp_header header; + uint8_t acp_seid:6; + uint8_t rfa0:2; +} __attribute__ ((packed)); + +struct avdtp_media_codec_capability { + uint8_t media_type:4; + uint8_t rfa0:4; + uint8_t media_codec_type; + uint8_t data[0]; +} __attribute__ ((packed)); + +struct sbc_codec_cap { + struct avdtp_media_codec_capability cap; + uint8_t frequency:4; + uint8_t channel_mode:4; + uint8_t block_length:4; + uint8_t subbands:2; + uint8_t allocation_method:2; + uint8_t min_bitpool; + uint8_t max_bitpool; +} __attribute__ ((packed)); + +struct mpeg_codec_cap { + struct avdtp_media_codec_capability cap; + uint8_t layer:3; + uint8_t crc:1; + uint8_t channel_mode:4; + uint8_t rfa:1; + uint8_t mpf:1; + uint8_t frequency:6; + uint16_t bitrate; +} __attribute__ ((packed)); + +#else +#error "Unknown byte order" +#endif + +struct discover_resp { + struct avdtp_header header; + struct seid_info seps[0]; +} __attribute__ ((packed)); + +struct getcap_resp { + struct avdtp_header header; + uint8_t caps[0]; +} __attribute__ ((packed)); + + +static void print_mpeg12(struct mpeg_codec_cap *mpeg) +{ + printf("\tMedia Codec: MPEG12\n\t\tChannel Modes: "); + + if (mpeg->channel_mode & MPEG_CHANNEL_MODE_MONO) + printf("Mono "); + if (mpeg->channel_mode & MPEG_CHANNEL_MODE_DUAL_CHANNEL) + printf("DualChannel "); + if (mpeg->channel_mode & MPEG_CHANNEL_MODE_STEREO) + printf("Stereo "); + if (mpeg->channel_mode & MPEG_CHANNEL_MODE_JOINT_STEREO) + printf("JointStereo"); + + printf("\n\t\tFrequencies: "); + if (mpeg->frequency & MPEG_SAMPLING_FREQ_16000) + printf("16Khz "); + if (mpeg->frequency & MPEG_SAMPLING_FREQ_22050) + printf("22.05Khz "); + if (mpeg->frequency & MPEG_SAMPLING_FREQ_24000) + printf("24Khz "); + if (mpeg->frequency & MPEG_SAMPLING_FREQ_32000) + printf("32Khz "); + if (mpeg->frequency & MPEG_SAMPLING_FREQ_44100) + printf("44.1Khz "); + if (mpeg->frequency & MPEG_SAMPLING_FREQ_48000) + printf("48Khz "); + + printf("\n\t\tCRC: %s", mpeg->crc ? "Yes" : "No"); + + printf("\n\t\tLayer: "); + if (mpeg->layer & MPEG_LAYER_MP1) + printf("1 "); + if (mpeg->layer & MPEG_LAYER_MP2) + printf("2 "); + if (mpeg->layer & MPEG_LAYER_MP3) + printf("3 "); + + printf("\n\t\tBit Rate: "); + if (mpeg->bitrate & MPEG_BIT_RATE_FREE) + printf("Free format"); + else { + if (mpeg->bitrate & MPEG_BIT_RATE_32000) + printf("32kbps "); + if (mpeg->bitrate & MPEG_BIT_RATE_40000) + printf("40kbps "); + if (mpeg->bitrate & MPEG_BIT_RATE_48000) + printf("48kbps "); + if (mpeg->bitrate & MPEG_BIT_RATE_56000) + printf("56kbps "); + if (mpeg->bitrate & MPEG_BIT_RATE_64000) + printf("64kbps "); + if (mpeg->bitrate & MPEG_BIT_RATE_80000) + printf("80kbps "); + if (mpeg->bitrate & MPEG_BIT_RATE_96000) + printf("96kbps "); + if (mpeg->bitrate & MPEG_BIT_RATE_112000) + printf("112kbps "); + if (mpeg->bitrate & MPEG_BIT_RATE_128000) + printf("128kbps "); + if (mpeg->bitrate & MPEG_BIT_RATE_160000) + printf("160kbps "); + if (mpeg->bitrate & MPEG_BIT_RATE_192000) + printf("192kbps "); + if (mpeg->bitrate & MPEG_BIT_RATE_224000) + printf("224kbps "); + if (mpeg->bitrate & MPEG_BIT_RATE_256000) + printf("256kbps "); + if (mpeg->bitrate & MPEG_BIT_RATE_320000) + printf("320kbps "); + } + + printf("\n\t\tVBR: %s", mpeg->bitrate & MPEG_BIT_RATE_VBR ? "Yes" : + "No"); + + printf("\n\t\tPayload Format: "); + if (mpeg->mpf) + printf("RFC-2250 RFC-3119\n"); + else + printf("RFC-2250\n"); +} + +static void print_sbc(struct sbc_codec_cap *sbc) +{ + printf("\tMedia Codec: SBC\n\t\tChannel Modes: "); + + if (sbc->channel_mode & SBC_CHANNEL_MODE_MONO) + printf("Mono "); + if (sbc->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL) + printf("DualChannel "); + if (sbc->channel_mode & SBC_CHANNEL_MODE_STEREO) + printf("Stereo "); + if (sbc->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO) + printf("JointStereo"); + + printf("\n\t\tFrequencies: "); + if (sbc->frequency & SBC_SAMPLING_FREQ_16000) + printf("16Khz "); + if (sbc->frequency & SBC_SAMPLING_FREQ_32000) + printf("32Khz "); + if (sbc->frequency & SBC_SAMPLING_FREQ_44100) + printf("44.1Khz "); + if (sbc->frequency & SBC_SAMPLING_FREQ_48000) + printf("48Khz "); + + printf("\n\t\tSubbands: "); + if (sbc->allocation_method & SBC_SUBBANDS_4) + printf("4 "); + if (sbc->allocation_method & SBC_SUBBANDS_8) + printf("8"); + + printf("\n\t\tBlocks: "); + if (sbc->block_length & SBC_BLOCK_LENGTH_4) + printf("4 "); + if (sbc->block_length & SBC_BLOCK_LENGTH_8) + printf("8 "); + if (sbc->block_length & SBC_BLOCK_LENGTH_12) + printf("12 "); + if (sbc->block_length & SBC_BLOCK_LENGTH_16) + printf("16 "); + + printf("\n\t\tBitpool Range: %d-%d\n", + sbc->min_bitpool, sbc->max_bitpool); +} + +static void print_media_codec(struct avdtp_media_codec_capability *cap) +{ + switch (cap->media_codec_type) { + case A2DP_CODEC_SBC: + print_sbc((void *) cap); + break; + case A2DP_CODEC_MPEG12: + print_mpeg12((void *) cap); + break; + default: + printf("\tMedia Codec: Unknown\n"); + } +} + +static void print_caps(void *data, int size) +{ + int processed; + + for (processed = 0; processed + 2 < size;) { + struct avdtp_service_capability *cap; + + cap = data; + + if (processed + 2 + cap->length > size) { + printf("Invalid capability data in getcap resp\n"); + break; + } + + switch (cap->category) { + case AVDTP_MEDIA_TRANSPORT: + case AVDTP_REPORTING: + case AVDTP_RECOVERY: + case AVDTP_CONTENT_PROTECTION: + case AVDTP_MULTIPLEXING: + /* FIXME: Add proper functions */ + break; + case AVDTP_MEDIA_CODEC: + print_media_codec((void *) cap->data); + break; + } + + processed += 2 + cap->length; + data += 2 + cap->length; + } +} + +static void init_request(struct avdtp_header *header, int request_id) +{ + static int transaction = 0; + + header->packet_type = AVDTP_PKT_TYPE_SINGLE; + header->message_type = AVDTP_MSG_TYPE_COMMAND; + header->transaction = transaction; + header->signal_id = request_id; + + /* clear rfa bits */ + header->rfa0 = 0; + + transaction = (transaction + 1) % 16; +} + +static ssize_t avdtp_send(int sk, void *data, int len) +{ + ssize_t ret; + + ret = send(sk, data, len, 0); + + if (ret < 0) + ret = -errno; + else if (ret != len) + ret = -EIO; + + if (ret < 0) { + printf("Unable to send message: %s (%zd)\n", + strerror(-ret), -ret); + return ret; + } + + return ret; +} + +static ssize_t avdtp_receive(int sk, void *data, int len) +{ + ssize_t ret; + + ret = recv(sk, data, len, 0); + + if (ret < 0) { + printf("Unable to receive message: %s (%d)\n", + strerror(errno), errno); + return -errno; + } + + return ret; +} + +static ssize_t avdtp_get_caps(int sk, int seid) +{ + struct seid_req req; + char buffer[1024]; + struct getcap_resp *caps = (void *) buffer; + ssize_t ret; + + memset(&req, 0, sizeof(req)); + init_request(&req.header, AVDTP_GET_CAPABILITIES); + req.acp_seid = seid; + + ret = avdtp_send(sk, &req, sizeof(req)); + if (ret < 0) + return ret; + + memset(&buffer, 0, sizeof(buffer)); + ret = avdtp_receive(sk, caps, sizeof(buffer)); + if (ret < 0) + return ret; + + if ((size_t) ret < (sizeof(struct getcap_resp) + 4 + + sizeof(struct avdtp_media_codec_capability))) { + printf("Invalid capabilities\n"); + return -1; + } + + print_caps(caps, ret); + + return 0; +} + +static ssize_t avdtp_discover(int sk) +{ + struct avdtp_header req; + char buffer[256]; + struct discover_resp *discover = (void *) buffer; + int seps, i; + ssize_t ret; + + memset(&req, 0, sizeof(req)); + init_request(&req, AVDTP_DISCOVER); + + ret = avdtp_send(sk, &req, sizeof(req)); + if (ret < 0) + return ret; + + memset(&buffer, 0, sizeof(buffer)); + ret = avdtp_receive(sk, discover, sizeof(buffer)); + if (ret < 0) + return ret; + + seps = (ret - sizeof(struct avdtp_header)) / sizeof(struct seid_info); + for (i = 0; i < seps; i++) { + const char *type, *media; + + switch (discover->seps[i].type) { + case AVDTP_SEP_TYPE_SOURCE: + type = "Source"; + break; + case AVDTP_SEP_TYPE_SINK: + type = "Sink"; + break; + default: + type = "Invalid"; + } + + switch (discover->seps[i].media_type) { + case AVDTP_MEDIA_TYPE_AUDIO: + media = "Audio"; + break; + case AVDTP_MEDIA_TYPE_VIDEO: + media = "Video"; + break; + case AVDTP_MEDIA_TYPE_MULTIMEDIA: + media = "Multimedia"; + break; + default: + media = "Invalid"; + } + + printf("Stream End-Point #%d: %s %s %s\n", + discover->seps[i].seid, media, type, + discover->seps[i].inuse ? "*" : ""); + + avdtp_get_caps(sk, discover->seps[i].seid); + } + + return 0; +} + +static int l2cap_connect(bdaddr_t *src, bdaddr_t *dst) +{ + struct sockaddr_l2 l2a; + int sk; + + memset(&l2a, 0, sizeof(l2a)); + l2a.l2_family = AF_BLUETOOTH; + bacpy(&l2a.l2_bdaddr, src); + + sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); + if (sk < 0) { + printf("Cannot create L2CAP socket. %s(%d)\n", strerror(errno), + errno); + return -errno; + } + + if (bind(sk, (struct sockaddr *) &l2a, sizeof(l2a)) < 0) { + printf("Bind failed. %s (%d)\n", strerror(errno), errno); + return -errno; + } + + memset(&l2a, 0, sizeof(l2a)); + l2a.l2_family = AF_BLUETOOTH; + bacpy(&l2a.l2_bdaddr, dst); + l2a.l2_psm = htobs(AVDTP_PSM); + + if (connect(sk, (struct sockaddr *) &l2a, sizeof(l2a)) < 0) { + printf("Connect failed. %s(%d)\n", strerror(errno), errno); + return -errno; + } + + return sk; +} + +static void usage() +{ + printf("avinfo - Audio/Video Info Tool ver %s\n", VERSION); + printf("Usage:\n" + "\tavinfo [options] \n"); + printf("Options:\n" + "\t-h\t\tDisplay help\n" + "\t-i\t\tSpecify source interface\n"); +} + +static struct option main_options[] = { + { "help", 0, 0, 'h' }, + { "device", 1, 0, 'i' }, + { 0, 0, 0, 0 } +}; + +int main(int argc, char *argv[]) +{ + bdaddr_t src, dst; + int opt, sk, dev_id; + + if (argc < 2) { + usage(); + exit(0); + } + + bacpy(&src, BDADDR_ANY); + dev_id = hci_get_route(&src); + if ((dev_id < 0) || (hci_devba(dev_id, &src) < 0)) { + printf("Cannot find any local adapter\n"); + exit(-1); + } + + while ((opt = getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) { + switch (opt) { + case 'i': + if (!strncmp(optarg, "hci", 3)) + hci_devba(atoi(optarg + 3), &src); + else + str2ba(optarg, &src); + break; + + case 'h': + default: + usage(); + exit(0); + } + } + + printf("Connecting ... \n"); + + if (bachk(argv[optind]) < 0) { + printf("Invalid argument\n"); + exit(1); + } + + str2ba(argv[optind], &dst); + sk = l2cap_connect(&src, &dst); + if (sk < 0) + exit(1); + + if (avdtp_discover(sk) < 0) + exit(1); + + return 0; +} diff --git a/tools/bccmd.8 b/tools/bccmd.8 new file mode 100644 index 0000000..28cbe88 --- /dev/null +++ b/tools/bccmd.8 @@ -0,0 +1,130 @@ +.TH BCCMD 8 "Jun 20 2006" BlueZ "Linux System Administration" +.SH NAME +bccmd \- Utility for the CSR BCCMD interface +.SH SYNOPSIS +.B bccmd +.br +.B bccmd [-t ] [-d ] [] +.br +.B bccmd [-h --help] +.br +.SH DESCRIPTION +.B +bccmd +issues BlueCore commands to +.B +Cambridge Silicon Radio +devices. If run without the argument, a short help page will be displayed. +.SH OPTIONS +.TP +.BI -t\ +Specify the communication transport. Valid options are: +.RS +.TP +.BI HCI +Local device with Host Controller Interface (default). +.TP +.BI USB +Direct USB connection. +.TP +.BI BCSP +Blue Core Serial Protocol. +.TP +.BI H4 +H4 serial protocol. +.TP +.BI 3WIRE +3WIRE protocol (not implemented). +.SH +.TP +.BI -d\ +Specify a particular device to operate on. If not specified, default is the first available HCI device +or /dev/ttyS0 for serial transports. +.SH COMMANDS +.TP +.BI builddef +Get build definitions +.TP +.BI keylen\ +Get current crypt key length +.TP +.BI clock +Get local Bluetooth clock +.TP +.BI rand +Get random number +.TP +.BI chiprev +Get chip revision +.TP +.BI buildname +Get the full build name +.TP +.BI panicarg +Get panic code argument +.TP +.BI faultarg +Get fault code argument +.TP +.BI coldreset +Perform cold reset +.TP +.BI warmreset +Perform warm reset +.TP +.BI disabletx +Disable TX on the device +.TP +.BI enabletx +Enable TX on the device +.TP +.BI singlechan\ +Lock radio on specific channel +.TP +.BI hoppingon +Revert to channel hopping +.TP +.BI rttxdata1\ \ +TXData1 radio test +.TP +.BI radiotest\ \ \ +Run radio tests, tests 4, 6 and 7 are transmit tests +.TP +.BI memtypes +Get memory types +.TP +.BI psget\ [-r]\ [-s\ ]\ +Get value for PS key. +-r sends a warm reset afterwards +.TP +.BI psset\ [-r]\ [-s\ ]\ \ +Set value for PS key. +-r sends a warm reset afterwards +.TP +.BI psclr\ [-r]\ [-s\ ]\ +Clear value for PS key. +-r sends a warm reset afterwards +.TP +.BI pslist\ [-r]\ [-s\ ] +List all PS keys. +-r sends a warm reset afterwards +.TP +.BI psread\ [-r]\ [-s\ ] +Read all PS keys. +-r sends a warm reset afterwards +.TP +.BI psload\ [-r]\ [-s\ ]\ +Load all PS keys from PSR file. +-r sends a warm reset afterwards +.TP +.BI pscheck\ [-r]\ [-s\ ]\ +Check syntax of PSR file. +-r sends a warm reset afterwards +.SH KEYS +bdaddr country devclass keymin keymax features commands version +remver hciextn mapsco baudrate hostintf anafreq anaftrim usbvid +usbpid dfupid bootmode +.SH AUTHORS +Written by Marcel Holtmann , +man page by Adam Laurie +.PP diff --git a/tools/bccmd.c b/tools/bccmd.c new file mode 100644 index 0000000..5cb9255 --- /dev/null +++ b/tools/bccmd.c @@ -0,0 +1,1254 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "csr.h" + +#define CSR_TRANSPORT_UNKNOWN 0 +#define CSR_TRANSPORT_HCI 1 +#define CSR_TRANSPORT_USB 2 +#define CSR_TRANSPORT_BCSP 3 +#define CSR_TRANSPORT_H4 4 +#define CSR_TRANSPORT_3WIRE 5 + +#define CSR_STORES_PSI (0x0001) +#define CSR_STORES_PSF (0x0002) +#define CSR_STORES_PSROM (0x0004) +#define CSR_STORES_PSRAM (0x0008) +#define CSR_STORES_DEFAULT (CSR_STORES_PSI | CSR_STORES_PSF) + +#define CSR_TYPE_NULL 0 +#define CSR_TYPE_COMPLEX 1 +#define CSR_TYPE_UINT8 2 +#define CSR_TYPE_UINT16 3 +#define CSR_TYPE_UINT32 4 + +#define CSR_TYPE_ARRAY CSR_TYPE_COMPLEX +#define CSR_TYPE_BDADDR CSR_TYPE_COMPLEX + +static inline int transport_open(int transport, char *device, speed_t bcsp_rate) +{ + switch (transport) { + case CSR_TRANSPORT_HCI: + return csr_open_hci(device); +#ifdef HAVE_LIBUSB + case CSR_TRANSPORT_USB: + return csr_open_usb(device); +#endif + case CSR_TRANSPORT_BCSP: + return csr_open_bcsp(device, bcsp_rate); + case CSR_TRANSPORT_H4: + return csr_open_h4(device); + case CSR_TRANSPORT_3WIRE: + return csr_open_3wire(device); + default: + fprintf(stderr, "Unsupported transport\n"); + return -1; + } +} + +static inline int transport_read(int transport, uint16_t varid, uint8_t *value, uint16_t length) +{ + switch (transport) { + case CSR_TRANSPORT_HCI: + return csr_read_hci(varid, value, length); +#ifdef HAVE_LIBUSB + case CSR_TRANSPORT_USB: + return csr_read_usb(varid, value, length); +#endif + case CSR_TRANSPORT_BCSP: + return csr_read_bcsp(varid, value, length); + case CSR_TRANSPORT_H4: + return csr_read_h4(varid, value, length); + case CSR_TRANSPORT_3WIRE: + return csr_read_3wire(varid, value, length); + default: + errno = EOPNOTSUPP; + return -1; + } +} + +static inline int transport_write(int transport, uint16_t varid, uint8_t *value, uint16_t length) +{ + switch (transport) { + case CSR_TRANSPORT_HCI: + return csr_write_hci(varid, value, length); +#ifdef HAVE_LIBUSB + case CSR_TRANSPORT_USB: + return csr_write_usb(varid, value, length); +#endif + case CSR_TRANSPORT_BCSP: + return csr_write_bcsp(varid, value, length); + case CSR_TRANSPORT_H4: + return csr_write_h4(varid, value, length); + case CSR_TRANSPORT_3WIRE: + return csr_write_3wire(varid, value, length); + default: + errno = EOPNOTSUPP; + return -1; + } +} + +static inline void transport_close(int transport) +{ + switch (transport) { + case CSR_TRANSPORT_HCI: + csr_close_hci(); + break; +#ifdef HAVE_LIBUSB + case CSR_TRANSPORT_USB: + csr_close_usb(); + break; +#endif + case CSR_TRANSPORT_BCSP: + csr_close_bcsp(); + break; + case CSR_TRANSPORT_H4: + csr_close_h4(); + break; + case CSR_TRANSPORT_3WIRE: + csr_close_3wire(); + break; + } +} + +static struct { + uint16_t pskey; + int type; + int size; + char *str; +} storage[] = { + { CSR_PSKEY_BDADDR, CSR_TYPE_BDADDR, 8, "bdaddr" }, + { CSR_PSKEY_COUNTRYCODE, CSR_TYPE_UINT16, 0, "country" }, + { CSR_PSKEY_CLASSOFDEVICE, CSR_TYPE_UINT32, 0, "devclass" }, + { CSR_PSKEY_ENC_KEY_LMIN, CSR_TYPE_UINT16, 0, "keymin" }, + { CSR_PSKEY_ENC_KEY_LMAX, CSR_TYPE_UINT16, 0, "keymax" }, + { CSR_PSKEY_LOCAL_SUPPORTED_FEATURES, CSR_TYPE_ARRAY, 8, "features" }, + { CSR_PSKEY_LOCAL_SUPPORTED_COMMANDS, CSR_TYPE_ARRAY, 18, "commands" }, + { CSR_PSKEY_HCI_LMP_LOCAL_VERSION, CSR_TYPE_UINT16, 0, "version" }, + { CSR_PSKEY_LMP_REMOTE_VERSION, CSR_TYPE_UINT8, 0, "remver" }, + { CSR_PSKEY_HOSTIO_USE_HCI_EXTN, CSR_TYPE_UINT16, 0, "hciextn" }, + { CSR_PSKEY_HOSTIO_MAP_SCO_PCM, CSR_TYPE_UINT16, 0, "mapsco" }, + { CSR_PSKEY_UART_BAUDRATE, CSR_TYPE_UINT16, 0, "baudrate" }, + { CSR_PSKEY_HOST_INTERFACE, CSR_TYPE_UINT16, 0, "hostintf" }, + { CSR_PSKEY_ANA_FREQ, CSR_TYPE_UINT16, 0, "anafreq" }, + { CSR_PSKEY_ANA_FTRIM, CSR_TYPE_UINT16, 0, "anaftrim" }, + { CSR_PSKEY_USB_VENDOR_ID, CSR_TYPE_UINT16, 0, "usbvid" }, + { CSR_PSKEY_USB_PRODUCT_ID, CSR_TYPE_UINT16, 0, "usbpid" }, + { CSR_PSKEY_USB_DFU_PRODUCT_ID, CSR_TYPE_UINT16, 0, "dfupid" }, + { CSR_PSKEY_INITIAL_BOOTMODE, CSR_TYPE_UINT16, 0, "bootmode" }, + { 0x0000 }, +}; + +static char *storestostr(uint16_t stores) +{ + switch (stores) { + case 0x0000: + return "Default"; + case 0x0001: + return "psi"; + case 0x0002: + return "psf"; + case 0x0004: + return "psrom"; + case 0x0008: + return "psram"; + default: + return "Unknown"; + } +} + +static char *memorytostr(uint16_t type) +{ + switch (type) { + case 0x0000: + return "Flash memory"; + case 0x0001: + return "EEPROM"; + case 0x0002: + return "RAM (transient)"; + case 0x0003: + return "ROM (or \"read-only\" flash memory)"; + default: + return "Unknown"; + } +} + +#define OPT_RANGE(min, max) \ + if (argc < (min)) { errno = EINVAL; return -1; } \ + if (argc > (max)) { errno = E2BIG; return -1; } + +static struct option help_options[] = { + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } +}; + +static int opt_help(int argc, char *argv[], int *help) +{ + int opt; + + while ((opt=getopt_long(argc, argv, "+h", help_options, NULL)) != EOF) { + switch (opt) { + case 'h': + if (help) + *help = 1; + break; + } + } + + return optind; +} + +#define OPT_HELP(range, help) \ + opt_help(argc, argv, (help)); \ + argc -= optind; argv += optind; optind = 0; \ + OPT_RANGE((range), (range)) + +static int cmd_builddef(int transport, int argc, char *argv[]) +{ + uint8_t array[8]; + uint16_t def = 0x0000, nextdef = 0x0000; + int err = 0; + + OPT_HELP(0, NULL); + + printf("Build definitions:\n"); + + while (1) { + memset(array, 0, sizeof(array)); + array[0] = def & 0xff; + array[1] = def >> 8; + + err = transport_read(transport, CSR_VARID_GET_NEXT_BUILDDEF, array, 8); + if (err < 0) { + errno = -err; + break; + } + + nextdef = array[2] | (array[3] << 8); + + if (nextdef == 0x0000) + break; + + def = nextdef; + + printf("0x%04x - %s\n", def, csr_builddeftostr(def)); + } + + return err; +} + +static int cmd_keylen(int transport, int argc, char *argv[]) +{ + uint8_t array[8]; + uint16_t handle, keylen; + int err; + + OPT_HELP(1, NULL); + + handle = atoi(argv[0]); + + memset(array, 0, sizeof(array)); + array[0] = handle & 0xff; + array[1] = handle >> 8; + + err = transport_read(transport, CSR_VARID_CRYPT_KEY_LENGTH, array, 8); + if (err < 0) { + errno = -err; + return -1; + } + + handle = array[0] | (array[1] << 8); + keylen = array[2] | (array[3] << 8); + + printf("Crypt key length: %d bit\n", keylen * 8); + + return 0; +} + +static int cmd_clock(int transport, int argc, char *argv[]) +{ + uint8_t array[8]; + uint32_t clock; + int err; + + OPT_HELP(0, NULL); + + memset(array, 0, sizeof(array)); + + err = transport_read(transport, CSR_VARID_BT_CLOCK, array, 8); + if (err < 0) { + errno = -err; + return -1; + } + + clock = array[2] | (array[3] << 8) | (array[0] << 16) | (array[1] << 24); + + printf("Bluetooth clock: 0x%04x (%d)\n", clock, clock); + + return 0; +} + +static int cmd_rand(int transport, int argc, char *argv[]) +{ + uint8_t array[8]; + uint16_t rand; + int err; + + OPT_HELP(0, NULL); + + memset(array, 0, sizeof(array)); + + err = transport_read(transport, CSR_VARID_RAND, array, 8); + if (err < 0) { + errno = -err; + return -1; + } + + rand = array[0] | (array[1] << 8); + + printf("Random number: 0x%02x (%d)\n", rand, rand); + + return 0; +} + +static int cmd_chiprev(int transport, int argc, char *argv[]) +{ + uint8_t array[8]; + uint16_t rev; + char *str; + int err; + + OPT_HELP(0, NULL); + + memset(array, 0, sizeof(array)); + + err = transport_read(transport, CSR_VARID_CHIPREV, array, 8); + if (err < 0) { + errno = -err; + return -1; + } + + rev = array[0] | (array[1] << 8); + + switch (rev) { + case 0x64: + str = "BC1 ES"; + break; + case 0x65: + str = "BC1"; + break; + case 0x89: + str = "BC2-External A"; + break; + case 0x8a: + str = "BC2-External B"; + break; + case 0x28: + str = "BC2-ROM"; + break; + case 0x43: + str = "BC3-Multimedia"; + break; + case 0x15: + str = "BC3-ROM"; + break; + case 0xe2: + str = "BC3-Flash"; + break; + case 0x26: + str = "BC4-External"; + break; + case 0x30: + str = "BC4-ROM"; + break; + default: + str = "NA"; + break; + } + + printf("Chip revision: 0x%04x (%s)\n", rev, str); + + return 0; +} + +static int cmd_buildname(int transport, int argc, char *argv[]) +{ + uint8_t array[130]; + char name[64]; + unsigned int i; + int err; + + OPT_HELP(0, NULL); + + memset(array, 0, sizeof(array)); + + err = transport_read(transport, CSR_VARID_READ_BUILD_NAME, array, 128); + if (err < 0) { + errno = -err; + return -1; + } + + for (i = 0; i < sizeof(name); i++) + name[i] = array[(i * 2) + 4]; + + printf("Build name: %s\n", name); + + return 0; +} + +static int cmd_panicarg(int transport, int argc, char *argv[]) +{ + uint8_t array[8]; + uint16_t error; + int err; + + OPT_HELP(0, NULL); + + memset(array, 0, sizeof(array)); + + err = transport_read(transport, CSR_VARID_PANIC_ARG, array, 8); + if (err < 0) { + errno = -err; + return -1; + } + + error = array[0] | (array[1] << 8); + + printf("Panic code: 0x%02x (%s)\n", error, + error < 0x100 ? "valid" : "invalid"); + + return 0; +} + +static int cmd_faultarg(int transport, int argc, char *argv[]) +{ + uint8_t array[8]; + uint16_t error; + int err; + + OPT_HELP(0, NULL); + + memset(array, 0, sizeof(array)); + + err = transport_read(transport, CSR_VARID_FAULT_ARG, array, 8); + if (err < 0) { + errno = -err; + return -1; + } + + error = array[0] | (array[1] << 8); + + printf("Fault code: 0x%02x (%s)\n", error, + error < 0x100 ? "valid" : "invalid"); + + return 0; +} + +static int cmd_coldreset(int transport, int argc, char *argv[]) +{ + return transport_write(transport, CSR_VARID_COLD_RESET, NULL, 0); +} + +static int cmd_warmreset(int transport, int argc, char *argv[]) +{ + return transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0); +} + +static int cmd_disabletx(int transport, int argc, char *argv[]) +{ + return transport_write(transport, CSR_VARID_DISABLE_TX, NULL, 0); +} + +static int cmd_enabletx(int transport, int argc, char *argv[]) +{ + return transport_write(transport, CSR_VARID_ENABLE_TX, NULL, 0); +} + +static int cmd_singlechan(int transport, int argc, char *argv[]) +{ + uint8_t array[8]; + uint16_t channel; + + OPT_HELP(1, NULL); + + channel = atoi(argv[0]); + + if (channel > 2401 && channel < 2481) + channel -= 2402; + + if (channel > 78) { + errno = EINVAL; + return -1; + } + + memset(array, 0, sizeof(array)); + array[0] = channel & 0xff; + array[1] = channel >> 8; + + return transport_write(transport, CSR_VARID_SINGLE_CHAN, array, 8); +} + +static int cmd_hoppingon(int transport, int argc, char *argv[]) +{ + return transport_write(transport, CSR_VARID_HOPPING_ON, NULL, 0); +} + +static int cmd_rttxdata1(int transport, int argc, char *argv[]) +{ + uint8_t array[8]; + uint16_t freq, level; + + OPT_HELP(2, NULL); + + freq = atoi(argv[0]); + + if (!strncasecmp(argv[1], "0x", 2)) + level = strtol(argv[1], NULL, 16); + else + level = atoi(argv[1]); + + memset(array, 0, sizeof(array)); + array[0] = 0x04; + array[1] = 0x00; + array[2] = freq & 0xff; + array[3] = freq >> 8; + array[4] = level & 0xff; + array[5] = level >> 8; + + return transport_write(transport, CSR_VARID_RADIOTEST, array, 8); +} + +static int cmd_radiotest(int transport, int argc, char *argv[]) +{ + uint8_t array[8]; + uint16_t freq, level, test; + + OPT_HELP(3, NULL); + + freq = atoi(argv[0]); + + if (!strncasecmp(argv[1], "0x", 2)) + level = strtol(argv[1], NULL, 16); + else + level = atoi(argv[1]); + + test = atoi(argv[2]); + + memset(array, 0, sizeof(array)); + array[0] = test & 0xff; + array[1] = test >> 8; + array[2] = freq & 0xff; + array[3] = freq >> 8; + array[4] = level & 0xff; + array[5] = level >> 8; + + return transport_write(transport, CSR_VARID_RADIOTEST, array, 8); +} + +static int cmd_memtypes(int transport, int argc, char *argv[]) +{ + uint8_t array[8]; + uint16_t type, stores[4] = { 0x0001, 0x0002, 0x0004, 0x0008 }; + int i, err; + + OPT_HELP(0, NULL); + + for (i = 0; i < 4; i++) { + memset(array, 0, sizeof(array)); + array[0] = stores[i] & 0xff; + array[1] = stores[i] >> 8; + + err = transport_read(transport, CSR_VARID_PS_MEMORY_TYPE, array, 8); + if (err < 0) + continue; + + type = array[2] + (array[3] << 8); + + printf("%s (0x%04x) = %s (%d)\n", storestostr(stores[i]), + stores[i], memorytostr(type), type); + } + + return 0; +} + +static struct option pskey_options[] = { + { "stores", 1, 0, 's' }, + { "reset", 0, 0, 'r' }, + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } +}; + +static int opt_pskey(int argc, char *argv[], uint16_t *stores, int *reset, int *help) +{ + int opt; + + while ((opt=getopt_long(argc, argv, "+s:rh", pskey_options, NULL)) != EOF) { + switch (opt) { + case 's': + if (!stores) + break; + if (!strcasecmp(optarg, "default")) + *stores = 0x0000; + else if (!strcasecmp(optarg, "implementation")) + *stores = 0x0001; + else if (!strcasecmp(optarg, "factory")) + *stores = 0x0002; + else if (!strcasecmp(optarg, "rom")) + *stores = 0x0004; + else if (!strcasecmp(optarg, "ram")) + *stores = 0x0008; + else if (!strcasecmp(optarg, "psi")) + *stores = 0x0001; + else if (!strcasecmp(optarg, "psf")) + *stores = 0x0002; + else if (!strcasecmp(optarg, "psrom")) + *stores = 0x0004; + else if (!strcasecmp(optarg, "psram")) + *stores = 0x0008; + else if (!strncasecmp(optarg, "0x", 2)) + *stores = strtol(optarg, NULL, 16); + else + *stores = atoi(optarg); + break; + + case 'r': + if (reset) + *reset = 1; + break; + + case 'h': + if (help) + *help = 1; + break; + } + } + + return optind; +} + +#define OPT_PSKEY(min, max, stores, reset, help) \ + opt_pskey(argc, argv, (stores), (reset), (help)); \ + argc -= optind; argv += optind; optind = 0; \ + OPT_RANGE((min), (max)) + +static int cmd_psget(int transport, int argc, char *argv[]) +{ + uint8_t array[128]; + uint16_t pskey, length, value, stores = CSR_STORES_DEFAULT; + uint32_t val32; + int i, err, reset = 0; + + memset(array, 0, sizeof(array)); + + OPT_PSKEY(1, 1, &stores, &reset, NULL); + + if (strncasecmp(argv[0], "0x", 2)) { + pskey = atoi(argv[0]); + + for (i = 0; storage[i].pskey; i++) { + if (strcasecmp(storage[i].str, argv[0])) + continue; + + pskey = storage[i].pskey; + break; + } + } else + pskey = strtol(argv[0] + 2, NULL, 16); + + memset(array, 0, sizeof(array)); + array[0] = pskey & 0xff; + array[1] = pskey >> 8; + array[2] = stores & 0xff; + array[3] = stores >> 8; + + err = transport_read(transport, CSR_VARID_PS_SIZE, array, 8); + if (err < 0) + return err; + + length = array[2] + (array[3] << 8); + if (length + 6 > (int) sizeof(array) / 2) + return -EIO; + + memset(array, 0, sizeof(array)); + array[0] = pskey & 0xff; + array[1] = pskey >> 8; + array[2] = length & 0xff; + array[3] = length >> 8; + array[4] = stores & 0xff; + array[5] = stores >> 8; + + err = transport_read(transport, CSR_VARID_PS, array, (length + 3) * 2); + if (err < 0) + return err; + + switch (length) { + case 1: + value = array[6] | (array[7] << 8); + printf("%s: 0x%04x (%d)\n", csr_pskeytostr(pskey), value, value); + break; + + case 2: + val32 = array[8] | (array[9] << 8) | (array[6] << 16) | (array[7] << 24); + printf("%s: 0x%08x (%d)\n", csr_pskeytostr(pskey), val32, val32); + break; + + default: + printf("%s:", csr_pskeytostr(pskey)); + for (i = 0; i < length; i++) + printf(" 0x%02x%02x", array[(i * 2) + 6], array[(i * 2) + 7]); + printf("\n"); + break; + } + + if (reset) + transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0); + + return err; +} + +static int cmd_psset(int transport, int argc, char *argv[]) +{ + uint8_t array[128]; + uint16_t pskey, length, value, stores = CSR_STORES_PSRAM; + uint32_t val32; + int i, err, reset = 0; + + memset(array, 0, sizeof(array)); + + OPT_PSKEY(2, 81, &stores, &reset, NULL); + + if (strncasecmp(argv[0], "0x", 2)) { + pskey = atoi(argv[0]); + + for (i = 0; storage[i].pskey; i++) { + if (strcasecmp(storage[i].str, argv[0])) + continue; + + pskey = storage[i].pskey; + break; + } + } else + pskey = strtol(argv[0] + 2, NULL, 16); + + memset(array, 0, sizeof(array)); + array[0] = pskey & 0xff; + array[1] = pskey >> 8; + array[2] = stores & 0xff; + array[3] = stores >> 8; + + err = transport_read(transport, CSR_VARID_PS_SIZE, array, 8); + if (err < 0) + return err; + + length = array[2] + (array[3] << 8); + if (length + 6 > (int) sizeof(array) / 2) + return -EIO; + + memset(array, 0, sizeof(array)); + array[0] = pskey & 0xff; + array[1] = pskey >> 8; + array[2] = length & 0xff; + array[3] = length >> 8; + array[4] = stores & 0xff; + array[5] = stores >> 8; + + argc--; + argv++; + + switch (length) { + case 1: + if (argc != 1) { + errno = E2BIG; + return -1; + } + + if (!strncasecmp(argv[0], "0x", 2)) + value = strtol(argv[0] + 2, NULL, 16); + else + value = atoi(argv[0]); + + array[6] = value & 0xff; + array[7] = value >> 8; + break; + + case 2: + if (argc != 1) { + errno = E2BIG; + return -1; + } + + if (!strncasecmp(argv[0], "0x", 2)) + val32 = strtol(argv[0] + 2, NULL, 16); + else + val32 = atoi(argv[0]); + + array[6] = (val32 & 0xff0000) >> 16; + array[7] = val32 >> 24; + array[8] = val32 & 0xff; + array[9] = (val32 & 0xff00) >> 8; + break; + + default: + if (argc != length * 2) { + errno = EINVAL; + return -1; + } + + for (i = 0; i < length * 2; i++) + if (!strncasecmp(argv[0], "0x", 2)) + array[i + 6] = strtol(argv[i] + 2, NULL, 16); + else + array[i + 6] = atoi(argv[i]); + break; + } + + err = transport_write(transport, CSR_VARID_PS, array, (length + 3) * 2); + if (err < 0) + return err; + + if (reset) + transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0); + + return err; +} + +static int cmd_psclr(int transport, int argc, char *argv[]) +{ + uint8_t array[8]; + uint16_t pskey, stores = CSR_STORES_PSRAM; + int i, err, reset = 0; + + OPT_PSKEY(1, 1, &stores, &reset, NULL); + + if (strncasecmp(argv[0], "0x", 2)) { + pskey = atoi(argv[0]); + + for (i = 0; storage[i].pskey; i++) { + if (strcasecmp(storage[i].str, argv[0])) + continue; + + pskey = storage[i].pskey; + break; + } + } else + pskey = strtol(argv[0] + 2, NULL, 16); + + memset(array, 0, sizeof(array)); + array[0] = pskey & 0xff; + array[1] = pskey >> 8; + array[2] = stores & 0xff; + array[3] = stores >> 8; + + err = transport_write(transport, CSR_VARID_PS_CLR_STORES, array, 8); + if (err < 0) + return err; + + if (reset) + transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0); + + return err; +} + +static int cmd_pslist(int transport, int argc, char *argv[]) +{ + uint8_t array[8]; + uint16_t pskey = 0x0000, length, stores = CSR_STORES_DEFAULT; + int err, reset = 0; + + OPT_PSKEY(0, 0, &stores, &reset, NULL); + + while (1) { + memset(array, 0, sizeof(array)); + array[0] = pskey & 0xff; + array[1] = pskey >> 8; + array[2] = stores & 0xff; + array[3] = stores >> 8; + + err = transport_read(transport, CSR_VARID_PS_NEXT, array, 8); + if (err < 0) + break; + + pskey = array[4] + (array[5] << 8); + if (pskey == 0x0000) + break; + + memset(array, 0, sizeof(array)); + array[0] = pskey & 0xff; + array[1] = pskey >> 8; + array[2] = stores & 0xff; + array[3] = stores >> 8; + + err = transport_read(transport, CSR_VARID_PS_SIZE, array, 8); + if (err < 0) + continue; + + length = array[2] + (array[3] << 8); + + printf("0x%04x - %s (%d bytes)\n", pskey, + csr_pskeytostr(pskey), length * 2); + } + + if (reset) + transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0); + + return 0; +} + +static int cmd_psread(int transport, int argc, char *argv[]) +{ + uint8_t array[256]; + uint16_t pskey = 0x0000, length, stores = CSR_STORES_DEFAULT; + char *str, val[7]; + int i, err, reset = 0; + + OPT_PSKEY(0, 0, &stores, &reset, NULL); + + while (1) { + memset(array, 0, sizeof(array)); + array[0] = pskey & 0xff; + array[1] = pskey >> 8; + array[2] = stores & 0xff; + array[3] = stores >> 8; + + err = transport_read(transport, CSR_VARID_PS_NEXT, array, 8); + if (err < 0) + break; + + pskey = array[4] + (array[5] << 8); + if (pskey == 0x0000) + break; + + memset(array, 0, sizeof(array)); + array[0] = pskey & 0xff; + array[1] = pskey >> 8; + array[2] = stores & 0xff; + array[3] = stores >> 8; + + err = transport_read(transport, CSR_VARID_PS_SIZE, array, 8); + if (err < 0) + continue; + + length = array[2] + (array[3] << 8); + if (length + 6 > (int) sizeof(array) / 2) + continue; + + memset(array, 0, sizeof(array)); + array[0] = pskey & 0xff; + array[1] = pskey >> 8; + array[2] = length & 0xff; + array[3] = length >> 8; + array[4] = stores & 0xff; + array[5] = stores >> 8; + + err = transport_read(transport, CSR_VARID_PS, array, (length + 3) * 2); + if (err < 0) + continue; + + str = csr_pskeytoval(pskey); + if (!strcasecmp(str, "UNKNOWN")) { + sprintf(val, "0x%04x", pskey); + str = NULL; + } + + printf("// %s%s\n&%04x =", str ? "PSKEY_" : "", + str ? str : val, pskey); + for (i = 0; i < length; i++) + printf(" %02x%02x", array[(i * 2) + 7], array[(i * 2) + 6]); + printf("\n"); + } + + if (reset) + transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0); + + return 0; +} + +static int cmd_psload(int transport, int argc, char *argv[]) +{ + uint8_t array[256]; + uint16_t pskey, length, size, stores = CSR_STORES_PSRAM; + char *str, val[7]; + int err, reset = 0; + + OPT_PSKEY(1, 1, &stores, &reset, NULL); + + psr_read(argv[0]); + + memset(array, 0, sizeof(array)); + size = sizeof(array) - 6; + + while (psr_get(&pskey, array + 6, &size) == 0) { + str = csr_pskeytoval(pskey); + if (!strcasecmp(str, "UNKNOWN")) { + sprintf(val, "0x%04x", pskey); + str = NULL; + } + + printf("Loading %s%s ... ", str ? "PSKEY_" : "", + str ? str : val); + fflush(stdout); + + length = size / 2; + + array[0] = pskey & 0xff; + array[1] = pskey >> 8; + array[2] = length & 0xff; + array[3] = length >> 8; + array[4] = stores & 0xff; + array[5] = stores >> 8; + + err = transport_write(transport, CSR_VARID_PS, array, size + 6); + + printf("%s\n", err < 0 ? "failed" : "done"); + + memset(array, 0, sizeof(array)); + size = sizeof(array) - 6; + } + + if (reset) + transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0); + + return 0; +} + +static int cmd_pscheck(int transport, int argc, char *argv[]) +{ + uint8_t array[256]; + uint16_t pskey, size; + int i; + + OPT_HELP(1, NULL); + + psr_read(argv[0]); + + while (psr_get(&pskey, array, &size) == 0) { + printf("0x%04x =", pskey); + for (i = 0; i < size; i++) + printf(" 0x%02x", array[i]); + printf("\n"); + } + + return 0; +} + +static struct { + char *str; + int (*func)(int transport, int argc, char *argv[]); + char *arg; + char *doc; +} commands[] = { + { "builddef", cmd_builddef, "", "Get build definitions" }, + { "keylen", cmd_keylen, "", "Get current crypt key length" }, + { "clock", cmd_clock, "", "Get local Bluetooth clock" }, + { "rand", cmd_rand, "", "Get random number" }, + { "chiprev", cmd_chiprev, "", "Get chip revision" }, + { "buildname", cmd_buildname, "", "Get the full build name" }, + { "panicarg", cmd_panicarg, "", "Get panic code argument" }, + { "faultarg", cmd_faultarg, "", "Get fault code argument" }, + { "coldreset", cmd_coldreset, "", "Perform cold reset" }, + { "warmreset", cmd_warmreset, "", "Perform warm reset" }, + { "disabletx", cmd_disabletx, "", "Disable TX on the device" }, + { "enabletx", cmd_enabletx, "", "Enable TX on the device" }, + { "singlechan",cmd_singlechan,"", "Lock radio on specific channel" }, + { "hoppingon", cmd_hoppingon, "", "Revert to channel hopping" }, + { "rttxdata1", cmd_rttxdata1, " ", "TXData1 radio test" }, + { "radiotest", cmd_radiotest, " ", "Run radio tests" }, + { "memtypes", cmd_memtypes, NULL, "Get memory types" }, + { "psget", cmd_psget, "", "Get value for PS key" }, + { "psset", cmd_psset, " ", "Set value for PS key" }, + { "psclr", cmd_psclr, "", "Clear value for PS key" }, + { "pslist", cmd_pslist, NULL, "List all PS keys" }, + { "psread", cmd_psread, NULL, "Read all PS keys" }, + { "psload", cmd_psload, "", "Load all PS keys from PSR file" }, + { "pscheck", cmd_pscheck, "", "Check PSR file" }, + { NULL } +}; + +static void usage(void) +{ + int i, pos = 0; + + printf("bccmd - Utility for the CSR BCCMD interface\n\n"); + printf("Usage:\n" + "\tbccmd [options] \n\n"); + + printf("Options:\n" + "\t-t Select the transport\n" + "\t-d Select the device\n" + "\t-b Select the bcsp transfer rate\n" + "\t-h, --help Display help\n" + "\n"); + + printf("Transports:\n" + "\tHCI USB BCSP H4 3WIRE\n\n"); + + printf("Commands:\n"); + for (i = 0; commands[i].str; i++) + printf("\t%-10s %-20s\t%s\n", commands[i].str, + commands[i].arg ? commands[i].arg : " ", + commands[i].doc); + printf("\n"); + + printf("Keys:\n\t"); + for (i = 0; storage[i].pskey; i++) { + printf("%s ", storage[i].str); + pos += strlen(storage[i].str) + 1; + if (pos > 60) { + printf("\n\t"); + pos = 0; + } + } + printf("\n"); +} + +static struct option main_options[] = { + { "transport", 1, 0, 't' }, + { "device", 1, 0, 'd' }, + { "bcsprate", 1, 0, 'b'}, + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } +}; + +int main(int argc, char *argv[]) +{ + char *device = NULL; + int i, err, opt, transport = CSR_TRANSPORT_HCI; + speed_t bcsp_rate = B38400; + + while ((opt=getopt_long(argc, argv, "+t:d:i:b:h", main_options, NULL)) != EOF) { + switch (opt) { + case 't': + if (!strcasecmp(optarg, "hci")) + transport = CSR_TRANSPORT_HCI; + else if (!strcasecmp(optarg, "usb")) + transport = CSR_TRANSPORT_USB; + else if (!strcasecmp(optarg, "bcsp")) + transport = CSR_TRANSPORT_BCSP; + else if (!strcasecmp(optarg, "h4")) + transport = CSR_TRANSPORT_H4; + else if (!strcasecmp(optarg, "h5")) + transport = CSR_TRANSPORT_3WIRE; + else if (!strcasecmp(optarg, "3wire")) + transport = CSR_TRANSPORT_3WIRE; + else if (!strcasecmp(optarg, "twutl")) + transport = CSR_TRANSPORT_3WIRE; + else + transport = CSR_TRANSPORT_UNKNOWN; + break; + + case 'd': + case 'i': + device = strdup(optarg); + break; + case 'b': + switch (atoi(optarg)) { + case 9600: bcsp_rate = B9600; break; + case 19200: bcsp_rate = B19200; break; + case 38400: bcsp_rate = B38400; break; + case 57600: bcsp_rate = B57600; break; + case 115200: bcsp_rate = B115200; break; + case 230400: bcsp_rate = B230400; break; + case 460800: bcsp_rate = B460800; break; + case 500000: bcsp_rate = B500000; break; + case 576000: bcsp_rate = B576000; break; + case 921600: bcsp_rate = B921600; break; + case 1000000: bcsp_rate = B1000000; break; + case 1152000: bcsp_rate = B1152000; break; + case 1500000: bcsp_rate = B1500000; break; + case 2000000: bcsp_rate = B2000000; break; +#ifdef B2500000 + case 2500000: bcsp_rate = B2500000; break; +#endif +#ifdef B3000000 + case 3000000: bcsp_rate = B3000000; break; +#endif +#ifdef B3500000 + case 3500000: bcsp_rate = B3500000; break; +#endif +#ifdef B4000000 + case 4000000: bcsp_rate = B4000000; break; +#endif + default: + printf("Unknown BCSP baud rate specified, defaulting to 38400bps\n"); + bcsp_rate = B38400; + } + break; + case 'h': + default: + usage(); + exit(0); + } + } + + argc -= optind; + argv += optind; + optind = 0; + + if (argc < 1) { + usage(); + exit(1); + } + + if (transport_open(transport, device, bcsp_rate) < 0) + exit(1); + + if (device) + free(device); + + for (i = 0; commands[i].str; i++) { + if (strcasecmp(commands[i].str, argv[0])) + continue; + + err = commands[i].func(transport, argc, argv); + + transport_close(transport); + + if (err < 0) { + fprintf(stderr, "Can't execute command: %s (%d)\n", + strerror(errno), errno); + exit(1); + } + + exit(0); + } + + fprintf(stderr, "Unsupported command\n"); + + transport_close(transport); + + exit(1); +} diff --git a/tools/ciptool.1 b/tools/ciptool.1 new file mode 100644 index 0000000..65d903d --- /dev/null +++ b/tools/ciptool.1 @@ -0,0 +1,68 @@ +.\" +.\" This program is free software; you can redistribute it and/or modify +.\" it under the terms of the GNU General Public License as published by +.\" the Free Software Foundation; either version 2 of the License, or +.\" (at your option) any later version. +.\" +.\" This program is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.\" GNU General Public License for more details. +.\" +.\" You should have received a copy of the GNU General Public License +.\" along with this program; if not, write to the Free Software +.\" Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +.\" +.\" +.TH CIPTOOL 1 "JUNE 6, 2003" "" "" + +.SH NAME +ciptool \- Bluetooth Common ISDN Access Profile (CIP) +.SH SYNOPSIS +.BR "ciptool +[ +.I options +] < +.I command +> +.SH DESCRIPTION +.B ciptool +is used to set up, maintain, and inspect the CIP configuration +of the Bluetooth subsystem in the Linux kernel. +.SH OPTIONS +.TP +.BI -h +Gives a list of possible commands. +.TP +.BI -i " | " +The command is applied to device +.I +hciX +, which must be the name or the address of an installed Bluetooth +device. If not specified, the command will be use the first +available Bluetooth device. +.SH COMMANDS +.TP +.BI show +Display information about the connected devices. +.TP +.BI search +Search for Bluetooth devices and connect to first one that +offers CIP support. +.TP +.BI connect " [psm]" +Connect the local device to the remote Bluetooth device on the +specified PSM number. If no PSM is specified, it will use the +SDP to retrieve it from the remote device. +.TP +.BI release " [bdaddr]" +Release a connection to the specific device. If no address is +given and only one device is connected this will be released. +.TP +.BI loopback " [psm]" +Create a connection to the remote device for Bluetooth testing. +This command will not provide a CAPI controller, because it is +only for testing the CAPI Message Transport Protocol. +.SH AUTHOR +Written by Marcel Holtmann . +.br diff --git a/tools/ciptool.c b/tools/ciptool.c new file mode 100644 index 0000000..edce9da --- /dev/null +++ b/tools/ciptool.c @@ -0,0 +1,498 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2002-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef NEED_PPOLL +#include "ppoll.h" +#endif + +static volatile sig_atomic_t __io_canceled = 0; + +static void sig_hup(int sig) +{ + return; +} + +static void sig_term(int sig) +{ + __io_canceled = 1; +} + +static char *cmtp_state[] = { + "unknown", + "connected", + "open", + "bound", + "listening", + "connecting", + "connecting", + "config", + "disconnecting", + "closed" +}; + +static char *cmtp_flagstostr(uint32_t flags) +{ + static char str[100] = ""; + + strcat(str, "["); + + if (flags & (1 << CMTP_LOOPBACK)) + strcat(str, "loopback"); + + strcat(str, "]"); + + return str; +} + +static int get_psm(bdaddr_t *src, bdaddr_t *dst, unsigned short *psm) +{ + sdp_session_t *s; + sdp_list_t *srch, *attrs, *rsp; + uuid_t svclass; + uint16_t attr; + int err; + + if (!(s = sdp_connect(src, dst, 0))) + return -1; + + sdp_uuid16_create(&svclass, CIP_SVCLASS_ID); + srch = sdp_list_append(NULL, &svclass); + + attr = SDP_ATTR_PROTO_DESC_LIST; + attrs = sdp_list_append(NULL, &attr); + + err = sdp_service_search_attr_req(s, srch, SDP_ATTR_REQ_INDIVIDUAL, attrs, &rsp); + + sdp_close(s); + + if (err) + return 0; + + for (; rsp; rsp = rsp->next) { + sdp_record_t *rec = (sdp_record_t *) rsp->data; + sdp_list_t *protos; + + if (!sdp_get_access_protos(rec, &protos)) { + unsigned short p = sdp_get_proto_port(protos, L2CAP_UUID); + if (p > 0) { + *psm = p; + return 1; + } + } + } + + return 0; +} + +static int do_connect(int ctl, int dev_id, bdaddr_t *src, bdaddr_t *dst, unsigned short psm, uint32_t flags) +{ + struct cmtp_connadd_req req; + struct hci_dev_info di; + struct sockaddr_l2 addr; + struct l2cap_options opts; + socklen_t size; + int sk; + + hci_devinfo(dev_id, &di); + if (!(di.link_policy & HCI_LP_RSWITCH)) { + printf("Local device is not accepting role switch\n"); + } + + if ((sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0) { + perror("Can't create L2CAP socket"); + exit(1); + } + + memset(&addr, 0, sizeof(addr)); + addr.l2_family = AF_BLUETOOTH; + bacpy(&addr.l2_bdaddr, src); + + if (bind(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("Can't bind L2CAP socket"); + close(sk); + exit(1); + } + + memset(&opts, 0, sizeof(opts)); + size = sizeof(opts); + + if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &size) < 0) { + perror("Can't get L2CAP options"); + close(sk); + exit(1); + } + + opts.imtu = CMTP_DEFAULT_MTU; + opts.omtu = CMTP_DEFAULT_MTU; + opts.flush_to = 0xffff; + + if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)) < 0) { + perror("Can't set L2CAP options"); + close(sk); + exit(1); + } + + memset(&addr, 0, sizeof(addr)); + addr.l2_family = AF_BLUETOOTH; + bacpy(&addr.l2_bdaddr, dst); + addr.l2_psm = htobs(psm); + + if (connect(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("Can't connect L2CAP socket"); + close(sk); + exit(1); + } + + req.sock = sk; + req.flags = flags; + + if (ioctl(ctl, CMTPCONNADD, &req) < 0) { + perror("Can't create connection"); + exit(1); + } + + return sk; +} + +static void cmd_show(int ctl, bdaddr_t *bdaddr, int argc, char **argv) +{ + struct cmtp_connlist_req req; + struct cmtp_conninfo ci[16]; + char addr[18]; + unsigned int i; + + req.cnum = 16; + req.ci = ci; + + if (ioctl(ctl, CMTPGETCONNLIST, &req) < 0) { + perror("Can't get connection list"); + exit(1); + } + + for (i = 0; i < req.cnum; i++) { + ba2str(&ci[i].bdaddr, addr); + printf("%d %s %s %s\n", ci[i].num, addr, + cmtp_state[ci[i].state], + ci[i].flags ? cmtp_flagstostr(ci[i].flags) : ""); + } +} + +static void cmd_search(int ctl, bdaddr_t *bdaddr, int argc, char **argv) +{ + inquiry_info *info = NULL; + bdaddr_t src, dst; + unsigned short psm; + int i, dev_id, num_rsp, length, flags; + char addr[18]; + uint8_t class[3]; + + ba2str(bdaddr, addr); + dev_id = hci_devid(addr); + if (dev_id < 0) { + dev_id = hci_get_route(NULL); + hci_devba(dev_id, &src); + } else + bacpy(&src, bdaddr); + + length = 8; /* ~10 seconds */ + num_rsp = 0; + flags = 0; + + printf("Searching ...\n"); + + num_rsp = hci_inquiry(dev_id, length, num_rsp, NULL, &info, flags); + + for (i = 0; i < num_rsp; i++) { + memcpy(class, (info+i)->dev_class, 3); + if ((class[1] == 2) && ((class[0] / 4) == 5)) { + bacpy(&dst, &(info+i)->bdaddr); + ba2str(&dst, addr); + + printf("\tChecking service for %s\n", addr); + if (!get_psm(&src, &dst, &psm)) + continue; + + bt_free(info); + + printf("\tConnecting to device %s\n", addr); + do_connect(ctl, dev_id, &src, &dst, psm, 0); + return; + } + } + + bt_free(info); + fprintf(stderr, "\tNo devices in range or visible\n"); + exit(1); +} + +static void cmd_create(int ctl, bdaddr_t *bdaddr, int argc, char **argv) +{ + bdaddr_t src, dst; + unsigned short psm; + int dev_id; + char addr[18]; + + if (argc < 2) + return; + + str2ba(argv[1], &dst); + + ba2str(bdaddr, addr); + dev_id = hci_devid(addr); + if (dev_id < 0) { + dev_id = hci_get_route(&dst); + hci_devba(dev_id, &src); + } else + bacpy(&src, bdaddr); + + if (argc < 3) { + if (!get_psm(&src, &dst, &psm)) + psm = 4099; + } else + psm = atoi(argv[2]); + + do_connect(ctl, dev_id, &src, &dst, psm, 0); +} + +static void cmd_release(int ctl, bdaddr_t *bdaddr, int argc, char **argv) +{ + struct cmtp_conndel_req req; + struct cmtp_connlist_req cl; + struct cmtp_conninfo ci[16]; + + if (argc < 2) { + cl.cnum = 16; + cl.ci = ci; + + if (ioctl(ctl, CMTPGETCONNLIST, &cl) < 0) { + perror("Can't get connection list"); + exit(1); + } + + if (cl.cnum == 0) + return; + + if (cl.cnum != 1) { + fprintf(stderr, "You have to specifiy the device address.\n"); + exit(1); + } + + bacpy(&req.bdaddr, &ci[0].bdaddr); + } else + str2ba(argv[1], &req.bdaddr); + + if (ioctl(ctl, CMTPCONNDEL, &req) < 0) { + perror("Can't release connection"); + exit(1); + } +} + +static void cmd_loopback(int ctl, bdaddr_t *bdaddr, int argc, char **argv) +{ + struct cmtp_conndel_req req; + struct sigaction sa; + struct pollfd p; + sigset_t sigs; + bdaddr_t src, dst; + unsigned short psm; + int dev_id, sk; + char addr[18]; + + if (argc < 2) + return; + + str2ba(argv[1], &dst); + + ba2str(bdaddr, addr); + dev_id = hci_devid(addr); + if (dev_id < 0) { + dev_id = hci_get_route(&dst); + hci_devba(dev_id, &src); + } else + bacpy(&src, bdaddr); + + ba2str(&dst, addr); + printf("Connecting to %s in loopback mode\n", addr); + + if (argc < 3) { + if (!get_psm(&src, &dst, &psm)) + psm = 4099; + } else + psm = atoi(argv[2]); + + sk = do_connect(ctl, dev_id, &src, &dst, psm, (1 << CMTP_LOOPBACK)); + + printf("Press CTRL-C for hangup\n"); + + memset(&sa, 0, sizeof(sa)); + sa.sa_flags = SA_NOCLDSTOP; + sa.sa_handler = SIG_IGN; + sigaction(SIGCHLD, &sa, NULL); + sigaction(SIGPIPE, &sa, NULL); + + sa.sa_handler = sig_term; + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + + sa.sa_handler = sig_hup; + sigaction(SIGHUP, &sa, NULL); + + sigfillset(&sigs); + sigdelset(&sigs, SIGCHLD); + sigdelset(&sigs, SIGPIPE); + sigdelset(&sigs, SIGTERM); + sigdelset(&sigs, SIGINT); + sigdelset(&sigs, SIGHUP); + + p.fd = sk; + p.events = POLLERR | POLLHUP; + + while (!__io_canceled) { + p.revents = 0; + if (ppoll(&p, 1, NULL, &sigs) > 0) + break; + } + + bacpy(&req.bdaddr, &dst); + ioctl(ctl, CMTPCONNDEL, &req); +} + +static struct { + char *cmd; + char *alt; + void (*func)(int ctl, bdaddr_t *bdaddr, int argc, char **argv); + char *opt; + char *doc; +} command[] = { + { "show", "list", cmd_show, 0, "Show remote connections" }, + { "search", "scan", cmd_search, 0, "Search for a remote device" }, + { "connect", "create", cmd_create, "", "Connect a remote device" }, + { "release", "disconnect", cmd_release, "[bdaddr]", "Disconnect the remote device" }, + { "loopback", "test", cmd_loopback, "", "Loopback test of a device" }, + { NULL, NULL, NULL, 0, 0 } +}; + +static void usage(void) +{ + int i; + + printf("ciptool - Bluetooth Common ISDN Access Profile (CIP)\n\n"); + + printf("Usage:\n" + "\tciptool [options] [command]\n" + "\n"); + + printf("Options:\n" + "\t-i [hciX|bdaddr] Local HCI device or BD Address\n" + "\t-h, --help Display help\n" + "\n"); + + printf("Commands:\n"); + for (i = 0; command[i].cmd; i++) + printf("\t%-8s %-10s\t%s\n", command[i].cmd, + command[i].opt ? command[i].opt : " ", + command[i].doc); + printf("\n"); +} + +static struct option main_options[] = { + { "help", 0, 0, 'h' }, + { "device", 1, 0, 'i' }, + { 0, 0, 0, 0 } +}; + +int main(int argc, char *argv[]) +{ + bdaddr_t bdaddr; + int i, opt, ctl; + + bacpy(&bdaddr, BDADDR_ANY); + + while ((opt = getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) { + switch(opt) { + case 'i': + if (!strncmp(optarg, "hci", 3)) + hci_devba(atoi(optarg + 3), &bdaddr); + else + str2ba(optarg, &bdaddr); + break; + case 'h': + usage(); + exit(0); + default: + exit(0); + } + } + + argc -= optind; + argv += optind; + optind = 0; + + if (argc < 1) { + usage(); + return 0; + } + + if ((ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_CMTP)) < 0 ) { + perror("Can't open CMTP control socket"); + exit(1); + } + + for (i = 0; command[i].cmd; i++) { + if (strncmp(command[i].cmd, argv[0], 4) && strncmp(command[i].alt, argv[0], 4)) + continue; + command[i].func(ctl, &bdaddr, argc, argv); + close(ctl); + exit(0); + } + + usage(); + + close(ctl); + + return 0; +} diff --git a/tools/csr.c b/tools/csr.c new file mode 100644 index 0000000..b4ea1fb --- /dev/null +++ b/tools/csr.c @@ -0,0 +1,2853 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2003-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "csr.h" + +struct psr_data { + uint16_t pskey; + uint8_t *value; + uint8_t size; + struct psr_data *next; +}; + +static struct psr_data *head = NULL, *tail = NULL; + +static struct { + uint16_t id; + char *str; +} csr_map[] = { + { 66, "HCI 9.8" }, + { 97, "HCI 10.3" }, + { 101, "HCI 10.5" }, + { 111, "HCI 11.0" }, + { 112, "HCI 11.1" }, + { 114, "HCI 11.2" }, + { 115, "HCI 11.3" }, + { 117, "HCI 12.0" }, + { 119, "HCI 12.1" }, + { 133, "HCI 12.2" }, + { 134, "HCI 12.3" }, + { 162, "HCI 12.4" }, + { 165, "HCI 12.5" }, + { 169, "HCI 12.6" }, + { 188, "HCI 12.7" }, + { 218, "HCI 12.8" }, + { 283, "HCI 12.9" }, + { 203, "HCI 13.2" }, + { 204, "HCI 13.2" }, + { 210, "HCI 13.3" }, + { 211, "HCI 13.3" }, + { 213, "HCI 13.4" }, + { 214, "HCI 13.4" }, + { 225, "HCI 13.5" }, + { 226, "HCI 13.5" }, + { 237, "HCI 13.6" }, + { 238, "HCI 13.6" }, + { 242, "HCI 14.0" }, + { 243, "HCI 14.0" }, + { 244, "HCI 14.0" }, + { 245, "HCI 14.0" }, + { 254, "HCI 13.7" }, + { 255, "HCI 13.7" }, + { 264, "HCI 14.1" }, + { 265, "HCI 14.1" }, + { 267, "HCI 14.2" }, + { 268, "HCI 14.2" }, + { 272, "HCI 14.3" }, + { 273, "HCI 14.3" }, + { 274, "HCI 13.8" }, + { 275, "HCI 13.8" }, + { 286, "HCI 13.9" }, + { 287, "HCI 13.9" }, + { 309, "HCI 13.10" }, + { 310, "HCI 13.10" }, + { 313, "HCI 14.4" }, + { 314, "HCI 14.4" }, + { 323, "HCI 14.5" }, + { 324, "HCI 14.5" }, + { 336, "HCI 14.6" }, + { 337, "HCI 14.6" }, + { 351, "HCI 13.11" }, + { 352, "HCI 13.11" }, + { 362, "HCI 15.0" }, + { 363, "HCI 15.0" }, + { 364, "HCI 15.0" }, + { 365, "HCI 15.0" }, + { 373, "HCI 14.7" }, + { 374, "HCI 14.7" }, + { 379, "HCI 15.1" }, + { 380, "HCI 15.1" }, + { 381, "HCI 15.1" }, + { 382, "HCI 15.1" }, + { 392, "HCI 15.2" }, + { 393, "HCI 15.2" }, + { 394, "HCI 15.2" }, + { 395, "HCI 15.2" }, + { 436, "HCI 16.0" }, + { 437, "HCI 16.0" }, + { 438, "HCI 16.0" }, + { 439, "HCI 16.0" }, + { 443, "HCI 15.3" }, + { 444, "HCI 15.3" }, + { 465, "HCI 16.1" }, + { 466, "HCI 16.1" }, + { 467, "HCI 16.1" }, + { 468, "HCI 16.1" }, + { 487, "HCI 14.8" }, + { 488, "HCI 14.8" }, + { 492, "HCI 16.2" }, + { 493, "HCI 16.2" }, + { 495, "HCI 16.2" }, + { 496, "HCI 16.2" }, + { 502, "HCI 16.1.1" }, + { 503, "HCI 16.1.1" }, + { 504, "HCI 16.1.1" }, + { 505, "HCI 16.1.1" }, + { 506, "HCI 16.1.2" }, + { 507, "HCI 16.1.2" }, + { 508, "HCI 16.1.2" }, + { 509, "HCI 16.1.2" }, + { 516, "HCI 16.3" }, + { 517, "HCI 16.3" }, + { 518, "HCI 16.3" }, + { 519, "HCI 16.3" }, + { 523, "HCI 16.4" }, + { 524, "HCI 16.4" }, + { 525, "HCI 16.4" }, + { 526, "HCI 16.4" }, + { 553, "HCI 15.3" }, + { 554, "HCI 15.3" }, + { 562, "HCI 16.5" }, + { 563, "HCI 16.5" }, + { 564, "HCI 16.5" }, + { 565, "HCI 16.5" }, + { 593, "HCI 17.0" }, + { 594, "HCI 17.0" }, + { 595, "HCI 17.0" }, + { 599, "HCI 17.0" }, + { 600, "HCI 17.0" }, + { 608, "HCI 13.10.1" }, + { 609, "HCI 13.10.1" }, + { 613, "HCI 17.1" }, + { 614, "HCI 17.1" }, + { 615, "HCI 17.1" }, + { 616, "HCI 17.1" }, + { 618, "HCI 17.1" }, + { 624, "HCI 17.2" }, + { 625, "HCI 17.2" }, + { 626, "HCI 17.2" }, + { 627, "HCI 17.2" }, + { 637, "HCI 16.6" }, + { 638, "HCI 16.6" }, + { 639, "HCI 16.6" }, + { 640, "HCI 16.6" }, + { 642, "HCI 13.10.2" }, + { 643, "HCI 13.10.2" }, + { 644, "HCI 13.10.3" }, + { 645, "HCI 13.10.3" }, + { 668, "HCI 13.10.4" }, + { 669, "HCI 13.10.4" }, + { 681, "HCI 16.7" }, + { 682, "HCI 16.7" }, + { 683, "HCI 16.7" }, + { 684, "HCI 16.7" }, + { 704, "HCI 16.8" }, + { 718, "HCI 16.4.1" }, + { 719, "HCI 16.4.1" }, + { 720, "HCI 16.4.1" }, + { 721, "HCI 16.4.1" }, + { 722, "HCI 16.7.1" }, + { 723, "HCI 16.7.1" }, + { 724, "HCI 16.7.1" }, + { 725, "HCI 16.7.1" }, + { 731, "HCI 16.7.2" }, + { 732, "HCI 16.7.2" }, + { 733, "HCI 16.7.2" }, + { 734, "HCI 16.7.2" }, + { 735, "HCI 16.4.2" }, + { 736, "HCI 16.4.2" }, + { 737, "HCI 16.4.2" }, + { 738, "HCI 16.4.2" }, + { 750, "HCI 16.7.3" }, + { 751, "HCI 16.7.3" }, + { 752, "HCI 16.7.3" }, + { 753, "HCI 16.7.3" }, + { 760, "HCI 16.7.4" }, + { 761, "HCI 16.7.4" }, + { 762, "HCI 16.7.4" }, + { 763, "HCI 16.7.4" }, + { 770, "HCI 16.9" }, + { 771, "HCI 16.9" }, + { 772, "HCI 16.9" }, + { 773, "HCI 16.9" }, + { 774, "HCI 17.3" }, + { 775, "HCI 17.3" }, + { 776, "HCI 17.3" }, + { 777, "HCI 17.3" }, + { 781, "HCI 16.7.5" }, + { 786, "HCI 16.10" }, + { 787, "HCI 16.10" }, + { 788, "HCI 16.10" }, + { 789, "HCI 16.10" }, + { 791, "HCI 16.4.3" }, + { 792, "HCI 16.4.3" }, + { 793, "HCI 16.4.3" }, + { 794, "HCI 16.4.3" }, + { 798, "HCI 16.11" }, + { 799, "HCI 16.11" }, + { 800, "HCI 16.11" }, + { 801, "HCI 16.11" }, + { 806, "HCI 16.7.5" }, + { 807, "HCI 16.12" }, + { 808, "HCI 16.12" }, + { 809, "HCI 16.12" }, + { 810, "HCI 16.12" }, + { 817, "HCI 16.13" }, + { 818, "HCI 16.13" }, + { 819, "HCI 16.13" }, + { 820, "HCI 16.13" }, + { 823, "HCI 13.10.5" }, + { 824, "HCI 13.10.5" }, + { 826, "HCI 16.14" }, + { 827, "HCI 16.14" }, + { 828, "HCI 16.14" }, + { 829, "HCI 16.14" }, + { 843, "HCI 17.3.1" }, + { 856, "HCI 17.3.2" }, + { 857, "HCI 17.3.2" }, + { 858, "HCI 17.3.2" }, + { 1120, "HCI 17.11" }, + { 1168, "HCI 18.1" }, + { 1169, "HCI 18.1" }, + { 1241, "HCI 18.x" }, + { 1298, "HCI 18.2" }, + { 1354, "HCI 18.2" }, + { 1392, "HCI 18.2" }, + { 1393, "HCI 18.2" }, + { 1501, "HCI 18.2" }, + { 1503, "HCI 18.2" }, + { 1504, "HCI 18.2" }, + { 1505, "HCI 18.2" }, + { 1506, "HCI 18.2" }, + { 1520, "HCI 18.2" }, + { 1586, "HCI 18.2" }, + { 1591, "HCI 18.2" }, + { 1592, "HCI 18.2" }, + { 1593, "HCI 18.2.1" }, + { 1733, "HCI 18.3" }, + { 1734, "HCI 18.3" }, + { 1735, "HCI 18.3" }, + { 1737, "HCI 18.3" }, + { 1915, "HCI 19.2" }, + { 1916, "HCI 19.2" }, + { 1958, "HCI 19.2" }, + { 1981, "Unified 20a" }, + { 1982, "Unified 20a" }, + { 1989, "HCI 18.4" }, + { 2062, "Unified 20a1" }, + { 2063, "Unified 20a1" }, + { 2067, "Unified 18f" }, + { 2068, "Unified 18f" }, + { 2243, "Unified 18e" }, + { 2244, "Unified 18e" }, + { 2258, "Unified 20d" }, + { 2259, "Unified 20d" }, + { 2361, "Unified 20e" }, + { 2362, "Unified 20e" }, + { 2386, "Unified 21a" }, + { 2387, "Unified 21a" }, + { 2423, "Unified 21a" }, + { 2424, "Unified 21a" }, + { 2623, "Unified 21c" }, + { 2624, "Unified 21c" }, + { 2625, "Unified 21c" }, + { 2626, "Unified 21c" }, + { 2627, "Unified 21c" }, + { 2628, "Unified 21c" }, + { 2629, "Unified 21c" }, + { 2630, "Unified 21c" }, + { 2631, "Unified 21c" }, + { 2632, "Unified 21c" }, + { 2633, "Unified 21c" }, + { 2634, "Unified 21c" }, + { 2635, "Unified 21c" }, + { 2636, "Unified 21c" }, + { 2649, "Unified 21c" }, + { 2650, "Unified 21c" }, + { 2651, "Unified 21c" }, + { 2652, "Unified 21c" }, + { 2653, "Unified 21c" }, + { 2654, "Unified 21c" }, + { 2655, "Unified 21c" }, + { 2656, "Unified 21c" }, + { 2658, "Unified 21c" }, + { 3057, "Unified 21d" }, + { 3058, "Unified 21d" }, + { 3059, "Unified 21d" }, + { 3060, "Unified 21d" }, + { 3062, "Unified 21d" }, + { 3063, "Unified 21d" }, + { 3064, "Unified 21d" }, + { 3164, "Unified 21e" }, + { 3413, "Unified 21f" }, + { 3414, "Unified 21f" }, + { 3415, "Unified 21f" }, + { 3424, "Unified 21f" }, + { 3454, "Unified 21f" }, + { 3684, "Unified 21f" }, + { 3764, "Unified 21f" }, + { 4276, "Unified 22b" }, + { 4277, "Unified 22b" }, + { 4279, "Unified 22b" }, + { 4281, "Unified 22b" }, + { 4282, "Unified 22b" }, + { 4283, "Unified 22b" }, + { 4284, "Unified 22b" }, + { 4285, "Unified 22b" }, + { 4289, "Unified 22b" }, + { 4290, "Unified 22b" }, + { 4291, "Unified 22b" }, + { 4292, "Unified 22b" }, + { 4293, "Unified 22b" }, + { 4294, "Unified 22b" }, + { 4295, "Unified 22b" }, + { 4363, "Unified 22c" }, + { 4373, "Unified 22c" }, + { 4374, "Unified 22c" }, + { 4532, "Unified 22d" }, + { 4533, "Unified 22d" }, + { 4698, "Unified 23c" }, + { 4839, "Unified 23c" }, + { 4841, "Unified 23c" }, + { 4866, "Unified 23c" }, + { 4867, "Unified 23c" }, + { 4868, "Unified 23c" }, + { 4869, "Unified 23c" }, + { 4870, "Unified 23c" }, + { 4871, "Unified 23c" }, + { 4872, "Unified 23c" }, + { 4874, "Unified 23c" }, + { 4875, "Unified 23c" }, + { 4876, "Unified 23c" }, + { 4877, "Unified 23c" }, + { 2526, "Marcel 1 (2005-09-26)" }, + { 2543, "Marcel 2 (2005-09-28)" }, + { 2622, "Marcel 3 (2005-10-27)" }, + { 3326, "Marcel 4 (2006-06-16)" }, + { 3612, "Marcel 5 (2006-10-24)" }, + { 4509, "Marcel 6 (2007-06-11)" }, + { 5417, "Marcel 7 (2008-08-26)" }, + { 195, "Sniff 1 (2001-11-27)" }, + { 220, "Sniff 2 (2002-01-03)" }, + { 269, "Sniff 3 (2002-02-22)" }, + { 270, "Sniff 4 (2002-02-26)" }, + { 284, "Sniff 5 (2002-03-12)" }, + { 292, "Sniff 6 (2002-03-20)" }, + { 305, "Sniff 7 (2002-04-12)" }, + { 306, "Sniff 8 (2002-04-12)" }, + { 343, "Sniff 9 (2002-05-02)" }, + { 346, "Sniff 10 (2002-05-03)" }, + { 355, "Sniff 11 (2002-05-16)" }, + { 256, "Sniff 11 (2002-05-16)" }, + { 390, "Sniff 12 (2002-06-26)" }, + { 450, "Sniff 13 (2002-08-16)" }, + { 451, "Sniff 13 (2002-08-16)" }, + { 533, "Sniff 14 (2002-10-11)" }, + { 580, "Sniff 15 (2002-11-14)" }, + { 623, "Sniff 16 (2002-12-12)" }, + { 678, "Sniff 17 (2003-01-29)" }, + { 847, "Sniff 18 (2003-04-17)" }, + { 876, "Sniff 19 (2003-06-10)" }, + { 997, "Sniff 22 (2003-09-05)" }, + { 1027, "Sniff 23 (2003-10-03)" }, + { 1029, "Sniff 24 (2003-10-03)" }, + { 1112, "Sniff 25 (2003-12-03)" }, + { 1113, "Sniff 25 (2003-12-03)" }, + { 1133, "Sniff 26 (2003-12-18)" }, + { 1134, "Sniff 26 (2003-12-18)" }, + { 1223, "Sniff 27 (2004-03-08)" }, + { 1224, "Sniff 27 (2004-03-08)" }, + { 1319, "Sniff 31 (2004-04-22)" }, + { 1320, "Sniff 31 (2004-04-22)" }, + { 1427, "Sniff 34 (2004-06-16)" }, + { 1508, "Sniff 35 (2004-07-19)" }, + { 1509, "Sniff 35 (2004-07-19)" }, + { 1587, "Sniff 36 (2004-08-18)" }, + { 1588, "Sniff 36 (2004-08-18)" }, + { 1641, "Sniff 37 (2004-09-16)" }, + { 1642, "Sniff 37 (2004-09-16)" }, + { 1699, "Sniff 38 (2004-10-07)" }, + { 1700, "Sniff 38 (2004-10-07)" }, + { 1752, "Sniff 39 (2004-11-02)" }, + { 1753, "Sniff 39 (2004-11-02)" }, + { 1759, "Sniff 40 (2004-11-03)" }, + { 1760, "Sniff 40 (2004-11-03)" }, + { 1761, "Sniff 40 (2004-11-03)" }, + { 2009, "Sniff 41 (2005-04-06)" }, + { 2010, "Sniff 41 (2005-04-06)" }, + { 2011, "Sniff 41 (2005-04-06)" }, + { 2016, "Sniff 42 (2005-04-11)" }, + { 2017, "Sniff 42 (2005-04-11)" }, + { 2018, "Sniff 42 (2005-04-11)" }, + { 2023, "Sniff 43 (2005-04-14)" }, + { 2024, "Sniff 43 (2005-04-14)" }, + { 2025, "Sniff 43 (2005-04-14)" }, + { 2032, "Sniff 44 (2005-04-18)" }, + { 2033, "Sniff 44 (2005-04-18)" }, + { 2034, "Sniff 44 (2005-04-18)" }, + { 2288, "Sniff 45 (2005-07-08)" }, + { 2289, "Sniff 45 (2005-07-08)" }, + { 2290, "Sniff 45 (2005-07-08)" }, + { 2388, "Sniff 46 (2005-08-17)" }, + { 2389, "Sniff 46 (2005-08-17)" }, + { 2390, "Sniff 46 (2005-08-17)" }, + { 2869, "Sniff 47 (2006-02-15)" }, + { 2870, "Sniff 47 (2006-02-15)" }, + { 2871, "Sniff 47 (2006-02-15)" }, + { 3214, "Sniff 48 (2006-05-16)" }, + { 3215, "Sniff 48 (2006-05-16)" }, + { 3216, "Sniff 48 (2006-05-16)" }, + { 3356, "Sniff 49 (2006-07-17)" }, + { 3529, "Sniff 50 (2006-09-21)" }, + { 3546, "Sniff 51 (2006-09-29)" }, + { 3683, "Sniff 52 (2006-11-03)" }, + { 0, } +}; + +char *csr_builddeftostr(uint16_t def) +{ + switch (def) { + case 0x0000: + return "NONE"; + case 0x0001: + return "CHIP_BASE_BC01"; + case 0x0002: + return "CHIP_BASE_BC02"; + case 0x0003: + return "CHIP_BC01B"; + case 0x0004: + return "CHIP_BC02_EXTERNAL"; + case 0x0005: + return "BUILD_HCI"; + case 0x0006: + return "BUILD_RFCOMM"; + case 0x0007: + return "BT_VER_1_1"; + case 0x0008: + return "TRANSPORT_ALL"; + case 0x0009: + return "TRANSPORT_BCSP"; + case 0x000a: + return "TRANSPORT_H4"; + case 0x000b: + return "TRANSPORT_USB"; + case 0x000c: + return "MAX_CRYPT_KEY_LEN_56"; + case 0x000d: + return "MAX_CRYPT_KEY_LEN_128"; + case 0x000e: + return "TRANSPORT_USER"; + case 0x000f: + return "CHIP_BC02_KATO"; + case 0x0010: + return "TRANSPORT_NONE"; + case 0x0012: + return "REQUIRE_8MBIT"; + case 0x0013: + return "RADIOTEST"; + case 0x0014: + return "RADIOTEST_LITE"; + case 0x0015: + return "INSTALL_FLASH"; + case 0x0016: + return "INSTALL_EEPROM"; + case 0x0017: + return "INSTALL_COMBO_DOT11"; + case 0x0018: + return "LOWPOWER_TX"; + case 0x0019: + return "TRANSPORT_TWUTL"; + case 0x001a: + return "COMPILER_GCC"; + case 0x001b: + return "CHIP_BC02_CLOUSEAU"; + case 0x001c: + return "CHIP_BC02_TOULOUSE"; + case 0x001d: + return "CHIP_BASE_BC3"; + case 0x001e: + return "CHIP_BC3_NICKNACK"; + case 0x001f: + return "CHIP_BC3_KALIMBA"; + case 0x0020: + return "INSTALL_HCI_MODULE"; + case 0x0021: + return "INSTALL_L2CAP_MODULE"; + case 0x0022: + return "INSTALL_DM_MODULE"; + case 0x0023: + return "INSTALL_SDP_MODULE"; + case 0x0024: + return "INSTALL_RFCOMM_MODULE"; + case 0x0025: + return "INSTALL_HIDIO_MODULE"; + case 0x0026: + return "INSTALL_PAN_MODULE"; + case 0x0027: + return "INSTALL_IPV4_MODULE"; + case 0x0028: + return "INSTALL_IPV6_MODULE"; + case 0x0029: + return "INSTALL_TCP_MODULE"; + case 0x002a: + return "BT_VER_1_2"; + case 0x002b: + return "INSTALL_UDP_MODULE"; + case 0x002c: + return "REQUIRE_0_WAIT_STATES"; + case 0x002d: + return "CHIP_BC3_PADDYWACK"; + case 0x002e: + return "CHIP_BC4_COYOTE"; + case 0x002f: + return "CHIP_BC4_ODDJOB"; + case 0x0030: + return "TRANSPORT_H4DS"; + case 0x0031: + return "CHIP_BASE_BC4"; + default: + return "UNKNOWN"; + } +} + +char *csr_buildidtostr(uint16_t id) +{ + static char str[12]; + int i; + + for (i = 0; csr_map[i].id; i++) + if (csr_map[i].id == id) + return csr_map[i].str; + + snprintf(str, 11, "Build %d", id); + return str; +} + +char *csr_chipvertostr(uint16_t ver, uint16_t rev) +{ + switch (ver) { + case 0x00: + return "BlueCore01a"; + case 0x01: + switch (rev) { + case 0x64: + return "BlueCore01b (ES)"; + case 0x65: + default: + return "BlueCore01b"; + } + case 0x02: + switch (rev) { + case 0x89: + return "BlueCore02-External (ES2)"; + case 0x8a: + return "BlueCore02-External"; + case 0x28: + return "BlueCore02-ROM/Audio/Flash"; + default: + return "BlueCore02"; + } + case 0x03: + switch (rev) { + case 0x43: + return "BlueCore3-MM"; + case 0x15: + return "BlueCore3-ROM"; + case 0xe2: + return "BlueCore3-Flash"; + case 0x26: + return "BlueCore4-External"; + case 0x30: + return "BlueCore4-ROM"; + default: + return "BlueCore3 or BlueCore4"; + } + default: + return "Unknown"; + } +} + +char *csr_pskeytostr(uint16_t pskey) +{ + switch (pskey) { + case CSR_PSKEY_BDADDR: + return "Bluetooth address"; + case CSR_PSKEY_COUNTRYCODE: + return "Country code"; + case CSR_PSKEY_CLASSOFDEVICE: + return "Class of device"; + case CSR_PSKEY_DEVICE_DRIFT: + return "Device drift"; + case CSR_PSKEY_DEVICE_JITTER: + return "Device jitter"; + case CSR_PSKEY_MAX_ACLS: + return "Maximum ACL links"; + case CSR_PSKEY_MAX_SCOS: + return "Maximum SCO links"; + case CSR_PSKEY_MAX_REMOTE_MASTERS: + return "Maximum remote masters"; + case CSR_PSKEY_ENABLE_MASTERY_WITH_SLAVERY: + return "Support master and slave roles simultaneously"; + case CSR_PSKEY_H_HC_FC_MAX_ACL_PKT_LEN: + return "Maximum HCI ACL packet length"; + case CSR_PSKEY_H_HC_FC_MAX_SCO_PKT_LEN: + return "Maximum HCI SCO packet length"; + case CSR_PSKEY_H_HC_FC_MAX_ACL_PKTS: + return "Maximum number of HCI ACL packets"; + case CSR_PSKEY_H_HC_FC_MAX_SCO_PKTS: + return "Maximum number of HCI SCO packets"; + case CSR_PSKEY_LC_FC_BUFFER_LOW_WATER_MARK: + return "Flow control low water mark"; + case CSR_PSKEY_LC_MAX_TX_POWER: + return "Maximum transmit power"; + case CSR_PSKEY_TX_GAIN_RAMP: + return "Transmit gain ramp rate"; + case CSR_PSKEY_LC_POWER_TABLE: + return "Radio power table"; + case CSR_PSKEY_LC_PEER_POWER_PERIOD: + return "Peer transmit power control interval"; + case CSR_PSKEY_LC_FC_POOLS_LOW_WATER_MARK: + return "Flow control pool low water mark"; + case CSR_PSKEY_LC_DEFAULT_TX_POWER: + return "Default transmit power"; + case CSR_PSKEY_LC_RSSI_GOLDEN_RANGE: + return "RSSI at bottom of golden receive range"; + case CSR_PSKEY_LC_COMBO_DISABLE_PIO_MASK: + return "Combo: PIO lines and logic to disable transmit"; + case CSR_PSKEY_LC_COMBO_PRIORITY_PIO_MASK: + return "Combo: priority activity PIO lines and logic"; + case CSR_PSKEY_LC_COMBO_DOT11_CHANNEL_PIO_BASE: + return "Combo: 802.11b channel number base PIO line"; + case CSR_PSKEY_LC_COMBO_DOT11_BLOCK_CHANNELS: + return "Combo: channels to block either side of 802.11b"; + case CSR_PSKEY_LC_MAX_TX_POWER_NO_RSSI: + return "Maximum transmit power when peer has no RSSI"; + case CSR_PSKEY_LC_CONNECTION_RX_WINDOW: + return "Receive window size during connections"; + case CSR_PSKEY_LC_COMBO_DOT11_TX_PROTECTION_MODE: + return "Combo: which TX packets shall we protect"; + case CSR_PSKEY_LC_ENHANCED_POWER_TABLE: + return "Radio power table"; + case CSR_PSKEY_LC_WIDEBAND_RSSI_CONFIG: + return "RSSI configuration for use with wideband RSSI"; + case CSR_PSKEY_LC_COMBO_DOT11_PRIORITY_LEAD: + return "Combo: How much notice will we give the Combo Card"; + case CSR_PSKEY_BT_CLOCK_INIT: + return "Initial value of Bluetooth clock"; + case CSR_PSKEY_TX_MR_MOD_DELAY: + return "TX Mod delay"; + case CSR_PSKEY_RX_MR_SYNC_TIMING: + return "RX MR Sync Timing"; + case CSR_PSKEY_RX_MR_SYNC_CONFIG: + return "RX MR Sync Configuration"; + case CSR_PSKEY_LC_LOST_SYNC_SLOTS: + return "Time in ms for lost sync in low power modes"; + case CSR_PSKEY_RX_MR_SAMP_CONFIG: + return "RX MR Sync Configuration"; + case CSR_PSKEY_AGC_HYST_LEVELS: + return "AGC hysteresis levels"; + case CSR_PSKEY_RX_LEVEL_LOW_SIGNAL: + return "ANA_RX_LVL at low signal strengths"; + case CSR_PSKEY_AGC_IQ_LVL_VALUES: + return "ANA_IQ_LVL values for AGC algorithmn"; + case CSR_PSKEY_MR_FTRIM_OFFSET_12DB: + return "ANA_RX_FTRIM offset when using 12 dB IF atten "; + case CSR_PSKEY_MR_FTRIM_OFFSET_6DB: + return "ANA_RX_FTRIM offset when using 6 dB IF atten "; + case CSR_PSKEY_NO_CAL_ON_BOOT: + return "Do not calibrate radio on boot"; + case CSR_PSKEY_RSSI_HI_TARGET: + return "RSSI high target"; + case CSR_PSKEY_PREFERRED_MIN_ATTENUATION: + return "Preferred minimum attenuator setting"; + case CSR_PSKEY_LC_COMBO_DOT11_PRIORITY_OVERRIDE: + return "Combo: Treat all packets as high priority"; + case CSR_PSKEY_LC_MULTISLOT_HOLDOFF: + return "Time till single slot packets are used for resync"; + case CSR_PSKEY_FREE_KEY_PIGEON_HOLE: + return "Link key store bitfield"; + case CSR_PSKEY_LINK_KEY_BD_ADDR0: + return "Bluetooth address + link key 0"; + case CSR_PSKEY_LINK_KEY_BD_ADDR1: + return "Bluetooth address + link key 1"; + case CSR_PSKEY_LINK_KEY_BD_ADDR2: + return "Bluetooth address + link key 2"; + case CSR_PSKEY_LINK_KEY_BD_ADDR3: + return "Bluetooth address + link key 3"; + case CSR_PSKEY_LINK_KEY_BD_ADDR4: + return "Bluetooth address + link key 4"; + case CSR_PSKEY_LINK_KEY_BD_ADDR5: + return "Bluetooth address + link key 5"; + case CSR_PSKEY_LINK_KEY_BD_ADDR6: + return "Bluetooth address + link key 6"; + case CSR_PSKEY_LINK_KEY_BD_ADDR7: + return "Bluetooth address + link key 7"; + case CSR_PSKEY_LINK_KEY_BD_ADDR8: + return "Bluetooth address + link key 8"; + case CSR_PSKEY_LINK_KEY_BD_ADDR9: + return "Bluetooth address + link key 9"; + case CSR_PSKEY_LINK_KEY_BD_ADDR10: + return "Bluetooth address + link key 10"; + case CSR_PSKEY_LINK_KEY_BD_ADDR11: + return "Bluetooth address + link key 11"; + case CSR_PSKEY_LINK_KEY_BD_ADDR12: + return "Bluetooth address + link key 12"; + case CSR_PSKEY_LINK_KEY_BD_ADDR13: + return "Bluetooth address + link key 13"; + case CSR_PSKEY_LINK_KEY_BD_ADDR14: + return "Bluetooth address + link key 14"; + case CSR_PSKEY_LINK_KEY_BD_ADDR15: + return "Bluetooth address + link key 15"; + case CSR_PSKEY_ENC_KEY_LMIN: + return "Minimum encryption key length"; + case CSR_PSKEY_ENC_KEY_LMAX: + return "Maximum encryption key length"; + case CSR_PSKEY_LOCAL_SUPPORTED_FEATURES: + return "Local supported features block"; + case CSR_PSKEY_LM_USE_UNIT_KEY: + return "Allow use of unit key for authentication?"; + case CSR_PSKEY_HCI_NOP_DISABLE: + return "Disable the HCI Command_Status event on boot"; + case CSR_PSKEY_LM_MAX_EVENT_FILTERS: + return "Maximum number of event filters"; + case CSR_PSKEY_LM_USE_ENC_MODE_BROADCAST: + return "Allow LM to use enc_mode=2"; + case CSR_PSKEY_LM_TEST_SEND_ACCEPTED_TWICE: + return "LM sends two LMP_accepted messages in test mode"; + case CSR_PSKEY_LM_MAX_PAGE_HOLD_TIME: + return "Maximum time we hold a device around page"; + case CSR_PSKEY_AFH_ADAPTATION_RESPONSE_TIME: + return "LM period for AFH adaption"; + case CSR_PSKEY_AFH_OPTIONS: + return "Options to configure AFH"; + case CSR_PSKEY_AFH_RSSI_RUN_PERIOD: + return "AFH RSSI reading period"; + case CSR_PSKEY_AFH_REENABLE_CHANNEL_TIME: + return "AFH good channel adding time"; + case CSR_PSKEY_NO_DROP_ON_ACR_MS_FAIL: + return "Complete link if acr barge-in role switch refused"; + case CSR_PSKEY_MAX_PRIVATE_KEYS: + return "Max private link keys stored"; + case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR0: + return "Bluetooth address + link key 0"; + case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR1: + return "Bluetooth address + link key 1"; + case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR2: + return "Bluetooth address + link key 2"; + case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR3: + return "Bluetooth address + link key 3"; + case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR4: + return "Bluetooth address + link key 4"; + case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR5: + return "Bluetooth address + link key 5"; + case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR6: + return "Bluetooth address + link key 6"; + case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR7: + return "Bluetooth address + link key 7"; + case CSR_PSKEY_LOCAL_SUPPORTED_COMMANDS: + return "Local supported commands"; + case CSR_PSKEY_LM_MAX_ABSENCE_INDEX: + return "Maximum absence index allowed"; + case CSR_PSKEY_DEVICE_NAME: + return "Local device's \"user friendly\" name"; + case CSR_PSKEY_AFH_RSSI_THRESHOLD: + return "AFH RSSI threshold"; + case CSR_PSKEY_LM_CASUAL_SCAN_INTERVAL: + return "Scan interval in slots for casual scanning"; + case CSR_PSKEY_AFH_MIN_MAP_CHANGE: + return "The minimum amount to change an AFH map by"; + case CSR_PSKEY_AFH_RSSI_LP_RUN_PERIOD: + return "AFH RSSI reading period when in low power mode"; + case CSR_PSKEY_HCI_LMP_LOCAL_VERSION: + return "The HCI and LMP version reported locally"; + case CSR_PSKEY_LMP_REMOTE_VERSION: + return "The LMP version reported remotely"; + case CSR_PSKEY_HOLD_ERROR_MESSAGE_NUMBER: + return "Maximum number of queued HCI Hardware Error Events"; + case CSR_PSKEY_DFU_ATTRIBUTES: + return "DFU attributes"; + case CSR_PSKEY_DFU_DETACH_TO: + return "DFU detach timeout"; + case CSR_PSKEY_DFU_TRANSFER_SIZE: + return "DFU transfer size"; + case CSR_PSKEY_DFU_ENABLE: + return "DFU enable"; + case CSR_PSKEY_DFU_LIN_REG_ENABLE: + return "Linear Regulator enabled at boot in DFU mode"; + case CSR_PSKEY_DFUENC_VMAPP_PK_MODULUS_MSB: + return "DFU encryption VM application public key MSB"; + case CSR_PSKEY_DFUENC_VMAPP_PK_MODULUS_LSB: + return "DFU encryption VM application public key LSB"; + case CSR_PSKEY_DFUENC_VMAPP_PK_M_DASH: + return "DFU encryption VM application M dash"; + case CSR_PSKEY_DFUENC_VMAPP_PK_R2N_MSB: + return "DFU encryption VM application public key R2N MSB"; + case CSR_PSKEY_DFUENC_VMAPP_PK_R2N_LSB: + return "DFU encryption VM application public key R2N LSB"; + case CSR_PSKEY_BCSP_LM_PS_BLOCK: + return "BCSP link establishment block"; + case CSR_PSKEY_HOSTIO_FC_PS_BLOCK: + return "HCI flow control block"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO0: + return "Host transport channel 0 settings (BCSP ACK)"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO1: + return "Host transport channel 1 settings (BCSP-LE)"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO2: + return "Host transport channel 2 settings (BCCMD)"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO3: + return "Host transport channel 3 settings (HQ)"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO4: + return "Host transport channel 4 settings (DM)"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO5: + return "Host transport channel 5 settings (HCI CMD/EVT)"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO6: + return "Host transport channel 6 settings (HCI ACL)"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO7: + return "Host transport channel 7 settings (HCI SCO)"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO8: + return "Host transport channel 8 settings (L2CAP)"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO9: + return "Host transport channel 9 settings (RFCOMM)"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO10: + return "Host transport channel 10 settings (SDP)"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO11: + return "Host transport channel 11 settings (TEST)"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO12: + return "Host transport channel 12 settings (DFU)"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO13: + return "Host transport channel 13 settings (VM)"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO14: + return "Host transport channel 14 settings"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO15: + return "Host transport channel 15 settings"; + case CSR_PSKEY_HOSTIO_UART_RESET_TIMEOUT: + return "UART reset counter timeout"; + case CSR_PSKEY_HOSTIO_USE_HCI_EXTN: + return "Use hci_extn to route non-hci channels"; + case CSR_PSKEY_HOSTIO_USE_HCI_EXTN_CCFC: + return "Use command-complete flow control for hci_extn"; + case CSR_PSKEY_HOSTIO_HCI_EXTN_PAYLOAD_SIZE: + return "Maximum hci_extn payload size"; + case CSR_PSKEY_BCSP_LM_CNF_CNT_LIMIT: + return "BCSP link establishment conf message count"; + case CSR_PSKEY_HOSTIO_MAP_SCO_PCM: + return "Map SCO over PCM"; + case CSR_PSKEY_HOSTIO_AWKWARD_PCM_SYNC: + return "PCM interface synchronisation is difficult"; + case CSR_PSKEY_HOSTIO_BREAK_POLL_PERIOD: + return "Break poll period (microseconds)"; + case CSR_PSKEY_HOSTIO_MIN_UART_HCI_SCO_SIZE: + return "Minimum SCO packet size sent to host over UART HCI"; + case CSR_PSKEY_HOSTIO_MAP_SCO_CODEC: + return "Map SCO over the built-in codec"; + case CSR_PSKEY_PCM_CVSD_TX_HI_FREQ_BOOST: + return "High frequency boost for PCM when transmitting CVSD"; + case CSR_PSKEY_PCM_CVSD_RX_HI_FREQ_BOOST: + return "High frequency boost for PCM when receiving CVSD"; + case CSR_PSKEY_PCM_CONFIG32: + return "PCM interface settings bitfields"; + case CSR_PSKEY_USE_OLD_BCSP_LE: + return "Use the old version of BCSP link establishment"; + case CSR_PSKEY_PCM_CVSD_USE_NEW_FILTER: + return "CVSD uses the new filter if available"; + case CSR_PSKEY_PCM_FORMAT: + return "PCM data format"; + case CSR_PSKEY_CODEC_OUT_GAIN: + return "Audio output gain when using built-in codec"; + case CSR_PSKEY_CODEC_IN_GAIN: + return "Audio input gain when using built-in codec"; + case CSR_PSKEY_CODEC_PIO: + return "PIO to enable when built-in codec is enabled"; + case CSR_PSKEY_PCM_LOW_JITTER_CONFIG: + return "PCM interface settings for low jitter master mode"; + case CSR_PSKEY_HOSTIO_SCO_PCM_THRESHOLDS: + return "Thresholds for SCO PCM buffers"; + case CSR_PSKEY_HOSTIO_SCO_HCI_THRESHOLDS: + return "Thresholds for SCO HCI buffers"; + case CSR_PSKEY_HOSTIO_MAP_SCO_PCM_SLOT: + return "Route SCO data to specified slot in pcm frame"; + case CSR_PSKEY_UART_BAUDRATE: + return "UART Baud rate"; + case CSR_PSKEY_UART_CONFIG_BCSP: + return "UART configuration when using BCSP"; + case CSR_PSKEY_UART_CONFIG_H4: + return "UART configuration when using H4"; + case CSR_PSKEY_UART_CONFIG_H5: + return "UART configuration when using H5"; + case CSR_PSKEY_UART_CONFIG_USR: + return "UART configuration when under VM control"; + case CSR_PSKEY_UART_TX_CRCS: + return "Use CRCs for BCSP or H5"; + case CSR_PSKEY_UART_ACK_TIMEOUT: + return "Acknowledgement timeout for BCSP and H5"; + case CSR_PSKEY_UART_TX_MAX_ATTEMPTS: + return "Max times to send reliable BCSP or H5 message"; + case CSR_PSKEY_UART_TX_WINDOW_SIZE: + return "Transmit window size for BCSP and H5"; + case CSR_PSKEY_UART_HOST_WAKE: + return "UART host wakeup"; + case CSR_PSKEY_HOSTIO_THROTTLE_TIMEOUT: + return "Host interface performance control."; + case CSR_PSKEY_PCM_ALWAYS_ENABLE: + return "PCM port is always enable when chip is running"; + case CSR_PSKEY_UART_HOST_WAKE_SIGNAL: + return "Signal to use for uart host wakeup protocol"; + case CSR_PSKEY_UART_CONFIG_H4DS: + return "UART configuration when using H4DS"; + case CSR_PSKEY_H4DS_WAKE_DURATION: + return "How long to spend waking the host when using H4DS"; + case CSR_PSKEY_H4DS_MAXWU: + return "Maximum number of H4DS Wake-Up messages to send"; + case CSR_PSKEY_H4DS_LE_TIMER_PERIOD: + return "H4DS Link Establishment Tsync and Tconf period"; + case CSR_PSKEY_H4DS_TWU_TIMER_PERIOD: + return "H4DS Twu timer period"; + case CSR_PSKEY_H4DS_UART_IDLE_TIMER_PERIOD: + return "H4DS Tuart_idle timer period"; + case CSR_PSKEY_ANA_FTRIM: + return "Crystal frequency trim"; + case CSR_PSKEY_WD_TIMEOUT: + return "Watchdog timeout (microseconds)"; + case CSR_PSKEY_WD_PERIOD: + return "Watchdog period (microseconds)"; + case CSR_PSKEY_HOST_INTERFACE: + return "Host interface"; + case CSR_PSKEY_HQ_HOST_TIMEOUT: + return "HQ host command timeout"; + case CSR_PSKEY_HQ_ACTIVE: + return "Enable host query task?"; + case CSR_PSKEY_BCCMD_SECURITY_ACTIVE: + return "Enable configuration security"; + case CSR_PSKEY_ANA_FREQ: + return "Crystal frequency"; + case CSR_PSKEY_PIO_PROTECT_MASK: + return "Access to PIO pins"; + case CSR_PSKEY_PMALLOC_SIZES: + return "pmalloc sizes array"; + case CSR_PSKEY_UART_BAUD_RATE: + return "UART Baud rate (pre 18)"; + case CSR_PSKEY_UART_CONFIG: + return "UART configuration bitfield"; + case CSR_PSKEY_STUB: + return "Stub"; + case CSR_PSKEY_TXRX_PIO_CONTROL: + return "TX and RX PIO control"; + case CSR_PSKEY_ANA_RX_LEVEL: + return "ANA_RX_LVL register initial value"; + case CSR_PSKEY_ANA_RX_FTRIM: + return "ANA_RX_FTRIM register initial value"; + case CSR_PSKEY_PSBC_DATA_VERSION: + return "Persistent store version"; + case CSR_PSKEY_PCM0_ATTENUATION: + return "Volume control on PCM channel 0"; + case CSR_PSKEY_LO_LVL_MAX: + return "Maximum value of LO level control register"; + case CSR_PSKEY_LO_ADC_AMPL_MIN: + return "Minimum value of the LO amplitude measured on the ADC"; + case CSR_PSKEY_LO_ADC_AMPL_MAX: + return "Maximum value of the LO amplitude measured on the ADC"; + case CSR_PSKEY_IQ_TRIM_CHANNEL: + return "IQ calibration channel"; + case CSR_PSKEY_IQ_TRIM_GAIN: + return "IQ calibration gain"; + case CSR_PSKEY_IQ_TRIM_ENABLE: + return "IQ calibration enable"; + case CSR_PSKEY_TX_OFFSET_HALF_MHZ: + return "Transmit offset"; + case CSR_PSKEY_GBL_MISC_ENABLES: + return "Global miscellaneous hardware enables"; + case CSR_PSKEY_UART_SLEEP_TIMEOUT: + return "Time in ms to deep sleep if nothing received"; + case CSR_PSKEY_DEEP_SLEEP_STATE: + return "Deep sleep state usage"; + case CSR_PSKEY_IQ_ENABLE_PHASE_TRIM: + return "IQ phase enable"; + case CSR_PSKEY_HCI_HANDLE_FREEZE_PERIOD: + return "Time for which HCI handle is frozen after link removal"; + case CSR_PSKEY_MAX_FROZEN_HCI_HANDLES: + return "Maximum number of frozen HCI handles"; + case CSR_PSKEY_PAGETABLE_DESTRUCTION_DELAY: + return "Delay from freezing buf handle to deleting page table"; + case CSR_PSKEY_IQ_TRIM_PIO_SETTINGS: + return "IQ PIO settings"; + case CSR_PSKEY_USE_EXTERNAL_CLOCK: + return "Device uses an external clock"; + case CSR_PSKEY_DEEP_SLEEP_WAKE_CTS: + return "Exit deep sleep on CTS line activity"; + case CSR_PSKEY_FC_HC2H_FLUSH_DELAY: + return "Delay from disconnect to flushing HC->H FC tokens"; + case CSR_PSKEY_RX_HIGHSIDE: + return "Disable the HIGHSIDE bit in ANA_CONFIG"; + case CSR_PSKEY_TX_PRE_LVL: + return "TX pre-amplifier level"; + case CSR_PSKEY_RX_SINGLE_ENDED: + return "RX single ended"; + case CSR_PSKEY_TX_FILTER_CONFIG: + return "TX filter configuration"; + case CSR_PSKEY_CLOCK_REQUEST_ENABLE: + return "External clock request enable"; + case CSR_PSKEY_RX_MIN_ATTEN: + return "Minimum attenuation allowed for receiver"; + case CSR_PSKEY_XTAL_TARGET_AMPLITUDE: + return "Crystal target amplitude"; + case CSR_PSKEY_PCM_MIN_CPU_CLOCK: + return "Minimum CPU clock speed with PCM port running"; + case CSR_PSKEY_HOST_INTERFACE_PIO_USB: + return "USB host interface selection PIO line"; + case CSR_PSKEY_CPU_IDLE_MODE: + return "CPU idle mode when radio is active"; + case CSR_PSKEY_DEEP_SLEEP_CLEAR_RTS: + return "Deep sleep clears the UART RTS line"; + case CSR_PSKEY_RF_RESONANCE_TRIM: + return "Frequency trim for IQ and LNA resonant circuits"; + case CSR_PSKEY_DEEP_SLEEP_PIO_WAKE: + return "PIO line to wake the chip from deep sleep"; + case CSR_PSKEY_DRAIN_BORE_TIMERS: + return "Energy consumption measurement settings"; + case CSR_PSKEY_DRAIN_TX_POWER_BASE: + return "Energy consumption measurement settings"; + case CSR_PSKEY_MODULE_ID: + return "Module serial number"; + case CSR_PSKEY_MODULE_DESIGN: + return "Module design ID"; + case CSR_PSKEY_MODULE_SECURITY_CODE: + return "Module security code"; + case CSR_PSKEY_VM_DISABLE: + return "VM disable"; + case CSR_PSKEY_MOD_MANUF0: + return "Module manufactuer data 0"; + case CSR_PSKEY_MOD_MANUF1: + return "Module manufactuer data 1"; + case CSR_PSKEY_MOD_MANUF2: + return "Module manufactuer data 2"; + case CSR_PSKEY_MOD_MANUF3: + return "Module manufactuer data 3"; + case CSR_PSKEY_MOD_MANUF4: + return "Module manufactuer data 4"; + case CSR_PSKEY_MOD_MANUF5: + return "Module manufactuer data 5"; + case CSR_PSKEY_MOD_MANUF6: + return "Module manufactuer data 6"; + case CSR_PSKEY_MOD_MANUF7: + return "Module manufactuer data 7"; + case CSR_PSKEY_MOD_MANUF8: + return "Module manufactuer data 8"; + case CSR_PSKEY_MOD_MANUF9: + return "Module manufactuer data 9"; + case CSR_PSKEY_DUT_VM_DISABLE: + return "VM disable when entering radiotest modes"; + case CSR_PSKEY_USR0: + return "User configuration data 0"; + case CSR_PSKEY_USR1: + return "User configuration data 1"; + case CSR_PSKEY_USR2: + return "User configuration data 2"; + case CSR_PSKEY_USR3: + return "User configuration data 3"; + case CSR_PSKEY_USR4: + return "User configuration data 4"; + case CSR_PSKEY_USR5: + return "User configuration data 5"; + case CSR_PSKEY_USR6: + return "User configuration data 6"; + case CSR_PSKEY_USR7: + return "User configuration data 7"; + case CSR_PSKEY_USR8: + return "User configuration data 8"; + case CSR_PSKEY_USR9: + return "User configuration data 9"; + case CSR_PSKEY_USR10: + return "User configuration data 10"; + case CSR_PSKEY_USR11: + return "User configuration data 11"; + case CSR_PSKEY_USR12: + return "User configuration data 12"; + case CSR_PSKEY_USR13: + return "User configuration data 13"; + case CSR_PSKEY_USR14: + return "User configuration data 14"; + case CSR_PSKEY_USR15: + return "User configuration data 15"; + case CSR_PSKEY_USR16: + return "User configuration data 16"; + case CSR_PSKEY_USR17: + return "User configuration data 17"; + case CSR_PSKEY_USR18: + return "User configuration data 18"; + case CSR_PSKEY_USR19: + return "User configuration data 19"; + case CSR_PSKEY_USR20: + return "User configuration data 20"; + case CSR_PSKEY_USR21: + return "User configuration data 21"; + case CSR_PSKEY_USR22: + return "User configuration data 22"; + case CSR_PSKEY_USR23: + return "User configuration data 23"; + case CSR_PSKEY_USR24: + return "User configuration data 24"; + case CSR_PSKEY_USR25: + return "User configuration data 25"; + case CSR_PSKEY_USR26: + return "User configuration data 26"; + case CSR_PSKEY_USR27: + return "User configuration data 27"; + case CSR_PSKEY_USR28: + return "User configuration data 28"; + case CSR_PSKEY_USR29: + return "User configuration data 29"; + case CSR_PSKEY_USR30: + return "User configuration data 30"; + case CSR_PSKEY_USR31: + return "User configuration data 31"; + case CSR_PSKEY_USR32: + return "User configuration data 32"; + case CSR_PSKEY_USR33: + return "User configuration data 33"; + case CSR_PSKEY_USR34: + return "User configuration data 34"; + case CSR_PSKEY_USR35: + return "User configuration data 35"; + case CSR_PSKEY_USR36: + return "User configuration data 36"; + case CSR_PSKEY_USR37: + return "User configuration data 37"; + case CSR_PSKEY_USR38: + return "User configuration data 38"; + case CSR_PSKEY_USR39: + return "User configuration data 39"; + case CSR_PSKEY_USR40: + return "User configuration data 40"; + case CSR_PSKEY_USR41: + return "User configuration data 41"; + case CSR_PSKEY_USR42: + return "User configuration data 42"; + case CSR_PSKEY_USR43: + return "User configuration data 43"; + case CSR_PSKEY_USR44: + return "User configuration data 44"; + case CSR_PSKEY_USR45: + return "User configuration data 45"; + case CSR_PSKEY_USR46: + return "User configuration data 46"; + case CSR_PSKEY_USR47: + return "User configuration data 47"; + case CSR_PSKEY_USR48: + return "User configuration data 48"; + case CSR_PSKEY_USR49: + return "User configuration data 49"; + case CSR_PSKEY_USB_VERSION: + return "USB specification version number"; + case CSR_PSKEY_USB_DEVICE_CLASS_CODES: + return "USB device class codes"; + case CSR_PSKEY_USB_VENDOR_ID: + return "USB vendor identifier"; + case CSR_PSKEY_USB_PRODUCT_ID: + return "USB product identifier"; + case CSR_PSKEY_USB_MANUF_STRING: + return "USB manufacturer string"; + case CSR_PSKEY_USB_PRODUCT_STRING: + return "USB product string"; + case CSR_PSKEY_USB_SERIAL_NUMBER_STRING: + return "USB serial number string"; + case CSR_PSKEY_USB_CONFIG_STRING: + return "USB configuration string"; + case CSR_PSKEY_USB_ATTRIBUTES: + return "USB attributes bitmap"; + case CSR_PSKEY_USB_MAX_POWER: + return "USB device maximum power consumption"; + case CSR_PSKEY_USB_BT_IF_CLASS_CODES: + return "USB Bluetooth interface class codes"; + case CSR_PSKEY_USB_LANGID: + return "USB language strings supported"; + case CSR_PSKEY_USB_DFU_CLASS_CODES: + return "USB DFU class codes block"; + case CSR_PSKEY_USB_DFU_PRODUCT_ID: + return "USB DFU product ID"; + case CSR_PSKEY_USB_PIO_DETACH: + return "USB detach/attach PIO line"; + case CSR_PSKEY_USB_PIO_WAKEUP: + return "USB wakeup PIO line"; + case CSR_PSKEY_USB_PIO_PULLUP: + return "USB D+ pullup PIO line"; + case CSR_PSKEY_USB_PIO_VBUS: + return "USB VBus detection PIO Line"; + case CSR_PSKEY_USB_PIO_WAKE_TIMEOUT: + return "Timeout for assertion of USB PIO wake signal"; + case CSR_PSKEY_USB_PIO_RESUME: + return "PIO signal used in place of bus resume"; + case CSR_PSKEY_USB_BT_SCO_IF_CLASS_CODES: + return "USB Bluetooth SCO interface class codes"; + case CSR_PSKEY_USB_SUSPEND_PIO_LEVEL: + return "USB PIO levels to set when suspended"; + case CSR_PSKEY_USB_SUSPEND_PIO_DIR: + return "USB PIO I/O directions to set when suspended"; + case CSR_PSKEY_USB_SUSPEND_PIO_MASK: + return "USB PIO lines to be set forcibly in suspend"; + case CSR_PSKEY_USB_ENDPOINT_0_MAX_PACKET_SIZE: + return "The maxmimum packet size for USB endpoint 0"; + case CSR_PSKEY_USB_CONFIG: + return "USB config params for new chips (>bc2)"; + case CSR_PSKEY_RADIOTEST_ATTEN_INIT: + return "Radio test initial attenuator"; + case CSR_PSKEY_RADIOTEST_FIRST_TRIM_TIME: + return "IQ first calibration period in test"; + case CSR_PSKEY_RADIOTEST_SUBSEQUENT_TRIM_TIME: + return "IQ subsequent calibration period in test"; + case CSR_PSKEY_RADIOTEST_LO_LVL_TRIM_ENABLE: + return "LO_LVL calibration enable"; + case CSR_PSKEY_RADIOTEST_DISABLE_MODULATION: + return "Disable modulation during radiotest transmissions"; + case CSR_PSKEY_RFCOMM_FCON_THRESHOLD: + return "RFCOMM aggregate flow control on threshold"; + case CSR_PSKEY_RFCOMM_FCOFF_THRESHOLD: + return "RFCOMM aggregate flow control off threshold"; + case CSR_PSKEY_IPV6_STATIC_ADDR: + return "Static IPv6 address"; + case CSR_PSKEY_IPV4_STATIC_ADDR: + return "Static IPv4 address"; + case CSR_PSKEY_IPV6_STATIC_PREFIX_LEN: + return "Static IPv6 prefix length"; + case CSR_PSKEY_IPV6_STATIC_ROUTER_ADDR: + return "Static IPv6 router address"; + case CSR_PSKEY_IPV4_STATIC_SUBNET_MASK: + return "Static IPv4 subnet mask"; + case CSR_PSKEY_IPV4_STATIC_ROUTER_ADDR: + return "Static IPv4 router address"; + case CSR_PSKEY_MDNS_NAME: + return "Multicast DNS name"; + case CSR_PSKEY_FIXED_PIN: + return "Fixed PIN"; + case CSR_PSKEY_MDNS_PORT: + return "Multicast DNS port"; + case CSR_PSKEY_MDNS_TTL: + return "Multicast DNS TTL"; + case CSR_PSKEY_MDNS_IPV4_ADDR: + return "Multicast DNS IPv4 address"; + case CSR_PSKEY_ARP_CACHE_TIMEOUT: + return "ARP cache timeout"; + case CSR_PSKEY_HFP_POWER_TABLE: + return "HFP power table"; + case CSR_PSKEY_DRAIN_BORE_TIMER_COUNTERS: + return "Energy consumption estimation timer counters"; + case CSR_PSKEY_DRAIN_BORE_COUNTERS: + return "Energy consumption estimation counters"; + case CSR_PSKEY_LOOP_FILTER_TRIM: + return "Trim value to optimise loop filter"; + case CSR_PSKEY_DRAIN_BORE_CURRENT_PEAK: + return "Energy consumption estimation current peak"; + case CSR_PSKEY_VM_E2_CACHE_LIMIT: + return "Maximum RAM for caching EEPROM VM application"; + case CSR_PSKEY_FORCE_16MHZ_REF_PIO: + return "PIO line to force 16 MHz reference to be assumed"; + case CSR_PSKEY_CDMA_LO_REF_LIMITS: + return "Local oscillator frequency reference limits for CDMA"; + case CSR_PSKEY_CDMA_LO_ERROR_LIMITS: + return "Local oscillator frequency error limits for CDMA"; + case CSR_PSKEY_CLOCK_STARTUP_DELAY: + return "Clock startup delay in milliseconds"; + case CSR_PSKEY_DEEP_SLEEP_CORRECTION_FACTOR: + return "Deep sleep clock correction factor"; + case CSR_PSKEY_TEMPERATURE_CALIBRATION: + return "Temperature in deg C for a given internal setting"; + case CSR_PSKEY_TEMPERATURE_VS_DELTA_INTERNAL_PA: + return "Temperature for given internal PA adjustment"; + case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_PRE_LVL: + return "Temperature for a given TX_PRE_LVL adjustment"; + case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB: + return "Temperature for a given TX_BB adjustment"; + case CSR_PSKEY_TEMPERATURE_VS_DELTA_ANA_FTRIM: + return "Temperature for given crystal trim adjustment"; + case CSR_PSKEY_TEST_DELTA_OFFSET: + return "Frequency offset applied to synthesiser in test mode"; + case CSR_PSKEY_RX_DYNAMIC_LVL_OFFSET: + return "Receiver dynamic level offset depending on channel"; + case CSR_PSKEY_TEST_FORCE_OFFSET: + return "Force use of exact value in PSKEY_TEST_DELTA_OFFSET"; + case CSR_PSKEY_RF_TRAP_BAD_DIVISION_RATIOS: + return "Trap bad division ratios in radio frequency tables"; + case CSR_PSKEY_RADIOTEST_CDMA_LO_REF_LIMITS: + return "LO frequency reference limits for CDMA in radiotest"; + case CSR_PSKEY_INITIAL_BOOTMODE: + return "Initial device bootmode"; + case CSR_PSKEY_ONCHIP_HCI_CLIENT: + return "HCI traffic routed internally"; + case CSR_PSKEY_RX_ATTEN_BACKOFF: + return "Receiver attenuation back-off"; + case CSR_PSKEY_RX_ATTEN_UPDATE_RATE: + return "Receiver attenuation update rate"; + case CSR_PSKEY_SYNTH_TXRX_THRESHOLDS: + return "Local oscillator tuning voltage limits for tx and rx"; + case CSR_PSKEY_MIN_WAIT_STATES: + return "Flash wait state indicator"; + case CSR_PSKEY_RSSI_CORRECTION: + return "RSSI correction factor."; + case CSR_PSKEY_SCHED_THROTTLE_TIMEOUT: + return "Scheduler performance control."; + case CSR_PSKEY_DEEP_SLEEP_USE_EXTERNAL_CLOCK: + return "Deep sleep uses external 32 kHz clock source"; + case CSR_PSKEY_TRIM_RADIO_FILTERS: + return "Trim rx and tx radio filters if true."; + case CSR_PSKEY_TRANSMIT_OFFSET: + return "Transmit offset in units of 62.5 kHz"; + case CSR_PSKEY_USB_VM_CONTROL: + return "VM application will supply USB descriptors"; + case CSR_PSKEY_MR_ANA_RX_FTRIM: + return "Medium rate value for the ANA_RX_FTRIM register"; + case CSR_PSKEY_I2C_CONFIG: + return "I2C configuration"; + case CSR_PSKEY_IQ_LVL_RX: + return "IQ demand level for reception"; + case CSR_PSKEY_MR_TX_FILTER_CONFIG: + return "TX filter configuration used for enhanced data rate"; + case CSR_PSKEY_MR_TX_CONFIG2: + return "TX filter configuration used for enhanced data rate"; + case CSR_PSKEY_USB_DONT_RESET_BOOTMODE_ON_HOST_RESET: + return "Don't reset bootmode if USB host resets"; + case CSR_PSKEY_LC_USE_THROTTLING: + return "Adjust packet selection on packet error rate"; + case CSR_PSKEY_CHARGER_TRIM: + return "Trim value for the current charger"; + case CSR_PSKEY_CLOCK_REQUEST_FEATURES: + return "Clock request is tristated if enabled"; + case CSR_PSKEY_TRANSMIT_OFFSET_CLASS1: + return "Transmit offset / 62.5 kHz for class 1 radios"; + case CSR_PSKEY_TX_AVOID_PA_CLASS1_PIO: + return "PIO line asserted in class1 operation to avoid PA"; + case CSR_PSKEY_MR_PIO_CONFIG: + return "PIO line asserted in class1 operation to avoid PA"; + case CSR_PSKEY_UART_CONFIG2: + return "The UART Sampling point"; + case CSR_PSKEY_CLASS1_IQ_LVL: + return "IQ demand level for class 1 power level"; + case CSR_PSKEY_CLASS1_TX_CONFIG2: + return "TX filter configuration used for class 1 tx power"; + case CSR_PSKEY_TEMPERATURE_VS_DELTA_INTERNAL_PA_CLASS1: + return "Temperature for given internal PA adjustment"; + case CSR_PSKEY_TEMPERATURE_VS_DELTA_EXTERNAL_PA_CLASS1: + return "Temperature for given internal PA adjustment"; + case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_PRE_LVL_MR: + return "Temperature adjustment for TX_PRE_LVL in EDR"; + case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB_MR_HEADER: + return "Temperature for a given TX_BB in EDR header"; + case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB_MR_PAYLOAD: + return "Temperature for a given TX_BB in EDR payload"; + case CSR_PSKEY_RX_MR_EQ_TAPS: + return "Adjust receiver configuration for EDR"; + case CSR_PSKEY_TX_PRE_LVL_CLASS1: + return "TX pre-amplifier level in class 1 operation"; + case CSR_PSKEY_ANALOGUE_ATTENUATOR: + return "TX analogue attenuator setting"; + case CSR_PSKEY_MR_RX_FILTER_TRIM: + return "Trim for receiver used in EDR."; + case CSR_PSKEY_MR_RX_FILTER_RESPONSE: + return "Filter response for receiver used in EDR."; + case CSR_PSKEY_PIO_WAKEUP_STATE: + return "PIO deep sleep wake up state "; + case CSR_PSKEY_MR_TX_IF_ATTEN_OFF_TEMP: + return "TX IF atten off temperature when using EDR."; + case CSR_PSKEY_LO_DIV_LATCH_BYPASS: + return "Bypass latch for LO dividers"; + case CSR_PSKEY_LO_VCO_STANDBY: + return "Use standby mode for the LO VCO"; + case CSR_PSKEY_SLOW_CLOCK_FILTER_SHIFT: + return "Slow clock sampling filter constant"; + case CSR_PSKEY_SLOW_CLOCK_FILTER_DIVIDER: + return "Slow clock filter fractional threshold"; + case CSR_PSKEY_USB_ATTRIBUTES_POWER: + return "USB self powered"; + case CSR_PSKEY_USB_ATTRIBUTES_WAKEUP: + return "USB responds to wake-up"; + case CSR_PSKEY_DFU_ATTRIBUTES_MANIFESTATION_TOLERANT: + return "DFU manifestation tolerant"; + case CSR_PSKEY_DFU_ATTRIBUTES_CAN_UPLOAD: + return "DFU can upload"; + case CSR_PSKEY_DFU_ATTRIBUTES_CAN_DOWNLOAD: + return "DFU can download"; + case CSR_PSKEY_UART_CONFIG_STOP_BITS: + return "UART: stop bits"; + case CSR_PSKEY_UART_CONFIG_PARITY_BIT: + return "UART: parity bit"; + case CSR_PSKEY_UART_CONFIG_FLOW_CTRL_EN: + return "UART: hardware flow control"; + case CSR_PSKEY_UART_CONFIG_RTS_AUTO_EN: + return "UART: RTS auto-enabled"; + case CSR_PSKEY_UART_CONFIG_RTS: + return "UART: RTS asserted"; + case CSR_PSKEY_UART_CONFIG_TX_ZERO_EN: + return "UART: TX zero enable"; + case CSR_PSKEY_UART_CONFIG_NON_BCSP_EN: + return "UART: enable BCSP-specific hardware"; + case CSR_PSKEY_UART_CONFIG_RX_RATE_DELAY: + return "UART: RX rate delay"; + case CSR_PSKEY_UART_SEQ_TIMEOUT: + return "UART: BCSP ack timeout"; + case CSR_PSKEY_UART_SEQ_RETRIES: + return "UART: retry limit in sequencing layer"; + case CSR_PSKEY_UART_SEQ_WINSIZE: + return "UART: BCSP transmit window size"; + case CSR_PSKEY_UART_USE_CRC_ON_TX: + return "UART: use BCSP CRCs"; + case CSR_PSKEY_UART_HOST_INITIAL_STATE: + return "UART: initial host state"; + case CSR_PSKEY_UART_HOST_ATTENTION_SPAN: + return "UART: host attention span"; + case CSR_PSKEY_UART_HOST_WAKEUP_TIME: + return "UART: host wakeup time"; + case CSR_PSKEY_UART_HOST_WAKEUP_WAIT: + return "UART: host wakeup wait"; + case CSR_PSKEY_BCSP_LM_MODE: + return "BCSP link establishment mode"; + case CSR_PSKEY_BCSP_LM_SYNC_RETRIES: + return "BCSP link establishment sync retries"; + case CSR_PSKEY_BCSP_LM_TSHY: + return "BCSP link establishment Tshy"; + case CSR_PSKEY_UART_DFU_CONFIG_STOP_BITS: + return "DFU mode UART: stop bits"; + case CSR_PSKEY_UART_DFU_CONFIG_PARITY_BIT: + return "DFU mode UART: parity bit"; + case CSR_PSKEY_UART_DFU_CONFIG_FLOW_CTRL_EN: + return "DFU mode UART: hardware flow control"; + case CSR_PSKEY_UART_DFU_CONFIG_RTS_AUTO_EN: + return "DFU mode UART: RTS auto-enabled"; + case CSR_PSKEY_UART_DFU_CONFIG_RTS: + return "DFU mode UART: RTS asserted"; + case CSR_PSKEY_UART_DFU_CONFIG_TX_ZERO_EN: + return "DFU mode UART: TX zero enable"; + case CSR_PSKEY_UART_DFU_CONFIG_NON_BCSP_EN: + return "DFU mode UART: enable BCSP-specific hardware"; + case CSR_PSKEY_UART_DFU_CONFIG_RX_RATE_DELAY: + return "DFU mode UART: RX rate delay"; + case CSR_PSKEY_AMUX_AIO0: + return "Multiplexer for AIO 0"; + case CSR_PSKEY_AMUX_AIO1: + return "Multiplexer for AIO 1"; + case CSR_PSKEY_AMUX_AIO2: + return "Multiplexer for AIO 2"; + case CSR_PSKEY_AMUX_AIO3: + return "Multiplexer for AIO 3"; + case CSR_PSKEY_LOCAL_NAME_SIMPLIFIED: + return "Local Name (simplified)"; + case CSR_PSKEY_EXTENDED_STUB: + return "Extended stub"; + default: + return "Unknown"; + } +} + +char *csr_pskeytoval(uint16_t pskey) +{ + switch (pskey) { + case CSR_PSKEY_BDADDR: + return "BDADDR"; + case CSR_PSKEY_COUNTRYCODE: + return "COUNTRYCODE"; + case CSR_PSKEY_CLASSOFDEVICE: + return "CLASSOFDEVICE"; + case CSR_PSKEY_DEVICE_DRIFT: + return "DEVICE_DRIFT"; + case CSR_PSKEY_DEVICE_JITTER: + return "DEVICE_JITTER"; + case CSR_PSKEY_MAX_ACLS: + return "MAX_ACLS"; + case CSR_PSKEY_MAX_SCOS: + return "MAX_SCOS"; + case CSR_PSKEY_MAX_REMOTE_MASTERS: + return "MAX_REMOTE_MASTERS"; + case CSR_PSKEY_ENABLE_MASTERY_WITH_SLAVERY: + return "ENABLE_MASTERY_WITH_SLAVERY"; + case CSR_PSKEY_H_HC_FC_MAX_ACL_PKT_LEN: + return "H_HC_FC_MAX_ACL_PKT_LEN"; + case CSR_PSKEY_H_HC_FC_MAX_SCO_PKT_LEN: + return "H_HC_FC_MAX_SCO_PKT_LEN"; + case CSR_PSKEY_H_HC_FC_MAX_ACL_PKTS: + return "H_HC_FC_MAX_ACL_PKTS"; + case CSR_PSKEY_H_HC_FC_MAX_SCO_PKTS: + return "H_HC_FC_MAX_SCO_PKTS"; + case CSR_PSKEY_LC_FC_BUFFER_LOW_WATER_MARK: + return "LC_FC_BUFFER_LOW_WATER_MARK"; + case CSR_PSKEY_LC_MAX_TX_POWER: + return "LC_MAX_TX_POWER"; + case CSR_PSKEY_TX_GAIN_RAMP: + return "TX_GAIN_RAMP"; + case CSR_PSKEY_LC_POWER_TABLE: + return "LC_POWER_TABLE"; + case CSR_PSKEY_LC_PEER_POWER_PERIOD: + return "LC_PEER_POWER_PERIOD"; + case CSR_PSKEY_LC_FC_POOLS_LOW_WATER_MARK: + return "LC_FC_POOLS_LOW_WATER_MARK"; + case CSR_PSKEY_LC_DEFAULT_TX_POWER: + return "LC_DEFAULT_TX_POWER"; + case CSR_PSKEY_LC_RSSI_GOLDEN_RANGE: + return "LC_RSSI_GOLDEN_RANGE"; + case CSR_PSKEY_LC_COMBO_DISABLE_PIO_MASK: + return "LC_COMBO_DISABLE_PIO_MASK"; + case CSR_PSKEY_LC_COMBO_PRIORITY_PIO_MASK: + return "LC_COMBO_PRIORITY_PIO_MASK"; + case CSR_PSKEY_LC_COMBO_DOT11_CHANNEL_PIO_BASE: + return "LC_COMBO_DOT11_CHANNEL_PIO_BASE"; + case CSR_PSKEY_LC_COMBO_DOT11_BLOCK_CHANNELS: + return "LC_COMBO_DOT11_BLOCK_CHANNELS"; + case CSR_PSKEY_LC_MAX_TX_POWER_NO_RSSI: + return "LC_MAX_TX_POWER_NO_RSSI"; + case CSR_PSKEY_LC_CONNECTION_RX_WINDOW: + return "LC_CONNECTION_RX_WINDOW"; + case CSR_PSKEY_LC_COMBO_DOT11_TX_PROTECTION_MODE: + return "LC_COMBO_DOT11_TX_PROTECTION_MODE"; + case CSR_PSKEY_LC_ENHANCED_POWER_TABLE: + return "LC_ENHANCED_POWER_TABLE"; + case CSR_PSKEY_LC_WIDEBAND_RSSI_CONFIG: + return "LC_WIDEBAND_RSSI_CONFIG"; + case CSR_PSKEY_LC_COMBO_DOT11_PRIORITY_LEAD: + return "LC_COMBO_DOT11_PRIORITY_LEAD"; + case CSR_PSKEY_BT_CLOCK_INIT: + return "BT_CLOCK_INIT"; + case CSR_PSKEY_TX_MR_MOD_DELAY: + return "TX_MR_MOD_DELAY"; + case CSR_PSKEY_RX_MR_SYNC_TIMING: + return "RX_MR_SYNC_TIMING"; + case CSR_PSKEY_RX_MR_SYNC_CONFIG: + return "RX_MR_SYNC_CONFIG"; + case CSR_PSKEY_LC_LOST_SYNC_SLOTS: + return "LC_LOST_SYNC_SLOTS"; + case CSR_PSKEY_RX_MR_SAMP_CONFIG: + return "RX_MR_SAMP_CONFIG"; + case CSR_PSKEY_AGC_HYST_LEVELS: + return "AGC_HYST_LEVELS"; + case CSR_PSKEY_RX_LEVEL_LOW_SIGNAL: + return "RX_LEVEL_LOW_SIGNAL"; + case CSR_PSKEY_AGC_IQ_LVL_VALUES: + return "AGC_IQ_LVL_VALUES"; + case CSR_PSKEY_MR_FTRIM_OFFSET_12DB: + return "MR_FTRIM_OFFSET_12DB"; + case CSR_PSKEY_MR_FTRIM_OFFSET_6DB: + return "MR_FTRIM_OFFSET_6DB"; + case CSR_PSKEY_NO_CAL_ON_BOOT: + return "NO_CAL_ON_BOOT"; + case CSR_PSKEY_RSSI_HI_TARGET: + return "RSSI_HI_TARGET"; + case CSR_PSKEY_PREFERRED_MIN_ATTENUATION: + return "PREFERRED_MIN_ATTENUATION"; + case CSR_PSKEY_LC_COMBO_DOT11_PRIORITY_OVERRIDE: + return "LC_COMBO_DOT11_PRIORITY_OVERRIDE"; + case CSR_PSKEY_LC_MULTISLOT_HOLDOFF: + return "LC_MULTISLOT_HOLDOFF"; + case CSR_PSKEY_FREE_KEY_PIGEON_HOLE: + return "FREE_KEY_PIGEON_HOLE"; + case CSR_PSKEY_LINK_KEY_BD_ADDR0: + return "LINK_KEY_BD_ADDR0"; + case CSR_PSKEY_LINK_KEY_BD_ADDR1: + return "LINK_KEY_BD_ADDR1"; + case CSR_PSKEY_LINK_KEY_BD_ADDR2: + return "LINK_KEY_BD_ADDR2"; + case CSR_PSKEY_LINK_KEY_BD_ADDR3: + return "LINK_KEY_BD_ADDR3"; + case CSR_PSKEY_LINK_KEY_BD_ADDR4: + return "LINK_KEY_BD_ADDR4"; + case CSR_PSKEY_LINK_KEY_BD_ADDR5: + return "LINK_KEY_BD_ADDR5"; + case CSR_PSKEY_LINK_KEY_BD_ADDR6: + return "LINK_KEY_BD_ADDR6"; + case CSR_PSKEY_LINK_KEY_BD_ADDR7: + return "LINK_KEY_BD_ADDR7"; + case CSR_PSKEY_LINK_KEY_BD_ADDR8: + return "LINK_KEY_BD_ADDR8"; + case CSR_PSKEY_LINK_KEY_BD_ADDR9: + return "LINK_KEY_BD_ADDR9"; + case CSR_PSKEY_LINK_KEY_BD_ADDR10: + return "LINK_KEY_BD_ADDR10"; + case CSR_PSKEY_LINK_KEY_BD_ADDR11: + return "LINK_KEY_BD_ADDR11"; + case CSR_PSKEY_LINK_KEY_BD_ADDR12: + return "LINK_KEY_BD_ADDR12"; + case CSR_PSKEY_LINK_KEY_BD_ADDR13: + return "LINK_KEY_BD_ADDR13"; + case CSR_PSKEY_LINK_KEY_BD_ADDR14: + return "LINK_KEY_BD_ADDR14"; + case CSR_PSKEY_LINK_KEY_BD_ADDR15: + return "LINK_KEY_BD_ADDR15"; + case CSR_PSKEY_ENC_KEY_LMIN: + return "ENC_KEY_LMIN"; + case CSR_PSKEY_ENC_KEY_LMAX: + return "ENC_KEY_LMAX"; + case CSR_PSKEY_LOCAL_SUPPORTED_FEATURES: + return "LOCAL_SUPPORTED_FEATURES"; + case CSR_PSKEY_LM_USE_UNIT_KEY: + return "LM_USE_UNIT_KEY"; + case CSR_PSKEY_HCI_NOP_DISABLE: + return "HCI_NOP_DISABLE"; + case CSR_PSKEY_LM_MAX_EVENT_FILTERS: + return "LM_MAX_EVENT_FILTERS"; + case CSR_PSKEY_LM_USE_ENC_MODE_BROADCAST: + return "LM_USE_ENC_MODE_BROADCAST"; + case CSR_PSKEY_LM_TEST_SEND_ACCEPTED_TWICE: + return "LM_TEST_SEND_ACCEPTED_TWICE"; + case CSR_PSKEY_LM_MAX_PAGE_HOLD_TIME: + return "LM_MAX_PAGE_HOLD_TIME"; + case CSR_PSKEY_AFH_ADAPTATION_RESPONSE_TIME: + return "AFH_ADAPTATION_RESPONSE_TIME"; + case CSR_PSKEY_AFH_OPTIONS: + return "AFH_OPTIONS"; + case CSR_PSKEY_AFH_RSSI_RUN_PERIOD: + return "AFH_RSSI_RUN_PERIOD"; + case CSR_PSKEY_AFH_REENABLE_CHANNEL_TIME: + return "AFH_REENABLE_CHANNEL_TIME"; + case CSR_PSKEY_NO_DROP_ON_ACR_MS_FAIL: + return "NO_DROP_ON_ACR_MS_FAIL"; + case CSR_PSKEY_MAX_PRIVATE_KEYS: + return "MAX_PRIVATE_KEYS"; + case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR0: + return "PRIVATE_LINK_KEY_BD_ADDR0"; + case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR1: + return "PRIVATE_LINK_KEY_BD_ADDR1"; + case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR2: + return "PRIVATE_LINK_KEY_BD_ADDR2"; + case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR3: + return "PRIVATE_LINK_KEY_BD_ADDR3"; + case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR4: + return "PRIVATE_LINK_KEY_BD_ADDR4"; + case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR5: + return "PRIVATE_LINK_KEY_BD_ADDR5"; + case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR6: + return "PRIVATE_LINK_KEY_BD_ADDR6"; + case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR7: + return "PRIVATE_LINK_KEY_BD_ADDR7"; + case CSR_PSKEY_LOCAL_SUPPORTED_COMMANDS: + return "LOCAL_SUPPORTED_COMMANDS"; + case CSR_PSKEY_LM_MAX_ABSENCE_INDEX: + return "LM_MAX_ABSENCE_INDEX"; + case CSR_PSKEY_DEVICE_NAME: + return "DEVICE_NAME"; + case CSR_PSKEY_AFH_RSSI_THRESHOLD: + return "AFH_RSSI_THRESHOLD"; + case CSR_PSKEY_LM_CASUAL_SCAN_INTERVAL: + return "LM_CASUAL_SCAN_INTERVAL"; + case CSR_PSKEY_AFH_MIN_MAP_CHANGE: + return "AFH_MIN_MAP_CHANGE"; + case CSR_PSKEY_AFH_RSSI_LP_RUN_PERIOD: + return "AFH_RSSI_LP_RUN_PERIOD"; + case CSR_PSKEY_HCI_LMP_LOCAL_VERSION: + return "HCI_LMP_LOCAL_VERSION"; + case CSR_PSKEY_LMP_REMOTE_VERSION: + return "LMP_REMOTE_VERSION"; + case CSR_PSKEY_HOLD_ERROR_MESSAGE_NUMBER: + return "HOLD_ERROR_MESSAGE_NUMBER"; + case CSR_PSKEY_DFU_ATTRIBUTES: + return "DFU_ATTRIBUTES"; + case CSR_PSKEY_DFU_DETACH_TO: + return "DFU_DETACH_TO"; + case CSR_PSKEY_DFU_TRANSFER_SIZE: + return "DFU_TRANSFER_SIZE"; + case CSR_PSKEY_DFU_ENABLE: + return "DFU_ENABLE"; + case CSR_PSKEY_DFU_LIN_REG_ENABLE: + return "DFU_LIN_REG_ENABLE"; + case CSR_PSKEY_DFUENC_VMAPP_PK_MODULUS_MSB: + return "DFUENC_VMAPP_PK_MODULUS_MSB"; + case CSR_PSKEY_DFUENC_VMAPP_PK_MODULUS_LSB: + return "DFUENC_VMAPP_PK_MODULUS_LSB"; + case CSR_PSKEY_DFUENC_VMAPP_PK_M_DASH: + return "DFUENC_VMAPP_PK_M_DASH"; + case CSR_PSKEY_DFUENC_VMAPP_PK_R2N_MSB: + return "DFUENC_VMAPP_PK_R2N_MSB"; + case CSR_PSKEY_DFUENC_VMAPP_PK_R2N_LSB: + return "DFUENC_VMAPP_PK_R2N_LSB"; + case CSR_PSKEY_BCSP_LM_PS_BLOCK: + return "BCSP_LM_PS_BLOCK"; + case CSR_PSKEY_HOSTIO_FC_PS_BLOCK: + return "HOSTIO_FC_PS_BLOCK"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO0: + return "HOSTIO_PROTOCOL_INFO0"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO1: + return "HOSTIO_PROTOCOL_INFO1"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO2: + return "HOSTIO_PROTOCOL_INFO2"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO3: + return "HOSTIO_PROTOCOL_INFO3"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO4: + return "HOSTIO_PROTOCOL_INFO4"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO5: + return "HOSTIO_PROTOCOL_INFO5"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO6: + return "HOSTIO_PROTOCOL_INFO6"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO7: + return "HOSTIO_PROTOCOL_INFO7"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO8: + return "HOSTIO_PROTOCOL_INFO8"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO9: + return "HOSTIO_PROTOCOL_INFO9"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO10: + return "HOSTIO_PROTOCOL_INFO10"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO11: + return "HOSTIO_PROTOCOL_INFO11"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO12: + return "HOSTIO_PROTOCOL_INFO12"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO13: + return "HOSTIO_PROTOCOL_INFO13"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO14: + return "HOSTIO_PROTOCOL_INFO14"; + case CSR_PSKEY_HOSTIO_PROTOCOL_INFO15: + return "HOSTIO_PROTOCOL_INFO15"; + case CSR_PSKEY_HOSTIO_UART_RESET_TIMEOUT: + return "HOSTIO_UART_RESET_TIMEOUT"; + case CSR_PSKEY_HOSTIO_USE_HCI_EXTN: + return "HOSTIO_USE_HCI_EXTN"; + case CSR_PSKEY_HOSTIO_USE_HCI_EXTN_CCFC: + return "HOSTIO_USE_HCI_EXTN_CCFC"; + case CSR_PSKEY_HOSTIO_HCI_EXTN_PAYLOAD_SIZE: + return "HOSTIO_HCI_EXTN_PAYLOAD_SIZE"; + case CSR_PSKEY_BCSP_LM_CNF_CNT_LIMIT: + return "BCSP_LM_CNF_CNT_LIMIT"; + case CSR_PSKEY_HOSTIO_MAP_SCO_PCM: + return "HOSTIO_MAP_SCO_PCM"; + case CSR_PSKEY_HOSTIO_AWKWARD_PCM_SYNC: + return "HOSTIO_AWKWARD_PCM_SYNC"; + case CSR_PSKEY_HOSTIO_BREAK_POLL_PERIOD: + return "HOSTIO_BREAK_POLL_PERIOD"; + case CSR_PSKEY_HOSTIO_MIN_UART_HCI_SCO_SIZE: + return "HOSTIO_MIN_UART_HCI_SCO_SIZE"; + case CSR_PSKEY_HOSTIO_MAP_SCO_CODEC: + return "HOSTIO_MAP_SCO_CODEC"; + case CSR_PSKEY_PCM_CVSD_TX_HI_FREQ_BOOST: + return "PCM_CVSD_TX_HI_FREQ_BOOST"; + case CSR_PSKEY_PCM_CVSD_RX_HI_FREQ_BOOST: + return "PCM_CVSD_RX_HI_FREQ_BOOST"; + case CSR_PSKEY_PCM_CONFIG32: + return "PCM_CONFIG32"; + case CSR_PSKEY_USE_OLD_BCSP_LE: + return "USE_OLD_BCSP_LE"; + case CSR_PSKEY_PCM_CVSD_USE_NEW_FILTER: + return "PCM_CVSD_USE_NEW_FILTER"; + case CSR_PSKEY_PCM_FORMAT: + return "PCM_FORMAT"; + case CSR_PSKEY_CODEC_OUT_GAIN: + return "CODEC_OUT_GAIN"; + case CSR_PSKEY_CODEC_IN_GAIN: + return "CODEC_IN_GAIN"; + case CSR_PSKEY_CODEC_PIO: + return "CODEC_PIO"; + case CSR_PSKEY_PCM_LOW_JITTER_CONFIG: + return "PCM_LOW_JITTER_CONFIG"; + case CSR_PSKEY_HOSTIO_SCO_PCM_THRESHOLDS: + return "HOSTIO_SCO_PCM_THRESHOLDS"; + case CSR_PSKEY_HOSTIO_SCO_HCI_THRESHOLDS: + return "HOSTIO_SCO_HCI_THRESHOLDS"; + case CSR_PSKEY_HOSTIO_MAP_SCO_PCM_SLOT: + return "HOSTIO_MAP_SCO_PCM_SLOT"; + case CSR_PSKEY_UART_BAUDRATE: + return "UART_BAUDRATE"; + case CSR_PSKEY_UART_CONFIG_BCSP: + return "UART_CONFIG_BCSP"; + case CSR_PSKEY_UART_CONFIG_H4: + return "UART_CONFIG_H4"; + case CSR_PSKEY_UART_CONFIG_H5: + return "UART_CONFIG_H5"; + case CSR_PSKEY_UART_CONFIG_USR: + return "UART_CONFIG_USR"; + case CSR_PSKEY_UART_TX_CRCS: + return "UART_TX_CRCS"; + case CSR_PSKEY_UART_ACK_TIMEOUT: + return "UART_ACK_TIMEOUT"; + case CSR_PSKEY_UART_TX_MAX_ATTEMPTS: + return "UART_TX_MAX_ATTEMPTS"; + case CSR_PSKEY_UART_TX_WINDOW_SIZE: + return "UART_TX_WINDOW_SIZE"; + case CSR_PSKEY_UART_HOST_WAKE: + return "UART_HOST_WAKE"; + case CSR_PSKEY_HOSTIO_THROTTLE_TIMEOUT: + return "HOSTIO_THROTTLE_TIMEOUT"; + case CSR_PSKEY_PCM_ALWAYS_ENABLE: + return "PCM_ALWAYS_ENABLE"; + case CSR_PSKEY_UART_HOST_WAKE_SIGNAL: + return "UART_HOST_WAKE_SIGNAL"; + case CSR_PSKEY_UART_CONFIG_H4DS: + return "UART_CONFIG_H4DS"; + case CSR_PSKEY_H4DS_WAKE_DURATION: + return "H4DS_WAKE_DURATION"; + case CSR_PSKEY_H4DS_MAXWU: + return "H4DS_MAXWU"; + case CSR_PSKEY_H4DS_LE_TIMER_PERIOD: + return "H4DS_LE_TIMER_PERIOD"; + case CSR_PSKEY_H4DS_TWU_TIMER_PERIOD: + return "H4DS_TWU_TIMER_PERIOD"; + case CSR_PSKEY_H4DS_UART_IDLE_TIMER_PERIOD: + return "H4DS_UART_IDLE_TIMER_PERIOD"; + case CSR_PSKEY_ANA_FTRIM: + return "ANA_FTRIM"; + case CSR_PSKEY_WD_TIMEOUT: + return "WD_TIMEOUT"; + case CSR_PSKEY_WD_PERIOD: + return "WD_PERIOD"; + case CSR_PSKEY_HOST_INTERFACE: + return "HOST_INTERFACE"; + case CSR_PSKEY_HQ_HOST_TIMEOUT: + return "HQ_HOST_TIMEOUT"; + case CSR_PSKEY_HQ_ACTIVE: + return "HQ_ACTIVE"; + case CSR_PSKEY_BCCMD_SECURITY_ACTIVE: + return "BCCMD_SECURITY_ACTIVE"; + case CSR_PSKEY_ANA_FREQ: + return "ANA_FREQ"; + case CSR_PSKEY_PIO_PROTECT_MASK: + return "PIO_PROTECT_MASK"; + case CSR_PSKEY_PMALLOC_SIZES: + return "PMALLOC_SIZES"; + case CSR_PSKEY_UART_BAUD_RATE: + return "UART_BAUD_RATE"; + case CSR_PSKEY_UART_CONFIG: + return "UART_CONFIG"; + case CSR_PSKEY_STUB: + return "STUB"; + case CSR_PSKEY_TXRX_PIO_CONTROL: + return "TXRX_PIO_CONTROL"; + case CSR_PSKEY_ANA_RX_LEVEL: + return "ANA_RX_LEVEL"; + case CSR_PSKEY_ANA_RX_FTRIM: + return "ANA_RX_FTRIM"; + case CSR_PSKEY_PSBC_DATA_VERSION: + return "PSBC_DATA_VERSION"; + case CSR_PSKEY_PCM0_ATTENUATION: + return "PCM0_ATTENUATION"; + case CSR_PSKEY_LO_LVL_MAX: + return "LO_LVL_MAX"; + case CSR_PSKEY_LO_ADC_AMPL_MIN: + return "LO_ADC_AMPL_MIN"; + case CSR_PSKEY_LO_ADC_AMPL_MAX: + return "LO_ADC_AMPL_MAX"; + case CSR_PSKEY_IQ_TRIM_CHANNEL: + return "IQ_TRIM_CHANNEL"; + case CSR_PSKEY_IQ_TRIM_GAIN: + return "IQ_TRIM_GAIN"; + case CSR_PSKEY_IQ_TRIM_ENABLE: + return "IQ_TRIM_ENABLE"; + case CSR_PSKEY_TX_OFFSET_HALF_MHZ: + return "TX_OFFSET_HALF_MHZ"; + case CSR_PSKEY_GBL_MISC_ENABLES: + return "GBL_MISC_ENABLES"; + case CSR_PSKEY_UART_SLEEP_TIMEOUT: + return "UART_SLEEP_TIMEOUT"; + case CSR_PSKEY_DEEP_SLEEP_STATE: + return "DEEP_SLEEP_STATE"; + case CSR_PSKEY_IQ_ENABLE_PHASE_TRIM: + return "IQ_ENABLE_PHASE_TRIM"; + case CSR_PSKEY_HCI_HANDLE_FREEZE_PERIOD: + return "HCI_HANDLE_FREEZE_PERIOD"; + case CSR_PSKEY_MAX_FROZEN_HCI_HANDLES: + return "MAX_FROZEN_HCI_HANDLES"; + case CSR_PSKEY_PAGETABLE_DESTRUCTION_DELAY: + return "PAGETABLE_DESTRUCTION_DELAY"; + case CSR_PSKEY_IQ_TRIM_PIO_SETTINGS: + return "IQ_TRIM_PIO_SETTINGS"; + case CSR_PSKEY_USE_EXTERNAL_CLOCK: + return "USE_EXTERNAL_CLOCK"; + case CSR_PSKEY_DEEP_SLEEP_WAKE_CTS: + return "DEEP_SLEEP_WAKE_CTS"; + case CSR_PSKEY_FC_HC2H_FLUSH_DELAY: + return "FC_HC2H_FLUSH_DELAY"; + case CSR_PSKEY_RX_HIGHSIDE: + return "RX_HIGHSIDE"; + case CSR_PSKEY_TX_PRE_LVL: + return "TX_PRE_LVL"; + case CSR_PSKEY_RX_SINGLE_ENDED: + return "RX_SINGLE_ENDED"; + case CSR_PSKEY_TX_FILTER_CONFIG: + return "TX_FILTER_CONFIG"; + case CSR_PSKEY_CLOCK_REQUEST_ENABLE: + return "CLOCK_REQUEST_ENABLE"; + case CSR_PSKEY_RX_MIN_ATTEN: + return "RX_MIN_ATTEN"; + case CSR_PSKEY_XTAL_TARGET_AMPLITUDE: + return "XTAL_TARGET_AMPLITUDE"; + case CSR_PSKEY_PCM_MIN_CPU_CLOCK: + return "PCM_MIN_CPU_CLOCK"; + case CSR_PSKEY_HOST_INTERFACE_PIO_USB: + return "HOST_INTERFACE_PIO_USB"; + case CSR_PSKEY_CPU_IDLE_MODE: + return "CPU_IDLE_MODE"; + case CSR_PSKEY_DEEP_SLEEP_CLEAR_RTS: + return "DEEP_SLEEP_CLEAR_RTS"; + case CSR_PSKEY_RF_RESONANCE_TRIM: + return "RF_RESONANCE_TRIM"; + case CSR_PSKEY_DEEP_SLEEP_PIO_WAKE: + return "DEEP_SLEEP_PIO_WAKE"; + case CSR_PSKEY_DRAIN_BORE_TIMERS: + return "DRAIN_BORE_TIMERS"; + case CSR_PSKEY_DRAIN_TX_POWER_BASE: + return "DRAIN_TX_POWER_BASE"; + case CSR_PSKEY_MODULE_ID: + return "MODULE_ID"; + case CSR_PSKEY_MODULE_DESIGN: + return "MODULE_DESIGN"; + case CSR_PSKEY_MODULE_SECURITY_CODE: + return "MODULE_SECURITY_CODE"; + case CSR_PSKEY_VM_DISABLE: + return "VM_DISABLE"; + case CSR_PSKEY_MOD_MANUF0: + return "MOD_MANUF0"; + case CSR_PSKEY_MOD_MANUF1: + return "MOD_MANUF1"; + case CSR_PSKEY_MOD_MANUF2: + return "MOD_MANUF2"; + case CSR_PSKEY_MOD_MANUF3: + return "MOD_MANUF3"; + case CSR_PSKEY_MOD_MANUF4: + return "MOD_MANUF4"; + case CSR_PSKEY_MOD_MANUF5: + return "MOD_MANUF5"; + case CSR_PSKEY_MOD_MANUF6: + return "MOD_MANUF6"; + case CSR_PSKEY_MOD_MANUF7: + return "MOD_MANUF7"; + case CSR_PSKEY_MOD_MANUF8: + return "MOD_MANUF8"; + case CSR_PSKEY_MOD_MANUF9: + return "MOD_MANUF9"; + case CSR_PSKEY_DUT_VM_DISABLE: + return "DUT_VM_DISABLE"; + case CSR_PSKEY_USR0: + return "USR0"; + case CSR_PSKEY_USR1: + return "USR1"; + case CSR_PSKEY_USR2: + return "USR2"; + case CSR_PSKEY_USR3: + return "USR3"; + case CSR_PSKEY_USR4: + return "USR4"; + case CSR_PSKEY_USR5: + return "USR5"; + case CSR_PSKEY_USR6: + return "USR6"; + case CSR_PSKEY_USR7: + return "USR7"; + case CSR_PSKEY_USR8: + return "USR8"; + case CSR_PSKEY_USR9: + return "USR9"; + case CSR_PSKEY_USR10: + return "USR10"; + case CSR_PSKEY_USR11: + return "USR11"; + case CSR_PSKEY_USR12: + return "USR12"; + case CSR_PSKEY_USR13: + return "USR13"; + case CSR_PSKEY_USR14: + return "USR14"; + case CSR_PSKEY_USR15: + return "USR15"; + case CSR_PSKEY_USR16: + return "USR16"; + case CSR_PSKEY_USR17: + return "USR17"; + case CSR_PSKEY_USR18: + return "USR18"; + case CSR_PSKEY_USR19: + return "USR19"; + case CSR_PSKEY_USR20: + return "USR20"; + case CSR_PSKEY_USR21: + return "USR21"; + case CSR_PSKEY_USR22: + return "USR22"; + case CSR_PSKEY_USR23: + return "USR23"; + case CSR_PSKEY_USR24: + return "USR24"; + case CSR_PSKEY_USR25: + return "USR25"; + case CSR_PSKEY_USR26: + return "USR26"; + case CSR_PSKEY_USR27: + return "USR27"; + case CSR_PSKEY_USR28: + return "USR28"; + case CSR_PSKEY_USR29: + return "USR29"; + case CSR_PSKEY_USR30: + return "USR30"; + case CSR_PSKEY_USR31: + return "USR31"; + case CSR_PSKEY_USR32: + return "USR32"; + case CSR_PSKEY_USR33: + return "USR33"; + case CSR_PSKEY_USR34: + return "USR34"; + case CSR_PSKEY_USR35: + return "USR35"; + case CSR_PSKEY_USR36: + return "USR36"; + case CSR_PSKEY_USR37: + return "USR37"; + case CSR_PSKEY_USR38: + return "USR38"; + case CSR_PSKEY_USR39: + return "USR39"; + case CSR_PSKEY_USR40: + return "USR40"; + case CSR_PSKEY_USR41: + return "USR41"; + case CSR_PSKEY_USR42: + return "USR42"; + case CSR_PSKEY_USR43: + return "USR43"; + case CSR_PSKEY_USR44: + return "USR44"; + case CSR_PSKEY_USR45: + return "USR45"; + case CSR_PSKEY_USR46: + return "USR46"; + case CSR_PSKEY_USR47: + return "USR47"; + case CSR_PSKEY_USR48: + return "USR48"; + case CSR_PSKEY_USR49: + return "USR49"; + case CSR_PSKEY_USB_VERSION: + return "USB_VERSION"; + case CSR_PSKEY_USB_DEVICE_CLASS_CODES: + return "USB_DEVICE_CLASS_CODES"; + case CSR_PSKEY_USB_VENDOR_ID: + return "USB_VENDOR_ID"; + case CSR_PSKEY_USB_PRODUCT_ID: + return "USB_PRODUCT_ID"; + case CSR_PSKEY_USB_MANUF_STRING: + return "USB_MANUF_STRING"; + case CSR_PSKEY_USB_PRODUCT_STRING: + return "USB_PRODUCT_STRING"; + case CSR_PSKEY_USB_SERIAL_NUMBER_STRING: + return "USB_SERIAL_NUMBER_STRING"; + case CSR_PSKEY_USB_CONFIG_STRING: + return "USB_CONFIG_STRING"; + case CSR_PSKEY_USB_ATTRIBUTES: + return "USB_ATTRIBUTES"; + case CSR_PSKEY_USB_MAX_POWER: + return "USB_MAX_POWER"; + case CSR_PSKEY_USB_BT_IF_CLASS_CODES: + return "USB_BT_IF_CLASS_CODES"; + case CSR_PSKEY_USB_LANGID: + return "USB_LANGID"; + case CSR_PSKEY_USB_DFU_CLASS_CODES: + return "USB_DFU_CLASS_CODES"; + case CSR_PSKEY_USB_DFU_PRODUCT_ID: + return "USB_DFU_PRODUCT_ID"; + case CSR_PSKEY_USB_PIO_DETACH: + return "USB_PIO_DETACH"; + case CSR_PSKEY_USB_PIO_WAKEUP: + return "USB_PIO_WAKEUP"; + case CSR_PSKEY_USB_PIO_PULLUP: + return "USB_PIO_PULLUP"; + case CSR_PSKEY_USB_PIO_VBUS: + return "USB_PIO_VBUS"; + case CSR_PSKEY_USB_PIO_WAKE_TIMEOUT: + return "USB_PIO_WAKE_TIMEOUT"; + case CSR_PSKEY_USB_PIO_RESUME: + return "USB_PIO_RESUME"; + case CSR_PSKEY_USB_BT_SCO_IF_CLASS_CODES: + return "USB_BT_SCO_IF_CLASS_CODES"; + case CSR_PSKEY_USB_SUSPEND_PIO_LEVEL: + return "USB_SUSPEND_PIO_LEVEL"; + case CSR_PSKEY_USB_SUSPEND_PIO_DIR: + return "USB_SUSPEND_PIO_DIR"; + case CSR_PSKEY_USB_SUSPEND_PIO_MASK: + return "USB_SUSPEND_PIO_MASK"; + case CSR_PSKEY_USB_ENDPOINT_0_MAX_PACKET_SIZE: + return "USB_ENDPOINT_0_MAX_PACKET_SIZE"; + case CSR_PSKEY_USB_CONFIG: + return "USB_CONFIG"; + case CSR_PSKEY_RADIOTEST_ATTEN_INIT: + return "RADIOTEST_ATTEN_INIT"; + case CSR_PSKEY_RADIOTEST_FIRST_TRIM_TIME: + return "RADIOTEST_FIRST_TRIM_TIME"; + case CSR_PSKEY_RADIOTEST_SUBSEQUENT_TRIM_TIME: + return "RADIOTEST_SUBSEQUENT_TRIM_TIME"; + case CSR_PSKEY_RADIOTEST_LO_LVL_TRIM_ENABLE: + return "RADIOTEST_LO_LVL_TRIM_ENABLE"; + case CSR_PSKEY_RADIOTEST_DISABLE_MODULATION: + return "RADIOTEST_DISABLE_MODULATION"; + case CSR_PSKEY_RFCOMM_FCON_THRESHOLD: + return "RFCOMM_FCON_THRESHOLD"; + case CSR_PSKEY_RFCOMM_FCOFF_THRESHOLD: + return "RFCOMM_FCOFF_THRESHOLD"; + case CSR_PSKEY_IPV6_STATIC_ADDR: + return "IPV6_STATIC_ADDR"; + case CSR_PSKEY_IPV4_STATIC_ADDR: + return "IPV4_STATIC_ADDR"; + case CSR_PSKEY_IPV6_STATIC_PREFIX_LEN: + return "IPV6_STATIC_PREFIX_LEN"; + case CSR_PSKEY_IPV6_STATIC_ROUTER_ADDR: + return "IPV6_STATIC_ROUTER_ADDR"; + case CSR_PSKEY_IPV4_STATIC_SUBNET_MASK: + return "IPV4_STATIC_SUBNET_MASK"; + case CSR_PSKEY_IPV4_STATIC_ROUTER_ADDR: + return "IPV4_STATIC_ROUTER_ADDR"; + case CSR_PSKEY_MDNS_NAME: + return "MDNS_NAME"; + case CSR_PSKEY_FIXED_PIN: + return "FIXED_PIN"; + case CSR_PSKEY_MDNS_PORT: + return "MDNS_PORT"; + case CSR_PSKEY_MDNS_TTL: + return "MDNS_TTL"; + case CSR_PSKEY_MDNS_IPV4_ADDR: + return "MDNS_IPV4_ADDR"; + case CSR_PSKEY_ARP_CACHE_TIMEOUT: + return "ARP_CACHE_TIMEOUT"; + case CSR_PSKEY_HFP_POWER_TABLE: + return "HFP_POWER_TABLE"; + case CSR_PSKEY_DRAIN_BORE_TIMER_COUNTERS: + return "DRAIN_BORE_TIMER_COUNTERS"; + case CSR_PSKEY_DRAIN_BORE_COUNTERS: + return "DRAIN_BORE_COUNTERS"; + case CSR_PSKEY_LOOP_FILTER_TRIM: + return "LOOP_FILTER_TRIM"; + case CSR_PSKEY_DRAIN_BORE_CURRENT_PEAK: + return "DRAIN_BORE_CURRENT_PEAK"; + case CSR_PSKEY_VM_E2_CACHE_LIMIT: + return "VM_E2_CACHE_LIMIT"; + case CSR_PSKEY_FORCE_16MHZ_REF_PIO: + return "FORCE_16MHZ_REF_PIO"; + case CSR_PSKEY_CDMA_LO_REF_LIMITS: + return "CDMA_LO_REF_LIMITS"; + case CSR_PSKEY_CDMA_LO_ERROR_LIMITS: + return "CDMA_LO_ERROR_LIMITS"; + case CSR_PSKEY_CLOCK_STARTUP_DELAY: + return "CLOCK_STARTUP_DELAY"; + case CSR_PSKEY_DEEP_SLEEP_CORRECTION_FACTOR: + return "DEEP_SLEEP_CORRECTION_FACTOR"; + case CSR_PSKEY_TEMPERATURE_CALIBRATION: + return "TEMPERATURE_CALIBRATION"; + case CSR_PSKEY_TEMPERATURE_VS_DELTA_INTERNAL_PA: + return "TEMPERATURE_VS_DELTA_INTERNAL_PA"; + case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_PRE_LVL: + return "TEMPERATURE_VS_DELTA_TX_PRE_LVL"; + case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB: + return "TEMPERATURE_VS_DELTA_TX_BB"; + case CSR_PSKEY_TEMPERATURE_VS_DELTA_ANA_FTRIM: + return "TEMPERATURE_VS_DELTA_ANA_FTRIM"; + case CSR_PSKEY_TEST_DELTA_OFFSET: + return "TEST_DELTA_OFFSET"; + case CSR_PSKEY_RX_DYNAMIC_LVL_OFFSET: + return "RX_DYNAMIC_LVL_OFFSET"; + case CSR_PSKEY_TEST_FORCE_OFFSET: + return "TEST_FORCE_OFFSET"; + case CSR_PSKEY_RF_TRAP_BAD_DIVISION_RATIOS: + return "RF_TRAP_BAD_DIVISION_RATIOS"; + case CSR_PSKEY_RADIOTEST_CDMA_LO_REF_LIMITS: + return "RADIOTEST_CDMA_LO_REF_LIMITS"; + case CSR_PSKEY_INITIAL_BOOTMODE: + return "INITIAL_BOOTMODE"; + case CSR_PSKEY_ONCHIP_HCI_CLIENT: + return "ONCHIP_HCI_CLIENT"; + case CSR_PSKEY_RX_ATTEN_BACKOFF: + return "RX_ATTEN_BACKOFF"; + case CSR_PSKEY_RX_ATTEN_UPDATE_RATE: + return "RX_ATTEN_UPDATE_RATE"; + case CSR_PSKEY_SYNTH_TXRX_THRESHOLDS: + return "SYNTH_TXRX_THRESHOLDS"; + case CSR_PSKEY_MIN_WAIT_STATES: + return "MIN_WAIT_STATES"; + case CSR_PSKEY_RSSI_CORRECTION: + return "RSSI_CORRECTION"; + case CSR_PSKEY_SCHED_THROTTLE_TIMEOUT: + return "SCHED_THROTTLE_TIMEOUT"; + case CSR_PSKEY_DEEP_SLEEP_USE_EXTERNAL_CLOCK: + return "DEEP_SLEEP_USE_EXTERNAL_CLOCK"; + case CSR_PSKEY_TRIM_RADIO_FILTERS: + return "TRIM_RADIO_FILTERS"; + case CSR_PSKEY_TRANSMIT_OFFSET: + return "TRANSMIT_OFFSET"; + case CSR_PSKEY_USB_VM_CONTROL: + return "USB_VM_CONTROL"; + case CSR_PSKEY_MR_ANA_RX_FTRIM: + return "MR_ANA_RX_FTRIM"; + case CSR_PSKEY_I2C_CONFIG: + return "I2C_CONFIG"; + case CSR_PSKEY_IQ_LVL_RX: + return "IQ_LVL_RX"; + case CSR_PSKEY_MR_TX_FILTER_CONFIG: + return "MR_TX_FILTER_CONFIG"; + case CSR_PSKEY_MR_TX_CONFIG2: + return "MR_TX_CONFIG2"; + case CSR_PSKEY_USB_DONT_RESET_BOOTMODE_ON_HOST_RESET: + return "USB_DONT_RESET_BOOTMODE_ON_HOST_RESET"; + case CSR_PSKEY_LC_USE_THROTTLING: + return "LC_USE_THROTTLING"; + case CSR_PSKEY_CHARGER_TRIM: + return "CHARGER_TRIM"; + case CSR_PSKEY_CLOCK_REQUEST_FEATURES: + return "CLOCK_REQUEST_FEATURES"; + case CSR_PSKEY_TRANSMIT_OFFSET_CLASS1: + return "TRANSMIT_OFFSET_CLASS1"; + case CSR_PSKEY_TX_AVOID_PA_CLASS1_PIO: + return "TX_AVOID_PA_CLASS1_PIO"; + case CSR_PSKEY_MR_PIO_CONFIG: + return "MR_PIO_CONFIG"; + case CSR_PSKEY_UART_CONFIG2: + return "UART_CONFIG2"; + case CSR_PSKEY_CLASS1_IQ_LVL: + return "CLASS1_IQ_LVL"; + case CSR_PSKEY_CLASS1_TX_CONFIG2: + return "CLASS1_TX_CONFIG2"; + case CSR_PSKEY_TEMPERATURE_VS_DELTA_INTERNAL_PA_CLASS1: + return "TEMPERATURE_VS_DELTA_INTERNAL_PA_CLASS1"; + case CSR_PSKEY_TEMPERATURE_VS_DELTA_EXTERNAL_PA_CLASS1: + return "TEMPERATURE_VS_DELTA_EXTERNAL_PA_CLASS1"; + case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_PRE_LVL_MR: + return "TEMPERATURE_VS_DELTA_TX_PRE_LVL_MR"; + case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB_MR_HEADER: + return "TEMPERATURE_VS_DELTA_TX_BB_MR_HEADER"; + case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB_MR_PAYLOAD: + return "TEMPERATURE_VS_DELTA_TX_BB_MR_PAYLOAD"; + case CSR_PSKEY_RX_MR_EQ_TAPS: + return "RX_MR_EQ_TAPS"; + case CSR_PSKEY_TX_PRE_LVL_CLASS1: + return "TX_PRE_LVL_CLASS1"; + case CSR_PSKEY_ANALOGUE_ATTENUATOR: + return "ANALOGUE_ATTENUATOR"; + case CSR_PSKEY_MR_RX_FILTER_TRIM: + return "MR_RX_FILTER_TRIM"; + case CSR_PSKEY_MR_RX_FILTER_RESPONSE: + return "MR_RX_FILTER_RESPONSE"; + case CSR_PSKEY_PIO_WAKEUP_STATE: + return "PIO_WAKEUP_STATE"; + case CSR_PSKEY_MR_TX_IF_ATTEN_OFF_TEMP: + return "MR_TX_IF_ATTEN_OFF_TEMP"; + case CSR_PSKEY_LO_DIV_LATCH_BYPASS: + return "LO_DIV_LATCH_BYPASS"; + case CSR_PSKEY_LO_VCO_STANDBY: + return "LO_VCO_STANDBY"; + case CSR_PSKEY_SLOW_CLOCK_FILTER_SHIFT: + return "SLOW_CLOCK_FILTER_SHIFT"; + case CSR_PSKEY_SLOW_CLOCK_FILTER_DIVIDER: + return "SLOW_CLOCK_FILTER_DIVIDER"; + case CSR_PSKEY_USB_ATTRIBUTES_POWER: + return "USB_ATTRIBUTES_POWER"; + case CSR_PSKEY_USB_ATTRIBUTES_WAKEUP: + return "USB_ATTRIBUTES_WAKEUP"; + case CSR_PSKEY_DFU_ATTRIBUTES_MANIFESTATION_TOLERANT: + return "DFU_ATTRIBUTES_MANIFESTATION_TOLERANT"; + case CSR_PSKEY_DFU_ATTRIBUTES_CAN_UPLOAD: + return "DFU_ATTRIBUTES_CAN_UPLOAD"; + case CSR_PSKEY_DFU_ATTRIBUTES_CAN_DOWNLOAD: + return "DFU_ATTRIBUTES_CAN_DOWNLOAD"; + case CSR_PSKEY_UART_CONFIG_STOP_BITS: + return "UART_CONFIG_STOP_BITS"; + case CSR_PSKEY_UART_CONFIG_PARITY_BIT: + return "UART_CONFIG_PARITY_BIT"; + case CSR_PSKEY_UART_CONFIG_FLOW_CTRL_EN: + return "UART_CONFIG_FLOW_CTRL_EN"; + case CSR_PSKEY_UART_CONFIG_RTS_AUTO_EN: + return "UART_CONFIG_RTS_AUTO_EN"; + case CSR_PSKEY_UART_CONFIG_RTS: + return "UART_CONFIG_RTS"; + case CSR_PSKEY_UART_CONFIG_TX_ZERO_EN: + return "UART_CONFIG_TX_ZERO_EN"; + case CSR_PSKEY_UART_CONFIG_NON_BCSP_EN: + return "UART_CONFIG_NON_BCSP_EN"; + case CSR_PSKEY_UART_CONFIG_RX_RATE_DELAY: + return "UART_CONFIG_RX_RATE_DELAY"; + case CSR_PSKEY_UART_SEQ_TIMEOUT: + return "UART_SEQ_TIMEOUT"; + case CSR_PSKEY_UART_SEQ_RETRIES: + return "UART_SEQ_RETRIES"; + case CSR_PSKEY_UART_SEQ_WINSIZE: + return "UART_SEQ_WINSIZE"; + case CSR_PSKEY_UART_USE_CRC_ON_TX: + return "UART_USE_CRC_ON_TX"; + case CSR_PSKEY_UART_HOST_INITIAL_STATE: + return "UART_HOST_INITIAL_STATE"; + case CSR_PSKEY_UART_HOST_ATTENTION_SPAN: + return "UART_HOST_ATTENTION_SPAN"; + case CSR_PSKEY_UART_HOST_WAKEUP_TIME: + return "UART_HOST_WAKEUP_TIME"; + case CSR_PSKEY_UART_HOST_WAKEUP_WAIT: + return "UART_HOST_WAKEUP_WAIT"; + case CSR_PSKEY_BCSP_LM_MODE: + return "BCSP_LM_MODE"; + case CSR_PSKEY_BCSP_LM_SYNC_RETRIES: + return "BCSP_LM_SYNC_RETRIES"; + case CSR_PSKEY_BCSP_LM_TSHY: + return "BCSP_LM_TSHY"; + case CSR_PSKEY_UART_DFU_CONFIG_STOP_BITS: + return "UART_DFU_CONFIG_STOP_BITS"; + case CSR_PSKEY_UART_DFU_CONFIG_PARITY_BIT: + return "UART_DFU_CONFIG_PARITY_BIT"; + case CSR_PSKEY_UART_DFU_CONFIG_FLOW_CTRL_EN: + return "UART_DFU_CONFIG_FLOW_CTRL_EN"; + case CSR_PSKEY_UART_DFU_CONFIG_RTS_AUTO_EN: + return "UART_DFU_CONFIG_RTS_AUTO_EN"; + case CSR_PSKEY_UART_DFU_CONFIG_RTS: + return "UART_DFU_CONFIG_RTS"; + case CSR_PSKEY_UART_DFU_CONFIG_TX_ZERO_EN: + return "UART_DFU_CONFIG_TX_ZERO_EN"; + case CSR_PSKEY_UART_DFU_CONFIG_NON_BCSP_EN: + return "UART_DFU_CONFIG_NON_BCSP_EN"; + case CSR_PSKEY_UART_DFU_CONFIG_RX_RATE_DELAY: + return "UART_DFU_CONFIG_RX_RATE_DELAY"; + case CSR_PSKEY_AMUX_AIO0: + return "AMUX_AIO0"; + case CSR_PSKEY_AMUX_AIO1: + return "AMUX_AIO1"; + case CSR_PSKEY_AMUX_AIO2: + return "AMUX_AIO2"; + case CSR_PSKEY_AMUX_AIO3: + return "AMUX_AIO3"; + case CSR_PSKEY_LOCAL_NAME_SIMPLIFIED: + return "LOCAL_NAME_SIMPLIFIED"; + case CSR_PSKEY_EXTENDED_STUB: + return "EXTENDED_STUB"; + default: + return "UNKNOWN"; + } +} + +int csr_write_varid_valueless(int dd, uint16_t seqnum, uint16_t varid) +{ + unsigned char cmd[] = { 0x02, 0x00, 0x09, 0x00, + seqnum & 0xff, seqnum >> 8, varid & 0xff, varid >> 8, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + unsigned char cp[254], rp[254]; + struct hci_request rq; + + memset(&cp, 0, sizeof(cp)); + cp[0] = 0xc2; + memcpy(cp + 1, cmd, sizeof(cmd)); + + switch (varid) { + case CSR_VARID_COLD_RESET: + case CSR_VARID_WARM_RESET: + case CSR_VARID_COLD_HALT: + case CSR_VARID_WARM_HALT: + return hci_send_cmd(dd, OGF_VENDOR_CMD, 0x00, sizeof(cmd) + 1, cp); + } + + memset(&rq, 0, sizeof(rq)); + rq.ogf = OGF_VENDOR_CMD; + rq.ocf = 0x00; + rq.event = EVT_VENDOR; + rq.cparam = cp; + rq.clen = sizeof(cmd) + 1; + rq.rparam = rp; + rq.rlen = sizeof(rp); + + if (hci_send_req(dd, &rq, 2000) < 0) + return -1; + + if (rp[0] != 0xc2) { + errno = EIO; + return -1; + } + + if ((rp[9] + (rp[10] << 8)) != 0) { + errno = ENXIO; + return -1; + } + + return 0; +} + +int csr_write_varid_complex(int dd, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length) +{ + unsigned char cmd[] = { 0x02, 0x00, ((length / 2) + 5) & 0xff, ((length / 2) + 5) >> 8, + seqnum & 0xff, seqnum >> 8, varid & 0xff, varid >> 8, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + unsigned char cp[254], rp[254]; + struct hci_request rq; + + memset(&cp, 0, sizeof(cp)); + cp[0] = 0xc2; + memcpy(cp + 1, cmd, sizeof(cmd)); + memcpy(cp + 11, value, length); + + memset(&rq, 0, sizeof(rq)); + rq.ogf = OGF_VENDOR_CMD; + rq.ocf = 0x00; + rq.event = EVT_VENDOR; + rq.cparam = cp; + rq.clen = sizeof(cmd) + length + 1; + rq.rparam = rp; + rq.rlen = sizeof(rp); + + if (hci_send_req(dd, &rq, 2000) < 0) + return -1; + + if (rp[0] != 0xc2) { + errno = EIO; + return -1; + } + + if ((rp[9] + (rp[10] << 8)) != 0) { + errno = ENXIO; + return -1; + } + + return 0; +} + +int csr_read_varid_complex(int dd, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length) +{ + unsigned char cmd[] = { 0x00, 0x00, ((length / 2) + 5) & 0xff, ((length / 2) + 5) >> 8, + seqnum & 0xff, seqnum >> 8, varid & 0xff, varid >> 8, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + unsigned char cp[254], rp[254]; + struct hci_request rq; + + memset(&cp, 0, sizeof(cp)); + cp[0] = 0xc2; + memcpy(cp + 1, cmd, sizeof(cmd)); + memcpy(cp + 11, value, length); + + memset(&rq, 0, sizeof(rq)); + rq.ogf = OGF_VENDOR_CMD; + rq.ocf = 0x00; + rq.event = EVT_VENDOR; + rq.cparam = cp; + rq.clen = sizeof(cmd) + length + 1; + rq.rparam = rp; + rq.rlen = sizeof(rp); + + if (hci_send_req(dd, &rq, 2000) < 0) + return -1; + + if (rp[0] != 0xc2) { + errno = EIO; + return -1; + } + + if ((rp[9] + (rp[10] << 8)) != 0) { + errno = ENXIO; + return -1; + } + + memcpy(value, rp + 11, length); + + return 0; +} + +int csr_read_varid_uint16(int dd, uint16_t seqnum, uint16_t varid, uint16_t *value) +{ + unsigned char cmd[] = { 0x00, 0x00, 0x09, 0x00, + seqnum & 0xff, seqnum >> 8, varid & 0xff, varid >> 8, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + unsigned char cp[254], rp[254]; + struct hci_request rq; + + memset(&cp, 0, sizeof(cp)); + cp[0] = 0xc2; + memcpy(cp + 1, cmd, sizeof(cmd)); + + memset(&rq, 0, sizeof(rq)); + rq.ogf = OGF_VENDOR_CMD; + rq.ocf = 0x00; + rq.event = EVT_VENDOR; + rq.cparam = cp; + rq.clen = sizeof(cmd) + 1; + rq.rparam = rp; + rq.rlen = sizeof(rp); + + if (hci_send_req(dd, &rq, 2000) < 0) + return -1; + + if (rp[0] != 0xc2) { + errno = EIO; + return -1; + } + + if ((rp[9] + (rp[10] << 8)) != 0) { + errno = ENXIO; + return -1; + } + + *value = rp[11] + (rp[12] << 8); + + return 0; +} + +int csr_read_varid_uint32(int dd, uint16_t seqnum, uint16_t varid, uint32_t *value) +{ + unsigned char cmd[] = { 0x00, 0x00, 0x09, 0x00, + seqnum & 0xff, seqnum >> 8, varid & 0xff, varid >> 8, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + unsigned char cp[254], rp[254]; + struct hci_request rq; + + memset(&cp, 0, sizeof(cp)); + cp[0] = 0xc2; + memcpy(cp + 1, cmd, sizeof(cmd)); + + memset(&rq, 0, sizeof(rq)); + rq.ogf = OGF_VENDOR_CMD; + rq.ocf = 0x00; + rq.event = EVT_VENDOR; + rq.cparam = cp; + rq.clen = sizeof(cmd) + 1; + rq.rparam = rp; + rq.rlen = sizeof(rp); + + if (hci_send_req(dd, &rq, 2000) < 0) + return -1; + + if (rp[0] != 0xc2) { + errno = EIO; + return -1; + } + + if ((rp[9] + (rp[10] << 8)) != 0) { + errno = ENXIO; + return -1; + } + + *value = ((rp[11] + (rp[12] << 8)) << 16) + (rp[13] + (rp[14] << 8)); + + return 0; +} + +int csr_read_pskey_complex(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint8_t *value, uint16_t length) +{ + unsigned char cmd[] = { 0x00, 0x00, ((length / 2) + 8) & 0xff, ((length / 2) + 8) >> 8, + seqnum & 0xff, seqnum >> 8, 0x03, 0x70, 0x00, 0x00, + pskey & 0xff, pskey >> 8, + (length / 2) & 0xff, (length / 2) >> 8, + stores & 0xff, stores >> 8, 0x00, 0x00 }; + + unsigned char cp[254], rp[254]; + struct hci_request rq; + + memset(&cp, 0, sizeof(cp)); + cp[0] = 0xc2; + memcpy(cp + 1, cmd, sizeof(cmd)); + + memset(&rq, 0, sizeof(rq)); + rq.ogf = OGF_VENDOR_CMD; + rq.ocf = 0x00; + rq.event = EVT_VENDOR; + rq.cparam = cp; + rq.clen = sizeof(cmd) + length - 1; + rq.rparam = rp; + rq.rlen = sizeof(rp); + + if (hci_send_req(dd, &rq, 2000) < 0) + return -1; + + if (rp[0] != 0xc2) { + errno = EIO; + return -1; + } + + if ((rp[9] + (rp[10] << 8)) != 0) { + errno = ENXIO; + return -1; + } + + memcpy(value, rp + 17, length); + + return 0; +} + +int csr_write_pskey_complex(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint8_t *value, uint16_t length) +{ + unsigned char cmd[] = { 0x02, 0x00, ((length / 2) + 8) & 0xff, ((length / 2) + 8) >> 8, + seqnum & 0xff, seqnum >> 8, 0x03, 0x70, 0x00, 0x00, + pskey & 0xff, pskey >> 8, + (length / 2) & 0xff, (length / 2) >> 8, + stores & 0xff, stores >> 8, 0x00, 0x00 }; + + unsigned char cp[254], rp[254]; + struct hci_request rq; + + memset(&cp, 0, sizeof(cp)); + cp[0] = 0xc2; + memcpy(cp + 1, cmd, sizeof(cmd)); + + memcpy(cp + 17, value, length); + + memset(&rq, 0, sizeof(rq)); + rq.ogf = OGF_VENDOR_CMD; + rq.ocf = 0x00; + rq.event = EVT_VENDOR; + rq.cparam = cp; + rq.clen = sizeof(cmd) + length - 1; + rq.rparam = rp; + rq.rlen = sizeof(rp); + + if (hci_send_req(dd, &rq, 2000) < 0) + return -1; + + if (rp[0] != 0xc2) { + errno = EIO; + return -1; + } + + if ((rp[9] + (rp[10] << 8)) != 0) { + errno = ENXIO; + return -1; + } + + return 0; +} + +int csr_read_pskey_uint16(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint16_t *value) +{ + uint8_t array[2] = { 0x00, 0x00 }; + int err; + + err = csr_read_pskey_complex(dd, seqnum, pskey, stores, array, 2); + + *value = array[0] + (array[1] << 8); + + return err; +} + +int csr_write_pskey_uint16(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint16_t value) +{ + uint8_t array[2] = { value & 0xff, value >> 8 }; + + return csr_write_pskey_complex(dd, seqnum, pskey, stores, array, 2); +} + +int csr_read_pskey_uint32(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint32_t *value) +{ + uint8_t array[4] = { 0x00, 0x00, 0x00, 0x00 }; + int err; + + err = csr_read_pskey_complex(dd, seqnum, pskey, stores, array, 4); + + *value = ((array[0] + (array[1] << 8)) << 16) + + (array[2] + (array[3] << 8)); + + return err; +} + +int csr_write_pskey_uint32(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint32_t value) +{ + uint8_t array[4] = { (value & 0xff0000) >> 16, value >> 24, + value & 0xff, (value & 0xff00) >> 8 }; + + return csr_write_pskey_complex(dd, seqnum, pskey, stores, array, 4); +} + +int psr_put(uint16_t pskey, uint8_t *value, uint16_t size) +{ + struct psr_data *item; + + item = malloc(sizeof(*item)); + if (!item) + return -ENOMEM; + + item->pskey = pskey; + + if (size > 0) { + item->value = malloc(size); + if (!item->value) { + free(item); + return -ENOMEM; + } + + memcpy(item->value, value, size); + item->size = size; + } else { + item->value = NULL; + item->size = 0; + } + + item->next = NULL; + + if (!head) + head = item; + else + tail->next = item; + + tail = item; + + return 0; +} + +int psr_get(uint16_t *pskey, uint8_t *value, uint16_t *size) +{ + struct psr_data *item = head; + + if (!head) + return -ENOENT; + + *pskey = item->pskey; + + if (item->value) { + if (value && item->size > 0) + memcpy(value, item->value, item->size); + free(item->value); + *size = item->size; + } else + *size = 0; + + if (head == tail) + tail = NULL; + + head = head->next; + free(item); + + return 0; +} + +static int parse_line(char *str) +{ + uint8_t array[256]; + uint16_t value, pskey, length = 0; + char *off, *end; + + pskey = strtol(str + 1, NULL, 16); + off = strstr(str, "=") + 1; + if (!off) + return -EIO; + + while (1) { + value = strtol(off, &end, 16); + if (value == 0 && off == end) + break; + + array[length++] = value & 0xff; + array[length++] = value >> 8; + + if (*end == '\0') + break; + + off = end + 1; + } + + return psr_put(pskey, array, length); +} + +int psr_read(const char *filename) +{ + struct stat st; + char *str, *map, *off, *end; + int fd, err = 0; + + fd = open(filename, O_RDONLY); + if (fd < 0) + return fd; + + if (fstat(fd, &st) < 0) { + err = -errno; + goto close; + } + + map = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (!map || map == MAP_FAILED) { + err = -errno; + goto close; + } + + off = map; + + while (1) { + if (*off == '\r' || *off == '\n') { + off++; + continue; + } + + end = strpbrk(off, "\r\n"); + if (!end) + break; + + str = malloc(end - off + 1); + if (!str) + break; + + memset(str, 0, end - off + 1); + strncpy(str, off, end - off); + if (*str == '&') + parse_line(str); + + free(str); + off = end + 1; + } + + munmap(map, st.st_size); + +close: + close(fd); + + return err; +} + +int psr_print(void) +{ + uint8_t array[256]; + uint16_t pskey, length; + char *str, val[7]; + int i; + + while (1) { + if (psr_get(&pskey, array, &length) < 0) + break; + + str = csr_pskeytoval(pskey); + if (!strcasecmp(str, "UNKNOWN")) { + sprintf(val, "0x%04x", pskey); + str = NULL; + } + + printf("// %s%s\n&%04x =", str ? "PSKEY_" : "", + str ? str : val, pskey); + for (i = 0; i < length / 2; i++) + printf(" %02x%02x", array[i * 2 + 1], array[i * 2]); + printf("\n"); + } + + return 0; +} diff --git a/tools/csr.h b/tools/csr.h new file mode 100644 index 0000000..8b94d7b --- /dev/null +++ b/tools/csr.h @@ -0,0 +1,553 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2003-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include + +#define CSR_VARID_PS_CLR_ALL 0x000b /* valueless */ +#define CSR_VARID_PS_FACTORY_SET 0x000c /* valueless */ +#define CSR_VARID_PS_CLR_ALL_STORES 0x082d /* uint16 */ +#define CSR_VARID_BC01_STATUS 0x2801 /* uint16 */ +#define CSR_VARID_BUILDID 0x2819 /* uint16 */ +#define CSR_VARID_CHIPVER 0x281a /* uint16 */ +#define CSR_VARID_CHIPREV 0x281b /* uint16 */ +#define CSR_VARID_INTERFACE_VERSION 0x2825 /* uint16 */ +#define CSR_VARID_RAND 0x282a /* uint16 */ +#define CSR_VARID_MAX_CRYPT_KEY_LENGTH 0x282c /* uint16 */ +#define CSR_VARID_CHIPANAREV 0x2836 /* uint16 */ +#define CSR_VARID_BUILDID_LOADER 0x2838 /* uint16 */ +#define CSR_VARID_BT_CLOCK 0x2c00 /* uint32 */ +#define CSR_VARID_PS_NEXT 0x3005 /* complex */ +#define CSR_VARID_PS_SIZE 0x3006 /* complex */ +#define CSR_VARID_CRYPT_KEY_LENGTH 0x3008 /* complex */ +#define CSR_VARID_PICONET_INSTANCE 0x3009 /* complex */ +#define CSR_VARID_GET_CLR_EVT 0x300a /* complex */ +#define CSR_VARID_GET_NEXT_BUILDDEF 0x300b /* complex */ +#define CSR_VARID_PS_MEMORY_TYPE 0x3012 /* complex */ +#define CSR_VARID_READ_BUILD_NAME 0x301c /* complex */ +#define CSR_VARID_COLD_RESET 0x4001 /* valueless */ +#define CSR_VARID_WARM_RESET 0x4002 /* valueless */ +#define CSR_VARID_COLD_HALT 0x4003 /* valueless */ +#define CSR_VARID_WARM_HALT 0x4004 /* valueless */ +#define CSR_VARID_INIT_BT_STACK 0x4005 /* valueless */ +#define CSR_VARID_ACTIVATE_BT_STACK 0x4006 /* valueless */ +#define CSR_VARID_ENABLE_TX 0x4007 /* valueless */ +#define CSR_VARID_DISABLE_TX 0x4008 /* valueless */ +#define CSR_VARID_RECAL 0x4009 /* valueless */ +#define CSR_VARID_PS_FACTORY_RESTORE 0x400d /* valueless */ +#define CSR_VARID_PS_FACTORY_RESTORE_ALL 0x400e /* valueless */ +#define CSR_VARID_PS_DEFRAG_RESET 0x400f /* valueless */ +#define CSR_VARID_KILL_VM_APPLICATION 0x4010 /* valueless */ +#define CSR_VARID_HOPPING_ON 0x4011 /* valueless */ +#define CSR_VARID_CANCEL_PAGE 0x4012 /* valueless */ +#define CSR_VARID_PS_CLR 0x4818 /* uint16 */ +#define CSR_VARID_MAP_SCO_PCM 0x481c /* uint16 */ +#define CSR_VARID_SINGLE_CHAN 0x482e /* uint16 */ +#define CSR_VARID_RADIOTEST 0x5004 /* complex */ +#define CSR_VARID_PS_CLR_STORES 0x500c /* complex */ +#define CSR_VARID_NO_VARIABLE 0x6000 /* valueless */ +#define CSR_VARID_CONFIG_UART 0x6802 /* uint16 */ +#define CSR_VARID_PANIC_ARG 0x6805 /* uint16 */ +#define CSR_VARID_FAULT_ARG 0x6806 /* uint16 */ +#define CSR_VARID_MAX_TX_POWER 0x6827 /* int8 */ +#define CSR_VARID_DEFAULT_TX_POWER 0x682b /* int8 */ +#define CSR_VARID_PS 0x7003 /* complex */ + +#define CSR_PSKEY_BDADDR 0x0001 /* bdaddr / uint16[] = { 0x00A5A5, 0x5b, 0x0002 } */ +#define CSR_PSKEY_COUNTRYCODE 0x0002 /* uint16 */ +#define CSR_PSKEY_CLASSOFDEVICE 0x0003 /* bdcod */ +#define CSR_PSKEY_DEVICE_DRIFT 0x0004 /* uint16 */ +#define CSR_PSKEY_DEVICE_JITTER 0x0005 /* uint16 */ +#define CSR_PSKEY_MAX_ACLS 0x000d /* uint16 */ +#define CSR_PSKEY_MAX_SCOS 0x000e /* uint16 */ +#define CSR_PSKEY_MAX_REMOTE_MASTERS 0x000f /* uint16 */ +#define CSR_PSKEY_ENABLE_MASTERY_WITH_SLAVERY 0x0010 /* bool */ +#define CSR_PSKEY_H_HC_FC_MAX_ACL_PKT_LEN 0x0011 /* uint16 */ +#define CSR_PSKEY_H_HC_FC_MAX_SCO_PKT_LEN 0x0012 /* uint8 */ +#define CSR_PSKEY_H_HC_FC_MAX_ACL_PKTS 0x0013 /* uint16 */ +#define CSR_PSKEY_H_HC_FC_MAX_SCO_PKTS 0x0014 /* uint16 */ +#define CSR_PSKEY_LC_FC_BUFFER_LOW_WATER_MARK 0x0015 /* lc_fc_lwm */ +#define CSR_PSKEY_LC_MAX_TX_POWER 0x0017 /* int16 */ +#define CSR_PSKEY_TX_GAIN_RAMP 0x001d /* uint16 */ +#define CSR_PSKEY_LC_POWER_TABLE 0x001e /* power_setting[] */ +#define CSR_PSKEY_LC_PEER_POWER_PERIOD 0x001f /* TIME */ +#define CSR_PSKEY_LC_FC_POOLS_LOW_WATER_MARK 0x0020 /* lc_fc_lwm */ +#define CSR_PSKEY_LC_DEFAULT_TX_POWER 0x0021 /* int16 */ +#define CSR_PSKEY_LC_RSSI_GOLDEN_RANGE 0x0022 /* uint8 */ +#define CSR_PSKEY_LC_COMBO_DISABLE_PIO_MASK 0x0028 /* uint16[] */ +#define CSR_PSKEY_LC_COMBO_PRIORITY_PIO_MASK 0x0029 /* uint16[] */ +#define CSR_PSKEY_LC_COMBO_DOT11_CHANNEL_PIO_BASE 0x002a /* uint16 */ +#define CSR_PSKEY_LC_COMBO_DOT11_BLOCK_CHANNELS 0x002b /* uint16 */ +#define CSR_PSKEY_LC_MAX_TX_POWER_NO_RSSI 0x002d /* int8 */ +#define CSR_PSKEY_LC_CONNECTION_RX_WINDOW 0x002e /* uint16 */ +#define CSR_PSKEY_LC_COMBO_DOT11_TX_PROTECTION_MODE 0x0030 /* uint16 */ +#define CSR_PSKEY_LC_ENHANCED_POWER_TABLE 0x0031 /* enhanced_power_setting[] */ +#define CSR_PSKEY_LC_WIDEBAND_RSSI_CONFIG 0x0032 /* wideband_rssi_config */ +#define CSR_PSKEY_LC_COMBO_DOT11_PRIORITY_LEAD 0x0033 /* uint16 */ +#define CSR_PSKEY_BT_CLOCK_INIT 0x0034 /* uint32 */ +#define CSR_PSKEY_TX_MR_MOD_DELAY 0x0038 /* uint8 */ +#define CSR_PSKEY_RX_MR_SYNC_TIMING 0x0039 /* uint16 */ +#define CSR_PSKEY_RX_MR_SYNC_CONFIG 0x003a /* uint16 */ +#define CSR_PSKEY_LC_LOST_SYNC_SLOTS 0x003b /* uint16 */ +#define CSR_PSKEY_RX_MR_SAMP_CONFIG 0x003c /* uint16 */ +#define CSR_PSKEY_AGC_HYST_LEVELS 0x003d /* agc_hyst_config */ +#define CSR_PSKEY_RX_LEVEL_LOW_SIGNAL 0x003e /* uint16 */ +#define CSR_PSKEY_AGC_IQ_LVL_VALUES 0x003f /* IQ_LVL_VAL[] */ +#define CSR_PSKEY_MR_FTRIM_OFFSET_12DB 0x0040 /* uint16 */ +#define CSR_PSKEY_MR_FTRIM_OFFSET_6DB 0x0041 /* uint16 */ +#define CSR_PSKEY_NO_CAL_ON_BOOT 0x0042 /* bool */ +#define CSR_PSKEY_RSSI_HI_TARGET 0x0043 /* uint8 */ +#define CSR_PSKEY_PREFERRED_MIN_ATTENUATION 0x0044 /* uint8 */ +#define CSR_PSKEY_LC_COMBO_DOT11_PRIORITY_OVERRIDE 0x0045 /* bool */ +#define CSR_PSKEY_LC_MULTISLOT_HOLDOFF 0x0047 /* TIME */ +#define CSR_PSKEY_FREE_KEY_PIGEON_HOLE 0x00c9 /* uint16 */ +#define CSR_PSKEY_LINK_KEY_BD_ADDR0 0x00ca /* LM_LINK_KEY_BD_ADDR_T */ +#define CSR_PSKEY_LINK_KEY_BD_ADDR1 0x00cb /* LM_LINK_KEY_BD_ADDR_T */ +#define CSR_PSKEY_LINK_KEY_BD_ADDR2 0x00cc /* LM_LINK_KEY_BD_ADDR_T */ +#define CSR_PSKEY_LINK_KEY_BD_ADDR3 0x00cd /* LM_LINK_KEY_BD_ADDR_T */ +#define CSR_PSKEY_LINK_KEY_BD_ADDR4 0x00ce /* LM_LINK_KEY_BD_ADDR_T */ +#define CSR_PSKEY_LINK_KEY_BD_ADDR5 0x00cf /* LM_LINK_KEY_BD_ADDR_T */ +#define CSR_PSKEY_LINK_KEY_BD_ADDR6 0x00d0 /* LM_LINK_KEY_BD_ADDR_T */ +#define CSR_PSKEY_LINK_KEY_BD_ADDR7 0x00d1 /* LM_LINK_KEY_BD_ADDR_T */ +#define CSR_PSKEY_LINK_KEY_BD_ADDR8 0x00d2 /* LM_LINK_KEY_BD_ADDR_T */ +#define CSR_PSKEY_LINK_KEY_BD_ADDR9 0x00d3 /* LM_LINK_KEY_BD_ADDR_T */ +#define CSR_PSKEY_LINK_KEY_BD_ADDR10 0x00d4 /* LM_LINK_KEY_BD_ADDR_T */ +#define CSR_PSKEY_LINK_KEY_BD_ADDR11 0x00d5 /* LM_LINK_KEY_BD_ADDR_T */ +#define CSR_PSKEY_LINK_KEY_BD_ADDR12 0x00d6 /* LM_LINK_KEY_BD_ADDR_T */ +#define CSR_PSKEY_LINK_KEY_BD_ADDR13 0x00d7 /* LM_LINK_KEY_BD_ADDR_T */ +#define CSR_PSKEY_LINK_KEY_BD_ADDR14 0x00d8 /* LM_LINK_KEY_BD_ADDR_T */ +#define CSR_PSKEY_LINK_KEY_BD_ADDR15 0x00d9 /* LM_LINK_KEY_BD_ADDR_T */ +#define CSR_PSKEY_ENC_KEY_LMIN 0x00da /* uint16 */ +#define CSR_PSKEY_ENC_KEY_LMAX 0x00db /* uint16 */ +#define CSR_PSKEY_LOCAL_SUPPORTED_FEATURES 0x00ef /* uint16[] = { 0xffff, 0xfe8f, 0xf99b, 0x8000 }*/ +#define CSR_PSKEY_LM_USE_UNIT_KEY 0x00f0 /* bool */ +#define CSR_PSKEY_HCI_NOP_DISABLE 0x00f2 /* bool */ +#define CSR_PSKEY_LM_MAX_EVENT_FILTERS 0x00f4 /* uint8 */ +#define CSR_PSKEY_LM_USE_ENC_MODE_BROADCAST 0x00f5 /* bool */ +#define CSR_PSKEY_LM_TEST_SEND_ACCEPTED_TWICE 0x00f6 /* bool */ +#define CSR_PSKEY_LM_MAX_PAGE_HOLD_TIME 0x00f7 /* uint16 */ +#define CSR_PSKEY_AFH_ADAPTATION_RESPONSE_TIME 0x00f8 /* uint16 */ +#define CSR_PSKEY_AFH_OPTIONS 0x00f9 /* uint16 */ +#define CSR_PSKEY_AFH_RSSI_RUN_PERIOD 0x00fa /* uint16 */ +#define CSR_PSKEY_AFH_REENABLE_CHANNEL_TIME 0x00fb /* uint16 */ +#define CSR_PSKEY_NO_DROP_ON_ACR_MS_FAIL 0x00fc /* bool */ +#define CSR_PSKEY_MAX_PRIVATE_KEYS 0x00fd /* uint8 */ +#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR0 0x00fe /* LM_LINK_KEY_BD_ADDR_T */ +#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR1 0x00ff /* LM_LINK_KEY_BD_ADDR_T */ +#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR2 0x0100 /* LM_LINK_KEY_BD_ADDR_T */ +#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR3 0x0101 /* LM_LINK_KEY_BD_ADDR_T */ +#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR4 0x0102 /* LM_LINK_KEY_BD_ADDR_T */ +#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR5 0x0103 /* LM_LINK_KEY_BD_ADDR_T */ +#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR6 0x0104 /* LM_LINK_KEY_BD_ADDR_T */ +#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR7 0x0105 /* LM_LINK_KEY_BD_ADDR_T */ +#define CSR_PSKEY_LOCAL_SUPPORTED_COMMANDS 0x0106 /* uint16[] = { 0xffff, 0x03ff, 0xfffe, 0xffff, 0xffff, 0xffff, 0x0ff3, 0xfff8, 0x003f } */ +#define CSR_PSKEY_LM_MAX_ABSENCE_INDEX 0x0107 /* uint8 */ +#define CSR_PSKEY_DEVICE_NAME 0x0108 /* uint16[] */ +#define CSR_PSKEY_AFH_RSSI_THRESHOLD 0x0109 /* uint16 */ +#define CSR_PSKEY_LM_CASUAL_SCAN_INTERVAL 0x010a /* uint16 */ +#define CSR_PSKEY_AFH_MIN_MAP_CHANGE 0x010b /* uint16[] */ +#define CSR_PSKEY_AFH_RSSI_LP_RUN_PERIOD 0x010c /* uint16 */ +#define CSR_PSKEY_HCI_LMP_LOCAL_VERSION 0x010d /* uint16 */ +#define CSR_PSKEY_LMP_REMOTE_VERSION 0x010e /* uint8 */ +#define CSR_PSKEY_HOLD_ERROR_MESSAGE_NUMBER 0x0113 /* uint16 */ +#define CSR_PSKEY_DFU_ATTRIBUTES 0x0136 /* uint8 */ +#define CSR_PSKEY_DFU_DETACH_TO 0x0137 /* uint16 */ +#define CSR_PSKEY_DFU_TRANSFER_SIZE 0x0138 /* uint16 */ +#define CSR_PSKEY_DFU_ENABLE 0x0139 /* bool */ +#define CSR_PSKEY_DFU_LIN_REG_ENABLE 0x013a /* bool */ +#define CSR_PSKEY_DFUENC_VMAPP_PK_MODULUS_MSB 0x015e /* uint16[] */ +#define CSR_PSKEY_DFUENC_VMAPP_PK_MODULUS_LSB 0x015f /* uint16[] */ +#define CSR_PSKEY_DFUENC_VMAPP_PK_M_DASH 0x0160 /* uint16 */ +#define CSR_PSKEY_DFUENC_VMAPP_PK_R2N_MSB 0x0161 /* uint16[] */ +#define CSR_PSKEY_DFUENC_VMAPP_PK_R2N_LSB 0x0162 /* uint16[] */ +#define CSR_PSKEY_BCSP_LM_PS_BLOCK 0x0192 /* BCSP_LM_PS_BLOCK */ +#define CSR_PSKEY_HOSTIO_FC_PS_BLOCK 0x0193 /* HOSTIO_FC_PS_BLOCK */ +#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO0 0x0194 /* PROTOCOL_INFO */ +#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO1 0x0195 /* PROTOCOL_INFO */ +#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO2 0x0196 /* PROTOCOL_INFO */ +#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO3 0x0197 /* PROTOCOL_INFO */ +#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO4 0x0198 /* PROTOCOL_INFO */ +#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO5 0x0199 /* PROTOCOL_INFO */ +#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO6 0x019a /* PROTOCOL_INFO */ +#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO7 0x019b /* PROTOCOL_INFO */ +#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO8 0x019c /* PROTOCOL_INFO */ +#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO9 0x019d /* PROTOCOL_INFO */ +#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO10 0x019e /* PROTOCOL_INFO */ +#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO11 0x019f /* PROTOCOL_INFO */ +#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO12 0x01a0 /* PROTOCOL_INFO */ +#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO13 0x01a1 /* PROTOCOL_INFO */ +#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO14 0x01a2 /* PROTOCOL_INFO */ +#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO15 0x01a3 /* PROTOCOL_INFO */ +#define CSR_PSKEY_HOSTIO_UART_RESET_TIMEOUT 0x01a4 /* TIME */ +#define CSR_PSKEY_HOSTIO_USE_HCI_EXTN 0x01a5 /* bool */ +#define CSR_PSKEY_HOSTIO_USE_HCI_EXTN_CCFC 0x01a6 /* bool */ +#define CSR_PSKEY_HOSTIO_HCI_EXTN_PAYLOAD_SIZE 0x01a7 /* uint16 */ +#define CSR_PSKEY_BCSP_LM_CNF_CNT_LIMIT 0x01aa /* uint16 */ +#define CSR_PSKEY_HOSTIO_MAP_SCO_PCM 0x01ab /* bool */ +#define CSR_PSKEY_HOSTIO_AWKWARD_PCM_SYNC 0x01ac /* bool */ +#define CSR_PSKEY_HOSTIO_BREAK_POLL_PERIOD 0x01ad /* TIME */ +#define CSR_PSKEY_HOSTIO_MIN_UART_HCI_SCO_SIZE 0x01ae /* uint16 */ +#define CSR_PSKEY_HOSTIO_MAP_SCO_CODEC 0x01b0 /* bool */ +#define CSR_PSKEY_PCM_CVSD_TX_HI_FREQ_BOOST 0x01b1 /* uint16 */ +#define CSR_PSKEY_PCM_CVSD_RX_HI_FREQ_BOOST 0x01b2 /* uint16 */ +#define CSR_PSKEY_PCM_CONFIG32 0x01b3 /* uint32 */ +#define CSR_PSKEY_USE_OLD_BCSP_LE 0x01b4 /* uint16 */ +#define CSR_PSKEY_PCM_CVSD_USE_NEW_FILTER 0x01b5 /* bool */ +#define CSR_PSKEY_PCM_FORMAT 0x01b6 /* uint16 */ +#define CSR_PSKEY_CODEC_OUT_GAIN 0x01b7 /* uint16 */ +#define CSR_PSKEY_CODEC_IN_GAIN 0x01b8 /* uint16 */ +#define CSR_PSKEY_CODEC_PIO 0x01b9 /* uint16 */ +#define CSR_PSKEY_PCM_LOW_JITTER_CONFIG 0x01ba /* uint32 */ +#define CSR_PSKEY_HOSTIO_SCO_PCM_THRESHOLDS 0x01bb /* uint16[] */ +#define CSR_PSKEY_HOSTIO_SCO_HCI_THRESHOLDS 0x01bc /* uint16[] */ +#define CSR_PSKEY_HOSTIO_MAP_SCO_PCM_SLOT 0x01bd /* uint16 */ +#define CSR_PSKEY_UART_BAUDRATE 0x01be /* uint16 */ +#define CSR_PSKEY_UART_CONFIG_BCSP 0x01bf /* uint16 */ +#define CSR_PSKEY_UART_CONFIG_H4 0x01c0 /* uint16 */ +#define CSR_PSKEY_UART_CONFIG_H5 0x01c1 /* uint16 */ +#define CSR_PSKEY_UART_CONFIG_USR 0x01c2 /* uint16 */ +#define CSR_PSKEY_UART_TX_CRCS 0x01c3 /* bool */ +#define CSR_PSKEY_UART_ACK_TIMEOUT 0x01c4 /* uint16 */ +#define CSR_PSKEY_UART_TX_MAX_ATTEMPTS 0x01c5 /* uint16 */ +#define CSR_PSKEY_UART_TX_WINDOW_SIZE 0x01c6 /* uint16 */ +#define CSR_PSKEY_UART_HOST_WAKE 0x01c7 /* uint16[] */ +#define CSR_PSKEY_HOSTIO_THROTTLE_TIMEOUT 0x01c8 /* TIME */ +#define CSR_PSKEY_PCM_ALWAYS_ENABLE 0x01c9 /* bool */ +#define CSR_PSKEY_UART_HOST_WAKE_SIGNAL 0x01ca /* uint16 */ +#define CSR_PSKEY_UART_CONFIG_H4DS 0x01cb /* uint16 */ +#define CSR_PSKEY_H4DS_WAKE_DURATION 0x01cc /* uint16 */ +#define CSR_PSKEY_H4DS_MAXWU 0x01cd /* uint16 */ +#define CSR_PSKEY_H4DS_LE_TIMER_PERIOD 0x01cf /* uint16 */ +#define CSR_PSKEY_H4DS_TWU_TIMER_PERIOD 0x01d0 /* uint16 */ +#define CSR_PSKEY_H4DS_UART_IDLE_TIMER_PERIOD 0x01d1 /* uint16 */ +#define CSR_PSKEY_ANA_FTRIM 0x01f6 /* uint16 */ +#define CSR_PSKEY_WD_TIMEOUT 0x01f7 /* TIME */ +#define CSR_PSKEY_WD_PERIOD 0x01f8 /* TIME */ +#define CSR_PSKEY_HOST_INTERFACE 0x01f9 /* phys_bus */ +#define CSR_PSKEY_HQ_HOST_TIMEOUT 0x01fb /* TIME */ +#define CSR_PSKEY_HQ_ACTIVE 0x01fc /* bool */ +#define CSR_PSKEY_BCCMD_SECURITY_ACTIVE 0x01fd /* bool */ +#define CSR_PSKEY_ANA_FREQ 0x01fe /* uint16 */ +#define CSR_PSKEY_PIO_PROTECT_MASK 0x0202 /* uint16 */ +#define CSR_PSKEY_PMALLOC_SIZES 0x0203 /* uint16[] */ +#define CSR_PSKEY_UART_BAUD_RATE 0x0204 /* uint16 */ +#define CSR_PSKEY_UART_CONFIG 0x0205 /* uint16 */ +#define CSR_PSKEY_STUB 0x0207 /* uint16 */ +#define CSR_PSKEY_TXRX_PIO_CONTROL 0x0209 /* uint16 */ +#define CSR_PSKEY_ANA_RX_LEVEL 0x020b /* uint16 */ +#define CSR_PSKEY_ANA_RX_FTRIM 0x020c /* uint16 */ +#define CSR_PSKEY_PSBC_DATA_VERSION 0x020d /* uint16 */ +#define CSR_PSKEY_PCM0_ATTENUATION 0x020f /* uint16 */ +#define CSR_PSKEY_LO_LVL_MAX 0x0211 /* uint16 */ +#define CSR_PSKEY_LO_ADC_AMPL_MIN 0x0212 /* uint16 */ +#define CSR_PSKEY_LO_ADC_AMPL_MAX 0x0213 /* uint16 */ +#define CSR_PSKEY_IQ_TRIM_CHANNEL 0x0214 /* uint16 */ +#define CSR_PSKEY_IQ_TRIM_GAIN 0x0215 /* uint16 */ +#define CSR_PSKEY_IQ_TRIM_ENABLE 0x0216 /* iq_trim_enable_flag */ +#define CSR_PSKEY_TX_OFFSET_HALF_MHZ 0x0217 /* int16 */ +#define CSR_PSKEY_GBL_MISC_ENABLES 0x0221 /* uint16 */ +#define CSR_PSKEY_UART_SLEEP_TIMEOUT 0x0222 /* uint16 */ +#define CSR_PSKEY_DEEP_SLEEP_STATE 0x0229 /* deep_sleep_state */ +#define CSR_PSKEY_IQ_ENABLE_PHASE_TRIM 0x022d /* bool */ +#define CSR_PSKEY_HCI_HANDLE_FREEZE_PERIOD 0x0237 /* TIME */ +#define CSR_PSKEY_MAX_FROZEN_HCI_HANDLES 0x0238 /* uint16 */ +#define CSR_PSKEY_PAGETABLE_DESTRUCTION_DELAY 0x0239 /* TIME */ +#define CSR_PSKEY_IQ_TRIM_PIO_SETTINGS 0x023a /* uint8 */ +#define CSR_PSKEY_USE_EXTERNAL_CLOCK 0x023b /* bool */ +#define CSR_PSKEY_DEEP_SLEEP_WAKE_CTS 0x023c /* uint16 */ +#define CSR_PSKEY_FC_HC2H_FLUSH_DELAY 0x023d /* TIME */ +#define CSR_PSKEY_RX_HIGHSIDE 0x023e /* bool */ +#define CSR_PSKEY_TX_PRE_LVL 0x0240 /* uint8 */ +#define CSR_PSKEY_RX_SINGLE_ENDED 0x0242 /* bool */ +#define CSR_PSKEY_TX_FILTER_CONFIG 0x0243 /* uint32 */ +#define CSR_PSKEY_CLOCK_REQUEST_ENABLE 0x0246 /* uint16 */ +#define CSR_PSKEY_RX_MIN_ATTEN 0x0249 /* uint16 */ +#define CSR_PSKEY_XTAL_TARGET_AMPLITUDE 0x024b /* uint8 */ +#define CSR_PSKEY_PCM_MIN_CPU_CLOCK 0x024d /* uint16 */ +#define CSR_PSKEY_HOST_INTERFACE_PIO_USB 0x0250 /* uint16 */ +#define CSR_PSKEY_CPU_IDLE_MODE 0x0251 /* cpu_idle_mode */ +#define CSR_PSKEY_DEEP_SLEEP_CLEAR_RTS 0x0252 /* bool */ +#define CSR_PSKEY_RF_RESONANCE_TRIM 0x0254 /* uint16 */ +#define CSR_PSKEY_DEEP_SLEEP_PIO_WAKE 0x0255 /* uint16 */ +#define CSR_PSKEY_DRAIN_BORE_TIMERS 0x0256 /* uint32[] */ +#define CSR_PSKEY_DRAIN_TX_POWER_BASE 0x0257 /* uint16 */ +#define CSR_PSKEY_MODULE_ID 0x0259 /* uint32 */ +#define CSR_PSKEY_MODULE_DESIGN 0x025a /* uint16 */ +#define CSR_PSKEY_MODULE_SECURITY_CODE 0x025c /* uint16[] */ +#define CSR_PSKEY_VM_DISABLE 0x025d /* bool */ +#define CSR_PSKEY_MOD_MANUF0 0x025e /* uint16[] */ +#define CSR_PSKEY_MOD_MANUF1 0x025f /* uint16[] */ +#define CSR_PSKEY_MOD_MANUF2 0x0260 /* uint16[] */ +#define CSR_PSKEY_MOD_MANUF3 0x0261 /* uint16[] */ +#define CSR_PSKEY_MOD_MANUF4 0x0262 /* uint16[] */ +#define CSR_PSKEY_MOD_MANUF5 0x0263 /* uint16[] */ +#define CSR_PSKEY_MOD_MANUF6 0x0264 /* uint16[] */ +#define CSR_PSKEY_MOD_MANUF7 0x0265 /* uint16[] */ +#define CSR_PSKEY_MOD_MANUF8 0x0266 /* uint16[] */ +#define CSR_PSKEY_MOD_MANUF9 0x0267 /* uint16[] */ +#define CSR_PSKEY_DUT_VM_DISABLE 0x0268 /* bool */ +#define CSR_PSKEY_USR0 0x028a /* uint16[] */ +#define CSR_PSKEY_USR1 0x028b /* uint16[] */ +#define CSR_PSKEY_USR2 0x028c /* uint16[] */ +#define CSR_PSKEY_USR3 0x028d /* uint16[] */ +#define CSR_PSKEY_USR4 0x028e /* uint16[] */ +#define CSR_PSKEY_USR5 0x028f /* uint16[] */ +#define CSR_PSKEY_USR6 0x0290 /* uint16[] */ +#define CSR_PSKEY_USR7 0x0291 /* uint16[] */ +#define CSR_PSKEY_USR8 0x0292 /* uint16[] */ +#define CSR_PSKEY_USR9 0x0293 /* uint16[] */ +#define CSR_PSKEY_USR10 0x0294 /* uint16[] */ +#define CSR_PSKEY_USR11 0x0295 /* uint16[] */ +#define CSR_PSKEY_USR12 0x0296 /* uint16[] */ +#define CSR_PSKEY_USR13 0x0297 /* uint16[] */ +#define CSR_PSKEY_USR14 0x0298 /* uint16[] */ +#define CSR_PSKEY_USR15 0x0299 /* uint16[] */ +#define CSR_PSKEY_USR16 0x029a /* uint16[] */ +#define CSR_PSKEY_USR17 0x029b /* uint16[] */ +#define CSR_PSKEY_USR18 0x029c /* uint16[] */ +#define CSR_PSKEY_USR19 0x029d /* uint16[] */ +#define CSR_PSKEY_USR20 0x029e /* uint16[] */ +#define CSR_PSKEY_USR21 0x029f /* uint16[] */ +#define CSR_PSKEY_USR22 0x02a0 /* uint16[] */ +#define CSR_PSKEY_USR23 0x02a1 /* uint16[] */ +#define CSR_PSKEY_USR24 0x02a2 /* uint16[] */ +#define CSR_PSKEY_USR25 0x02a3 /* uint16[] */ +#define CSR_PSKEY_USR26 0x02a4 /* uint16[] */ +#define CSR_PSKEY_USR27 0x02a5 /* uint16[] */ +#define CSR_PSKEY_USR28 0x02a6 /* uint16[] */ +#define CSR_PSKEY_USR29 0x02a7 /* uint16[] */ +#define CSR_PSKEY_USR30 0x02a8 /* uint16[] */ +#define CSR_PSKEY_USR31 0x02a9 /* uint16[] */ +#define CSR_PSKEY_USR32 0x02aa /* uint16[] */ +#define CSR_PSKEY_USR33 0x02ab /* uint16[] */ +#define CSR_PSKEY_USR34 0x02ac /* uint16[] */ +#define CSR_PSKEY_USR35 0x02ad /* uint16[] */ +#define CSR_PSKEY_USR36 0x02ae /* uint16[] */ +#define CSR_PSKEY_USR37 0x02af /* uint16[] */ +#define CSR_PSKEY_USR38 0x02b0 /* uint16[] */ +#define CSR_PSKEY_USR39 0x02b1 /* uint16[] */ +#define CSR_PSKEY_USR40 0x02b2 /* uint16[] */ +#define CSR_PSKEY_USR41 0x02b3 /* uint16[] */ +#define CSR_PSKEY_USR42 0x02b4 /* uint16[] */ +#define CSR_PSKEY_USR43 0x02b5 /* uint16[] */ +#define CSR_PSKEY_USR44 0x02b6 /* uint16[] */ +#define CSR_PSKEY_USR45 0x02b7 /* uint16[] */ +#define CSR_PSKEY_USR46 0x02b8 /* uint16[] */ +#define CSR_PSKEY_USR47 0x02b9 /* uint16[] */ +#define CSR_PSKEY_USR48 0x02ba /* uint16[] */ +#define CSR_PSKEY_USR49 0x02bb /* uint16[] */ +#define CSR_PSKEY_USB_VERSION 0x02bc /* uint16 */ +#define CSR_PSKEY_USB_DEVICE_CLASS_CODES 0x02bd /* usbclass */ +#define CSR_PSKEY_USB_VENDOR_ID 0x02be /* uint16 */ +#define CSR_PSKEY_USB_PRODUCT_ID 0x02bf /* uint16 */ +#define CSR_PSKEY_USB_MANUF_STRING 0x02c1 /* unicodestring */ +#define CSR_PSKEY_USB_PRODUCT_STRING 0x02c2 /* unicodestring */ +#define CSR_PSKEY_USB_SERIAL_NUMBER_STRING 0x02c3 /* unicodestring */ +#define CSR_PSKEY_USB_CONFIG_STRING 0x02c4 /* unicodestring */ +#define CSR_PSKEY_USB_ATTRIBUTES 0x02c5 /* uint8 */ +#define CSR_PSKEY_USB_MAX_POWER 0x02c6 /* uint16 */ +#define CSR_PSKEY_USB_BT_IF_CLASS_CODES 0x02c7 /* usbclass */ +#define CSR_PSKEY_USB_LANGID 0x02c9 /* uint16 */ +#define CSR_PSKEY_USB_DFU_CLASS_CODES 0x02ca /* usbclass */ +#define CSR_PSKEY_USB_DFU_PRODUCT_ID 0x02cb /* uint16 */ +#define CSR_PSKEY_USB_PIO_DETACH 0x02ce /* uint16 */ +#define CSR_PSKEY_USB_PIO_WAKEUP 0x02cf /* uint16 */ +#define CSR_PSKEY_USB_PIO_PULLUP 0x02d0 /* uint16 */ +#define CSR_PSKEY_USB_PIO_VBUS 0x02d1 /* uint16 */ +#define CSR_PSKEY_USB_PIO_WAKE_TIMEOUT 0x02d2 /* uint16 */ +#define CSR_PSKEY_USB_PIO_RESUME 0x02d3 /* uint16 */ +#define CSR_PSKEY_USB_BT_SCO_IF_CLASS_CODES 0x02d4 /* usbclass */ +#define CSR_PSKEY_USB_SUSPEND_PIO_LEVEL 0x02d5 /* uint16 */ +#define CSR_PSKEY_USB_SUSPEND_PIO_DIR 0x02d6 /* uint16 */ +#define CSR_PSKEY_USB_SUSPEND_PIO_MASK 0x02d7 /* uint16 */ +#define CSR_PSKEY_USB_ENDPOINT_0_MAX_PACKET_SIZE 0x02d8 /* uint8 */ +#define CSR_PSKEY_USB_CONFIG 0x02d9 /* uint16 */ +#define CSR_PSKEY_RADIOTEST_ATTEN_INIT 0x0320 /* uint16 */ +#define CSR_PSKEY_RADIOTEST_FIRST_TRIM_TIME 0x0326 /* TIME */ +#define CSR_PSKEY_RADIOTEST_SUBSEQUENT_TRIM_TIME 0x0327 /* TIME */ +#define CSR_PSKEY_RADIOTEST_LO_LVL_TRIM_ENABLE 0x0328 /* bool */ +#define CSR_PSKEY_RADIOTEST_DISABLE_MODULATION 0x032c /* bool */ +#define CSR_PSKEY_RFCOMM_FCON_THRESHOLD 0x0352 /* uint16 */ +#define CSR_PSKEY_RFCOMM_FCOFF_THRESHOLD 0x0353 /* uint16 */ +#define CSR_PSKEY_IPV6_STATIC_ADDR 0x0354 /* uint16[] */ +#define CSR_PSKEY_IPV4_STATIC_ADDR 0x0355 /* uint32 */ +#define CSR_PSKEY_IPV6_STATIC_PREFIX_LEN 0x0356 /* uint8 */ +#define CSR_PSKEY_IPV6_STATIC_ROUTER_ADDR 0x0357 /* uint16[] */ +#define CSR_PSKEY_IPV4_STATIC_SUBNET_MASK 0x0358 /* uint32 */ +#define CSR_PSKEY_IPV4_STATIC_ROUTER_ADDR 0x0359 /* uint32 */ +#define CSR_PSKEY_MDNS_NAME 0x035a /* char[] */ +#define CSR_PSKEY_FIXED_PIN 0x035b /* uint8[] */ +#define CSR_PSKEY_MDNS_PORT 0x035c /* uint16 */ +#define CSR_PSKEY_MDNS_TTL 0x035d /* uint8 */ +#define CSR_PSKEY_MDNS_IPV4_ADDR 0x035e /* uint32 */ +#define CSR_PSKEY_ARP_CACHE_TIMEOUT 0x035f /* uint16 */ +#define CSR_PSKEY_HFP_POWER_TABLE 0x0360 /* uint16[] */ +#define CSR_PSKEY_DRAIN_BORE_TIMER_COUNTERS 0x03e7 /* uint32[] */ +#define CSR_PSKEY_DRAIN_BORE_COUNTERS 0x03e6 /* uint32[] */ +#define CSR_PSKEY_LOOP_FILTER_TRIM 0x03e4 /* uint16 */ +#define CSR_PSKEY_DRAIN_BORE_CURRENT_PEAK 0x03e3 /* uint32[] */ +#define CSR_PSKEY_VM_E2_CACHE_LIMIT 0x03e2 /* uint16 */ +#define CSR_PSKEY_FORCE_16MHZ_REF_PIO 0x03e1 /* uint16 */ +#define CSR_PSKEY_CDMA_LO_REF_LIMITS 0x03df /* uint16 */ +#define CSR_PSKEY_CDMA_LO_ERROR_LIMITS 0x03de /* uint16 */ +#define CSR_PSKEY_CLOCK_STARTUP_DELAY 0x03dd /* uint16 */ +#define CSR_PSKEY_DEEP_SLEEP_CORRECTION_FACTOR 0x03dc /* int16 */ +#define CSR_PSKEY_TEMPERATURE_CALIBRATION 0x03db /* temperature_calibration */ +#define CSR_PSKEY_TEMPERATURE_VS_DELTA_INTERNAL_PA 0x03da /* temperature_calibration[] */ +#define CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_PRE_LVL 0x03d9 /* temperature_calibration[] */ +#define CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB 0x03d8 /* temperature_calibration[] */ +#define CSR_PSKEY_TEMPERATURE_VS_DELTA_ANA_FTRIM 0x03d7 /* temperature_calibration[] */ +#define CSR_PSKEY_TEST_DELTA_OFFSET 0x03d6 /* uint16 */ +#define CSR_PSKEY_RX_DYNAMIC_LVL_OFFSET 0x03d4 /* uint16 */ +#define CSR_PSKEY_TEST_FORCE_OFFSET 0x03d3 /* bool */ +#define CSR_PSKEY_RF_TRAP_BAD_DIVISION_RATIOS 0x03cf /* uint16 */ +#define CSR_PSKEY_RADIOTEST_CDMA_LO_REF_LIMITS 0x03ce /* uint16 */ +#define CSR_PSKEY_INITIAL_BOOTMODE 0x03cd /* int16 */ +#define CSR_PSKEY_ONCHIP_HCI_CLIENT 0x03cc /* bool */ +#define CSR_PSKEY_RX_ATTEN_BACKOFF 0x03ca /* uint16 */ +#define CSR_PSKEY_RX_ATTEN_UPDATE_RATE 0x03c9 /* uint16 */ +#define CSR_PSKEY_SYNTH_TXRX_THRESHOLDS 0x03c7 /* uint16 */ +#define CSR_PSKEY_MIN_WAIT_STATES 0x03c6 /* uint16 */ +#define CSR_PSKEY_RSSI_CORRECTION 0x03c5 /* int8 */ +#define CSR_PSKEY_SCHED_THROTTLE_TIMEOUT 0x03c4 /* TIME */ +#define CSR_PSKEY_DEEP_SLEEP_USE_EXTERNAL_CLOCK 0x03c3 /* bool */ +#define CSR_PSKEY_TRIM_RADIO_FILTERS 0x03c2 /* uint16 */ +#define CSR_PSKEY_TRANSMIT_OFFSET 0x03c1 /* int16 */ +#define CSR_PSKEY_USB_VM_CONTROL 0x03c0 /* bool */ +#define CSR_PSKEY_MR_ANA_RX_FTRIM 0x03bf /* uint16 */ +#define CSR_PSKEY_I2C_CONFIG 0x03be /* uint16 */ +#define CSR_PSKEY_IQ_LVL_RX 0x03bd /* uint16 */ +#define CSR_PSKEY_MR_TX_FILTER_CONFIG 0x03bb /* uint32 */ +#define CSR_PSKEY_MR_TX_CONFIG2 0x03ba /* uint16 */ +#define CSR_PSKEY_USB_DONT_RESET_BOOTMODE_ON_HOST_RESET 0x03b9 /* bool */ +#define CSR_PSKEY_LC_USE_THROTTLING 0x03b8 /* bool */ +#define CSR_PSKEY_CHARGER_TRIM 0x03b7 /* uint16 */ +#define CSR_PSKEY_CLOCK_REQUEST_FEATURES 0x03b6 /* uint16 */ +#define CSR_PSKEY_TRANSMIT_OFFSET_CLASS1 0x03b4 /* int16 */ +#define CSR_PSKEY_TX_AVOID_PA_CLASS1_PIO 0x03b3 /* uint16 */ +#define CSR_PSKEY_MR_PIO_CONFIG 0x03b2 /* uint16 */ +#define CSR_PSKEY_UART_CONFIG2 0x03b1 /* uint8 */ +#define CSR_PSKEY_CLASS1_IQ_LVL 0x03b0 /* uint16 */ +#define CSR_PSKEY_CLASS1_TX_CONFIG2 0x03af /* uint16 */ +#define CSR_PSKEY_TEMPERATURE_VS_DELTA_INTERNAL_PA_CLASS1 0x03ae /* temperature_calibration[] */ +#define CSR_PSKEY_TEMPERATURE_VS_DELTA_EXTERNAL_PA_CLASS1 0x03ad /* temperature_calibration[] */ +#define CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_PRE_LVL_MR 0x03ac /* temperature_calibration[] */ +#define CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB_MR_HEADER 0x03ab /* temperature_calibration[] */ +#define CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB_MR_PAYLOAD 0x03aa /* temperature_calibration[] */ +#define CSR_PSKEY_RX_MR_EQ_TAPS 0x03a9 /* uint16[] */ +#define CSR_PSKEY_TX_PRE_LVL_CLASS1 0x03a8 /* uint8 */ +#define CSR_PSKEY_ANALOGUE_ATTENUATOR 0x03a7 /* bool */ +#define CSR_PSKEY_MR_RX_FILTER_TRIM 0x03a6 /* uint16 */ +#define CSR_PSKEY_MR_RX_FILTER_RESPONSE 0x03a5 /* int16[] */ +#define CSR_PSKEY_PIO_WAKEUP_STATE 0x039f /* uint16 */ +#define CSR_PSKEY_MR_TX_IF_ATTEN_OFF_TEMP 0x0394 /* int16 */ +#define CSR_PSKEY_LO_DIV_LATCH_BYPASS 0x0393 /* bool */ +#define CSR_PSKEY_LO_VCO_STANDBY 0x0392 /* bool */ +#define CSR_PSKEY_SLOW_CLOCK_FILTER_SHIFT 0x0391 /* uint16 */ +#define CSR_PSKEY_SLOW_CLOCK_FILTER_DIVIDER 0x0390 /* uint16 */ +#define CSR_PSKEY_USB_ATTRIBUTES_POWER 0x03f2 /* bool */ +#define CSR_PSKEY_USB_ATTRIBUTES_WAKEUP 0x03f3 /* bool */ +#define CSR_PSKEY_DFU_ATTRIBUTES_MANIFESTATION_TOLERANT 0x03f4 /* bool */ +#define CSR_PSKEY_DFU_ATTRIBUTES_CAN_UPLOAD 0x03f5 /* bool */ +#define CSR_PSKEY_DFU_ATTRIBUTES_CAN_DOWNLOAD 0x03f6 /* bool */ +#define CSR_PSKEY_UART_CONFIG_STOP_BITS 0x03fc /* bool */ +#define CSR_PSKEY_UART_CONFIG_PARITY_BIT 0x03fd /* uint16 */ +#define CSR_PSKEY_UART_CONFIG_FLOW_CTRL_EN 0x03fe /* bool */ +#define CSR_PSKEY_UART_CONFIG_RTS_AUTO_EN 0x03ff /* bool */ +#define CSR_PSKEY_UART_CONFIG_RTS 0x0400 /* bool */ +#define CSR_PSKEY_UART_CONFIG_TX_ZERO_EN 0x0401 /* bool */ +#define CSR_PSKEY_UART_CONFIG_NON_BCSP_EN 0x0402 /* bool */ +#define CSR_PSKEY_UART_CONFIG_RX_RATE_DELAY 0x0403 /* uint16 */ +#define CSR_PSKEY_UART_SEQ_TIMEOUT 0x0405 /* uint16 */ +#define CSR_PSKEY_UART_SEQ_RETRIES 0x0406 /* uint16 */ +#define CSR_PSKEY_UART_SEQ_WINSIZE 0x0407 /* uint16 */ +#define CSR_PSKEY_UART_USE_CRC_ON_TX 0x0408 /* bool */ +#define CSR_PSKEY_UART_HOST_INITIAL_STATE 0x0409 /* hwakeup_state */ +#define CSR_PSKEY_UART_HOST_ATTENTION_SPAN 0x040a /* uint16 */ +#define CSR_PSKEY_UART_HOST_WAKEUP_TIME 0x040b /* uint16 */ +#define CSR_PSKEY_UART_HOST_WAKEUP_WAIT 0x040c /* uint16 */ +#define CSR_PSKEY_BCSP_LM_MODE 0x0410 /* uint16 */ +#define CSR_PSKEY_BCSP_LM_SYNC_RETRIES 0x0411 /* uint16 */ +#define CSR_PSKEY_BCSP_LM_TSHY 0x0412 /* uint16 */ +#define CSR_PSKEY_UART_DFU_CONFIG_STOP_BITS 0x0417 /* bool */ +#define CSR_PSKEY_UART_DFU_CONFIG_PARITY_BIT 0x0418 /* uint16 */ +#define CSR_PSKEY_UART_DFU_CONFIG_FLOW_CTRL_EN 0x0419 /* bool */ +#define CSR_PSKEY_UART_DFU_CONFIG_RTS_AUTO_EN 0x041a /* bool */ +#define CSR_PSKEY_UART_DFU_CONFIG_RTS 0x041b /* bool */ +#define CSR_PSKEY_UART_DFU_CONFIG_TX_ZERO_EN 0x041c /* bool */ +#define CSR_PSKEY_UART_DFU_CONFIG_NON_BCSP_EN 0x041d /* bool */ +#define CSR_PSKEY_UART_DFU_CONFIG_RX_RATE_DELAY 0x041e /* uint16 */ +#define CSR_PSKEY_AMUX_AIO0 0x041f /* ana_amux_sel */ +#define CSR_PSKEY_AMUX_AIO1 0x0420 /* ana_amux_sel */ +#define CSR_PSKEY_AMUX_AIO2 0x0421 /* ana_amux_sel */ +#define CSR_PSKEY_AMUX_AIO3 0x0422 /* ana_amux_sel */ +#define CSR_PSKEY_LOCAL_NAME_SIMPLIFIED 0x0423 /* local_name_complete */ +#define CSR_PSKEY_EXTENDED_STUB 0x2001 /* uint16 */ + +char *csr_builddeftostr(uint16_t def); +char *csr_buildidtostr(uint16_t id); +char *csr_chipvertostr(uint16_t ver, uint16_t rev); +char *csr_pskeytostr(uint16_t pskey); +char *csr_pskeytoval(uint16_t pskey); + +int csr_open_hci(char *device); +int csr_read_hci(uint16_t varid, uint8_t *value, uint16_t length); +int csr_write_hci(uint16_t varid, uint8_t *value, uint16_t length); +void csr_close_hci(void); + +int csr_open_usb(char *device); +int csr_read_usb(uint16_t varid, uint8_t *value, uint16_t length); +int csr_write_usb(uint16_t varid, uint8_t *value, uint16_t length); +void csr_close_usb(void); + +int csr_open_bcsp(char *device, speed_t bcsp_rate); +int csr_read_bcsp(uint16_t varid, uint8_t *value, uint16_t length); +int csr_write_bcsp(uint16_t varid, uint8_t *value, uint16_t length); +void csr_close_bcsp(void); + +int csr_open_h4(char *device); +int csr_read_h4(uint16_t varid, uint8_t *value, uint16_t length); +int csr_write_h4(uint16_t varid, uint8_t *value, uint16_t length); +void csr_close_h4(void); + +int csr_open_3wire(char *device); +int csr_read_3wire(uint16_t varid, uint8_t *value, uint16_t length); +int csr_write_3wire(uint16_t varid, uint8_t *value, uint16_t length); +void csr_close_3wire(void); + +int csr_write_varid_valueless(int dd, uint16_t seqnum, uint16_t varid); +int csr_write_varid_complex(int dd, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length); +int csr_read_varid_complex(int dd, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length); +int csr_read_varid_uint16(int dd, uint16_t seqnum, uint16_t varid, uint16_t *value); +int csr_read_varid_uint32(int dd, uint16_t seqnum, uint16_t varid, uint32_t *value); +int csr_read_pskey_complex(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint8_t *value, uint16_t length); +int csr_write_pskey_complex(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint8_t *value, uint16_t length); +int csr_read_pskey_uint16(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint16_t *value); +int csr_write_pskey_uint16(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint16_t value); +int csr_read_pskey_uint32(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint32_t *value); +int csr_write_pskey_uint32(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint32_t value); + +int psr_put(uint16_t pskey, uint8_t *value, uint16_t size); +int psr_get(uint16_t *pskey, uint8_t *value, uint16_t *size); +int psr_read(const char *filename); +int psr_print(void); diff --git a/tools/csr_3wire.c b/tools/csr_3wire.c new file mode 100644 index 0000000..33fcf38 --- /dev/null +++ b/tools/csr_3wire.c @@ -0,0 +1,62 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "csr.h" + +static uint16_t seqnum = 0x0000; + +int csr_open_3wire(char *device) +{ + fprintf(stderr, "Transport not implemented\n"); + + return -1; +} + +static int do_command(uint16_t command, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length) +{ + errno = EIO; + + return -1; +} + +int csr_read_3wire(uint16_t varid, uint8_t *value, uint16_t length) +{ + return do_command(0x0000, seqnum++, varid, value, length); +} + +int csr_write_3wire(uint16_t varid, uint8_t *value, uint16_t length) +{ + return do_command(0x0002, seqnum++, varid, value, length); +} + +void csr_close_3wire(void) +{ +} diff --git a/tools/csr_bcsp.c b/tools/csr_bcsp.c new file mode 100644 index 0000000..df247a2 --- /dev/null +++ b/tools/csr_bcsp.c @@ -0,0 +1,255 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "csr.h" +#include "ubcsp.h" + +static uint16_t seqnum = 0x0000; + +static int fd = -1; + +static struct ubcsp_packet send_packet; +static uint8_t send_buffer[512]; + +static struct ubcsp_packet receive_packet; +static uint8_t receive_buffer[512]; + +int csr_open_bcsp(char *device, speed_t bcsp_rate) +{ + struct termios ti; + uint8_t delay, activity = 0x00; + int timeout = 0; + + if (!device) + device = "/dev/ttyS0"; + + fd = open(device, O_RDWR | O_NOCTTY); + if (fd < 0) { + fprintf(stderr, "Can't open serial port: %s (%d)\n", + strerror(errno), errno); + return -1; + } + + tcflush(fd, TCIOFLUSH); + + if (tcgetattr(fd, &ti) < 0) { + fprintf(stderr, "Can't get port settings: %s (%d)\n", + strerror(errno), errno); + close(fd); + return -1; + } + + cfmakeraw(&ti); + + ti.c_cflag |= CLOCAL; + ti.c_cflag &= ~CRTSCTS; + ti.c_cflag |= PARENB; + ti.c_cflag &= ~PARODD; + ti.c_cflag &= ~CSIZE; + ti.c_cflag |= CS8; + ti.c_cflag &= ~CSTOPB; + + ti.c_cc[VMIN] = 1; + ti.c_cc[VTIME] = 0; + + cfsetospeed(&ti, bcsp_rate); + + if (tcsetattr(fd, TCSANOW, &ti) < 0) { + fprintf(stderr, "Can't change port settings: %s (%d)\n", + strerror(errno), errno); + close(fd); + return -1; + } + + tcflush(fd, TCIOFLUSH); + + if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) < 0) { + fprintf(stderr, "Can't set non blocking mode: %s (%d)\n", + strerror(errno), errno); + close(fd); + return -1; + } + + memset(&send_packet, 0, sizeof(send_packet)); + memset(&receive_packet, 0, sizeof(receive_packet)); + + ubcsp_initialize(); + + send_packet.length = 512; + send_packet.payload = send_buffer; + + receive_packet.length = 512; + receive_packet.payload = receive_buffer; + + ubcsp_receive_packet(&receive_packet); + + while (1) { + delay = ubcsp_poll(&activity); + + if (activity & UBCSP_PACKET_RECEIVED) + break; + + if (delay) { + usleep(delay * 100); + + if (timeout++ > 5000) { + fprintf(stderr, "Initialization timed out\n"); + return -1; + } + } + } + + return 0; +} + +void put_uart(uint8_t ch) +{ + if (write(fd, &ch, 1) < 0) + fprintf(stderr, "UART write error\n"); +} + +uint8_t get_uart(uint8_t *ch) +{ + int res = read(fd, ch, 1); + return res > 0 ? res : 0; +} + +static int do_command(uint16_t command, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length) +{ + unsigned char cp[254], rp[254]; + uint8_t cmd[10]; + uint16_t size; + uint8_t delay, activity = 0x00; + int timeout = 0, sent = 0; + + size = (length < 8) ? 9 : ((length + 1) / 2) + 5; + + cmd[0] = command & 0xff; + cmd[1] = command >> 8; + cmd[2] = size & 0xff; + cmd[3] = size >> 8; + cmd[4] = seqnum & 0xff; + cmd[5] = seqnum >> 8; + cmd[6] = varid & 0xff; + cmd[7] = varid >> 8; + cmd[8] = 0x00; + cmd[9] = 0x00; + + memset(cp, 0, sizeof(cp)); + cp[0] = 0x00; + cp[1] = 0xfc; + cp[2] = (size * 2) + 1; + cp[3] = 0xc2; + memcpy(cp + 4, cmd, sizeof(cmd)); + memcpy(cp + 14, value, length); + + receive_packet.length = 512; + ubcsp_receive_packet(&receive_packet); + + send_packet.channel = 5; + send_packet.reliable = 1; + send_packet.length = (size * 2) + 4; + memcpy(send_packet.payload, cp, (size * 2) + 4); + + ubcsp_send_packet(&send_packet); + + while (1) { + delay = ubcsp_poll(&activity); + + if (activity & UBCSP_PACKET_SENT) { + switch (varid) { + case CSR_VARID_COLD_RESET: + case CSR_VARID_WARM_RESET: + case CSR_VARID_COLD_HALT: + case CSR_VARID_WARM_HALT: + return 0; + } + + sent = 1; + timeout = 0; + } + + if (activity & UBCSP_PACKET_RECEIVED) { + if (sent && receive_packet.channel == 5 && + receive_packet.payload[0] == 0xff) { + memcpy(rp, receive_packet.payload, + receive_packet.length); + break; + } + + receive_packet.length = 512; + ubcsp_receive_packet(&receive_packet); + timeout = 0; + } + + if (delay) { + usleep(delay * 100); + + if (timeout++ > 5000) { + fprintf(stderr, "Operation timed out\n"); + return -1; + } + } + } + + if (rp[0] != 0xff || rp[2] != 0xc2) { + errno = EIO; + return -1; + } + + if ((rp[11] + (rp[12] << 8)) != 0) { + errno = ENXIO; + return -1; + } + + memcpy(value, rp + 13, length); + + return 0; +} + +int csr_read_bcsp(uint16_t varid, uint8_t *value, uint16_t length) +{ + return do_command(0x0000, seqnum++, varid, value, length); +} + +int csr_write_bcsp(uint16_t varid, uint8_t *value, uint16_t length) +{ + return do_command(0x0002, seqnum++, varid, value, length); +} + +void csr_close_bcsp(void) +{ + close(fd); +} diff --git a/tools/csr_h4.c b/tools/csr_h4.c new file mode 100644 index 0000000..3371770 --- /dev/null +++ b/tools/csr_h4.c @@ -0,0 +1,165 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "csr.h" + +static uint16_t seqnum = 0x0000; + +static int fd = -1; + +int csr_open_h4(char *device) +{ + struct termios ti; + + if (!device) + device = "/dev/ttyS0"; + + fd = open(device, O_RDWR | O_NOCTTY); + if (fd < 0) { + fprintf(stderr, "Can't open serial port: %s (%d)\n", + strerror(errno), errno); + return -1; + } + + tcflush(fd, TCIOFLUSH); + + if (tcgetattr(fd, &ti) < 0) { + fprintf(stderr, "Can't get port settings: %s (%d)\n", + strerror(errno), errno); + close(fd); + return -1; + } + + cfmakeraw(&ti); + + ti.c_cflag |= CLOCAL; + ti.c_cflag |= CRTSCTS; + + cfsetospeed(&ti, B38400); + + if (tcsetattr(fd, TCSANOW, &ti) < 0) { + fprintf(stderr, "Can't change port settings: %s (%d)\n", + strerror(errno), errno); + close(fd); + return -1; + } + + tcflush(fd, TCIOFLUSH); + + return 0; +} + +static int do_command(uint16_t command, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length) +{ + unsigned char cp[254], rp[254]; + uint8_t cmd[10]; + uint16_t size; + int len, offset = 3; + + size = (length < 8) ? 9 : ((length + 1) / 2) + 5; + + cmd[0] = command & 0xff; + cmd[1] = command >> 8; + cmd[2] = size & 0xff; + cmd[3] = size >> 8; + cmd[4] = seqnum & 0xff; + cmd[5] = seqnum >> 8; + cmd[6] = varid & 0xff; + cmd[7] = varid >> 8; + cmd[8] = 0x00; + cmd[9] = 0x00; + + memset(cp, 0, sizeof(cp)); + cp[0] = 0x01; + cp[1] = 0x00; + cp[2] = 0xfc; + cp[3] = (size * 2) + 1; + cp[4] = 0xc2; + memcpy(cp + 5, cmd, sizeof(cmd)); + memcpy(cp + 15, value, length); + + if (write(fd, cp, (size * 2) + 5) < 0) + return -1; + + switch (varid) { + case CSR_VARID_COLD_RESET: + case CSR_VARID_WARM_RESET: + case CSR_VARID_COLD_HALT: + case CSR_VARID_WARM_HALT: + return 0; + } + + do { + if (read(fd, rp, 1) < 1) + return -1; + } while (rp[0] != 0x04); + + if (read(fd, rp + 1, 2) < 2) + return -1; + + do { + len = read(fd, rp + offset, sizeof(rp) - offset); + offset += len; + } while (offset < rp[2] + 3); + + if (rp[0] != 0x04 || rp[1] != 0xff || rp[3] != 0xc2) { + errno = EIO; + return -1; + } + + if ((rp[12] + (rp[13] << 8)) != 0) { + errno = ENXIO; + return -1; + } + + memcpy(value, rp + 14, length); + + return 0; +} + +int csr_read_h4(uint16_t varid, uint8_t *value, uint16_t length) +{ + return do_command(0x0000, seqnum++, varid, value, length); +} + +int csr_write_h4(uint16_t varid, uint8_t *value, uint16_t length) +{ + return do_command(0x0002, seqnum++, varid, value, length); +} + +void csr_close_h4(void) +{ + close(fd); +} diff --git a/tools/csr_hci.c b/tools/csr_hci.c new file mode 100644 index 0000000..6bd37c3 --- /dev/null +++ b/tools/csr_hci.c @@ -0,0 +1,160 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include + +#include "csr.h" + +static uint16_t seqnum = 0x0000; + +static int dd = -1; + +int csr_open_hci(char *device) +{ + struct hci_dev_info di; + struct hci_version ver; + int dev = 0; + + if (device) { + dev = hci_devid(device); + if (dev < 0) { + fprintf(stderr, "Device not available\n"); + return -1; + } + } + + dd = hci_open_dev(dev); + if (dd < 0) { + fprintf(stderr, "Can't open device hci%d: %s (%d)\n", + dev, strerror(errno), errno); + return -1; + } + + if (hci_devinfo(dev, &di) < 0) { + fprintf(stderr, "Can't get device info for hci%d: %s (%d)\n", + dev, strerror(errno), errno); + hci_close_dev(dd); + return -1; + } + + if (hci_read_local_version(dd, &ver, 1000) < 0) { + fprintf(stderr, "Can't read version info for hci%d: %s (%d)\n", + dev, strerror(errno), errno); + hci_close_dev(dd); + return -1; + } + + if (ver.manufacturer != 10) { + fprintf(stderr, "Unsupported manufacturer\n"); + hci_close_dev(dd); + return -1; + } + + return 0; +} + +static int do_command(uint16_t command, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length) +{ + unsigned char cp[254], rp[254]; + struct hci_request rq; + uint8_t cmd[10]; + uint16_t size; + + size = (length < 8) ? 9 : ((length + 1) / 2) + 5; + + cmd[0] = command & 0xff; + cmd[1] = command >> 8; + cmd[2] = size & 0xff; + cmd[3] = size >> 8; + cmd[4] = seqnum & 0xff; + cmd[5] = seqnum >> 8; + cmd[6] = varid & 0xff; + cmd[7] = varid >> 8; + cmd[8] = 0x00; + cmd[9] = 0x00; + + memset(cp, 0, sizeof(cp)); + cp[0] = 0xc2; + memcpy(cp + 1, cmd, sizeof(cmd)); + memcpy(cp + 11, value, length); + + switch (varid) { + case CSR_VARID_COLD_RESET: + case CSR_VARID_WARM_RESET: + case CSR_VARID_COLD_HALT: + case CSR_VARID_WARM_HALT: + return hci_send_cmd(dd, OGF_VENDOR_CMD, 0x00, (size * 2) + 1, cp); + } + + memset(&rq, 0, sizeof(rq)); + rq.ogf = OGF_VENDOR_CMD; + rq.ocf = 0x00; + rq.event = EVT_VENDOR; + rq.cparam = cp; + rq.clen = (size * 2) + 1; + rq.rparam = rp; + rq.rlen = sizeof(rp); + + if (hci_send_req(dd, &rq, 2000) < 0) + return -1; + + if (rp[0] != 0xc2) { + errno = EIO; + return -1; + } + + if ((rp[9] + (rp[10] << 8)) != 0) { + errno = ENXIO; + return -1; + } + + memcpy(value, rp + 11, length); + + return 0; +} + +int csr_read_hci(uint16_t varid, uint8_t *value, uint16_t length) +{ + return do_command(0x0000, seqnum++, varid, value, length); +} + +int csr_write_hci(uint16_t varid, uint8_t *value, uint16_t length) +{ + return do_command(0x0002, seqnum++, varid, value, length); +} + +void csr_close_hci(void) +{ + hci_close_dev(dd); +} diff --git a/tools/csr_usb.c b/tools/csr_usb.c new file mode 100644 index 0000000..19903b0 --- /dev/null +++ b/tools/csr_usb.c @@ -0,0 +1,180 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include + +#include "csr.h" + +#ifdef NEED_USB_GET_BUSSES +static inline struct usb_bus *usb_get_busses(void) +{ + return usb_busses; +} +#endif + +#ifdef NEED_USB_INTERRUPT_READ +static inline int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout) +{ + return usb_bulk_read(dev, ep, bytes, size, timeout); +} +#endif + +#ifndef USB_DIR_OUT +#define USB_DIR_OUT 0x00 +#endif + +static uint16_t seqnum = 0x0000; + +static struct usb_dev_handle *udev = NULL; + +int csr_open_usb(char *device) +{ + struct usb_bus *bus; + struct usb_device *dev; + + usb_init(); + + usb_find_busses(); + usb_find_devices(); + + for (bus = usb_get_busses(); bus; bus = bus->next) { + for (dev = bus->devices; dev; dev = dev->next) { + if (dev->descriptor.bDeviceClass == USB_CLASS_HUB) + continue; + + if (dev->descriptor.idVendor != 0x0a12 || + dev->descriptor.idProduct != 0x0001) + continue; + + goto found; + } + } + + fprintf(stderr, "Device not available\n"); + + return -1; + +found: + udev = usb_open(dev); + if (!udev) { + fprintf(stderr, "Can't open device: %s (%d)\n", + strerror(errno), errno); + return -1; + } + + if (usb_claim_interface(udev, 0) < 0) { + fprintf(stderr, "Can't claim interface: %s (%d)\n", + strerror(errno), errno); + usb_close(udev); + return -1; + } + + return 0; +} + +static int do_command(uint16_t command, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length) +{ + unsigned char cp[254], rp[254]; + uint8_t cmd[10]; + uint16_t size; + int len, offset = 0; + + size = (length < 8) ? 9 : ((length + 1) / 2) + 5; + + cmd[0] = command & 0xff; + cmd[1] = command >> 8; + cmd[2] = size & 0xff; + cmd[3] = size >> 8; + cmd[4] = seqnum & 0xff; + cmd[5] = seqnum >> 8; + cmd[6] = varid & 0xff; + cmd[7] = varid >> 8; + cmd[8] = 0x00; + cmd[9] = 0x00; + + memset(cp, 0, sizeof(cp)); + cp[0] = 0x00; + cp[1] = 0xfc; + cp[2] = (size * 2) + 1; + cp[3] = 0xc2; + memcpy(cp + 4, cmd, sizeof(cmd)); + memcpy(cp + 14, value, length); + + usb_interrupt_read(udev, 0x81, (void *) rp, sizeof(rp), 2); + + if (usb_control_msg(udev, USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_DEVICE, + 0, 0, 0, (void *) cp, (size * 2) + 4, 1000) < 0) + return -1; + + switch (varid) { + case CSR_VARID_COLD_RESET: + case CSR_VARID_WARM_RESET: + case CSR_VARID_COLD_HALT: + case CSR_VARID_WARM_HALT: + return 0; + } + + do { + len = usb_interrupt_read(udev, 0x81, + (void *) (rp + offset), sizeof(rp) - offset, 10); + offset += len; + } while (len > 0); + + if (rp[0] != 0xff || rp[2] != 0xc2) { + errno = EIO; + return -1; + } + + if ((rp[11] + (rp[12] << 8)) != 0) { + errno = ENXIO; + return -1; + } + + memcpy(value, rp + 13, length); + + return 0; +} + +int csr_read_usb(uint16_t varid, uint8_t *value, uint16_t length) +{ + return do_command(0x0000, seqnum++, varid, value, length); +} + +int csr_write_usb(uint16_t varid, uint8_t *value, uint16_t length) +{ + return do_command(0x0002, seqnum++, varid, value, length); +} + +void csr_close_usb(void) +{ + usb_release_interface(udev, 0); + usb_close(udev); +} diff --git a/tools/dfu.c b/tools/dfu.c new file mode 100644 index 0000000..39ec088 --- /dev/null +++ b/tools/dfu.c @@ -0,0 +1,168 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2003-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include + +#include "dfu.h" + +#ifndef USB_DIR_OUT +#define USB_DIR_OUT 0x00 +#endif + +#ifndef USB_DIR_IN +#define USB_DIR_IN 0x80 +#endif + +#ifndef USB_DT_DFU +#define USB_DT_DFU 0x21 +#endif + +#define DFU_PACKETSIZE 0x03ff /* CSR default value: 1023 */ +#define DFU_TIMEOUT 10000 + +static uint32_t dfu_crc32_table[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +uint32_t crc32_init(void) +{ + return 0xffffffff; +} + +uint32_t crc32_byte(uint32_t accum, uint8_t delta) +{ + return dfu_crc32_table[(accum ^ delta) & 0xff] ^ (accum >> 8); +} + +int dfu_detach(struct usb_dev_handle *udev, int intf) +{ + if (!udev) + return -EIO; + + return usb_control_msg(udev, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + DFU_DETACH, 0x1388, intf, NULL, 0, DFU_TIMEOUT); +} + +int dfu_upload(struct usb_dev_handle *udev, int intf, int block, char *buffer, int size) +{ + if (!udev) + return -EIO; + + return usb_control_msg(udev, USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE, + DFU_UPLOAD, block, intf, buffer, size, DFU_TIMEOUT); +} + +int dfu_download(struct usb_dev_handle *udev, int intf, int block, char *buffer, int size) +{ + if (!udev) + return -EIO; + + return usb_control_msg(udev, USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE, + DFU_DNLOAD, block, intf, buffer, size, DFU_TIMEOUT); +} + +int dfu_get_status(struct usb_dev_handle *udev, int intf, struct dfu_status *status) +{ + if (!udev || !status) + return -EIO; + + return usb_control_msg(udev, USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE, + DFU_GETSTATUS, 0, intf, (char *) status, DFU_STATUS_SIZE, DFU_TIMEOUT); +} + +int dfu_clear_status(struct usb_dev_handle *udev, int intf) +{ + if (!udev) + return -EIO; + + return usb_control_msg(udev, USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE, + DFU_CLRSTATUS, 0, intf, NULL, 0, DFU_TIMEOUT); +} + +int dfu_get_state(struct usb_dev_handle *udev, int intf, uint8_t *state) +{ + if (!udev || !state) + return -EIO; + + return usb_control_msg(udev, USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE, + DFU_GETSTATE, 0, intf, (char *) state, 1, DFU_TIMEOUT); +} + +int dfu_abort(struct usb_dev_handle *udev, int intf) +{ + if (!udev) + return -EIO; + + return usb_control_msg(udev, USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE, + DFU_ABORT, 0, intf, NULL, 0, DFU_TIMEOUT); +} diff --git a/tools/dfu.h b/tools/dfu.h new file mode 100644 index 0000000..7f999f4 --- /dev/null +++ b/tools/dfu.h @@ -0,0 +1,107 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2003-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include + +/* CRC interface */ +uint32_t crc32_init(void); +uint32_t crc32_byte(uint32_t accum, uint8_t delta); + +/* DFU descriptor */ +struct usb_dfu_descriptor { + u_int8_t bLength; + u_int8_t bDescriptorType; + u_int8_t bmAttributes; + u_int16_t wDetachTimeout; + u_int16_t wTransferSize; +}; + +/* DFU commands */ +#define DFU_DETACH 0 +#define DFU_DNLOAD 1 +#define DFU_UPLOAD 2 +#define DFU_GETSTATUS 3 +#define DFU_CLRSTATUS 4 +#define DFU_GETSTATE 5 +#define DFU_ABORT 6 + +/* DFU status */ +struct dfu_status { + uint8_t bStatus; + uint8_t bwPollTimeout[3]; + uint8_t bState; + uint8_t iString; +} __attribute__ ((packed)); +#define DFU_STATUS_SIZE 6 + +/* DFU status */ +#define DFU_OK 0x00 +#define DFU_ERR_TARGET 0x01 +#define DFU_ERR_FILE 0x02 +#define DFU_ERR_WRITE 0x03 +#define DFU_ERR_ERASE 0x04 +#define DFU_ERR_CHECK_ERASED 0x05 +#define DFU_ERR_PROG 0x06 +#define DFU_ERR_VERIFY 0x07 +#define DFU_ERR_ADDRESS 0x08 +#define DFU_ERR_NOTDONE 0x09 +#define DFU_ERR_FIRMWARE 0x0a +#define DFU_ERR_VENDOR 0x0b +#define DFU_ERR_USBR 0x0c +#define DFU_ERR_POR 0x0d +#define DFU_ERR_UNKNOWN 0x0e +#define DFU_ERR_STALLEDPKT 0x0f + +/* DFU state */ +#define DFU_STATE_APP_IDLE 0 +#define DFU_STATE_APP_DETACH 1 +#define DFU_STATE_DFU_IDLE 2 +#define DFU_STATE_DFU_DNLOAD_SYNC 3 +#define DFU_STATE_DFU_DNLOAD_BUSY 4 +#define DFU_STATE_DFU_DNLOAD_IDLE 5 +#define DFU_STATE_DFU_MANIFEST_SYNC 6 +#define DFU_STATE_DFU_MANIFEST 7 +#define DFU_STATE_MANIFEST_WAIT_RESET 8 +#define DFU_STATE_UPLOAD_IDLE 9 +#define DFU_STATE_ERROR 10 + +/* DFU suffix */ +struct dfu_suffix { + uint16_t bcdDevice; + uint16_t idProduct; + uint16_t idVendor; + uint16_t bcdDFU; + uint8_t ucDfuSignature[3]; + uint8_t bLength; + uint32_t dwCRC; +} __attribute__ ((packed)); +#define DFU_SUFFIX_SIZE 16 + +/* DFU interface */ +int dfu_detach(struct usb_dev_handle *udev, int intf); +int dfu_upload(struct usb_dev_handle *udev, int intf, int block, char *buffer, int size); +int dfu_download(struct usb_dev_handle *udev, int intf, int block, char *buffer, int size); +int dfu_get_status(struct usb_dev_handle *udev, int intf, struct dfu_status *status); +int dfu_clear_status(struct usb_dev_handle *udev, int intf); +int dfu_get_state(struct usb_dev_handle *udev, int intf, uint8_t *state); +int dfu_abort(struct usb_dev_handle *udev, int intf); diff --git a/tools/dfubabel.1 b/tools/dfubabel.1 new file mode 100644 index 0000000..5e0f805 --- /dev/null +++ b/tools/dfubabel.1 @@ -0,0 +1,38 @@ +.\" +.\" This program is free software; you can redistribute it and/or modify +.\" it under the terms of the GNU General Public License as published by +.\" the Free Software Foundation; either version 2 of the License, or +.\" (at your option) any later version. +.\" +.\" This program is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.\" GNU General Public License for more details. +.\" +.\" You should have received a copy of the GNU General Public License +.\" along with this program; if not, write to the Free Software +.\" Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +.\" +.\" +.TH DFUBABEL 8 "JUNE 6, 2005" "" "" + +.SH NAME +dfubabel \- Babel DFU mode switching utility +.SH SYNOPSIS +.BR "dfubabel +[ +.I options +] +.SH DESCRIPTION +.B dfubabel +is used to switch Babel devices into DFU mode. +.SH OPTIONS +.TP +.BI -h +Gives a list of possible options. +.TP +.BI -q +Don't display any messages. +.SH AUTHOR +Written by Marcel Holtmann . +.br diff --git a/tools/dfubabel.c b/tools/dfubabel.c new file mode 100644 index 0000000..612accc --- /dev/null +++ b/tools/dfubabel.c @@ -0,0 +1,211 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include + +#ifdef NEED_USB_GET_BUSSES +static inline struct usb_bus *usb_get_busses(void) +{ + return usb_busses; +} +#endif + +struct device_info; + +struct device_id { + uint16_t vendor; + uint16_t product; + int (*func)(struct device_info *dev, int argc, char *argv[]); +}; + +struct device_info { + struct usb_device *dev; + struct device_id *id; +}; + +static int switch_babel(struct device_info *devinfo, int argc, char *argv[]) +{ + char buf[3]; + struct usb_dev_handle *udev; + int err; + + memset(buf, 0, sizeof(buf)); + + buf[0] = 0x00; + buf[1] = 0x06; + buf[2] = 0x00; + + udev = usb_open(devinfo->dev); + if (!udev) + return -errno; + + if (usb_claim_interface(udev, 0) < 0) { + err = -errno; + usb_close(udev); + return err; + } + + err = usb_bulk_write(udev, 0x02, buf, sizeof(buf), 10000); + + if (err == 0) { + err = -1; + errno = EALREADY; + } else { + if (errno == ETIMEDOUT) + err = 0; + } + + usb_release_interface(udev, 0); + usb_close(udev); + + return err; +} + +static struct device_id device_list[] = { + { 0x0a12, 0x0042, switch_babel }, + { -1 } +}; + +static struct device_id *match_device(uint16_t vendor, uint16_t product) +{ + int i; + + for (i = 0; device_list[i].func; i++) { + if (vendor == device_list[i].vendor && + product == device_list[i].product) + return &device_list[i]; + } + + return NULL; +} + +static int find_devices(struct device_info *devinfo, size_t size) +{ + struct usb_bus *bus; + struct usb_device *dev; + struct device_id *id; + unsigned int count = 0; + + usb_find_busses(); + usb_find_devices(); + + for (bus = usb_get_busses(); bus; bus = bus->next) + for (dev = bus->devices; dev; dev = dev->next) { + id = match_device(dev->descriptor.idVendor, + dev->descriptor.idProduct); + if (!id) + continue; + + if (count < size) { + devinfo[count].dev = dev; + devinfo[count].id = id; + count++; + } + } + + return count; +} + +static void usage(void) +{ + printf("dfubabel - Babel DFU mode switching utility\n\n"); + + printf("Usage:\n" + "\tdfubabel [options]\n" + "\n"); + + printf("Options:\n" + "\t-h, --help Display help\n" + "\t-q, --quiet Don't display any messages\n" + "\n"); +} + +static struct option main_options[] = { + { "help", 0, 0, 'h' }, + { "quiet", 0, 0, 'q' }, + { 0, 0, 0, 0 } +}; + +int main(int argc, char *argv[]) +{ + struct device_info dev[16]; + int i, opt, num, quiet = 0; + + while ((opt = getopt_long(argc, argv, "+qh", main_options, NULL)) != -1) { + switch (opt) { + case 'q': + quiet = 1; + break; + case 'h': + usage(); + exit(0); + default: + exit(0); + } + } + + argc -= optind; + argv += optind; + optind = 0; + + usb_init(); + + num = find_devices(dev, sizeof(dev) / sizeof(dev[0])); + if (num <= 0) { + if (!quiet) + fprintf(stderr, "No Babel devices found\n"); + exit(1); + } + + for (i = 0; i < num; i++) { + struct device_id *id = dev[i].id; + int err; + + if (!quiet) + printf("Switching device %04x:%04x ", + id->vendor, id->product); + fflush(stdout); + + err = id->func(&dev[i], argc, argv); + if (err < 0) { + if (!quiet) + printf("failed (%s)\n", strerror(-err)); + } else { + if (!quiet) + printf("was successful\n"); + } + } + + return 0; +} diff --git a/tools/dfutool.1 b/tools/dfutool.1 new file mode 100644 index 0000000..115114b --- /dev/null +++ b/tools/dfutool.1 @@ -0,0 +1,53 @@ +.\" +.\" This program is free software; you can redistribute it and/or modify +.\" it under the terms of the GNU General Public License as published by +.\" the Free Software Foundation; either version 2 of the License, or +.\" (at your option) any later version. +.\" +.\" This program is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.\" GNU General Public License for more details. +.\" +.\" You should have received a copy of the GNU General Public License +.\" along with this program; if not, write to the Free Software +.\" Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +.\" +.\" +.TH DFUTOOL 1 "APRIL 21, 2005" "" "" + +.SH NAME +dfutool \- Device Firmware Upgrade utility +.SH SYNOPSIS +.BR "dfutool +[ +.I options +] < +.I command +> +.SH DESCRIPTION +.B dfutool +is used to verify, archive and upgrade firmware files. +.SH OPTIONS +.TP +.BI -h +Gives a list of possible commands. +.TP +.BI -d " " +The command specifies the device to use. +.SH COMMANDS +.TP +.BI verify " " +Display information about the firmware file. +.TP +.BI modify " " +Change DFU specific values in the firmware file. +.TP +.BI upgrade " " +Upgrade the device with a new firmware. +.TP +.BI archive " " +Archive the current firmware of the device. +.SH AUTHOR +Written by Marcel Holtmann . +.br diff --git a/tools/dfutool.c b/tools/dfutool.c new file mode 100644 index 0000000..16dd62e --- /dev/null +++ b/tools/dfutool.c @@ -0,0 +1,791 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2003-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dfu.h" + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define cpu_to_le16(d) (d) +#define cpu_to_le32(d) (d) +#define le16_to_cpu(d) (d) +#define le32_to_cpu(d) (d) +#elif __BYTE_ORDER == __BIG_ENDIAN +#define cpu_to_le16(d) bswap_16(d) +#define cpu_to_le32(d) bswap_32(d) +#define le16_to_cpu(d) bswap_16(d) +#define le32_to_cpu(d) bswap_32(d) +#else +#error "Unknown byte order" +#endif + +#ifdef NEED_USB_GET_BUSSES +static inline struct usb_bus *usb_get_busses(void) +{ + return usb_busses; +} +#endif + +#ifndef USB_CLASS_WIRELESS +#define USB_CLASS_WIRELESS 0xe0 +#endif + +#ifndef USB_CLASS_APPLICATION +#define USB_CLASS_APPLICATION 0xfe +#endif + +static int get_interface_number(struct usb_device *dev) +{ + int c, i, a; + + for (c = 0; c < dev->descriptor.bNumConfigurations; c++) { + struct usb_config_descriptor *config = &dev->config[c]; + + for (i = 0; i < config->bNumInterfaces; i++) { + struct usb_interface *interface = &config->interface[i]; + + for (a = 0; a < interface->num_altsetting; a++) { + struct usb_interface_descriptor *desc = &interface->altsetting[a]; + + if (desc->bInterfaceClass != USB_CLASS_APPLICATION) + continue; + if (desc->bInterfaceSubClass != 0x01) + continue; + if (desc->bInterfaceProtocol != 0x00) + continue; + + return desc->bInterfaceNumber; + } + } + } + + return -1; +} + +static void print_device(struct usb_device *dev) +{ + printf("Bus %s Device %s: ID %04x:%04x Interface %d%s\n", + dev->bus->dirname, dev->filename, + dev->descriptor.idVendor, dev->descriptor.idProduct, + get_interface_number(dev), + dev->descriptor.bDeviceClass == USB_CLASS_APPLICATION ? " (DFU mode)" : ""); +} + +static struct usb_dev_handle *open_device(char *device, struct dfu_suffix *suffix) +{ + struct usb_bus *bus; + struct usb_device *dev, *dfu_dev[10]; + struct usb_dev_handle *udev; + struct dfu_status status; + char str[8]; + int i, intf, sel = 0, num = 0, try = 5, bus_id = -1, dev_id = -1; + + printf("Scanning USB busses ... "); + fflush(stdout); + + usb_find_busses(); + usb_find_devices(); + + for (bus = usb_get_busses(); bus; bus = bus->next) { + if (bus_id > 0) { + snprintf(str, sizeof(str) - 1, "%03i", bus_id); + if (strcmp(str, bus->dirname)) + continue; + } + + for (dev = bus->devices; dev; dev = dev->next) { + if (bus_id > 0 && dev_id > 0) { + snprintf(str, sizeof(str) - 1, "%03i", dev_id); + if (strcmp(str, dev->filename)) + continue; + } + + if (dev->descriptor.bDeviceClass == USB_CLASS_HUB) + continue; + + if (num > 9 || get_interface_number(dev) < 0) + continue; + + dfu_dev[num++] = dev; + } + } + + if (num < 1) { + printf("\rCan't find any DFU devices\n"); + return NULL; + } + + printf("\rAvailable devices with DFU support:\n\n"); + for (i = 0; i < num; i++) { + printf("\t%2d) ", i + 1); + print_device(dfu_dev[i]); + } + printf("\n"); + + do { + printf("\rSelect device (abort with 0): "); + fflush(stdout); + memset(str, 0, sizeof(str)); + if (!fgets(str, sizeof(str) - 1, stdin)) + continue; + sel = atoi(str); + } while (!isdigit(str[0]) || sel < 0 || sel > num ); + + if (sel < 1) + return NULL; + + sel--; + intf = get_interface_number(dfu_dev[sel]); + printf("\n"); + + udev = usb_open(dfu_dev[sel]); + if (!udev) { + printf("Can't open device: %s (%d)\n", strerror(errno), errno); + return NULL; + } + + if (usb_claim_interface(udev, intf) < 0) { + printf("Can't claim interface: %s (%d)\n", strerror(errno), errno); + usb_close(udev); + return NULL; + } + + if (dfu_get_status(udev, intf, &status) < 0) { + printf("Can't get status: %s (%d)\n", strerror(errno), errno); + goto error; + } + + if (status.bState == DFU_STATE_ERROR) { + if (dfu_clear_status(udev, intf) < 0) { + printf("Can't clear status: %s (%d)\n", strerror(errno), errno); + goto error; + } + if (dfu_abort(udev, intf) < 0) { + printf("Can't abort previous action: %s (%d)\n", strerror(errno), errno); + goto error; + } + if (dfu_get_status(udev, intf, &status) < 0) { + printf("Can't get status: %s (%d)\n", strerror(errno), errno); + goto error; + } + } + + if (status.bState == DFU_STATE_DFU_IDLE) { + if (suffix) { + suffix->idVendor = cpu_to_le16(0x0000); + suffix->idProduct = cpu_to_le16(0x0000); + suffix->bcdDevice = cpu_to_le16(0x0000); + } + return udev; + } + + if (status.bState != DFU_STATE_APP_IDLE) { + printf("Device is not idle, can't detach it (state %d)\n", status.bState); + goto error; + } + + printf("Switching device into DFU mode ... "); + fflush(stdout); + + if (suffix) { + suffix->idVendor = cpu_to_le16(dfu_dev[sel]->descriptor.idVendor); + suffix->idProduct = cpu_to_le16(dfu_dev[sel]->descriptor.idProduct); + suffix->bcdDevice = cpu_to_le16(dfu_dev[sel]->descriptor.bcdDevice); + } + + if (dfu_detach(udev, intf) < 0) { + printf("\rCan't detach device: %s (%d)\n", strerror(errno), errno); + goto error; + } + + if (dfu_get_status(udev, intf, &status) < 0) { + printf("\rCan't get status: %s (%d)\n", strerror(errno), errno); + goto error; + } + + if (status.bState != DFU_STATE_APP_DETACH) { + printf("\rDevice is not in detach mode, try again\n"); + goto error; + } + + usb_release_interface(udev, intf); + usb_reset(udev); + usb_close(udev); + + bus = dfu_dev[sel]->bus; + num = 0; + + while (num != 1 && try-- > 0) { + sleep(1); + usb_find_devices(); + + for (dev = bus->devices; dev; dev = dev->next) { + if (dev->descriptor.bDeviceClass != USB_CLASS_APPLICATION) + continue; + + if (suffix && dev->descriptor.idVendor != le16_to_cpu(suffix->idVendor)) + continue; + + if (num > 9 || get_interface_number(dev) != 0) + continue; + + dfu_dev[num++] = dev; + } + } + + if (num != 1) { + printf("\rCan't identify device with DFU mode\n"); + goto error; + } + + printf("\r"); + + intf = 0; + + udev = usb_open(dfu_dev[0]); + if (!udev) { + printf("Can't open device: %s (%d)\n", strerror(errno), errno); + return NULL; + } + + if (usb_claim_interface(udev, intf) < 0) { + printf("Can't claim interface: %s (%d)\n", strerror(errno), errno); + usb_close(udev); + return NULL; + } + + if (dfu_get_status(udev, intf, &status) < 0) { + printf("Can't get status: %s (%d)\n", strerror(errno), errno); + goto error; + } + + if (status.bState != DFU_STATE_DFU_IDLE) { + printf("Device is not in DFU mode, can't use it\n"); + goto error; + } + + return udev; + +error: + usb_release_interface(udev, intf); + usb_close(udev); + return NULL; +} + +static void usage(void); + +static void cmd_verify(char *device, int argc, char **argv) +{ + struct stat st; + struct dfu_suffix *suffix; + uint32_t crc; + uint16_t bcd; + char str[16]; + unsigned char *buf; + size_t size; + char *filename; + unsigned int i, len; + int fd; + + if (argc < 2) { + usage(); + exit(1); + } + + filename = argv[1]; + + if (stat(filename, &st) < 0) { + perror("Can't access firmware"); + exit(1); + } + + size = st.st_size; + + if (!(buf = malloc(size))) { + perror("Unable to allocate file buffer"); + exit(1); + } + + if ((fd = open(filename, O_RDONLY)) < 0) { + perror("Can't open firmware"); + free(buf); + exit(1); + } + + if (read(fd, buf, size) < (ssize_t) size) { + perror("Can't load firmware"); + free(buf); + close(fd); + exit(1); + } + + printf("Filename\t%s\n", basename(filename)); + printf("Filesize\t%zd\n", size); + + crc = crc32_init(); + for (i = 0; i < size - 4; i++) + crc = crc32_byte(crc, buf[i]); + printf("Checksum\t%08x\n", crc); + + printf("\n"); + len = buf[size - 5]; + printf("DFU suffix\t"); + for (i = 0; i < len; i++) { + printf("%02x ", buf[size - len + i]); + } + printf("\n\n"); + + suffix = (struct dfu_suffix *) (buf + size - DFU_SUFFIX_SIZE); + + printf("idVendor\t%04x\n", le16_to_cpu(suffix->idVendor)); + printf("idProduct\t%04x\n", le16_to_cpu(suffix->idProduct)); + printf("bcdDevice\t%x\n", le16_to_cpu(suffix->bcdDevice)); + + printf("\n"); + + bcd = le16_to_cpu(suffix->bcdDFU); + + printf("bcdDFU\t\t%x.%x\n", bcd >> 8, bcd & 0xff); + printf("ucDfuSignature\t%c%c%c\n", suffix->ucDfuSignature[2], + suffix->ucDfuSignature[1], suffix->ucDfuSignature[0]); + printf("bLength\t\t%d\n", suffix->bLength); + printf("dwCRC\t\t%08x\n", le32_to_cpu(suffix->dwCRC)); + printf("\n"); + + memset(str, 0, sizeof(str)); + memcpy(str, buf, 8); + + if (!strcmp(str, "CSR-dfu1") || !strcmp(str, "CSR-dfu2")) { + crc = crc32_init(); + for (i = 0; i < size - DFU_SUFFIX_SIZE; i++) + crc = crc32_byte(crc, buf[i]); + + printf("Firmware type\t%s\n", str); + printf("Firmware check\t%s checksum\n", crc == 0 ? "valid" : "corrupt"); + printf("\n"); + } + + free(buf); + + close(fd); +} + +static void cmd_modify(char *device, int argc, char **argv) +{ +} + +static void cmd_upgrade(char *device, int argc, char **argv) +{ + struct usb_dev_handle *udev; + struct dfu_status status; + struct dfu_suffix suffix; + struct stat st; + char *buf; + size_t filesize; + unsigned long count, timeout = 0; + char *filename; + uint32_t crc, dwCRC; + unsigned int i; + int fd, block, len, size, sent = 0, try = 10; + + if (argc < 2) { + usage(); + exit(1); + } + + filename = argv[1]; + + if (stat(filename, &st) < 0) { + perror("Can't access firmware"); + exit(1); + } + + filesize = st.st_size; + + if (!(buf = malloc(filesize))) { + perror("Unable to allocate file buffer"); + exit(1); + } + + if ((fd = open(filename, O_RDONLY)) < 0) { + perror("Can't open firmware"); + free(buf); + exit(1); + } + + if (read(fd, buf, filesize) < (ssize_t) filesize) { + perror("Can't load firmware"); + free(buf); + close(fd); + exit(1); + } + + memcpy(&suffix, buf + filesize - DFU_SUFFIX_SIZE, sizeof(suffix)); + dwCRC = le32_to_cpu(suffix.dwCRC); + + printf("Filename\t%s\n", basename(filename)); + printf("Filesize\t%zd\n", filesize); + + crc = crc32_init(); + for (i = 0; i < filesize - 4; i++) + crc = crc32_byte(crc, buf[i]); + + printf("Checksum\t%08x (%s)\n", crc, + crc == dwCRC ? "valid" : "corrupt"); + + if (crc != dwCRC) { + free(buf); + close(fd); + exit(1); + } + + printf("\n"); + + udev = open_device(device, &suffix); + if (!udev) + exit(1); + + printf("\r" " " " " " " " " " "); + printf("\rFirmware download ... "); + fflush(stdout); + + count = filesize - DFU_SUFFIX_SIZE; + block = 0; + + while (count) { + size = (count > 1023) ? 1023 : count; + + if (dfu_get_status(udev, 0, &status) < 0) { + if (try-- > 0) { + sleep(1); + continue; + } + printf("\rCan't get status: %s (%d)\n", strerror(errno), errno); + goto done; + } + + if (status.bStatus != DFU_OK) { + if (try-- > 0) { + dfu_clear_status(udev, 0); + sleep(1); + continue; + } + printf("\rFirmware download ... aborting (status %d state %d)\n", + status.bStatus, status.bState); + goto done; + } + + if (status.bState != DFU_STATE_DFU_IDLE && + status.bState != DFU_STATE_DFU_DNLOAD_IDLE) { + sleep(1); + continue; + } + + timeout = (status.bwPollTimeout[2] << 16) | + (status.bwPollTimeout[1] << 8) | + status.bwPollTimeout[0]; + + usleep(timeout * 1000); + + len = dfu_download(udev, 0, block, buf + sent, size); + if (len < 0) { + if (try-- > 0) { + sleep(1); + continue; + } + printf("\rCan't upload next block: %s (%d)\n", strerror(errno), errno); + goto done; + } + + printf("\rFirmware download ... %d bytes ", block * 1023 + len); + fflush(stdout); + + sent += len; + count -= len; + block++; + } + + printf("\r" " " " " " " " " " "); + printf("\rFinishing firmware download ... "); + fflush(stdout); + + sleep(1); + + if (dfu_get_status(udev, 0, &status) < 0) { + printf("\rCan't get status: %s (%d)\n", strerror(errno), errno); + goto done; + } + + timeout = (status.bwPollTimeout[2] << 16) | + (status.bwPollTimeout[1] << 8) | + status.bwPollTimeout[0]; + + usleep(timeout * 1000); + + if (count == 0) { + len = dfu_download(udev, 0, block, NULL, 0); + if (len < 0) { + printf("\rCan't send final block: %s (%d)\n", strerror(errno), errno); + goto done; + } + } + + printf("\r" " " " " " " " " " "); + printf("\rWaiting for device ... "); + fflush(stdout); + + sleep(10); + + printf("\n"); + +done: + free(buf); + close(fd); + + usb_release_interface(udev, 0); + usb_reset(udev); + usb_close(udev); +} + +static void cmd_archive(char *device, int argc, char **argv) +{ + struct usb_dev_handle *udev; + struct dfu_status status; + struct dfu_suffix suffix; + char buf[2048]; + unsigned long timeout = 0; + char *filename; + uint32_t crc; + int fd, i, n, len, try = 8; + + if (argc < 2) { + usage(); + exit(1); + } + + filename = argv[1]; + + udev = open_device(device, &suffix); + if (!udev) + exit(1); + + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd < 0) { + printf("Can't open firmware file: %s (%d)\n", strerror(errno), errno); + goto done; + } + + printf("\r" " " " " " " " " " "); + printf("\rFirmware upload ... "); + fflush(stdout); + + crc = crc32_init(); + n = 0; + while (1) { + if (dfu_get_status(udev, 0, &status) < 0) { + if (try-- > 0) { + sleep(1); + continue; + } + printf("\rCan't get status: %s (%d)\n", strerror(errno), errno); + goto done; + } + + if (status.bStatus != DFU_OK) { + if (try-- > 0) { + dfu_clear_status(udev, 0); + sleep(1); + continue; + } + printf("\rFirmware upload ... aborting (status %d state %d)\n", + status.bStatus, status.bState); + goto done; + } + + if (status.bState != DFU_STATE_DFU_IDLE && + status.bState != DFU_STATE_UPLOAD_IDLE) { + sleep(1); + continue; + } + + timeout = (status.bwPollTimeout[2] << 16) | + (status.bwPollTimeout[1] << 8) | + status.bwPollTimeout[0]; + + usleep(timeout * 1000); + + len = dfu_upload(udev, 0, n, buf, 1023); + if (len < 0) { + if (try-- > 0) { + sleep(1); + continue; + } + printf("\rCan't upload next block: %s (%d)\n", strerror(errno), errno); + goto done; + } + + printf("\rFirmware upload ... %d bytes ", n * 1023 + len); + fflush(stdout); + + for (i = 0; i < len; i++) + crc = crc32_byte(crc, buf[i]); + + if (len > 0) { + if (write(fd, buf, len) < 0) { + printf("\rCan't write next block: %s (%d)\n", strerror(errno), errno); + goto done; + } + } + + n++; + if (len != 1023) + break; + } + printf("\n"); + + suffix.bcdDFU = cpu_to_le16(0x0100); + suffix.ucDfuSignature[0] = 'U'; + suffix.ucDfuSignature[1] = 'F'; + suffix.ucDfuSignature[2] = 'D'; + suffix.bLength = DFU_SUFFIX_SIZE; + + memcpy(buf, &suffix, DFU_SUFFIX_SIZE); + for (i = 0; i < DFU_SUFFIX_SIZE - 4; i++) + crc = crc32_byte(crc, buf[i]); + + suffix.dwCRC = cpu_to_le32(crc); + + if (write(fd, &suffix, DFU_SUFFIX_SIZE) < 0) + printf("Can't write suffix block: %s (%d)\n", strerror(errno), errno); + +done: + close(fd); + + usb_release_interface(udev, 0); + usb_reset(udev); + usb_close(udev); +} + +struct { + char *cmd; + char *alt; + void (*func)(char *device, int argc, char **argv); + char *opt; + char *doc; +} command[] = { + { "verify", "check", cmd_verify, "", "Check firmware file" }, + { "modify", "change", cmd_modify, "", "Change firmware attributes" }, + { "upgrade", "download", cmd_upgrade, "", "Download a new firmware" }, + { "archive", "upload", cmd_archive, "", "Upload the current firmware" }, + { NULL, NULL, NULL, 0, 0 } +}; + +static void usage(void) +{ + int i; + + printf("dfutool - Device Firmware Upgrade utility ver %s\n\n", VERSION); + + printf("Usage:\n" + "\tdfutool [options] \n" + "\n"); + + printf("Options:\n" + "\t-d, --device USB device\n" + "\t-h, --help Display help\n" + "\n"); + + printf("Commands:\n"); + for (i = 0; command[i].cmd; i++) + printf("\t%-8s %-10s\t%s\n", command[i].cmd, + command[i].opt ? command[i].opt : " ", + command[i].doc); + printf("\n"); +} + +static struct option main_options[] = { + { "help", 0, 0, 'h' }, + { "device", 1, 0, 'd' }, + { 0, 0, 0, 0 } +}; + +int main(int argc, char *argv[]) +{ + char *device = NULL; + int i, opt; + + while ((opt = getopt_long(argc, argv, "+d:h", main_options, NULL)) != -1) { + switch(opt) { + case 'd': + device = strdup(optarg); + break; + + case 'h': + usage(); + exit(0); + + default: + exit(0); + } + } + + argc -= optind; + argv += optind; + optind = 0; + + if (argc < 1) { + usage(); + exit(1); + } + + usb_init(); + + for (i = 0; command[i].cmd; i++) { + if (strcmp(command[i].cmd, argv[0]) && strcmp(command[i].alt, argv[0])) + continue; + command[i].func(device, argc, argv); + exit(0); + } + + usage(); + exit(1); +} diff --git a/tools/hciattach.8 b/tools/hciattach.8 new file mode 100644 index 0000000..e0e2730 --- /dev/null +++ b/tools/hciattach.8 @@ -0,0 +1,155 @@ +.TH HCIATTACH 8 "Jan 22 2002" BlueZ "Linux System Administration" +.SH NAME +hciattach \- attach serial devices via UART HCI to BlueZ stack +.SH SYNOPSIS +.B hciattach +.RB [\| \-b \|] +.RB [\| \-n \|] +.RB [\| \-p \|] +.RB [\| \-t +.IR timeout \|] +.RB [\| \-s +.IR speed \|] +.RB [\| \-l \|] +.RB [\| \-r \|] +.I tty +.IR type \||\| id +.I speed +.I flow +.I bdaddr +.SH DESCRIPTION +.LP +Hciattach is used to attach a serial UART to the Bluetooth stack as HCI +transport interface. +.SH OPTIONS +.TP +.B \-b +Send break. +.TP +.B \-n +Don't detach from controlling terminal. +.TP +.B \-p +Print the PID when detaching. +.TP +.BI \-t " timeout" +Specify an initialization timeout. (Default is 5 seconds.) +.TP +.BI \-s " speed" +Specify an initial speed instead of the hardware default. +.TP +.B \-l +List all available configurations. +.TP +.B \-r +Set the HCI device into raw mode (the kernel and bluetoothd will ignore it). +.TP +.I tty +This specifies the serial device to attach. A leading +.B /dev +can be omitted. Examples: +.B /dev/ttyS1 +.B ttyS2 +.TP +.IR type \||\| id +The +.I type +or +.I id +of the Bluetooth device that is to be attached, i.e. vendor or other device +specific identifier. Currently supported types are +.RS +.TP +.B type +.B description +.TP +.B any +Unspecified HCI_UART interface, no vendor specific options +.TP +.B ericsson +Ericsson based modules +.TP +.B digi +Digianswer based cards +.TP +.B xircom +Xircom PCMCIA cards: Credit Card Adapter and Real Port Adapter +.TP +.B csr +CSR Casira serial adapter or BrainBoxes serial dongle (BL642) +.TP +.B bboxes +BrainBoxes PCMCIA card (BL620) +.TP +.B swave +Silicon Wave kits +.TP +.B bcsp +Serial adapters using CSR chips with BCSP serial protocol +.TP +.B ath3k +Atheros AR300x based serial Bluetooth device +.RE + +Supported IDs are (manufacturer id, product id) +.RS +.TP +.B 0x0105, 0x080a +Xircom PCMCIA cards: Credit Card Adapter and Real Port Adapter +.TP +.B 0x0160, 0x0002 +BrainBoxes PCMCIA card (BL620) +.RE + +.TP +.I speed +The +.I speed +specifies the UART speed to use. Baudrates higher than 115.200bps require +vendor specific initializations that are not implemented for all types of +devices. In general the following speeds are supported: + +.B 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600 + +Supported vendor devices are automatically initialised to their respective +best settings. +.TP +.I flow +If the keyword +.I flow +is appended to the list of options then hardware flow control is forced on +the serial link ( +.B CRTSCTS +). All above mentioned device types have +.B flow +set by default. To force no flow control use +.B noflow +instead. +.TP +.I sleep +Enables hardware specific power management feature. If +.I sleep +is appended to the list of options then this feature is enabled. To disable +this feature use +.B nosleep +instead. +All above mentioned device types have +.B nosleep +set by default. + +Note: This option will only be valid for hardware which support +hardware specific power management enable option from host. +.TP +.I bdaddr +The +.I bdaddr +specifies the Bluetooth Address to use. Some devices (like the STLC2500) +do not store the Bluetooth address in hardware memory. Instead it must +be uploaded during the initialization process. If this argument +is specified, then the address will be used to initialize the device. +Otherwise, a default address will be used. + +.SH AUTHORS +Written by Maxim Krasnyansky +.PP +Manual page by Nils Faerber diff --git a/tools/hciattach.c b/tools/hciattach.c new file mode 100644 index 0000000..e4d5aa1 --- /dev/null +++ b/tools/hciattach.c @@ -0,0 +1,1446 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2000-2001 Qualcomm Incorporated + * Copyright (C) 2002-2003 Maxim Krasnyansky + * Copyright (C) 2002-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "hciattach.h" + +#ifdef NEED_PPOLL +#include "ppoll.h" +#endif + +struct uart_t { + char *type; + int m_id; + int p_id; + int proto; + int init_speed; + int speed; + int flags; + int pm; + char *bdaddr; + int (*init) (int fd, struct uart_t *u, struct termios *ti); + int (*post) (int fd, struct uart_t *u, struct termios *ti); +}; + +#define FLOW_CTL 0x0001 +#define ENABLE_PM 1 +#define DISABLE_PM 0 + +static volatile sig_atomic_t __io_canceled = 0; + +static void sig_hup(int sig) +{ +} + +static void sig_term(int sig) +{ + __io_canceled = 1; +} + +static void sig_alarm(int sig) +{ + fprintf(stderr, "Initialization timed out.\n"); + exit(1); +} + +static int uart_speed(int s) +{ + switch (s) { + case 9600: + return B9600; + case 19200: + return B19200; + case 38400: + return B38400; + case 57600: + return B57600; + case 115200: + return B115200; + case 230400: + return B230400; + case 460800: + return B460800; + case 500000: + return B500000; + case 576000: + return B576000; + case 921600: + return B921600; + case 1000000: + return B1000000; + case 1152000: + return B1152000; + case 1500000: + return B1500000; + case 2000000: + return B2000000; +#ifdef B2500000 + case 2500000: + return B2500000; +#endif +#ifdef B3000000 + case 3000000: + return B3000000; +#endif +#ifdef B3500000 + case 3500000: + return B3500000; +#endif +#ifdef B4000000 + case 4000000: + return B4000000; +#endif + default: + return B57600; + } +} + +int set_speed(int fd, struct termios *ti, int speed) +{ + if (cfsetospeed(ti, uart_speed(speed)) < 0) + return -errno; + + if (cfsetispeed(ti, uart_speed(speed)) < 0) + return -errno; + + if (tcsetattr(fd, TCSANOW, ti) < 0) + return -errno; + + return 0; +} + +/* + * Read an HCI event from the given file descriptor. + */ +int read_hci_event(int fd, unsigned char* buf, int size) +{ + int remain, r; + int count = 0; + + if (size <= 0) + return -1; + + /* The first byte identifies the packet type. For HCI event packets, it + * should be 0x04, so we read until we get to the 0x04. */ + while (1) { + r = read(fd, buf, 1); + if (r <= 0) + return -1; + if (buf[0] == 0x04) + break; + } + count++; + + /* The next two bytes are the event code and parameter total length. */ + while (count < 3) { + r = read(fd, buf + count, 3 - count); + if (r <= 0) + return -1; + count += r; + } + + /* Now we read the parameters. */ + if (buf[2] < (size - 3)) + remain = buf[2]; + else + remain = size - 3; + + while ((count - 3) < remain) { + r = read(fd, buf + count, remain - (count - 3)); + if (r <= 0) + return -1; + count += r; + } + + return count; +} + +/* + * Ericsson specific initialization + */ +static int ericsson(int fd, struct uart_t *u, struct termios *ti) +{ + struct timespec tm = {0, 50000}; + char cmd[5]; + + cmd[0] = HCI_COMMAND_PKT; + cmd[1] = 0x09; + cmd[2] = 0xfc; + cmd[3] = 0x01; + + switch (u->speed) { + case 57600: + cmd[4] = 0x03; + break; + case 115200: + cmd[4] = 0x02; + break; + case 230400: + cmd[4] = 0x01; + break; + case 460800: + cmd[4] = 0x00; + break; + case 921600: + cmd[4] = 0x20; + break; + case 2000000: + cmd[4] = 0x25; + break; + case 3000000: + cmd[4] = 0x27; + break; + case 4000000: + cmd[4] = 0x2B; + break; + default: + cmd[4] = 0x03; + u->speed = 57600; + fprintf(stderr, "Invalid speed requested, using %d bps instead\n", u->speed); + break; + } + + /* Send initialization command */ + if (write(fd, cmd, 5) != 5) { + perror("Failed to write init command"); + return -1; + } + + nanosleep(&tm, NULL); + return 0; +} + +/* + * Digianswer specific initialization + */ +static int digi(int fd, struct uart_t *u, struct termios *ti) +{ + struct timespec tm = {0, 50000}; + char cmd[5]; + + /* DigiAnswer set baud rate command */ + cmd[0] = HCI_COMMAND_PKT; + cmd[1] = 0x07; + cmd[2] = 0xfc; + cmd[3] = 0x01; + + switch (u->speed) { + case 57600: + cmd[4] = 0x08; + break; + case 115200: + cmd[4] = 0x09; + break; + default: + cmd[4] = 0x09; + u->speed = 115200; + break; + } + + /* Send initialization command */ + if (write(fd, cmd, 5) != 5) { + perror("Failed to write init command"); + return -1; + } + + nanosleep(&tm, NULL); + return 0; +} + +static int texas(int fd, struct uart_t *u, struct termios *ti) +{ + return texas_init(fd, ti); +} + +static int texas2(int fd, struct uart_t *u, struct termios *ti) +{ + return texas_post(fd, ti); +} + +static int texasalt(int fd, struct uart_t *u, struct termios *ti) +{ + return texasalt_init(fd, u->speed, ti); +} + +static int ath3k_ps(int fd, struct uart_t *u, struct termios *ti) +{ + return ath3k_init(fd, u->speed, u->init_speed, u->bdaddr, ti); +} + +static int ath3k_pm(int fd, struct uart_t *u, struct termios *ti) +{ + return ath3k_post(fd, u->pm); +} + +static int qualcomm(int fd, struct uart_t *u, struct termios *ti) +{ + return qualcomm_init(fd, u->speed, ti, u->bdaddr); +} + +static int read_check(int fd, void *buf, int count) +{ + int res; + + do { + res = read(fd, buf, count); + if (res != -1) { + buf += res; + count -= res; + } + } while (count && (errno == 0 || errno == EINTR)); + + if (count) + return -1; + + return 0; +} + +/* + * BCSP specific initialization + */ +static int serial_fd; +static int bcsp_max_retries = 10; + +static void bcsp_tshy_sig_alarm(int sig) +{ + unsigned char bcsp_sync_pkt[10] = {0xc0,0x00,0x41,0x00,0xbe,0xda,0xdc,0xed,0xed,0xc0}; + int len; + static int retries = 0; + + if (retries < bcsp_max_retries) { + retries++; + len = write(serial_fd, &bcsp_sync_pkt, 10); + alarm(1); + return; + } + + tcflush(serial_fd, TCIOFLUSH); + fprintf(stderr, "BCSP initialization timed out\n"); + exit(1); +} + +static void bcsp_tconf_sig_alarm(int sig) +{ + unsigned char bcsp_conf_pkt[10] = {0xc0,0x00,0x41,0x00,0xbe,0xad,0xef,0xac,0xed,0xc0}; + int len; + static int retries = 0; + + if (retries < bcsp_max_retries){ + retries++; + len = write(serial_fd, &bcsp_conf_pkt, 10); + alarm(1); + return; + } + + tcflush(serial_fd, TCIOFLUSH); + fprintf(stderr, "BCSP initialization timed out\n"); + exit(1); +} + +static int bcsp(int fd, struct uart_t *u, struct termios *ti) +{ + unsigned char byte, bcsph[4], bcspp[4], + bcsp_sync_resp_pkt[10] = {0xc0,0x00,0x41,0x00,0xbe,0xac,0xaf,0xef,0xee,0xc0}, + bcsp_conf_resp_pkt[10] = {0xc0,0x00,0x41,0x00,0xbe,0xde,0xad,0xd0,0xd0,0xc0}, + bcspsync[4] = {0xda, 0xdc, 0xed, 0xed}, + bcspsyncresp[4] = {0xac,0xaf,0xef,0xee}, + bcspconf[4] = {0xad,0xef,0xac,0xed}, + bcspconfresp[4] = {0xde,0xad,0xd0,0xd0}; + struct sigaction sa; + int len; + + if (set_speed(fd, ti, u->speed) < 0) { + perror("Can't set default baud rate"); + return -1; + } + + ti->c_cflag |= PARENB; + ti->c_cflag &= ~(PARODD); + + if (tcsetattr(fd, TCSANOW, ti) < 0) { + perror("Can't set port settings"); + return -1; + } + + alarm(0); + + serial_fd = fd; + memset(&sa, 0, sizeof(sa)); + sa.sa_flags = SA_NOCLDSTOP; + sa.sa_handler = bcsp_tshy_sig_alarm; + sigaction(SIGALRM, &sa, NULL); + + /* State = shy */ + + bcsp_tshy_sig_alarm(0); + while (1) { + do { + if (read_check(fd, &byte, 1) == -1){ + perror("Failed to read"); + return -1; + } + } while (byte != 0xC0); + + do { + if ( read_check(fd, &bcsph[0], 1) == -1){ + perror("Failed to read"); + return -1; + } + } while (bcsph[0] == 0xC0); + + if ( read_check(fd, &bcsph[1], 3) == -1){ + perror("Failed to read"); + return -1; + } + + if (((bcsph[0] + bcsph[1] + bcsph[2]) & 0xFF) != (unsigned char)~bcsph[3]) + continue; + if (bcsph[1] != 0x41 || bcsph[2] != 0x00) + continue; + + if (read_check(fd, &bcspp, 4) == -1){ + perror("Failed to read"); + return -1; + } + + if (!memcmp(bcspp, bcspsync, 4)) { + len = write(fd, &bcsp_sync_resp_pkt,10); + } else if (!memcmp(bcspp, bcspsyncresp, 4)) + break; + } + + /* State = curious */ + + alarm(0); + sa.sa_handler = bcsp_tconf_sig_alarm; + sigaction(SIGALRM, &sa, NULL); + alarm(1); + + while (1) { + do { + if (read_check(fd, &byte, 1) == -1){ + perror("Failed to read"); + return -1; + } + } while (byte != 0xC0); + + do { + if (read_check(fd, &bcsph[0], 1) == -1){ + perror("Failed to read"); + return -1; + } + } while (bcsph[0] == 0xC0); + + if (read_check(fd, &bcsph[1], 3) == -1){ + perror("Failed to read"); + return -1; + } + + if (((bcsph[0] + bcsph[1] + bcsph[2]) & 0xFF) != (unsigned char)~bcsph[3]) + continue; + + if (bcsph[1] != 0x41 || bcsph[2] != 0x00) + continue; + + if (read_check(fd, &bcspp, 4) == -1){ + perror("Failed to read"); + return -1; + } + + if (!memcmp(bcspp, bcspsync, 4)) + len = write(fd, &bcsp_sync_resp_pkt, 10); + else if (!memcmp(bcspp, bcspconf, 4)) + len = write(fd, &bcsp_conf_resp_pkt, 10); + else if (!memcmp(bcspp, bcspconfresp, 4)) + break; + } + + /* State = garrulous */ + + return 0; +} + +/* + * CSR specific initialization + * Inspired strongly by code in OpenBT and experimentations with Brainboxes + * Pcmcia card. + * Jean Tourrilhes - 14.11.01 + */ +static int csr(int fd, struct uart_t *u, struct termios *ti) +{ + struct timespec tm = {0, 10000000}; /* 10ms - be generous */ + unsigned char cmd[30]; /* Command */ + unsigned char resp[30]; /* Response */ + int clen = 0; /* Command len */ + static int csr_seq = 0; /* Sequence number of command */ + int divisor; + + /* It seems that if we set the CSR UART speed straight away, it + * won't work, the CSR UART gets into a state where we can't talk + * to it anymore. + * On the other hand, doing a read before setting the CSR speed + * seems to be ok. + * Therefore, the strategy is to read the build ID (useful for + * debugging) and only then set the CSR UART speed. Doing like + * this is more complex but at least it works ;-) + * The CSR UART control may be slow to wake up or something because + * every time I read its speed, its bogus... + * Jean II */ + + /* Try to read the build ID of the CSR chip */ + clen = 5 + (5 + 6) * 2; + /* HCI header */ + cmd[0] = HCI_COMMAND_PKT; + cmd[1] = 0x00; /* CSR command */ + cmd[2] = 0xfc; /* MANUFACTURER_SPEC */ + cmd[3] = 1 + (5 + 6) * 2; /* len */ + /* CSR MSG header */ + cmd[4] = 0xC2; /* first+last+channel=BCC */ + /* CSR BCC header */ + cmd[5] = 0x00; /* type = GET-REQ */ + cmd[6] = 0x00; /* - msB */ + cmd[7] = 5 + 4; /* len */ + cmd[8] = 0x00; /* - msB */ + cmd[9] = csr_seq & 0xFF;/* seq num */ + cmd[10] = (csr_seq >> 8) & 0xFF; /* - msB */ + csr_seq++; + cmd[11] = 0x19; /* var_id = CSR_CMD_BUILD_ID */ + cmd[12] = 0x28; /* - msB */ + cmd[13] = 0x00; /* status = STATUS_OK */ + cmd[14] = 0x00; /* - msB */ + /* CSR BCC payload */ + memset(cmd + 15, 0, 6 * 2); + + /* Send command */ + do { + if (write(fd, cmd, clen) != clen) { + perror("Failed to write init command (GET_BUILD_ID)"); + return -1; + } + + /* Read reply. */ + if (read_hci_event(fd, resp, 100) < 0) { + perror("Failed to read init response (GET_BUILD_ID)"); + return -1; + } + + /* Event code 0xFF is for vendor-specific events, which is + * what we're looking for. */ + } while (resp[1] != 0xFF); + +#ifdef CSR_DEBUG + { + char temp[512]; + int i; + for (i=0; i < rlen; i++) + sprintf(temp + (i*3), "-%02X", resp[i]); + fprintf(stderr, "Reading CSR build ID %d [%s]\n", rlen, temp + 1); + // In theory, it should look like : + // 04-FF-13-FF-01-00-09-00-00-00-19-28-00-00-73-00-00-00-00-00-00-00 + } +#endif + /* Display that to user */ + fprintf(stderr, "CSR build ID 0x%02X-0x%02X\n", + resp[15] & 0xFF, resp[14] & 0xFF); + + /* Try to read the current speed of the CSR chip */ + clen = 5 + (5 + 4)*2; + /* -- HCI header */ + cmd[3] = 1 + (5 + 4)*2; /* len */ + /* -- CSR BCC header -- */ + cmd[9] = csr_seq & 0xFF; /* seq num */ + cmd[10] = (csr_seq >> 8) & 0xFF; /* - msB */ + csr_seq++; + cmd[11] = 0x02; /* var_id = CONFIG_UART */ + cmd[12] = 0x68; /* - msB */ + +#ifdef CSR_DEBUG + /* Send command */ + do { + if (write(fd, cmd, clen) != clen) { + perror("Failed to write init command (GET_BUILD_ID)"); + return -1; + } + + /* Read reply. */ + if (read_hci_event(fd, resp, 100) < 0) { + perror("Failed to read init response (GET_BUILD_ID)"); + return -1; + } + + /* Event code 0xFF is for vendor-specific events, which is + * what we're looking for. */ + } while (resp[1] != 0xFF); + + { + char temp[512]; + int i; + for (i=0; i < rlen; i++) + sprintf(temp + (i*3), "-%02X", resp[i]); + fprintf(stderr, "Reading CSR UART speed %d [%s]\n", rlen, temp+1); + } +#endif + + if (u->speed > 1500000) { + fprintf(stderr, "Speed %d too high. Remaining at %d baud\n", + u->speed, u->init_speed); + u->speed = u->init_speed; + } else if (u->speed != 57600 && uart_speed(u->speed) == B57600) { + /* Unknown speed. Why oh why can't we just pass an int to the kernel? */ + fprintf(stderr, "Speed %d unrecognised. Remaining at %d baud\n", + u->speed, u->init_speed); + u->speed = u->init_speed; + } + if (u->speed == u->init_speed) + return 0; + + /* Now, create the command that will set the UART speed */ + /* CSR BCC header */ + cmd[5] = 0x02; /* type = SET-REQ */ + cmd[6] = 0x00; /* - msB */ + cmd[9] = csr_seq & 0xFF; /* seq num */ + cmd[10] = (csr_seq >> 8) & 0xFF;/* - msB */ + csr_seq++; + + divisor = (u->speed*64+7812)/15625; + + /* No parity, one stop bit -> divisor |= 0x0000; */ + cmd[15] = (divisor) & 0xFF; /* divider */ + cmd[16] = (divisor >> 8) & 0xFF; /* - msB */ + /* The rest of the payload will be 0x00 */ + +#ifdef CSR_DEBUG + { + char temp[512]; + int i; + for(i = 0; i < clen; i++) + sprintf(temp + (i*3), "-%02X", cmd[i]); + fprintf(stderr, "Writing CSR UART speed %d [%s]\n", clen, temp + 1); + // In theory, it should look like : + // 01-00-FC-13-C2-02-00-09-00-03-00-02-68-00-00-BF-0E-00-00-00-00-00-00 + // 01-00-FC-13-C2-02-00-09-00-01-00-02-68-00-00-D8-01-00-00-00-00-00-00 + } +#endif + + /* Send the command to set the CSR UART speed */ + if (write(fd, cmd, clen) != clen) { + perror("Failed to write init command (SET_UART_SPEED)"); + return -1; + } + + nanosleep(&tm, NULL); + return 0; +} + +/* + * Silicon Wave specific initialization + * Thomas Moser + */ +static int swave(int fd, struct uart_t *u, struct termios *ti) +{ + struct timespec tm = { 0, 500000 }; + char cmd[10], rsp[100]; + int r; + + // Silicon Wave set baud rate command + // see HCI Vendor Specific Interface from Silicon Wave + // first send a "param access set" command to set the + // appropriate data fields in RAM. Then send a "HCI Reset + // Subcommand", e.g. "soft reset" to make the changes effective. + + cmd[0] = HCI_COMMAND_PKT; // it's a command packet + cmd[1] = 0x0B; // OCF 0x0B = param access set + cmd[2] = 0xfc; // OGF bx111111 = vendor specific + cmd[3] = 0x06; // 6 bytes of data following + cmd[4] = 0x01; // param sub command + cmd[5] = 0x11; // tag 17 = 0x11 = HCI Transport Params + cmd[6] = 0x03; // length of the parameter following + cmd[7] = 0x01; // HCI Transport flow control enable + cmd[8] = 0x01; // HCI Transport Type = UART + + switch (u->speed) { + case 19200: + cmd[9] = 0x03; + break; + case 38400: + cmd[9] = 0x02; + break; + case 57600: + cmd[9] = 0x01; + break; + case 115200: + cmd[9] = 0x00; + break; + default: + u->speed = 115200; + cmd[9] = 0x00; + break; + } + + /* Send initialization command */ + if (write(fd, cmd, 10) != 10) { + perror("Failed to write init command"); + return -1; + } + + // We should wait for a "GET Event" to confirm the success of + // the baud rate setting. Wait some time before reading. Better: + // read with timeout, parse data + // until correct answer, else error handling ... todo ... + + nanosleep(&tm, NULL); + + r = read(fd, rsp, sizeof(rsp)); + if (r > 0) { + // guess it's okay, but we should parse the reply. But since + // I don't react on an error anyway ... todo + // Response packet format: + // 04 Event + // FF Vendor specific + // 07 Parameter length + // 0B Subcommand + // 01 Setevent + // 11 Tag specifying HCI Transport Layer Parameter + // 03 length + // 01 flow on + // 01 Hci Transport type = Uart + // xx Baud rate set (see above) + } else { + // ups, got error. + return -1; + } + + // we probably got the reply. Now we must send the "soft reset" + // which is standard HCI RESET. + + cmd[0] = HCI_COMMAND_PKT; // it's a command packet + cmd[1] = 0x03; + cmd[2] = 0x0c; + cmd[3] = 0x00; + + /* Send reset command */ + if (write(fd, cmd, 4) != 4) { + perror("Can't write Silicon Wave reset cmd."); + return -1; + } + + nanosleep(&tm, NULL); + + // now the uart baud rate on the silicon wave module is set and effective. + // change our own baud rate as well. Then there is a reset event comming in + // on the *new* baud rate. This is *undocumented*! The packet looks like this: + // 04 FF 01 0B (which would make that a confirmation of 0x0B = "Param + // subcommand class". So: change to new baud rate, read with timeout, parse + // data, error handling. BTW: all param access in Silicon Wave is done this way. + // Maybe this code would belong in a seperate file, or at least code reuse... + + return 0; +} + +/* + * ST Microelectronics specific initialization + * Marcel Holtmann + */ +static int st(int fd, struct uart_t *u, struct termios *ti) +{ + struct timespec tm = {0, 50000}; + char cmd[5]; + + /* ST Microelectronics set baud rate command */ + cmd[0] = HCI_COMMAND_PKT; + cmd[1] = 0x46; // OCF = Hci_Cmd_ST_Set_Uart_Baud_Rate + cmd[2] = 0xfc; // OGF = Vendor specific + cmd[3] = 0x01; + + switch (u->speed) { + case 9600: + cmd[4] = 0x09; + break; + case 19200: + cmd[4] = 0x0b; + break; + case 38400: + cmd[4] = 0x0d; + break; + case 57600: + cmd[4] = 0x0e; + break; + case 115200: + cmd[4] = 0x10; + break; + case 230400: + cmd[4] = 0x12; + break; + case 460800: + cmd[4] = 0x13; + break; + case 921600: + cmd[4] = 0x14; + break; + default: + cmd[4] = 0x10; + u->speed = 115200; + break; + } + + /* Send initialization command */ + if (write(fd, cmd, 5) != 5) { + perror("Failed to write init command"); + return -1; + } + + nanosleep(&tm, NULL); + return 0; +} + +static int stlc2500(int fd, struct uart_t *u, struct termios *ti) +{ + bdaddr_t bdaddr; + unsigned char resp[10]; + int n; + int rvalue; + + /* STLC2500 has an ericsson core */ + rvalue = ericsson(fd, u, ti); + if (rvalue != 0) + return rvalue; + +#ifdef STLC2500_DEBUG + fprintf(stderr, "Setting speed\n"); +#endif + if (set_speed(fd, ti, u->speed) < 0) { + perror("Can't set baud rate"); + return -1; + } + +#ifdef STLC2500_DEBUG + fprintf(stderr, "Speed set...\n"); +#endif + + /* Read reply */ + if ((n = read_hci_event(fd, resp, 10)) < 0) { + fprintf(stderr, "Failed to set baud rate on chip\n"); + return -1; + } + +#ifdef STLC2500_DEBUG + for (i = 0; i < n; i++) { + fprintf(stderr, "resp[%d] = %02x\n", i, resp[i]); + } +#endif + + str2ba(u->bdaddr, &bdaddr); + return stlc2500_init(fd, &bdaddr); +} + +static int bgb2xx(int fd, struct uart_t *u, struct termios *ti) +{ + bdaddr_t bdaddr; + + str2ba(u->bdaddr, &bdaddr); + + return bgb2xx_init(fd, &bdaddr); +} + +/* + * Broadcom specific initialization + * Extracted from Jungo openrg + */ +static int bcm2035(int fd, struct uart_t *u, struct termios *ti) +{ + int n; + unsigned char cmd[30], resp[30]; + + /* Reset the BT Chip */ + memset(cmd, 0, sizeof(cmd)); + memset(resp, 0, sizeof(resp)); + cmd[0] = HCI_COMMAND_PKT; + cmd[1] = 0x03; + cmd[2] = 0x0c; + cmd[3] = 0x00; + + /* Send command */ + if (write(fd, cmd, 4) != 4) { + fprintf(stderr, "Failed to write reset command\n"); + return -1; + } + + /* Read reply */ + if ((n = read_hci_event(fd, resp, 4)) < 0) { + fprintf(stderr, "Failed to reset chip\n"); + return -1; + } + + if (u->bdaddr != NULL) { + /* Set BD_ADDR */ + memset(cmd, 0, sizeof(cmd)); + memset(resp, 0, sizeof(resp)); + cmd[0] = HCI_COMMAND_PKT; + cmd[1] = 0x01; + cmd[2] = 0xfc; + cmd[3] = 0x06; + str2ba(u->bdaddr, (bdaddr_t *) (cmd + 4)); + + /* Send command */ + if (write(fd, cmd, 10) != 10) { + fprintf(stderr, "Failed to write BD_ADDR command\n"); + return -1; + } + + /* Read reply */ + if ((n = read_hci_event(fd, resp, 10)) < 0) { + fprintf(stderr, "Failed to set BD_ADDR\n"); + return -1; + } + } + + /* Read the local version info */ + memset(cmd, 0, sizeof(cmd)); + memset(resp, 0, sizeof(resp)); + cmd[0] = HCI_COMMAND_PKT; + cmd[1] = 0x01; + cmd[2] = 0x10; + cmd[3] = 0x00; + + /* Send command */ + if (write(fd, cmd, 4) != 4) { + fprintf(stderr, "Failed to write \"read local version\" " + "command\n"); + return -1; + } + + /* Read reply */ + if ((n = read_hci_event(fd, resp, 4)) < 0) { + fprintf(stderr, "Failed to read local version\n"); + return -1; + } + + /* Read the local supported commands info */ + memset(cmd, 0, sizeof(cmd)); + memset(resp, 0, sizeof(resp)); + cmd[0] = HCI_COMMAND_PKT; + cmd[1] = 0x02; + cmd[2] = 0x10; + cmd[3] = 0x00; + + /* Send command */ + if (write(fd, cmd, 4) != 4) { + fprintf(stderr, "Failed to write \"read local supported " + "commands\" command\n"); + return -1; + } + + /* Read reply */ + if ((n = read_hci_event(fd, resp, 4)) < 0) { + fprintf(stderr, "Failed to read local supported commands\n"); + return -1; + } + + /* Set the baud rate */ + memset(cmd, 0, sizeof(cmd)); + memset(resp, 0, sizeof(resp)); + cmd[0] = HCI_COMMAND_PKT; + cmd[1] = 0x18; + cmd[2] = 0xfc; + cmd[3] = 0x02; + switch (u->speed) { + case 57600: + cmd[4] = 0x00; + cmd[5] = 0xe6; + break; + case 230400: + cmd[4] = 0x22; + cmd[5] = 0xfa; + break; + case 460800: + cmd[4] = 0x22; + cmd[5] = 0xfd; + break; + case 921600: + cmd[4] = 0x55; + cmd[5] = 0xff; + break; + default: + /* Default is 115200 */ + cmd[4] = 0x00; + cmd[5] = 0xf3; + break; + } + fprintf(stderr, "Baud rate parameters: DHBR=0x%2x,DLBR=0x%2x\n", + cmd[4], cmd[5]); + + /* Send command */ + if (write(fd, cmd, 6) != 6) { + fprintf(stderr, "Failed to write \"set baud rate\" command\n"); + return -1; + } + + if ((n = read_hci_event(fd, resp, 6)) < 0) { + fprintf(stderr, "Failed to set baud rate\n"); + return -1; + } + + return 0; +} + +struct uart_t uart[] = { + { "any", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200, + FLOW_CTL, DISABLE_PM, NULL, NULL }, + + { "ericsson", 0x0000, 0x0000, HCI_UART_H4, 57600, 115200, + FLOW_CTL, DISABLE_PM, NULL, ericsson }, + + { "digi", 0x0000, 0x0000, HCI_UART_H4, 9600, 115200, + FLOW_CTL, DISABLE_PM, NULL, digi }, + + { "bcsp", 0x0000, 0x0000, HCI_UART_BCSP, 115200, 115200, + 0, DISABLE_PM, NULL, bcsp }, + + /* Xircom PCMCIA cards: Credit Card Adapter and Real Port Adapter */ + { "xircom", 0x0105, 0x080a, HCI_UART_H4, 115200, 115200, + FLOW_CTL, DISABLE_PM, NULL, NULL }, + + /* CSR Casira serial adapter or BrainBoxes serial dongle (BL642) */ + { "csr", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200, + FLOW_CTL, DISABLE_PM, NULL, csr }, + + /* BrainBoxes PCMCIA card (BL620) */ + { "bboxes", 0x0160, 0x0002, HCI_UART_H4, 115200, 460800, + FLOW_CTL, DISABLE_PM, NULL, csr }, + + /* Silicon Wave kits */ + { "swave", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200, + FLOW_CTL, DISABLE_PM, NULL, swave }, + + /* Texas Instruments Bluelink (BRF) modules */ + { "texas", 0x0000, 0x0000, HCI_UART_LL, 115200, 115200, + FLOW_CTL, DISABLE_PM, NULL, texas, texas2 }, + + { "texasalt", 0x0000, 0x0000, HCI_UART_LL, 115200, 115200, + FLOW_CTL, DISABLE_PM, NULL, texasalt, NULL }, + + /* ST Microelectronics minikits based on STLC2410/STLC2415 */ + { "st", 0x0000, 0x0000, HCI_UART_H4, 57600, 115200, + FLOW_CTL, DISABLE_PM, NULL, st }, + + /* ST Microelectronics minikits based on STLC2500 */ + { "stlc2500", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200, + FLOW_CTL, DISABLE_PM, "00:80:E1:00:AB:BA", stlc2500 }, + + /* Philips generic Ericsson IP core based */ + { "philips", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200, + FLOW_CTL, DISABLE_PM, NULL, NULL }, + + /* Philips BGB2xx Module */ + { "bgb2xx", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200, + FLOW_CTL, DISABLE_PM, "BD:B2:10:00:AB:BA", bgb2xx }, + + /* Sphinx Electronics PICO Card */ + { "picocard", 0x025e, 0x1000, HCI_UART_H4, 115200, 115200, + FLOW_CTL, DISABLE_PM, NULL, NULL }, + + /* Inventel BlueBird Module */ + { "inventel", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200, + FLOW_CTL, DISABLE_PM, NULL, NULL }, + + /* COM One Platinium Bluetooth PC Card */ + { "comone", 0xffff, 0x0101, HCI_UART_BCSP, 115200, 115200, + 0, DISABLE_PM, NULL, bcsp }, + + /* TDK Bluetooth PC Card and IBM Bluetooth PC Card II */ + { "tdk", 0x0105, 0x4254, HCI_UART_BCSP, 115200, 115200, + 0, DISABLE_PM, NULL, bcsp }, + + /* Socket Bluetooth CF Card (Rev G) */ + { "socket", 0x0104, 0x0096, HCI_UART_BCSP, 230400, 230400, + 0, DISABLE_PM, NULL, bcsp }, + + /* 3Com Bluetooth Card (Version 3.0) */ + { "3com", 0x0101, 0x0041, HCI_UART_H4, 115200, 115200, + FLOW_CTL, DISABLE_PM, NULL, csr }, + + /* AmbiCom BT2000C Bluetooth PC/CF Card */ + { "bt2000c", 0x022d, 0x2000, HCI_UART_H4, 57600, 460800, + FLOW_CTL, DISABLE_PM, NULL, csr }, + + /* Zoom Bluetooth PCMCIA Card */ + { "zoom", 0x0279, 0x950b, HCI_UART_BCSP, 115200, 115200, + 0, DISABLE_PM, NULL, bcsp }, + + /* Sitecom CN-504 PCMCIA Card */ + { "sitecom", 0x0279, 0x950b, HCI_UART_BCSP, 115200, 115200, + 0, DISABLE_PM, NULL, bcsp }, + + /* Billionton PCBTC1 PCMCIA Card */ + { "billionton", 0x0279, 0x950b, HCI_UART_BCSP, 115200, 115200, + 0, DISABLE_PM, NULL, bcsp }, + + /* Broadcom BCM2035 */ + { "bcm2035", 0x0A5C, 0x2035, HCI_UART_H4, 115200, 460800, + FLOW_CTL, DISABLE_PM, NULL, bcm2035 }, + + { "ath3k", 0x0000, 0x0000, HCI_UART_ATH3K, 115200, 115200, + FLOW_CTL, DISABLE_PM, NULL, ath3k_ps, ath3k_pm }, + + /* QUALCOMM BTS */ + { "qualcomm", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200, + FLOW_CTL, DISABLE_PM, NULL, qualcomm, NULL }, + + { NULL, 0 } +}; + +static struct uart_t * get_by_id(int m_id, int p_id) +{ + int i; + for (i = 0; uart[i].type; i++) { + if (uart[i].m_id == m_id && uart[i].p_id == p_id) + return &uart[i]; + } + return NULL; +} + +static struct uart_t * get_by_type(char *type) +{ + int i; + for (i = 0; uart[i].type; i++) { + if (!strcmp(uart[i].type, type)) + return &uart[i]; + } + return NULL; +} + +/* Initialize UART driver */ +static int init_uart(char *dev, struct uart_t *u, int send_break, int raw) +{ + struct termios ti; + int fd, i; + unsigned long flags = 0; + + if (raw) + flags |= 1 << HCI_UART_RAW_DEVICE; + + fd = open(dev, O_RDWR | O_NOCTTY); + if (fd < 0) { + perror("Can't open serial port"); + return -1; + } + + tcflush(fd, TCIOFLUSH); + + if (tcgetattr(fd, &ti) < 0) { + perror("Can't get port settings"); + return -1; + } + + cfmakeraw(&ti); + + ti.c_cflag |= CLOCAL; + if (u->flags & FLOW_CTL) + ti.c_cflag |= CRTSCTS; + else + ti.c_cflag &= ~CRTSCTS; + + if (tcsetattr(fd, TCSANOW, &ti) < 0) { + perror("Can't set port settings"); + return -1; + } + + /* Set initial baudrate */ + if (set_speed(fd, &ti, u->init_speed) < 0) { + perror("Can't set initial baud rate"); + return -1; + } + + tcflush(fd, TCIOFLUSH); + + if (send_break) { + tcsendbreak(fd, 0); + usleep(500000); + } + + if (u->init && u->init(fd, u, &ti) < 0) + return -1; + + tcflush(fd, TCIOFLUSH); + + /* Set actual baudrate */ + if (set_speed(fd, &ti, u->speed) < 0) { + perror("Can't set baud rate"); + return -1; + } + + /* Set TTY to N_HCI line discipline */ + i = N_HCI; + if (ioctl(fd, TIOCSETD, &i) < 0) { + perror("Can't set line discipline"); + return -1; + } + + if (flags && ioctl(fd, HCIUARTSETFLAGS, flags) < 0) { + perror("Can't set UART flags"); + return -1; + } + + if (ioctl(fd, HCIUARTSETPROTO, u->proto) < 0) { + perror("Can't set device"); + return -1; + } + + if (u->post && u->post(fd, u, &ti) < 0) + return -1; + + return fd; +} + +static void usage(void) +{ + printf("hciattach - HCI UART driver initialization utility\n"); + printf("Usage:\n"); + printf("\thciattach [-n] [-p] [-b] [-r] [-t timeout] [-s initial_speed] [speed] [flow|noflow] [bdaddr]\n"); + printf("\thciattach -l\n"); +} + +int main(int argc, char *argv[]) +{ + struct uart_t *u = NULL; + int detach, printpid, raw, opt, i, n, ld, err; + int to = 10; + int init_speed = 0; + int send_break = 0; + pid_t pid; + struct sigaction sa; + struct pollfd p; + sigset_t sigs; + char dev[PATH_MAX]; + + detach = 1; + printpid = 0; + raw = 0; + + while ((opt=getopt(argc, argv, "bnpt:s:lr")) != EOF) { + switch(opt) { + case 'b': + send_break = 1; + break; + + case 'n': + detach = 0; + break; + + case 'p': + printpid = 1; + break; + + case 't': + to = atoi(optarg); + break; + + case 's': + init_speed = atoi(optarg); + break; + + case 'l': + for (i = 0; uart[i].type; i++) { + printf("%-10s0x%04x,0x%04x\n", uart[i].type, + uart[i].m_id, uart[i].p_id); + } + exit(0); + + case 'r': + raw = 1; + break; + + default: + usage(); + exit(1); + } + } + + n = argc - optind; + if (n < 2) { + usage(); + exit(1); + } + + for (n = 0; optind < argc; n++, optind++) { + char *opt; + + opt = argv[optind]; + + switch(n) { + case 0: + dev[0] = 0; + if (!strchr(opt, '/')) + strcpy(dev, "/dev/"); + strcat(dev, opt); + break; + + case 1: + if (strchr(argv[optind], ',')) { + int m_id, p_id; + sscanf(argv[optind], "%x,%x", &m_id, &p_id); + u = get_by_id(m_id, p_id); + } else { + u = get_by_type(opt); + } + + if (!u) { + fprintf(stderr, "Unknown device type or id\n"); + exit(1); + } + + break; + + case 2: + u->speed = atoi(argv[optind]); + break; + + case 3: + if (!strcmp("flow", argv[optind])) + u->flags |= FLOW_CTL; + else + u->flags &= ~FLOW_CTL; + break; + + case 4: + if (!strcmp("sleep", argv[optind])) + u->pm = ENABLE_PM; + else + u->pm = DISABLE_PM; + break; + + case 5: + u->bdaddr = argv[optind]; + break; + } + } + + if (!u) { + fprintf(stderr, "Unknown device type or id\n"); + exit(1); + } + + /* If user specified a initial speed, use that instead of + the hardware's default */ + if (init_speed) + u->init_speed = init_speed; + + memset(&sa, 0, sizeof(sa)); + sa.sa_flags = SA_NOCLDSTOP; + sa.sa_handler = sig_alarm; + sigaction(SIGALRM, &sa, NULL); + + /* 10 seconds should be enough for initialization */ + alarm(to); + bcsp_max_retries = to; + + n = init_uart(dev, u, send_break, raw); + if (n < 0) { + perror("Can't initialize device"); + exit(1); + } + + printf("Device setup complete\n"); + + alarm(0); + + memset(&sa, 0, sizeof(sa)); + sa.sa_flags = SA_NOCLDSTOP; + sa.sa_handler = SIG_IGN; + sigaction(SIGCHLD, &sa, NULL); + sigaction(SIGPIPE, &sa, NULL); + + sa.sa_handler = sig_term; + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + + sa.sa_handler = sig_hup; + sigaction(SIGHUP, &sa, NULL); + + if (detach) { + if ((pid = fork())) { + if (printpid) + printf("%d\n", pid); + return 0; + } + + for (i = 0; i < 20; i++) + if (i != n) + close(i); + } + + p.fd = n; + p.events = POLLERR | POLLHUP; + + sigfillset(&sigs); + sigdelset(&sigs, SIGCHLD); + sigdelset(&sigs, SIGPIPE); + sigdelset(&sigs, SIGTERM); + sigdelset(&sigs, SIGINT); + sigdelset(&sigs, SIGHUP); + + while (!__io_canceled) { + p.revents = 0; + err = ppoll(&p, 1, NULL, &sigs); + if (err < 0 && errno == EINTR) + continue; + if (err) + break; + } + + /* Restore TTY line discipline */ + ld = N_TTY; + if (ioctl(n, TIOCSETD, &ld) < 0) { + perror("Can't restore line discipline"); + exit(1); + } + + return 0; +} diff --git a/tools/hciattach.h b/tools/hciattach.h new file mode 100644 index 0000000..fed0d11 --- /dev/null +++ b/tools/hciattach.h @@ -0,0 +1,56 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2003-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include + +#ifndef N_HCI +#define N_HCI 15 +#endif + +#define HCIUARTSETPROTO _IOW('U', 200, int) +#define HCIUARTGETPROTO _IOR('U', 201, int) +#define HCIUARTGETDEVICE _IOR('U', 202, int) +#define HCIUARTSETFLAGS _IOW('U', 203, int) +#define HCIUARTGETFLAGS _IOR('U', 204, int) + +#define HCI_UART_H4 0 +#define HCI_UART_BCSP 1 +#define HCI_UART_3WIRE 2 +#define HCI_UART_H4DS 3 +#define HCI_UART_LL 4 +#define HCI_UART_ATH3K 5 + +#define HCI_UART_RAW_DEVICE 0 + +int read_hci_event(int fd, unsigned char* buf, int size); +int set_speed(int fd, struct termios *ti, int speed); + +int texas_init(int fd, struct termios *ti); +int texas_post(int fd, struct termios *ti); +int texasalt_init(int fd, int speed, struct termios *ti); +int stlc2500_init(int fd, bdaddr_t *bdaddr); +int bgb2xx_init(int dd, bdaddr_t *bdaddr); +int ath3k_init(int fd, int speed, int init_speed, char *bdaddr, + struct termios *ti); +int ath3k_post(int fd, int pm); +int qualcomm_init(int fd, int speed, struct termios *ti, const char *bdaddr); diff --git a/tools/hciattach_ath3k.c b/tools/hciattach_ath3k.c new file mode 100644 index 0000000..728e660 --- /dev/null +++ b/tools/hciattach_ath3k.c @@ -0,0 +1,1049 @@ +/* + * Copyright (c) 2009-2010 Atheros Communications Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "hciattach.h" + +#define TRUE 1 +#define FALSE 0 + +#define FW_PATH "/lib/firmware/ar3k/" + +struct ps_cfg_entry { + uint32_t id; + uint32_t len; + uint8_t *data; +}; + +struct ps_entry_type { + unsigned char type; + unsigned char array; +}; + +#define MAX_TAGS 50 +#define PS_HDR_LEN 4 +#define HCI_VENDOR_CMD_OGF 0x3F +#define HCI_PS_CMD_OCF 0x0B + +struct ps_cfg_entry ps_list[MAX_TAGS]; + +static void load_hci_ps_hdr(uint8_t *cmd, uint8_t ps_op, int len, int index) +{ + hci_command_hdr *ch = (void *)cmd; + + ch->opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF, + HCI_PS_CMD_OCF)); + ch->plen = len + PS_HDR_LEN; + cmd += HCI_COMMAND_HDR_SIZE; + + cmd[0] = ps_op; + cmd[1] = index; + cmd[2] = index >> 8; + cmd[3] = len; +} + +#define PS_EVENT_LEN 100 + +/* + * Send HCI command and wait for command complete event. + * The event buffer has to be freed by the caller. + */ +static int send_hci_cmd_sync(int dev, uint8_t *cmd, int len, uint8_t **event) +{ + int err; + uint8_t *hci_event; + uint8_t pkt_type = HCI_COMMAND_PKT; + + if (len == 0) + return len; + + if (write(dev, &pkt_type, 1) != 1) + return -EILSEQ; + if (write(dev, (unsigned char *)cmd, len) != len) + return -EILSEQ; + + hci_event = (uint8_t *)malloc(PS_EVENT_LEN); + if (!hci_event) + return -ENOMEM; + + err = read_hci_event(dev, (unsigned char *)hci_event, PS_EVENT_LEN); + if (err > 0) { + *event = hci_event; + } else { + free(hci_event); + return -EILSEQ; + } + + return len; +} + +#define HCI_EV_SUCCESS 0x00 + +static int read_ps_event(uint8_t *event, uint16_t ocf) +{ + hci_event_hdr *eh; + uint16_t opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF, ocf)); + + event++; + + eh = (void *)event; + event += HCI_EVENT_HDR_SIZE; + + if (eh->evt == EVT_CMD_COMPLETE) { + evt_cmd_complete *cc = (void *)event; + + event += EVT_CMD_COMPLETE_SIZE; + + if (cc->opcode == opcode && event[0] == HCI_EV_SUCCESS) + return 0; + else + return -EILSEQ; + } + + return -EILSEQ; +} + +static int write_cmd(int fd, uint8_t *buffer, int len) +{ + uint8_t *event; + int err; + + err = send_hci_cmd_sync(fd, buffer, len, &event); + if (err < 0) + return err; + + err = read_ps_event(event, HCI_PS_CMD_OCF); + if (event) + free(event); + + return err; +} + +#define PS_WRITE 1 +#define PS_RESET 2 +#define WRITE_PATCH 8 +#define ENABLE_PATCH 11 + +#define HCI_PS_CMD_HDR_LEN 7 + +#define PS_RESET_PARAM_LEN 6 +#define HCI_MAX_CMD_SIZE 260 +#define PS_RESET_CMD_LEN (HCI_PS_CMD_HDR_LEN + PS_RESET_PARAM_LEN) + +#define PS_ID_MASK 0xFF + +/* Sends PS commands using vendor specficic HCI commands */ +static int write_ps_cmd(int fd, uint8_t opcode, uint32_t ps_param) +{ + uint8_t cmd[HCI_MAX_CMD_SIZE]; + uint32_t i; + + switch (opcode) { + case ENABLE_PATCH: + load_hci_ps_hdr(cmd, opcode, 0, 0x00); + + if (write_cmd(fd, cmd, HCI_PS_CMD_HDR_LEN) < 0) + return -EILSEQ; + break; + + case PS_RESET: + load_hci_ps_hdr(cmd, opcode, PS_RESET_PARAM_LEN, 0x00); + + cmd[7] = 0x00; + cmd[PS_RESET_CMD_LEN - 2] = ps_param & PS_ID_MASK; + cmd[PS_RESET_CMD_LEN - 1] = (ps_param >> 8) & PS_ID_MASK; + + if (write_cmd(fd, cmd, PS_RESET_CMD_LEN) < 0) + return -EILSEQ; + break; + + case PS_WRITE: + for (i = 0; i < ps_param; i++) { + load_hci_ps_hdr(cmd, opcode, ps_list[i].len, + ps_list[i].id); + + memcpy(&cmd[HCI_PS_CMD_HDR_LEN], ps_list[i].data, + ps_list[i].len); + + if (write_cmd(fd, cmd, ps_list[i].len + + HCI_PS_CMD_HDR_LEN) < 0) + return -EILSEQ; + } + break; + } + + return 0; +} + +#define __is_delim(ch) ((ch) == ':') +#define MAX_PREAMBLE_LEN 4 + +/* Parse PS entry preamble of format [X:X] for main type and subtype */ +static int get_ps_type(char *ptr, int index, char *type, char *sub_type) +{ + int i; + int delim = FALSE; + + if (index > MAX_PREAMBLE_LEN) + return -EILSEQ; + + for (i = 1; i < index; i++) { + if (__is_delim(ptr[i])) { + delim = TRUE; + continue; + } + + if (isalpha(ptr[i])) { + if (delim == FALSE) + (*type) = toupper(ptr[i]); + else + (*sub_type) = toupper(ptr[i]); + } + } + + return 0; +} + +#define ARRAY 'A' +#define STRING 'S' +#define DECIMAL 'D' +#define BINARY 'B' + +#define PS_HEX 0 +#define PS_DEC 1 + +static int get_input_format(char *buf, struct ps_entry_type *format) +{ + char *ptr = NULL; + char type = '\0'; + char sub_type = '\0'; + + format->type = PS_HEX; + format->array = TRUE; + + if (strstr(buf, "[") != buf) + return 0; + + ptr = strstr(buf, "]"); + if (!ptr) + return -EILSEQ; + + if (get_ps_type(buf, ptr - buf, &type, &sub_type) < 0) + return -EILSEQ; + + /* Check is data type is of array */ + if (type == ARRAY || sub_type == ARRAY) + format->array = TRUE; + + if (type == STRING || sub_type == STRING) + format->array = FALSE; + + if (type == DECIMAL || type == BINARY) + format->type = PS_DEC; + else + format->type = PS_HEX; + + return 0; +} + +#define UNDEFINED 0xFFFF + +static unsigned int read_data_in_section(char *buf, struct ps_entry_type type) +{ + char *ptr = buf; + + if (!buf) + return UNDEFINED; + + if (buf == strstr(buf, "[")) { + ptr = strstr(buf, "]"); + if (!ptr) + return UNDEFINED; + + ptr++; + } + + if (type.type == PS_HEX && type.array != TRUE) + return strtol(ptr, NULL, 16); + + return UNDEFINED; +} + +struct tag_info { + unsigned section; + unsigned line_count; + unsigned char_cnt; + unsigned byte_count; +}; + +static inline int update_char_count(const char *buf) +{ + char *end_ptr; + + if (strstr(buf, "[") == buf) { + end_ptr = strstr(buf, "]"); + if (!end_ptr) + return 0; + else + return (end_ptr - buf) + 1; + } + + return 0; +} + +/* Read PS entries as string, convert and add to Hex array */ +static void update_tag_data(struct ps_cfg_entry *tag, + struct tag_info *info, const char *ptr) +{ + char buf[3]; + + buf[2] = '\0'; + + strncpy(buf, &ptr[info->char_cnt], 2); + tag->data[info->byte_count] = strtol(buf, NULL, 16); + info->char_cnt += 3; + info->byte_count++; + + strncpy(buf, &ptr[info->char_cnt], 2); + tag->data[info->byte_count] = strtol(buf, NULL, 16); + info->char_cnt += 3; + info->byte_count++; +} + +#define PS_UNDEF 0 +#define PS_ID 1 +#define PS_LEN 2 +#define PS_DATA 3 + +#define PS_MAX_LEN 500 +#define LINE_SIZE_MAX (PS_MAX_LEN * 2) +#define ENTRY_PER_LINE 16 + +#define __check_comment(buf) (((buf)[0] == '/') && ((buf)[1] == '/')) +#define __skip_space(str) while (*(str) == ' ') ((str)++) + +static int ath_parse_ps(FILE *stream) +{ + char buf[LINE_SIZE_MAX + 1]; + char *ptr; + uint8_t tag_cnt = 0; + int16_t byte_count = 0; + struct ps_entry_type format; + struct tag_info status = { 0, 0, 0, 0 }; + + do { + int read_count; + struct ps_cfg_entry *tag; + + ptr = fgets(buf, LINE_SIZE_MAX, stream); + if (!ptr) + break; + + __skip_space(ptr); + if (__check_comment(ptr)) + continue; + + /* Lines with a '#' will be followed by new PS entry */ + if (ptr == strstr(ptr, "#")) { + if (status.section != PS_UNDEF) { + return -EILSEQ; + } else { + status.section = PS_ID; + continue; + } + } + + tag = &ps_list[tag_cnt]; + + switch (status.section) { + case PS_ID: + if (get_input_format(ptr, &format) < 0) + return -EILSEQ; + + tag->id = read_data_in_section(ptr, format); + status.section = PS_LEN; + break; + + case PS_LEN: + if (get_input_format(ptr, &format) < 0) + return -EILSEQ; + + byte_count = read_data_in_section(ptr, format); + if (byte_count > PS_MAX_LEN) + return -EILSEQ; + + tag->len = byte_count; + tag->data = (uint8_t *)malloc(byte_count); + + status.section = PS_DATA; + status.line_count = 0; + break; + + case PS_DATA: + if (status.line_count == 0) + if (get_input_format(ptr, &format) < 0) + return -EILSEQ; + + __skip_space(ptr); + + status.char_cnt = update_char_count(ptr); + + read_count = (byte_count > ENTRY_PER_LINE) ? + ENTRY_PER_LINE : byte_count; + + if (format.type == PS_HEX && format.array == TRUE) { + while (read_count > 0) { + update_tag_data(tag, &status, ptr); + read_count -= 2; + } + + if (byte_count > ENTRY_PER_LINE) + byte_count -= ENTRY_PER_LINE; + else + byte_count = 0; + } + + status.line_count++; + + if (byte_count == 0) + memset(&status, 0x00, sizeof(struct tag_info)); + + if (status.section == PS_UNDEF) + tag_cnt++; + + if (tag_cnt == MAX_TAGS) + return -EILSEQ; + break; + } + } while (ptr); + + return tag_cnt; +} + +#define MAX_PATCH_CMD 244 +struct patch_entry { + int16_t len; + uint8_t data[MAX_PATCH_CMD]; +}; + +#define SET_PATCH_RAM_ID 0x0D +#define SET_PATCH_RAM_CMD_SIZE 11 +#define ADDRESS_LEN 4 +static int set_patch_ram(int dev, char *patch_loc, int len) +{ + int err; + uint8_t cmd[20]; + int i, j; + char loc_byte[3]; + uint8_t *event; + uint8_t *loc_ptr = &cmd[7]; + + if (!patch_loc) + return -1; + + loc_byte[2] = '\0'; + + load_hci_ps_hdr(cmd, SET_PATCH_RAM_ID, ADDRESS_LEN, 0); + + for (i = 0, j = 3; i < 4; i++, j--) { + loc_byte[0] = patch_loc[0]; + loc_byte[1] = patch_loc[1]; + loc_ptr[j] = strtol(loc_byte, NULL, 16); + patch_loc += 2; + } + + err = send_hci_cmd_sync(dev, cmd, SET_PATCH_RAM_CMD_SIZE, &event); + if (err < 0) + return err; + + err = read_ps_event(event, HCI_PS_CMD_OCF); + + if (event) + free(event); + + return err; +} + +#define PATCH_LOC_KEY "DA:" +#define PATCH_LOC_STRING_LEN 8 +static int ps_patch_download(int fd, FILE *stream) +{ + char byte[3]; + char ptr[MAX_PATCH_CMD + 1]; + int byte_cnt; + int patch_count = 0; + char patch_loc[PATCH_LOC_STRING_LEN + 1]; + + byte[2] = '\0'; + + while (fgets(ptr, MAX_PATCH_CMD, stream)) { + if (strlen(ptr) <= 1) + continue; + else if (strstr(ptr, PATCH_LOC_KEY) == ptr) { + strncpy(patch_loc, &ptr[sizeof(PATCH_LOC_KEY) - 1], + PATCH_LOC_STRING_LEN); + if (set_patch_ram(fd, patch_loc, sizeof(patch_loc)) < 0) + return -1; + } else if (isxdigit(ptr[0])) + break; + else + return -1; + } + + byte_cnt = strtol(ptr, NULL, 16); + + while (byte_cnt > 0) { + int i; + uint8_t cmd[HCI_MAX_CMD_SIZE]; + struct patch_entry patch; + + if (byte_cnt > MAX_PATCH_CMD) + patch.len = MAX_PATCH_CMD; + else + patch.len = byte_cnt; + + for (i = 0; i < patch.len; i++) { + if (!fgets(byte, 3, stream)) + return -1; + + patch.data[i] = strtoul(byte, NULL, 16); + } + + load_hci_ps_hdr(cmd, WRITE_PATCH, patch.len, patch_count); + memcpy(&cmd[HCI_PS_CMD_HDR_LEN], patch.data, patch.len); + + if (write_cmd(fd, cmd, patch.len + HCI_PS_CMD_HDR_LEN) < 0) + return -1; + + patch_count++; + byte_cnt = byte_cnt - MAX_PATCH_CMD; + } + + if (write_ps_cmd(fd, ENABLE_PATCH, 0) < 0) + return -1; + + return patch_count; +} + +#define PS_RAM_SIZE 2048 + +static int ps_config_download(int fd, int tag_count) +{ + if (write_ps_cmd(fd, PS_RESET, PS_RAM_SIZE) < 0) + return -1; + + if (tag_count > 0) + if (write_ps_cmd(fd, PS_WRITE, tag_count) < 0) + return -1; + return 0; +} + +#define PS_ASIC_FILE "PS_ASIC.pst" +#define PS_FPGA_FILE "PS_FPGA.pst" + +static void get_ps_file_name(uint32_t devtype, uint32_t rom_version, + char *path) +{ + char *filename; + + if (devtype == 0xdeadc0de) + filename = PS_ASIC_FILE; + else + filename = PS_FPGA_FILE; + + snprintf(path, MAXPATHLEN, "%s%x/%s", FW_PATH, rom_version, filename); +} + +#define PATCH_FILE "RamPatch.txt" +#define FPGA_ROM_VERSION 0x99999999 +#define ROM_DEV_TYPE 0xdeadc0de + +static void get_patch_file_name(uint32_t dev_type, uint32_t rom_version, + uint32_t build_version, char *path) +{ + if (rom_version == FPGA_ROM_VERSION && dev_type != ROM_DEV_TYPE && + dev_type != 0 && build_version == 1) + path[0] = '\0'; + else + snprintf(path, MAXPATHLEN, "%s%x/%s", + FW_PATH, rom_version, PATCH_FILE); +} + +#define VERIFY_CRC 9 +#define PS_REGION 1 +#define PATCH_REGION 2 + +static int get_ath3k_crc(int dev) +{ + uint8_t cmd[7]; + uint8_t *event; + int err; + + load_hci_ps_hdr(cmd, VERIFY_CRC, 0, PS_REGION | PATCH_REGION); + + err = send_hci_cmd_sync(dev, cmd, sizeof(cmd), &event); + if (err < 0) + return err; + /* Send error code if CRC check patched */ + if (read_ps_event(event, HCI_PS_CMD_OCF) >= 0) + err = -EILSEQ; + + if (!event) + free(event); + + return err; +} + +#define DEV_REGISTER 0x4FFC +#define GET_DEV_TYPE_OCF 0x05 + +static int get_device_type(int dev, uint32_t *code) +{ + uint8_t cmd[8]; + uint8_t *event; + uint32_t reg; + int err; + uint8_t *ptr = cmd; + hci_command_hdr *ch = (void *)cmd; + + ch->opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF, + GET_DEV_TYPE_OCF)); + ch->plen = 5; + ptr += HCI_COMMAND_HDR_SIZE; + + ptr[0] = (uint8_t)DEV_REGISTER; + ptr[1] = (uint8_t)DEV_REGISTER >> 8; + ptr[2] = (uint8_t)DEV_REGISTER >> 16; + ptr[3] = (uint8_t)DEV_REGISTER >> 24; + ptr[4] = 0x04; + + err = send_hci_cmd_sync(dev, cmd, sizeof(cmd), &event); + if (err < 0) + return err; + + err = read_ps_event(event, GET_DEV_TYPE_OCF); + if (err < 0) + goto cleanup; + + reg = event[10]; + reg = (reg << 8) | event[9]; + reg = (reg << 8) | event[8]; + reg = (reg << 8) | event[7]; + *code = reg; + +cleanup: + if (event) + free(event); + + return err; +} + +#define GET_VERSION_OCF 0x1E + +static int read_ath3k_version(int pConfig, uint32_t *rom_version, + uint32_t *build_version) +{ + uint8_t cmd[3]; + uint8_t *event; + int err; + int status; + hci_command_hdr *ch = (void *)cmd; + + ch->opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF, + GET_VERSION_OCF)); + ch->plen = 0; + + err = send_hci_cmd_sync(pConfig, cmd, sizeof(cmd), &event); + if (err < 0) + return err; + + err = read_ps_event(event, GET_VERSION_OCF); + if (err < 0) + goto cleanup; + + status = event[10]; + status = (status << 8) | event[9]; + status = (status << 8) | event[8]; + status = (status << 8) | event[7]; + *rom_version = status; + + status = event[14]; + status = (status << 8) | event[13]; + status = (status << 8) | event[12]; + status = (status << 8) | event[11]; + *build_version = status; + +cleanup: + if (event) + free(event); + + return err; +} + +static void convert_bdaddr(char *str_bdaddr, char *bdaddr) +{ + char bdbyte[3]; + char *str_byte = str_bdaddr; + int i, j; + int colon_present = 0; + + if (strstr(str_bdaddr, ":")) + colon_present = 1; + + bdbyte[2] = '\0'; + + /* Reverse the BDADDR to LSB first */ + for (i = 0, j = 5; i < 6; i++, j--) { + bdbyte[0] = str_byte[0]; + bdbyte[1] = str_byte[1]; + bdaddr[j] = strtol(bdbyte, NULL, 16); + + if (colon_present == 1) + str_byte += 3; + else + str_byte += 2; + } +} + +static int write_bdaddr(int pConfig, char *bdaddr) +{ + uint8_t *event; + int err; + uint8_t cmd[13]; + uint8_t *ptr = cmd; + hci_command_hdr *ch = (void *)cmd; + + memset(cmd, 0, sizeof(cmd)); + + ch->opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF, + HCI_PS_CMD_OCF)); + ch->plen = 10; + ptr += HCI_COMMAND_HDR_SIZE; + + ptr[0] = 0x01; + ptr[1] = 0x01; + ptr[2] = 0x00; + ptr[3] = 0x06; + + convert_bdaddr(bdaddr, (char *)&ptr[4]); + + err = send_hci_cmd_sync(pConfig, cmd, sizeof(cmd), &event); + if (err < 0) + return err; + + err = read_ps_event(event, HCI_PS_CMD_OCF); + + if (event) + free(event); + + return err; +} + +#define BDADDR_FILE "ar3kbdaddr.pst" + +static void write_bdaddr_from_file(int rom_version, int fd) +{ + FILE *stream; + char bdaddr[PATH_MAX]; + char bdaddr_file[PATH_MAX]; + + snprintf(bdaddr_file, MAXPATHLEN, "%s%x/%s", + FW_PATH, rom_version, BDADDR_FILE); + + stream = fopen(bdaddr_file, "r"); + if (!stream) + return; + + if (fgets(bdaddr, PATH_MAX - 1, stream)) + write_bdaddr(fd, bdaddr); + + fclose(stream); +} + +static int ath_ps_download(int fd) +{ + int err = 0; + int tag_count; + int patch_count = 0; + uint32_t rom_version = 0; + uint32_t build_version = 0; + uint32_t dev_type = 0; + char patch_file[PATH_MAX]; + char ps_file[PATH_MAX]; + FILE *stream; + + /* + * Verfiy firmware version. depending on it select the PS + * config file to download. + */ + if (get_device_type(fd, &dev_type) < 0) { + err = -EILSEQ; + goto download_cmplete; + } + + if (read_ath3k_version(fd, &rom_version, &build_version) < 0) { + err = -EILSEQ; + goto download_cmplete; + } + + /* Do not download configuration if CRC passes */ + if (get_ath3k_crc(fd) < 0) { + err = 0; + goto download_cmplete; + } + + get_ps_file_name(dev_type, rom_version, ps_file); + get_patch_file_name(dev_type, rom_version, build_version, patch_file); + + stream = fopen(ps_file, "r"); + if (!stream) { + perror("firmware file open error\n"); + err = -EILSEQ; + goto download_cmplete; + } + tag_count = ath_parse_ps(stream); + + fclose(stream); + + if (tag_count < 0) { + err = -EILSEQ; + goto download_cmplete; + } + + /* + * It is not necessary that Patch file be available, + * continue with PS Operations if patch file is not available. + */ + if (patch_file[0] == '\0') + err = 0; + + stream = fopen(patch_file, "r"); + if (!stream) + err = 0; + else { + patch_count = ps_patch_download(fd, stream); + fclose(stream); + + if (patch_count < 0) { + err = -EILSEQ; + goto download_cmplete; + } + } + + err = ps_config_download(fd, tag_count); + +download_cmplete: + if (!err) + write_bdaddr_from_file(rom_version, fd); + + return err; +} + +#define HCI_SLEEP_CMD_OCF 0x04 + +/* + * Atheros AR300x specific initialization post callback + */ +int ath3k_post(int fd, int pm) +{ + int dev_id, dd; + struct timespec tm = { 0, 50000 }; + + sleep(1); + + dev_id = ioctl(fd, HCIUARTGETDEVICE, 0); + if (dev_id < 0) { + perror("cannot get device id"); + return dev_id; + } + + dd = hci_open_dev(dev_id); + if (dd < 0) { + perror("HCI device open failed"); + return dd; + } + + if (ioctl(dd, HCIDEVUP, dev_id) < 0 && errno != EALREADY) { + perror("hci down:Power management Disabled"); + hci_close_dev(dd); + return -1; + } + + /* send vendor specific command with Sleep feature Enabled */ + if (hci_send_cmd(dd, OGF_VENDOR_CMD, HCI_SLEEP_CMD_OCF, 1, &pm) < 0) + perror("PM command failed, power management Disabled"); + + nanosleep(&tm, NULL); + hci_close_dev(dd); + + return 0; +} + +#define HCI_VENDOR_CMD_OGF 0x3F +#define HCI_PS_CMD_OCF 0x0B +#define HCI_CHG_BAUD_CMD_OCF 0x0C + +#define WRITE_BDADDR_CMD_LEN 14 +#define WRITE_BAUD_CMD_LEN 6 +#define MAX_CMD_LEN WRITE_BDADDR_CMD_LEN + +static int set_cntrlr_baud(int fd, int speed) +{ + int baud; + struct timespec tm = { 0, 500000 }; + unsigned char cmd[MAX_CMD_LEN], rsp[HCI_MAX_EVENT_SIZE]; + unsigned char *ptr = cmd + 1; + hci_command_hdr *ch = (void *)ptr; + + cmd[0] = HCI_COMMAND_PKT; + + /* set controller baud rate to user specified value */ + ptr = cmd + 1; + ch->opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF, + HCI_CHG_BAUD_CMD_OCF)); + ch->plen = 2; + ptr += HCI_COMMAND_HDR_SIZE; + + baud = speed/100; + ptr[0] = (char)baud; + ptr[1] = (char)(baud >> 8); + + if (write(fd, cmd, WRITE_BAUD_CMD_LEN) != WRITE_BAUD_CMD_LEN) { + perror("Failed to write change baud rate command"); + return -ETIMEDOUT; + } + + nanosleep(&tm, NULL); + + if (read_hci_event(fd, rsp, sizeof(rsp)) < 0) + return -ETIMEDOUT; + + return 0; +} + +/* + * Atheros AR300x specific initialization and configuration file + * download + */ +int ath3k_init(int fd, int speed, int init_speed, char *bdaddr, + struct termios *ti) +{ + int r; + int err = 0; + struct timespec tm = { 0, 500000 }; + unsigned char cmd[MAX_CMD_LEN], rsp[HCI_MAX_EVENT_SIZE]; + unsigned char *ptr = cmd + 1; + hci_command_hdr *ch = (void *)ptr; + + cmd[0] = HCI_COMMAND_PKT; + + /* set both controller and host baud rate to maximum possible value */ + err = set_cntrlr_baud(fd, speed); + if (err < 0) + return err; + + err = set_speed(fd, ti, speed); + if (err < 0) { + perror("Can't set required baud rate"); + return err; + } + + /* Download PS and patch */ + r = ath_ps_download(fd); + if (r < 0) { + perror("Failed to Download configuration"); + err = -ETIMEDOUT; + goto failed; + } + + /* Write BDADDR */ + if (bdaddr) { + ch->opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF, + HCI_PS_CMD_OCF)); + ch->plen = 10; + ptr += HCI_COMMAND_HDR_SIZE; + + ptr[0] = 0x01; + ptr[1] = 0x01; + ptr[2] = 0x00; + ptr[3] = 0x06; + str2ba(bdaddr, (bdaddr_t *)(ptr + 4)); + + if (write(fd, cmd, WRITE_BDADDR_CMD_LEN) != + WRITE_BDADDR_CMD_LEN) { + perror("Failed to write BD_ADDR command\n"); + err = -ETIMEDOUT; + goto failed; + } + + if (read_hci_event(fd, rsp, sizeof(rsp)) < 0) { + perror("Failed to set BD_ADDR\n"); + err = -ETIMEDOUT; + goto failed; + } + } + + /* Send HCI Reset */ + cmd[1] = 0x03; + cmd[2] = 0x0C; + cmd[3] = 0x00; + + r = write(fd, cmd, 4); + if (r != 4) { + err = -ETIMEDOUT; + goto failed; + } + + nanosleep(&tm, NULL); + if (read_hci_event(fd, rsp, sizeof(rsp)) < 0) { + err = -ETIMEDOUT; + goto failed; + } + + err = set_cntrlr_baud(fd, speed); + if (err < 0) + return err; + +failed: + if (err < 0) { + set_cntrlr_baud(fd, init_speed); + set_speed(fd, ti, init_speed); + } + + return err; +} diff --git a/tools/hciattach_qualcomm.c b/tools/hciattach_qualcomm.c new file mode 100644 index 0000000..e4ff5e3 --- /dev/null +++ b/tools/hciattach_qualcomm.c @@ -0,0 +1,275 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2005-2010 Marcel Holtmann + * Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "hciattach.h" + +#define FAILIF(x, args...) do { \ + if (x) { \ + fprintf(stderr, ##args); \ + return -1; \ + } \ +} while (0) + +typedef struct { + uint8_t uart_prefix; + hci_event_hdr hci_hdr; + evt_cmd_complete cmd_complete; + uint8_t status; + uint8_t data[16]; +} __attribute__((packed)) command_complete_t; + +static int read_command_complete(int fd, + unsigned short opcode, + unsigned char len) +{ + command_complete_t resp; + unsigned char vsevent[512]; + int n; + + /* Read reply. */ + n = read_hci_event(fd, vsevent, sizeof(vsevent)); + FAILIF(n < 0, "Failed to read response"); + + FAILIF(vsevent[1] != 0xFF, "Failed to read response"); + + n = read_hci_event(fd, (unsigned char *)&resp, sizeof(resp)); + FAILIF(n < 0, "Failed to read response"); + + /* event must be event-complete */ + FAILIF(resp.hci_hdr.evt != EVT_CMD_COMPLETE, + "Error in response: not a cmd-complete event, " + "but 0x%02x!\n", resp.hci_hdr.evt); + + FAILIF(resp.hci_hdr.plen < 4, /* plen >= 4 for EVT_CMD_COMPLETE */ + "Error in response: plen is not >= 4, but 0x%02x!\n", + resp.hci_hdr.plen); + + /* cmd-complete event: opcode */ + FAILIF(resp.cmd_complete.opcode != 0, + "Error in response: opcode is 0x%04x, not 0!", + resp.cmd_complete.opcode); + + return resp.status == 0 ? 0 : -1; +} + +static int qualcomm_load_firmware(int fd, const char *firmware, const char *bdaddr_s) +{ + + int fw = open(firmware, O_RDONLY); + + fprintf(stdout, "Opening firmware file: %s\n", firmware); + + FAILIF(fw < 0, + "Could not open firmware file %s: %s (%d).\n", + firmware, strerror(errno), errno); + + fprintf(stdout, "Uploading firmware...\n"); + do { + /* Read each command and wait for a response. */ + unsigned char data[1024]; + unsigned char cmdp[1 + sizeof(hci_command_hdr)]; + hci_command_hdr *cmd = (hci_command_hdr *) (cmdp + 1); + int nr; + + nr = read(fw, cmdp, sizeof(cmdp)); + if (!nr) + break; + + FAILIF(nr != sizeof(cmdp), + "Could not read H4 + HCI header!\n"); + FAILIF(*cmdp != HCI_COMMAND_PKT, + "Command is not an H4 command packet!\n"); + + FAILIF(read(fw, data, cmd->plen) != cmd->plen, + "Could not read %d bytes of data \ + for command with opcode %04x!\n", + cmd->plen, cmd->opcode); + + if ((data[0] == 1) && (data[1] == 2) && (data[2] == 6)) { + bdaddr_t bdaddr; + if (bdaddr_s != NULL) { + str2ba(bdaddr_s, &bdaddr); + memcpy(&data[3], &bdaddr, sizeof(bdaddr_t)); + } + } + + { + int nw; + struct iovec iov_cmd[2]; + iov_cmd[0].iov_base = cmdp; + iov_cmd[0].iov_len = sizeof(cmdp); + iov_cmd[1].iov_base = data; + iov_cmd[1].iov_len = cmd->plen; + nw = writev(fd, iov_cmd, 2); + FAILIF(nw != (int) sizeof(cmdp) + cmd->plen, + "Could not send entire command \ + (sent only %d bytes)!\n", + nw); + } + + /* Wait for response */ + if (read_command_complete(fd, cmd->opcode, cmd->plen) < 0) + return -1; + } while (1); + fprintf(stdout, "Firmware upload successful.\n"); + + close(fw); + + return 0; +} + +int qualcomm_init(int fd, int speed, struct termios *ti, const char *bdaddr) +{ + struct timespec tm = {0, 50000}; + char cmd[5]; + unsigned char resp[100]; /* Response */ + char fw[100]; + int n; + + memset(resp, 0, 100); + + /* Get Manufacturer and LMP version */ + cmd[0] = HCI_COMMAND_PKT; + cmd[1] = 0x01; + cmd[2] = 0x10; + cmd[3] = 0x00; + + do { + n = write(fd, cmd, 4); + if (n < 4) { + perror("Failed to write init command"); + return -1; + } + + /* Read reply. */ + if (read_hci_event(fd, resp, 100) < 0) { + perror("Failed to read init response"); + return -1; + } + + /* Wait for command complete event for our Opcode */ + } while (resp[4] != cmd[1] && resp[5] != cmd[2]); + + /* Verify manufacturer */ + if ((resp[11] & 0xFF) != 0x1d) + fprintf(stderr, + "WARNING : module's manufacturer is not Qualcomm\n"); + + /* Print LMP version */ + fprintf(stderr, + "Qualcomm module LMP version : 0x%02x\n", resp[10] & 0xFF); + + /* Print LMP subversion */ + { + unsigned short lmp_subv = resp[13] | (resp[14] << 8); + + fprintf(stderr, "Qualcomm module LMP sub-version : 0x%04x\n", + lmp_subv); + } + + /* Get SoC type */ + cmd[0] = HCI_COMMAND_PKT; + cmd[1] = 0x00; + cmd[2] = 0xFC; + cmd[3] = 0x01; + cmd[4] = 0x06; + + do { + n = write(fd, cmd, 5); + if (n < 5) { + perror("Failed to write vendor init command"); + return -1; + } + + /* Read reply. */ + if ((n = read_hci_event(fd, resp, 100)) < 0) { + perror("Failed to read vendor init response"); + return -1; + } + + } while (resp[3] != 0 && resp[4] != 2); + + snprintf(fw, sizeof(fw), "/etc/firmware/%c%c%c%c%c%c_%c%c%c%c.bin", + resp[18], resp[19], resp[20], resp[21], + resp[22], resp[23], + resp[32], resp[33], resp[34], resp[35]); + + /* Wait for command complete event for our Opcode */ + if (read_hci_event(fd, resp, 100) < 0) { + perror("Failed to read init response"); + return -1; + } + + qualcomm_load_firmware(fd, fw, bdaddr); + + /* Reset */ + cmd[0] = HCI_COMMAND_PKT; + cmd[1] = 0x03; + cmd[2] = 0x0C; + cmd[3] = 0x00; + + do { + n = write(fd, cmd, 4); + if (n < 4) { + perror("Failed to write reset command"); + return -1; + } + + /* Read reply. */ + if ((n = read_hci_event(fd, resp, 100)) < 0) { + perror("Failed to read reset response"); + return -1; + } + + } while (resp[4] != cmd[1] && resp[5] != cmd[2]); + + nanosleep(&tm, NULL); + + return 0; +} diff --git a/tools/hciattach_st.c b/tools/hciattach_st.c new file mode 100644 index 0000000..dbb7c47 --- /dev/null +++ b/tools/hciattach_st.c @@ -0,0 +1,278 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2005-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "hciattach.h" + +static int debug = 0; + +static int do_command(int fd, uint8_t ogf, uint16_t ocf, + uint8_t *cparam, int clen, uint8_t *rparam, int rlen) +{ + //uint16_t opcode = (uint16_t) ((ocf & 0x03ff) | (ogf << 10)); + unsigned char cp[260], rp[260]; + int len, size, offset = 3; + + cp[0] = 0x01; + cp[1] = ocf & 0xff; + cp[2] = ogf << 2 | ocf >> 8; + cp[3] = clen; + + if (clen > 0) + memcpy(cp + 4, cparam, clen); + + if (debug) { + int i; + printf("[<"); + for (i = 0; i < clen + 4; i++) + printf(" %02x", cp[i]); + printf("]\n"); + } + + if (write(fd, cp, clen + 4) < 0) + return -1; + + do { + if (read(fd, rp, 1) < 1) + return -1; + } while (rp[0] != 0x04); + + if (read(fd, rp + 1, 2) < 2) + return -1; + + do { + len = read(fd, rp + offset, sizeof(rp) - offset); + offset += len; + } while (offset < rp[2] + 3); + + if (debug) { + int i; + printf("[>"); + for (i = 0; i < offset; i++) + printf(" %02x", rp[i]); + printf("]\n"); + } + + if (rp[0] != 0x04) { + errno = EIO; + return -1; + } + + switch (rp[1]) { + case 0x0e: /* command complete */ + if (rp[6] != 0x00) + return -ENXIO; + offset = 3 + 4; + size = rp[2] - 4; + break; + case 0x0f: /* command status */ + /* fall through */ + default: + offset = 3; + size = rp[2]; + break; + } + + if (!rparam || rlen < size) + return -ENXIO; + + memcpy(rparam, rp + offset, size); + + return size; +} + +static int load_file(int dd, uint16_t version, const char *suffix) +{ + DIR *dir; + struct dirent *d; + char pathname[PATH_MAX], filename[NAME_MAX], prefix[20]; + unsigned char cmd[256]; + unsigned char buf[256]; + uint8_t seqnum = 0; + int fd, size, len, found_fw_file; + + memset(filename, 0, sizeof(filename)); + + snprintf(prefix, sizeof(prefix), "STLC2500_R%d_%02d_", + version >> 8, version & 0xff); + + strcpy(pathname, "/lib/firmware"); + dir = opendir(pathname); + if (!dir) { + strcpy(pathname, "."); + dir = opendir(pathname); + if (!dir) + return -errno; + } + + found_fw_file = 0; + while (1) { + d = readdir(dir); + if (!d) + break; + + if (strncmp(d->d_name + strlen(d->d_name) - strlen(suffix), + suffix, strlen(suffix))) + continue; + + if (strncmp(d->d_name, prefix, strlen(prefix))) + continue; + + snprintf(filename, sizeof(filename), "%s/%s", + pathname, d->d_name); + found_fw_file = 1; + } + + closedir(dir); + + if (!found_fw_file) + return -ENOENT; + + printf("Loading file %s\n", filename); + + fd = open(filename, O_RDONLY); + if (fd < 0) { + perror("Can't open firmware file"); + return -errno; + } + + while (1) { + size = read(fd, cmd + 1, 254); + if (size <= 0) + break; + + cmd[0] = seqnum; + + len = do_command(dd, 0xff, 0x002e, cmd, size + 1, buf, sizeof(buf)); + if (len < 1) + break; + + if (buf[0] != seqnum) { + fprintf(stderr, "Sequence number mismatch\n"); + break; + } + + seqnum++; + } + + close(fd); + + return 0; +} + +int stlc2500_init(int dd, bdaddr_t *bdaddr) +{ + unsigned char cmd[16]; + unsigned char buf[254]; + uint16_t version; + int len; + int err; + + /* Hci_Cmd_Ericsson_Read_Revision_Information */ + len = do_command(dd, 0xff, 0x000f, NULL, 0, buf, sizeof(buf)); + if (len < 0) + return -1; + + printf("%s\n", buf); + + /* HCI_Read_Local_Version_Information */ + len = do_command(dd, 0x04, 0x0001, NULL, 0, buf, sizeof(buf)); + if (len < 0) + return -1; + + version = buf[2] << 8 | buf[1]; + + err = load_file(dd, version, ".ptc"); + if (err < 0) { + if (err == -ENOENT) + fprintf(stderr, "No ROM patch file loaded.\n"); + else + return -1; + } + + err = load_file(dd, buf[2] << 8 | buf[1], ".ssf"); + if (err < 0) { + if (err == -ENOENT) + fprintf(stderr, "No static settings file loaded.\n"); + else + return -1; + } + + cmd[0] = 0xfe; + cmd[1] = 0x06; + bacpy((bdaddr_t *) (cmd + 2), bdaddr); + + /* Hci_Cmd_ST_Store_In_NVDS */ + len = do_command(dd, 0xff, 0x0022, cmd, 8, buf, sizeof(buf)); + if (len < 0) + return -1; + + /* HCI_Reset : applies parameters*/ + len = do_command(dd, 0x03, 0x0003, NULL, 0, buf, sizeof(buf)); + if (len < 0) + return -1; + + return 0; +} + +int bgb2xx_init(int dd, bdaddr_t *bdaddr) +{ + unsigned char cmd[16]; + unsigned char buf[254]; + int len; + + len = do_command(dd, 0xff, 0x000f, NULL, 0, buf, sizeof(buf)); + if (len < 0) + return -1; + + printf("%s\n", buf); + + cmd[0] = 0xfe; + cmd[1] = 0x06; + bacpy((bdaddr_t *) (cmd + 2), bdaddr); + + len = do_command(dd, 0xff, 0x0022, cmd, 8, buf, sizeof(buf)); + if (len < 0) + return -1; + + len = do_command(dd, 0x03, 0x0003, NULL, 0, buf, sizeof(buf)); + if (len < 0) + return -1; + + return 0; +} diff --git a/tools/hciattach_ti.c b/tools/hciattach_ti.c new file mode 100644 index 0000000..e107a65 --- /dev/null +++ b/tools/hciattach_ti.c @@ -0,0 +1,529 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2007-2008 Texas Instruments, Inc. + * Copyright (C) 2005-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "hciattach.h" + +#ifdef HCIATTACH_DEBUG +#define DPRINTF(x...) printf(x) +#else +#define DPRINTF(x...) +#endif + +#define HCIUARTGETDEVICE _IOR('U', 202, int) + +#define MAKEWORD(a, b) ((uint16_t)(((uint8_t)(a)) | ((uint16_t)((uint8_t)(b))) << 8)) + +#define TI_MANUFACTURER_ID 13 + +#define FIRMWARE_DIRECTORY "/lib/firmware/" + +#define ACTION_SEND_COMMAND 1 +#define ACTION_WAIT_EVENT 2 +#define ACTION_SERIAL 3 +#define ACTION_DELAY 4 +#define ACTION_RUN_SCRIPT 5 +#define ACTION_REMARKS 6 + +#define BRF_DEEP_SLEEP_OPCODE_BYTE_1 0x0c +#define BRF_DEEP_SLEEP_OPCODE_BYTE_2 0xfd +#define BRF_DEEP_SLEEP_OPCODE \ + (BRF_DEEP_SLEEP_OPCODE_BYTE_1 | (BRF_DEEP_SLEEP_OPCODE_BYTE_2 << 8)) + +#define FILE_HEADER_MAGIC 0x42535442 + +/* + * BRF Firmware header + */ +struct bts_header { + uint32_t magic; + uint32_t version; + uint8_t future[24]; + uint8_t actions[0]; +}__attribute__ ((packed)); + +/* + * BRF Actions structure + */ +struct bts_action { + uint16_t type; + uint16_t size; + uint8_t data[0]; +} __attribute__ ((packed)); + +struct bts_action_send { + uint8_t data[0]; +} __attribute__ ((packed)); + +struct bts_action_wait { + uint32_t msec; + uint32_t size; + uint8_t data[0]; +}__attribute__ ((packed)); + +struct bts_action_delay { + uint32_t msec; +}__attribute__ ((packed)); + +struct bts_action_serial { + uint32_t baud; + uint32_t flow_control; +}__attribute__ ((packed)); + +static FILE *bts_load_script(const char* file_name, uint32_t* version) +{ + struct bts_header header; + FILE* fp; + + fp = fopen(file_name, "rb"); + if (!fp) { + perror("can't open firmware file"); + goto out; + } + + if (1 != fread(&header, sizeof(struct bts_header), 1, fp)) { + perror("can't read firmware file"); + goto errclose; + } + + if (header.magic != FILE_HEADER_MAGIC) { + fprintf(stderr, "%s not a legal TI firmware file\n", file_name); + goto errclose; + } + + if (NULL != version) + *version = header.version; + + goto out; + +errclose: + fclose(fp); + fp = NULL; +out: + return fp; +} + +static unsigned long bts_fetch_action(FILE* fp, unsigned char* action_buf, + unsigned long buf_size, uint16_t* action_type) +{ + struct bts_action action_hdr; + unsigned long nread; + + if (!fp) + return 0; + + if (1 != fread(&action_hdr, sizeof(struct bts_action), 1, fp)) + return 0; + + if (action_hdr.size > buf_size) { + fprintf(stderr, "bts_next_action: not enough space to read next action\n"); + return 0; + } + + nread = fread(action_buf, sizeof(uint8_t), action_hdr.size, fp); + if (nread != (action_hdr.size)) { + fprintf(stderr, "bts_next_action: fread failed to read next action\n"); + return 0; + } + + *action_type = action_hdr.type; + + return nread * sizeof(uint8_t); +} + +static void bts_unload_script(FILE* fp) +{ + if (fp) + fclose(fp); +} + +static int is_it_texas(const uint8_t *respond) +{ + uint16_t manufacturer_id; + + manufacturer_id = MAKEWORD(respond[11], respond[12]); + + return TI_MANUFACTURER_ID == manufacturer_id ? 1 : 0; +} + +static const char *get_firmware_name(const uint8_t *respond) +{ + static char firmware_file_name[PATH_MAX] = {0}; + uint16_t version = 0, chip = 0, min_ver = 0, maj_ver = 0; + + version = MAKEWORD(respond[13], respond[14]); + chip = (version & 0x7C00) >> 10; + min_ver = (version & 0x007F); + maj_ver = (version & 0x0380) >> 7; + + if (version & 0x8000) + maj_ver |= 0x0008; + + sprintf(firmware_file_name, FIRMWARE_DIRECTORY "TIInit_%d.%d.%d.bts", chip, maj_ver, min_ver); + + return firmware_file_name; +} + +static void brf_delay(struct bts_action_delay *delay) +{ + usleep(1000 * delay->msec); +} + +static int brf_set_serial_params(struct bts_action_serial *serial_action, + int fd, struct termios *ti) +{ + fprintf(stderr, "texas: changing baud rate to %u, flow control to %u\n", + serial_action->baud, serial_action->flow_control ); + tcflush(fd, TCIOFLUSH); + + if (serial_action->flow_control) + ti->c_cflag |= CRTSCTS; + else + ti->c_cflag &= ~CRTSCTS; + + if (tcsetattr(fd, TCSANOW, ti) < 0) { + perror("Can't set port settings"); + return -1; + } + + tcflush(fd, TCIOFLUSH); + + if (set_speed(fd, ti, serial_action->baud) < 0) { + perror("Can't set baud rate"); + return -1; + } + + return 0; +} + +static int brf_send_command_socket(int fd, struct bts_action_send* send_action) +{ + char response[1024] = {0}; + hci_command_hdr *cmd = (hci_command_hdr *) send_action->data; + uint16_t opcode = cmd->opcode; + + struct hci_request rq; + memset(&rq, 0, sizeof(rq)); + rq.ogf = cmd_opcode_ogf(opcode); + rq.ocf = cmd_opcode_ocf(opcode); + rq.event = EVT_CMD_COMPLETE; + rq.cparam = &send_action->data[3]; + rq.clen = send_action->data[2]; + rq.rparam = response; + rq.rlen = sizeof(response); + + if (hci_send_req(fd, &rq, 15) < 0) { + perror("Cannot send hci command to socket"); + return -1; + } + + /* verify success */ + if (response[0]) { + errno = EIO; + return -1; + } + + return 0; +} + +static int brf_send_command_file(int fd, struct bts_action_send* send_action, long size) +{ + unsigned char response[1024] = {0}; + long ret = 0; + + /* send command */ + if (size != write(fd, send_action, size)) { + perror("Texas: Failed to write action command"); + return -1; + } + + /* read response */ + ret = read_hci_event(fd, response, sizeof(response)); + if (ret < 0) { + perror("texas: failed to read command response"); + return -1; + } + + /* verify success */ + if (ret < 7 || 0 != response[6]) { + fprintf( stderr, "TI init command failed.\n" ); + errno = EIO; + return -1; + } + + return 0; +} + + +static int brf_send_command(int fd, struct bts_action_send* send_action, long size, int hcill_installed) +{ + int ret = 0; + char *fixed_action; + + /* remove packet type when giving to socket API */ + if (hcill_installed) { + fixed_action = ((char *) send_action) + 1; + ret = brf_send_command_socket(fd, (struct bts_action_send *) fixed_action); + } else { + ret = brf_send_command_file(fd, send_action, size); + } + + return ret; +} + +static int brf_do_action(uint16_t brf_type, uint8_t *brf_action, long brf_size, + int fd, struct termios *ti, int hcill_installed) +{ + int ret = 0; + + switch (brf_type) { + case ACTION_SEND_COMMAND: + DPRINTF("W"); + ret = brf_send_command(fd, (struct bts_action_send*) brf_action, brf_size, hcill_installed); + break; + case ACTION_WAIT_EVENT: + DPRINTF("R"); + break; + case ACTION_SERIAL: + DPRINTF("S"); + ret = brf_set_serial_params((struct bts_action_serial *) brf_action, fd, ti); + break; + case ACTION_DELAY: + DPRINTF("D"); + brf_delay((struct bts_action_delay *) brf_action); + break; + case ACTION_REMARKS: + DPRINTF("C"); + break; + default: + fprintf(stderr, "brf_init: unknown firmware action type (%d)\n", brf_type); + break; + } + + return ret; +} + +/* + * tests whether a given brf action is a HCI_VS_Sleep_Mode_Configurations cmd + */ +static int brf_action_is_deep_sleep(uint8_t *brf_action, long brf_size, + uint16_t brf_type) +{ + uint16_t opcode; + + if (brf_type != ACTION_SEND_COMMAND) + return 0; + + if (brf_size < 3) + return 0; + + if (brf_action[0] != HCI_COMMAND_PKT) + return 0; + + /* HCI data is little endian */ + opcode = brf_action[1] | (brf_action[2] << 8); + + if (opcode != BRF_DEEP_SLEEP_OPCODE) + return 0; + + /* action is deep sleep configuration command ! */ + return 1; +} + +/* + * This function is called twice. + * The first time it is called, it loads the brf script, and executes its + * commands until it reaches a deep sleep command (or its end). + * The second time it is called, it assumes HCILL protocol is set up, + * and sends rest of brf script via the supplied socket. + */ +static int brf_do_script(int fd, struct termios *ti, const char *bts_file) +{ + int ret = 0, hcill_installed = bts_file ? 0 : 1; + uint32_t vers; + static FILE *brf_script_file = NULL; + static uint8_t brf_action[512]; + static long brf_size; + static uint16_t brf_type; + + /* is it the first time we are called ? */ + if (0 == hcill_installed) { + DPRINTF("Sending script to serial device\n"); + brf_script_file = bts_load_script(bts_file, &vers ); + if (!brf_script_file) { + fprintf(stderr, "Warning: cannot find BTS file: %s\n", + bts_file); + return 0; + } + + fprintf( stderr, "Loaded BTS script version %u\n", vers ); + + brf_size = bts_fetch_action(brf_script_file, brf_action, + sizeof(brf_action), &brf_type); + if (brf_size == 0) { + fprintf(stderr, "Warning: BTS file is empty !"); + return 0; + } + } + else { + DPRINTF("Sending script to bluetooth socket\n"); + } + + /* execute current action and continue to parse brf script file */ + while (brf_size != 0) { + ret = brf_do_action(brf_type, brf_action, brf_size, + fd, ti, hcill_installed); + if (ret == -1) + break; + + brf_size = bts_fetch_action(brf_script_file, brf_action, + sizeof(brf_action), &brf_type); + + /* if this is the first time we run (no HCILL yet) */ + /* and a deep sleep command is encountered */ + /* we exit */ + if (!hcill_installed && + brf_action_is_deep_sleep(brf_action, + brf_size, brf_type)) + return 0; + } + + bts_unload_script(brf_script_file); + brf_script_file = NULL; + DPRINTF("\n"); + + return ret; +} + +int texas_init(int fd, struct termios *ti) +{ + struct timespec tm = {0, 50000}; + char cmd[4]; + unsigned char resp[100]; /* Response */ + const char *bts_file; + int n; + + memset(resp,'\0', 100); + + /* It is possible to get software version with manufacturer specific + HCI command HCI_VS_TI_Version_Number. But the only thing you get more + is if this is point-to-point or point-to-multipoint module */ + + /* Get Manufacturer and LMP version */ + cmd[0] = HCI_COMMAND_PKT; + cmd[1] = 0x01; + cmd[2] = 0x10; + cmd[3] = 0x00; + + do { + n = write(fd, cmd, 4); + if (n < 0) { + perror("Failed to write init command (READ_LOCAL_VERSION_INFORMATION)"); + return -1; + } + if (n < 4) { + fprintf(stderr, "Wanted to write 4 bytes, could only write %d. Stop\n", n); + return -1; + } + + /* Read reply. */ + if (read_hci_event(fd, resp, 100) < 0) { + perror("Failed to read init response (READ_LOCAL_VERSION_INFORMATION)"); + return -1; + } + + /* Wait for command complete event for our Opcode */ + } while (resp[4] != cmd[1] && resp[5] != cmd[2]); + + /* Verify manufacturer */ + if (! is_it_texas(resp)) { + fprintf(stderr,"ERROR: module's manufacturer is not Texas Instruments\n"); + return -1; + } + + fprintf(stderr, "Found a Texas Instruments' chip!\n"); + + bts_file = get_firmware_name(resp); + fprintf(stderr, "Firmware file : %s\n", bts_file); + + n = brf_do_script(fd, ti, bts_file); + + nanosleep(&tm, NULL); + + return n; +} + +int texas_post(int fd, struct termios *ti) +{ + int dev_id, dd, ret = 0; + + sleep(1); + + dev_id = ioctl(fd, HCIUARTGETDEVICE, 0); + if (dev_id < 0) { + perror("cannot get device id"); + return -1; + } + + DPRINTF("\nAdded device hci%d\n", dev_id); + + dd = hci_open_dev(dev_id); + if (dd < 0) { + perror("HCI device open failed"); + return -1; + } + + if (ioctl(dd, HCIDEVUP, dev_id) < 0 && errno != EALREADY) { + fprintf(stderr, "Can't init device hci%d: %s (%d)", dev_id, + strerror(errno), errno); + hci_close_dev(dd); + return -1; + } + + ret = brf_do_script(dd, ti, NULL); + + hci_close_dev(dd); + + return ret; +} diff --git a/tools/hciattach_tialt.c b/tools/hciattach_tialt.c new file mode 100644 index 0000000..1ba009c --- /dev/null +++ b/tools/hciattach_tialt.c @@ -0,0 +1,244 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2005-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "hciattach.h" + +#define FAILIF(x, args...) do { \ + if (x) { \ + fprintf(stderr, ##args); \ + return -1; \ + } \ +} while(0) + +typedef struct { + uint8_t uart_prefix; + hci_event_hdr hci_hdr; + evt_cmd_complete cmd_complete; + uint8_t status; + uint8_t data[16]; +} __attribute__((packed)) command_complete_t; + +static int read_command_complete(int fd, unsigned short opcode, unsigned char len) { + command_complete_t resp; + /* Read reply. */ + FAILIF(read_hci_event(fd, (unsigned char *)&resp, sizeof(resp)) < 0, + "Failed to read response"); + + /* Parse speed-change reply */ + FAILIF(resp.uart_prefix != HCI_EVENT_PKT, + "Error in response: not an event packet, but 0x%02x!\n", + resp.uart_prefix); + + FAILIF(resp.hci_hdr.evt != EVT_CMD_COMPLETE, /* event must be event-complete */ + "Error in response: not a cmd-complete event, " + "but 0x%02x!\n", resp.hci_hdr.evt); + + FAILIF(resp.hci_hdr.plen < 4, /* plen >= 4 for EVT_CMD_COMPLETE */ + "Error in response: plen is not >= 4, but 0x%02x!\n", + resp.hci_hdr.plen); + + /* cmd-complete event: opcode */ + FAILIF(resp.cmd_complete.opcode != (uint16_t)opcode, + "Error in response: opcode is 0x%04x, not 0x%04x!", + resp.cmd_complete.opcode, opcode); + + return resp.status == 0 ? 0 : -1; +} + +typedef struct { + uint8_t uart_prefix; + hci_command_hdr hci_hdr; + uint32_t speed; +} __attribute__((packed)) texas_speed_change_cmd_t; + +static int texas_change_speed(int fd, uint32_t speed) +{ + return 0; +} + +static int texas_load_firmware(int fd, const char *firmware) { + + int fw = open(firmware, O_RDONLY); + + fprintf(stdout, "Opening firmware file: %s\n", firmware); + + FAILIF(fw < 0, + "Could not open firmware file %s: %s (%d).\n", + firmware, strerror(errno), errno); + + fprintf(stdout, "Uploading firmware...\n"); + do { + /* Read each command and wait for a response. */ + unsigned char data[1024]; + unsigned char cmdp[1 + sizeof(hci_command_hdr)]; + hci_command_hdr *cmd = (hci_command_hdr *)(cmdp + 1); + int nr; + nr = read(fw, cmdp, sizeof(cmdp)); + if (!nr) + break; + FAILIF(nr != sizeof(cmdp), "Could not read H4 + HCI header!\n"); + FAILIF(*cmdp != HCI_COMMAND_PKT, "Command is not an H4 command packet!\n"); + + FAILIF(read(fw, data, cmd->plen) != cmd->plen, + "Could not read %d bytes of data for command with opcode %04x!\n", + cmd->plen, + cmd->opcode); + + { + int nw; +#if 0 + fprintf(stdout, "\topcode 0x%04x (%d bytes of data).\n", + cmd->opcode, + cmd->plen); +#endif + struct iovec iov_cmd[2]; + iov_cmd[0].iov_base = cmdp; + iov_cmd[0].iov_len = sizeof(cmdp); + iov_cmd[1].iov_base = data; + iov_cmd[1].iov_len = cmd->plen; + nw = writev(fd, iov_cmd, 2); + FAILIF(nw != (int) sizeof(cmd) + cmd->plen, + "Could not send entire command (sent only %d bytes)!\n", + nw); + } + + /* Wait for response */ + if (read_command_complete(fd, + cmd->opcode, + cmd->plen) < 0) { + return -1; + } + + } while(1); + fprintf(stdout, "Firmware upload successful.\n"); + + close(fw); + return 0; +} + +int texasalt_init(int fd, int speed, struct termios *ti) +{ + struct timespec tm = {0, 50000}; + char cmd[4]; + unsigned char resp[100]; /* Response */ + int n; + + memset(resp,'\0', 100); + + /* It is possible to get software version with manufacturer specific + HCI command HCI_VS_TI_Version_Number. But the only thing you get more + is if this is point-to-point or point-to-multipoint module */ + + /* Get Manufacturer and LMP version */ + cmd[0] = HCI_COMMAND_PKT; + cmd[1] = 0x01; + cmd[2] = 0x10; + cmd[3] = 0x00; + + do { + n = write(fd, cmd, 4); + if (n < 0) { + perror("Failed to write init command (READ_LOCAL_VERSION_INFORMATION)"); + return -1; + } + if (n < 4) { + fprintf(stderr, "Wanted to write 4 bytes, could only write %d. Stop\n", n); + return -1; + } + + /* Read reply. */ + if (read_hci_event(fd, resp, 100) < 0) { + perror("Failed to read init response (READ_LOCAL_VERSION_INFORMATION)"); + return -1; + } + + /* Wait for command complete event for our Opcode */ + } while (resp[4] != cmd[1] && resp[5] != cmd[2]); + + /* Verify manufacturer */ + if ((resp[11] & 0xFF) != 0x0d) + fprintf(stderr,"WARNING : module's manufacturer is not Texas Instrument\n"); + + /* Print LMP version */ + fprintf(stderr, "Texas module LMP version : 0x%02x\n", resp[10] & 0xFF); + + /* Print LMP subversion */ + { + unsigned short lmp_subv = resp[13] | (resp[14] << 8); + unsigned short brf_chip = (lmp_subv & 0x7c00) >> 10; + static const char *c_brf_chip[8] = { + "unknown", + "unknown", + "brf6100", + "brf6150", + "brf6300", + "brf6350", + "unknown", + "wl1271" + }; + char fw[100]; + + fprintf(stderr, "Texas module LMP sub-version : 0x%04x\n", lmp_subv); + + fprintf(stderr, + "\tinternal version freeze: %d\n" + "\tsoftware version: %d\n" + "\tchip: %s (%d)\n", + lmp_subv & 0x7f, + ((lmp_subv & 0x8000) >> (15-3)) | ((lmp_subv & 0x380) >> 7), + ((brf_chip > 7) ? "unknown" : c_brf_chip[brf_chip]), + brf_chip); + + sprintf(fw, "/etc/firmware/%s.bin", c_brf_chip[brf_chip]); + texas_load_firmware(fd, fw); + + texas_change_speed(fd, speed); + } + nanosleep(&tm, NULL); + return 0; +} diff --git a/tools/hciconfig.8 b/tools/hciconfig.8 new file mode 100644 index 0000000..35956c4 --- /dev/null +++ b/tools/hciconfig.8 @@ -0,0 +1,277 @@ +.TH HCICONFIG 8 "Nov 11 2002" BlueZ "Linux System Administration" +.SH NAME +hciconfig \- configure Bluetooth devices +.SH SYNOPSIS +.B hciconfig +.B \-h +.br +.B hciconfig +.RB [\| \-a \|] +.br +.B hciconfig +.RB [\| \-a \|] +.B hciX +.RI [\| command +.RI [\| "command parameters" \|]\|] + +.SH DESCRIPTION +.LP +.B hciconfig +is used to configure Bluetooth devices. +.I hciX +is the name of a Bluetooth device installed in the system. If +.I hciX +is not given, +.B hciconfig +prints name and basic information about all the Bluetooth devices installed in +the system. If +.I hciX +is given but no command is given, it prints basic information on device +.I hciX +only. Basic information is +interface type, BD address, ACL MTU, SCO MTU, flags (up, init, running, raw, +page scan enabled, inquiry scan enabled, inquiry, authentication enabled, +encryption enabled). +.SH OPTIONS +.TP +.B \-h, \-\-help +Gives a list of possible commands. +.TP +.B \-a, \-\-all +Other than the basic info, print features, packet type, link policy, link mode, +name, class, version. +.SH COMMANDS +.TP +.B up +Open and initialize HCI device. +.TP +.B down +Close HCI device. +.TP +.B reset +Reset HCI device. +.TP +.B rstat +Reset statistic counters. +.TP +.B auth +Enable authentication (sets device to security mode 3). +.TP +.B noauth +Disable authentication. +.TP +.B encrypt +Enable encryption (sets device to security mode 3). +.TP +.B noencrypt +Disable encryption. +.TP +.B secmgr +Enable security manager (current kernel support is limited). +.TP +.B nosecmgr +Disable security manager. +.TP +.B piscan +Enable page and inquiry scan. +.TP +.B noscan +Disable page and inquiry scan. +.TP +.B iscan +Enable inquiry scan, disable page scan. +.TP +.B pscan +Enable page scan, disable inquiry scan. +.TP +\fBptype\fP [\fItype\fP] +With no +.I type +, displays the current packet types. Otherwise, all the packet types specified +by +.I type +are set. +.I type +is a comma-separated list of packet types, where the possible packet types are +.BR DM1 , +.BR DM3 , +.BR DM5 , +.BR DH1 , +.BR DH3 , +.BR DH5 , +.BR HV1 , +.BR HV2 , +.BR HV3 . +.TP +.BI name " [name]" +With no +.IR name , +prints local name. Otherwise, sets local name to +.IR name . +.TP +.BI class " [class]" +With no +.IR class , +prints class of device. Otherwise, sets class of device to +.IR class . +.I +class +is a 24-bit hex number describing the class of device, as specified in section +1.2 of the Bluetooth Assigned Numers document. +.TP +.BI voice " [voice]" +With no +.IR voice , +prints voice setting. Otherwise, sets voice setting to +.IR voice . +.I voice +is a 16-bit hex number describing the voice setting. +.TP +.BI iac " [iac]" +With no +.IR iac , +prints the current IAC setting. Otherwise, sets the IAC to +.IR iac . +.TP +.BI inqtpl " [level]" +With no +.IR level , +prints out the current inquiry transmit power level. Otherwise, sets +inquiry transmit power level to +.IR level . +.TP +.BI inqmode " [mode]" +With no +.IR mode , +prints out the current inquiry mode. Otherwise, sets inquiry mode to +.IR mode . +.TP +.BI inqdata " [data]" +With no +.IR name , +prints out the current inquiry data. Otherwise, sets inquiry data to +.IR data . +.TP +.BI inqtype " [type]" +With no +.IR type , +prints out the current inquiry scan type. Otherwise, sets inquiry scan type to +.IR type . +.TP +\fBinqparams\fP [\fIwin\fP:\fIint\fP] +With no +.IR win : int , +prints inquiry scan window and interval. Otherwise, sets inquiry scan window +to +.I win +slots and inquiry scan interval to +.I int +slots. +.TP +\fBpageparms\fP [\fIwin\fP:\fIint\fP] +With no +.IR win : int , +prints page scan window and interval. Otherwise, sets page scan window to +.I win +slots and page scan interval to +.I int +slots. +.TP +.BI pageto " [to]" +With no +.IR to , +prints page timeout. Otherwise, sets page timeout +to .I +to +slots. +.TP +.BI afhmode " [mode]" +With no +.IR mode , +prints out the current AFH mode. Otherwise, sets AFH mode to +.IR mode . +.TP +.BI sspmode " [mode]" +With no +.IR mode , +prints out the current Simple Pairing mode. Otherwise, sets Simple Pairing mode to +.IR mode . +.TP +\fBaclmtu\fP \fImtu\fP:\fIpkt\fP +Sets ACL MTU to +to +.I mtu +bytes and ACL buffer size to +.I pkt +packets. +.TP +\fBscomtu\fP \fImtu\fP:\fIpkt\fP +Sets SCO MTU to +.I mtu +bytes and SCO buffer size to +.I pkt +packets. +.TP +.BI putkey " " +This command stores the link key for +.I bdaddr +on the device. +.TP +.BI delkey " " +This command deletes the stored link key for +.I bdaddr +from the device. +.TP +.BI oobdata +Display local OOB data. +.TP +.BI commands +Display supported commands. +.TP +.BI features +Display device features. +.TP +.BI version +Display version information. +.TP +.BI revision +Display revision information. +.TP +.BI lm " [mode]" +With no +.I mode +, prints link mode. +.B MASTER +or +.B SLAVE +mean, respectively, to ask to become master or to remain slave when a +connection request comes in. The additional keyword +.B ACCEPT +means that baseband connections will be accepted even if there are no +listening +.I AF_BLUETOOTH +sockets. +.I mode +is +.B NONE +or a comma-separated list of keywords, where possible keywords are +.B MASTER +and +.B "ACCEPT" . +.B NONE +sets link policy to the default behaviour of remaining slave and not accepting +baseband connections when there are no listening +.I AF_BLUETOOTH +sockets. If +.B MASTER +is present, the device will ask to become master if a connection request comes +in. If +.B ACCEPT +is present, the device will accept baseband connections even when there are no +listening +.I AF_BLUETOOTH +sockets. +.SH AUTHORS +Written by Maxim Krasnyansky and Marcel Holtmann +.PP +man page by Fabrizio Gennari diff --git a/tools/hciconfig.c b/tools/hciconfig.c new file mode 100644 index 0000000..3db70a4 --- /dev/null +++ b/tools/hciconfig.c @@ -0,0 +1,2036 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2000-2001 Qualcomm Incorporated + * Copyright (C) 2002-2003 Maxim Krasnyansky + * Copyright (C) 2002-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "textfile.h" +#include "csr.h" + +static struct hci_dev_info di; +static int all; + +static void print_dev_hdr(struct hci_dev_info *di); +static void print_dev_info(int ctl, struct hci_dev_info *di); + +static 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) < 0) { + 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) < 0) + continue; + if (hci_test_bit(HCI_RAW, &di.flags) && + !bacmp(&di.bdaddr, BDADDR_ANY)) { + int dd = hci_open_dev(di.dev_id); + hci_read_bd_addr(dd, &di.bdaddr, 1000); + hci_close_dev(dd); + } + print_dev_info(ctl, &di); + } +} + +static void print_pkt_type(struct hci_dev_info *di) +{ + char *str; + str = hci_ptypetostr(di->pkt_type); + printf("\tPacket type: %s\n", str); + bt_free(str); +} + +static void print_link_policy(struct hci_dev_info *di) +{ + printf("\tLink policy: %s\n", hci_lptostr(di->link_policy)); +} + +static void print_link_mode(struct hci_dev_info *di) +{ + char *str; + str = hci_lmtostr(di->link_mode); + printf("\tLink mode: %s\n", str); + bt_free(str); +} + +static void print_dev_features(struct hci_dev_info *di, int format) +{ + 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", + di->features[0], di->features[1], di->features[2], + di->features[3], di->features[4], di->features[5], + di->features[6], di->features[7]); + + if (format) { + char *tmp = lmp_featurestostr(di->features, "\t\t", 63); + printf("%s\n", tmp); + bt_free(tmp); + } +} + +static void print_le_states(uint64_t states) +{ + int i; + const char *le_states[] = { + "Non-connectable Advertising State" , + "Scannable Advertising State", + "Connectable Advertising State", + "Directed Advertising State", + "Passive Scanning State", + "Active Scanning State", + "Initiating State/Connection State in Master Role", + "Connection State in the Slave Role", + "Non-connectable Advertising State and Passive Scanning State combination", + "Scannable Advertising State and Passive Scanning State combination", + "Connectable Advertising State and Passive Scanning State combination", + "Directed Advertising State and Passive Scanning State combination", + "Non-connectable Advertising State and Active Scanning State combination", + "Scannable Advertising State and Active Scanning State combination", + "Connectable Advertising State and Active Scanning State combination", + "Directed Advertising State and Active Scanning State combination", + "Non-connectable Advertising State and Initiating State combination", + "Scannable Advertising State and Initiating State combination", + "Non-connectable Advertising State and Master Role combination", + "Scannable Advertising State and Master Role combination", + "Non-connectable Advertising State and Slave Role combination", + "Scannable Advertising State and Slave Role combination", + "Passive Scanning State and Initiating State combination", + "Active Scanning State and Initiating State combination", + "Passive Scanning State and Master Role combination", + "Active Scanning State and Master Role combination", + "Passive Scanning State and Slave Role combination", + "Active Scanning State and Slave Role combination", + "Initiating State and Master Role combination/Master Role and Master Role combination", + NULL + }; + + printf("Supported link layer states:\n"); + for (i = 0; le_states[i]; i++) { + const char *status; + + status = states & (1 << i) ? "YES" : "NO "; + printf("\t%s %s\n", status, le_states[i]); + } +} + +static void cmd_rstat(int ctl, int hdev, char *opt) +{ + /* Reset HCI device stat counters */ + if (ioctl(ctl, HCIDEVRESTAT, hdev) < 0) { + fprintf(stderr, "Can't reset stats counters hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } +} + +static 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) { + fprintf(stderr, "Can't set scan mode on hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } +} + +static void cmd_le_addr(int ctl, int hdev, char *opt) +{ + struct hci_request rq; + le_set_random_address_cp cp; + uint8_t status; + int dd, err, ret; + + if (!opt) + return; + + if (hdev < 0) + hdev = hci_get_route(NULL); + + dd = hci_open_dev(hdev); + if (dd < 0) { + err = errno; + fprintf(stderr, "Could not open device: %s(%d)\n", + strerror(err), err); + exit(1); + } + + memset(&cp, 0, sizeof(cp)); + + str2ba(opt, &cp.bdaddr); + + memset(&rq, 0, sizeof(rq)); + rq.ogf = OGF_LE_CTL; + rq.ocf = OCF_LE_SET_RANDOM_ADDRESS; + rq.cparam = &cp; + rq.clen = LE_SET_RANDOM_ADDRESS_CP_SIZE; + rq.rparam = &status; + rq.rlen = 1; + + ret = hci_send_req(dd, &rq, 1000); + if (status || ret < 0) { + err = errno; + fprintf(stderr, "Can't set random address for hci%d: " + "%s (%d)\n", hdev, strerror(err), err); + } + + hci_close_dev(dd); +} + +static void cmd_le_adv(int ctl, int hdev, char *opt) +{ + struct hci_request rq; + le_set_advertise_enable_cp advertise_cp; + uint8_t status; + int dd, ret; + + if (hdev < 0) + hdev = hci_get_route(NULL); + + dd = hci_open_dev(hdev); + if (dd < 0) { + perror("Could not open device"); + exit(1); + } + + memset(&advertise_cp, 0, sizeof(advertise_cp)); + if (strcmp(opt, "noleadv") == 0) + advertise_cp.enable = 0x00; + else + advertise_cp.enable = 0x01; + + memset(&rq, 0, sizeof(rq)); + rq.ogf = OGF_LE_CTL; + rq.ocf = OCF_LE_SET_ADVERTISE_ENABLE; + rq.cparam = &advertise_cp; + rq.clen = LE_SET_ADVERTISE_ENABLE_CP_SIZE; + rq.rparam = &status; + rq.rlen = 1; + + ret = hci_send_req(dd, &rq, 1000); + + hci_close_dev(dd); + + if (ret < 0) { + fprintf(stderr, "Can't set advertise mode on hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + + if (status) { + fprintf(stderr, "LE set advertise enable on hci%d returned status %d\n", + hdev, status); + exit(1); + } +} + +static void cmd_le_states(int ctl, int hdev, char *opt) +{ + le_read_supported_states_rp rp; + struct hci_request rq; + int err, dd; + + if (hdev < 0) + hdev = hci_get_route(NULL); + + dd = hci_open_dev(hdev); + if (dd < 0) { + fprintf(stderr, "Can't open device hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + + memset(&rp, 0, sizeof(rp)); + memset(&rq, 0, sizeof(rq)); + + rq.ogf = OGF_LE_CTL; + rq.ocf = OCF_LE_READ_SUPPORTED_STATES; + rq.rparam = &rp; + rq.rlen = LE_READ_SUPPORTED_STATES_RP_SIZE; + + err = hci_send_req(dd, &rq, 1000); + + hci_close_dev(dd); + + if (err < 0) { + fprintf(stderr, "Can't read LE supported states on hci%d:" + " %s(%d)\n", hdev, strerror(errno), errno); + exit(1); + } + + if (rp.status) { + fprintf(stderr, "Read LE supported states on hci%d" + " returned status %d\n", hdev, rp.status); + exit(1); + } + + print_le_states(rp.states); +} + +static void cmd_iac(int ctl, int hdev, char *opt) +{ + int s = hci_open_dev(hdev); + + if (s < 0) { + fprintf(stderr, "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 (!strcasecmp(opt, "giac")) { + l = 0x9e8b33; + } else if (!strcasecmp(opt, "liac")) { + l = 0x9e8b00; + } else 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); +} + +static 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) { + fprintf(stderr, "Can't set auth on hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } +} + +static 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) { + fprintf(stderr, "Can't set encrypt on hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } +} + +static void cmd_up(int ctl, int hdev, char *opt) +{ + /* Start HCI device */ + if (ioctl(ctl, HCIDEVUP, hdev) < 0) { + if (errno == EALREADY) + return; + fprintf(stderr, "Can't init device hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } +} + +static void cmd_down(int ctl, int hdev, char *opt) +{ + /* Stop HCI device */ + if (ioctl(ctl, HCIDEVDOWN, hdev) < 0) { + fprintf(stderr, "Can't down device hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } +} + +static void cmd_reset(int ctl, int hdev, char *opt) +{ + /* Reset HCI device */ +#if 0 + if (ioctl(ctl, HCIDEVRESET, hdev) < 0 ){ + fprintf(stderr, "Reset failed for device hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } +#endif + cmd_down(ctl, hdev, "down"); + cmd_up(ctl, hdev, "up"); +} + +static 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) { + fprintf(stderr, "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); + } +} + +static 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) { + fprintf(stderr, "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); + } +} + +static 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) { + fprintf(stderr, "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); + } +} + +static 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; + + dr.dev_opt = htobl(htobs(mpkt) | (htobs(mtu) << 16)); + + if (ioctl(ctl, HCISETACLMTU, (unsigned long) &dr) < 0) { + fprintf(stderr, "Can't set ACL mtu on hci%d: %s(%d)\n", + hdev, strerror(errno), errno); + exit(1); + } +} + +static 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; + + dr.dev_opt = htobl(htobs(mpkt) | (htobs(mtu) << 16)); + + if (ioctl(ctl, HCISETSCOMTU, (unsigned long) &dr) < 0) { + fprintf(stderr, "Can't set SCO mtu on hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } +} + +static void cmd_features(int ctl, int hdev, char *opt) +{ + uint8_t features[8], max_page = 0; + char *tmp; + int i, dd; + + if (!(di.features[7] & LMP_EXT_FEAT)) { + print_dev_hdr(&di); + print_dev_features(&di, 1); + return; + } + + dd = hci_open_dev(hdev); + if (dd < 0) { + fprintf(stderr, "Can't open device hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + + if (hci_read_local_ext_features(dd, 0, &max_page, features, 1000) < 0) { + fprintf(stderr, "Can't read extended features hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + + print_dev_hdr(&di); + printf("\tFeatures%s: 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", + (max_page > 0) ? " page 0" : "", + features[0], features[1], features[2], features[3], + features[4], features[5], features[6], features[7]); + + tmp = lmp_featurestostr(di.features, "\t\t", 63); + printf("%s\n", tmp); + bt_free(tmp); + + for (i = 1; i <= max_page; i++) { + if (hci_read_local_ext_features(dd, i, NULL, + features, 1000) < 0) + continue; + + printf("\tFeatures page %d: 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", i, + features[0], features[1], features[2], features[3], + features[4], features[5], features[6], features[7]); + } + + hci_close_dev(dd); +} + +static void cmd_name(int ctl, int hdev, char *opt) +{ + int dd; + + dd = hci_open_dev(hdev); + if (dd < 0) { + fprintf(stderr, "Can't open device hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + + if (opt) { + if (hci_write_local_name(dd, opt, 2000) < 0) { + fprintf(stderr, "Can't change local name on hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + } else { + char name[249]; + int i; + + if (hci_read_local_name(dd, sizeof(name), name, 1000) < 0) { + fprintf(stderr, "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 ((unsigned char) name[i] < 32 || name[i] == 127) + name[i] = '.'; + } + + name[248] = '\0'; + + print_dev_hdr(&di); + printf("\tName: '%s'\n", name); + } + + hci_close_dev(dd); +} + +/* + * 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 */ { + static char cls_str[48]; + + cls_str[0] = '\0'; + + switch (minor & 48) { + case 16: + strncpy(cls_str, "Keyboard", sizeof(cls_str)); + break; + case 32: + strncpy(cls_str, "Pointing device", sizeof(cls_str)); + break; + case 48: + strncpy(cls_str, "Combo keyboard/pointing device", sizeof(cls_str)); + break; + } + if ((minor & 15) && (strlen(cls_str) > 0)) + strcat(cls_str, "/"); + + switch (minor & 15) { + case 0: + break; + case 1: + strncat(cls_str, "Joystick", sizeof(cls_str) - strlen(cls_str)); + break; + case 2: + strncat(cls_str, "Gamepad", sizeof(cls_str) - strlen(cls_str)); + break; + case 3: + strncat(cls_str, "Remote control", sizeof(cls_str) - strlen(cls_str)); + break; + case 4: + strncat(cls_str, "Sensing device", sizeof(cls_str) - strlen(cls_str)); + break; + case 5: + strncat(cls_str, "Digitizer tablet", sizeof(cls_str) - strlen(cls_str)); + break; + case 6: + strncat(cls_str, "Card reader", sizeof(cls_str) - strlen(cls_str)); + break; + default: + strncat(cls_str, "(reserved)", sizeof(cls_str) - strlen(cls_str)); + break; + } + if (strlen(cls_str) > 0) + return cls_str; + } + 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 7: /* wearable */ + switch (minor) { + case 1: + return "Wrist Watch"; + case 2: + return "Pager"; + case 3: + return "Jacket"; + case 4: + return "Helmet"; + case 5: + return "Glasses"; + } + break; + case 8: /* toy */ + switch (minor) { + case 1: + return "Robot"; + case 2: + return "Vehicle"; + case 3: + return "Doll / Action Figure"; + case 4: + return "Controller"; + case 5: + return "Game"; + } + break; + case 63: /* uncategorised */ + return ""; + } + return "Unknown (reserved) minor device class"; +} + +static void cmd_class(int ctl, int hdev, char *opt) +{ + static const char *services[] = { "Positioning", + "Networking", + "Rendering", + "Capturing", + "Object Transfer", + "Audio", + "Telephony", + "Information" }; + static const char *major_devices[] = { "Miscellaneous", + "Computer", + "Phone", + "LAN Access", + "Audio/Video", + "Peripheral", + "Imaging", + "Uncategorized" }; + int s = hci_open_dev(hdev); + + if (s < 0) { + fprintf(stderr, "Can't open device hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + if (opt) { + uint32_t cod = strtoul(opt, NULL, 16); + if (hci_write_class_of_dev(s, cod, 2000) < 0) { + fprintf(stderr, "Can't write local class of device on hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + } else { + uint8_t cls[3]; + if (hci_read_class_of_dev(s, cls, 1000) < 0) { + fprintf(stderr, "Can't read class of device on hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + print_dev_hdr(&di); + printf("\tClass: 0x%02x%02x%02x\n", cls[2], cls[1], cls[0]); + printf("\tService Classes: "); + if (cls[2]) { + unsigned int i; + int first = 1; + for (i = 0; i < (sizeof(services) / sizeof(*services)); i++) + if (cls[2] & (1 << i)) { + if (!first) + printf(", "); + printf("%s", services[i]); + first = 0; + } + } else + printf("Unspecified"); + printf("\n\tDevice Class: "); + if ((cls[1] & 0x1f) >= sizeof(major_devices) / sizeof(*major_devices)) + printf("Invalid Device Class!\n"); + else + printf("%s, %s\n", major_devices[cls[1] & 0x1f], + get_minor_device_name(cls[1] & 0x1f, cls[0] >> 2)); + } +} + +static 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) { + fprintf(stderr, "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)); + if (hci_write_voice_setting(s, vs, 2000) < 0) { + fprintf(stderr, "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 (hci_read_voice_setting(s, &vs, 1000) < 0) { + fprintf(stderr, "Can't read voice setting on hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + vs = htobs(vs); + 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]); + } +} + +static int get_link_key(const bdaddr_t *local, const bdaddr_t *peer, + uint8_t *key) +{ + char filename[PATH_MAX + 1], addr[18], tmp[3], *str; + int i; + + ba2str(local, addr); + create_name(filename, PATH_MAX, STORAGEDIR, addr, "linkkeys"); + + ba2str(peer, addr); + str = textfile_get(filename, addr); + if (!str) + return -EIO; + + memset(tmp, 0, sizeof(tmp)); + for (i = 0; i < 16; i++) { + memcpy(tmp, str + (i * 2), 2); + key[i] = (uint8_t) strtol(tmp, NULL, 16); + } + + free(str); + + return 0; +} + +static void cmd_putkey(int ctl, int hdev, char *opt) +{ + struct hci_dev_info di; + bdaddr_t bdaddr; + uint8_t key[16]; + int dd; + + if (!opt) + return; + + dd = hci_open_dev(hdev); + if (dd < 0) { + fprintf(stderr, "Can't open device hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + + if (hci_devinfo(hdev, &di) < 0) { + fprintf(stderr, "Can't get device info for hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + + str2ba(opt, &bdaddr); + if (get_link_key(&di.bdaddr, &bdaddr, key) < 0) { + fprintf(stderr, "Can't find link key for %s on hci%d\n", opt, hdev); + exit(1); + } + + if (hci_write_stored_link_key(dd, &bdaddr, key, 1000) < 0) { + fprintf(stderr, "Can't write stored link key on hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + + hci_close_dev(dd); +} + +static void cmd_delkey(int ctl, int hdev, char *opt) +{ + bdaddr_t bdaddr; + uint8_t all; + int dd; + + if (!opt) + return; + + dd = hci_open_dev(hdev); + if (dd < 0) { + fprintf(stderr, "Can't open device hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + + if (!strcasecmp(opt, "all")) { + bacpy(&bdaddr, BDADDR_ANY); + all = 1; + } else { + str2ba(opt, &bdaddr); + all = 0; + } + + if (hci_delete_stored_link_key(dd, &bdaddr, all, 1000) < 0) { + fprintf(stderr, "Can't delete stored link key on hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + + hci_close_dev(dd); +} + +static void cmd_oob_data(int ctl, int hdev, char *opt) +{ + uint8_t hash[16], randomizer[16]; + int i, dd; + + dd = hci_open_dev(hdev); + if (dd < 0) { + fprintf(stderr, "Can't open device hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + + if (hci_read_local_oob_data(dd, hash, randomizer, 1000) < 0) { + fprintf(stderr, "Can't read local OOB data on hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + + print_dev_hdr(&di); + printf("\tOOB Hash: "); + for (i = 0; i < 16; i++) + printf(" %02x", hash[i]); + printf("\n\tRandomizer:"); + for (i = 0; i < 16; i++) + printf(" %02x", randomizer[i]); + printf("\n"); + + hci_close_dev(dd); +} + +static void cmd_commands(int ctl, int hdev, char *opt) +{ + uint8_t cmds[64]; + char *str; + int i, n, dd; + + dd = hci_open_dev(hdev); + if (dd < 0) { + fprintf(stderr, "Can't open device hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + + if (hci_read_local_commands(dd, cmds, 1000) < 0) { + fprintf(stderr, "Can't read support commands on hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + + print_dev_hdr(&di); + for (i = 0; i < 64; i++) { + if (!cmds[i]) + continue; + + printf("%s Octet %-2d = 0x%02x (Bit", + i ? "\t\t ": "\tCommands:", i, cmds[i]); + for (n = 0; n < 8; n++) + if (cmds[i] & (1 << n)) + printf(" %d", n); + printf(")\n"); + } + + str = hci_commandstostr(cmds, "\t", 71); + printf("%s\n", str); + bt_free(str); + + hci_close_dev(dd); +} + +static void cmd_version(int ctl, int hdev, char *opt) +{ + struct hci_version ver; + char *hciver, *lmpver; + int dd; + + dd = hci_open_dev(hdev); + if (dd < 0) { + fprintf(stderr, "Can't open device hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + + if (hci_read_local_version(dd, &ver, 1000) < 0) { + fprintf(stderr, "Can't read version info hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + + hciver = hci_vertostr(ver.hci_ver); + lmpver = lmp_vertostr(ver.lmp_ver); + + print_dev_hdr(&di); + printf("\tHCI Version: %s (0x%x) Revision: 0x%x\n" + "\tLMP Version: %s (0x%x) Subversion: 0x%x\n" + "\tManufacturer: %s (%d)\n", + hciver ? hciver : "n/a", ver.hci_ver, ver.hci_rev, + lmpver ? lmpver : "n/a", ver.lmp_ver, ver.lmp_subver, + bt_compidtostr(ver.manufacturer), ver.manufacturer); + + if (hciver) + bt_free(hciver); + if (lmpver) + bt_free(lmpver); + + hci_close_dev(dd); +} + +static void cmd_inq_tpl(int ctl, int hdev, char *opt) +{ + int dd; + + dd = hci_open_dev(hdev); + if (dd < 0) { + fprintf(stderr, "Can't open device hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + + if (opt) { + int8_t level = atoi(opt); + + if (hci_write_inquiry_transmit_power_level(dd, level, 2000) < 0) { + fprintf(stderr, "Can't set inquiry transmit power level on hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + } else { + int8_t level; + + if (hci_read_inq_response_tx_power_level(dd, &level, 1000) < 0) { + fprintf(stderr, "Can't read inquiry transmit power level on hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + + print_dev_hdr(&di); + printf("\tInquiry transmit power level: %d\n", level); + } + + hci_close_dev(dd); +} + +static void cmd_inq_mode(int ctl, int hdev, char *opt) +{ + int dd; + + dd = hci_open_dev(hdev); + if (dd < 0) { + fprintf(stderr, "Can't open device hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + + if (opt) { + uint8_t mode = atoi(opt); + + if (hci_write_inquiry_mode(dd, mode, 2000) < 0) { + fprintf(stderr, "Can't set inquiry mode on hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + } else { + uint8_t mode; + + if (hci_read_inquiry_mode(dd, &mode, 1000) < 0) { + fprintf(stderr, "Can't read inquiry mode on hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + + print_dev_hdr(&di); + printf("\tInquiry mode: "); + switch (mode) { + case 0: + printf("Standard Inquiry\n"); + break; + case 1: + printf("Inquiry with RSSI\n"); + break; + case 2: + printf("Inquiry with RSSI or Extended Inquiry\n"); + break; + default: + printf("Unknown (0x%02x)\n", mode); + break; + } + } + + hci_close_dev(dd); +} + +static void cmd_inq_data(int ctl, int hdev, char *opt) +{ + int i, dd; + + dd = hci_open_dev(hdev); + if (dd < 0) { + fprintf(stderr, "Can't open device hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + + if (opt) { + uint8_t fec = 0, data[240]; + char tmp[3]; + int i, size; + + memset(data, 0, sizeof(data)); + + memset(tmp, 0, sizeof(tmp)); + size = (strlen(opt) + 1) / 2; + if (size > 240) + size = 240; + + for (i = 0; i < size; i++) { + memcpy(tmp, opt + (i * 2), 2); + data[i] = strtol(tmp, NULL, 16); + } + + if (hci_write_ext_inquiry_response(dd, fec, data, 2000) < 0) { + fprintf(stderr, "Can't set extended inquiry response on hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + } else { + uint8_t fec, data[240], len, type, *ptr; + char *str; + + if (hci_read_ext_inquiry_response(dd, &fec, data, 1000) < 0) { + fprintf(stderr, "Can't read extended inquiry response on hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + + print_dev_hdr(&di); + printf("\tFEC %s\n\t\t", fec ? "enabled" : "disabled"); + for (i = 0; i < 240; i++) + printf("%02x%s%s", data[i], (i + 1) % 8 ? "" : " ", + (i + 1) % 16 ? " " : (i < 239 ? "\n\t\t" : "\n")); + + ptr = data; + while (*ptr) { + len = *ptr++; + type = *ptr++; + switch (type) { + case 0x01: + printf("\tFlags:"); + for (i = 0; i < len - 1; i++) + printf(" 0x%2.2x", *((uint8_t *) (ptr + i))); + printf("\n"); + break; + case 0x02: + case 0x03: + printf("\t%s service classes:", + type == 0x02 ? "Shortened" : "Complete"); + for (i = 0; i < (len - 1) / 2; i++) { + uint16_t val = btohs(bt_get_unaligned((uint16_t *) (ptr + (i * 2)))); + printf(" 0x%4.4x", val); + } + printf("\n"); + break; + case 0x08: + case 0x09: + str = malloc(len); + if (str) { + snprintf(str, len, "%s", ptr); + for (i = 0; i < len - 1; i++) { + if ((unsigned char) str[i] < 32 || str[i] == 127) + str[i] = '.'; + } + printf("\t%s local name: \'%s\'\n", + type == 0x08 ? "Shortened" : "Complete", str); + free(str); + } + break; + case 0x0a: + printf("\tTX power level: %d\n", *((int8_t *) ptr)); + break; + case 0x10: + printf("\tDevice ID with %d bytes data\n", + len - 1); + break; + default: + printf("\tUnknown type 0x%02x with %d bytes data\n", + type, len - 1); + break; + } + + ptr += (len - 1); + } + + printf("\n"); + } + + hci_close_dev(dd); +} + +static void cmd_inq_type(int ctl, int hdev, char *opt) +{ + int dd; + + dd = hci_open_dev(hdev); + if (dd < 0) { + fprintf(stderr, "Can't open device hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + + if (opt) { + uint8_t type = atoi(opt); + + if (hci_write_inquiry_scan_type(dd, type, 2000) < 0) { + fprintf(stderr, "Can't set inquiry scan type on hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + } else { + uint8_t type; + + if (hci_read_inquiry_scan_type(dd, &type, 1000) < 0) { + fprintf(stderr, "Can't read inquiry scan type on hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + + print_dev_hdr(&di); + printf("\tInquiry scan type: %s\n", + type == 1 ? "Interlaced Inquiry Scan" : "Standard Inquiry Scan"); + } + + hci_close_dev(dd); +} + +static void cmd_inq_parms(int ctl, int hdev, char *opt) +{ + struct hci_request rq; + int s; + + if ((s = hci_open_dev(hdev)) < 0) { + fprintf(stderr, "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, 2000) < 0) { + fprintf(stderr, "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) { + fprintf(stderr, "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); + } +} + +static void cmd_page_parms(int ctl, int hdev, char *opt) +{ + struct hci_request rq; + int s; + + if ((s = hci_open_dev(hdev)) < 0) { + fprintf(stderr, "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); + } + + rq.ogf = OGF_HOST_CTL; + rq.ocf = OCF_WRITE_PAGE_ACTIVITY; + rq.cparam = &cp; + rq.clen = WRITE_PAGE_ACTIVITY_CP_SIZE; + + cp.window = htobs((uint16_t) window); + cp.interval = htobs((uint16_t) interval); + + if (window < 0x12 || window > 0x1000) + printf("Warning: page window out of range!\n"); + + if (interval < 0x12 || interval > 0x1000) + printf("Warning: page interval out of range!\n"); + + if (hci_send_req(s, &rq, 2000) < 0) { + fprintf(stderr, "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; + + rq.ogf = OGF_HOST_CTL; + rq.ocf = OCF_READ_PAGE_ACTIVITY; + rq.rparam = &rp; + rq.rlen = READ_PAGE_ACTIVITY_RP_SIZE; + + if (hci_send_req(s, &rq, 1000) < 0) { + fprintf(stderr, "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); + + 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); + } +} + +static void cmd_page_to(int ctl, int hdev, char *opt) +{ + struct hci_request rq; + int s; + + if ((s = hci_open_dev(hdev)) < 0) { + fprintf(stderr, "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; + + if (sscanf(opt,"%5u", &timeout) != 1) { + printf("Invalid argument format\n"); + exit(1); + } + + rq.ogf = OGF_HOST_CTL; + rq.ocf = OCF_WRITE_PAGE_TIMEOUT; + rq.cparam = &cp; + rq.clen = WRITE_PAGE_TIMEOUT_CP_SIZE; + + cp.timeout = htobs((uint16_t) timeout); + + if (timeout < 0x01 || timeout > 0xFFFF) + printf("Warning: page timeout out of range!\n"); + + if (hci_send_req(s, &rq, 2000) < 0) { + fprintf(stderr, "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; + + rq.ogf = OGF_HOST_CTL; + rq.ocf = OCF_READ_PAGE_TIMEOUT; + rq.rparam = &rp; + rq.rlen = READ_PAGE_TIMEOUT_RP_SIZE; + + if (hci_send_req(s, &rq, 1000) < 0) { + fprintf(stderr, "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); + } +} + +static void cmd_afh_mode(int ctl, int hdev, char *opt) +{ + int dd; + + dd = hci_open_dev(hdev); + if (dd < 0) { + fprintf(stderr, "Can't open device hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + + if (opt) { + uint8_t mode = atoi(opt); + + if (hci_write_afh_mode(dd, mode, 2000) < 0) { + fprintf(stderr, "Can't set AFH mode on hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + } else { + uint8_t mode; + + if (hci_read_afh_mode(dd, &mode, 1000) < 0) { + fprintf(stderr, "Can't read AFH mode on hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + + print_dev_hdr(&di); + printf("\tAFH mode: %s\n", mode == 1 ? "Enabled" : "Disabled"); + } +} + +static void cmd_ssp_mode(int ctl, int hdev, char *opt) +{ + int dd; + + dd = hci_open_dev(hdev); + if (dd < 0) { + fprintf(stderr, "Can't open device hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + + if (opt) { + uint8_t mode = atoi(opt); + + if (hci_write_simple_pairing_mode(dd, mode, 2000) < 0) { + fprintf(stderr, "Can't set Simple Pairing mode on hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + } else { + uint8_t mode; + + if (hci_read_simple_pairing_mode(dd, &mode, 1000) < 0) { + fprintf(stderr, "Can't read Simple Pairing mode on hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + + print_dev_hdr(&di); + printf("\tSimple Pairing mode: %s\n", + mode == 1 ? "Enabled" : "Disabled"); + } +} + +static void print_rev_ericsson(int dd) +{ + 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, 0x0000, &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 / %d\n", + hci_rev & 0xff, lmp_subver >> 8, lmp_subver & 0xff); +} + +static void print_rev_avm(uint16_t hci_rev, uint16_t lmp_subver) +{ + 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; + int dd; + + dd = hci_open_dev(hdev); + if (dd < 0) { + fprintf(stderr, "Can't open device hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + return; + } + + if (hci_read_local_version(dd, &ver, 1000) < 0) { + fprintf(stderr, "Can't read version info for hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + return; + } + + print_dev_hdr(&di); + switch (ver.manufacturer) { + case 0: + case 37: + case 48: + print_rev_ericsson(dd); + break; + case 10: + print_rev_csr(dd, ver.hci_rev); + break; + case 12: + print_rev_digianswer(dd); + break; + case 15: + print_rev_broadcom(ver.hci_rev, ver.lmp_subver); + break; + case 31: + print_rev_avm(ver.hci_rev, ver.lmp_subver); + break; + default: + printf("\tUnsupported manufacturer\n"); + break; + } + return; +} + +static void cmd_block(int ctl, int hdev, char *opt) +{ + bdaddr_t bdaddr; + int dd; + + if (!opt) + return; + + dd = hci_open_dev(hdev); + if (dd < 0) { + fprintf(stderr, "Can't open device hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + + str2ba(opt, &bdaddr); + + if (ioctl(dd, HCIBLOCKADDR, &bdaddr) < 0) { + perror("ioctl(HCIBLOCKADDR)"); + exit(1); + } + + hci_close_dev(dd); +} + +static void cmd_unblock(int ctl, int hdev, char *opt) +{ + bdaddr_t bdaddr; + int dd; + + if (!opt) + return; + + dd = hci_open_dev(hdev); + if (dd < 0) { + fprintf(stderr, "Can't open device hci%d: %s (%d)\n", + hdev, strerror(errno), errno); + exit(1); + } + + if (!strcasecmp(opt, "all")) + bacpy(&bdaddr, BDADDR_ANY); + else + str2ba(opt, &bdaddr); + + if (ioctl(dd, HCIUNBLOCKADDR, &bdaddr) < 0) { + perror("ioctl(HCIUNBLOCKADDR)"); + exit(1); + } + + hci_close_dev(dd); +} + +static void print_dev_hdr(struct hci_dev_info *di) +{ + static int hdr = -1; + char addr[18]; + + if (hdr == di->dev_id) + return; + hdr = di->dev_id; + + ba2str(&di->bdaddr, addr); + + printf("%s:\tType: %s Bus: %s\n", di->name, + hci_typetostr(di->type >> 4), + hci_bustostr(di->type & 0x0f)); + printf("\tBD Address: %s ACL MTU: %d:%d SCO MTU: %d:%d\n", + addr, di->acl_mtu, di->acl_pkts, + di->sco_mtu, di->sco_pkts); +} + +static void print_dev_info(int ctl, struct hci_dev_info *di) +{ + struct hci_dev_stats *st = &di->stat; + char *str; + + print_dev_hdr(di); + + str = hci_dflagstostr(di->flags); + printf("\t%s\n", str); + bt_free(str); + + 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 && !hci_test_bit(HCI_RAW, &di->flags) && + bacmp(&di->bdaddr, BDADDR_ANY)) { + print_dev_features(di, 0); + print_pkt_type(di); + print_link_policy(di); + print_link_mode(di); + + 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); + } + } + + printf("\n"); +} + +static 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" }, + { "voice", cmd_voice, "[voice]", "Get/Set voice setting" }, + { "iac", cmd_iac, "[iac]", "Get/Set inquiry access code" }, + { "inqtpl", cmd_inq_tpl, "[level]", "Get/Set inquiry transmit power level" }, + { "inqmode", cmd_inq_mode, "[mode]", "Get/Set inquiry mode" }, + { "inqdata", cmd_inq_data, "[data]", "Get/Set inquiry data" }, + { "inqtype", cmd_inq_type, "[type]", "Get/Set inquiry scan type" }, + { "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" }, + { "afhmode", cmd_afh_mode, "[mode]", "Get/Set AFH mode" }, + { "sspmode", cmd_ssp_mode, "[mode]", "Get/Set Simple Pairing Mode" }, + { "aclmtu", cmd_aclmtu, "", "Set ACL MTU and number of packets" }, + { "scomtu", cmd_scomtu, "", "Set SCO MTU and number of packets" }, + { "putkey", cmd_putkey, "", "Store link key on the device" }, + { "delkey", cmd_delkey, "", "Delete link key from the device" }, + { "oobdata", cmd_oob_data, 0, "Display local OOB data" }, + { "commands", cmd_commands, 0, "Display supported commands" }, + { "features", cmd_features, 0, "Display device features" }, + { "version", cmd_version, 0, "Display version information" }, + { "revision", cmd_revision, 0, "Display revision information" }, + { "block", cmd_block, "", "Add a device to the blacklist" }, + { "unblock", cmd_unblock, "", "Remove a device from the blacklist" }, + { "lerandaddr", cmd_le_addr, "", "Set LE Random Address" }, + { "leadv", cmd_le_adv, 0, "Enable LE advertising" }, + { "noleadv", cmd_le_adv, 0, "Disable LE advertising" }, + { "lestates", cmd_le_states, 0, "Display the supported LE states" }, + { NULL, NULL, 0 } +}; + +static 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[]) +{ + 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); + } + + if (hci_test_bit(HCI_RAW, &di.flags) && + !bacmp(&di.bdaddr, BDADDR_ANY)) { + int dd = hci_open_dev(di.dev_id); + hci_read_bd_addr(dd, &di.bdaddr, 1000); + hci_close_dev(dd); + } + + while (argc > 0) { + for (i = 0; command[i].cmd; i++) { + if (strncmp(command[i].cmd, + *argv, strlen(command[i].cmd))) + continue; + + if (command[i].opt) { + argc--; argv++; + } + + command[i].func(ctl, di.dev_id, *argv); + cmd = 1; + break; + } + + if (command[i].cmd == 0) + fprintf(stderr, "Warning: unknown command - \"%s\"\n", + *argv); + + argc--; argv++; + } + + if (!cmd) + print_dev_info(ctl, &di); + + close(ctl); + return 0; +} diff --git a/tools/hcieventmask.c b/tools/hcieventmask.c new file mode 100644 index 0000000..87beac9 --- /dev/null +++ b/tools/hcieventmask.c @@ -0,0 +1,130 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2002-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include + +static struct option main_options[] = { + { "device", 1, 0, 'i' }, + { 0, 0, 0, 0 } +}; + +int main(int argc, char *argv[]) +{ + uint8_t events[8] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00 }; + struct hci_dev_info di; + struct hci_version ver; + int dd, opt, dev = 0; + + while ((opt=getopt_long(argc, argv, "+i:", main_options, NULL)) != -1) { + switch (opt) { + case 'i': + dev = hci_devid(optarg); + if (dev < 0) { + perror("Invalid device"); + exit(1); + } + break; + } + } + + dd = hci_open_dev(dev); + if (dd < 0) { + fprintf(stderr, "Can't open device hci%d: %s (%d)\n", + dev, strerror(errno), errno); + exit(1); + } + + if (hci_devinfo(dev, &di) < 0) { + fprintf(stderr, "Can't get device info for hci%d: %s (%d)\n", + dev, strerror(errno), errno); + hci_close_dev(dd); + exit(1); + } + + if (hci_read_local_version(dd, &ver, 1000) < 0) { + fprintf(stderr, "Can't read version info for hci%d: %s (%d)\n", + dev, strerror(errno), errno); + hci_close_dev(dd); + exit(1); + } + + hci_close_dev(dd); + + if (ver.hci_ver > 1) { + if (di.features[5] & LMP_SNIFF_SUBR) + events[5] |= 0x20; + + if (di.features[5] & LMP_PAUSE_ENC) + events[5] |= 0x80; + + if (di.features[6] & LMP_EXT_INQ) + events[5] |= 0x40; + + if (di.features[6] & LMP_NFLUSH_PKTS) + events[7] |= 0x01; + + if (di.features[7] & LMP_LSTO) + events[6] |= 0x80; + + if (di.features[6] & LMP_SIMPLE_PAIR) { + events[6] |= 0x01; /* IO Capability Request */ + events[6] |= 0x02; /* IO Capability Response */ + events[6] |= 0x04; /* User Confirmation Request */ + events[6] |= 0x08; /* User Passkey Request */ + events[6] |= 0x10; /* Remote OOB Data Request */ + events[6] |= 0x20; /* Simple Pairing Complete */ + events[7] |= 0x04; /* User Passkey Notification */ + events[7] |= 0x08; /* Keypress Notification */ + events[7] |= 0x10; /* Remote Host Supported + * Features Notification */ + } + + if (di.features[4] & LMP_LE) + events[7] |= 0x20; + + if (di.features[6] & LMP_LE_BREDR) + events[7] |= 0x20; + } + + printf("Setting event mask:\n"); + printf("\thcitool cmd 0x%02x 0x%04x " + "0x%02x 0x%02x 0x%02x 0x%02x " + "0x%02x 0x%02x 0x%02x 0x%02x\n", + OGF_HOST_CTL, OCF_SET_EVENT_MASK, + events[0], events[1], events[2], events[3], + events[4], events[5], events[6], events[7]); + + return 0; +} diff --git a/tools/hcisecfilter.c b/tools/hcisecfilter.c new file mode 100644 index 0000000..9ad4ce0 --- /dev/null +++ b/tools/hcisecfilter.c @@ -0,0 +1,155 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2002-2003 Maxim Krasnyansky + * Copyright (C) 2002-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include +#include +#include + +int main(void) +{ + uint32_t type_mask; + uint32_t event_mask[2]; + uint32_t ocf_mask[4]; + + /* Packet types */ + memset(&type_mask, 0, sizeof(type_mask)); + hci_set_bit(HCI_EVENT_PKT, &type_mask); + + printf("Type mask: { 0x%02x }\n", type_mask); + + /* Events */ + memset(event_mask, 0, sizeof(event_mask)); + hci_set_bit(EVT_INQUIRY_COMPLETE, event_mask); + hci_set_bit(EVT_INQUIRY_RESULT, event_mask); + hci_set_bit(EVT_CONN_COMPLETE, event_mask); + hci_set_bit(EVT_CONN_REQUEST, event_mask); + hci_set_bit(EVT_DISCONN_COMPLETE, event_mask); + hci_set_bit(EVT_AUTH_COMPLETE, event_mask); + hci_set_bit(EVT_REMOTE_NAME_REQ_COMPLETE, event_mask); + hci_set_bit(EVT_ENCRYPT_CHANGE, event_mask); + hci_set_bit(EVT_READ_REMOTE_FEATURES_COMPLETE, event_mask); + hci_set_bit(EVT_READ_REMOTE_VERSION_COMPLETE, event_mask); + hci_set_bit(EVT_CMD_COMPLETE, event_mask); + hci_set_bit(EVT_CMD_STATUS, event_mask); + hci_set_bit(EVT_READ_CLOCK_OFFSET_COMPLETE, event_mask); + hci_set_bit(EVT_INQUIRY_RESULT_WITH_RSSI, event_mask); + hci_set_bit(EVT_READ_REMOTE_EXT_FEATURES_COMPLETE, event_mask); + hci_set_bit(EVT_SYNC_CONN_COMPLETE, event_mask); + hci_set_bit(EVT_SYNC_CONN_CHANGED, event_mask); + hci_set_bit(EVT_EXTENDED_INQUIRY_RESULT, event_mask); + + printf("Event mask: { 0x%08x, 0x%08x }\n", + event_mask[0], event_mask[1]); + + /* OGF_LINK_CTL */ + memset(ocf_mask, 0, sizeof(ocf_mask)); + hci_set_bit(OCF_INQUIRY, ocf_mask); + hci_set_bit(OCF_INQUIRY_CANCEL, ocf_mask); + hci_set_bit(OCF_REMOTE_NAME_REQ, ocf_mask); + hci_set_bit(OCF_REMOTE_NAME_REQ_CANCEL, ocf_mask); + hci_set_bit(OCF_READ_REMOTE_FEATURES, ocf_mask); + hci_set_bit(OCF_READ_REMOTE_EXT_FEATURES, ocf_mask); + hci_set_bit(OCF_READ_REMOTE_VERSION, ocf_mask); + hci_set_bit(OCF_READ_CLOCK_OFFSET, ocf_mask); + hci_set_bit(OCF_READ_LMP_HANDLE, ocf_mask); + + printf("OGF_LINK_CTL: { 0x%08x, 0x%08x, 0x%08x, 0x%02x }\n", + ocf_mask[0], ocf_mask[1], ocf_mask[2], ocf_mask[3]); + + /* OGF_LINK_POLICY */ + memset(ocf_mask, 0, sizeof(ocf_mask)); + hci_set_bit(OCF_ROLE_DISCOVERY, ocf_mask); + hci_set_bit(OCF_READ_LINK_POLICY, ocf_mask); + hci_set_bit(OCF_READ_DEFAULT_LINK_POLICY, ocf_mask); + + printf("OGF_LINK_POLICY: { 0x%08x, 0x%08x, 0x%08x, 0x%02x }\n", + ocf_mask[0], ocf_mask[1], ocf_mask[2], ocf_mask[3]); + + /* OGF_HOST_CTL */ + memset(ocf_mask, 0, sizeof(ocf_mask)); + hci_set_bit(OCF_READ_PIN_TYPE, ocf_mask); + hci_set_bit(OCF_READ_LOCAL_NAME, ocf_mask); + hci_set_bit(OCF_READ_CONN_ACCEPT_TIMEOUT, ocf_mask); + hci_set_bit(OCF_READ_PAGE_TIMEOUT, ocf_mask); + hci_set_bit(OCF_READ_SCAN_ENABLE, ocf_mask); + hci_set_bit(OCF_READ_PAGE_ACTIVITY, ocf_mask); + hci_set_bit(OCF_READ_INQ_ACTIVITY, ocf_mask); + hci_set_bit(OCF_READ_AUTH_ENABLE, ocf_mask); + hci_set_bit(OCF_READ_ENCRYPT_MODE, ocf_mask); + hci_set_bit(OCF_READ_CLASS_OF_DEV, ocf_mask); + hci_set_bit(OCF_READ_VOICE_SETTING, ocf_mask); + hci_set_bit(OCF_READ_AUTOMATIC_FLUSH_TIMEOUT, ocf_mask); + hci_set_bit(OCF_READ_NUM_BROADCAST_RETRANS, ocf_mask); + hci_set_bit(OCF_READ_HOLD_MODE_ACTIVITY, ocf_mask); + hci_set_bit(OCF_READ_TRANSMIT_POWER_LEVEL, ocf_mask); + hci_set_bit(OCF_READ_LINK_SUPERVISION_TIMEOUT, ocf_mask); + hci_set_bit(OCF_READ_NUM_SUPPORTED_IAC, ocf_mask); + hci_set_bit(OCF_READ_CURRENT_IAC_LAP, ocf_mask); + hci_set_bit(OCF_READ_PAGE_SCAN_PERIOD_MODE, ocf_mask); + hci_set_bit(OCF_READ_PAGE_SCAN_MODE, ocf_mask); + hci_set_bit(OCF_READ_INQUIRY_SCAN_TYPE, ocf_mask); + hci_set_bit(OCF_READ_INQUIRY_MODE, ocf_mask); + hci_set_bit(OCF_READ_PAGE_SCAN_TYPE, ocf_mask); + hci_set_bit(OCF_READ_AFH_MODE, ocf_mask); + hci_set_bit(OCF_READ_EXT_INQUIRY_RESPONSE, ocf_mask); + hci_set_bit(OCF_READ_SIMPLE_PAIRING_MODE, ocf_mask); + hci_set_bit(OCF_READ_INQ_RESPONSE_TX_POWER_LEVEL, ocf_mask); + hci_set_bit(OCF_READ_DEFAULT_ERROR_DATA_REPORTING, ocf_mask); + + printf("OGF_HOST_CTL: { 0x%08x, 0x%08x, 0x%08x, 0x%02x }\n", + ocf_mask[0], ocf_mask[1], ocf_mask[2], ocf_mask[3]); + + /* OGF_INFO_PARAM */ + memset(ocf_mask, 0, sizeof(ocf_mask)); + hci_set_bit(OCF_READ_LOCAL_VERSION, ocf_mask); + hci_set_bit(OCF_READ_LOCAL_COMMANDS, ocf_mask); + hci_set_bit(OCF_READ_LOCAL_FEATURES, ocf_mask); + hci_set_bit(OCF_READ_LOCAL_EXT_FEATURES, ocf_mask); + hci_set_bit(OCF_READ_BUFFER_SIZE, ocf_mask); + hci_set_bit(OCF_READ_COUNTRY_CODE, ocf_mask); + hci_set_bit(OCF_READ_BD_ADDR, ocf_mask); + + printf("OGF_INFO_PARAM: { 0x%08x, 0x%08x, 0x%08x, 0x%02x }\n", + ocf_mask[0], ocf_mask[1], ocf_mask[2], ocf_mask[3]); + + /* OGF_STATUS_PARAM */ + memset(ocf_mask, 0, sizeof(ocf_mask)); + hci_set_bit(OCF_READ_FAILED_CONTACT_COUNTER, ocf_mask); + hci_set_bit(OCF_READ_LINK_QUALITY, ocf_mask); + hci_set_bit(OCF_READ_RSSI, ocf_mask); + hci_set_bit(OCF_READ_AFH_MAP, ocf_mask); + hci_set_bit(OCF_READ_CLOCK, ocf_mask); + + printf("OGF_STATUS_PARAM: { 0x%08x, 0x%08x, 0x%08x, 0x%02x }\n", + ocf_mask[0], ocf_mask[1], ocf_mask[2], ocf_mask[3]); + + return 0; +} diff --git a/tools/hcitool.1 b/tools/hcitool.1 new file mode 100644 index 0000000..85498dc --- /dev/null +++ b/tools/hcitool.1 @@ -0,0 +1,209 @@ +.TH HCITOOL 1 "Nov 12 2002" BlueZ "Linux System Administration" +.SH NAME +hcitool \- configure Bluetooth connections +.SH SYNOPSIS +.B hcitool [-h] +.br +.B hcitool [-i ] [command [command parameters]] + +.SH DESCRIPTION +.LP +.B +hcitool +is used to configure Bluetooth connections and send some special command to +Bluetooth devices. If no +.B +command +is given, or if the option +.B +-h +is used, +.B +hcitool +prints some usage information and exits. +.SH OPTIONS +.TP +.BI -h +Gives a list of possible commands +.TP +.BI -i " " +The command is applied to device +.I +hciX +, which must be the name of an installed Bluetooth device. If not specified, +the command will be sent to the first available Bluetooth device. +.SH COMMANDS +.TP +.BI dev +Display local devices +.TP +.BI inq +Inquire remote devices. For each discovered device, Bluetooth device address, +clock offset and class are printed. +.TP +.BI scan +Inquire remote devices. For each discovered device, device name are printed. +.TP +.BI name " " +Print device name of remote device with Bluetooth address +.IR bdaddr . +.TP +.BI info " " +Print device name, version and supported features of remote device with +Bluetooth address +.IR bdaddr . +.TP +.BI spinq +Start periodic inquiry process. No inquiry results are printed. +.TP +.BI epinq +Exit periodic inquiry process. +.TP +.BI cmd " [parameters]" +Submit an arbitrary HCI command to local device. +.IR ogf , +.IR ocf +and +.IR parameters +are hexadecimal bytes. +.TP +.BI con +Display active baseband connections +.TP +.BI cc " [--role=m|s] [--pkt-type=] " +Create baseband connection to remote device with Bluetooth address +.IR bdaddr . +Option +.I +--pkt-type +specifies a list of allowed packet types. +.I + +is a comma-separated list of packet types, where the possible packet types are +.BR DM1 , +.BR DM3 , +.BR DM5 , +.BR DH1 , +.BR DH3 , +.BR DH5 , +.BR HV1 , +.BR HV2 , +.BR HV3 . +Default is to allow all packet types. Option +.I +--role +can have value +.I +m +(do not allow role switch, stay master) or +.I +s +(allow role switch, become slave if the peer asks to become master). Default is +.IR m . +.TP +.BI dc " [reason]" +Delete baseband connection from remote device with Bluetooth address +.IR bdaddr . +The reason can be one of the Bluetooth HCI error codes. Default is +.IR 19 +for user ended connections. The value must be given in decimal. +.TP +.BI sr " " +Switch role for the baseband connection from the remote device to +.BR master +or +.BR slave . +.TP +.BI cpt " " +Change packet types for baseband connection to device with Bluetooth address +.IR bdaddr . +.I +packet types +is a comma-separated list of packet types, where the possible packet types are +.BR DM1 , +.BR DM3 , +.BR DM5 , +.BR DH1 , +.BR DH3 , +.BR DH5 , +.BR HV1 , +.BR HV2 , +.BR HV3 . +.TP +.BI rssi " " +Display received signal strength information for the connection to the device +with Bluetooth address +.IR bdaddr . +.TP +.BI lq " " +Display link quality for the connection to the device with Bluetooth address +.IR bdaddr . +.TP +.BI tpl " [type]" +Display transmit power level for the connection to the device with Bluetooth address +.IR bdaddr . +The type can be +.BR 0 +for the current transmit power level (which is default) or +.BR 1 +for the maximum transmit power level. +.TP +.BI afh " " +Display AFH channel map for the connection to the device with Bluetooth address +.IR bdaddr . +.TP +.BI lp " [value]" +With no +.IR value , +displays link policy settings for the connection to the device with Bluetooth address +.IR bdaddr . +If +.IR value +is given, sets the link policy settings for that connection to +.IR value . +Possible values are RSWITCH, HOLD, SNIFF and PARK. +.TP +.BI lst " [value]" +With no +.IR value , +displays link supervision timeout for the connection to the device with Bluetooth address +.IR bdaddr . +If +.I +value +is given, sets the link supervision timeout for that connection to +.I +value +slots, or to infinite if +.I +value +is 0. +.TP +.BI auth " " +Request authentication for the device with Bluetooth address +.IR bdaddr . +.TP +.BI enc " [encrypt enable]" +Enable or disable the encryption for the device with Bluetooth address +.IR bdaddr . +.TP +.BI key " " +Change the connection link key for the device with Bluetooth address +.IR bdaddr . +.TP +.BI clkoff " " +Read the clock offset for the device with Bluetooth address +.IR bdaddr . +.TP +.BI clock " [bdaddr] [which clock]" +Read the clock for the device with Bluetooth address +.IR bdaddr . +The clock can be +.BR 0 +for the local clock or +.BR 1 +for the piconet clock (which is default). +.SH AUTHORS +Written by Maxim Krasnyansky and Marcel Holtmann +.PP +man page by Fabrizio Gennari diff --git a/tools/hcitool.c b/tools/hcitool.c new file mode 100644 index 0000000..a117449 --- /dev/null +++ b/tools/hcitool.c @@ -0,0 +1,3007 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2000-2001 Qualcomm Incorporated + * Copyright (C) 2002-2003 Maxim Krasnyansky + * Copyright (C) 2002-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "textfile.h" +#include "oui.h" + +/* Unofficial value, might still change */ +#define LE_LINK 0x03 + +#define FLAGS_AD_TYPE 0x01 +#define FLAGS_LIMITED_MODE_BIT 0x01 +#define FLAGS_GENERAL_MODE_BIT 0x02 + +#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 void helper_arg(int min_num_arg, int max_num_arg, int *argc, + char ***argv, const char *usage) +{ + *argc -= optind; + /* too many arguments, but when "max_num_arg < min_num_arg" then no + limiting (prefer "max_num_arg=-1" to gen infinity) + */ + if ( (*argc > max_num_arg) && (max_num_arg >= min_num_arg ) ) { + fprintf(stderr, "%s: too many arguments (maximal: %i)\n", + *argv[0], max_num_arg); + printf("%s", usage); + exit(1); + } + + /* print usage */ + if (*argc < min_num_arg) { + fprintf(stderr, "%s: too few arguments (minimal: %i)\n", + *argv[0], min_num_arg); + printf("%s", usage); + exit(0); + } + + *argv += optind; +} + +static char *type2str(uint8_t type) +{ + switch (type) { + case SCO_LINK: + return "SCO"; + case ACL_LINK: + return "ACL"; + case ESCO_LINK: + return "eSCO"; + case LE_LINK: + return "LE"; + default: + return "Unknown"; + } +} + +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]; + char *str; + ba2str(&ci->bdaddr, addr); + str = hci_lmtostr(ci->link_mode); + printf("\t%s %s %s handle %d state %d lm %s\n", + ci->out ? "<" : ">", type2str(ci->type), + addr, ci->handle, ci->state, str); + bt_free(str); + } + + free(cl); + 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)) { + free(cl); + return 1; + } + + free(cl); + 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"); +} + +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 */ { + static char cls_str[48]; cls_str[0] = 0; + + switch (minor & 48) { + case 16: + strncpy(cls_str, "Keyboard", sizeof(cls_str)); + break; + case 32: + strncpy(cls_str, "Pointing device", sizeof(cls_str)); + break; + case 48: + strncpy(cls_str, "Combo keyboard/pointing device", sizeof(cls_str)); + break; + } + if ((minor & 15) && (strlen(cls_str) > 0)) + strcat(cls_str, "/"); + + switch (minor & 15) { + case 0: + break; + case 1: + strncat(cls_str, "Joystick", sizeof(cls_str) - strlen(cls_str)); + break; + case 2: + strncat(cls_str, "Gamepad", sizeof(cls_str) - strlen(cls_str)); + break; + case 3: + strncat(cls_str, "Remote control", sizeof(cls_str) - strlen(cls_str)); + break; + case 4: + strncat(cls_str, "Sensing device", sizeof(cls_str) - strlen(cls_str)); + break; + case 5: + strncat(cls_str, "Digitizer tablet", sizeof(cls_str) - strlen(cls_str)); + break; + case 6: + strncat(cls_str, "Card reader", sizeof(cls_str) - strlen(cls_str)); + break; + default: + strncat(cls_str, "(reserved)", sizeof(cls_str) - strlen(cls_str)); + break; + } + if (strlen(cls_str) > 0) + return cls_str; + } + 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 7: /* wearable */ + switch (minor) { + case 1: + return "Wrist Watch"; + case 2: + return "Pager"; + case 3: + return "Jacket"; + case 4: + return "Helmet"; + case 5: + return "Glasses"; + } + break; + case 8: /* toy */ + switch (minor) { + case 1: + return "Robot"; + case 2: + return "Vehicle"; + case 3: + return "Doll / Action Figure"; + case 4: + return "Controller"; + case 5: + return "Game"; + } + break; + case 63: /* uncategorised */ + return ""; + } + return "Unknown (reserved) minor device class"; +} + +static char *major_classes[] = { + "Miscellaneous", "Computer", "Phone", "LAN Access", + "Audio/Video", "Peripheral", "Imaging", "Uncategorized" +}; + +static char *get_device_name(const bdaddr_t *local, const bdaddr_t *peer) +{ + char filename[PATH_MAX + 1], addr[18]; + + ba2str(local, addr); + create_name(filename, PATH_MAX, STORAGEDIR, addr, "names"); + + ba2str(peer, addr); + return textfile_get(filename, addr); +} + +/* Display local devices */ + +static struct option dev_options[] = { + { "help", 0, 0, 'h' }, + {0, 0, 0, 0 } +}; + +static const 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("%s", dev_help); + return; + } + } + helper_arg(0, 0, &argc, &argv, dev_help); + + 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' }, + { "iac", 1, 0, 'i' }, + { "flush", 0, 0, 'f' }, + { 0, 0, 0, 0 } +}; + +static const 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 [--iac=lap] specify the inquiry access code\n" + "\t [--flush] flush the inquiry cache\n"; + +static void cmd_inq(int dev_id, int argc, char **argv) +{ + inquiry_info *info = NULL; + uint8_t lap[3] = { 0x33, 0x8b, 0x9e }; + int num_rsp, length, flags; + char addr[18]; + int i, l, opt; + + length = 8; /* ~10 seconds */ + num_rsp = 0; + 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 'i': + l = strtoul(optarg, 0, 16); + if (!strcasecmp(optarg, "giac")) { + l = 0x9e8b33; + } else if (!strcasecmp(optarg, "liac")) { + l = 0x9e8b00; + } 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; + break; + + case 'f': + flags |= IREQ_CACHE_FLUSH; + break; + + default: + printf("%s", inq_help); + return; + } + } + helper_arg(0, 0, &argc, &argv, inq_help); + + printf("Inquiring ...\n"); + + num_rsp = hci_inquiry(dev_id, length, num_rsp, lap, &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, btohs((info+i)->clock_offset), + (info+i)->dev_class[2], + (info+i)->dev_class[1], + (info+i)->dev_class[0]); + } + + bt_free(info); +} + +/* Device scanning */ + +static struct option scan_options[] = { + { "help", 0, 0, 'h' }, + { "length", 1, 0, 'l' }, + { "numrsp", 1, 0, 'n' }, + { "iac", 1, 0, 'i' }, + { "flush", 0, 0, 'f' }, + { "refresh", 0, 0, 'r' }, + { "class", 0, 0, 'C' }, + { "info", 0, 0, 'I' }, + { "oui", 0, 0, 'O' }, + { "all", 0, 0, 'A' }, + { "ext", 0, 0, 'A' }, + { 0, 0, 0, 0 } +}; + +static const char *scan_help = + "Usage:\n" + "\tscan [--length=N] [--numrsp=N] [--iac=lap] [--flush] [--class] [--info] [--oui] [--refresh]\n"; + +static void cmd_scan(int dev_id, int argc, char **argv) +{ + inquiry_info *info = NULL; + uint8_t lap[3] = { 0x33, 0x8b, 0x9e }; + int num_rsp, length, flags; + uint8_t cls[3], features[8]; + char addr[18], name[249], oui[9], *comp, *tmp; + struct hci_version version; + struct hci_dev_info di; + struct hci_conn_info_req *cr; + int refresh = 0, extcls = 0, extinf = 0, extoui = 0; + int i, n, l, opt, dd, cc, nc; + + length = 8; /* ~10 seconds */ + num_rsp = 0; + 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 'i': + l = strtoul(optarg, 0, 16); + if (!strcasecmp(optarg, "giac")) { + l = 0x9e8b33; + } else if (!strcasecmp(optarg, "liac")) { + l = 0x9e8b00; + } else 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; + break; + + case 'f': + flags |= IREQ_CACHE_FLUSH; + break; + + case 'r': + refresh = 1; + break; + + case 'C': + extcls = 1; + break; + + case 'I': + extinf = 1; + break; + + case 'O': + extoui = 1; + break; + + case 'A': + extcls = 1; + extinf = 1; + extoui = 1; + break; + + default: + printf("%s", scan_help); + return; + } + } + helper_arg(0, 0, &argc, &argv, scan_help); + + if (dev_id < 0) { + dev_id = hci_get_route(NULL); + if (dev_id < 0) { + perror("Device is not available"); + exit(1); + } + } + + if (hci_devinfo(dev_id, &di) < 0) { + perror("Can't get device info"); + exit(1); + } + + printf("Scanning ...\n"); + num_rsp = hci_inquiry(dev_id, length, num_rsp, lap, &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); + } + + if (extcls || extinf || extoui) + printf("\n"); + + for (i = 0; i < num_rsp; i++) { + uint16_t handle = 0; + + if (!refresh) { + memset(name, 0, sizeof(name)); + tmp = get_device_name(&di.bdaddr, &(info+i)->bdaddr); + if (tmp) { + strncpy(name, tmp, 249); + free(tmp); + nc = 1; + } else + nc = 0; + } else + nc = 0; + + if (!extcls && !extinf && !extoui) { + ba2str(&(info+i)->bdaddr, addr); + + if (nc) { + printf("\t%s\t%s\n", addr, name); + continue; + } + + if (hci_read_remote_name_with_clock_offset(dd, + &(info+i)->bdaddr, + (info+i)->pscan_rep_mode, + (info+i)->clock_offset | 0x8000, + sizeof(name), name, 100000) < 0) + strcpy(name, "n/a"); + + for (n = 0; n < 248 && name[n]; n++) { + if ((unsigned char) name[i] < 32 || name[i] == 127) + name[i] = '.'; + } + + name[248] = '\0'; + + printf("\t%s\t%s\n", addr, name); + continue; + } + + ba2str(&(info+i)->bdaddr, addr); + printf("BD Address:\t%s [mode %d, clkoffset 0x%4.4x]\n", addr, + (info+i)->pscan_rep_mode, btohs((info+i)->clock_offset)); + + if (extoui) { + ba2oui(&(info+i)->bdaddr, oui); + comp = ouitocomp(oui); + if (comp) { + printf("OUI company:\t%s (%s)\n", comp, oui); + free(comp); + } + } + + cc = 0; + + if (extinf) { + cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); + if (cr) { + bacpy(&cr->bdaddr, &(info+i)->bdaddr); + cr->type = ACL_LINK; + if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) { + handle = 0; + cc = 1; + } else { + handle = htobs(cr->conn_info->handle); + cc = 0; + } + free(cr); + } + + if (cc) { + if (hci_create_connection(dd, &(info+i)->bdaddr, + htobs(di.pkt_type & ACL_PTYPE_MASK), + (info+i)->clock_offset | 0x8000, + 0x01, &handle, 25000) < 0) { + handle = 0; + cc = 0; + } + } + } + + if (handle > 0 || !nc) { + if (hci_read_remote_name_with_clock_offset(dd, + &(info+i)->bdaddr, + (info+i)->pscan_rep_mode, + (info+i)->clock_offset | 0x8000, + sizeof(name), name, 100000) < 0) { + if (!nc) + strcpy(name, "n/a"); + } else { + for (n = 0; n < 248 && name[n]; n++) { + if ((unsigned char) name[i] < 32 || name[i] == 127) + name[i] = '.'; + } + + name[248] = '\0'; + nc = 0; + } + } + + if (strlen(name) > 0) + printf("Device name:\t%s%s\n", name, nc ? " [cached]" : ""); + + if (extcls) { + memcpy(cls, (info+i)->dev_class, 3); + printf("Device class:\t"); + if ((cls[1] & 0x1f) > sizeof(major_classes) / sizeof(char *)) + printf("Invalid"); + else + printf("%s, %s", major_classes[cls[1] & 0x1f], + get_minor_device_name(cls[1] & 0x1f, cls[0] >> 2)); + printf(" (0x%2.2x%2.2x%2.2x)\n", cls[2], cls[1], cls[0]); + } + + if (extinf && handle > 0) { + if (hci_read_remote_version(dd, handle, &version, 20000) == 0) { + char *ver = lmp_vertostr(version.lmp_ver); + printf("Manufacturer:\t%s (%d)\n", + bt_compidtostr(version.manufacturer), + version.manufacturer); + printf("LMP version:\t%s (0x%x) [subver 0x%x]\n", + ver ? ver : "n/a", + version.lmp_ver, version.lmp_subver); + if (ver) + bt_free(ver); + } + + if (hci_read_remote_features(dd, handle, features, 20000) == 0) { + char *tmp = lmp_featurestostr(features, "\t\t", 63); + printf("LMP features:\t0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x" + " 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", + features[0], features[1], + features[2], features[3], + features[4], features[5], + features[6], features[7]); + printf("%s\n", tmp); + bt_free(tmp); + } + + if (cc) { + usleep(10000); + hci_disconnect(dd, handle, HCI_OE_USER_ENDED_CONNECTION, 10000); + } + } + + printf("\n"); + } + + bt_free(info); + + hci_close_dev(dd); +} + +/* Remote name */ + +static struct option name_options[] = { + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } +}; + +static const char *name_help = + "Usage:\n" + "\tname \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("%s", name_help); + return; + } + } + helper_arg(1, 1, &argc, &argv, name_help); + + 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); + + hci_close_dev(dd); +} + +/* Info about remote device */ + +static struct option info_options[] = { + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } +}; + +static const char *info_help = + "Usage:\n" + "\tinfo \n"; + +static void cmd_info(int dev_id, int argc, char **argv) +{ + bdaddr_t bdaddr; + uint16_t handle; + uint8_t features[8], max_page = 0; + char name[249], oui[9], *comp, *tmp; + struct hci_version version; + struct hci_dev_info di; + struct hci_conn_info_req *cr; + int i, opt, dd, cc = 0; + + for_each_opt(opt, info_options, NULL) { + switch (opt) { + default: + printf("%s", info_help); + return; + } + } + helper_arg(1, 1, &argc, &argv, info_help); + + 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); + } + + if (hci_devinfo(dev_id, &di) < 0) { + perror("Can't get device info"); + 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(di.pkt_type & ACL_PTYPE_MASK), + 0, 0x01, &handle, 25000) < 0) { + perror("Can't create connection"); + close(dd); + exit(1); + } + sleep(1); + cc = 1; + } else + handle = htobs(cr->conn_info->handle); + + printf("\tBD Address: %s\n", argv[0]); + + ba2oui(&bdaddr, oui); + comp = ouitocomp(oui); + if (comp) { + printf("\tOUI Company: %s (%s)\n", comp, oui); + free(comp); + } + + 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) { + char *ver = lmp_vertostr(version.lmp_ver); + printf("\tLMP Version: %s (0x%x) LMP Subversion: 0x%x\n" + "\tManufacturer: %s (%d)\n", + ver ? ver : "n/a", + version.lmp_ver, + version.lmp_subver, + bt_compidtostr(version.manufacturer), + version.manufacturer); + if (ver) + bt_free(ver); + } + + memset(features, 0, sizeof(features)); + hci_read_remote_features(dd, handle, features, 20000); + + if ((di.features[7] & LMP_EXT_FEAT) && (features[7] & LMP_EXT_FEAT)) + hci_read_remote_ext_features(dd, handle, 0, &max_page, + features, 20000); + + printf("\tFeatures%s: 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", + (max_page > 0) ? " page 0" : "", + features[0], features[1], features[2], features[3], + features[4], features[5], features[6], features[7]); + + tmp = lmp_featurestostr(features, "\t\t", 63); + printf("%s\n", tmp); + bt_free(tmp); + + for (i = 1; i <= max_page; i++) { + if (hci_read_remote_ext_features(dd, handle, i, NULL, + features, 20000) < 0) + continue; + + printf("\tFeatures page %d: 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", i, + features[0], features[1], features[2], features[3], + features[4], features[5], features[6], features[7]); + } + + if (cc) { + usleep(10000); + hci_disconnect(dd, handle, HCI_OE_USER_ENDED_CONNECTION, 10000); + } + + hci_close_dev(dd); +} + +/* Start periodic inquiry */ + +static struct option spinq_options[] = { + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } +}; + +static const char *spinq_help = + "Usage:\n" + "\tspinq\n"; + +static void cmd_spinq(int dev_id, int argc, char **argv) +{ + uint8_t lap[3] = { 0x33, 0x8b, 0x9e }; + struct hci_request rq; + periodic_inquiry_cp cp; + int opt, dd; + + for_each_opt(opt, spinq_options, NULL) { + switch (opt) { + default: + printf("%s", spinq_help); + return; + } + } + helper_arg(0, 0, &argc, &argv, spinq_help); + + if (dev_id < 0) + dev_id = hci_get_route(NULL); + + dd = hci_open_dev(dev_id); + if (dd < 0) { + perror("Device open failed"); + exit(EXIT_FAILURE); + } + + memset(&cp, 0, sizeof(cp)); + memcpy(cp.lap, lap, 3); + cp.max_period = htobs(16); + cp.min_period = htobs(10); + cp.length = 8; + cp.num_rsp = 0; + + memset(&rq, 0, sizeof(rq)); + rq.ogf = OGF_LINK_CTL; + rq.ocf = OCF_PERIODIC_INQUIRY; + rq.cparam = &cp; + rq.clen = PERIODIC_INQUIRY_CP_SIZE; + + if (hci_send_req(dd, &rq, 100) < 0) { + perror("Periodic inquiry failed"); + exit(EXIT_FAILURE); + } + + hci_close_dev(dd); +} + +/* Exit periodic inquiry */ + +static struct option epinq_options[] = { + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } +}; + +static const char *epinq_help = + "Usage:\n" + "\tepinq\n"; + +static void cmd_epinq(int dev_id, int argc, char **argv) +{ + int opt, dd; + + for_each_opt(opt, epinq_options, NULL) { + switch (opt) { + default: + printf("%s", epinq_help); + return; + } + } + helper_arg(0, 0, &argc, &argv, epinq_help); + + if (dev_id < 0) + dev_id = hci_get_route(NULL); + + dd = hci_open_dev(dev_id); + if (dd < 0) { + perror("Device open failed"); + exit(EXIT_FAILURE); + } + + if (hci_send_cmd(dd, OGF_LINK_CTL, + OCF_EXIT_PERIODIC_INQUIRY, 0, NULL) < 0) { + perror("Exit periodic inquiry failed"); + exit(EXIT_FAILURE); + } + + hci_close_dev(dd); +} + +/* Send arbitrary HCI commands */ + +static struct option cmd_options[] = { + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } +}; + +static const char *cmd_help = + "Usage:\n" + "\tcmd [parameters]\n" + "Example:\n" + "\tcmd 0x03 0x0013 0x41 0x42 0x43 0x44\n"; + +static void cmd_cmd(int dev_id, int argc, char **argv) +{ + unsigned 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("%s", cmd_help); + return; + } + } + helper_arg(2, -1, &argc, &argv, cmd_help); + + 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("%s", cmd_help); + return; + } + + for (i = 2, len = 0; i < argc && len < (int) 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); +} + +/* Display active connections */ + +static struct option con_options[] = { + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } +}; + +static const 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("%s", con_help); + return; + } + } + helper_arg(0, 0, &argc, &argv, con_help); + + 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 const char *cc_help = + "Usage:\n" + "\tcc [--role=m|s] [--ptype=pkt_types] \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; + uint16_t handle; + uint8_t role; + unsigned int ptype; + int dd, opt; + + role = 0x01; + 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("%s", cc_help); + return; + } + } + helper_arg(1, 1, &argc, &argv, cc_help); + + 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), + htobs(0x0000), role, &handle, 25000) < 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 const char *dc_help = + "Usage:\n" + "\tdc [reason]\n"; + +static void cmd_dc(int dev_id, int argc, char **argv) +{ + struct hci_conn_info_req *cr; + bdaddr_t bdaddr; + uint8_t reason; + int opt, dd; + + for_each_opt(opt, dc_options, NULL) { + switch (opt) { + default: + printf("%s", dc_help); + return; + } + } + helper_arg(1, 2, &argc, &argv, dc_help); + + str2ba(argv[0], &bdaddr); + reason = (argc > 1) ? atoi(argv[1]) : HCI_OE_USER_ENDED_CONNECTION; + + 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) { + perror("Can't allocate memory"); + exit(1); + } + + 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, htobs(cr->conn_info->handle), + reason, 10000) < 0) + perror("Disconnect failed"); + + free(cr); + + hci_close_dev(dd); +} + +/* Role switch */ + +static struct option sr_options[] = { + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } +}; + +static const char *sr_help = + "Usage:\n" + "\tsr \n"; + +static void cmd_sr(int dev_id, int argc, char **argv) +{ + bdaddr_t bdaddr; + uint8_t role; + int opt, dd; + + for_each_opt(opt, sr_options, NULL) { + switch (opt) { + default: + printf("%s", sr_help); + return; + } + } + helper_arg(2, 2, &argc, &argv, sr_help); + + str2ba(argv[0], &bdaddr); + switch (argv[1][0]) { + case 'm': + role = 0; + break; + case 's': + role = 1; + break; + default: + role = atoi(argv[1]); + break; + } + + 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); + } + + if (hci_switch_role(dd, &bdaddr, role, 10000) < 0) { + perror("Switch role request failed"); + exit(1); + } + + hci_close_dev(dd); +} + +/* Read RSSI */ + +static struct option rssi_options[] = { + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } +}; + +static const char *rssi_help = + "Usage:\n" + "\trssi \n"; + +static void cmd_rssi(int dev_id, int argc, char **argv) +{ + struct hci_conn_info_req *cr; + bdaddr_t bdaddr; + int8_t rssi; + int opt, dd; + + for_each_opt(opt, rssi_options, NULL) { + switch (opt) { + default: + printf("%s", rssi_help); + return; + } + } + helper_arg(1, 1, &argc, &argv, rssi_help); + + 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) { + perror("Can't allocate memory"); + exit(1); + } + + 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_read_rssi(dd, htobs(cr->conn_info->handle), &rssi, 1000) < 0) { + perror("Read RSSI failed"); + exit(1); + } + + printf("RSSI return value: %d\n", rssi); + + free(cr); + + hci_close_dev(dd); +} + +/* Get link quality */ + +static struct option lq_options[] = { + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } +}; + +static const char *lq_help = + "Usage:\n" + "\tlq \n"; + +static void cmd_lq(int dev_id, int argc, char **argv) +{ + struct hci_conn_info_req *cr; + bdaddr_t bdaddr; + uint8_t lq; + int opt, dd; + + for_each_opt(opt, lq_options, NULL) { + switch (opt) { + default: + printf("%s", lq_help); + return; + } + } + helper_arg(1, 1, &argc, &argv, lq_help); + + 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) { + perror("Can't allocate memory"); + exit(1); + } + + 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_read_link_quality(dd, htobs(cr->conn_info->handle), &lq, 1000) < 0) { + perror("HCI read_link_quality request failed"); + exit(1); + } + + printf("Link quality: %d\n", lq); + + free(cr); + + hci_close_dev(dd); +} + +/* Get transmit power level */ + +static struct option tpl_options[] = { + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } +}; + +static const char *tpl_help = + "Usage:\n" + "\ttpl [type]\n"; + +static void cmd_tpl(int dev_id, int argc, char **argv) +{ + struct hci_conn_info_req *cr; + bdaddr_t bdaddr; + uint8_t type; + int8_t level; + int opt, dd; + + for_each_opt(opt, tpl_options, NULL) { + switch (opt) { + default: + printf("%s", tpl_help); + return; + } + } + helper_arg(1, 2, &argc, &argv, tpl_help); + + str2ba(argv[0], &bdaddr); + 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) { + perror("Can't allocate memory"); + exit(1); + } + + 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_read_transmit_power_level(dd, htobs(cr->conn_info->handle), type, &level, 1000) < 0) { + perror("HCI read transmit power level request failed"); + exit(1); + } + + printf("%s transmit power level: %d\n", + (type == 0) ? "Current" : "Maximum", level); + + free(cr); + + hci_close_dev(dd); +} + +/* Get AFH channel map */ + +static struct option afh_options[] = { + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } +}; + +static const char *afh_help = + "Usage:\n" + "\tafh \n"; + +static void cmd_afh(int dev_id, int argc, char **argv) +{ + struct hci_conn_info_req *cr; + bdaddr_t bdaddr; + uint16_t handle; + uint8_t mode, map[10]; + int opt, dd; + + for_each_opt(opt, afh_options, NULL) { + switch (opt) { + default: + printf("%s", afh_help); + return; + } + } + helper_arg(1, 1, &argc, &argv, afh_help); + + 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) { + perror("Can't allocate memory"); + exit(1); + } + + bacpy(&cr->bdaddr, &bdaddr); + cr->type = ACL_LINK; + if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) { + perror("Get connection info failed"); + exit(1); + } + + handle = htobs(cr->conn_info->handle); + + if (hci_read_afh_map(dd, handle, &mode, map, 1000) < 0) { + perror("HCI read AFH map request failed"); + exit(1); + } + + if (mode == 0x01) { + int i; + printf("AFH map: 0x"); + for (i = 0; i < 10; i++) + printf("%02x", map[i]); + printf("\n"); + } else + printf("AFH disabled\n"); + + free(cr); + + hci_close_dev(dd); +} + +/* Set connection packet type */ + +static struct option cpt_options[] = { + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } +}; + +static const char *cpt_help = + "Usage:\n" + "\tcpt \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; + unsigned int ptype; + int dd, opt; + + for_each_opt(opt, cpt_options, NULL) { + switch (opt) { + default: + printf("%s", cpt_help); + return; + } + } + helper_arg(2, 2, &argc, &argv, cpt_help); + + 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) { + perror("Can't allocate memory"); + exit(1); + } + + 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 = htobs(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); + } + + free(cr); + + hci_close_dev(dd); +} + +/* Get/Set link policy settings */ + +static struct option lp_options[] = { + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } +}; + +static const char *lp_help = + "Usage:\n" + "\tlp [link policy]\n"; + +static void cmd_lp(int dev_id, int argc, char **argv) +{ + struct hci_conn_info_req *cr; + bdaddr_t bdaddr; + uint16_t policy; + int opt, dd; + + for_each_opt(opt, lp_options, NULL) { + switch (opt) { + default: + printf("%s", lp_help); + return; + } + } + helper_arg(1, 2, &argc, &argv, lp_help); + + 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) { + perror("Can't allocate memory"); + exit(1); + } + + 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) { + char *str; + if (hci_read_link_policy(dd, htobs(cr->conn_info->handle), + &policy, 1000) < 0) { + perror("HCI read_link_policy_settings request failed"); + exit(1); + } + + policy = btohs(policy); + str = hci_lptostr(policy); + if (str) { + printf("Link policy settings: %s\n", str); + bt_free(str); + } else { + fprintf(stderr, "Invalig settings\n"); + exit(1); + } + } else { + unsigned int val; + if (hci_strtolp(argv[1], &val) < 0) { + fprintf(stderr, "Invalig arguments\n"); + exit(1); + } + policy = val; + + if (hci_write_link_policy(dd, htobs(cr->conn_info->handle), + htobs(policy), 1000) < 0) { + perror("HCI write_link_policy_settings request failed"); + exit(1); + } + } + + free(cr); + + hci_close_dev(dd); +} + +/* Get/Set link supervision timeout */ + +static struct option lst_options[] = { + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } +}; + +static const char *lst_help = + "Usage:\n" + "\tlst [new value in slots]\n"; + +static void cmd_lst(int dev_id, int argc, char **argv) +{ + struct hci_conn_info_req *cr; + bdaddr_t bdaddr; + uint16_t timeout; + int opt, dd; + + for_each_opt(opt, lst_options, NULL) { + switch (opt) { + default: + printf("%s", lst_help); + return; + } + } + helper_arg(1, 2, &argc, &argv, lst_help); + + 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) { + perror("Can't allocate memory"); + exit(1); + } + + 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) { + if (hci_read_link_supervision_timeout(dd, htobs(cr->conn_info->handle), + &timeout, 1000) < 0) { + perror("HCI read_link_supervision_timeout request failed"); + exit(1); + } + + timeout = btohs(timeout); + + if (timeout) + printf("Link supervision timeout: %u slots (%.2f msec)\n", + timeout, (float) timeout * 0.625); + else + printf("Link supervision timeout never expires\n"); + } else { + timeout = strtol(argv[1], NULL, 10); + + if (hci_write_link_supervision_timeout(dd, htobs(cr->conn_info->handle), + htobs(timeout), 1000) < 0) { + perror("HCI write_link_supervision_timeout request failed"); + exit(1); + } + } + + free(cr); + + hci_close_dev(dd); +} + +/* Request authentication */ + +static struct option auth_options[] = { + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } +}; + +static const char *auth_help = + "Usage:\n" + "\tauth \n"; + +static void cmd_auth(int dev_id, int argc, char **argv) +{ + struct hci_conn_info_req *cr; + bdaddr_t bdaddr; + int opt, dd; + + for_each_opt(opt, auth_options, NULL) { + switch (opt) { + default: + printf("%s", auth_help); + return; + } + } + helper_arg(1, 1, &argc, &argv, auth_help); + + 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) { + perror("Can't allocate memory"); + exit(1); + } + + 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_authenticate_link(dd, htobs(cr->conn_info->handle), 25000) < 0) { + perror("HCI authentication request failed"); + exit(1); + } + + free(cr); + + hci_close_dev(dd); +} + +/* Activate encryption */ + +static struct option enc_options[] = { + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } +}; + +static const char *enc_help = + "Usage:\n" + "\tenc [encrypt enable]\n"; + +static void cmd_enc(int dev_id, int argc, char **argv) +{ + struct hci_conn_info_req *cr; + bdaddr_t bdaddr; + uint8_t encrypt; + int opt, dd; + + for_each_opt(opt, enc_options, NULL) { + switch (opt) { + default: + printf("%s", enc_help); + return; + } + } + helper_arg(1, 2, &argc, &argv, enc_help); + + 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) { + perror("Can't allocate memory"); + exit(1); + } + + bacpy(&cr->bdaddr, &bdaddr); + cr->type = ACL_LINK; + if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) { + perror("Get connection info failed"); + exit(1); + } + + encrypt = (argc > 1) ? atoi(argv[1]) : 1; + + if (hci_encrypt_link(dd, htobs(cr->conn_info->handle), encrypt, 25000) < 0) { + perror("HCI set encryption request failed"); + exit(1); + } + + free(cr); + + hci_close_dev(dd); +} + +/* Change connection link key */ + +static struct option key_options[] = { + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } +}; + +static const char *key_help = + "Usage:\n" + "\tkey \n"; + +static void cmd_key(int dev_id, int argc, char **argv) +{ + struct hci_conn_info_req *cr; + bdaddr_t bdaddr; + int opt, dd; + + for_each_opt(opt, key_options, NULL) { + switch (opt) { + default: + printf("%s", key_help); + return; + } + } + helper_arg(1, 1, &argc, &argv, key_help); + + 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) { + perror("Can't allocate memory"); + exit(1); + } + + 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_change_link_key(dd, htobs(cr->conn_info->handle), 25000) < 0) { + perror("Changing link key failed"); + exit(1); + } + + free(cr); + + hci_close_dev(dd); +} + +/* Read clock offset */ + +static struct option clkoff_options[] = { + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } +}; + +static const char *clkoff_help = + "Usage:\n" + "\tclkoff \n"; + +static void cmd_clkoff(int dev_id, int argc, char **argv) +{ + struct hci_conn_info_req *cr; + bdaddr_t bdaddr; + uint16_t offset; + int opt, dd; + + for_each_opt(opt, clkoff_options, NULL) { + switch (opt) { + default: + printf("%s", clkoff_help); + return; + } + } + helper_arg(1, 1, &argc, &argv, clkoff_help); + + 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) { + perror("Can't allocate memory"); + exit(1); + } + + 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_read_clock_offset(dd, htobs(cr->conn_info->handle), &offset, 1000) < 0) { + perror("Reading clock offset failed"); + exit(1); + } + + printf("Clock offset: 0x%4.4x\n", btohs(offset)); + + free(cr); + + hci_close_dev(dd); +} + +/* Read clock */ + +static struct option clock_options[] = { + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } +}; + +static const char *clock_help = + "Usage:\n" + "\tclock [bdaddr] [which clock]\n"; + +static void cmd_clock(int dev_id, int argc, char **argv) +{ + struct hci_conn_info_req *cr; + bdaddr_t bdaddr; + uint8_t which; + uint32_t handle, clock; + uint16_t accuracy; + int opt, dd; + + for_each_opt(opt, clock_options, NULL) { + switch (opt) { + default: + printf("%s", clock_help); + return; + } + } + helper_arg(0, 2, &argc, &argv, clock_help); + + if (argc > 0) + str2ba(argv[0], &bdaddr); + else + bacpy(&bdaddr, BDADDR_ANY); + + if (dev_id < 0 && !bacmp(&bdaddr, BDADDR_ANY)) + dev_id = hci_get_route(NULL); + + 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); + } + + if (bacmp(&bdaddr, BDADDR_ANY)) { + cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); + if (!cr) { + perror("Can't allocate memory"); + exit(1); + } + + bacpy(&cr->bdaddr, &bdaddr); + cr->type = ACL_LINK; + if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) { + perror("Get connection info failed"); + free(cr); + exit(1); + } + + handle = htobs(cr->conn_info->handle); + which = (argc > 1) ? atoi(argv[1]) : 0x01; + + free(cr); + } else { + handle = 0x00; + which = 0x00; + } + + if (hci_read_clock(dd, handle, which, &clock, &accuracy, 1000) < 0) { + perror("Reading clock failed"); + exit(1); + } + + accuracy = btohs(accuracy); + + printf("Clock: 0x%4.4x\n", btohl(clock)); + printf("Accuracy: %.2f msec\n", (float) accuracy * 0.3125); + + hci_close_dev(dd); +} + +static int read_flags(uint8_t *flags, const uint8_t *data, size_t size) +{ + unsigned int offset; + + if (!flags || !data) + return -EINVAL; + + offset = 0; + while (offset < size) { + uint8_t len = data[offset]; + uint8_t type = data[offset + 1]; + + /* Check if it is the end of the significant part */ + if (len == 0) + break; + + if (type == FLAGS_AD_TYPE) { + *flags = data[offset + 2]; + return 0; + } + + offset += 1 + len; + } + + return -ENOENT; +} + +static int check_report_filter(uint8_t procedure, le_advertising_info *info) +{ + uint8_t flags; + + /* If no discovery procedure is set, all reports are treat as valid */ + if (procedure == 0) + return 1; + + /* Read flags AD type value from the advertising report if it exists */ + if (read_flags(&flags, info->data, info->length)) + return 0; + + switch (procedure) { + case 'l': /* Limited Discovery Procedure */ + if (flags & FLAGS_LIMITED_MODE_BIT) + return 1; + break; + case 'g': /* General Discovery Procedure */ + if (flags & (FLAGS_LIMITED_MODE_BIT | FLAGS_GENERAL_MODE_BIT)) + return 1; + break; + default: + fprintf(stderr, "Unknown discovery procedure\n"); + } + + return 0; +} + +static int print_advertising_devices(int dd, uint8_t filter_type) +{ + unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr; + struct hci_filter nf, of; + socklen_t olen; + hci_event_hdr *hdr; + int num, len; + + olen = sizeof(of); + if (getsockopt(dd, SOL_HCI, HCI_FILTER, &of, &olen) < 0) { + printf("Could not get socket options\n"); + return -1; + } + + hci_filter_clear(&nf); + hci_filter_set_ptype(HCI_EVENT_PKT, &nf); + hci_filter_set_event(EVT_LE_META_EVENT, &nf); + + if (setsockopt(dd, SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0) { + printf("Could not set socket options\n"); + return -1; + } + + /* Wait for 10 report events */ + num = 10; + while (num--) { + evt_le_meta_event *meta; + le_advertising_info *info; + char addr[18]; + + while ((len = read(dd, buf, sizeof(buf))) < 0) { + if (errno == EAGAIN || errno == EINTR) + continue; + goto done; + } + + hdr = (void *) (buf + 1); + ptr = buf + (1 + HCI_EVENT_HDR_SIZE); + len -= (1 + HCI_EVENT_HDR_SIZE); + + meta = (void *) ptr; + + if (meta->subevent != 0x02) + goto done; + + /* Ignoring multiple reports */ + info = (le_advertising_info *) (meta->data + 1); + if (check_report_filter(filter_type, info)) { + ba2str(&info->bdaddr, addr); + printf("%s\n", addr); + } + } + +done: + setsockopt(dd, SOL_HCI, HCI_FILTER, &of, sizeof(of)); + + if (len < 0) + return -1; + + return 0; +} + +static struct option lescan_options[] = { + { "help", 0, 0, 'h' }, + { "privacy", 0, 0, 'p' }, + { "passive", 0, 0, 'P' }, + { "discovery", 1, 0, 'd' }, + { 0, 0, 0, 0 } +}; + +static const char *lescan_help = + "Usage:\n" + "\tlescan [--privacy] enable privacy\n" + "\tlescan [--passive] set scan type passive (default active)\n" + "\tlescan [--discovery=g|l] enable general or limited discovery" + "procedure\n"; + +static void cmd_lescan(int dev_id, int argc, char **argv) +{ + int err, opt, dd; + uint8_t own_type = 0x00; + uint8_t scan_type = 0x01; + uint8_t filter_type = 0; + uint16_t interval = htobs(0x0010); + uint16_t window = htobs(0x0010); + + for_each_opt(opt, lescan_options, NULL) { + switch (opt) { + case 'p': + own_type = 0x01; /* Random */ + break; + case 'P': + scan_type = 0x00; /* Passive */ + break; + case 'd': + filter_type = optarg[0]; + if (filter_type != 'g' && filter_type != 'l') { + fprintf(stderr, "Unknown discovery procedure\n"); + exit(1); + } + + interval = htobs(0x0012); + window = htobs(0x0012); + break; + default: + printf("%s", lescan_help); + return; + } + } + helper_arg(0, 1, &argc, &argv, lescan_help); + + if (dev_id < 0) + dev_id = hci_get_route(NULL); + + dd = hci_open_dev(dev_id); + if (dd < 0) { + perror("Could not open device"); + exit(1); + } + + err = hci_le_set_scan_parameters(dd, scan_type, interval, window, + own_type, 0x00, 1000); + if (err < 0) { + perror("Set scan parameters failed"); + exit(1); + } + + err = hci_le_set_scan_enable(dd, 0x01, 0x00, 1000); + if (err < 0) { + perror("Enable scan failed"); + exit(1); + } + + printf("LE Scan ...\n"); + + err = print_advertising_devices(dd, filter_type); + if (err < 0) { + perror("Could not receive advertising events"); + exit(1); + } + + err = hci_le_set_scan_enable(dd, 0x00, 0x00, 1000); + if (err < 0) { + perror("Disable scan failed"); + exit(1); + } + + hci_close_dev(dd); +} + +static struct option lecc_options[] = { + { "help", 0, 0, 'h' }, + { "random", 0, 0, 'r' }, + { "whitelist", 0, 0, 'w' }, + { 0, 0, 0, 0 } +}; + +static const char *lecc_help = + "Usage:\n" + "\tlecc [--random] \n" + "\tlecc --whitelist\n"; + +static void cmd_lecc(int dev_id, int argc, char **argv) +{ + int err, opt, dd; + bdaddr_t bdaddr; + uint16_t interval, latency, max_ce_length, max_interval, min_ce_length; + uint16_t min_interval, supervision_timeout, window, handle; + uint8_t initiator_filter, own_bdaddr_type, peer_bdaddr_type; + + peer_bdaddr_type = LE_PUBLIC_ADDRESS; + initiator_filter = 0; /* Use peer address */ + + for_each_opt(opt, lecc_options, NULL) { + switch (opt) { + case 'r': + peer_bdaddr_type = LE_RANDOM_ADDRESS; + break; + case 'w': + initiator_filter = 0x01; /* Use white list */ + break; + default: + printf("%s", lecc_help); + return; + } + } + helper_arg(0, 1, &argc, &argv, lecc_help); + + if (dev_id < 0) + dev_id = hci_get_route(NULL); + + dd = hci_open_dev(dev_id); + if (dd < 0) { + perror("Could not open device"); + exit(1); + } + + memset(&bdaddr, 0, sizeof(bdaddr_t)); + if (argv[0]) + str2ba(argv[0], &bdaddr); + + interval = htobs(0x0004); + window = htobs(0x0004); + own_bdaddr_type = 0x00; + min_interval = htobs(0x000F); + max_interval = htobs(0x000F); + latency = htobs(0x0000); + supervision_timeout = htobs(0x0C80); + min_ce_length = htobs(0x0001); + max_ce_length = htobs(0x0001); + + err = hci_le_create_conn(dd, interval, window, initiator_filter, + peer_bdaddr_type, bdaddr, own_bdaddr_type, min_interval, + max_interval, latency, supervision_timeout, + min_ce_length, max_ce_length, &handle, 25000); + if (err < 0) { + perror("Could not create connection"); + exit(1); + } + + printf("Connection handle %d\n", handle); + + hci_close_dev(dd); +} + +static struct option lewladd_options[] = { + { "help", 0, 0, 'h' }, + { "random", 0, 0, 'r' }, + { 0, 0, 0, 0 } +}; + +static const char *lewladd_help = + "Usage:\n" + "\tlewladd [--random] \n"; + +static void cmd_lewladd(int dev_id, int argc, char **argv) +{ + int err, opt, dd; + bdaddr_t bdaddr; + uint8_t bdaddr_type = LE_PUBLIC_ADDRESS; + + for_each_opt(opt, lewladd_options, NULL) { + switch (opt) { + case 'r': + bdaddr_type = LE_RANDOM_ADDRESS; + break; + default: + printf("%s", lewladd_help); + return; + } + } + + helper_arg(1, 1, &argc, &argv, lewladd_help); + + if (dev_id < 0) + dev_id = hci_get_route(NULL); + + dd = hci_open_dev(dev_id); + if (dd < 0) { + perror("Could not open device"); + exit(1); + } + + str2ba(argv[0], &bdaddr); + + err = hci_le_add_white_list(dd, &bdaddr, bdaddr_type, 1000); + hci_close_dev(dd); + + if (err < 0) { + err = errno; + fprintf(stderr, "Can't add to white list: %s(%d)\n", + strerror(err), err); + exit(1); + } +} + +static struct option lewlrm_options[] = { + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } +}; + +static const char *lewlrm_help = + "Usage:\n" + "\tlewlrm \n"; + +static void cmd_lewlrm(int dev_id, int argc, char **argv) +{ + int err, opt, dd; + bdaddr_t bdaddr; + + for_each_opt(opt, lewlrm_options, NULL) { + switch (opt) { + default: + printf("%s", lewlrm_help); + return; + } + } + + helper_arg(1, 1, &argc, &argv, lewlrm_help); + + if (dev_id < 0) + dev_id = hci_get_route(NULL); + + dd = hci_open_dev(dev_id); + if (dd < 0) { + perror("Could not open device"); + exit(1); + } + + str2ba(argv[0], &bdaddr); + + err = hci_le_rm_white_list(dd, &bdaddr, LE_PUBLIC_ADDRESS, 1000); + hci_close_dev(dd); + + if (err < 0) { + err = errno; + fprintf(stderr, "Can't remove from white list: %s(%d)\n", + strerror(err), err); + exit(1); + } +} + +static struct option lewlsz_options[] = { + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } +}; + +static const char *lewlsz_help = + "Usage:\n" + "\tlewlsz\n"; + +static void cmd_lewlsz(int dev_id, int argc, char **argv) +{ + int err, dd, opt; + uint8_t size; + + for_each_opt(opt, lewlsz_options, NULL) { + switch (opt) { + default: + printf("%s", lewlsz_help); + return; + } + } + + helper_arg(0, 0, &argc, &argv, lewlsz_help); + + if (dev_id < 0) + dev_id = hci_get_route(NULL); + + dd = hci_open_dev(dev_id); + if (dd < 0) { + perror("Could not open device"); + exit(1); + } + + err = hci_le_read_white_list_size(dd, &size, 1000); + hci_close_dev(dd); + + if (err < 0) { + err = errno; + fprintf(stderr, "Can't read white list size: %s(%d)\n", + strerror(err), err); + exit(1); + } + + printf("White list size: %d\n", size); +} + +static struct option lewlclr_options[] = { + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } +}; + +static const char *lewlclr_help = + "Usage:\n" + "\tlewlclr\n"; + +static void cmd_lewlclr(int dev_id, int argc, char **argv) +{ + int err, dd, opt; + + for_each_opt(opt, lewlclr_options, NULL) { + switch (opt) { + default: + printf("%s", lewlclr_help); + return; + } + } + + helper_arg(0, 0, &argc, &argv, lewlclr_help); + + if (dev_id < 0) + dev_id = hci_get_route(NULL); + + dd = hci_open_dev(dev_id); + if (dd < 0) { + perror("Could not open device"); + exit(1); + } + + err = hci_le_clear_white_list(dd, 1000); + hci_close_dev(dd); + + if (err < 0) { + err = errno; + fprintf(stderr, "Can't clear white list: %s(%d)\n", + strerror(err), err); + exit(1); + } +} + +static struct option ledc_options[] = { + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } +}; + +static const char *ledc_help = + "Usage:\n" + "\tledc [reason]\n"; + +static void cmd_ledc(int dev_id, int argc, char **argv) +{ + int err, opt, dd; + uint16_t handle; + uint8_t reason; + + for_each_opt(opt, ledc_options, NULL) { + switch (opt) { + default: + printf("%s", ledc_help); + return; + } + } + helper_arg(1, 2, &argc, &argv, ledc_help); + + if (dev_id < 0) + dev_id = hci_get_route(NULL); + + dd = hci_open_dev(dev_id); + if (dd < 0) { + perror("Could not open device"); + exit(1); + } + + handle = atoi(argv[0]); + + reason = (argc > 1) ? atoi(argv[1]) : HCI_OE_USER_ENDED_CONNECTION; + + err = hci_disconnect(dd, handle, reason, 10000); + if (err < 0) { + perror("Could not disconnect"); + exit(1); + } + + hci_close_dev(dd); +} + +static struct option lecup_options[] = { + { "help", 0, 0, 'h' }, + { "handle", 1, 0, 'H' }, + { "min", 1, 0, 'm' }, + { "max", 1, 0, 'M' }, + { "latency", 1, 0, 'l' }, + { "timeout", 1, 0, 't' }, + { 0, 0, 0, 0 } +}; + +static const char *lecup_help = + "Usage:\n" + "\tlecup \n" + "\tOptions:\n" + "\t -H, --handle <0xXXXX> LE connection handle\n" + "\t -m, --min Range: 0x0006 to 0x0C80\n" + "\t -M, --max Range: 0x0006 to 0x0C80\n" + "\t -l, --latency Slave latency. Range: 0x0000 to 0x03E8\n" + "\t -t, --timeout