summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorAdrian Bunk <adrian.bunk@movial.com>2011-06-03 09:17:04 +0000
committerAdrian Bunk <adrian.bunk@movial.com>2011-06-03 09:17:04 +0000
commit799757ccf1d03c33c75bc597cd5ef77741dcb6a7 (patch)
treea8c3be85c730de28b012586591b76301033d3d21 /tools
Imported upstream 4.91upstream-4.91upstreampackaging
Diffstat (limited to 'tools')
-rw-r--r--tools/avctrl.839
-rw-r--r--tools/avctrl.c248
-rw-r--r--tools/avinfo.c672
-rw-r--r--tools/bccmd.8130
-rw-r--r--tools/bccmd.c1254
-rw-r--r--tools/ciptool.168
-rw-r--r--tools/ciptool.c498
-rw-r--r--tools/csr.c2853
-rw-r--r--tools/csr.h553
-rw-r--r--tools/csr_3wire.c62
-rw-r--r--tools/csr_bcsp.c255
-rw-r--r--tools/csr_h4.c165
-rw-r--r--tools/csr_hci.c160
-rw-r--r--tools/csr_usb.c180
-rw-r--r--tools/dfu.c168
-rw-r--r--tools/dfu.h107
-rw-r--r--tools/dfubabel.138
-rw-r--r--tools/dfubabel.c211
-rw-r--r--tools/dfutool.153
-rw-r--r--tools/dfutool.c791
-rw-r--r--tools/hciattach.8155
-rw-r--r--tools/hciattach.c1446
-rw-r--r--tools/hciattach.h56
-rw-r--r--tools/hciattach_ath3k.c1049
-rw-r--r--tools/hciattach_qualcomm.c275
-rw-r--r--tools/hciattach_st.c278
-rw-r--r--tools/hciattach_ti.c529
-rw-r--r--tools/hciattach_tialt.c244
-rw-r--r--tools/hciconfig.8277
-rw-r--r--tools/hciconfig.c2036
-rw-r--r--tools/hcieventmask.c130
-rw-r--r--tools/hcisecfilter.c155
-rw-r--r--tools/hcitool.1209
-rw-r--r--tools/hcitool.c3007
-rw-r--r--tools/hid2hci.851
-rw-r--r--tools/hid2hci.c375
-rw-r--r--tools/kword.c65
-rw-r--r--tools/kword.h46
-rw-r--r--tools/l2ping.876
-rw-r--r--tools/l2ping.c324
-rw-r--r--tools/lexer.c1834
-rw-r--r--tools/lexer.l120
-rw-r--r--tools/parser.c1768
-rw-r--r--tools/parser.h94
-rw-r--r--tools/parser.y171
-rw-r--r--tools/ppporc.c271
-rw-r--r--tools/rfcomm.1137
-rw-r--r--tools/rfcomm.c849
-rw-r--r--tools/rfcomm.conf17
-rw-r--r--tools/sdptool.1130
-rw-r--r--tools/sdptool.c4274
-rw-r--r--tools/ubcsp.c1180
-rw-r--r--tools/ubcsp.h208
53 files changed, 30341 insertions, 0 deletions
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
+]
+<command>
+.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 <marcel@holtmann.org>.
+.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 <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+
+#include <usb.h>
+
+#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] <command>\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 <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
+
+#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] <remote address>\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 <transport>] [-d <device>] <command> [<args>]
+.br
+.B bccmd [-h --help]
+.br
+.SH DESCRIPTION
+.B
+bccmd
+issues BlueCore commands to
+.B
+Cambridge Silicon Radio
+devices. If run without the <command> argument, a short help page will be displayed.
+.SH OPTIONS
+.TP
+.BI -t\ <transport>
+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\ <dev>
+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\ <handle>
+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\ <channel>
+Lock radio on specific channel
+.TP
+.BI hoppingon
+Revert to channel hopping
+.TP
+.BI rttxdata1\ <decimal\ freq\ MHz>\ <level>
+TXData1 radio test
+.TP
+.BI radiotest\ <decimal\ freq\ MHz>\ <level>\ <id>
+Run radio tests, tests 4, 6 and 7 are transmit tests
+.TP
+.BI memtypes
+Get memory types
+.TP
+.BI psget\ [-r]\ [-s\ <stores>]\ <key>
+Get value for PS key.
+-r sends a warm reset afterwards
+.TP
+.BI psset\ [-r]\ [-s\ <stores>]\ <key>\ <value>
+Set value for PS key.
+-r sends a warm reset afterwards
+.TP
+.BI psclr\ [-r]\ [-s\ <stores>]\ <key>
+Clear value for PS key.
+-r sends a warm reset afterwards
+.TP
+.BI pslist\ [-r]\ [-s\ <stores>]
+List all PS keys.
+-r sends a warm reset afterwards
+.TP
+.BI psread\ [-r]\ [-s\ <stores>]
+Read all PS keys.
+-r sends a warm reset afterwards
+.TP
+.BI psload\ [-r]\ [-s\ <stores>]\ <file>
+Load all PS keys from PSR file.
+-r sends a warm reset afterwards
+.TP
+.BI pscheck\ [-r]\ [-s\ <stores>]\ <file>
+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 <marcel@holtmann.org>,
+man page by Adam Laurie <adam@algroup.co.uk>
+.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 <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#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, "<handle>", "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,"<channel>", "Lock radio on specific channel" },
+ { "hoppingon", cmd_hoppingon, "", "Revert to channel hopping" },
+ { "rttxdata1", cmd_rttxdata1, "<freq> <level>", "TXData1 radio test" },
+ { "radiotest", cmd_radiotest, "<freq> <level> <id>", "Run radio tests" },
+ { "memtypes", cmd_memtypes, NULL, "Get memory types" },
+ { "psget", cmd_psget, "<key>", "Get value for PS key" },
+ { "psset", cmd_psset, "<key> <value>", "Set value for PS key" },
+ { "psclr", cmd_psclr, "<key>", "Clear value for PS key" },
+ { "pslist", cmd_pslist, NULL, "List all PS keys" },
+ { "psread", cmd_psread, NULL, "Read all PS keys" },
+ { "psload", cmd_psload, "<file>", "Load all PS keys from PSR file" },
+ { "pscheck", cmd_pscheck, "<file>", "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] <command>\n\n");
+
+ printf("Options:\n"
+ "\t-t <transport> Select the transport\n"
+ "\t-d <device> Select the device\n"
+ "\t-b <bcsp rate> 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 " <hciX> | <bdaddr>"
+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 " <bdaddr> [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 " <bdaddr> [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 <marcel@holtmann.org>.
+.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 <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <getopt.h>
+#include <signal.h>
+#include <sys/poll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/cmtp.h>
+
+#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, "<bdaddr>", "Connect a remote device" },
+ { "release", "disconnect", cmd_release, "[bdaddr]", "Disconnect the remote device" },
+ { "loopback", "test", cmd_loopback, "<bdaddr>", "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 <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#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 <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <stdint.h>
+#include <termios.h>
+
+#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 <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdint.h>
+
+#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 <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+#include <termios.h>
+
+#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 <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+#include <termios.h>
+
+#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 <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#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 <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include <usb.h>
+
+#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 <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include <usb.h>
+
+#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 <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <stdint.h>
+
+/* 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 <marcel@holtmann.org>.
+.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 <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+
+#include <usb.h>
+
+#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 " <device>"
+The command specifies the device to use.
+.SH COMMANDS
+.TP
+.BI verify " <dfu-file>"
+Display information about the firmware file.
+.TP
+.BI modify " <dfu-file>"
+Change DFU specific values in the firmware file.
+.TP
+.BI upgrade " <dfu-file>"
+Upgrade the device with a new firmware.
+.TP
+.BI archive " <dfu-file>"
+Archive the current firmware of the device.
+.SH AUTHOR
+Written by Marcel Holtmann <marcel@holtmann.org>.
+.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 <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <string.h>
+#include <libgen.h>
+#include <endian.h>
+#include <byteswap.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <usb.h>
+
+#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, "<dfu-file>", "Check firmware file" },
+ { "modify", "change", cmd_modify, "<dfu-file>", "Change firmware attributes" },
+ { "upgrade", "download", cmd_upgrade, "<dfu-file>", "Download a new firmware" },
+ { "archive", "upload", cmd_archive, "<dfu-file>", "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] <command>\n"
+ "\n");
+
+ printf("Options:\n"
+ "\t-d, --device <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 <maxk@qualcomm.com>
+.PP
+Manual page by Nils Faerber <nils@kernelconcepts.de>
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 <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <syslog.h>
+#include <termios.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#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 <jt@hpl.hp.com> - 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 <thomas.moser@tmoser.ch>
+ */
+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 <marcel@holtmann.org>
+ */
+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] <tty> <type | id> [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 <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <termios.h>
+
+#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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#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 <marcel@holtmann.org>
+ * 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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <syslog.h>
+#include <termios.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#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 <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/param.h>
+
+#include <bluetooth/bluetooth.h>
+
+#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 <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#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 <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <syslog.h>
+#include <termios.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#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 " <bdaddr>"
+This command stores the link key for
+.I bdaddr
+on the device.
+.TP
+.BI delkey " <bdaddr>"
+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 <maxk@qualcomm.com> and Marcel Holtmann <marcel@holtmann.org>
+.PP
+man page by Fabrizio Gennari <fabrizio.gennari@philips.com>
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 <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#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, "<mtu:pkt>", "Set ACL MTU and number of packets" },
+ { "scomtu", cmd_scomtu, "<mtu:pkt>", "Set SCO MTU and number of packets" },
+ { "putkey", cmd_putkey, "<bdaddr>", "Store link key on the device" },
+ { "delkey", cmd_delkey, "<bdaddr>", "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, "<bdaddr>", "Add a device to the blacklist" },
+ { "unblock", cmd_unblock, "<bdaddr>", "Remove a device from the blacklist" },
+ { "lerandaddr", cmd_le_addr, "<bdaddr>", "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 <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+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 <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+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 <hciX>] [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 " <hciX>"
+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 " <bdaddr>"
+Print device name of remote device with Bluetooth address
+.IR bdaddr .
+.TP
+.BI info " <bdaddr>"
+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 " <ogf> <ocf> [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=<ptype>] <bdaddr>"
+Create baseband connection to remote device with Bluetooth address
+.IR bdaddr .
+Option
+.I
+--pkt-type
+specifies a list of allowed packet types.
+.I
+<ptype>
+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 " <bdaddr> [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 " <bdaddr> <role>"
+Switch role for the baseband connection from the remote device to
+.BR master
+or
+.BR slave .
+.TP
+.BI cpt " <bdaddr> <packet types>"
+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 " <bdaddr>"
+Display received signal strength information for the connection to the device
+with Bluetooth address
+.IR bdaddr .
+.TP
+.BI lq " <bdaddr>"
+Display link quality for the connection to the device with Bluetooth address
+.IR bdaddr .
+.TP
+.BI tpl " <bdaddr> [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 " <bdaddr>"
+Display AFH channel map for the connection to the device with Bluetooth address
+.IR bdaddr .
+.TP
+.BI lp " <bdaddr> [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 " <bdaddr> [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 " <bdaddr>"
+Request authentication for the device with Bluetooth address
+.IR bdaddr .
+.TP
+.BI enc " <bdaddr> [encrypt enable]"
+Enable or disable the encryption for the device with Bluetooth address
+.IR bdaddr .
+.TP
+.BI key " <bdaddr>"
+Change the connection link key for the device with Bluetooth address
+.IR bdaddr .
+.TP
+.BI clkoff " <bdaddr>"
+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 <maxk@qualcomm.com> and Marcel Holtmann <marcel@holtmann.org>
+.PP
+man page by Fabrizio Gennari <fabrizio.gennari@philips.com>
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 <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#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 <bdaddr>\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 <bdaddr>\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 <ogf> <ocf> [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] <bdaddr>\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 <bdaddr> [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 <bdaddr> <role>\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 <bdaddr>\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 <bdaddr>\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 <bdaddr> [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 <bdaddr>\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 <bdaddr> <packet_types>\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 <bdaddr> [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 <bdaddr> [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 <bdaddr>\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 <bdaddr> [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 <bdaddr>\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 <bdaddr>\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] <bdaddr>\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] <bdaddr>\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 <bdaddr>\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 <handle> [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 <handle> <min> <max> <latency> <timeout>\n"
+ "\tOptions:\n"
+ "\t -H, --handle <0xXXXX> LE connection handle\n"
+ "\t -m, --min <interval> Range: 0x0006 to 0x0C80\n"
+ "\t -M, --max <interval> Range: 0x0006 to 0x0C80\n"
+ "\t -l, --latency <range> Slave latency. Range: 0x0000 to 0x03E8\n"
+ "\t -t, --timeout <time> N * 10ms. Range: 0x000A to 0x0C80\n"
+ "\n\t min/max range: 7.5ms to 4s. Multiply factor: 1.25ms"
+ "\n\t timeout range: 100ms to 32.0s. Larger than max interval\n";
+
+static void cmd_lecup(int dev_id, int argc, char **argv)
+{
+ uint16_t handle = 0, min, max, latency, timeout;
+ int opt, dd, base;
+
+ /* Aleatory valid values */
+ min = 0x0C8;
+ max = 0x0960;
+ latency = 0x0007;
+ timeout = 0x0C80;
+
+ for_each_opt(opt, lecup_options, NULL) {
+ if (optarg && strncasecmp("0x", optarg, 2) == 0)
+ base = 16;
+ else
+ base = 10;
+
+ switch (opt) {
+ case 'H':
+ handle = strtoul(optarg, NULL, base);
+ break;
+ case 'm':
+ min = strtoul(optarg, NULL, base);
+ break;
+ case 'M':
+ max = strtoul(optarg, NULL, base);
+ break;
+ case 'l':
+ latency = strtoul(optarg, NULL, base);
+ break;
+ case 't':
+ timeout = strtoul(optarg, NULL, base);
+ break;
+ default:
+ printf("%s", lecup_help);
+ return;
+ }
+ }
+
+ if (handle == 0) {
+ printf("%s", lecup_help);
+ return;
+ }
+
+ if (dev_id < 0)
+ dev_id = hci_get_route(NULL);
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ fprintf(stderr, "HCI device open failed\n");
+ exit(1);
+ }
+
+ if (hci_le_conn_update(dd, htobs(handle), htobs(min), htobs(max),
+ htobs(latency), htobs(timeout), 5000) < 0) {
+ int err = errno;
+ fprintf(stderr, "Could not change connection params: %s(%d)\n",
+ strerror(err), err);
+ }
+
+ hci_close_dev(dd);
+}
+
+static struct {
+ char *cmd;
+ void (*func)(int dev_id, int argc, char **argv);
+ char *doc;
+} command[] = {
+ { "dev", cmd_dev, "Display local devices" },
+ { "inq", cmd_inq, "Inquire remote devices" },
+ { "scan", cmd_scan, "Scan for remote devices" },
+ { "name", cmd_name, "Get name from remote device" },
+ { "info", cmd_info, "Get information from remote device" },
+ { "spinq", cmd_spinq, "Start periodic inquiry" },
+ { "epinq", cmd_epinq, "Exit periodic inquiry" },
+ { "cmd", cmd_cmd, "Submit arbitrary HCI commands" },
+ { "con", cmd_con, "Display active connections" },
+ { "cc", cmd_cc, "Create connection to remote device" },
+ { "dc", cmd_dc, "Disconnect from remote device" },
+ { "sr", cmd_sr, "Switch master/slave role" },
+ { "cpt", cmd_cpt, "Change connection packet type" },
+ { "rssi", cmd_rssi, "Display connection RSSI" },
+ { "lq", cmd_lq, "Display link quality" },
+ { "tpl", cmd_tpl, "Display transmit power level" },
+ { "afh", cmd_afh, "Display AFH channel map" },
+ { "lp", cmd_lp, "Set/display link policy settings" },
+ { "lst", cmd_lst, "Set/display link supervision timeout" },
+ { "auth", cmd_auth, "Request authentication" },
+ { "enc", cmd_enc, "Set connection encryption" },
+ { "key", cmd_key, "Change connection link key" },
+ { "clkoff", cmd_clkoff, "Read clock offset" },
+ { "clock", cmd_clock, "Read local or remote clock" },
+ { "lescan", cmd_lescan, "Start LE scan" },
+ { "lewladd", cmd_lewladd, "Add device to LE White List" },
+ { "lewlrm", cmd_lewlrm, "Remove device from LE White List" },
+ { "lewlsz", cmd_lewlsz, "Read size of LE White List" },
+ { "lewlclr", cmd_lewlclr, "Clear LE White list" },
+ { "lecc", cmd_lecc, "Create a LE Connection" },
+ { "ledc", cmd_ledc, "Disconnect a LE Connection" },
+ { "lecup", cmd_lecup, "LE Connection Update" },
+ { NULL, NULL, 0 }
+};
+
+static void usage(void)
+{
+ int i;
+
+ printf("hcitool - HCI Tool ver %s\n", VERSION);
+ printf("Usage:\n"
+ "\thcitool [options] <command> [command parameters]\n");
+ printf("Options:\n"
+ "\t--help\tDisplay help\n"
+ "\t-i dev\tHCI device\n");
+ printf("Commands:\n");
+ for (i = 0; command[i].cmd; i++)
+ printf("\t%-4s\t%s\n", command[i].cmd,
+ command[i].doc);
+ printf("\n"
+ "For more information on the usage of each command use:\n"
+ "\thcitool <command> --help\n" );
+}
+
+static struct option main_options[] = {
+ { "help", 0, 0, 'h' },
+ { "device", 1, 0, 'i' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ int opt, i, dev_id = -1;
+ bdaddr_t ba;
+
+ while ((opt=getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) {
+ switch (opt) {
+ case 'i':
+ dev_id = hci_devid(optarg);
+ if (dev_id < 0) {
+ perror("Invalid device");
+ exit(1);
+ }
+ break;
+
+ case 'h':
+ default:
+ usage();
+ exit(0);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (argc < 1) {
+ usage();
+ exit(0);
+ }
+
+ if (dev_id != -1 && hci_devba(dev_id, &ba) < 0) {
+ perror("Device is not available");
+ exit(1);
+ }
+
+ for (i = 0; command[i].cmd; i++) {
+ if (strncmp(command[i].cmd,
+ argv[0], strlen(command[i].cmd)))
+ continue;
+
+ command[i].func(dev_id, argc, argv);
+ break;
+ }
+
+ if (command[i].cmd == 0) {
+ fprintf(stderr, "Unknown command - \"%s\"\n", *argv);
+ exit(1);
+ }
+
+ return 0;
+}
diff --git a/tools/hid2hci.8 b/tools/hid2hci.8
new file mode 100644
index 0000000..5d35274
--- /dev/null
+++ b/tools/hid2hci.8
@@ -0,0 +1,51 @@
+.\"
+.\" 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 HID2HCI 8 "MAY 15, 2009" "" ""
+
+.SH NAME
+hid2hci \- Bluetooth HID to HCI mode switching utility
+.SH SYNOPSIS
+.BR "hid2hci
+[
+.I options
+]
+.SH DESCRIPTION
+.B hid2hci
+is used to set up switch supported Bluetooth devices into the HCI
+mode and back.
+.SH OPTIONS
+.TP
+.BI -h
+Gives a list of possible options.
+.TP
+.BI -q
+Don't display any messages.
+.TP
+.BI -r [hid,hci]
+Sets the mode to switch the device into
+.TP
+.BI -v
+Specifies the 4 digit vendor ID assigned to the device being switched
+.TP
+.BI -p
+Specifies the 4 digit product ID assigned to the device being switched
+.TP
+.BI -m [csr, logitech, dell]
+Which vendor method to use for switching the device.
+.SH AUTHOR
+Written by Marcel Holtmann <marcel@holtmann.org>.
+.br
diff --git a/tools/hid2hci.c b/tools/hid2hci.c
new file mode 100644
index 0000000..a640772
--- /dev/null
+++ b/tools/hid2hci.c
@@ -0,0 +1,375 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2003-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+
+#include <usb.h>
+
+#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
+
+static char devpath[PATH_MAX + 1] = "/dev";
+
+struct hiddev_devinfo {
+ unsigned int bustype;
+ unsigned int busnum;
+ unsigned int devnum;
+ unsigned int ifnum;
+ short vendor;
+ short product;
+ short version;
+ unsigned num_applications;
+};
+
+struct hiddev_report_info {
+ unsigned report_type;
+ unsigned report_id;
+ unsigned num_fields;
+};
+
+typedef __signed__ int __s32;
+
+struct hiddev_usage_ref {
+ unsigned report_type;
+ unsigned report_id;
+ unsigned field_index;
+ unsigned usage_index;
+ unsigned usage_code;
+ __s32 value;
+};
+
+#define HIDIOCGDEVINFO _IOR('H', 0x03, struct hiddev_devinfo)
+#define HIDIOCINITREPORT _IO('H', 0x05)
+#define HIDIOCSREPORT _IOW('H', 0x08, struct hiddev_report_info)
+#define HIDIOCSUSAGE _IOW('H', 0x0C, struct hiddev_usage_ref)
+
+#define HID_REPORT_TYPE_OUTPUT 2
+
+#define HCI 0
+#define HID 1
+
+struct device_info {
+ struct usb_device *dev;
+ int mode;
+ uint16_t vendor;
+ uint16_t product;
+};
+
+static int switch_csr(struct device_info *devinfo)
+{
+ struct usb_dev_handle *udev;
+ int err;
+
+ udev = usb_open(devinfo->dev);
+ if (!udev)
+ return -errno;
+
+ err = usb_control_msg(udev, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, devinfo->mode, 0, NULL, 0, 10000);
+
+ if (err == 0) {
+ err = -1;
+ errno = EALREADY;
+ } else {
+ if (errno == ETIMEDOUT)
+ err = 0;
+ }
+
+ usb_close(udev);
+
+ return err;
+}
+
+static int send_report(int fd, const char *buf, size_t size)
+{
+ struct hiddev_report_info rinfo;
+ struct hiddev_usage_ref uref;
+ unsigned int i;
+ int err;
+
+ for (i = 0; i < size; i++) {
+ memset(&uref, 0, sizeof(uref));
+ uref.report_type = HID_REPORT_TYPE_OUTPUT;
+ uref.report_id = 0x10;
+ uref.field_index = 0;
+ uref.usage_index = i;
+ uref.usage_code = 0xff000001;
+ uref.value = buf[i] & 0x000000ff;
+ err = ioctl(fd, HIDIOCSUSAGE, &uref);
+ if (err < 0)
+ return err;
+ }
+
+ memset(&rinfo, 0, sizeof(rinfo));
+ rinfo.report_type = HID_REPORT_TYPE_OUTPUT;
+ rinfo.report_id = 0x10;
+ rinfo.num_fields = 1;
+ err = ioctl(fd, HIDIOCSREPORT, &rinfo);
+
+ return err;
+}
+
+static int switch_logitech(struct device_info *devinfo)
+{
+ char devname[PATH_MAX + 1];
+ int i, fd, err = -1;
+
+ for (i = 0; i < 16; i++) {
+ struct hiddev_devinfo dinfo;
+ char rep1[] = { 0xff, 0x80, 0x80, 0x01, 0x00, 0x00 };
+ char rep2[] = { 0xff, 0x80, 0x00, 0x00, 0x30, 0x00 };
+ char rep3[] = { 0xff, 0x81, 0x80, 0x00, 0x00, 0x00 };
+
+ sprintf(devname, "%s/hiddev%d", devpath, i);
+ fd = open(devname, O_RDWR);
+ if (fd < 0) {
+ sprintf(devname, "%s/usb/hiddev%d", devpath, i);
+ fd = open(devname, O_RDWR);
+ if (fd < 0) {
+ sprintf(devname, "%s/usb/hid/hiddev%d", devpath, i);
+ fd = open(devname, O_RDWR);
+ if (fd < 0)
+ continue;
+ }
+ }
+
+ memset(&dinfo, 0, sizeof(dinfo));
+ err = ioctl(fd, HIDIOCGDEVINFO, &dinfo);
+ if (err < 0 || (int) dinfo.busnum != atoi(devinfo->dev->bus->dirname) ||
+ (int) dinfo.devnum != atoi(devinfo->dev->filename)) {
+ close(fd);
+ continue;
+ }
+
+ err = ioctl(fd, HIDIOCINITREPORT, 0);
+ if (err < 0) {
+ close(fd);
+ break;
+ }
+
+ err = send_report(fd, rep1, sizeof(rep1));
+ if (err < 0) {
+ close(fd);
+ break;
+ }
+
+ err = send_report(fd, rep2, sizeof(rep2));
+ if (err < 0) {
+ close(fd);
+ break;
+ }
+
+ err = send_report(fd, rep3, sizeof(rep3));
+ close(fd);
+ break;
+ }
+
+ return err;
+}
+
+static int switch_dell(struct device_info *devinfo)
+{
+ char report[] = { 0x7f, 0x00, 0x00, 0x00 };
+
+ struct usb_dev_handle *handle;
+ int err;
+
+ switch (devinfo->mode) {
+ case HCI:
+ report[1] = 0x13;
+ break;
+ case HID:
+ report[1] = 0x14;
+ break;
+ }
+
+ handle = usb_open(devinfo->dev);
+ if (!handle)
+ return -EIO;
+
+ /* Don't need to check return, as might not be in use */
+ usb_detach_kernel_driver_np(handle, 0);
+
+ if (usb_claim_interface(handle, 0) < 0) {
+ usb_close(handle);
+ return -EIO;
+ }
+
+ err = usb_control_msg(handle,
+ USB_ENDPOINT_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ USB_REQ_SET_CONFIGURATION, 0x7f | (0x03 << 8), 0,
+ report, sizeof(report), 5000);
+
+ if (err == 0) {
+ err = -1;
+ errno = EALREADY;
+ } else {
+ if (errno == ETIMEDOUT)
+ err = 0;
+ }
+
+ usb_close(handle);
+
+ return err;
+}
+
+static int find_device(struct device_info* devinfo)
+{
+ struct usb_bus *bus;
+ struct usb_device *dev;
+
+ 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.idVendor == devinfo->vendor &&
+ dev->descriptor.idProduct == devinfo->product) {
+ devinfo->dev=dev;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void usage(char* error)
+{
+ if (error)
+ fprintf(stderr,"\n%s\n", error);
+ else
+ printf("hid2hci - Bluetooth HID to HCI mode switching utility\n\n");
+
+ printf("Usage:\n"
+ "\thid2hci [options]\n"
+ "\n");
+
+ printf("Options:\n"
+ "\t-h, --help Display help\n"
+ "\t-q, --quiet Don't display any messages\n"
+ "\t-r, --mode= Mode to switch to [hid, hci]\n"
+ "\t-v, --vendor= Vendor ID to act upon\n"
+ "\t-p, --product= Product ID to act upon\n"
+ "\t-m, --method= Method to use to switch [csr, logitech, dell]\n"
+ "\n");
+ if (error)
+ exit(1);
+}
+
+static struct option main_options[] = {
+ { "help", no_argument, 0, 'h' },
+ { "quiet", no_argument, 0, 'q' },
+ { "mode", required_argument, 0, 'r' },
+ { "vendor", required_argument, 0, 'v' },
+ { "product", required_argument, 0, 'p' },
+ { "method", required_argument, 0, 'm' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ struct device_info dev = { NULL, HCI, 0, 0 };
+ int opt, quiet = 0;
+ int (*method)(struct device_info *dev) = NULL;
+
+ while ((opt = getopt_long(argc, argv, "+r:v:p:m:qh", main_options, NULL)) != -1) {
+ switch (opt) {
+ case 'r':
+ if (optarg && !strcmp(optarg, "hid"))
+ dev.mode = HID;
+ else if (optarg && !strcmp(optarg, "hci"))
+ dev.mode = HCI;
+ else
+ usage("ERROR: Undefined radio mode\n");
+ break;
+ case 'v':
+ sscanf(optarg, "%4hx", &dev.vendor);
+ break;
+ case 'p':
+ sscanf(optarg, "%4hx", &dev.product);
+ break;
+ case 'm':
+ if (optarg && !strcmp(optarg, "csr"))
+ method = switch_csr;
+ else if (optarg && !strcmp(optarg, "logitech"))
+ method = switch_logitech;
+ else if (optarg && !strcmp(optarg, "dell"))
+ method = switch_dell;
+ else
+ usage("ERROR: Undefined switching method\n");
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 'h':
+ usage(NULL);
+ default:
+ exit(0);
+ }
+ }
+
+ if (!quiet && (!dev.vendor || !dev.product || !method))
+ usage("ERROR: Vendor ID, Product ID, and Switching Method must all be defined.\n");
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ usb_init();
+
+ if (!find_device(&dev)) {
+ if (!quiet)
+ fprintf(stderr, "Device %04x:%04x not found on USB bus.\n",
+ dev.vendor, dev.product);
+ exit(1);
+ }
+
+ if (!quiet)
+ printf("Attempting to switch device %04x:%04x to %s mode ",
+ dev.vendor, dev.product, dev.mode ? "HID" : "HCI");
+ fflush(stdout);
+
+ if (method(&dev) < 0 && !quiet)
+ printf("failed (%s)\n", strerror(errno));
+ else if (!quiet)
+ printf("was successful\n");
+
+ return errno;
+}
diff --git a/tools/kword.c b/tools/kword.c
new file mode 100644
index 0000000..62e24fe
--- /dev/null
+++ b/tools/kword.c
@@ -0,0 +1,65 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+
+#include "kword.h"
+#include "parser.h"
+
+int lineno;
+
+struct keyword_t rfcomm_keyword[] = {
+ { "bind", K_BIND },
+ { "device", K_DEVICE },
+ { "channel", K_CHANNEL },
+ { "comment", K_COMMENT },
+
+ { "yes", K_YES },
+ { "no", K_NO },
+ { "enable", K_YES },
+ { "disable", K_NO },
+
+ { NULL , 0 }
+};
+
+int rfcomm_find_keyword(struct keyword_t *keyword, char *string)
+{
+ while (keyword->string) {
+ if (!strcmp(string, keyword->string))
+ return keyword->type;
+ keyword++;
+ }
+
+ return -1;
+}
+
+struct rfcomm_opts rfcomm_opts[RFCOMM_MAX_DEV];
diff --git a/tools/kword.h b/tools/kword.h
new file mode 100644
index 0000000..81a2a88
--- /dev/null
+++ b/tools/kword.h
@@ -0,0 +1,46 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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
+ *
+ */
+
+extern int lineno;
+
+struct keyword_t {
+ char *string;
+ int type;
+};
+
+extern struct keyword_t rfcomm_keyword[];
+
+int rfcomm_find_keyword(struct keyword_t *keyword, char *string);
+
+#define MAXCOMMENTLEN 100
+
+struct rfcomm_opts {
+ int bind;
+ bdaddr_t bdaddr;
+ int channel;
+ char comment[MAXCOMMENTLEN + 1];
+};
+
+extern struct rfcomm_opts rfcomm_opts[RFCOMM_MAX_DEV];
+
+int rfcomm_read_config(char *filename);
diff --git a/tools/l2ping.8 b/tools/l2ping.8
new file mode 100644
index 0000000..8b77ee2
--- /dev/null
+++ b/tools/l2ping.8
@@ -0,0 +1,76 @@
+.TH L2PING 8 "Jan 22 2002" BlueZ "Linux System Administration"
+.SH NAME
+l2ping \- Send L2CAP echo request and receive answer
+.SH SYNOPSIS
+.B l2ping
+.RB [\| \-i
+.IR <hciX> \|]
+.RB [\| \-s
+.IR size \|]
+.RB [\| \-c
+.IR count \|]
+.RB [\| \-t
+.IR timeout \|]
+.RB [\| \-d
+.IR delay \|]
+.RB [\| \-f \|]
+.RB [\| \-r \|]
+.RB [\| \-v \|]
+.I bd_addr
+
+.SH DESCRIPTION
+.LP
+L2ping sends a L2CAP echo request to the Bluetooth MAC address
+.I bd_addr
+given in dotted hex notation.
+.SH OPTIONS
+.TP
+.BI \-i " <hciX>"
+The command is applied to device
+.BI
+hciX
+, which must be the name of an installed Bluetooth device (X = 0, 1, 2, ...)
+If not specified, the command will be sent to the first available Bluetooth
+device.
+.TP
+.BI \-s " size"
+The
+.I size
+of the data packets to be sent.
+.TP
+.BI \-c " count"
+Send
+.I count
+number of packets then exit.
+.TP
+.BI \-t " timeout"
+Wait
+.I timeout
+seconds for the response.
+.TP
+.BI \-d " delay"
+Wait
+.I delay
+seconds between pings.
+.TP
+.B \-f
+Kind of flood ping. Use with care! It reduces the delay time between packets
+to 0.
+.TP
+.B \-r
+Reverse ping (gnip?). Send echo response instead of echo request.
+.TP
+.B \-v
+Verify response payload is identical to request payload. It is not required for
+remote stacks to return the request payload, but most stacks do (including
+Bluez).
+.TP
+.I bd_addr
+The Bluetooth MAC address to be pinged in dotted hex notation like
+.B 01:02:03:ab:cd:ef
+or
+.B 01:EF:cd:aB:02:03
+.SH AUTHORS
+Written by Maxim Krasnyansky <maxk@qualcomm.com> and Marcel Holtmann <marcel@holtmann.org>
+.PP
+man page by Nils Faerber <nils@kernelconcepts.de>, Adam Laurie <adam@algroup.co.uk>.
diff --git a/tools/l2ping.c b/tools/l2ping.c
new file mode 100644
index 0000000..29fb3d0
--- /dev/null
+++ b/tools/l2ping.c
@@ -0,0 +1,324 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
+
+/* Defaults */
+static bdaddr_t bdaddr;
+static int size = 44;
+static int ident = 200;
+static int delay = 1;
+static int count = -1;
+static int timeout = 10;
+static int reverse = 0;
+static int verify = 0;
+
+/* Stats */
+static int sent_pkt = 0;
+static int recv_pkt = 0;
+
+static float tv2fl(struct timeval tv)
+{
+ return (float)(tv.tv_sec*1000.0) + (float)(tv.tv_usec/1000.0);
+}
+
+static void stat(int sig)
+{
+ int loss = sent_pkt ? (float)((sent_pkt-recv_pkt)/(sent_pkt/100.0)) : 0;
+ printf("%d sent, %d received, %d%% loss\n", sent_pkt, recv_pkt, loss);
+ exit(0);
+}
+
+static void ping(char *svr)
+{
+ struct sigaction sa;
+ struct sockaddr_l2 addr;
+ socklen_t optlen;
+ unsigned char *send_buf;
+ unsigned char *recv_buf;
+ char str[18];
+ int i, sk, lost;
+ uint8_t id;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = stat;
+ sigaction(SIGINT, &sa, NULL);
+
+ send_buf = malloc(L2CAP_CMD_HDR_SIZE + size);
+ recv_buf = malloc(L2CAP_CMD_HDR_SIZE + size);
+ if (!send_buf || !recv_buf) {
+ perror("Can't allocate buffer");
+ exit(1);
+ }
+
+ /* Create socket */
+ sk = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
+ if (sk < 0) {
+ perror("Can't create socket");
+ goto error;
+ }
+
+ /* Bind to local address */
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, &bdaddr);
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("Can't bind socket");
+ goto error;
+ }
+
+ /* Connect to remote device */
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ str2ba(svr, &addr.l2_bdaddr);
+
+ if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("Can't connect");
+ goto error;
+ }
+
+ /* Get local address */
+ memset(&addr, 0, sizeof(addr));
+ optlen = sizeof(addr);
+
+ if (getsockname(sk, (struct sockaddr *) &addr, &optlen) < 0) {
+ perror("Can't get local address");
+ goto error;
+ }
+
+ ba2str(&addr.l2_bdaddr, str);
+ printf("Ping: %s from %s (data size %d) ...\n", svr, str, size);
+
+ /* Initialize send buffer */
+ for (i = 0; i < size; i++)
+ send_buf[L2CAP_CMD_HDR_SIZE + i] = (i % 40) + 'A';
+
+ id = ident;
+
+ while (count == -1 || count-- > 0) {
+ struct timeval tv_send, tv_recv, tv_diff;
+ l2cap_cmd_hdr *send_cmd = (l2cap_cmd_hdr *) send_buf;
+ l2cap_cmd_hdr *recv_cmd = (l2cap_cmd_hdr *) recv_buf;
+
+ /* Build command header */
+ send_cmd->ident = id;
+ send_cmd->len = htobs(size);
+
+ if (reverse)
+ send_cmd->code = L2CAP_ECHO_RSP;
+ else
+ send_cmd->code = L2CAP_ECHO_REQ;
+
+ gettimeofday(&tv_send, NULL);
+
+ /* Send Echo Command */
+ if (send(sk, send_buf, L2CAP_CMD_HDR_SIZE + size, 0) <= 0) {
+ perror("Send failed");
+ goto error;
+ }
+
+ /* Wait for Echo Response */
+ lost = 0;
+ while (1) {
+ struct pollfd pf[1];
+ int err;
+
+ pf[0].fd = sk;
+ pf[0].events = POLLIN;
+
+ if ((err = poll(pf, 1, timeout * 1000)) < 0) {
+ perror("Poll failed");
+ goto error;
+ }
+
+ if (!err) {
+ lost = 1;
+ break;
+ }
+
+ if ((err = recv(sk, recv_buf, L2CAP_CMD_HDR_SIZE + size, 0)) < 0) {
+ perror("Recv failed");
+ goto error;
+ }
+
+ if (!err){
+ printf("Disconnected\n");
+ goto error;
+ }
+
+ recv_cmd->len = btohs(recv_cmd->len);
+
+ /* Check for our id */
+ if (recv_cmd->ident != id)
+ continue;
+
+ /* Check type */
+ if (!reverse && recv_cmd->code == L2CAP_ECHO_RSP)
+ break;
+
+ if (recv_cmd->code == L2CAP_COMMAND_REJ) {
+ printf("Peer doesn't support Echo packets\n");
+ goto error;
+ }
+
+ }
+ sent_pkt++;
+
+ if (!lost) {
+ recv_pkt++;
+
+ gettimeofday(&tv_recv, NULL);
+ timersub(&tv_recv, &tv_send, &tv_diff);
+
+ if (verify) {
+ /* Check payload length */
+ if (recv_cmd->len != size) {
+ fprintf(stderr, "Received %d bytes, expected %d\n",
+ recv_cmd->len, size);
+ goto error;
+ }
+
+ /* Check payload */
+ if (memcmp(&send_buf[L2CAP_CMD_HDR_SIZE],
+ &recv_buf[L2CAP_CMD_HDR_SIZE], size)) {
+ fprintf(stderr, "Response payload different.\n");
+ goto error;
+ }
+ }
+
+ printf("%d bytes from %s id %d time %.2fms\n", recv_cmd->len, svr,
+ id - ident, tv2fl(tv_diff));
+
+ if (delay)
+ sleep(delay);
+ } else {
+ printf("no response from %s: id %d\n", svr, id - ident);
+ }
+
+ if (++id > 254)
+ id = ident;
+ }
+ stat(0);
+ free(send_buf);
+ free(recv_buf);
+ return;
+
+error:
+ close(sk);
+ free(send_buf);
+ free(recv_buf);
+ exit(1);
+}
+
+static void usage(void)
+{
+ printf("l2ping - L2CAP ping\n");
+ printf("Usage:\n");
+ printf("\tl2ping [-i device] [-s size] [-c count] [-t timeout] [-d delay] [-f] [-r] [-v] <bdaddr>\n");
+ printf("\t-f Flood ping (delay = 0)\n");
+ printf("\t-r Reverse ping\n");
+ printf("\t-v Verify request and response payload\n");
+}
+
+int main(int argc, char *argv[])
+{
+ int opt;
+
+ /* Default options */
+ bacpy(&bdaddr, BDADDR_ANY);
+
+ while ((opt=getopt(argc,argv,"i:d:s:c:t:frv")) != EOF) {
+ switch(opt) {
+ case 'i':
+ if (!strncasecmp(optarg, "hci", 3))
+ hci_devba(atoi(optarg + 3), &bdaddr);
+ else
+ str2ba(optarg, &bdaddr);
+ break;
+
+ case 'd':
+ delay = atoi(optarg);
+ break;
+
+ case 'f':
+ /* Kinda flood ping */
+ delay = 0;
+ break;
+
+ case 'r':
+ /* Use responses instead of requests */
+ reverse = 1;
+ break;
+
+ case 'v':
+ verify = 1;
+ break;
+
+ case 'c':
+ count = atoi(optarg);
+ break;
+
+ case 't':
+ timeout = atoi(optarg);
+ break;
+
+ case 's':
+ size = atoi(optarg);
+ break;
+
+ default:
+ usage();
+ exit(1);
+ }
+ }
+
+ if (!(argc - optind)) {
+ usage();
+ exit(1);
+ }
+
+ ping(argv[optind]);
+
+ return 0;
+}
diff --git a/tools/lexer.c b/tools/lexer.c
new file mode 100644
index 0000000..4c7dcd4
--- /dev/null
+++ b/tools/lexer.c
@@ -0,0 +1,1834 @@
+
+#line 3 "tools/lexer.c"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 35
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif /* defined (__STDC__) */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart(yyin )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+extern int yyleng;
+
+extern FILE *yyin, *yyout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ #define YY_LESS_LINENO(n)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = (yy_hold_char); \
+ YY_RESTORE_YY_MORE_OFFSET \
+ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, (yytext_ptr) )
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via yyrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+ : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+static int yy_n_chars; /* number of characters read into yy_ch_buf */
+int yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 0; /* whether we need to initialize */
+static int yy_start = 0; /* start state number */
+
+/* Flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin. A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void yyrestart (FILE *input_file );
+void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer );
+YY_BUFFER_STATE yy_create_buffer (FILE *file,int size );
+void yy_delete_buffer (YY_BUFFER_STATE b );
+void yy_flush_buffer (YY_BUFFER_STATE b );
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer );
+void yypop_buffer_state (void );
+
+static void yyensure_buffer_stack (void );
+static void yy_load_buffer_state (void );
+static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file );
+
+#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size );
+YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str );
+YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len );
+
+void *yyalloc (yy_size_t );
+void *yyrealloc (void *,yy_size_t );
+void yyfree (void * );
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ yyensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer(yyin,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ yyensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer(yyin,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+typedef unsigned char YY_CHAR;
+
+FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;
+
+typedef int yy_state_type;
+
+extern int yylineno;
+
+int yylineno = 1;
+
+extern char *yytext;
+#define yytext_ptr yytext
+
+static yy_state_type yy_get_previous_state (void );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state );
+static int yy_get_next_buffer (void );
+static void yy_fatal_error (yyconst char msg[] );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ (yytext_ptr) = yy_bp; \
+ yyleng = (size_t) (yy_cp - yy_bp); \
+ (yy_hold_char) = *yy_cp; \
+ *yy_cp = '\0'; \
+ (yy_c_buf_p) = yy_cp;
+
+#define YY_NUM_RULES 9
+#define YY_END_OF_BUFFER 10
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static yyconst flex_int16_t yy_accept[36] =
+ { 0,
+ 0, 0, 10, 8, 1, 7, 8, 8, 6, 3,
+ 6, 0, 4, 0, 2, 6, 3, 6, 3, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 5, 0
+ } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 1, 4, 5, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 6, 1, 1, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 8, 1, 1,
+ 1, 1, 1, 1, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 1, 1, 1, 1, 6, 1, 9, 9, 9, 9,
+
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+static yyconst flex_int32_t yy_meta[10] =
+ { 0,
+ 1, 1, 2, 1, 1, 3, 4, 1, 4
+ } ;
+
+static yyconst flex_int16_t yy_base[49] =
+ { 0,
+ 0, 0, 46, 47, 47, 47, 41, 41, 0, 4,
+ 36, 38, 37, 37, 47, 0, 7, 31, 31, 0,
+ 0, 29, 0, 0, 28, 0, 0, 27, 0, 0,
+ 26, 0, 0, 47, 47, 15, 19, 21, 29, 28,
+ 27, 26, 25, 24, 23, 22, 13, 8
+ } ;
+
+static yyconst flex_int16_t yy_def[49] =
+ { 0,
+ 35, 1, 35, 35, 35, 35, 36, 37, 38, 35,
+ 10, 36, 36, 37, 35, 38, 38, 38, 38, 39,
+ 40, 35, 41, 42, 35, 43, 44, 35, 45, 46,
+ 35, 47, 48, 35, 0, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35
+ } ;
+
+static yyconst flex_int16_t yy_nxt[57] =
+ { 0,
+ 4, 5, 6, 7, 8, 9, 10, 4, 11, 16,
+ 17, 34, 18, 19, 20, 12, 33, 12, 12, 14,
+ 14, 14, 14, 16, 16, 31, 30, 28, 27, 25,
+ 24, 22, 21, 32, 29, 26, 23, 19, 20, 15,
+ 13, 13, 18, 15, 13, 35, 3, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35
+ } ;
+
+static yyconst flex_int16_t yy_chk[57] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 10,
+ 10, 48, 10, 17, 17, 36, 47, 36, 36, 37,
+ 37, 37, 37, 38, 38, 46, 45, 44, 43, 42,
+ 41, 40, 39, 31, 28, 25, 22, 19, 18, 14,
+ 13, 12, 11, 8, 7, 3, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35
+ } ;
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+extern int yy_flex_debug;
+int yy_flex_debug = 0;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *yytext;
+#line 1 "lexer.l"
+#line 2 "lexer.l"
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <config.h>
+#endif
+
+/* Nasty workaround, but flex defines isatty() twice */
+#define _UNISTD_H
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+
+#include "kword.h"
+#include "parser.h"
+
+int yylex(void);
+
+#define YY_NO_INPUT
+
+#define ECHO {;}
+#define YY_DECL int yylex(void)
+
+int yyerror(char *str);
+
+#line 520 "tools/lexer.c"
+
+#define INITIAL 0
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+static int yy_init_globals (void );
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+int yylex_destroy (void );
+
+int yyget_debug (void );
+
+void yyset_debug (int debug_flag );
+
+YY_EXTRA_TYPE yyget_extra (void );
+
+void yyset_extra (YY_EXTRA_TYPE user_defined );
+
+FILE *yyget_in (void );
+
+void yyset_in (FILE * in_str );
+
+FILE *yyget_out (void );
+
+void yyset_out (FILE * out_str );
+
+int yyget_leng (void );
+
+char *yyget_text (void );
+
+int yyget_lineno (void );
+
+void yyset_lineno (int line_number );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap (void );
+#else
+extern int yywrap (void );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (void );
+#else
+static int input (void );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0)
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ unsigned n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(yyin); \
+ } \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int yylex (void);
+
+#define YY_DECL int yylex (void)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+
+#line 64 "lexer.l"
+
+
+#line 703 "tools/lexer.c"
+
+ if ( !(yy_init) )
+ {
+ (yy_init) = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! (yy_start) )
+ (yy_start) = 1; /* first start state */
+
+ if ( ! yyin )
+ yyin = stdin;
+
+ if ( ! yyout )
+ yyout = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ yyensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer(yyin,YY_BUF_SIZE );
+ }
+
+ yy_load_buffer_state( );
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = (yy_c_buf_p);
+
+ /* Support of yytext. */
+ *yy_cp = (yy_hold_char);
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = (yy_start);
+yy_match:
+ do
+ {
+ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 36 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ ++yy_cp;
+ }
+ while ( yy_base[yy_current_state] != 47 );
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+ if ( yy_act == 0 )
+ { /* have to back up */
+ yy_cp = (yy_last_accepting_cpos);
+ yy_current_state = (yy_last_accepting_state);
+ yy_act = yy_accept[yy_current_state];
+ }
+
+ YY_DO_BEFORE_ACTION;
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+ case 0: /* must back up */
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = (yy_hold_char);
+ yy_cp = (yy_last_accepting_cpos);
+ yy_current_state = (yy_last_accepting_state);
+ goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 66 "lexer.l"
+{
+ /* Skip spaces and tabs */
+ ;
+ }
+ YY_BREAK
+case 2:
+/* rule 2 can match eol */
+YY_RULE_SETUP
+#line 71 "lexer.l"
+{
+ /* Skip comments */
+ lineno++;
+ }
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 76 "lexer.l"
+{
+ yylval.number = atoi(yytext);
+ return NUMBER;
+ }
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 81 "lexer.l"
+{
+ yylval.string = yytext;
+ return STRING;
+ }
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 86 "lexer.l"
+{
+ bdaddr_t *ba = malloc(sizeof(bdaddr_t));
+ str2ba(yytext, ba);
+ yylval.bdaddr = ba;
+ return BDADDR;
+ }
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 93 "lexer.l"
+{
+ int keyword = rfcomm_find_keyword(rfcomm_keyword, yytext);
+ if (keyword != -1)
+ return keyword;
+
+ if (strncmp(yytext, "rfcomm", 6) == 0) {
+ yylval.number = atoi(yytext + 6);
+ return RFCOMM;
+ }
+
+ yylval.string = yytext;
+ return WORD;
+ }
+ YY_BREAK
+case 7:
+/* rule 7 can match eol */
+YY_RULE_SETUP
+#line 107 "lexer.l"
+{
+ lineno++;
+ }
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 111 "lexer.l"
+{
+ return *yytext;
+ }
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 115 "lexer.l"
+ECHO;
+ YY_BREAK
+#line 866 "tools/lexer.c"
+case YY_STATE_EOF(INITIAL):
+ yyterminate();
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = (yy_hold_char);
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * yylex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++(yy_c_buf_p);
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = (yy_c_buf_p);
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ (yy_did_buffer_switch_on_eof) = 0;
+
+ if ( yywrap( ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) =
+ (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ (yy_c_buf_p) =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+} /* end of yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (void)
+{
+ register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ register char *source = (yytext_ptr);
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+ else
+ {
+ int num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+ int yy_c_buf_p_offset =
+ (int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ number_to_move - 1;
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ (yy_n_chars), (size_t) num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ if ( (yy_n_chars) == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ yyrestart(yyin );
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+ /* Extend the array by 50%, plus the number we really need. */
+ yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size );
+ if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+ }
+
+ (yy_n_chars) += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+ (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (void)
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+
+ yy_current_state = (yy_start);
+
+ for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+ {
+ register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 36 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state )
+{
+ register int yy_is_jam;
+ register char *yy_cp = (yy_c_buf_p);
+
+ register YY_CHAR yy_c = 1;
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 36 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ yy_is_jam = (yy_current_state == 35);
+
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (void)
+#else
+ static int input (void)
+#endif
+
+{
+ int c;
+
+ *(yy_c_buf_p) = (yy_hold_char);
+
+ if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ /* This was really a NUL. */
+ *(yy_c_buf_p) = '\0';
+
+ else
+ { /* need more input */
+ int offset = (yy_c_buf_p) - (yytext_ptr);
+ ++(yy_c_buf_p);
+
+ switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ yyrestart(yyin );
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( yywrap( ) )
+ return EOF;
+
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput();
+#else
+ return input();
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) = (yytext_ptr) + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */
+ *(yy_c_buf_p) = '\0'; /* preserve yytext */
+ (yy_hold_char) = *++(yy_c_buf_p);
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ *
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void yyrestart (FILE * input_file )
+{
+
+ if ( ! YY_CURRENT_BUFFER ){
+ yyensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer(yyin,YY_BUF_SIZE );
+ }
+
+ yy_init_buffer(YY_CURRENT_BUFFER,input_file );
+ yy_load_buffer_state( );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ *
+ */
+ void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer )
+{
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * yypop_buffer_state();
+ * yypush_buffer_state(new_buffer);
+ */
+ yyensure_buffer_stack ();
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ yy_load_buffer_state( );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (yywrap()) processing, but the only time this flag
+ * is looked at is after yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+static void yy_load_buffer_state (void)
+{
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ (yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ *
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE yy_create_buffer (FILE * file, int size )
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ yy_init_buffer(b,file );
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with yy_create_buffer()
+ *
+ */
+ void yy_delete_buffer (YY_BUFFER_STATE b )
+{
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ yyfree((void *) b->yy_ch_buf );
+
+ yyfree((void *) b );
+}
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a yyrestart() or at EOF.
+ */
+ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file )
+
+{
+ int oerrno = errno;
+
+ yy_flush_buffer(b );
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then yy_init_buffer was _probably_
+ * called from yyrestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ *
+ */
+ void yy_flush_buffer (YY_BUFFER_STATE b )
+{
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ yy_load_buffer_state( );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ *
+ */
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer )
+{
+ if (new_buffer == NULL)
+ return;
+
+ yyensure_buffer_stack();
+
+ /* This block is copied from yy_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ (yy_buffer_stack_top)++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from yy_switch_to_buffer. */
+ yy_load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ *
+ */
+void yypop_buffer_state (void)
+{
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ yy_delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if ((yy_buffer_stack_top) > 0)
+ --(yy_buffer_stack_top);
+
+ if (YY_CURRENT_BUFFER) {
+ yy_load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void yyensure_buffer_stack (void)
+{
+ int num_to_alloc;
+
+ if (!(yy_buffer_stack)) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1;
+ (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ if ( ! (yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+ memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ (yy_buffer_stack_max) = num_to_alloc;
+ (yy_buffer_stack_top) = 0;
+ return;
+ }
+
+ if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ int grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = (yy_buffer_stack_max) + grow_size;
+ (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc
+ ((yy_buffer_stack),
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ if ( ! (yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+ /* zero only the new slots.*/
+ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+ (yy_buffer_stack_max) = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size )
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ yy_switch_to_buffer(b );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ *
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE yy_scan_string (yyconst char * yystr )
+{
+
+ return yy_scan_bytes(yystr,strlen(yystr) );
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, int _yybytes_len )
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = _yybytes_len + 2;
+ buf = (char *) yyalloc(n );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+ for ( i = 0; i < _yybytes_len; ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = yy_scan_buffer(buf,n );
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg )
+{
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ yytext[yyleng] = (yy_hold_char); \
+ (yy_c_buf_p) = yytext + yyless_macro_arg; \
+ (yy_hold_char) = *(yy_c_buf_p); \
+ *(yy_c_buf_p) = '\0'; \
+ yyleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the current line number.
+ *
+ */
+int yyget_lineno (void)
+{
+
+ return yylineno;
+}
+
+/** Get the input stream.
+ *
+ */
+FILE *yyget_in (void)
+{
+ return yyin;
+}
+
+/** Get the output stream.
+ *
+ */
+FILE *yyget_out (void)
+{
+ return yyout;
+}
+
+/** Get the length of the current token.
+ *
+ */
+int yyget_leng (void)
+{
+ return yyleng;
+}
+
+/** Get the current token.
+ *
+ */
+
+char *yyget_text (void)
+{
+ return yytext;
+}
+
+/** Set the current line number.
+ * @param line_number
+ *
+ */
+void yyset_lineno (int line_number )
+{
+
+ yylineno = line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ *
+ * @see yy_switch_to_buffer
+ */
+void yyset_in (FILE * in_str )
+{
+ yyin = in_str ;
+}
+
+void yyset_out (FILE * out_str )
+{
+ yyout = out_str ;
+}
+
+int yyget_debug (void)
+{
+ return yy_flex_debug;
+}
+
+void yyset_debug (int bdebug )
+{
+ yy_flex_debug = bdebug ;
+}
+
+static int yy_init_globals (void)
+{
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from yylex_destroy(), so don't allocate here.
+ */
+
+ (yy_buffer_stack) = 0;
+ (yy_buffer_stack_top) = 0;
+ (yy_buffer_stack_max) = 0;
+ (yy_c_buf_p) = (char *) 0;
+ (yy_init) = 0;
+ (yy_start) = 0;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ yyin = stdin;
+ yyout = stdout;
+#else
+ yyin = (FILE *) 0;
+ yyout = (FILE *) 0;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * yylex_init()
+ */
+ return 0;
+}
+
+/* yylex_destroy is for both reentrant and non-reentrant scanners. */
+int yylex_destroy (void)
+{
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ yy_delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ yypop_buffer_state();
+ }
+
+ /* Destroy the stack itself. */
+ yyfree((yy_buffer_stack) );
+ (yy_buffer_stack) = NULL;
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * yylex() is called, initialization will occur. */
+ yy_init_globals( );
+
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
+{
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s )
+{
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *yyalloc (yy_size_t size )
+{
+ return (void *) malloc( size );
+}
+
+void *yyrealloc (void * ptr, yy_size_t size )
+{
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+}
+
+void yyfree (void * ptr )
+{
+ free( (char *) ptr ); /* see yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 115 "lexer.l"
+
+
+
+int yywrap(void)
+{
+ return 1;
+}
+
diff --git a/tools/lexer.l b/tools/lexer.l
new file mode 100644
index 0000000..ff9ce81
--- /dev/null
+++ b/tools/lexer.l
@@ -0,0 +1,120 @@
+%{
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <config.h>
+#endif
+
+/* Nasty workaround, but flex defines isatty() twice */
+#define _UNISTD_H
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+
+#include "kword.h"
+#include "parser.h"
+
+int yylex(void);
+
+#define YY_NO_INPUT
+
+#define ECHO {;}
+#define YY_DECL int yylex(void)
+
+int yyerror(char *str);
+
+%}
+
+%option nounput
+
+space [ \t]
+linebreak \n
+comment \#.*\n
+keyword [A-Za-z0-9\_\-]+
+
+number [0-9]+
+string \".*\"
+bdaddr [A-Za-z0-9]{2}:[A-Za-z0-9]{2}:[A-Za-z0-9]{2}:[A-Za-z0-9]{2}:[A-Za-z0-9]{2}:[A-Za-z0-9]{2}
+
+%%
+
+{space} {
+ /* Skip spaces and tabs */
+ ;
+ }
+
+{comment} {
+ /* Skip comments */
+ lineno++;
+ }
+
+{number} {
+ yylval.number = atoi(yytext);
+ return NUMBER;
+ }
+
+{string} {
+ yylval.string = yytext;
+ return STRING;
+ }
+
+{bdaddr} {
+ bdaddr_t *ba = malloc(sizeof(bdaddr_t));
+ str2ba(yytext, ba);
+ yylval.bdaddr = ba;
+ return BDADDR;
+ }
+
+{keyword} {
+ int keyword = rfcomm_find_keyword(rfcomm_keyword, yytext);
+ if (keyword != -1)
+ return keyword;
+
+ if (strncmp(yytext, "rfcomm", 6) == 0) {
+ yylval.number = atoi(yytext + 6);
+ return RFCOMM;
+ }
+
+ yylval.string = yytext;
+ return WORD;
+ }
+
+{linebreak} {
+ lineno++;
+ }
+
+. {
+ return *yytext;
+ }
+
+%%
+
+int yywrap(void)
+{
+ return 1;
+}
diff --git a/tools/parser.c b/tools/parser.c
new file mode 100644
index 0000000..e9e1b6c
--- /dev/null
+++ b/tools/parser.c
@@ -0,0 +1,1768 @@
+
+/* A Bison parser, made by GNU Bison 2.4.1. */
+
+/* Skeleton implementation for Bison's Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.4.1"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 0
+
+/* Push parsers. */
+#define YYPUSH 0
+
+/* Pull parsers. */
+#define YYPULL 1
+
+/* Using locations. */
+#define YYLSP_NEEDED 0
+
+
+
+/* Copy the first part of user declarations. */
+
+/* Line 189 of yacc.c */
+#line 1 "parser.y"
+
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+
+#include "kword.h"
+
+int yylex(void);
+int yyerror(char *s);
+
+struct rfcomm_opts *opts;
+
+
+
+/* Line 189 of yacc.c */
+#line 122 "tools/parser.c"
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ K_BIND = 258,
+ K_DEVICE = 259,
+ K_CHANNEL = 260,
+ K_COMMENT = 261,
+ K_YES = 262,
+ K_NO = 263,
+ NUMBER = 264,
+ RFCOMM = 265,
+ STRING = 266,
+ WORD = 267,
+ BDADDR = 268
+ };
+#endif
+/* Tokens. */
+#define K_BIND 258
+#define K_DEVICE 259
+#define K_CHANNEL 260
+#define K_COMMENT 261
+#define K_YES 262
+#define K_NO 263
+#define NUMBER 264
+#define RFCOMM 265
+#define STRING 266
+#define WORD 267
+#define BDADDR 268
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+
+/* Line 214 of yacc.c */
+#line 49 "parser.y"
+
+ int number;
+ char *string;
+ bdaddr_t *bdaddr;
+
+
+
+/* Line 214 of yacc.c */
+#line 192 "tools/parser.c"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+/* Copy the second part of user declarations. */
+
+
+/* Line 264 of yacc.c */
+#line 204 "tools/parser.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int yyi)
+#else
+static int
+YYID (yyi)
+ int yyi;
+#endif
+{
+ return yyi;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined _STDLIB_H \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss_alloc;
+ YYSTYPE yyvs_alloc;
+};
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (YYID (0))
+# endif
+# endif
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \
+ Stack = &yyptr->Stack_alloc; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (YYID (0))
+
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 8
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 40
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 17
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 8
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 20
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 33
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 268
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 16,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 14, 2, 15, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const yytype_uint8 yyprhs[] =
+{
+ 0, 0, 3, 4, 6, 9, 14, 19, 21, 23,
+ 25, 27, 30, 33, 37, 40, 43, 46, 49, 51,
+ 53
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int8 yyrhs[] =
+{
+ 18, 0, -1, -1, 19, -1, 18, 19, -1, 20,
+ 14, 22, 15, -1, 21, 14, 22, 15, -1, 12,
+ -1, 1, -1, 12, -1, 10, -1, 23, 16, -1,
+ 1, 16, -1, 22, 23, 16, -1, 3, 24, -1,
+ 4, 13, -1, 5, 9, -1, 6, 11, -1, 12,
+ -1, 7, -1, 8, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const yytype_uint8 yyrline[] =
+{
+ 0, 66, 66, 67, 68, 71, 72, 73, 76, 83,
+ 89, 98, 99, 100, 103, 108, 113, 118, 123, 129,
+ 130
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "K_BIND", "K_DEVICE", "K_CHANNEL",
+ "K_COMMENT", "K_YES", "K_NO", "NUMBER", "RFCOMM", "STRING", "WORD",
+ "BDADDR", "'{'", "'}'", "';'", "$accept", "config", "statement",
+ "section", "rfcomm", "rfcomm_options", "rfcomm_option", "bool", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 123, 125, 59
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 17, 18, 18, 18, 19, 19, 19, 19, 20,
+ 21, 22, 22, 22, 23, 23, 23, 23, 23, 24,
+ 24
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 0, 1, 2, 4, 4, 1, 1, 1,
+ 1, 2, 2, 3, 2, 2, 2, 2, 1, 1,
+ 1
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+ STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint8 yydefact[] =
+{
+ 0, 8, 10, 7, 0, 3, 0, 0, 1, 4,
+ 0, 0, 0, 0, 0, 0, 0, 18, 0, 0,
+ 0, 12, 19, 20, 14, 15, 16, 17, 5, 0,
+ 11, 6, 13
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int8 yydefgoto[] =
+{
+ -1, 4, 5, 6, 7, 18, 19, 24
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -7
+static const yytype_int8 yypact[] =
+{
+ 10, -7, -7, -6, 14, -7, 4, 7, -7, -7,
+ 24, 24, 15, 25, 21, 26, 12, -7, -3, 22,
+ 1, -7, -7, -7, -7, -7, -7, -7, -7, 23,
+ -7, -7, -7
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int8 yypgoto[] =
+{
+ -7, -7, 33, -7, -7, 29, -1, -7
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If zero, do what YYDEFACT says.
+ If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -10
+static const yytype_int8 yytable[] =
+{
+ 13, 14, 15, 16, 13, 14, 15, 16, -9, 17,
+ -2, 1, 28, 17, 8, 1, 31, 29, 10, 29,
+ 2, 11, 3, 27, 2, 12, 3, 13, 14, 15,
+ 16, 21, 22, 23, 25, 26, 17, 9, 30, 32,
+ 20
+};
+
+static const yytype_uint8 yycheck[] =
+{
+ 3, 4, 5, 6, 3, 4, 5, 6, 14, 12,
+ 0, 1, 15, 12, 0, 1, 15, 18, 14, 20,
+ 10, 14, 12, 11, 10, 1, 12, 3, 4, 5,
+ 6, 16, 7, 8, 13, 9, 12, 4, 16, 16,
+ 11
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 1, 10, 12, 18, 19, 20, 21, 0, 19,
+ 14, 14, 1, 3, 4, 5, 6, 12, 22, 23,
+ 22, 16, 7, 8, 24, 13, 9, 11, 15, 23,
+ 16, 15, 16
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+
+#define YYFAIL goto yyerrlab
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ yytoken = YYTRANSLATE (yychar); \
+ YYPOPSTACK (1); \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (YYID (N)) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (YYID (0))
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+ This macro was not mandated originally: define only if we know
+ we won't break user code: when these are the locations we know. */
+
+#ifndef YY_LOCATION_PRINT
+# if YYLTYPE_IS_TRIVIAL
+# define YY_LOCATION_PRINT(File, Loc) \
+ fprintf (File, "%d.%d-%d.%d", \
+ (Loc).first_line, (Loc).first_column, \
+ (Loc).last_line, (Loc).last_column)
+# else
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (!yyvaluep)
+ return;
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+#else
+static void
+yy_stack_print (yybottom, yytop)
+ yytype_int16 *yybottom;
+ yytype_int16 *yytop;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; yybottom <= yytop; yybottom++)
+ {
+ int yybot = *yybottom;
+ YYFPRINTF (stderr, " %d", yybot);
+ }
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule)
+ YYSTYPE *yyvsp;
+ int yyrule;
+#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ YYFPRINTF (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ );
+ YYFPRINTF (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into YYRESULT an error message about the unexpected token
+ YYCHAR while in state YYSTATE. Return the number of bytes copied,
+ including the terminating null byte. If YYRESULT is null, do not
+ copy anything; just return the number of bytes that would be
+ copied. As a special case, return 0 if an ordinary "syntax error"
+ message will do. Return YYSIZE_MAXIMUM if overflow occurs during
+ size calculation. */
+static YYSIZE_T
+yysyntax_error (char *yyresult, int yystate, int yychar)
+{
+ int yyn = yypact[yystate];
+
+ if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
+ return 0;
+ else
+ {
+ int yytype = YYTRANSLATE (yychar);
+ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ int yysize_overflow = 0;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ int yyx;
+
+# if 0
+ /* This is so xgettext sees the translatable formats that are
+ constructed on the fly. */
+ YY_("syntax error, unexpected %s");
+ YY_("syntax error, unexpected %s, expecting %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
+# endif
+ char *yyfmt;
+ char const *yyf;
+ static char const yyunexpected[] = "syntax error, unexpected %s";
+ static char const yyexpecting[] = ", expecting %s";
+ static char const yyor[] = " or %s";
+ char yyformat[sizeof yyunexpected
+ + sizeof yyexpecting - 1
+ + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
+ * (sizeof yyor - 1))];
+ char const *yyprefix = yyexpecting;
+
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yycount = 1;
+
+ yyarg[0] = yytname[yytype];
+ yyfmt = yystpcpy (yyformat, yyunexpected);
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ yyformat[sizeof yyunexpected - 1] = '\0';
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+ yyfmt = yystpcpy (yyfmt, yyprefix);
+ yyprefix = yyor;
+ }
+
+ yyf = YY_(yyformat);
+ yysize1 = yysize + yystrlen (yyf);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+
+ if (yysize_overflow)
+ return YYSIZE_MAXIMUM;
+
+ if (yyresult)
+ {
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ char *yyp = yyresult;
+ int yyi = 0;
+ while ((*yyp = *yyf) != '\0')
+ {
+ if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyf += 2;
+ }
+ else
+ {
+ yyp++;
+ yyf++;
+ }
+ }
+ }
+ return yysize;
+ }
+}
+#endif /* YYERROR_VERBOSE */
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ YYUSE (yyvaluep);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+/* Prevent warnings from -Wmissing-prototypes. */
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+/* The lookahead symbol. */
+int yychar;
+
+/* The semantic value of the lookahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+
+
+/*-------------------------.
+| yyparse or yypush_parse. |
+`-------------------------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+
+
+ int yystate;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+
+ /* The stacks and their tools:
+ `yyss': related to states.
+ `yyvs': related to semantic values.
+
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs;
+ YYSTYPE *yyvsp;
+
+ YYSIZE_T yystacksize;
+
+ int yyn;
+ int yyresult;
+ /* Lookahead token as an internal (translated) token number. */
+ int yytoken;
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ yytoken = 0;
+ yyss = yyssa;
+ yyvs = yyvsa;
+ yystacksize = YYINITDEPTH;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+ yyssp = yyss;
+ yyvsp = yyvs;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss_alloc, yyss);
+ YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ if (yystate == YYFINAL)
+ YYACCEPT;
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ lookahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to lookahead token. */
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the lookahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token. */
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ *++yyvsp = yylval;
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 7:
+
+/* Line 1455 of yacc.c */
+#line 74 "parser.y"
+ {
+ }
+ break;
+
+ case 8:
+
+/* Line 1455 of yacc.c */
+#line 77 "parser.y"
+ {
+ yyclearin;
+ yyerrok;
+ }
+ break;
+
+ case 9:
+
+/* Line 1455 of yacc.c */
+#line 84 "parser.y"
+ {
+ opts = NULL;
+ }
+ break;
+
+ case 10:
+
+/* Line 1455 of yacc.c */
+#line 90 "parser.y"
+ {
+ if (((yyvsp[(1) - (1)].number) >= 0) && ((yyvsp[(1) - (1)].number) < RFCOMM_MAX_DEV))
+ opts = &rfcomm_opts[(yyvsp[(1) - (1)].number)];
+ else
+ opts = NULL;
+ }
+ break;
+
+ case 14:
+
+/* Line 1455 of yacc.c */
+#line 104 "parser.y"
+ {
+ if (opts)
+ opts->bind = (yyvsp[(2) - (2)].number);
+ }
+ break;
+
+ case 15:
+
+/* Line 1455 of yacc.c */
+#line 109 "parser.y"
+ {
+ if (opts)
+ bacpy(&opts->bdaddr, (yyvsp[(2) - (2)].bdaddr));
+ }
+ break;
+
+ case 16:
+
+/* Line 1455 of yacc.c */
+#line 114 "parser.y"
+ {
+ if (opts)
+ opts->channel = (yyvsp[(2) - (2)].number);
+ }
+ break;
+
+ case 17:
+
+/* Line 1455 of yacc.c */
+#line 119 "parser.y"
+ {
+ if (opts)
+ snprintf(opts->comment, MAXCOMMENTLEN, "%s", (yyvsp[(2) - (2)].string));
+ }
+ break;
+
+ case 18:
+
+/* Line 1455 of yacc.c */
+#line 124 "parser.y"
+ {
+ // Unknown option
+ }
+ break;
+
+ case 19:
+
+/* Line 1455 of yacc.c */
+#line 129 "parser.y"
+ { (yyval.number) = 1; }
+ break;
+
+ case 20:
+
+/* Line 1455 of yacc.c */
+#line 130 "parser.y"
+ { (yyval.number) = 0; }
+ break;
+
+
+
+/* Line 1455 of yacc.c */
+#line 1517 "tools/parser.c"
+ default: break;
+ }
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (YY_("syntax error"));
+#else
+ {
+ YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
+ if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
+ {
+ YYSIZE_T yyalloc = 2 * yysize;
+ if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
+ yyalloc = YYSTACK_ALLOC_MAXIMUM;
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yyalloc);
+ if (yymsg)
+ yymsg_alloc = yyalloc;
+ else
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ }
+ }
+
+ if (0 < yysize && yysize <= yymsg_alloc)
+ {
+ (void) yysyntax_error (yymsg, yystate, yychar);
+ yyerror (yymsg);
+ }
+ else
+ {
+ yyerror (YY_("syntax error"));
+ if (yysize != 0)
+ goto yyexhaustedlab;
+ }
+ }
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse lookahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse lookahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (yyn != YYPACT_NINF)
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ *++yyvsp = yylval;
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#if !defined(yyoverflow) || YYERROR_VERBOSE
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEMPTY)
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval);
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ /* Make sure YYID is used. */
+ return YYID (yyresult);
+}
+
+
+
+/* Line 1675 of yacc.c */
+#line 133 "parser.y"
+
+
+int yyerror(char *s)
+{
+ fprintf(stderr, "%s line %d\n", s, lineno);
+ return 0;
+}
+
+int rfcomm_read_config(char *filename)
+{
+ extern FILE *yyin;
+ char file[MAXPATHLEN + 1];
+ int i;
+
+ for (i = 0; i < RFCOMM_MAX_DEV; i++) {
+ rfcomm_opts[i].bind = 0;
+ bacpy(&rfcomm_opts[i].bdaddr, BDADDR_ANY);
+ rfcomm_opts[i].channel = 1;
+ }
+
+ if (filename) {
+ snprintf(file, MAXPATHLEN, "%s", filename);
+ } else {
+ snprintf(file, MAXPATHLEN, "%s/.bluetooth/rfcomm.conf", getenv("HOME"));
+
+ if ((getuid() == 0) || (access(file, R_OK) < 0))
+ snprintf(file, MAXPATHLEN, "%s/rfcomm.conf", CONFIGDIR);
+ }
+
+ if (!(yyin = fopen(file, "r")))
+ return -1;
+
+ lineno = 1;
+ yyparse();
+
+ fclose(yyin);
+
+ return 0;
+}
+
diff --git a/tools/parser.h b/tools/parser.h
new file mode 100644
index 0000000..2c444d6
--- /dev/null
+++ b/tools/parser.h
@@ -0,0 +1,94 @@
+
+/* A Bison parser, made by GNU Bison 2.4.1. */
+
+/* Skeleton interface for Bison's Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ K_BIND = 258,
+ K_DEVICE = 259,
+ K_CHANNEL = 260,
+ K_COMMENT = 261,
+ K_YES = 262,
+ K_NO = 263,
+ NUMBER = 264,
+ RFCOMM = 265,
+ STRING = 266,
+ WORD = 267,
+ BDADDR = 268
+ };
+#endif
+/* Tokens. */
+#define K_BIND 258
+#define K_DEVICE 259
+#define K_CHANNEL 260
+#define K_COMMENT 261
+#define K_YES 262
+#define K_NO 263
+#define NUMBER 264
+#define RFCOMM 265
+#define STRING 266
+#define WORD 267
+#define BDADDR 268
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+
+/* Line 1676 of yacc.c */
+#line 49 "parser.y"
+
+ int number;
+ char *string;
+ bdaddr_t *bdaddr;
+
+
+
+/* Line 1676 of yacc.c */
+#line 86 "tools/parser.h"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+extern YYSTYPE yylval;
+
+
diff --git a/tools/parser.y b/tools/parser.y
new file mode 100644
index 0000000..96e6a56
--- /dev/null
+++ b/tools/parser.y
@@ -0,0 +1,171 @@
+%{
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+
+#include "kword.h"
+
+int yylex(void);
+int yyerror(char *s);
+
+struct rfcomm_opts *opts;
+
+%}
+
+%union {
+ int number;
+ char *string;
+ bdaddr_t *bdaddr;
+}
+
+%token K_BIND K_DEVICE K_CHANNEL K_COMMENT
+%token K_YES K_NO
+
+%token <number> NUMBER RFCOMM
+%token <string> STRING WORD
+%token <bdaddr> BDADDR
+
+%type <number> bool
+
+%%
+
+config :
+ | statement
+ | config statement
+ ;
+
+statement : section '{' rfcomm_options '}'
+ | rfcomm '{' rfcomm_options '}'
+ | WORD
+ {
+ }
+ | error
+ {
+ yyclearin;
+ yyerrok;
+ }
+ ;
+
+section : WORD
+ {
+ opts = NULL;
+ }
+ ;
+
+rfcomm : RFCOMM
+ {
+ if (($1 >= 0) && ($1 < RFCOMM_MAX_DEV))
+ opts = &rfcomm_opts[$1];
+ else
+ opts = NULL;
+ }
+ ;
+
+rfcomm_options : rfcomm_option ';'
+ | error ';'
+ | rfcomm_options rfcomm_option ';'
+ ;
+
+rfcomm_option : K_BIND bool
+ {
+ if (opts)
+ opts->bind = $2;
+ }
+ | K_DEVICE BDADDR
+ {
+ if (opts)
+ bacpy(&opts->bdaddr, $2);
+ }
+ | K_CHANNEL NUMBER
+ {
+ if (opts)
+ opts->channel = $2;
+ }
+ | K_COMMENT STRING
+ {
+ if (opts)
+ snprintf(opts->comment, MAXCOMMENTLEN, "%s", $2);
+ }
+ | WORD
+ {
+ // Unknown option
+ }
+ ;
+
+bool : K_YES { $$ = 1; }
+ | K_NO { $$ = 0; }
+ ;
+
+%%
+
+int yyerror(char *s)
+{
+ fprintf(stderr, "%s line %d\n", s, lineno);
+ return 0;
+}
+
+int rfcomm_read_config(char *filename)
+{
+ extern FILE *yyin;
+ char file[MAXPATHLEN + 1];
+ int i;
+
+ for (i = 0; i < RFCOMM_MAX_DEV; i++) {
+ rfcomm_opts[i].bind = 0;
+ bacpy(&rfcomm_opts[i].bdaddr, BDADDR_ANY);
+ rfcomm_opts[i].channel = 1;
+ }
+
+ if (filename) {
+ snprintf(file, MAXPATHLEN, "%s", filename);
+ } else {
+ snprintf(file, MAXPATHLEN, "%s/.bluetooth/rfcomm.conf", getenv("HOME"));
+
+ if ((getuid() == 0) || (access(file, R_OK) < 0))
+ snprintf(file, MAXPATHLEN, "%s/rfcomm.conf", CONFIGDIR);
+ }
+
+ if (!(yyin = fopen(file, "r")))
+ return -1;
+
+ lineno = 1;
+ yyparse();
+
+ fclose(yyin);
+
+ return 0;
+}
diff --git a/tools/ppporc.c b/tools/ppporc.c
new file mode 100644
index 0000000..ca44b40
--- /dev/null
+++ b/tools/ppporc.c
@@ -0,0 +1,271 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <syslog.h>
+#include <getopt.h>
+#include <sys/poll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+
+/* IO cancelation */
+static volatile sig_atomic_t __io_canceled;
+
+static inline void io_init(void)
+{
+ __io_canceled = 0;
+}
+
+static inline void io_cancel(void)
+{
+ __io_canceled = 1;
+}
+
+/* Signal functions */
+static void sig_hup(int sig)
+{
+ return;
+}
+
+static void sig_term(int sig)
+{
+ syslog(LOG_INFO, "Closing RFCOMM channel");
+ io_cancel();
+}
+
+/* Read exactly len bytes (Signal safe)*/
+static inline int read_n(int fd, char *buf, int len)
+{
+ register int t = 0, w;
+
+ while (!__io_canceled && len > 0) {
+ if ((w = read(fd, buf, len)) < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ return -1;
+ }
+ if (!w)
+ return 0;
+ len -= w;
+ buf += w;
+ t += w;
+ }
+
+ return t;
+}
+
+/* Write exactly len bytes (Signal safe)*/
+static inline int write_n(int fd, char *buf, int len)
+{
+ register int t = 0, w;
+
+ while (!__io_canceled && len > 0) {
+ if ((w = write(fd, buf, len)) < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ return -1;
+ }
+ if (!w)
+ return 0;
+ len -= w;
+ buf += w;
+ t += w;
+ }
+
+ return t;
+}
+
+/* Create the RFCOMM connection */
+static int create_connection(bdaddr_t *bdaddr, uint8_t channel)
+{
+ struct sockaddr_rc remote_addr, local_addr;
+ int fd, err;
+
+ if ((fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0)
+ return fd;
+
+ memset(&local_addr, 0, sizeof(local_addr));
+ local_addr.rc_family = AF_BLUETOOTH;
+ bacpy(&local_addr.rc_bdaddr, BDADDR_ANY);
+ if ((err = bind(fd, (struct sockaddr *)&local_addr, sizeof(local_addr))) < 0) {
+ close(fd);
+ return err;
+ }
+
+ memset(&remote_addr, 0, sizeof(remote_addr));
+ remote_addr.rc_family = AF_BLUETOOTH;
+ bacpy(&remote_addr.rc_bdaddr, bdaddr);
+ remote_addr.rc_channel = channel;
+ if ((err = connect(fd, (struct sockaddr *)&remote_addr, sizeof(remote_addr))) < 0) {
+ close(fd);
+ return err;
+ }
+
+ syslog(LOG_INFO, "RFCOMM channel %d connected", channel);
+
+ return fd;
+}
+
+/* Process the data from socket and pseudo tty */
+static int process_data(int fd)
+{
+ struct pollfd p[2];
+ char buf[1024];
+ int err, r;
+
+ p[0].fd = 0;
+ p[0].events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
+
+ p[1].fd = fd;
+ p[1].events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
+
+ err = 0;
+
+ while (!__io_canceled) {
+ p[0].revents = 0;
+ p[1].revents = 0;
+
+ err = poll(p, 2, -1);
+ if (err < 0)
+ break;
+
+ err = 0;
+
+ if (p[0].revents) {
+ if (p[0].revents & (POLLERR | POLLHUP | POLLNVAL))
+ break;
+ r = read(0, buf, sizeof(buf));
+ if (r < 0) {
+ if (errno != EINTR && errno != EAGAIN) {
+ err = r;
+ break;
+ }
+ }
+
+ err = write_n(fd, buf, r);
+ if (err < 0)
+ break;
+ }
+
+ if (p[1].revents) {
+ if (p[1].revents & (POLLERR | POLLHUP | POLLNVAL))
+ break;
+ r = read(fd, buf, sizeof(buf));
+ if (r < 0) {
+ if (errno != EINTR && errno != EAGAIN) {
+ err = r;
+ break;
+ }
+ }
+
+ err = write_n(1, buf, r);
+ if (err < 0)
+ break;
+ }
+ }
+
+ return err;
+}
+
+static void usage(void)
+{
+ printf("Usage:\tppporc <bdaddr> [channel]\n");
+}
+
+int main(int argc, char** argv)
+{
+ struct sigaction sa;
+ int fd, err, opt;
+
+ bdaddr_t bdaddr;
+ uint8_t channel;
+
+ /* Parse command line options */
+ while ((opt = getopt(argc, argv, "h")) != EOF) {
+ switch(opt) {
+ case 'h':
+ usage();
+ exit(0);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ switch (argc) {
+ case 1:
+ str2ba(argv[0], &bdaddr);
+ channel = 1;
+ break;
+ case 2:
+ str2ba(argv[0], &bdaddr);
+ channel = atoi(argv[1]);
+ break;
+ default:
+ usage();
+ exit(0);
+ }
+
+ /* Initialize syslog */
+ openlog("ppporc", LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON);
+ syslog(LOG_INFO, "PPP over RFCOMM");
+
+ /* Initialize signals */
+ 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);
+
+ syslog(LOG_INFO, "Connecting to %s", argv[0]);
+
+ if ((fd = create_connection(&bdaddr, channel)) < 0) {
+ syslog(LOG_ERR, "Can't connect to remote device (%s)", strerror(errno));
+ return fd;
+ }
+
+ err = process_data(fd);
+
+ close(fd);
+
+ return err;
+}
diff --git a/tools/rfcomm.1 b/tools/rfcomm.1
new file mode 100644
index 0000000..06252e5
--- /dev/null
+++ b/tools/rfcomm.1
@@ -0,0 +1,137 @@
+.\"
+.\" 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 RFCOMM 1 "APRIL 28, 2002" "" ""
+
+.SH NAME
+rfcomm \- RFCOMM configuration utility
+.SH SYNOPSIS
+.BR "rfcomm
+[
+.I options
+] <
+.I command
+> <
+.I dev
+>
+.SH DESCRIPTION
+.B rfcomm
+is used to set up, maintain, and inspect the RFCOMM configuration
+of the Bluetooth subsystem in the Linux kernel. If no
+.B command
+is given, or if the option
+.B -a
+is used,
+.B rfcomm
+prints information about the configured RFCOMM devices.
+.SH OPTIONS
+.TP
+.BI -h
+Gives a list of possible commands.
+.TP
+.BI -a
+Prints information about all configured RFCOMM devices.
+.TP
+.BI -r
+Switch TTY into raw mode (doesn't work with "bind").
+.TP
+.BI -f " <file>"
+Specify alternate config file.
+.TP
+.BI -i " <hciX> | <bdaddr>"
+The command is applied to device
+.BI -A
+Enable authentication.
+.BI -E
+Enable encryption.
+.BI -S
+Secure connection.
+.BI -M
+Become the master of a piconet.
+.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.
+.TP
+.BI -A
+Enable authentification
+.TP
+.BI -E
+Enable encryption
+.TP
+.BI -S
+Secure connection
+.TP
+.BI -M
+Become the master of a piconet
+.TP
+.BI -L " <seconds>"
+Set linger timeout
+.SH COMMANDS
+.TP
+.BI show " <dev>"
+Display the information about the specified device.
+.TP
+.BI connect " <dev> [bdaddr] [channel]"
+Connect the RFCOMM device to the remote Bluetooth device on the
+specified channel. If no channel is specified, it will use the
+channel number 1. If also the Bluetooth address is left out, it
+tries to read the data from the config file. This command can
+be terminated with the key sequence CTRL-C.
+.TP
+.BI listen " <dev> [channel] [cmd]"
+Listen on a specified RFCOMM channel for incoming connections.
+If no channel is specified, it will use the channel number 1, but
+a channel must be specified before cmd. If cmd is given, it will be
+executed as soon as a client connects. When the child process
+terminates or the client disconnect, the command will terminate.
+Occurences of {} in cmd will be replaced by the name of the device
+used by the connection. This command can be terminated with the key
+sequence CTRL-C.
+.TP
+.BI watch " <dev> [channel] [cmd]"
+Watch is identical to
+.B listen
+except that when the child process terminates or the client
+disconnect, the command will restart listening with the same
+parameters.
+.TP
+.BI bind " <dev> [bdaddr] [channel]"
+This binds the RFCOMM device to a remote Bluetooth device. The
+command did not establish a connection to the remote device, it
+only creates the binding. The connection will be established right
+after an application tries to open the RFCOMM device. If no channel
+number is specified, it uses the channel number 1. If the Bluetooth
+address is also left out, it tries to read the data from the config
+file.
+
+If
+.B all
+is specified for the RFCOMM device, then all devices that have
+.B "bind yes"
+set in the config will be bound.
+.TP
+.BI release " <dev>"
+This command releases a defined RFCOMM binding.
+
+If
+.B all
+is specified for the RFCOMM device, then all bindings will be removed.
+This command didn't care about the settings in the config file.
+.SH AUTHOR
+Written by Marcel Holtmann <marcel@holtmann.org>.
+.br
diff --git a/tools/rfcomm.c b/tools/rfcomm.c
new file mode 100644
index 0000000..6800445
--- /dev/null
+++ b/tools/rfcomm.c
@@ -0,0 +1,849 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <signal.h>
+#include <termios.h>
+#include <sys/poll.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/rfcomm.h>
+
+#include "kword.h"
+
+#ifdef NEED_PPOLL
+#include "ppoll.h"
+#endif
+
+static char *rfcomm_config_file = NULL;
+static int rfcomm_raw_tty = 0;
+static int auth = 0;
+static int encryption = 0;
+static int secure = 0;
+static int master = 0;
+static int linger = 0;
+
+static char *rfcomm_state[] = {
+ "unknown",
+ "connected",
+ "clean",
+ "bound",
+ "listening",
+ "connecting",
+ "connecting",
+ "config",
+ "disconnecting",
+ "closed"
+};
+
+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 *rfcomm_flagstostr(uint32_t flags)
+{
+ static char str[100];
+ str[0] = 0;
+
+ strcat(str, "[");
+
+ if (flags & (1 << RFCOMM_REUSE_DLC))
+ strcat(str, "reuse-dlc ");
+
+ if (flags & (1 << RFCOMM_RELEASE_ONHUP))
+ strcat(str, "release-on-hup ");
+
+ if (flags & (1 << RFCOMM_TTY_ATTACHED))
+ strcat(str, "tty-attached");
+
+ strcat(str, "]");
+ return str;
+}
+
+static void print_dev_info(struct rfcomm_dev_info *di)
+{
+ char src[18], dst[18], addr[40];
+
+ ba2str(&di->src, src); ba2str(&di->dst, dst);
+
+ if (bacmp(&di->src, BDADDR_ANY) == 0)
+ sprintf(addr, "%s", dst);
+ else
+ sprintf(addr, "%s -> %s", src, dst);
+
+ printf("rfcomm%d: %s channel %d %s %s\n",
+ di->id, addr, di->channel,
+ rfcomm_state[di->state],
+ di->flags ? rfcomm_flagstostr(di->flags) : "");
+}
+
+static void print_dev_list(int ctl, int flags)
+{
+ struct rfcomm_dev_list_req *dl;
+ struct rfcomm_dev_info *di;
+ int i;
+
+ dl = malloc(sizeof(*dl) + RFCOMM_MAX_DEV * sizeof(*di));
+ if (!dl) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+
+ dl->dev_num = RFCOMM_MAX_DEV;
+ di = dl->dev_info;
+
+ if (ioctl(ctl, RFCOMMGETDEVLIST, (void *) dl) < 0) {
+ perror("Can't get device list");
+ free(dl);
+ exit(1);
+ }
+
+ for (i = 0; i < dl->dev_num; i++)
+ print_dev_info(di + i);
+ free(dl);
+}
+
+static int create_dev(int ctl, int dev, uint32_t flags, bdaddr_t *bdaddr, int argc, char **argv)
+{
+ struct rfcomm_dev_req req;
+ int err;
+
+ memset(&req, 0, sizeof(req));
+ req.dev_id = dev;
+ req.flags = flags;
+ bacpy(&req.src, bdaddr);
+
+ if (argc < 2) {
+ err = rfcomm_read_config(rfcomm_config_file);
+ if (err < 0) {
+ perror("Can't open RFCOMM config file");
+ return err;
+ }
+
+ bacpy(&req.dst, &rfcomm_opts[dev].bdaddr);
+ req.channel = rfcomm_opts[dev].channel;
+
+ if (bacmp(&req.dst, BDADDR_ANY) == 0) {
+ fprintf(stderr, "Can't find a config entry for rfcomm%d\n", dev);
+ return -EFAULT;
+ }
+ } else {
+ str2ba(argv[1], &req.dst);
+
+ if (argc > 2)
+ req.channel = atoi(argv[2]);
+ else
+ req.channel = 1;
+ }
+
+ err = ioctl(ctl, RFCOMMCREATEDEV, &req);
+ if (err == EOPNOTSUPP)
+ fprintf(stderr, "RFCOMM TTY support not available\n");
+ else if (err < 0)
+ perror("Can't create device");
+
+ return err;
+}
+
+static int create_all(int ctl)
+{
+ struct rfcomm_dev_req req;
+ int i, err;
+
+ err = rfcomm_read_config(rfcomm_config_file);
+ if (err < 0) {
+ perror("Can't open RFCOMM config file");
+ return err;
+ }
+
+ for (i = 0; i < RFCOMM_MAX_DEV; i++) {
+ if (!rfcomm_opts[i].bind)
+ continue;
+
+ memset(&req, 0, sizeof(req));
+ req.dev_id = i;
+ req.flags = 0;
+ bacpy(&req.src, BDADDR_ANY);
+ bacpy(&req.dst, &rfcomm_opts[i].bdaddr);
+ req.channel = rfcomm_opts[i].channel;
+
+ if (bacmp(&req.dst, BDADDR_ANY) != 0)
+ ioctl(ctl, RFCOMMCREATEDEV, &req);
+ }
+
+ return 0;
+}
+
+static int release_dev(int ctl, int dev, uint32_t flags)
+{
+ struct rfcomm_dev_req req;
+ int err;
+
+ memset(&req, 0, sizeof(req));
+ req.dev_id = dev;
+
+ err = ioctl(ctl, RFCOMMRELEASEDEV, &req);
+ if (err < 0)
+ perror("Can't release device");
+
+ return err;
+}
+
+static int release_all(int ctl)
+{
+ struct rfcomm_dev_list_req *dl;
+ struct rfcomm_dev_info *di;
+ int i;
+
+ dl = malloc(sizeof(*dl) + RFCOMM_MAX_DEV * sizeof(*di));
+ if (!dl) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+
+ dl->dev_num = RFCOMM_MAX_DEV;
+ di = dl->dev_info;
+
+ if (ioctl(ctl, RFCOMMGETDEVLIST, (void *) dl) < 0) {
+ perror("Can't get device list");
+ free(dl);
+ exit(1);
+ }
+
+ for (i = 0; i < dl->dev_num; i++)
+ release_dev(ctl, (di + i)->id, 0);
+
+ free(dl);
+ return 0;
+}
+
+static void run_cmdline(struct pollfd *p, sigset_t* sigs, char *devname,
+ int argc, char **argv)
+{
+ int i;
+ pid_t pid;
+ char **cmdargv;
+
+ cmdargv = malloc((argc + 1) * sizeof(char*));
+ if (!cmdargv)
+ return;
+
+ for (i = 0; i < argc; i++)
+ cmdargv[i] = (strcmp(argv[i], "{}") == 0) ? devname : argv[i];
+ cmdargv[i] = NULL;
+
+ pid = fork();
+
+ switch (pid) {
+ case 0:
+ i = execvp(cmdargv[0], cmdargv);
+ fprintf(stderr, "Couldn't execute command %s (errno=%d:%s)\n",
+ cmdargv[0], errno, strerror(errno));
+ break;
+ case -1:
+ fprintf(stderr, "Couldn't fork to execute command %s\n",
+ cmdargv[0]);
+ break;
+ default:
+ while (1) {
+ int status;
+ pid_t child;
+ struct timespec ts;
+
+ child = waitpid(-1, &status, WNOHANG);
+ if (child == pid || (child < 0 && errno != EAGAIN))
+ break;
+
+ p->revents = 0;
+ ts.tv_sec = 0;
+ ts.tv_nsec = 200;
+ if (ppoll(p, 1, &ts, sigs) || __io_canceled) {
+ kill(pid, SIGTERM);
+ waitpid(pid, &status, 0);
+ break;
+ }
+ }
+ break;
+ }
+
+ free(cmdargv);
+}
+
+static void cmd_connect(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv)
+{
+ struct sockaddr_rc laddr, raddr;
+ struct rfcomm_dev_req req;
+ struct termios ti;
+ struct sigaction sa;
+ struct pollfd p;
+ sigset_t sigs;
+ socklen_t alen;
+ char dst[18], devname[MAXPATHLEN];
+ int sk, fd, try = 30;
+
+ laddr.rc_family = AF_BLUETOOTH;
+ bacpy(&laddr.rc_bdaddr, bdaddr);
+ laddr.rc_channel = 0;
+
+ if (argc < 2) {
+ if (rfcomm_read_config(rfcomm_config_file) < 0) {
+ perror("Can't open RFCOMM config file");
+ return;
+ }
+
+ raddr.rc_family = AF_BLUETOOTH;
+ bacpy(&raddr.rc_bdaddr, &rfcomm_opts[dev].bdaddr);
+ raddr.rc_channel = rfcomm_opts[dev].channel;
+
+ if (bacmp(&raddr.rc_bdaddr, BDADDR_ANY) == 0) {
+ fprintf(stderr, "Can't find a config entry for rfcomm%d\n", dev);
+ return;
+ }
+ } else {
+ raddr.rc_family = AF_BLUETOOTH;
+ str2ba(argv[1], &raddr.rc_bdaddr);
+
+ if (argc > 2)
+ raddr.rc_channel = atoi(argv[2]);
+ else
+ raddr.rc_channel = 1;
+ }
+
+ sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+ if (sk < 0) {
+ perror("Can't create RFCOMM socket");
+ return;
+ }
+
+ if (linger) {
+ struct linger l = { .l_onoff = 1, .l_linger = linger };
+
+ if (setsockopt(sk, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) {
+ perror("Can't set linger option");
+ return;
+ }
+ }
+
+ if (bind(sk, (struct sockaddr *) &laddr, sizeof(laddr)) < 0) {
+ perror("Can't bind RFCOMM socket");
+ close(sk);
+ return;
+ }
+
+ if (connect(sk, (struct sockaddr *) &raddr, sizeof(raddr)) < 0) {
+ perror("Can't connect RFCOMM socket");
+ close(sk);
+ return;
+ }
+
+ alen = sizeof(laddr);
+ if (getsockname(sk, (struct sockaddr *)&laddr, &alen) < 0) {
+ perror("Can't get RFCOMM socket name");
+ close(sk);
+ return;
+ }
+
+ memset(&req, 0, sizeof(req));
+ req.dev_id = dev;
+ req.flags = (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP);
+
+ bacpy(&req.src, &laddr.rc_bdaddr);
+ bacpy(&req.dst, &raddr.rc_bdaddr);
+ req.channel = raddr.rc_channel;
+
+ dev = ioctl(sk, RFCOMMCREATEDEV, &req);
+ if (dev < 0) {
+ perror("Can't create RFCOMM TTY");
+ close(sk);
+ return;
+ }
+
+ snprintf(devname, MAXPATHLEN - 1, "/dev/rfcomm%d", dev);
+ while ((fd = open(devname, O_RDONLY | O_NOCTTY)) < 0) {
+ if (errno == EACCES) {
+ perror("Can't open RFCOMM device");
+ goto release;
+ }
+
+ snprintf(devname, MAXPATHLEN - 1, "/dev/bluetooth/rfcomm/%d", dev);
+ if ((fd = open(devname, O_RDONLY | O_NOCTTY)) < 0) {
+ if (try--) {
+ snprintf(devname, MAXPATHLEN - 1, "/dev/rfcomm%d", dev);
+ usleep(100 * 1000);
+ continue;
+ }
+ perror("Can't open RFCOMM device");
+ goto release;
+ }
+ }
+
+ if (rfcomm_raw_tty) {
+ tcflush(fd, TCIOFLUSH);
+
+ cfmakeraw(&ti);
+ tcsetattr(fd, TCSANOW, &ti);
+ }
+
+ close(sk);
+
+ ba2str(&req.dst, dst);
+ printf("Connected %s to %s on channel %d\n", devname, dst, req.channel);
+ 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 = fd;
+ p.events = POLLERR | POLLHUP;
+
+ while (!__io_canceled) {
+ p.revents = 0;
+ if (ppoll(&p, 1, NULL, &sigs) > 0)
+ break;
+ }
+
+ printf("Disconnected\n");
+
+ close(fd);
+ return;
+
+release:
+ memset(&req, 0, sizeof(req));
+ req.dev_id = dev;
+ req.flags = (1 << RFCOMM_HANGUP_NOW);
+ ioctl(ctl, RFCOMMRELEASEDEV, &req);
+
+ close(sk);
+}
+
+static void cmd_listen(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv)
+{
+ struct sockaddr_rc laddr, raddr;
+ struct rfcomm_dev_req req;
+ struct termios ti;
+ struct sigaction sa;
+ struct pollfd p;
+ sigset_t sigs;
+ socklen_t alen;
+ char dst[18], devname[MAXPATHLEN];
+ int sk, nsk, fd, lm, try = 30;
+
+ laddr.rc_family = AF_BLUETOOTH;
+ bacpy(&laddr.rc_bdaddr, bdaddr);
+ laddr.rc_channel = (argc < 2) ? 1 : atoi(argv[1]);
+
+ sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+ if (sk < 0) {
+ perror("Can't create RFCOMM socket");
+ return;
+ }
+
+ lm = 0;
+ if (master)
+ lm |= RFCOMM_LM_MASTER;
+ if (auth)
+ lm |= RFCOMM_LM_AUTH;
+ if (encryption)
+ lm |= RFCOMM_LM_ENCRYPT;
+ if (secure)
+ lm |= RFCOMM_LM_SECURE;
+
+ if (lm && setsockopt(sk, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) {
+ perror("Can't set RFCOMM link mode");
+ close(sk);
+ return;
+ }
+
+ if (bind(sk, (struct sockaddr *)&laddr, sizeof(laddr)) < 0) {
+ perror("Can't bind RFCOMM socket");
+ close(sk);
+ return;
+ }
+
+ printf("Waiting for connection on channel %d\n", laddr.rc_channel);
+
+ listen(sk, 10);
+
+ alen = sizeof(raddr);
+ nsk = accept(sk, (struct sockaddr *) &raddr, &alen);
+
+ alen = sizeof(laddr);
+ if (getsockname(nsk, (struct sockaddr *)&laddr, &alen) < 0) {
+ perror("Can't get RFCOMM socket name");
+ close(nsk);
+ return;
+ }
+
+ if (linger) {
+ struct linger l = { .l_onoff = 1, .l_linger = linger };
+
+ if (setsockopt(nsk, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) {
+ perror("Can't set linger option");
+ close(nsk);
+ return;
+ }
+ }
+
+ memset(&req, 0, sizeof(req));
+ req.dev_id = dev;
+ req.flags = (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP);
+
+ bacpy(&req.src, &laddr.rc_bdaddr);
+ bacpy(&req.dst, &raddr.rc_bdaddr);
+ req.channel = raddr.rc_channel;
+
+ dev = ioctl(nsk, RFCOMMCREATEDEV, &req);
+ if (dev < 0) {
+ perror("Can't create RFCOMM TTY");
+ close(sk);
+ return;
+ }
+
+ snprintf(devname, MAXPATHLEN - 1, "/dev/rfcomm%d", dev);
+ while ((fd = open(devname, O_RDONLY | O_NOCTTY)) < 0) {
+ if (errno == EACCES) {
+ perror("Can't open RFCOMM device");
+ goto release;
+ }
+
+ snprintf(devname, MAXPATHLEN - 1, "/dev/bluetooth/rfcomm/%d", dev);
+ if ((fd = open(devname, O_RDONLY | O_NOCTTY)) < 0) {
+ if (try--) {
+ snprintf(devname, MAXPATHLEN - 1, "/dev/rfcomm%d", dev);
+ usleep(100 * 1000);
+ continue;
+ }
+ perror("Can't open RFCOMM device");
+ goto release;
+ }
+ }
+
+ if (rfcomm_raw_tty) {
+ tcflush(fd, TCIOFLUSH);
+
+ cfmakeraw(&ti);
+ tcsetattr(fd, TCSANOW, &ti);
+ }
+
+ close(sk);
+ close(nsk);
+
+ ba2str(&req.dst, dst);
+ printf("Connection from %s to %s\n", dst, devname);
+ 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 = fd;
+ p.events = POLLERR | POLLHUP;
+
+ if (argc <= 2) {
+ while (!__io_canceled) {
+ p.revents = 0;
+ if (ppoll(&p, 1, NULL, &sigs) > 0)
+ break;
+ }
+ } else
+ run_cmdline(&p, &sigs, devname, argc - 2, argv + 2);
+
+ sa.sa_handler = NULL;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ printf("Disconnected\n");
+
+ close(fd);
+ return;
+
+release:
+ memset(&req, 0, sizeof(req));
+ req.dev_id = dev;
+ req.flags = (1 << RFCOMM_HANGUP_NOW);
+ ioctl(ctl, RFCOMMRELEASEDEV, &req);
+
+ close(sk);
+}
+
+static void cmd_watch(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv)
+{
+ while (!__io_canceled) {
+ cmd_listen(ctl, dev, bdaddr, argc, argv);
+ usleep(10000);
+ }
+}
+
+static void cmd_create(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv)
+{
+ if (strcmp(argv[0], "all") == 0)
+ create_all(ctl);
+ else
+ create_dev(ctl, dev, 0, bdaddr, argc, argv);
+}
+
+static void cmd_release(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv)
+{
+ if (strcmp(argv[0], "all") == 0)
+ release_all(ctl);
+ else
+ release_dev(ctl, dev, 0);
+}
+
+static void cmd_show(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv)
+{
+ if (strcmp(argv[0], "all") == 0)
+ print_dev_list(ctl, 0);
+ else {
+ struct rfcomm_dev_info di = { id: atoi(argv[0]) };
+ if (ioctl(ctl, RFCOMMGETDEVINFO, &di) < 0) {
+ perror("Get info failed");
+ exit(1);
+ }
+
+ print_dev_info(&di);
+ }
+}
+
+struct {
+ char *cmd;
+ char *alt;
+ void (*func)(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv);
+ char *opt;
+ char *doc;
+} command[] = {
+ { "bind", "create", cmd_create, "<dev> <bdaddr> [channel]", "Bind device" },
+ { "release", "unbind", cmd_release, "<dev>", "Release device" },
+ { "show", "info", cmd_show, "<dev>", "Show device" },
+ { "connect", "conn", cmd_connect, "<dev> <bdaddr> [channel]", "Connect device" },
+ { "listen", "server", cmd_listen, "<dev> [channel [cmd]]", "Listen" },
+ { "watch", "watch", cmd_watch, "<dev> [channel [cmd]]", "Watch" },
+ { NULL, NULL, NULL, 0, 0 }
+};
+
+static void usage(void)
+{
+ int i;
+
+ printf("RFCOMM configuration utility ver %s\n", VERSION);
+
+ printf("Usage:\n"
+ "\trfcomm [options] <command> <dev>\n"
+ "\n");
+
+ printf("Options:\n"
+ "\t-i [hciX|bdaddr] Local HCI device or BD Address\n"
+ "\t-h, --help Display help\n"
+ "\t-r, --raw Switch TTY into raw mode\n"
+ "\t-A, --auth Enable authentication\n"
+ "\t-E, --encrypt Enable encryption\n"
+ "\t-S, --secure Secure connection\n"
+ "\t-M, --master Become the master of a piconet\n"
+ "\t-f, --config [file] Specify alternate config file\n"
+ "\t-a Show all devices (default)\n"
+ "\n");
+
+ printf("Commands:\n");
+ for (i = 0; command[i].cmd; i++)
+ printf("\t%-8s %-24s\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' },
+ { "config", 1, 0, 'f' },
+ { "raw", 0, 0, 'r' },
+ { "auth", 0, 0, 'A' },
+ { "encrypt", 0, 0, 'E' },
+ { "secure", 0, 0, 'S' },
+ { "master", 0, 0, 'M' },
+ { "linger", 1, 0, 'L' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ bdaddr_t bdaddr;
+ int i, opt, ctl, dev_id, show_all = 0;
+
+ bacpy(&bdaddr, BDADDR_ANY);
+
+ while ((opt = getopt_long(argc, argv, "+i:f:rahAESML:", main_options, NULL)) != -1) {
+ switch(opt) {
+ case 'i':
+ if (strncmp(optarg, "hci", 3) == 0)
+ hci_devba(atoi(optarg + 3), &bdaddr);
+ else
+ str2ba(optarg, &bdaddr);
+ break;
+
+ case 'f':
+ rfcomm_config_file = strdup(optarg);
+ break;
+
+ case 'r':
+ rfcomm_raw_tty = 1;
+ break;
+
+ case 'a':
+ show_all = 1;
+ break;
+
+ case 'h':
+ usage();
+ exit(0);
+
+ case 'A':
+ auth = 1;
+ break;
+
+ case 'E':
+ encryption = 1;
+ break;
+
+ case 'S':
+ secure = 1;
+ break;
+
+ case 'M':
+ master = 1;
+ break;
+
+ case 'L':
+ linger = atoi(optarg);
+ break;
+
+ default:
+ exit(0);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (argc < 2) {
+ if (argc != 0) {
+ usage();
+ exit(1);
+ } else
+ show_all = 1;
+ }
+
+ ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_RFCOMM);
+ if (ctl < 0) {
+ perror("Can't open RFCOMM control socket");
+ exit(1);
+ }
+
+ if (show_all) {
+ print_dev_list(ctl, 0);
+ close(ctl);
+ exit(0);
+ }
+
+ if (strncmp(argv[1], "/dev/rfcomm", 11) == 0)
+ dev_id = atoi(argv[1] + 11);
+ else if (strncmp(argv[1], "rfcomm", 6) == 0)
+ dev_id = atoi(argv[1] + 6);
+ else
+ dev_id = atoi(argv[1]);
+
+ for (i = 0; command[i].cmd; i++) {
+ if (strncmp(command[i].cmd, argv[0], 4) && strncmp(command[i].alt, argv[0], 4))
+ continue;
+ argc--;
+ argv++;
+ command[i].func(ctl, dev_id, &bdaddr, argc, argv);
+ close(ctl);
+ exit(0);
+ }
+
+ usage();
+
+ close(ctl);
+
+ return 0;
+}
diff --git a/tools/rfcomm.conf b/tools/rfcomm.conf
new file mode 100644
index 0000000..6179ef7
--- /dev/null
+++ b/tools/rfcomm.conf
@@ -0,0 +1,17 @@
+#
+# RFCOMM configuration file.
+#
+
+#rfcomm0 {
+# # Automatically bind the device at startup
+# bind no;
+#
+# # Bluetooth address of the device
+# device 11:22:33:44:55:66;
+#
+# # RFCOMM channel for the connection
+# channel 1;
+#
+# # Description of the connection
+# comment "Example Bluetooth device";
+#}
diff --git a/tools/sdptool.1 b/tools/sdptool.1
new file mode 100644
index 0000000..0f100e2
--- /dev/null
+++ b/tools/sdptool.1
@@ -0,0 +1,130 @@
+.\" $Header$
+.\"
+.\" transcript compatibility for postscript use.
+.\"
+.\" synopsis: .P! <file.ps>
+.\"
+.de P!
+.fl
+\!!1 setgray
+.fl
+\\&.\"
+.fl
+\!!0 setgray
+.fl \" force out current output buffer
+\!!save /psv exch def currentpoint translate 0 0 moveto
+\!!/showpage{}def
+.fl \" prolog
+.sy sed -e 's/^/!/' \\$1\" bring in postscript file
+\!!psv restore
+.
+.de pF
+.ie \\*(f1 .ds f1 \\n(.f
+.el .ie \\*(f2 .ds f2 \\n(.f
+.el .ie \\*(f3 .ds f3 \\n(.f
+.el .ie \\*(f4 .ds f4 \\n(.f
+.el .tm ? font overflow
+.ft \\$1
+..
+.de fP
+.ie !\\*(f4 \{\
+. ft \\*(f4
+. ds f4\"
+' br \}
+.el .ie !\\*(f3 \{\
+. ft \\*(f3
+. ds f3\"
+' br \}
+.el .ie !\\*(f2 \{\
+. ft \\*(f2
+. ds f2\"
+' br \}
+.el .ie !\\*(f1 \{\
+. ft \\*(f1
+. ds f1\"
+' br \}
+.el .tm ? font underflow
+..
+.ds f1\"
+.ds f2\"
+.ds f3\"
+.ds f4\"
+'\" t
+.ta 8n 16n 24n 32n 40n 48n 56n 64n 72n
+.TH "sdptool" "1"
+.SH "NAME"
+sdptool \(em control and interrogate SDP servers
+.SH "SYNOPSIS"
+.PP
+\fBsdptool\fR [\fIoptions\fR] {\fIcommand\fR} [\fIcommand parameters\fR \&...]
+.SH "DESCRIPTION"
+.PP
+\fBsdptool\fR provides the interface for
+performing SDP queries on Bluetooth devices, and administering a
+local \fBsdpd\fR.
+.SH "COMMANDS"
+.PP
+The following commands are available. In all cases \fBbdaddr\fR
+specifies the device to search or browse. If \fIlocal\fP is used
+for \fBbdaddr\fP, then the local \fBsdpd\fR is searched.
+.PP
+Services are identified and manipulated with a 4-byte \fBrecord_handle\fP
+(NOT the service name). To find a service's \fBrecord_handle\fP, look for the
+"Service RecHandle" line in the \fBsearch\fP or \fBbrowse\fP results
+.IP "\fBsearch [--bdaddr bdaddr] [--tree] [--raw] [--xml] service_name\fP" 10
+Search for services..
+.IP "" 10
+Known service names are DID, SP, DUN, LAN, FAX, OPUSH,
+FTP, HS, HF, HFAG, SAP, NAP, GN, PANU, HCRP, HID, CIP,
+A2SRC, A2SNK, AVRCT, AVRTG, UDIUE, UDITE and SYNCML.
+.IP "\fBbrowse [--tree] [--raw] [--xml] [bdaddr]\fP" 10
+Browse all available services on the device
+specified by a Bluetooth address as a parameter.
+.IP "\fBrecords [--tree] [--raw] [--xml] bdaddr\fP" 10
+Retrieve all possible service records.
+.IP "\fBadd [ --handle=N --channel=N ]\fP" 10
+Add a service to the local
+\fBsdpd\fR.
+.IP "" 10
+You can specify a handle for this record using
+the \fB--handle\fP option.
+.IP "" 10
+You can specify a channel to add the service on
+using the \fB--channel\fP option.
+.IP "\fBdel record_handle\fP" 10
+Remove a service from the local
+\fBsdpd\fR.
+.IP "\fBget [--tree] [--raw] [--xml] [--bdaddr bdaddr] record_handle\fP" 10
+Retrieve a service from the local
+\fBsdpd\fR.
+.IP "\fBsetattr record_handle attrib_id attrib_value\fP" 10
+Set or add an attribute to an SDP record.
+
+.IP "\fBsetseq record_handle attrib_id attrib_values\fP" 10
+Set or add an attribute sequence to an
+SDP record.
+.SH "OPTIONS"
+.IP "\fB--help\fP" 10
+Displays help on using sdptool.
+
+.SH "EXAMPLES"
+.PP
+sdptool browse 00:80:98:24:15:6D
+.PP
+sdptool browse local
+.PP
+sdptool add DUN
+.PP
+sdptool del 0x10000
+.SH "BUGS"
+.PP
+Documentation needs improving.
+.SH "AUTHOR"
+.PP
+Maxim Krasnyansky <maxk@qualcomm.com>. Man page written
+by Edd Dumbill <ejad@debian.org>.
+
+.SH "SEE ALSO"
+.PP
+sdpd(8)
+.\" created by instant / docbook-to-man, Thu 15 Jan 2004, 21:01
diff --git a/tools/sdptool.c b/tools/sdptool.c
new file mode 100644
index 0000000..140a46a
--- /dev/null
+++ b/tools/sdptool.c
@@ -0,0 +1,4274 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2001-2002 Nokia Corporation
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2002-2003 Stephen Crane <steve.crane@rococosoft.com>
+ * Copyright (C) 2002-2003 Jean Tourrilhes <jt@hpl.hp.com>
+ *
+ *
+ * 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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <netinet/in.h>
+
+#include "sdp-xml.h"
+
+#ifndef APPLE_AGENT_SVCLASS_ID
+#define APPLE_AGENT_SVCLASS_ID 0x2112
+#endif
+
+#define for_each_opt(opt, long, short) while ((opt=getopt_long(argc, argv, short ? short:"+", long, 0)) != -1)
+
+/*
+ * Convert a string to a BDADDR, with a few "enhancements" - Jean II
+ */
+static int estr2ba(char *str, bdaddr_t *ba)
+{
+ /* Only trap "local", "any" is already dealt with */
+ if(!strcmp(str, "local")) {
+ bacpy(ba, BDADDR_LOCAL);
+ return 0;
+ }
+ return str2ba(str, ba);
+}
+
+#define DEFAULT_VIEW 0 /* Display only known attribute */
+#define TREE_VIEW 1 /* Display full attribute tree */
+#define RAW_VIEW 2 /* Display raw tree */
+#define XML_VIEW 3 /* Display xml tree */
+
+/* Pass args to the inquiry/search handler */
+struct search_context {
+ char *svc; /* Service */
+ uuid_t group; /* Browse group */
+ int view; /* View mode */
+ uint32_t handle; /* Service record handle */
+};
+
+typedef int (*handler_t)(bdaddr_t *bdaddr, struct search_context *arg);
+
+static char UUID_str[MAX_LEN_UUID_STR];
+static bdaddr_t interface;
+
+/* Definition of attribute members */
+struct member_def {
+ char *name;
+};
+
+/* Definition of an attribute */
+struct attrib_def {
+ int num; /* Numeric ID - 16 bits */
+ char *name; /* User readable name */
+ struct member_def *members; /* Definition of attribute args */
+ int member_max; /* Max of attribute arg definitions */
+};
+
+/* Definition of a service or protocol */
+struct uuid_def {
+ int num; /* Numeric ID - 16 bits */
+ char *name; /* User readable name */
+ struct attrib_def *attribs; /* Specific attribute definitions */
+ int attrib_max; /* Max of attribute definitions */
+};
+
+/* Context information about current attribute */
+struct attrib_context {
+ struct uuid_def *service; /* Service UUID, if known */
+ struct attrib_def *attrib; /* Description of the attribute */
+ int member_index; /* Index of current attribute member */
+};
+
+/* Context information about the whole service */
+struct service_context {
+ struct uuid_def *service; /* Service UUID, if known */
+};
+
+/* Allow us to do nice formatting of the lists */
+static char *indent_spaces = " ";
+
+/* ID of the service attribute.
+ * Most attributes after 0x200 are defined based on the service, so
+ * we need to find what is the service (which is messy) - Jean II */
+#define SERVICE_ATTR 0x1
+
+/* Definition of the optional arguments in protocol list */
+static struct member_def protocol_members[] = {
+ { "Protocol" },
+ { "Channel/Port" },
+ { "Version" },
+};
+
+/* Definition of the optional arguments in profile list */
+static struct member_def profile_members[] = {
+ { "Profile" },
+ { "Version" },
+};
+
+/* Definition of the optional arguments in Language list */
+static struct member_def language_members[] = {
+ { "Code ISO639" },
+ { "Encoding" },
+ { "Base Offset" },
+};
+
+/* Name of the various common attributes. See BT assigned numbers */
+static struct attrib_def attrib_names[] = {
+ { 0x0, "ServiceRecordHandle", NULL, 0 },
+ { 0x1, "ServiceClassIDList", NULL, 0 },
+ { 0x2, "ServiceRecordState", NULL, 0 },
+ { 0x3, "ServiceID", NULL, 0 },
+ { 0x4, "ProtocolDescriptorList",
+ protocol_members, sizeof(protocol_members)/sizeof(struct member_def) },
+ { 0x5, "BrowseGroupList", NULL, 0 },
+ { 0x6, "LanguageBaseAttributeIDList",
+ language_members, sizeof(language_members)/sizeof(struct member_def) },
+ { 0x7, "ServiceInfoTimeToLive", NULL, 0 },
+ { 0x8, "ServiceAvailability", NULL, 0 },
+ { 0x9, "BluetoothProfileDescriptorList",
+ profile_members, sizeof(profile_members)/sizeof(struct member_def) },
+ { 0xA, "DocumentationURL", NULL, 0 },
+ { 0xB, "ClientExecutableURL", NULL, 0 },
+ { 0xC, "IconURL", NULL, 0 },
+ { 0xD, "AdditionalProtocolDescriptorLists", NULL, 0 },
+ /* Definitions after that are tricky (per profile or offset) */
+};
+
+const int attrib_max = sizeof(attrib_names)/sizeof(struct attrib_def);
+
+/* Name of the various SPD attributes. See BT assigned numbers */
+static struct attrib_def sdp_attrib_names[] = {
+ { 0x200, "VersionNumberList", NULL, 0 },
+ { 0x201, "ServiceDatabaseState", NULL, 0 },
+};
+
+/* Name of the various SPD attributes. See BT assigned numbers */
+static struct attrib_def browse_attrib_names[] = {
+ { 0x200, "GroupID", NULL, 0 },
+};
+
+/* Name of the various Device ID attributes. See Device Id spec. */
+static struct attrib_def did_attrib_names[] = {
+ { 0x200, "SpecificationID", NULL, 0 },
+ { 0x201, "VendorID", NULL, 0 },
+ { 0x202, "ProductID", NULL, 0 },
+ { 0x203, "Version", NULL, 0 },
+ { 0x204, "PrimaryRecord", NULL, 0 },
+ { 0x205, "VendorIDSource", NULL, 0 },
+};
+
+/* Name of the various HID attributes. See HID spec. */
+static struct attrib_def hid_attrib_names[] = {
+ { 0x200, "DeviceReleaseNum", NULL, 0 },
+ { 0x201, "ParserVersion", NULL, 0 },
+ { 0x202, "DeviceSubclass", NULL, 0 },
+ { 0x203, "CountryCode", NULL, 0 },
+ { 0x204, "VirtualCable", NULL, 0 },
+ { 0x205, "ReconnectInitiate", NULL, 0 },
+ { 0x206, "DescriptorList", NULL, 0 },
+ { 0x207, "LangIDBaseList", NULL, 0 },
+ { 0x208, "SDPDisable", NULL, 0 },
+ { 0x209, "BatteryPower", NULL, 0 },
+ { 0x20a, "RemoteWakeup", NULL, 0 },
+ { 0x20b, "ProfileVersion", NULL, 0 },
+ { 0x20c, "SupervisionTimeout", NULL, 0 },
+ { 0x20d, "NormallyConnectable", NULL, 0 },
+ { 0x20e, "BootDevice", NULL, 0 },
+};
+
+/* Name of the various PAN attributes. See BT assigned numbers */
+/* Note : those need to be double checked - Jean II */
+static struct attrib_def pan_attrib_names[] = {
+ { 0x200, "IpSubnet", NULL, 0 }, /* Obsolete ??? */
+ { 0x30A, "SecurityDescription", NULL, 0 },
+ { 0x30B, "NetAccessType", NULL, 0 },
+ { 0x30C, "MaxNetAccessrate", NULL, 0 },
+ { 0x30D, "IPv4Subnet", NULL, 0 },
+ { 0x30E, "IPv6Subnet", NULL, 0 },
+};
+
+/* Name of the various Generic-Audio attributes. See BT assigned numbers */
+/* Note : totally untested - Jean II */
+static struct attrib_def audio_attrib_names[] = {
+ { 0x302, "Remote audio volume control", NULL, 0 },
+};
+
+/* Same for the UUIDs. See BT assigned numbers */
+static struct uuid_def uuid16_names[] = {
+ /* -- Protocols -- */
+ { 0x0001, "SDP", NULL, 0 },
+ { 0x0002, "UDP", NULL, 0 },
+ { 0x0003, "RFCOMM", NULL, 0 },
+ { 0x0004, "TCP", NULL, 0 },
+ { 0x0005, "TCS-BIN", NULL, 0 },
+ { 0x0006, "TCS-AT", NULL, 0 },
+ { 0x0008, "OBEX", NULL, 0 },
+ { 0x0009, "IP", NULL, 0 },
+ { 0x000a, "FTP", NULL, 0 },
+ { 0x000c, "HTTP", NULL, 0 },
+ { 0x000e, "WSP", NULL, 0 },
+ { 0x000f, "BNEP", NULL, 0 },
+ { 0x0010, "UPnP/ESDP", NULL, 0 },
+ { 0x0011, "HIDP", NULL, 0 },
+ { 0x0012, "HardcopyControlChannel", NULL, 0 },
+ { 0x0014, "HardcopyDataChannel", NULL, 0 },
+ { 0x0016, "HardcopyNotification", NULL, 0 },
+ { 0x0017, "AVCTP", NULL, 0 },
+ { 0x0019, "AVDTP", NULL, 0 },
+ { 0x001b, "CMTP", NULL, 0 },
+ { 0x001d, "UDI_C-Plane", NULL, 0 },
+ { 0x0100, "L2CAP", NULL, 0 },
+ /* -- Services -- */
+ { 0x1000, "ServiceDiscoveryServerServiceClassID",
+ sdp_attrib_names, sizeof(sdp_attrib_names)/sizeof(struct attrib_def) },
+ { 0x1001, "BrowseGroupDescriptorServiceClassID",
+ browse_attrib_names, sizeof(browse_attrib_names)/sizeof(struct attrib_def) },
+ { 0x1002, "PublicBrowseGroup", NULL, 0 },
+ { 0x1101, "SerialPort", NULL, 0 },
+ { 0x1102, "LANAccessUsingPPP", NULL, 0 },
+ { 0x1103, "DialupNetworking (DUN)", NULL, 0 },
+ { 0x1104, "IrMCSync", NULL, 0 },
+ { 0x1105, "OBEXObjectPush", NULL, 0 },
+ { 0x1106, "OBEXFileTransfer", NULL, 0 },
+ { 0x1107, "IrMCSyncCommand", NULL, 0 },
+ { 0x1108, "Headset",
+ audio_attrib_names, sizeof(audio_attrib_names)/sizeof(struct attrib_def) },
+ { 0x1109, "CordlessTelephony", NULL, 0 },
+ { 0x110a, "AudioSource", NULL, 0 },
+ { 0x110b, "AudioSink", NULL, 0 },
+ { 0x110c, "RemoteControlTarget", NULL, 0 },
+ { 0x110d, "AdvancedAudio", NULL, 0 },
+ { 0x110e, "RemoteControl", NULL, 0 },
+ { 0x110f, "VideoConferencing", NULL, 0 },
+ { 0x1110, "Intercom", NULL, 0 },
+ { 0x1111, "Fax", NULL, 0 },
+ { 0x1112, "HeadsetAudioGateway", NULL, 0 },
+ { 0x1113, "WAP", NULL, 0 },
+ { 0x1114, "WAP Client", NULL, 0 },
+ { 0x1115, "PANU (PAN/BNEP)",
+ pan_attrib_names, sizeof(pan_attrib_names)/sizeof(struct attrib_def) },
+ { 0x1116, "NAP (PAN/BNEP)",
+ pan_attrib_names, sizeof(pan_attrib_names)/sizeof(struct attrib_def) },
+ { 0x1117, "GN (PAN/BNEP)",
+ pan_attrib_names, sizeof(pan_attrib_names)/sizeof(struct attrib_def) },
+ { 0x1118, "DirectPrinting (BPP)", NULL, 0 },
+ { 0x1119, "ReferencePrinting (BPP)", NULL, 0 },
+ { 0x111a, "Imaging (BIP)", NULL, 0 },
+ { 0x111b, "ImagingResponder (BIP)", NULL, 0 },
+ { 0x111c, "ImagingAutomaticArchive (BIP)", NULL, 0 },
+ { 0x111d, "ImagingReferencedObjects (BIP)", NULL, 0 },
+ { 0x111e, "Handsfree", NULL, 0 },
+ { 0x111f, "HandsfreeAudioGateway", NULL, 0 },
+ { 0x1120, "DirectPrintingReferenceObjectsService (BPP)", NULL, 0 },
+ { 0x1121, "ReflectedUI (BPP)", NULL, 0 },
+ { 0x1122, "BasicPrinting (BPP)", NULL, 0 },
+ { 0x1123, "PrintingStatus (BPP)", NULL, 0 },
+ { 0x1124, "HumanInterfaceDeviceService (HID)",
+ hid_attrib_names, sizeof(hid_attrib_names)/sizeof(struct attrib_def) },
+ { 0x1125, "HardcopyCableReplacement (HCR)", NULL, 0 },
+ { 0x1126, "HCR_Print (HCR)", NULL, 0 },
+ { 0x1127, "HCR_Scan (HCR)", NULL, 0 },
+ { 0x1128, "Common ISDN Access (CIP)", NULL, 0 },
+ { 0x1129, "VideoConferencingGW (VCP)", NULL, 0 },
+ { 0x112a, "UDI-MT", NULL, 0 },
+ { 0x112b, "UDI-TA", NULL, 0 },
+ { 0x112c, "Audio/Video", NULL, 0 },
+ { 0x112d, "SIM Access (SAP)", NULL, 0 },
+ { 0x112e, "Phonebook Access (PBAP) - PCE", NULL, 0 },
+ { 0x112f, "Phonebook Access (PBAP) - PSE", NULL, 0 },
+ { 0x1130, "Phonebook Access (PBAP)", NULL, 0 },
+ /* ... */
+ { 0x1200, "PnPInformation",
+ did_attrib_names, sizeof(did_attrib_names)/sizeof(struct attrib_def) },
+ { 0x1201, "GenericNetworking", NULL, 0 },
+ { 0x1202, "GenericFileTransfer", NULL, 0 },
+ { 0x1203, "GenericAudio",
+ audio_attrib_names, sizeof(audio_attrib_names)/sizeof(struct attrib_def) },
+ { 0x1204, "GenericTelephony", NULL, 0 },
+ /* ... */
+ { 0x1303, "VideoSource", NULL, 0 },
+ { 0x1304, "VideoSink", NULL, 0 },
+ { 0x1305, "VideoDistribution", NULL, 0 },
+ { 0x1400, "HDP", NULL, 0 },
+ { 0x1401, "HDPSource", NULL, 0 },
+ { 0x1402, "HDPSink", NULL, 0 },
+ { 0x2112, "AppleAgent", NULL, 0 },
+};
+
+static const int uuid16_max = sizeof(uuid16_names)/sizeof(struct uuid_def);
+
+static void sdp_data_printf(sdp_data_t *, struct attrib_context *, int);
+
+/*
+ * Parse a UUID.
+ * The BT assigned numbers only list UUID16, so I'm not sure the
+ * other types will ever get used...
+ */
+static void sdp_uuid_printf(uuid_t *uuid, struct attrib_context *context, int indent)
+{
+ if (uuid) {
+ if (uuid->type == SDP_UUID16) {
+ uint16_t uuidNum = uuid->value.uuid16;
+ struct uuid_def *uuidDef = NULL;
+ int i;
+
+ for (i = 0; i < uuid16_max; i++)
+ if (uuid16_names[i].num == uuidNum) {
+ uuidDef = &uuid16_names[i];
+ break;
+ }
+
+ /* Check if it's the service attribute */
+ if (context->attrib && context->attrib->num == SERVICE_ATTR) {
+ /* We got the service ID !!! */
+ context->service = uuidDef;
+ }
+
+ if (uuidDef)
+ printf("%.*sUUID16 : 0x%.4x - %s\n",
+ indent, indent_spaces, uuidNum, uuidDef->name);
+ else
+ printf("%.*sUUID16 : 0x%.4x\n",
+ indent, indent_spaces, uuidNum);
+ } else if (uuid->type == SDP_UUID32) {
+ struct uuid_def *uuidDef = NULL;
+ int i;
+
+ if (!(uuid->value.uuid32 & 0xffff0000)) {
+ uint16_t uuidNum = uuid->value.uuid32;
+ for (i = 0; i < uuid16_max; i++)
+ if (uuid16_names[i].num == uuidNum) {
+ uuidDef = &uuid16_names[i];
+ break;
+ }
+ }
+
+ if (uuidDef)
+ printf("%.*sUUID32 : 0x%.8x - %s\n",
+ indent, indent_spaces, uuid->value.uuid32, uuidDef->name);
+ else
+ printf("%.*sUUID32 : 0x%.8x\n",
+ indent, indent_spaces, uuid->value.uuid32);
+ } else if (uuid->type == SDP_UUID128) {
+ unsigned int data0;
+ unsigned short data1;
+ unsigned short data2;
+ unsigned short data3;
+ unsigned int data4;
+ unsigned short data5;
+
+ memcpy(&data0, &uuid->value.uuid128.data[0], 4);
+ memcpy(&data1, &uuid->value.uuid128.data[4], 2);
+ memcpy(&data2, &uuid->value.uuid128.data[6], 2);
+ memcpy(&data3, &uuid->value.uuid128.data[8], 2);
+ memcpy(&data4, &uuid->value.uuid128.data[10], 4);
+ memcpy(&data5, &uuid->value.uuid128.data[14], 2);
+
+ printf("%.*sUUID128 : 0x%.8x-%.4x-%.4x-%.4x-%.8x-%.4x\n",
+ indent, indent_spaces,
+ ntohl(data0), ntohs(data1), ntohs(data2),
+ ntohs(data3), ntohl(data4), ntohs(data5));
+ } else
+ printf("%.*sEnum type of UUID not set\n",
+ indent, indent_spaces);
+ } else
+ printf("%.*sNull passed to print UUID\n",
+ indent, indent_spaces);
+}
+
+/*
+ * Parse a sequence of data elements (i.e. a list)
+ */
+static void printf_dataseq(sdp_data_t * pData, struct attrib_context *context, int indent)
+{
+ sdp_data_t *sdpdata = NULL;
+
+ sdpdata = pData;
+ if (sdpdata) {
+ context->member_index = 0;
+ do {
+ sdp_data_printf(sdpdata, context, indent + 2);
+ sdpdata = sdpdata->next;
+ context->member_index++;
+ } while (sdpdata);
+ } else {
+ printf("%.*sBroken dataseq link\n", indent, indent_spaces);
+ }
+}
+
+/*
+ * Parse a single data element (either in the attribute or in a data
+ * sequence).
+ */
+static void sdp_data_printf(sdp_data_t *sdpdata, struct attrib_context *context, int indent)
+{
+ char *member_name = NULL;
+
+ /* Find member name. Almost black magic ;-) */
+ if (context && context->attrib && context->attrib->members &&
+ context->member_index < context->attrib->member_max) {
+ member_name = context->attrib->members[context->member_index].name;
+ }
+
+ switch (sdpdata->dtd) {
+ case SDP_DATA_NIL:
+ printf("%.*sNil\n", indent, indent_spaces);
+ break;
+ case SDP_BOOL:
+ case SDP_UINT8:
+ case SDP_UINT16:
+ case SDP_UINT32:
+ case SDP_UINT64:
+ case SDP_UINT128:
+ case SDP_INT8:
+ case SDP_INT16:
+ case SDP_INT32:
+ case SDP_INT64:
+ case SDP_INT128:
+ if (member_name) {
+ printf("%.*s%s (Integer) : 0x%x\n",
+ indent, indent_spaces, member_name, sdpdata->val.uint32);
+ } else {
+ printf("%.*sInteger : 0x%x\n", indent, indent_spaces,
+ sdpdata->val.uint32);
+ }
+ break;
+
+ case SDP_UUID16:
+ case SDP_UUID32:
+ case SDP_UUID128:
+ //printf("%.*sUUID\n", indent, indent_spaces);
+ sdp_uuid_printf(&sdpdata->val.uuid, context, indent);
+ break;
+
+ case SDP_TEXT_STR8:
+ case SDP_TEXT_STR16:
+ case SDP_TEXT_STR32:
+ if (sdpdata->unitSize > (int) strlen(sdpdata->val.str)) {
+ int i;
+ printf("%.*sData :", indent, indent_spaces);
+ for (i = 0; i < sdpdata->unitSize; i++)
+ printf(" %02x", (unsigned char) sdpdata->val.str[i]);
+ printf("\n");
+ } else
+ printf("%.*sText : \"%s\"\n", indent, indent_spaces, sdpdata->val.str);
+ break;
+ case SDP_URL_STR8:
+ case SDP_URL_STR16:
+ case SDP_URL_STR32:
+ printf("%.*sURL : %s\n", indent, indent_spaces, sdpdata->val.str);
+ break;
+
+ case SDP_SEQ8:
+ case SDP_SEQ16:
+ case SDP_SEQ32:
+ printf("%.*sData Sequence\n", indent, indent_spaces);
+ printf_dataseq(sdpdata->val.dataseq, context, indent);
+ break;
+
+ case SDP_ALT8:
+ case SDP_ALT16:
+ case SDP_ALT32:
+ printf("%.*sData Sequence Alternates\n", indent, indent_spaces);
+ printf_dataseq(sdpdata->val.dataseq, context, indent);
+ break;
+ }
+}
+
+/*
+ * Parse a single attribute.
+ */
+static void print_tree_attr_func(void *value, void *userData)
+{
+ sdp_data_t *sdpdata = value;
+ uint16_t attrId;
+ struct service_context *service = (struct service_context *) userData;
+ struct attrib_context context;
+ struct attrib_def *attrDef = NULL;
+ int i;
+
+ if (!sdpdata)
+ return;
+
+ attrId = sdpdata->attrId;
+ /* Search amongst the generic attributes */
+ for (i = 0; i < attrib_max; i++)
+ if (attrib_names[i].num == attrId) {
+ attrDef = &attrib_names[i];
+ break;
+ }
+ /* Search amongst the specific attributes of this service */
+ if ((attrDef == NULL) && (service->service != NULL) &&
+ (service->service->attribs != NULL)) {
+ struct attrib_def *svc_attribs = service->service->attribs;
+ int svc_attrib_max = service->service->attrib_max;
+ for (i = 0; i < svc_attrib_max; i++)
+ if (svc_attribs[i].num == attrId) {
+ attrDef = &svc_attribs[i];
+ break;
+ }
+ }
+
+ if (attrDef)
+ printf("Attribute Identifier : 0x%x - %s\n", attrId, attrDef->name);
+ else
+ printf("Attribute Identifier : 0x%x\n", attrId);
+ /* Build context */
+ context.service = service->service;
+ context.attrib = attrDef;
+ context.member_index = 0;
+ /* Parse attribute members */
+ sdp_data_printf(sdpdata, &context, 2);
+ /* Update service */
+ service->service = context.service;
+}
+
+/*
+ * Main entry point of this library. Parse a SDP record.
+ * We assume the record has already been read, parsed and cached
+ * locally. Jean II
+ */
+static void print_tree_attr(sdp_record_t *rec)
+{
+ if (rec && rec->attrlist) {
+ struct service_context service = { NULL };
+ sdp_list_foreach(rec->attrlist, print_tree_attr_func, &service);
+ }
+}
+
+static void print_raw_data(sdp_data_t *data, int indent)
+{
+ struct uuid_def *def;
+ int i, hex;
+
+ if (!data)
+ return;
+
+ for (i = 0; i < indent; i++)
+ printf("\t");
+
+ switch (data->dtd) {
+ case SDP_DATA_NIL:
+ printf("NIL\n");
+ break;
+ case SDP_BOOL:
+ printf("Bool %s\n", data->val.uint8 ? "True" : "False");
+ break;
+ case SDP_UINT8:
+ printf("UINT8 0x%02x\n", data->val.uint8);
+ break;
+ case SDP_UINT16:
+ printf("UINT16 0x%04x\n", data->val.uint16);
+ break;
+ case SDP_UINT32:
+ printf("UINT32 0x%08x\n", data->val.uint32);
+ break;
+ case SDP_UINT64:
+ printf("UINT64 0x%016jx\n", data->val.uint64);
+ break;
+ case SDP_UINT128:
+ printf("UINT128 ...\n");
+ break;
+ case SDP_INT8:
+ printf("INT8 %d\n", data->val.int8);
+ break;
+ case SDP_INT16:
+ printf("INT16 %d\n", data->val.int16);
+ break;
+ case SDP_INT32:
+ printf("INT32 %d\n", data->val.int32);
+ break;
+ case SDP_INT64:
+ printf("INT64 %jd\n", data->val.int64);
+ break;
+ case SDP_INT128:
+ printf("INT128 ...\n");
+ break;
+ case SDP_UUID16:
+ case SDP_UUID32:
+ case SDP_UUID128:
+ switch (data->val.uuid.type) {
+ case SDP_UUID16:
+ def = NULL;
+ for (i = 0; i < uuid16_max; i++)
+ if (uuid16_names[i].num == data->val.uuid.value.uuid16) {
+ def = &uuid16_names[i];
+ break;
+ }
+ if (def)
+ printf("UUID16 0x%04x - %s\n", data->val.uuid.value.uuid16, def->name);
+ else
+ printf("UUID16 0x%04x\n", data->val.uuid.value.uuid16);
+ break;
+ case SDP_UUID32:
+ def = NULL;
+ if (!(data->val.uuid.value.uuid32 & 0xffff0000)) {
+ uint16_t value = data->val.uuid.value.uuid32;
+ for (i = 0; i < uuid16_max; i++)
+ if (uuid16_names[i].num == value) {
+ def = &uuid16_names[i];
+ break;
+ }
+ }
+ if (def)
+ printf("UUID32 0x%08x - %s\n", data->val.uuid.value.uuid32, def->name);
+ else
+ printf("UUID32 0x%08x\n", data->val.uuid.value.uuid32);
+ break;
+ case SDP_UUID128:
+ printf("UUID128 ");
+ for (i = 0; i < 16; i++) {
+ switch (i) {
+ case 4:
+ case 6:
+ case 8:
+ case 10:
+ printf("-");
+ break;
+ }
+ printf("%02x", (unsigned char ) data->val.uuid.value.uuid128.data[i]);
+ }
+ printf("\n");
+ break;
+ default:
+ printf("UUID type 0x%02x\n", data->val.uuid.type);
+ break;
+ }
+ break;
+ case SDP_TEXT_STR8:
+ case SDP_TEXT_STR16:
+ case SDP_TEXT_STR32:
+ hex = 0;
+ for (i = 0; i < data->unitSize; i++) {
+ if (i == (data->unitSize - 1) && data->val.str[i] == '\0')
+ break;
+ if (!isprint(data->val.str[i])) {
+ hex = 1;
+ break;
+ }
+ }
+ if (hex) {
+ printf("Data");
+ for (i = 0; i < data->unitSize; i++)
+ printf(" %02x", (unsigned char) data->val.str[i]);
+ } else {
+ printf("String ");
+ for (i = 0; i < data->unitSize; i++)
+ printf("%c", data->val.str[i]);
+ }
+ printf("\n");
+ break;
+ case SDP_URL_STR8:
+ case SDP_URL_STR16:
+ case SDP_URL_STR32:
+ printf("URL %s\n", data->val.str);
+ break;
+ case SDP_SEQ8:
+ case SDP_SEQ16:
+ case SDP_SEQ32:
+ printf("Sequence\n");
+ print_raw_data(data->val.dataseq, indent + 1);
+ break;
+ case SDP_ALT8:
+ case SDP_ALT16:
+ case SDP_ALT32:
+ printf("Alternate\n");
+ print_raw_data(data->val.dataseq, indent + 1);
+ break;
+ default:
+ printf("Unknown type 0x%02x\n", data->dtd);
+ break;
+ }
+
+ print_raw_data(data->next, indent);
+}
+
+static void print_raw_attr_func(void *value, void *userData)
+{
+ sdp_data_t *data = (sdp_data_t *) value;
+ struct attrib_def *def = NULL;
+ int i;
+
+ if (!data)
+ return;
+
+ /* Search amongst the generic attributes */
+ for (i = 0; i < attrib_max; i++)
+ if (attrib_names[i].num == data->attrId) {
+ def = &attrib_names[i];
+ break;
+ }
+
+ if (def)
+ printf("\tAttribute 0x%04x - %s\n", data->attrId, def->name);
+ else
+ printf("\tAttribute 0x%04x\n", data->attrId);
+
+ print_raw_data(data, 2);
+}
+
+static void print_raw_attr(sdp_record_t *rec)
+{
+ if (rec && rec->attrlist) {
+ printf("Sequence\n");
+ sdp_list_foreach(rec->attrlist, print_raw_attr_func, 0);
+ }
+}
+
+/*
+ * Set attributes with single values in SDP record
+ * Jean II
+ */
+static int set_attrib(sdp_session_t *sess, uint32_t handle, uint16_t attrib, char *value)
+{
+ sdp_list_t *attrid_list;
+ uint32_t range = 0x0000ffff;
+ sdp_record_t *rec;
+ int ret;
+
+ /* Get the old SDP record */
+ attrid_list = sdp_list_append(NULL, &range);
+ rec = sdp_service_attr_req(sess, handle, SDP_ATTR_REQ_RANGE, attrid_list);
+ sdp_list_free(attrid_list, NULL);
+
+ if (!rec) {
+ printf("Service get request failed.\n");
+ return -1;
+ }
+
+ /* Check the type of attribute */
+ if (!strncasecmp(value, "u0x", 3)) {
+ /* UUID16 */
+ uint16_t value_int = 0;
+ uuid_t value_uuid;
+ value_int = strtoul(value + 3, NULL, 16);
+ sdp_uuid16_create(&value_uuid, value_int);
+ printf("Adding attrib 0x%X uuid16 0x%X to record 0x%X\n",
+ attrib, value_int, handle);
+
+ sdp_attr_add_new(rec, attrib, SDP_UUID16, &value_uuid.value.uuid16);
+ } else if (!strncasecmp(value, "0x", 2)) {
+ /* Int */
+ uint32_t value_int;
+ value_int = strtoul(value + 2, NULL, 16);
+ printf("Adding attrib 0x%X int 0x%X to record 0x%X\n",
+ attrib, value_int, handle);
+
+ sdp_attr_add_new(rec, attrib, SDP_UINT32, &value_int);
+ } else {
+ /* String */
+ printf("Adding attrib 0x%X string \"%s\" to record 0x%X\n",
+ attrib, value, handle);
+
+ /* Add/Update our attribute to the record */
+ sdp_attr_add_new(rec, attrib, SDP_TEXT_STR8, value);
+ }
+
+ /* Update on the server */
+ ret = sdp_device_record_update(sess, &interface, rec);
+ if (ret < 0)
+ printf("Service Record update failed (%d).\n", errno);
+ sdp_record_free(rec);
+ return ret;
+}
+
+static struct option set_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *set_help =
+ "Usage:\n"
+ "\tget record_handle attrib_id attrib_value\n";
+
+/*
+ * Add an attribute to an existing SDP record on the local SDP server
+ */
+static int cmd_setattr(int argc, char **argv)
+{
+ int opt, status;
+ uint32_t handle;
+ uint16_t attrib;
+ sdp_session_t *sess;
+
+ for_each_opt(opt, set_options, NULL) {
+ switch(opt) {
+ default:
+ printf("%s", set_help);
+ return -1;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 3) {
+ printf("%s", set_help);
+ return -1;
+ }
+
+ /* Convert command line args */
+ handle = strtoul(argv[0], NULL, 16);
+ attrib = strtoul(argv[1], NULL, 16);
+
+ /* Do it */
+ sess = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, 0);
+ if (!sess)
+ return -1;
+
+ status = set_attrib(sess, handle, attrib, argv[2]);
+ sdp_close(sess);
+
+ return status;
+}
+
+/*
+ * We do only simple data sequences. Sequence of sequences is a pain ;-)
+ * Jean II
+ */
+static int set_attribseq(sdp_session_t *session, uint32_t handle, uint16_t attrib, int argc, char **argv)
+{
+ sdp_list_t *attrid_list;
+ uint32_t range = 0x0000ffff;
+ sdp_record_t *rec;
+ sdp_data_t *pSequenceHolder = NULL;
+ void **dtdArray;
+ void **valueArray;
+ void **allocArray;
+ uint8_t uuid16 = SDP_UUID16;
+ uint8_t uint32 = SDP_UINT32;
+ uint8_t str8 = SDP_TEXT_STR8;
+ int i, ret = 0;
+
+ /* Get the old SDP record */
+ attrid_list = sdp_list_append(NULL, &range);
+ rec = sdp_service_attr_req(session, handle, SDP_ATTR_REQ_RANGE, attrid_list);
+ sdp_list_free(attrid_list, NULL);
+
+ if (!rec) {
+ printf("Service get request failed.\n");
+ return -1;
+ }
+
+ /* Create arrays */
+ dtdArray = (void **)malloc(argc * sizeof(void *));
+ valueArray = (void **)malloc(argc * sizeof(void *));
+ allocArray = (void **)malloc(argc * sizeof(void *));
+
+ /* Loop on all args, add them in arrays */
+ for (i = 0; i < argc; i++) {
+ /* Check the type of attribute */
+ if (!strncasecmp(argv[i], "u0x", 3)) {
+ /* UUID16 */
+ uint16_t value_int = strtoul((argv[i]) + 3, NULL, 16);
+ uuid_t *value_uuid = (uuid_t *) malloc(sizeof(uuid_t));
+ allocArray[i] = value_uuid;
+ sdp_uuid16_create(value_uuid, value_int);
+
+ printf("Adding uuid16 0x%X to record 0x%X\n", value_int, handle);
+ dtdArray[i] = &uuid16;
+ valueArray[i] = &value_uuid->value.uuid16;
+ } else if (!strncasecmp(argv[i], "0x", 2)) {
+ /* Int */
+ uint32_t *value_int = (uint32_t *) malloc(sizeof(int));
+ allocArray[i] = value_int;
+ *value_int = strtoul((argv[i]) + 2, NULL, 16);
+
+ printf("Adding int 0x%X to record 0x%X\n", *value_int, handle);
+ dtdArray[i] = &uint32;
+ valueArray[i] = value_int;
+ } else {
+ /* String */
+ printf("Adding string \"%s\" to record 0x%X\n", argv[i], handle);
+ dtdArray[i] = &str8;
+ valueArray[i] = argv[i];
+ }
+ }
+
+ /* Add this sequence to the attrib list */
+ pSequenceHolder = sdp_seq_alloc(dtdArray, valueArray, argc);
+ if (pSequenceHolder) {
+ sdp_attr_replace(rec, attrib, pSequenceHolder);
+
+ /* Update on the server */
+ ret = sdp_device_record_update(session, &interface, rec);
+ if (ret < 0)
+ printf("Service Record update failed (%d).\n", errno);
+ } else
+ printf("Failed to create pSequenceHolder\n");
+
+ /* Cleanup */
+ for (i = 0; i < argc; i++)
+ free(allocArray[i]);
+
+ free(dtdArray);
+ free(valueArray);
+ free(allocArray);
+
+ sdp_record_free(rec);
+
+ return ret;
+}
+
+static struct option seq_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *seq_help =
+ "Usage:\n"
+ "\tget record_handle attrib_id attrib_values\n";
+
+/*
+ * Add an attribute sequence to an existing SDP record
+ * on the local SDP server
+ */
+static int cmd_setseq(int argc, char **argv)
+{
+ int opt, status;
+ uint32_t handle;
+ uint16_t attrib;
+ sdp_session_t *sess;
+
+ for_each_opt(opt, seq_options, NULL) {
+ switch(opt) {
+ default:
+ printf("%s", seq_help);
+ return -1;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 3) {
+ printf("%s", seq_help);
+ return -1;
+ }
+
+ /* Convert command line args */
+ handle = strtoul(argv[0], NULL, 16);
+ attrib = strtoul(argv[1], NULL, 16);
+
+ argc -= 2;
+ argv += 2;
+
+ /* Do it */
+ sess = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, 0);
+ if (!sess)
+ return -1;
+
+ status = set_attribseq(sess, handle, attrib, argc, argv);
+ sdp_close(sess);
+
+ return status;
+}
+
+static void print_service_class(void *value, void *userData)
+{
+ char ServiceClassUUID_str[MAX_LEN_SERVICECLASS_UUID_STR];
+ uuid_t *uuid = (uuid_t *)value;
+
+ sdp_uuid2strn(uuid, UUID_str, MAX_LEN_UUID_STR);
+ sdp_svclass_uuid2strn(uuid, ServiceClassUUID_str, MAX_LEN_SERVICECLASS_UUID_STR);
+ if (uuid->type != SDP_UUID128)
+ printf(" \"%s\" (0x%s)\n", ServiceClassUUID_str, UUID_str);
+ else
+ printf(" UUID 128: %s\n", UUID_str);
+}
+
+static void print_service_desc(void *value, void *user)
+{
+ char str[MAX_LEN_PROTOCOL_UUID_STR];
+ sdp_data_t *p = (sdp_data_t *)value, *s;
+ int i = 0, proto = 0;
+
+ for (; p; p = p->next, i++) {
+ switch (p->dtd) {
+ case SDP_UUID16:
+ case SDP_UUID32:
+ case SDP_UUID128:
+ sdp_uuid2strn(&p->val.uuid, UUID_str, MAX_LEN_UUID_STR);
+ sdp_proto_uuid2strn(&p->val.uuid, str, sizeof(str));
+ proto = sdp_uuid_to_proto(&p->val.uuid);
+ printf(" \"%s\" (0x%s)\n", str, UUID_str);
+ break;
+ case SDP_UINT8:
+ if (proto == RFCOMM_UUID)
+ printf(" Channel: %d\n", p->val.uint8);
+ else
+ printf(" uint8: 0x%x\n", p->val.uint8);
+ break;
+ case SDP_UINT16:
+ if (proto == L2CAP_UUID) {
+ if (i == 1)
+ printf(" PSM: %d\n", p->val.uint16);
+ else
+ printf(" Version: 0x%04x\n", p->val.uint16);
+ } else if (proto == BNEP_UUID)
+ if (i == 1)
+ printf(" Version: 0x%04x\n", p->val.uint16);
+ else
+ printf(" uint16: 0x%x\n", p->val.uint16);
+ else
+ printf(" uint16: 0x%x\n", p->val.uint16);
+ break;
+ case SDP_SEQ16:
+ printf(" SEQ16:");
+ for (s = p->val.dataseq; s; s = s->next)
+ printf(" %x", s->val.uint16);
+ printf("\n");
+ break;
+ case SDP_SEQ8:
+ printf(" SEQ8:");
+ for (s = p->val.dataseq; s; s = s->next)
+ printf(" %x", s->val.uint8);
+ printf("\n");
+ break;
+ default:
+ printf(" FIXME: dtd=0%x\n", p->dtd);
+ break;
+ }
+ }
+}
+
+static void print_lang_attr(void *value, void *user)
+{
+ sdp_lang_attr_t *lang = (sdp_lang_attr_t *)value;
+ printf(" code_ISO639: 0x%02x\n", lang->code_ISO639);
+ printf(" encoding: 0x%02x\n", lang->encoding);
+ printf(" base_offset: 0x%02x\n", lang->base_offset);
+}
+
+static void print_access_protos(void *value, void *userData)
+{
+ sdp_list_t *protDescSeq = (sdp_list_t *)value;
+ sdp_list_foreach(protDescSeq, print_service_desc, 0);
+}
+
+static void print_profile_desc(void *value, void *userData)
+{
+ sdp_profile_desc_t *desc = (sdp_profile_desc_t *)value;
+ char str[MAX_LEN_PROFILEDESCRIPTOR_UUID_STR];
+
+ sdp_uuid2strn(&desc->uuid, UUID_str, MAX_LEN_UUID_STR);
+ sdp_profile_uuid2strn(&desc->uuid, str, MAX_LEN_PROFILEDESCRIPTOR_UUID_STR);
+
+ printf(" \"%s\" (0x%s)\n", str, UUID_str);
+ if (desc->version)
+ printf(" Version: 0x%04x\n", desc->version);
+}
+
+/*
+ * Parse a SDP record in user friendly form.
+ */
+static void print_service_attr(sdp_record_t *rec)
+{
+ sdp_list_t *list = 0, *proto = 0;
+
+ sdp_record_print(rec);
+
+ printf("Service RecHandle: 0x%x\n", rec->handle);
+
+ if (sdp_get_service_classes(rec, &list) == 0) {
+ printf("Service Class ID List:\n");
+ sdp_list_foreach(list, print_service_class, 0);
+ sdp_list_free(list, free);
+ }
+ if (sdp_get_access_protos(rec, &proto) == 0) {
+ printf("Protocol Descriptor List:\n");
+ sdp_list_foreach(proto, print_access_protos, 0);
+ sdp_list_foreach(proto, (sdp_list_func_t)sdp_list_free, 0);
+ sdp_list_free(proto, 0);
+ }
+ if (sdp_get_lang_attr(rec, &list) == 0) {
+ printf("Language Base Attr List:\n");
+ sdp_list_foreach(list, print_lang_attr, 0);
+ sdp_list_free(list, free);
+ }
+ if (sdp_get_profile_descs(rec, &list) == 0) {
+ printf("Profile Descriptor List:\n");
+ sdp_list_foreach(list, print_profile_desc, 0);
+ sdp_list_free(list, free);
+ }
+}
+
+/*
+ * Support for Service (de)registration
+ */
+typedef struct {
+ uint32_t handle;
+ char *name;
+ char *provider;
+ char *desc;
+ unsigned int class;
+ unsigned int profile;
+ uint16_t psm;
+ uint8_t channel;
+ uint8_t network;
+} svc_info_t;
+
+static void add_lang_attr(sdp_record_t *r)
+{
+ sdp_lang_attr_t base_lang;
+ sdp_list_t *langs = 0;
+
+ /* UTF-8 MIBenum (http://www.iana.org/assignments/character-sets) */
+ base_lang.code_ISO639 = (0x65 << 8) | 0x6e;
+ base_lang.encoding = 106;
+ base_lang.base_offset = SDP_PRIMARY_LANG_BASE;
+ langs = sdp_list_append(0, &base_lang);
+ sdp_set_lang_attr(r, langs);
+ sdp_list_free(langs, 0);
+}
+
+static int add_sp(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *apseq, *proto[2], *profiles, *root, *aproto;
+ uuid_t root_uuid, sp_uuid, l2cap, rfcomm;
+ sdp_profile_desc_t profile;
+ sdp_record_t record;
+ uint8_t u8 = si->channel ? si->channel : 1;
+ sdp_data_t *channel;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+ sdp_list_free(root, 0);
+
+ sdp_uuid16_create(&sp_uuid, SERIAL_PORT_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &sp_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+ sdp_list_free(svclass_id, 0);
+
+ sdp_uuid16_create(&profile.uuid, SERIAL_PORT_PROFILE_ID);
+ profile.version = 0x0100;
+ profiles = sdp_list_append(0, &profile);
+ sdp_set_profile_descs(&record, profiles);
+ sdp_list_free(profiles, 0);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&rfcomm, RFCOMM_UUID);
+ proto[1] = sdp_list_append(0, &rfcomm);
+ channel = sdp_data_alloc(SDP_UINT8, &u8);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ add_lang_attr(&record);
+
+ sdp_set_info_attr(&record, "Serial Port", "BlueZ", "COM Port");
+
+ sdp_set_url_attr(&record, "http://www.bluez.org/",
+ "http://www.bluez.org/", "http://www.bluez.org/");
+
+ sdp_set_service_id(&record, sp_uuid);
+ sdp_set_service_ttl(&record, 0xffff);
+ sdp_set_service_avail(&record, 0xff);
+ sdp_set_record_state(&record, 0x00001234);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("Serial Port service registered\n");
+
+end:
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_dun(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root, *aproto;
+ uuid_t rootu, dun, gn, l2cap, rfcomm;
+ sdp_profile_desc_t profile;
+ sdp_list_t *proto[2];
+ sdp_record_t record;
+ uint8_t u8 = si->channel ? si->channel : 2;
+ sdp_data_t *channel;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&rootu, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &rootu);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&dun, DIALUP_NET_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &dun);
+ sdp_uuid16_create(&gn, GENERIC_NETWORKING_SVCLASS_ID);
+ svclass_id = sdp_list_append(svclass_id, &gn);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile.uuid, DIALUP_NET_PROFILE_ID);
+ profile.version = 0x0100;
+ pfseq = sdp_list_append(0, &profile);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&rfcomm, RFCOMM_UUID);
+ proto[1] = sdp_list_append(0, &rfcomm);
+ channel = sdp_data_alloc(SDP_UINT8, &u8);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "Dial-Up Networking", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("Dial-Up Networking service registered\n");
+
+end:
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_fax(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, fax_uuid, tel_uuid, l2cap_uuid, rfcomm_uuid;
+ sdp_profile_desc_t profile;
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ uint8_t u8 = si->channel? si->channel : 3;
+ sdp_data_t *channel;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&fax_uuid, FAX_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &fax_uuid);
+ sdp_uuid16_create(&tel_uuid, GENERIC_TELEPHONY_SVCLASS_ID);
+ svclass_id = sdp_list_append(svclass_id, &tel_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile.uuid, FAX_PROFILE_ID);
+ profile.version = 0x0100;
+ pfseq = sdp_list_append(0, &profile);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap_uuid);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(0, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &u8);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "Fax", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+ printf("Fax service registered\n");
+end:
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+ return ret;
+}
+
+static int add_lan(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
+ sdp_profile_desc_t profile;
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ uint8_t u8 = si->channel ? si->channel : 4;
+ sdp_data_t *channel;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&svclass_uuid, LAN_ACCESS_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &svclass_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile.uuid, LAN_ACCESS_PROFILE_ID);
+ profile.version = 0x0100;
+ pfseq = sdp_list_append(0, &profile);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap_uuid);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(0, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &u8);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "LAN Access over PPP", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("LAN Access service registered\n");
+
+end:
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_headset(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
+ sdp_profile_desc_t profile;
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ uint8_t u8 = si->channel ? si->channel : 5;
+ sdp_data_t *channel;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&svclass_uuid, HEADSET_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &svclass_uuid);
+ sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+ svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID);
+ profile.version = 0x0100;
+ pfseq = sdp_list_append(0, &profile);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap_uuid);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(0, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &u8);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "Headset", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("Headset service registered\n");
+
+end:
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_headset_ag(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
+ sdp_profile_desc_t profile;
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ uint8_t u8 = si->channel ? si->channel : 7;
+ sdp_data_t *channel;
+ uint8_t netid = si->network ? si->network : 0x01; // ???? profile document
+ sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid);
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&svclass_uuid, HEADSET_AGW_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &svclass_uuid);
+ sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+ svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID);
+ profile.version = 0x0100;
+ pfseq = sdp_list_append(0, &profile);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap_uuid);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(0, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &u8);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "Voice Gateway", 0, 0);
+
+ sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network);
+
+ if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("Headset AG service registered\n");
+
+end:
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_handsfree(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
+ sdp_profile_desc_t profile;
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ uint8_t u8 = si->channel ? si->channel : 6;
+ uint16_t u16 = 0x31;
+ sdp_data_t *channel, *features;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&svclass_uuid, HANDSFREE_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &svclass_uuid);
+ sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+ svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID);
+ profile.version = 0x0101;
+ pfseq = sdp_list_append(0, &profile);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap_uuid);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(0, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &u8);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ features = sdp_data_alloc(SDP_UINT16, &u16);
+ sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "Handsfree", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("Handsfree service registered\n");
+
+end:
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_handsfree_ag(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
+ sdp_profile_desc_t profile;
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ uint8_t u8 = si->channel ? si->channel : 7;
+ uint16_t u16 = 0x17;
+ sdp_data_t *channel, *features;
+ uint8_t netid = si->network ? si->network : 0x01; // ???? profile document
+ sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid);
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&svclass_uuid, HANDSFREE_AGW_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &svclass_uuid);
+ sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+ svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID);
+ profile.version = 0x0105;
+ pfseq = sdp_list_append(0, &profile);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap_uuid);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(0, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &u8);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ features = sdp_data_alloc(SDP_UINT16, &u16);
+ sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "Voice Gateway", 0, 0);
+
+ sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network);
+
+ if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("Handsfree AG service registered\n");
+
+end:
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_simaccess(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
+ sdp_profile_desc_t profile;
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ uint8_t u8 = si->channel? si->channel : 8;
+ uint16_t u16 = 0x31;
+ sdp_data_t *channel, *features;
+ int ret = 0;
+
+ memset((void *)&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&svclass_uuid, SAP_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &svclass_uuid);
+ sdp_uuid16_create(&ga_svclass_uuid, GENERIC_TELEPHONY_SVCLASS_ID);
+ svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile.uuid, SAP_PROFILE_ID);
+ profile.version = 0x0101;
+ pfseq = sdp_list_append(0, &profile);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap_uuid);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(0, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &u8);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ features = sdp_data_alloc(SDP_UINT16, &u16);
+ sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "SIM Access", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("SIM Access service registered\n");
+
+end:
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_opush(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, opush_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[3];
+ sdp_record_t record;
+ uint8_t chan = si->channel ? si->channel : 9;
+ sdp_data_t *channel;
+ uint8_t formats[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xff };
+ void *dtds[sizeof(formats)], *values[sizeof(formats)];
+ unsigned int i;
+ uint8_t dtd = SDP_UINT8;
+ sdp_data_t *sflist;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&opush_uuid, OBEX_OBJPUSH_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &opush_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, OBEX_OBJPUSH_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(0, profile);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap_uuid);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(0, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &chan);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+ proto[2] = sdp_list_append(0, &obex_uuid);
+ apseq = sdp_list_append(apseq, proto[2]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ for (i = 0; i < sizeof(formats); i++) {
+ dtds[i] = &dtd;
+ values[i] = &formats[i];
+ }
+ sflist = sdp_seq_alloc(dtds, values, sizeof(formats));
+ sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FORMATS_LIST, sflist);
+
+ sdp_set_info_attr(&record, "OBEX Object Push", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("OBEX Object Push service registered\n");
+
+end:
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(proto[2], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_pbap(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, pbap_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[3];
+ sdp_record_t record;
+ uint8_t chan = si->channel ? si->channel : 19;
+ sdp_data_t *channel;
+ uint8_t formats[] = {0x01};
+ uint8_t dtd = SDP_UINT8;
+ sdp_data_t *sflist;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&pbap_uuid, PBAP_PSE_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &pbap_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, PBAP_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(0, profile);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap_uuid);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(0, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &chan);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+ proto[2] = sdp_list_append(0, &obex_uuid);
+ apseq = sdp_list_append(apseq, proto[2]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sflist = sdp_data_alloc(dtd,formats);
+ sdp_attr_add(&record, SDP_ATTR_SUPPORTED_REPOSITORIES, sflist);
+
+ sdp_set_info_attr(&record, "OBEX Phonebook Access Server", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record,
+ SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("PBAP service registered\n");
+
+end:
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(proto[2], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_ftp(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, ftrn_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[3];
+ sdp_record_t record;
+ uint8_t u8 = si->channel ? si->channel: 10;
+ sdp_data_t *channel;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&ftrn_uuid, OBEX_FILETRANS_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &ftrn_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, OBEX_FILETRANS_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(0, &profile[0]);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap_uuid);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(0, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &u8);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+ proto[2] = sdp_list_append(0, &obex_uuid);
+ apseq = sdp_list_append(apseq, proto[2]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "OBEX File Transfer", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("OBEX File Transfer service registered\n");
+
+end:
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(proto[2], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_directprint(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, opush_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[3];
+ sdp_record_t record;
+ uint8_t chan = si->channel ? si->channel : 12;
+ sdp_data_t *channel;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&opush_uuid, DIRECT_PRINTING_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &opush_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, BASIC_PRINTING_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(0, profile);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap_uuid);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(0, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &chan);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+ proto[2] = sdp_list_append(0, &obex_uuid);
+ apseq = sdp_list_append(apseq, proto[2]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "Direct Printing", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("Direct Printing service registered\n");
+
+end:
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(proto[2], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_nap(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, ftrn_uuid, l2cap_uuid, bnep_uuid;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ uint16_t lp = 0x000f, ver = 0x0100;
+ sdp_data_t *psm, *version;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&ftrn_uuid, NAP_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &ftrn_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, NAP_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(0, &profile[0]);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap_uuid);
+ psm = sdp_data_alloc(SDP_UINT16, &lp);
+ proto[0] = sdp_list_append(proto[0], psm);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&bnep_uuid, BNEP_UUID);
+ proto[1] = sdp_list_append(0, &bnep_uuid);
+ version = sdp_data_alloc(SDP_UINT16, &ver);
+ proto[1] = sdp_list_append(proto[1], version);
+
+ {
+ uint16_t ptype[4] = { 0x0010, 0x0020, 0x0030, 0x0040 };
+ sdp_data_t *head, *pseq;
+ int p;
+
+ for (p = 0, head = NULL; p < 4; p++) {
+ sdp_data_t *data = sdp_data_alloc(SDP_UINT16, &ptype[p]);
+ head = sdp_seq_append(head, data);
+ }
+ pseq = sdp_data_alloc(SDP_SEQ16, head);
+ proto[1] = sdp_list_append(proto[1], pseq);
+ }
+
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "Network Access Point Service", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("NAP service registered\n");
+
+end:
+ sdp_data_free(version);
+ sdp_data_free(psm);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_gn(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, ftrn_uuid, l2cap_uuid, bnep_uuid;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ uint16_t lp = 0x000f, ver = 0x0100;
+ sdp_data_t *psm, *version;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&ftrn_uuid, GN_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &ftrn_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, GN_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(0, &profile[0]);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap_uuid);
+ psm = sdp_data_alloc(SDP_UINT16, &lp);
+ proto[0] = sdp_list_append(proto[0], psm);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&bnep_uuid, BNEP_UUID);
+ proto[1] = sdp_list_append(0, &bnep_uuid);
+ version = sdp_data_alloc(SDP_UINT16, &ver);
+ proto[1] = sdp_list_append(proto[1], version);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "Group Network Service", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("GN service registered\n");
+
+end:
+ sdp_data_free(version);
+ sdp_data_free(psm);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_panu(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, ftrn_uuid, l2cap_uuid, bnep_uuid;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ uint16_t lp = 0x000f, ver = 0x0100;
+ sdp_data_t *psm, *version;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+ sdp_list_free(root, NULL);
+
+ sdp_uuid16_create(&ftrn_uuid, PANU_SVCLASS_ID);
+ svclass_id = sdp_list_append(NULL, &ftrn_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+ sdp_list_free(svclass_id, NULL);
+
+ sdp_uuid16_create(&profile[0].uuid, PANU_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(NULL, &profile[0]);
+ sdp_set_profile_descs(&record, pfseq);
+ sdp_list_free(pfseq, NULL);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(NULL, &l2cap_uuid);
+ psm = sdp_data_alloc(SDP_UINT16, &lp);
+ proto[0] = sdp_list_append(proto[0], psm);
+ apseq = sdp_list_append(NULL, proto[0]);
+
+ sdp_uuid16_create(&bnep_uuid, BNEP_UUID);
+ proto[1] = sdp_list_append(NULL, &bnep_uuid);
+ version = sdp_data_alloc(SDP_UINT16, &ver);
+ proto[1] = sdp_list_append(proto[1], version);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(NULL, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "PAN User", NULL, NULL);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("PANU service registered\n");
+
+end:
+ sdp_data_free(version);
+ sdp_data_free(psm);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_hid_keyb(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_record_t record;
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, hidkb_uuid, l2cap_uuid, hidp_uuid;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[3];
+ sdp_data_t *psm, *lang_lst, *lang_lst2, *hid_spec_lst, *hid_spec_lst2;
+ unsigned int i;
+ uint8_t dtd = SDP_UINT16;
+ uint8_t dtd2 = SDP_UINT8;
+ uint8_t dtd_data = SDP_TEXT_STR8;
+ void *dtds[2];
+ void *values[2];
+ void *dtds2[2];
+ void *values2[2];
+ int leng[2];
+ uint8_t hid_spec_type = 0x22;
+ uint16_t hid_attr_lang[] = { 0x409, 0x100 };
+ static const uint16_t ctrl = 0x11;
+ static const uint16_t intr = 0x13;
+ static const uint16_t hid_attr[] = { 0x100, 0x111, 0x40, 0x0d, 0x01, 0x01 };
+ static const uint16_t hid_attr2[] = { 0x0, 0x01, 0x100, 0x1f40, 0x01, 0x01 };
+ const uint8_t hid_spec[] = {
+ 0x05, 0x01, // usage page
+ 0x09, 0x06, // keyboard
+ 0xa1, 0x01, // key codes
+ 0x85, 0x01, // minimum
+ 0x05, 0x07, // max
+ 0x19, 0xe0, // logical min
+ 0x29, 0xe7, // logical max
+ 0x15, 0x00, // report size
+ 0x25, 0x01, // report count
+ 0x75, 0x01, // input data variable absolute
+ 0x95, 0x08, // report count
+ 0x81, 0x02, // report size
+ 0x75, 0x08,
+ 0x95, 0x01,
+ 0x81, 0x01,
+ 0x75, 0x01,
+ 0x95, 0x05,
+ 0x05, 0x08,
+ 0x19, 0x01,
+ 0x29, 0x05,
+ 0x91, 0x02,
+ 0x75, 0x03,
+ 0x95, 0x01,
+ 0x91, 0x01,
+ 0x75, 0x08,
+ 0x95, 0x06,
+ 0x15, 0x00,
+ 0x26, 0xff,
+ 0x00, 0x05,
+ 0x07, 0x19,
+ 0x00, 0x2a,
+ 0xff, 0x00,
+ 0x81, 0x00,
+ 0x75, 0x01,
+ 0x95, 0x01,
+ 0x15, 0x00,
+ 0x25, 0x01,
+ 0x05, 0x0c,
+ 0x09, 0xb8,
+ 0x81, 0x06,
+ 0x09, 0xe2,
+ 0x81, 0x06,
+ 0x09, 0xe9,
+ 0x81, 0x02,
+ 0x09, 0xea,
+ 0x81, 0x02,
+ 0x75, 0x01,
+ 0x95, 0x04,
+ 0x81, 0x01,
+ 0xc0 // end tag
+ };
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ add_lang_attr(&record);
+
+ sdp_uuid16_create(&hidkb_uuid, HID_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &hidkb_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, HID_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(0, profile);
+ sdp_set_profile_descs(&record, pfseq);
+
+ /* protocols */
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[1] = sdp_list_append(0, &l2cap_uuid);
+ psm = sdp_data_alloc(SDP_UINT16, &ctrl);
+ proto[1] = sdp_list_append(proto[1], psm);
+ apseq = sdp_list_append(0, proto[1]);
+
+ sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
+ proto[2] = sdp_list_append(0, &hidp_uuid);
+ apseq = sdp_list_append(apseq, proto[2]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ /* additional protocols */
+ proto[1] = sdp_list_append(0, &l2cap_uuid);
+ psm = sdp_data_alloc(SDP_UINT16, &intr);
+ proto[1] = sdp_list_append(proto[1], psm);
+ apseq = sdp_list_append(0, proto[1]);
+
+ sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
+ proto[2] = sdp_list_append(0, &hidp_uuid);
+ apseq = sdp_list_append(apseq, proto[2]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_add_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "HID Keyboard", NULL, NULL);
+
+ for (i = 0; i < sizeof(hid_attr) / 2; i++)
+ sdp_attr_add_new(&record,
+ SDP_ATTR_HID_DEVICE_RELEASE_NUMBER + i,
+ SDP_UINT16, &hid_attr[i]);
+
+ dtds[0] = &dtd2;
+ values[0] = &hid_spec_type;
+ dtds[1] = &dtd_data;
+ values[1] = (uint8_t *) hid_spec;
+ leng[0] = 0;
+ leng[1] = sizeof(hid_spec);
+ hid_spec_lst = sdp_seq_alloc_with_length(dtds, values, leng, 2);
+ hid_spec_lst2 = sdp_data_alloc(SDP_SEQ8, hid_spec_lst);
+ sdp_attr_add(&record, SDP_ATTR_HID_DESCRIPTOR_LIST, hid_spec_lst2);
+
+ for (i = 0; i < sizeof(hid_attr_lang) / 2; i++) {
+ dtds2[i] = &dtd;
+ values2[i] = &hid_attr_lang[i];
+ }
+
+ lang_lst = sdp_seq_alloc(dtds2, values2, sizeof(hid_attr_lang) / 2);
+ lang_lst2 = sdp_data_alloc(SDP_SEQ8, lang_lst);
+ sdp_attr_add(&record, SDP_ATTR_HID_LANG_ID_BASE_LIST, lang_lst2);
+
+ sdp_attr_add_new(&record, SDP_ATTR_HID_SDP_DISABLE, SDP_UINT16, &hid_attr2[0]);
+
+ for (i = 0; i < sizeof(hid_attr2) / 2 - 1; i++)
+ sdp_attr_add_new(&record, SDP_ATTR_HID_REMOTE_WAKEUP + i,
+ SDP_UINT16, &hid_attr2[i + 1]);
+
+ if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ return -1;
+ }
+
+ printf("HID keyboard service registered\n");
+
+ return 0;
+}
+
+static int add_hid_wiimote(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_record_t record;
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, hid_uuid, l2cap_uuid, hidp_uuid;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[3];
+ sdp_data_t *psm, *lang_lst, *lang_lst2, *hid_spec_lst, *hid_spec_lst2;
+ unsigned int i;
+ uint8_t dtd = SDP_UINT16;
+ uint8_t dtd2 = SDP_UINT8;
+ uint8_t dtd_data = SDP_TEXT_STR8;
+ void *dtds[2];
+ void *values[2];
+ void *dtds2[2];
+ void *values2[2];
+ int leng[2];
+ uint8_t hid_spec_type = 0x22;
+ uint16_t hid_attr_lang[] = { 0x409, 0x100 };
+ uint16_t ctrl = 0x11, intr = 0x13;
+ uint16_t hid_release = 0x0100, parser_version = 0x0111;
+ uint8_t subclass = 0x04, country = 0x33;
+ uint8_t virtual_cable = 0, reconnect = 1, sdp_disable = 0;
+ uint8_t battery = 1, remote_wakeup = 1;
+ uint16_t profile_version = 0x0100, superv_timeout = 0x0c80;
+ uint8_t norm_connect = 0, boot_device = 0;
+ const uint8_t hid_spec[] = {
+ 0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x10,
+ 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95,
+ 0x01, 0x06, 0x00, 0xff, 0x09, 0x01, 0x91, 0x00,
+ 0x85, 0x11, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
+ 0x85, 0x12, 0x95, 0x02, 0x09, 0x01, 0x91, 0x00,
+ 0x85, 0x13, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
+ 0x85, 0x14, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
+ 0x85, 0x15, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
+ 0x85, 0x16, 0x95, 0x15, 0x09, 0x01, 0x91, 0x00,
+ 0x85, 0x17, 0x95, 0x06, 0x09, 0x01, 0x91, 0x00,
+ 0x85, 0x18, 0x95, 0x15, 0x09, 0x01, 0x91, 0x00,
+ 0x85, 0x19, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
+ 0x85, 0x1a, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
+ 0x85, 0x20, 0x95, 0x06, 0x09, 0x01, 0x81, 0x00,
+ 0x85, 0x21, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+ 0x85, 0x22, 0x95, 0x04, 0x09, 0x01, 0x81, 0x00,
+ 0x85, 0x30, 0x95, 0x02, 0x09, 0x01, 0x81, 0x00,
+ 0x85, 0x31, 0x95, 0x05, 0x09, 0x01, 0x81, 0x00,
+ 0x85, 0x32, 0x95, 0x0a, 0x09, 0x01, 0x81, 0x00,
+ 0x85, 0x33, 0x95, 0x11, 0x09, 0x01, 0x81, 0x00,
+ 0x85, 0x34, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+ 0x85, 0x35, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+ 0x85, 0x36, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+ 0x85, 0x37, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+ 0x85, 0x3d, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+ 0x85, 0x3e, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+ 0x85, 0x3f, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+ 0xc0, 0x00
+ };
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&hid_uuid, HID_SVCLASS_ID);
+ svclass_id = sdp_list_append(NULL, &hid_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, HID_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(NULL, profile);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[1] = sdp_list_append(0, &l2cap_uuid);
+ psm = sdp_data_alloc(SDP_UINT16, &ctrl);
+ proto[1] = sdp_list_append(proto[1], psm);
+ apseq = sdp_list_append(0, proto[1]);
+
+ sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
+ proto[2] = sdp_list_append(0, &hidp_uuid);
+ apseq = sdp_list_append(apseq, proto[2]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ proto[1] = sdp_list_append(0, &l2cap_uuid);
+ psm = sdp_data_alloc(SDP_UINT16, &intr);
+ proto[1] = sdp_list_append(proto[1], psm);
+ apseq = sdp_list_append(0, proto[1]);
+
+ sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
+ proto[2] = sdp_list_append(0, &hidp_uuid);
+ apseq = sdp_list_append(apseq, proto[2]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_add_access_protos(&record, aproto);
+
+ add_lang_attr(&record);
+
+ sdp_set_info_attr(&record, "Nintendo RVL-CNT-01",
+ "Nintendo", "Nintendo RVL-CNT-01");
+
+ sdp_attr_add_new(&record, SDP_ATTR_HID_DEVICE_RELEASE_NUMBER,
+ SDP_UINT16, &hid_release);
+
+ sdp_attr_add_new(&record, SDP_ATTR_HID_PARSER_VERSION,
+ SDP_UINT16, &parser_version);
+
+ sdp_attr_add_new(&record, SDP_ATTR_HID_DEVICE_SUBCLASS,
+ SDP_UINT8, &subclass);
+
+ sdp_attr_add_new(&record, SDP_ATTR_HID_COUNTRY_CODE,
+ SDP_UINT8, &country);
+
+ sdp_attr_add_new(&record, SDP_ATTR_HID_VIRTUAL_CABLE,
+ SDP_BOOL, &virtual_cable);
+
+ sdp_attr_add_new(&record, SDP_ATTR_HID_RECONNECT_INITIATE,
+ SDP_BOOL, &reconnect);
+
+ dtds[0] = &dtd2;
+ values[0] = &hid_spec_type;
+ dtds[1] = &dtd_data;
+ values[1] = (uint8_t *) hid_spec;
+ leng[0] = 0;
+ leng[1] = sizeof(hid_spec);
+ hid_spec_lst = sdp_seq_alloc_with_length(dtds, values, leng, 2);
+ hid_spec_lst2 = sdp_data_alloc(SDP_SEQ8, hid_spec_lst);
+ sdp_attr_add(&record, SDP_ATTR_HID_DESCRIPTOR_LIST, hid_spec_lst2);
+
+ for (i = 0; i < sizeof(hid_attr_lang) / 2; i++) {
+ dtds2[i] = &dtd;
+ values2[i] = &hid_attr_lang[i];
+ }
+
+ lang_lst = sdp_seq_alloc(dtds2, values2, sizeof(hid_attr_lang) / 2);
+ lang_lst2 = sdp_data_alloc(SDP_SEQ8, lang_lst);
+ sdp_attr_add(&record, SDP_ATTR_HID_LANG_ID_BASE_LIST, lang_lst2);
+
+ sdp_attr_add_new(&record, SDP_ATTR_HID_SDP_DISABLE,
+ SDP_BOOL, &sdp_disable);
+
+ sdp_attr_add_new(&record, SDP_ATTR_HID_BATTERY_POWER,
+ SDP_BOOL, &battery);
+
+ sdp_attr_add_new(&record, SDP_ATTR_HID_REMOTE_WAKEUP,
+ SDP_BOOL, &remote_wakeup);
+
+ sdp_attr_add_new(&record, SDP_ATTR_HID_PROFILE_VERSION,
+ SDP_UINT16, &profile_version);
+
+ sdp_attr_add_new(&record, SDP_ATTR_HID_SUPERVISION_TIMEOUT,
+ SDP_UINT16, &superv_timeout);
+
+ sdp_attr_add_new(&record, SDP_ATTR_HID_NORMALLY_CONNECTABLE,
+ SDP_BOOL, &norm_connect);
+
+ sdp_attr_add_new(&record, SDP_ATTR_HID_BOOT_DEVICE,
+ SDP_BOOL, &boot_device);
+
+ if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ return -1;
+ }
+
+ printf("Wii-Mote service registered\n");
+
+ return 0;
+}
+
+static int add_cip(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, l2cap, cmtp, cip;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ uint16_t psm = si->psm ? si->psm : 0x1001;
+ uint8_t netid = si->network ? si->network : 0x02; // 0x02 = ISDN, 0x03 = GSM
+ sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid);
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&cip, CIP_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &cip);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, CIP_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(0, &profile[0]);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap);
+ apseq = sdp_list_append(0, proto[0]);
+ proto[0] = sdp_list_append(proto[0], sdp_data_alloc(SDP_UINT16, &psm));
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&cmtp, CMTP_UUID);
+ proto[1] = sdp_list_append(0, &cmtp);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network);
+
+ sdp_set_info_attr(&record, "Common ISDN Access", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("CIP service registered\n");
+
+end:
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+ sdp_data_free(network);
+
+ return ret;
+}
+
+static int add_ctp(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, l2cap, tcsbin, ctp;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ uint8_t netid = si->network ? si->network : 0x02; // 0x01-0x07 cf. p120 profile document
+ sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid);
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&ctp, CORDLESS_TELEPHONY_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &ctp);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, CORDLESS_TELEPHONY_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(0, &profile[0]);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&tcsbin, TCS_BIN_UUID);
+ proto[1] = sdp_list_append(0, &tcsbin);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network);
+
+ sdp_set_info_attr(&record, "Cordless Telephony", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto end;
+ }
+
+ printf("CTP service registered\n");
+
+end:
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+ sdp_data_free(network);
+
+ return ret;
+}
+
+static int add_a2source(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, l2cap, avdtp, a2src;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ sdp_data_t *psm, *version;
+ uint16_t lp = 0x0019, ver = 0x0100;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&a2src, AUDIO_SOURCE_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &a2src);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(0, &profile[0]);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap);
+ psm = sdp_data_alloc(SDP_UINT16, &lp);
+ proto[0] = sdp_list_append(proto[0], psm);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&avdtp, AVDTP_UUID);
+ proto[1] = sdp_list_append(0, &avdtp);
+ version = sdp_data_alloc(SDP_UINT16, &ver);
+ proto[1] = sdp_list_append(proto[1], version);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "Audio Source", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto done;
+ }
+
+ printf("Audio source service registered\n");
+
+done:
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_a2sink(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, l2cap, avdtp, a2snk;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ sdp_data_t *psm, *version;
+ uint16_t lp = 0x0019, ver = 0x0100;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&a2snk, AUDIO_SINK_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &a2snk);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(0, &profile[0]);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap);
+ psm = sdp_data_alloc(SDP_UINT16, &lp);
+ proto[0] = sdp_list_append(proto[0], psm);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&avdtp, AVDTP_UUID);
+ proto[1] = sdp_list_append(0, &avdtp);
+ version = sdp_data_alloc(SDP_UINT16, &ver);
+ proto[1] = sdp_list_append(proto[1], version);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "Audio Sink", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto done;
+ }
+
+ printf("Audio sink service registered\n");
+
+done:
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_avrct(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, l2cap, avctp, avrct;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ sdp_data_t *psm, *version, *features;
+ uint16_t lp = 0x0017, ver = 0x0100, feat = 0x000f;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&avrct, AV_REMOTE_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &avrct);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(0, &profile[0]);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap);
+ psm = sdp_data_alloc(SDP_UINT16, &lp);
+ proto[0] = sdp_list_append(proto[0], psm);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&avctp, AVCTP_UUID);
+ proto[1] = sdp_list_append(0, &avctp);
+ version = sdp_data_alloc(SDP_UINT16, &ver);
+ proto[1] = sdp_list_append(proto[1], version);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ features = sdp_data_alloc(SDP_UINT16, &feat);
+ sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+ sdp_set_info_attr(&record, "AVRCP CT", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto done;
+ }
+
+ printf("Remote control service registered\n");
+
+done:
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_avrtg(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, l2cap, avctp, avrtg;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t record;
+ sdp_data_t *psm, *version, *features;
+ uint16_t lp = 0x0017, ver = 0x0100, feat = 0x000f;
+ int ret = 0;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&avrtg, AV_REMOTE_TARGET_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &avrtg);
+ sdp_set_service_classes(&record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(0, &profile[0]);
+ sdp_set_profile_descs(&record, pfseq);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap);
+ psm = sdp_data_alloc(SDP_UINT16, &lp);
+ proto[0] = sdp_list_append(proto[0], psm);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&avctp, AVCTP_UUID);
+ proto[1] = sdp_list_append(0, &avctp);
+ version = sdp_data_alloc(SDP_UINT16, &ver);
+ proto[1] = sdp_list_append(proto[1], version);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ features = sdp_data_alloc(SDP_UINT16, &feat);
+ sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+ sdp_set_info_attr(&record, "AVRCP TG", 0, 0);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ ret = -1;
+ goto done;
+ }
+
+ printf("Remote target service registered\n");
+
+done:
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(aproto, 0);
+
+ return ret;
+}
+
+static int add_udi_ue(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_record_t record;
+ sdp_list_t *root, *svclass, *proto;
+ uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
+ uint8_t channel = si->channel ? si->channel: 18;
+
+ memset(&record, 0, sizeof(record));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+ sdp_list_free(root, NULL);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto = sdp_list_append(proto, sdp_list_append(
+ sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
+
+ sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+ sdp_uuid16_create(&svclass_uuid, UDI_MT_SVCLASS_ID);
+ svclass = sdp_list_append(NULL, &svclass_uuid);
+ sdp_set_service_classes(&record, svclass);
+ sdp_list_free(svclass, NULL);
+
+ sdp_set_info_attr(&record, "UDI UE", NULL, NULL);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ return -1;
+ }
+
+ printf("UDI UE service registered\n");
+
+ return 0;
+}
+
+static int add_udi_te(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_record_t record;
+ sdp_list_t *root, *svclass, *proto;
+ uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
+ uint8_t channel = si->channel ? si->channel: 19;
+
+ memset(&record, 0, sizeof(record));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+ sdp_list_free(root, NULL);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto = sdp_list_append(proto, sdp_list_append(
+ sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
+
+ sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+ sdp_uuid16_create(&svclass_uuid, UDI_TA_SVCLASS_ID);
+ svclass = sdp_list_append(NULL, &svclass_uuid);
+ sdp_set_service_classes(&record, svclass);
+ sdp_list_free(svclass, NULL);
+
+ sdp_set_info_attr(&record, "UDI TE", NULL, NULL);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ return -1;
+ }
+
+ printf("UDI TE service registered\n");
+
+ return 0;
+}
+
+static unsigned char sr1_uuid[] = { 0xbc, 0x19, 0x9c, 0x24, 0x95, 0x8b, 0x4c, 0xc0,
+ 0xa2, 0xcb, 0xfd, 0x8a, 0x30, 0xbf, 0x32, 0x06 };
+
+static int add_sr1(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_record_t record;
+ sdp_list_t *root, *svclass;
+ uuid_t root_uuid, svclass_uuid;
+
+ memset(&record, 0, sizeof(record));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid128_create(&svclass_uuid, (void *) sr1_uuid);
+ svclass = sdp_list_append(NULL, &svclass_uuid);
+ sdp_set_service_classes(&record, svclass);
+
+ sdp_set_info_attr(&record, "TOSHIBA SR-1", NULL, NULL);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ return -1;
+ }
+
+ printf("Toshiba Speech Recognition SR-1 service record registered\n");
+
+ return 0;
+}
+
+static unsigned char syncmls_uuid[] = { 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x02 };
+
+static unsigned char syncmlc_uuid[] = { 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x02 };
+
+static int add_syncml(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_record_t record;
+ sdp_list_t *root, *svclass, *proto;
+ uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
+ uint8_t channel = si->channel ? si->channel: 15;
+
+ memset(&record, 0, sizeof(record));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid128_create(&svclass_uuid, (void *) syncmlc_uuid);
+ svclass = sdp_list_append(NULL, &svclass_uuid);
+ sdp_set_service_classes(&record, svclass);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto = sdp_list_append(proto, sdp_list_append(
+ sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
+
+ sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+ proto = sdp_list_append(proto, sdp_list_append(NULL, &obex_uuid));
+
+ sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+ sdp_set_info_attr(&record, "SyncML Client", NULL, NULL);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ return -1;
+ }
+
+ printf("SyncML Client service record registered\n");
+
+ return 0;
+}
+
+static unsigned char async_uuid[] = { 0x03, 0x50, 0x27, 0x8F, 0x3D, 0xCA, 0x4E, 0x62,
+ 0x83, 0x1D, 0xA4, 0x11, 0x65, 0xFF, 0x90, 0x6C };
+
+static int add_activesync(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_record_t record;
+ sdp_list_t *root, *svclass, *proto;
+ uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
+ uint8_t channel = si->channel ? si->channel: 21;
+
+ memset(&record, 0, sizeof(record));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto = sdp_list_append(proto, sdp_list_append(
+ sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
+
+ sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+ sdp_uuid128_create(&svclass_uuid, (void *) async_uuid);
+ svclass = sdp_list_append(NULL, &svclass_uuid);
+ sdp_set_service_classes(&record, svclass);
+
+ sdp_set_info_attr(&record, "Microsoft ActiveSync", NULL, NULL);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ return -1;
+ }
+
+ printf("ActiveSync service record registered\n");
+
+ return 0;
+}
+
+static unsigned char hotsync_uuid[] = { 0xD8, 0x0C, 0xF9, 0xEA, 0x13, 0x4C, 0x11, 0xD5,
+ 0x83, 0xCE, 0x00, 0x30, 0x65, 0x7C, 0x54, 0x3C };
+
+static int add_hotsync(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_record_t record;
+ sdp_list_t *root, *svclass, *proto;
+ uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
+ uint8_t channel = si->channel ? si->channel: 22;
+
+ memset(&record, 0, sizeof(record));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto = sdp_list_append(proto, sdp_list_append(
+ sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
+
+ sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+ sdp_uuid128_create(&svclass_uuid, (void *) hotsync_uuid);
+ svclass = sdp_list_append(NULL, &svclass_uuid);
+ sdp_set_service_classes(&record, svclass);
+
+ sdp_set_info_attr(&record, "PalmOS HotSync", NULL, NULL);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ return -1;
+ }
+
+ printf("HotSync service record registered\n");
+
+ return 0;
+}
+
+static unsigned char palmos_uuid[] = { 0xF5, 0xBE, 0xB6, 0x51, 0x41, 0x71, 0x40, 0x51,
+ 0xAC, 0xF5, 0x6C, 0xA7, 0x20, 0x22, 0x42, 0xF0 };
+
+static int add_palmos(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_record_t record;
+ sdp_list_t *root, *svclass;
+ uuid_t root_uuid, svclass_uuid;
+
+ memset(&record, 0, sizeof(record));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid128_create(&svclass_uuid, (void *) palmos_uuid);
+ svclass = sdp_list_append(NULL, &svclass_uuid);
+ sdp_set_service_classes(&record, svclass);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ return -1;
+ }
+
+ printf("PalmOS service record registered\n");
+
+ return 0;
+}
+
+static unsigned char nokid_uuid[] = { 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 };
+
+static int add_nokiaid(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_record_t record;
+ sdp_list_t *root, *svclass;
+ uuid_t root_uuid, svclass_uuid;
+ uint16_t verid = 0x005f;
+ sdp_data_t *version = sdp_data_alloc(SDP_UINT16, &verid);
+
+ memset(&record, 0, sizeof(record));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid128_create(&svclass_uuid, (void *) nokid_uuid);
+ svclass = sdp_list_append(NULL, &svclass_uuid);
+ sdp_set_service_classes(&record, svclass);
+
+ sdp_attr_add(&record, SDP_ATTR_SERVICE_VERSION, version);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ sdp_data_free(version);
+ return -1;
+ }
+
+ printf("Nokia ID service record registered\n");
+
+ return 0;
+}
+
+static unsigned char pcsuite_uuid[] = { 0x00, 0x00, 0x50, 0x02, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 };
+
+static int add_pcsuite(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_record_t record;
+ sdp_list_t *root, *svclass, *proto;
+ uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
+ uint8_t channel = si->channel ? si->channel: 14;
+
+ memset(&record, 0, sizeof(record));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto = sdp_list_append(proto, sdp_list_append(
+ sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
+
+ sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+ sdp_uuid128_create(&svclass_uuid, (void *) pcsuite_uuid);
+ svclass = sdp_list_append(NULL, &svclass_uuid);
+ sdp_set_service_classes(&record, svclass);
+
+ sdp_set_info_attr(&record, "Nokia PC Suite", NULL, NULL);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ return -1;
+ }
+
+ printf("Nokia PC Suite service registered\n");
+
+ return 0;
+}
+
+static unsigned char nftp_uuid[] = { 0x00, 0x00, 0x50, 0x05, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 };
+
+static unsigned char nsyncml_uuid[] = { 0x00, 0x00, 0x56, 0x01, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 };
+
+static unsigned char ngage_uuid[] = { 0x00, 0x00, 0x13, 0x01, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 };
+
+static unsigned char apple_uuid[] = { 0xf0, 0x72, 0x2e, 0x20, 0x0f, 0x8b, 0x4e, 0x90,
+ 0x8c, 0xc2, 0x1b, 0x46, 0xf5, 0xf2, 0xef, 0xe2 };
+
+static int add_apple(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_record_t record;
+ sdp_list_t *root;
+ uuid_t root_uuid;
+ uint32_t attr783 = 0x00000000;
+ uint32_t attr785 = 0x00000002;
+ uint16_t attr786 = 0x1234;
+
+ memset(&record, 0, sizeof(record));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_attr_add_new(&record, 0x0780, SDP_UUID128, (void *) apple_uuid);
+ sdp_attr_add_new(&record, 0x0781, SDP_TEXT_STR8, (void *) "Macmini");
+ sdp_attr_add_new(&record, 0x0782, SDP_TEXT_STR8, (void *) "PowerMac10,1");
+ sdp_attr_add_new(&record, 0x0783, SDP_UINT32, (void *) &attr783);
+ sdp_attr_add_new(&record, 0x0784, SDP_TEXT_STR8, (void *) "1.6.6f22");
+ sdp_attr_add_new(&record, 0x0785, SDP_UINT32, (void *) &attr785);
+ sdp_attr_add_new(&record, 0x0786, SDP_UUID16, (void *) &attr786);
+
+ sdp_set_info_attr(&record, "Apple Macintosh Attributes", NULL, NULL);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ return -1;
+ }
+
+ printf("Apple attribute service registered\n");
+
+ return 0;
+}
+
+static int add_isync(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_record_t record;
+ sdp_list_t *root, *svclass, *proto;
+ uuid_t root_uuid, svclass_uuid, serial_uuid, l2cap_uuid, rfcomm_uuid;
+ uint8_t channel = si->channel ? si->channel : 16;
+
+ memset(&record, 0, sizeof(record));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto = sdp_list_append(proto, sdp_list_append(
+ sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
+
+ sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+ sdp_uuid16_create(&serial_uuid, SERIAL_PORT_SVCLASS_ID);
+ svclass = sdp_list_append(NULL, &serial_uuid);
+
+ sdp_uuid16_create(&svclass_uuid, APPLE_AGENT_SVCLASS_ID);
+ svclass = sdp_list_append(svclass, &svclass_uuid);
+
+ sdp_set_service_classes(&record, svclass);
+
+ sdp_set_info_attr(&record, "AppleAgent", "Bluetooth acceptor", "Apple Computer Ltd.");
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ return -1;
+ }
+
+ printf("Apple iSync service registered\n");
+
+ return 0;
+}
+
+static int add_semchla(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_record_t record;
+ sdp_profile_desc_t profile;
+ sdp_list_t *root, *svclass, *proto, *profiles;
+ uuid_t root_uuid, service_uuid, l2cap_uuid, semchla_uuid;
+ uint16_t psm = 0xf0f9;
+
+ memset(&record, 0, sizeof(record));
+ record.handle = si->handle;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto = sdp_list_append(NULL, sdp_list_append(
+ sdp_list_append(NULL, &l2cap_uuid), sdp_data_alloc(SDP_UINT16, &psm)));
+
+ sdp_uuid32_create(&semchla_uuid, 0x8e770300);
+ proto = sdp_list_append(proto, sdp_list_append(NULL, &semchla_uuid));
+
+ sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+ sdp_uuid32_create(&service_uuid, 0x8e771301);
+ svclass = sdp_list_append(NULL, &service_uuid);
+
+ sdp_set_service_classes(&record, svclass);
+
+ sdp_uuid32_create(&profile.uuid, 0x8e771302); // Headset
+ //sdp_uuid32_create(&profile.uuid, 0x8e771303); // Phone
+ profile.version = 0x0100;
+ profiles = sdp_list_append(NULL, &profile);
+ sdp_set_profile_descs(&record, profiles);
+
+ sdp_set_info_attr(&record, "SEMC HLA", NULL, NULL);
+
+ if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+ printf("Service Record registration failed\n");
+ return -1;
+ }
+
+ /* SEMC High Level Authentication */
+ printf("SEMC HLA service registered\n");
+
+ return 0;
+}
+
+static int add_gatt(sdp_session_t *session, svc_info_t *si)
+{
+ sdp_list_t *svclass_id, *apseq, *proto[2], *profiles, *root, *aproto;
+ uuid_t root_uuid, proto_uuid, gatt_uuid, l2cap;
+ sdp_profile_desc_t profile;
+ sdp_record_t record;
+ sdp_data_t *psm, *sh, *eh;
+ uint16_t att_psm = 27, start = 0x0001, end = 0x000f;
+ int ret;
+
+ memset(&record, 0, sizeof(sdp_record_t));
+ record.handle = si->handle;
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(&record, root);
+ sdp_list_free(root, NULL);
+
+ sdp_uuid16_create(&gatt_uuid, GENERIC_ATTRIB_SVCLASS_ID);
+ svclass_id = sdp_list_append(NULL, &gatt_uuid);
+ sdp_set_service_classes(&record, svclass_id);
+ sdp_list_free(svclass_id, NULL);
+
+ sdp_uuid16_create(&profile.uuid, GENERIC_ATTRIB_PROFILE_ID);
+ profile.version = 0x0100;
+ profiles = sdp_list_append(NULL, &profile);
+ sdp_set_profile_descs(&record, profiles);
+ sdp_list_free(profiles, NULL);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(NULL, &l2cap);
+ psm = sdp_data_alloc(SDP_UINT16, &att_psm);
+ proto[0] = sdp_list_append(proto[0], psm);
+ apseq = sdp_list_append(NULL, proto[0]);
+
+ sdp_uuid16_create(&proto_uuid, ATT_UUID);
+ proto[1] = sdp_list_append(NULL, &proto_uuid);
+ sh = sdp_data_alloc(SDP_UINT16, &start);
+ proto[1] = sdp_list_append(proto[1], sh);
+ eh = sdp_data_alloc(SDP_UINT16, &end);
+ proto[1] = sdp_list_append(proto[1], eh);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(NULL, apseq);
+ sdp_set_access_protos(&record, aproto);
+
+ sdp_set_info_attr(&record, "Generic Attribute Profile", "BlueZ", NULL);
+
+ sdp_set_url_attr(&record, "http://www.bluez.org/",
+ "http://www.bluez.org/", "http://www.bluez.org/");
+
+ sdp_set_service_id(&record, gatt_uuid);
+
+ ret = sdp_device_record_register(session, &interface, &record,
+ SDP_RECORD_PERSIST);
+ if (ret < 0)
+ printf("Service Record registration failed\n");
+ else
+ printf("Generic Attribute Profile Service registered\n");
+
+ sdp_data_free(psm);
+ sdp_data_free(sh);
+ sdp_data_free(eh);
+ sdp_list_free(proto[0], NULL);
+ sdp_list_free(proto[1], NULL);
+ sdp_list_free(apseq, NULL);
+ sdp_list_free(aproto, NULL);
+
+ return ret;
+}
+
+struct {
+ char *name;
+ uint32_t class;
+ int (*add)(sdp_session_t *sess, svc_info_t *si);
+ unsigned char *uuid;
+} service[] = {
+ { "DID", PNP_INFO_SVCLASS_ID, NULL, },
+
+ { "SP", SERIAL_PORT_SVCLASS_ID, add_sp },
+ { "DUN", DIALUP_NET_SVCLASS_ID, add_dun },
+ { "LAN", LAN_ACCESS_SVCLASS_ID, add_lan },
+ { "FAX", FAX_SVCLASS_ID, add_fax },
+ { "OPUSH", OBEX_OBJPUSH_SVCLASS_ID, add_opush },
+ { "FTP", OBEX_FILETRANS_SVCLASS_ID, add_ftp },
+ { "PRINT", DIRECT_PRINTING_SVCLASS_ID, add_directprint },
+
+ { "HS", HEADSET_SVCLASS_ID, add_headset },
+ { "HSAG", HEADSET_AGW_SVCLASS_ID, add_headset_ag },
+ { "HF", HANDSFREE_SVCLASS_ID, add_handsfree },
+ { "HFAG", HANDSFREE_AGW_SVCLASS_ID, add_handsfree_ag},
+ { "SAP", SAP_SVCLASS_ID, add_simaccess },
+ { "PBAP", PBAP_SVCLASS_ID, add_pbap, },
+
+ { "NAP", NAP_SVCLASS_ID, add_nap },
+ { "GN", GN_SVCLASS_ID, add_gn },
+ { "PANU", PANU_SVCLASS_ID, add_panu },
+
+ { "HCRP", HCR_SVCLASS_ID, NULL },
+ { "HID", HID_SVCLASS_ID, NULL },
+ { "KEYB", HID_SVCLASS_ID, add_hid_keyb },
+ { "WIIMOTE", HID_SVCLASS_ID, add_hid_wiimote },
+ { "CIP", CIP_SVCLASS_ID, add_cip },
+ { "CTP", CORDLESS_TELEPHONY_SVCLASS_ID, add_ctp },
+
+ { "A2SRC", AUDIO_SOURCE_SVCLASS_ID, add_a2source },
+ { "A2SNK", AUDIO_SINK_SVCLASS_ID, add_a2sink },
+ { "AVRCT", AV_REMOTE_SVCLASS_ID, add_avrct },
+ { "AVRTG", AV_REMOTE_TARGET_SVCLASS_ID, add_avrtg },
+
+ { "UDIUE", UDI_MT_SVCLASS_ID, add_udi_ue },
+ { "UDITE", UDI_TA_SVCLASS_ID, add_udi_te },
+
+ { "SEMCHLA", 0x8e771301, add_semchla },
+
+ { "SR1", 0, add_sr1, sr1_uuid },
+ { "SYNCML", 0, add_syncml, syncmlc_uuid },
+ { "SYNCMLSERV", 0, NULL, syncmls_uuid },
+ { "ACTIVESYNC", 0, add_activesync, async_uuid },
+ { "HOTSYNC", 0, add_hotsync, hotsync_uuid },
+ { "PALMOS", 0, add_palmos, palmos_uuid },
+ { "NOKID", 0, add_nokiaid, nokid_uuid },
+ { "PCSUITE", 0, add_pcsuite, pcsuite_uuid },
+ { "NFTP", 0, NULL, nftp_uuid },
+ { "NSYNCML", 0, NULL, nsyncml_uuid },
+ { "NGAGE", 0, NULL, ngage_uuid },
+ { "APPLE", 0, add_apple, apple_uuid },
+
+ { "ISYNC", APPLE_AGENT_SVCLASS_ID, add_isync, },
+ { "GATT", GENERIC_ATTRIB_SVCLASS_ID, add_gatt, },
+
+ { 0 }
+};
+
+/* Add local service */
+static int add_service(bdaddr_t *bdaddr, svc_info_t *si)
+{
+ sdp_session_t *sess;
+ int i, ret = -1;
+
+ if (!si->name)
+ return -1;
+
+ sess = sdp_connect(&interface, BDADDR_LOCAL, SDP_RETRY_IF_BUSY);
+ if (!sess)
+ return -1;
+
+ for (i = 0; service[i].name; i++)
+ if (!strcasecmp(service[i].name, si->name)) {
+ if (service[i].add)
+ ret = service[i].add(sess, si);
+ goto done;
+ }
+
+ printf("Unknown service name: %s\n", si->name);
+
+done:
+ free(si->name);
+ sdp_close(sess);
+
+ return ret;
+}
+
+static struct option add_options[] = {
+ { "help", 0, 0, 'h' },
+ { "handle", 1, 0, 'r' },
+ { "psm", 1, 0, 'p' },
+ { "channel", 1, 0, 'c' },
+ { "network", 1, 0, 'n' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *add_help =
+ "Usage:\n"
+ "\tadd [--handle=RECORD_HANDLE --channel=CHANNEL] service\n";
+
+static int cmd_add(int argc, char **argv)
+{
+ svc_info_t si;
+ int opt;
+
+ memset(&si, 0, sizeof(si));
+ si.handle = 0xffffffff;
+
+ for_each_opt(opt, add_options, 0) {
+ switch (opt) {
+ case 'r':
+ if (strncasecmp(optarg, "0x", 2))
+ si.handle = atoi(optarg);
+ else
+ si.handle = strtol(optarg + 2, NULL, 16);
+ break;
+ case 'p':
+ if (strncasecmp(optarg, "0x", 2))
+ si.psm = atoi(optarg);
+ else
+ si.psm = strtol(optarg + 2, NULL, 16);
+ break;
+ case 'c':
+ if (strncasecmp(optarg, "0x", 2))
+ si.channel = atoi(optarg);
+ else
+ si.channel = strtol(optarg + 2, NULL, 16);
+ break;
+ case 'n':
+ if (strncasecmp(optarg, "0x", 2))
+ si.network = atoi(optarg);
+ else
+ si.network = strtol(optarg + 2, NULL, 16);
+ break;
+ default:
+ printf("%s", add_help);
+ return -1;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ printf("%s", add_help);
+ return -1;
+ }
+
+ si.name = strdup(argv[0]);
+
+ return add_service(0, &si);
+}
+
+/* Delete local service */
+static int del_service(bdaddr_t *bdaddr, void *arg)
+{
+ uint32_t handle, range = 0x0000ffff;
+ sdp_list_t *attr;
+ sdp_session_t *sess;
+ sdp_record_t *rec;
+
+ if (!arg) {
+ printf("Record handle was not specified.\n");
+ return -1;
+ }
+
+ sess = sdp_connect(&interface, BDADDR_LOCAL, SDP_RETRY_IF_BUSY);
+ if (!sess) {
+ printf("No local SDP server!\n");
+ return -1;
+ }
+
+ handle = strtoul((char *)arg, 0, 16);
+ attr = sdp_list_append(0, &range);
+ rec = sdp_service_attr_req(sess, handle, SDP_ATTR_REQ_RANGE, attr);
+ sdp_list_free(attr, 0);
+
+ if (!rec) {
+ printf("Service Record not found.\n");
+ sdp_close(sess);
+ return -1;
+ }
+
+ if (sdp_device_record_unregister(sess, &interface, rec)) {
+ printf("Failed to unregister service record: %s\n", strerror(errno));
+ sdp_close(sess);
+ return -1;
+ }
+
+ printf("Service Record deleted.\n");
+ sdp_close(sess);
+
+ return 0;
+}
+
+static struct option del_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *del_help =
+ "Usage:\n"
+ "\tdel record_handle\n";
+
+static int cmd_del(int argc, char **argv)
+{
+ int opt;
+
+ for_each_opt(opt, del_options, 0) {
+ switch (opt) {
+ default:
+ printf("%s", del_help);
+ return -1;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ printf("%s", del_help);
+ return -1;
+ }
+
+ return del_service(NULL, argv[0]);
+}
+
+/*
+ * Perform an inquiry and search/browse all peer found.
+ */
+static void inquiry(handler_t handler, void *arg)
+{
+ inquiry_info ii[20];
+ uint8_t count = 0;
+ int i;
+
+ printf("Inquiring ...\n");
+ if (sdp_general_inquiry(ii, 20, 8, &count) < 0) {
+ printf("Inquiry failed\n");
+ return;
+ }
+
+ for (i = 0; i < count; i++)
+ handler(&ii[i].bdaddr, arg);
+}
+
+static void doprintf(void *data, const char *str)
+{
+ printf("%s", str);
+}
+
+/*
+ * Search for a specific SDP service
+ */
+static int do_search(bdaddr_t *bdaddr, struct search_context *context)
+{
+ sdp_list_t *attrid, *search, *seq, *next;
+ uint32_t range = 0x0000ffff;
+ char str[20];
+ sdp_session_t *sess;
+
+ if (!bdaddr) {
+ inquiry(do_search, context);
+ return 0;
+ }
+
+ sess = sdp_connect(&interface, bdaddr, SDP_RETRY_IF_BUSY);
+ ba2str(bdaddr, str);
+ if (!sess) {
+ printf("Failed to connect to SDP server on %s: %s\n", str, strerror(errno));
+ return -1;
+ }
+
+ if (context->view != RAW_VIEW) {
+ if (context->svc)
+ printf("Searching for %s on %s ...\n", context->svc, str);
+ else
+ printf("Browsing %s ...\n", str);
+ }
+
+ attrid = sdp_list_append(0, &range);
+ search = sdp_list_append(0, &context->group);
+ if (sdp_service_search_attr_req(sess, search, SDP_ATTR_REQ_RANGE, attrid, &seq)) {
+ printf("Service Search failed: %s\n", strerror(errno));
+ sdp_close(sess);
+ return -1;
+ }
+ sdp_list_free(attrid, 0);
+ sdp_list_free(search, 0);
+
+ for (; seq; seq = next) {
+ sdp_record_t *rec = (sdp_record_t *) seq->data;
+ struct search_context sub_context;
+
+ switch (context->view) {
+ case DEFAULT_VIEW:
+ /* Display user friendly form */
+ print_service_attr(rec);
+ printf("\n");
+ break;
+ case TREE_VIEW:
+ /* Display full tree */
+ print_tree_attr(rec);
+ printf("\n");
+ break;
+ case XML_VIEW:
+ /* Display raw XML tree */
+ convert_sdp_record_to_xml(rec, 0, doprintf);
+ break;
+ default:
+ /* Display raw tree */
+ print_raw_attr(rec);
+ break;
+ }
+
+ if (sdp_get_group_id(rec, &sub_context.group) != -1) {
+ /* Set the subcontext for browsing the sub tree */
+ memcpy(&sub_context, context, sizeof(struct search_context));
+ /* Browse the next level down if not done */
+ if (sub_context.group.value.uuid16 != context->group.value.uuid16)
+ do_search(bdaddr, &sub_context);
+ }
+ next = seq->next;
+ free(seq);
+ sdp_record_free(rec);
+ }
+
+ sdp_close(sess);
+ return 0;
+}
+
+static struct option browse_options[] = {
+ { "help", 0, 0, 'h' },
+ { "tree", 0, 0, 't' },
+ { "raw", 0, 0, 'r' },
+ { "xml", 0, 0, 'x' },
+ { "uuid", 1, 0, 'u' },
+ { "l2cap", 0, 0, 'l' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *browse_help =
+ "Usage:\n"
+ "\tbrowse [--tree] [--raw] [--xml] [--uuid uuid] [--l2cap] [bdaddr]\n";
+
+/*
+ * Browse the full SDP database (i.e. list all services starting from the
+ * root/top-level).
+ */
+static int cmd_browse(int argc, char **argv)
+{
+ struct search_context context;
+ int opt, num;
+
+ /* Initialise context */
+ memset(&context, '\0', sizeof(struct search_context));
+ /* We want to browse the top-level/root */
+ sdp_uuid16_create(&context.group, PUBLIC_BROWSE_GROUP);
+
+ for_each_opt(opt, browse_options, 0) {
+ switch (opt) {
+ case 't':
+ context.view = TREE_VIEW;
+ break;
+ case 'r':
+ context.view = RAW_VIEW;
+ break;
+ case 'x':
+ context.view = XML_VIEW;
+ break;
+ case 'u':
+ if (sscanf(optarg, "%i", &num) != 1 || num < 0 || num > 0xffff) {
+ printf("Invalid uuid %s\n", optarg);
+ return -1;
+ }
+ sdp_uuid16_create(&context.group, num);
+ break;
+ case 'l':
+ sdp_uuid16_create(&context.group, L2CAP_UUID);
+ break;
+ default:
+ printf("%s", browse_help);
+ return -1;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc >= 1) {
+ bdaddr_t bdaddr;
+ estr2ba(argv[0], &bdaddr);
+ return do_search(&bdaddr, &context);
+ }
+
+ return do_search(NULL, &context);
+}
+
+static struct option search_options[] = {
+ { "help", 0, 0, 'h' },
+ { "bdaddr", 1, 0, 'b' },
+ { "tree", 0, 0, 't' },
+ { "raw", 0, 0, 'r' },
+ { "xml", 0, 0, 'x' },
+ { 0, 0, 0, 0}
+};
+
+static const char *search_help =
+ "Usage:\n"
+ "\tsearch [--bdaddr bdaddr] [--tree] [--raw] [--xml] SERVICE\n"
+ "SERVICE is a name (string) or UUID (0x1002)\n";
+
+/*
+ * Search for a specific SDP service
+ *
+ * Note : we should support multiple services on the command line :
+ * sdptool search 0x0100 0x000f 0x1002
+ * (this would search a service supporting both L2CAP and BNEP directly in
+ * the top level browse group)
+ */
+static int cmd_search(int argc, char **argv)
+{
+ struct search_context context;
+ unsigned char *uuid = NULL;
+ uint32_t class = 0;
+ bdaddr_t bdaddr;
+ int has_addr = 0;
+ int i;
+ int opt;
+
+ /* Initialise context */
+ memset(&context, '\0', sizeof(struct search_context));
+
+ for_each_opt(opt, search_options, 0) {
+ switch (opt) {
+ case 'b':
+ estr2ba(optarg, &bdaddr);
+ has_addr = 1;
+ break;
+ case 't':
+ context.view = TREE_VIEW;
+ break;
+ case 'r':
+ context.view = RAW_VIEW;
+ break;
+ case 'x':
+ context.view = XML_VIEW;
+ break;
+ default:
+ printf("%s", search_help);
+ return -1;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ printf("%s", search_help);
+ return -1;
+ }
+
+ /* Note : we need to find a way to support search combining
+ * multiple services */
+ context.svc = strdup(argv[0]);
+ if (!strncasecmp(context.svc, "0x", 2)) {
+ int num;
+ /* This is a UUID16, just convert to int */
+ sscanf(context.svc + 2, "%X", &num);
+ class = num;
+ printf("Class 0x%X\n", class);
+ } else {
+ /* Convert class name to an UUID */
+
+ for (i = 0; service[i].name; i++)
+ if (strcasecmp(context.svc, service[i].name) == 0) {
+ class = service[i].class;
+ uuid = service[i].uuid;
+ break;
+ }
+ if (!class && !uuid) {
+ printf("Unknown service %s\n", context.svc);
+ return -1;
+ }
+ }
+
+ if (class) {
+ if (class & 0xffff0000)
+ sdp_uuid32_create(&context.group, class);
+ else {
+ uint16_t class16 = class & 0xffff;
+ sdp_uuid16_create(&context.group, class16);
+ }
+ } else
+ sdp_uuid128_create(&context.group, uuid);
+
+ if (has_addr)
+ return do_search(&bdaddr, &context);
+
+ return do_search(NULL, &context);
+}
+
+/*
+ * Show how to get a specific SDP record by its handle.
+ * Not really useful to the user, just show how it can be done...
+ */
+static int get_service(bdaddr_t *bdaddr, struct search_context *context, int quite)
+{
+ sdp_list_t *attrid;
+ uint32_t range = 0x0000ffff;
+ sdp_record_t *rec;
+ sdp_session_t *session = sdp_connect(&interface, bdaddr, SDP_RETRY_IF_BUSY);
+
+ if (!session) {
+ char str[20];
+ ba2str(bdaddr, str);
+ printf("Failed to connect to SDP server on %s: %s\n", str, strerror(errno));
+ return -1;
+ }
+
+ attrid = sdp_list_append(0, &range);
+ rec = sdp_service_attr_req(session, context->handle, SDP_ATTR_REQ_RANGE, attrid);
+ sdp_list_free(attrid, 0);
+ sdp_close(session);
+
+ if (!rec) {
+ if (!quite) {
+ printf("Service get request failed.\n");
+ return -1;
+ } else
+ return 0;
+ }
+
+ switch (context->view) {
+ case DEFAULT_VIEW:
+ /* Display user friendly form */
+ print_service_attr(rec);
+ printf("\n");
+ break;
+ case TREE_VIEW:
+ /* Display full tree */
+ print_tree_attr(rec);
+ printf("\n");
+ break;
+ case XML_VIEW:
+ /* Display raw XML tree */
+ convert_sdp_record_to_xml(rec, 0, doprintf);
+ break;
+ default:
+ /* Display raw tree */
+ print_raw_attr(rec);
+ break;
+ }
+
+ sdp_record_free(rec);
+ return 0;
+}
+
+static struct option records_options[] = {
+ { "help", 0, 0, 'h' },
+ { "tree", 0, 0, 't' },
+ { "raw", 0, 0, 'r' },
+ { "xml", 0, 0, 'x' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *records_help =
+ "Usage:\n"
+ "\trecords [--tree] [--raw] [--xml] bdaddr\n";
+
+/*
+ * Request possible SDP service records
+ */
+static int cmd_records(int argc, char **argv)
+{
+ struct search_context context;
+ uint32_t base[] = { 0x10000, 0x10300, 0x10500,
+ 0x1002e, 0x110b, 0x90000, 0x2008000,
+ 0x4000000, 0x100000, 0x1000000,
+ 0x4f491100, 0x4f491200 };
+ bdaddr_t bdaddr;
+ unsigned int i, n, num = 32;
+ int opt, err = 0;
+
+ /* Initialise context */
+ memset(&context, '\0', sizeof(struct search_context));
+
+ for_each_opt(opt, records_options, 0) {
+ switch (opt) {
+ case 't':
+ context.view = TREE_VIEW;
+ break;
+ case 'r':
+ context.view = RAW_VIEW;
+ break;
+ case 'x':
+ context.view = XML_VIEW;
+ break;
+ default:
+ printf("%s", records_help);
+ return -1;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ printf("%s", records_help);
+ return -1;
+ }
+
+ /* Convert command line parameters */
+ estr2ba(argv[0], &bdaddr);
+
+ for (i = 0; i < sizeof(base) / sizeof(uint32_t); i++)
+ for (n = 0; n < num; n++) {
+ context.handle = base[i] + n;
+ err = get_service(&bdaddr, &context, 1);
+ if (err < 0)
+ goto done;
+ }
+
+done:
+ return 0;
+}
+
+static struct option get_options[] = {
+ { "help", 0, 0, 'h' },
+ { "bdaddr", 1, 0, 'b' },
+ { "tree", 0, 0, 't' },
+ { "raw", 0, 0, 'r' },
+ { "xml", 0, 0, 'x' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *get_help =
+ "Usage:\n"
+ "\tget [--tree] [--raw] [--xml] [--bdaddr bdaddr] record_handle\n";
+
+/*
+ * Get a specific SDP record on the local SDP server
+ */
+static int cmd_get(int argc, char **argv)
+{
+ struct search_context context;
+ bdaddr_t bdaddr;
+ int has_addr = 0;
+ int opt;
+
+ /* Initialise context */
+ memset(&context, '\0', sizeof(struct search_context));
+
+ for_each_opt(opt, get_options, 0) {
+ switch (opt) {
+ case 'b':
+ estr2ba(optarg, &bdaddr);
+ has_addr = 1;
+ break;
+ case 't':
+ context.view = TREE_VIEW;
+ break;
+ case 'r':
+ context.view = RAW_VIEW;
+ break;
+ case 'x':
+ context.view = XML_VIEW;
+ break;
+ default:
+ printf("%s", get_help);
+ return -1;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ printf("%s", get_help);
+ return -1;
+ }
+
+ /* Convert command line parameters */
+ context.handle = strtoul(argv[0], 0, 16);
+
+ return get_service(has_addr ? &bdaddr : BDADDR_LOCAL, &context, 0);
+}
+
+static struct {
+ char *cmd;
+ int (*func)(int argc, char **argv);
+ char *doc;
+} command[] = {
+ { "search", cmd_search, "Search for a service" },
+ { "browse", cmd_browse, "Browse all available services" },
+ { "records", cmd_records, "Request all records" },
+ { "add", cmd_add, "Add local service" },
+ { "del", cmd_del, "Delete local service" },
+ { "get", cmd_get, "Get local service" },
+ { "setattr", cmd_setattr, "Set/Add attribute to a SDP record" },
+ { "setseq", cmd_setseq, "Set/Add attribute sequence to a SDP record" },
+ { 0, 0, 0 }
+};
+
+static void usage(void)
+{
+ int i, pos = 0;
+
+ printf("sdptool - SDP tool v%s\n", VERSION);
+ printf("Usage:\n"
+ "\tsdptool [options] <command> [command parameters]\n");
+ printf("Options:\n"
+ "\t-h\t\tDisplay help\n"
+ "\t-i\t\tSpecify source interface\n");
+
+ printf("Commands:\n");
+ for (i = 0; command[i].cmd; i++)
+ printf("\t%-4s\t\t%s\n", command[i].cmd, command[i].doc);
+
+ printf("\nServices:\n\t");
+ for (i = 0; service[i].name; i++) {
+ printf("%s ", service[i].name);
+ pos += strlen(service[i].name) + 1;
+ if (pos > 60) {
+ printf("\n\t");
+ pos = 0;
+ }
+ }
+ 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[])
+{
+ int i, opt;
+
+ bacpy(&interface, 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), &interface);
+ else
+ str2ba(optarg, &interface);
+ break;
+
+ case 'h':
+ usage();
+ exit(0);
+
+ default:
+ exit(1);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (argc < 1) {
+ usage();
+ exit(1);
+ }
+
+ for (i = 0; command[i].cmd; i++)
+ if (strncmp(command[i].cmd, argv[0], 4) == 0)
+ return command[i].func(argc, argv);
+
+ return 1;
+}
diff --git a/tools/ubcsp.c b/tools/ubcsp.c
new file mode 100644
index 0000000..93b8c0f
--- /dev/null
+++ b/tools/ubcsp.c
@@ -0,0 +1,1180 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2000-2005 CSR Ltd.
+ *
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/*****************************************************************************/
+/*****************************************************************************/
+/*****************************************************************************/
+/** **/
+/** ubcsp,c **/
+/** **/
+/** MicroBCSP - a very low cost implementation of the BCSP protocol **/
+/** **/
+/*****************************************************************************/
+
+#include "ubcsp.h"
+
+#if SHOW_PACKET_ERRORS || SHOW_LE_STATES
+#include <stdio.h>
+#include <windows.h>
+#endif
+
+static uint16 ubcsp_calc_crc (uint8 ch, uint16 crc);
+static uint16 ubcsp_crc_reverse (uint16);
+
+/*****************************************************************************/
+/** **/
+/** Constant Data - ROM **/
+/** **/
+/*****************************************************************************/
+
+/* This is the storage for the link establishment messages */
+
+static const uint8 ubcsp_le_buffer[4][4] =
+ {
+ { 0xDA, 0xDC, 0xED, 0xED },
+ { 0xAC, 0xAF, 0xEF, 0xEE },
+ { 0xAD, 0xEF, 0xAC, 0xED },
+ { 0xDE, 0xAD, 0xD0, 0xD0 },
+ };
+
+/* These are the link establishment headers */
+/* The two version are for the CRC and non-CRC varients */
+
+#if UBCSP_CRC
+static const uint8 ubcsp_send_le_header[4] =
+ {
+ 0x40, 0x41, 0x00, 0x7E
+ };
+#else
+static const uint8 ubcsp_send_le_header[4] =
+ {
+ 0x00, 0x41, 0x00, 0xBE
+ };
+#endif
+
+/*****************************************************************************/
+/** **/
+/** Static Data - RAM **/
+/** **/
+/*****************************************************************************/
+
+/* This is the storage for all state data for ubcsp */
+
+static struct ubcsp_configuration ubcsp_config;
+
+/* This is the ACK packet header - this will be overwritten when
+ we create an ack packet */
+
+static uint8 ubcsp_send_ack_header[4] =
+ {
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+/* This is the deslip lookup table */
+
+static const uint8 ubcsp_deslip[2] =
+ {
+ SLIP_FRAME, SLIP_ESCAPE,
+ };
+
+/* This is a state machine table for link establishment */
+
+static uint8 next_le_packet[16] =
+ {
+ ubcsp_le_sync, // uninit
+ ubcsp_le_conf, // init
+ ubcsp_le_none, // active
+ ubcsp_le_none,
+ ubcsp_le_sync_resp, // sync_resp
+ ubcsp_le_sync_resp,
+ ubcsp_le_none,
+ ubcsp_le_none,
+ ubcsp_le_none, // conf_resp
+ ubcsp_le_conf_resp,
+ ubcsp_le_conf_resp,
+ ubcsp_le_none,
+ };
+
+/* This is the storage required for building send and crc data */
+
+static uint8 ubcsp_send_header[4];
+static uint8 ubcsp_send_crc[2];
+
+/* This is where the receive header is stored before the payload arrives */
+
+static uint8 ubcsp_receive_header[4];
+
+/*****************************************************************************/
+/** **/
+/** Code - ROM or RAM **/
+/** **/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/** **/
+/** ubcsp_initialize **/
+/** **/
+/** This initializes the state of the ubcsp engine to a known values **/
+/** **/
+/*****************************************************************************/
+
+void ubcsp_initialize (void)
+{
+ ubcsp_config.ack_number = 0;
+ ubcsp_config.sequence_number = 0;
+ ubcsp_config.send_ptr = 0;
+ ubcsp_config.send_size = 0;
+ ubcsp_config.receive_index = -4;
+
+ ubcsp_config.delay = 0;
+
+#if SHOW_LE_STATES
+ printf ("Hello Link Uninitialized\n");
+#endif
+
+ ubcsp_config.link_establishment_state = ubcsp_le_uninitialized;
+ ubcsp_config.link_establishment_packet = ubcsp_le_sync;
+}
+
+/*****************************************************************************/
+/** **/
+/** ubcsp_send_packet **/
+/** **/
+/** This sends a packet structure for sending to the ubcsp engine **/
+/** This can only be called when the activity indication from ubcsp_poll **/
+/** indicates that a packet can be sent with UBCSP_PACKET_SENT **/
+/** **/
+/*****************************************************************************/
+
+void ubcsp_send_packet (struct ubcsp_packet *send_packet)
+{
+ /* Initialize the send data to the packet we want to send */
+
+ ubcsp_config.send_packet = send_packet;
+
+ /* we cannot send the packet at the moment
+ when we can at the moment, just set things to 0 */
+
+ ubcsp_config.send_size = 0;
+ ubcsp_config.send_ptr = 0;
+}
+
+/*****************************************************************************/
+/** **/
+/** ubcsp_receive_packet **/
+/** **/
+/** This sends a packet structure for receiving to the ubcsp engine **/
+/** This can only be called when the activity indication from ubcsp_poll **/
+/** indicates that a packet can be sent with UBCSP_PACKET_RECEIVED **/
+/** **/
+/*****************************************************************************/
+
+void ubcsp_receive_packet (struct ubcsp_packet *receive_packet)
+{
+ /* Initialize the receive data to the packet we want to receive */
+
+ ubcsp_config.receive_packet = receive_packet;
+
+ /* setup to receive the header first */
+
+ ubcsp_config.receive_index = -4;
+}
+
+/*****************************************************************************/
+/** **/
+/** ubcsp_calc_crc **/
+/** **/
+/** Takes the next 8 bit value ch, and updates the crc with this value **/
+/** **/
+/*****************************************************************************/
+
+
+#ifdef UBCSP_CRC
+
+static uint16 ubcsp_calc_crc (uint8 ch, uint16 crc)
+{
+ /* Calculate the CRC using the above 16 entry lookup table */
+
+ static const uint16 crc_table[] =
+ {
+ 0x0000, 0x1081, 0x2102, 0x3183,
+ 0x4204, 0x5285, 0x6306, 0x7387,
+ 0x8408, 0x9489, 0xa50a, 0xb58b,
+ 0xc60c, 0xd68d, 0xe70e, 0xf78f
+ };
+
+ /* Do this four bits at a time - more code, less space */
+
+ crc = (crc >> 4) ^ crc_table[(crc ^ ch) & 0x000f];
+ crc = (crc >> 4) ^ crc_table[(crc ^ (ch >> 4)) & 0x000f];
+
+ return crc;
+}
+
+/*****************************************************************************/
+/** **/
+/** ubcsp_crc_reverse **/
+/** **/
+/** Reserves the bits in crc and returns the new value **/
+/** **/
+/*****************************************************************************/
+
+static uint16 ubcsp_crc_reverse (uint16 crc)
+{
+ int32
+ b,
+ rev;
+
+ /* Reserse the bits to compute the actual CRC value */
+
+ for (b = 0, rev=0; b < 16; b++)
+ {
+ rev = rev << 1;
+ rev |= (crc & 1);
+ crc = crc >> 1;
+ }
+
+ return rev;
+}
+
+#endif
+
+/*****************************************************************************/
+/** **/
+/** ubcsp_put_slip_uart **/
+/** **/
+/** Outputs a single octet to the uart **/
+/** If the octet needs to be escaped, then output the escape value **/
+/** and then store the second octet to be output later **/
+/** **/
+/*****************************************************************************/
+
+static void ubcsp_put_slip_uart (uint8 ch)
+{
+ /* output a single UART octet */
+
+ /* If it needs to be escaped, then output the escape octet
+ and set the send_slip_escape so that the next time we
+ output the second octet for the escape correctly.
+ This is done right at the top of ubcsp_poll */
+
+ if (ch == SLIP_FRAME)
+ {
+ put_uart (SLIP_ESCAPE);
+ ubcsp_config.send_slip_escape = SLIP_ESCAPE_FRAME;
+ }
+ else if (ch == SLIP_ESCAPE)
+ {
+ put_uart (SLIP_ESCAPE);
+ ubcsp_config.send_slip_escape = SLIP_ESCAPE_ESCAPE;
+ }
+ else
+ {
+ /* Not escaped, so just output octet */
+
+ put_uart (ch);
+ }
+}
+
+/*****************************************************************************/
+/** **/
+/** ubcsp_which_le_payload **/
+/** **/
+/** Check the payload of this packet, and determine which of the four **/
+/** link establishment packets this was. **/
+/** Can return 5 if it is not a valid link establishment packet **/
+/** **/
+/*****************************************************************************/
+
+static uint32 ubcsp_which_le_payload (const uint8 *payload)
+{
+ static int32
+ octet,
+ loop;
+
+ /* Search through the various link establishment payloads to find
+ which one we have received */
+
+ for (loop = 0; loop < 4; loop ++)
+ {
+ for (octet = 0; octet < 4; octet ++)
+ {
+ if (payload[octet] != ubcsp_le_buffer[loop][octet])
+ {
+ /* Bad match, just to loop again */
+ goto bad_match_loop;
+ }
+ }
+
+ /* All the octets matched, return the value */
+
+ return loop;
+
+ /* Jumps out of octet loop if we got a bad match */
+bad_match_loop:
+ {}
+ }
+
+ /* Non of the link establishment payloads matched - return invalid value */
+
+ return 5;
+}
+
+/*****************************************************************************/
+/** **/
+/** ubcsp_recevied_packet **/
+/** **/
+/** This function is called when we have a SLIP END octet and a full **/
+/** packet header and possibly data in the receive packet **/
+/** **/
+/*****************************************************************************/
+
+static uint8 ubcsp_recevied_packet (void)
+{
+ static uint8
+ receive_crc,
+ receive_seq,
+ receive_ack,
+ activity;
+
+#if UBCSP_CRC
+ static int32
+ loop;
+
+ static uint16
+ crc;
+#endif
+
+ static uint16
+ length;
+
+ /* Keep track of what activity this received packet will cause */
+
+ activity = 0;
+
+ /*** Do all error checks that we can ***/
+
+ /* First check the header checksum */
+
+ if (((ubcsp_receive_header[0] + ubcsp_receive_header[1] + ubcsp_receive_header[2] + ubcsp_receive_header[3]) & 0xff) != 0xff)
+ {
+ /* Header Checksum Error */
+
+#if SHOW_PACKET_ERRORS
+ printf ("\n######################## Header Checksum Error %02X %02X %02X %02X\n",
+ ubcsp_receive_header[0],
+ ubcsp_receive_header[1],
+ ubcsp_receive_header[2],
+ ubcsp_receive_header[3]);
+#endif
+
+ /* If we have a header checksum error, send an ack in return
+ this gets a packet to be resent as quickly as possible */
+
+ ubcsp_config.send_ack = 1;
+
+ return activity;
+ }
+
+ /* Decode the received packets header */
+
+ ubcsp_config.receive_packet->reliable = (ubcsp_receive_header[0] & 0x80) >> 7;
+
+ receive_crc = (ubcsp_receive_header[0] & 0x40) >> 6;
+ receive_ack = (ubcsp_receive_header[0] & 0x38) >> 3;
+ receive_seq = (ubcsp_receive_header[0] & 0x07);
+
+ ubcsp_config.receive_packet->channel = (ubcsp_receive_header[1] & 0x0f);
+
+ length =
+ ((ubcsp_receive_header[1] & 0xf0) >> 4) |
+ (ubcsp_receive_header[2] << 4);
+
+#if SHOW_PACKET_ERRORS
+ if (ubcsp_config.receive_packet->reliable)
+ {
+ printf (" : %10d Recv SEQ: %d ACK %d\n",
+ GetTickCount () % 100000,
+ receive_seq,
+ receive_ack);
+ }
+ else if (ubcsp_config.receive_packet->channel != 1)
+ {
+ printf (" : %10d Recv ACK %d\n",
+ GetTickCount () % 100000,
+ receive_ack);
+ }
+#endif
+
+ /* Check for length errors */
+
+#if UBCSP_CRC
+ if (receive_crc)
+ {
+ /* If this packet had a CRC, then the length of the payload
+ should be 2 less than the received size of the payload */
+
+ if (length + 2 != ubcsp_config.receive_index)
+ {
+ /* Slip Length Error */
+
+#if SHOW_PACKET_ERRORS
+ printf ("\n######################## Slip Length Error (With CRC) %d,%d\n", length, ubcsp_config.receive_index - 2);
+#endif
+
+ /* If we have a payload length error, send an ack in return
+ this gets a packet to be resent as quickly as possible */
+
+ ubcsp_config.send_ack = 1;
+ return activity;
+ }
+
+ /* We have a CRC at the end of this packet */
+
+ ubcsp_config.receive_index -= 2;
+
+ /* Calculate the packet CRC */
+
+ crc = 0xffff;
+
+ /* CRC the packet header */
+
+ for (loop = 0; loop < 4; loop ++)
+ {
+ crc = ubcsp_calc_crc (ubcsp_receive_header[loop], crc);
+ }
+
+ /* CRC the packet payload - without the CRC bytes */
+
+ for (loop = 0; loop < ubcsp_config.receive_index; loop ++)
+ {
+ crc = ubcsp_calc_crc (ubcsp_config.receive_packet->payload[loop], crc);
+ }
+
+ /* Reverse the CRC */
+
+ crc = ubcsp_crc_reverse (crc);
+
+ /* Check the CRC is correct */
+
+ if
+ (
+ (((crc & 0xff00) >> 8) != ubcsp_config.receive_packet->payload[ubcsp_config.receive_index]) ||
+ ((crc & 0xff) != ubcsp_config.receive_packet->payload[ubcsp_config.receive_index + 1])
+ )
+ {
+#if SHOW_PACKET_ERRORS
+ printf ("\n######################## CRC Error\n");
+#endif
+
+ /* If we have a packet crc error, send an ack in return
+ this gets a packet to be resent as quickly as possible */
+
+ ubcsp_config.send_ack = 1;
+ return activity;
+ }
+ }
+ else
+ {
+#endif
+ /* No CRC present, so just check the length of payload with that received */
+
+ if (length != ubcsp_config.receive_index)
+ {
+ /* Slip Length Error */
+
+#if SHOW_PACKET_ERRORS
+ printf ("\n######################## Slip Length Error (No CRC) %d,%d\n", length, ubcsp_config.receive_index);
+#endif
+
+ /* If we have a payload length error, send an ack in return
+ this gets a packet to be resent as quickly as possible */
+
+ ubcsp_config.send_ack = 1;
+ return activity;
+ }
+#if UBCSP_CRC
+ }
+#endif
+
+ /*** We have a fully formed packet having passed all data integrity checks ***/
+
+ /* Check if we have an ACK for the last packet we sent */
+
+ if (receive_ack != ubcsp_config.sequence_number)
+ {
+ /* Since we only have a window size of 1, if the ACK is not equal to SEQ
+ then the packet was sent */
+
+ if
+ (
+ (ubcsp_config.send_packet) &&
+ (ubcsp_config.send_packet->reliable)
+ )
+ {
+ /* We had sent a reliable packet, so clear this packet
+ Then increament the sequence number for the next packet */
+
+ ubcsp_config.send_packet = 0;
+ ubcsp_config.sequence_number ++;
+ ubcsp_config.delay = 0;
+
+ /* Notify the caller that we have SENT a packet */
+
+ activity |= UBCSP_PACKET_SENT;
+ }
+ }
+
+ /*** Now we can concentrate of the packet we have received ***/
+
+ /* Check for Link Establishment packets */
+
+ if (ubcsp_config.receive_packet->channel == 1)
+ {
+ /* Link Establishment */
+
+ ubcsp_config.delay = 0;
+
+ /* Find which link establishment packet this payload means
+ This could return 5, meaning none */
+
+ switch (ubcsp_which_le_payload (ubcsp_config.receive_packet->payload))
+ {
+ case 0:
+ {
+ /* SYNC Recv'd */
+
+#if SHOW_LE_STATES
+ printf ("Recv SYNC\n");
+#endif
+
+ /* If we receive a SYNC, then we respond to it with a SYNC RESP
+ but only if we are not active.
+ If we are active, then we have a PEER RESET */
+
+ if (ubcsp_config.link_establishment_state < ubcsp_le_active)
+ {
+ ubcsp_config.link_establishment_resp = 1;
+ }
+ else
+ {
+ /* Peer reset !!!! */
+
+#if SHOW_LE_STATES
+ printf ("\n\n\n\n\nPEER RESET\n\n");
+#endif
+
+ /* Reinitialize the link */
+
+ ubcsp_initialize ();
+
+ /* Tell the host what has happened */
+
+ return UBCSP_PEER_RESET;
+ }
+ break;
+ }
+
+ case 1:
+ {
+ /* SYNC RESP Recv'd */
+
+#if SHOW_LE_STATES
+ printf ("Recv SYNC RESP\n");
+#endif
+
+ /* If we receive a SYNC RESP, push us into the initialized state */
+
+ if (ubcsp_config.link_establishment_state < ubcsp_le_initialized)
+ {
+#if SHOW_LE_STATES
+ printf ("Link Initialized\n");
+#endif
+ ubcsp_config.link_establishment_state = ubcsp_le_initialized;
+ }
+
+ break;
+ }
+
+ case 2:
+ {
+ /* CONF Recv'd */
+
+#if SHOW_LE_STATES
+ printf ("Recv CONF\n");
+#endif
+
+ /* If we receive a CONF, and we are initialized or active
+ then respond with a CONF RESP */
+
+ if (ubcsp_config.link_establishment_state >= ubcsp_le_initialized)
+ {
+ ubcsp_config.link_establishment_resp = 2;
+ }
+
+ break;
+ }
+
+ case 3:
+ {
+ /* CONF RESP Recv'd */
+
+#if SHOW_LE_STATES
+ printf ("Recv CONF RESP\n");
+#endif
+
+ /* If we received a CONF RESP, then push us into the active state */
+
+ if (ubcsp_config.link_establishment_state < ubcsp_le_active)
+ {
+#if SHOW_LE_STATES
+ printf ("Link Active\n");
+#endif
+
+ ubcsp_config.link_establishment_state = ubcsp_le_active;
+ ubcsp_config.send_size = 0;
+
+ return activity | UBCSP_PACKET_SENT;
+ }
+
+ break;
+ }
+ }
+
+ /* We have finished processing Link Establishment packets */
+ }
+ else if (ubcsp_config.receive_index)
+ {
+ /* We have some payload data we need to process
+ but only if we are active - otherwise, we just ignore it */
+
+ if (ubcsp_config.link_establishment_state == ubcsp_le_active)
+ {
+ if (ubcsp_config.receive_packet->reliable)
+ {
+ /* If the packet we've just received was reliable
+ then send an ACK */
+
+ ubcsp_config.send_ack = 1;
+
+ /* We the sequence number we received is the same as
+ the last ACK we sent, then we have received a packet in sequence */
+
+ if (receive_seq == ubcsp_config.ack_number)
+ {
+ /* Increase the ACK number - which will be sent in the next ACK
+ or normal packet we send */
+
+ ubcsp_config.ack_number ++;
+
+ /* Set the values in the receive_packet structure, so the caller
+ knows how much data we have */
+
+ ubcsp_config.receive_packet->length = length;
+ ubcsp_config.receive_packet = 0;
+
+ /* Tell the caller that we have received a packet, and that it
+ will be ACK'ed */
+
+ activity |= UBCSP_PACKET_RECEIVED | UBCSP_PACKET_ACK;
+ }
+ }
+ else
+ {
+ /* Set the values in the receive_packet structure, so the caller
+ knows how much data we have */
+
+ ubcsp_config.receive_packet->length = length;
+ ubcsp_config.receive_packet = 0;
+
+ /* Tell the caller that we have received a packet */
+
+ activity |= UBCSP_PACKET_RECEIVED;
+ }
+ }
+ }
+
+ /* Just return any activity that occured */
+
+ return activity;
+}
+
+/*****************************************************************************/
+/** **/
+/** ubcsp_setup_packet **/
+/** **/
+/** This function is called to setup a packet to be sent **/
+/** This allows just a header, or a header and payload to be sent **/
+/** It also allows the header checksum to be precalcuated **/
+/** or calculated here **/
+/** part1 is always 4 bytes **/
+/** **/
+/*****************************************************************************/
+
+static void ubcsp_setup_packet (uint8 *part1, uint8 calc, uint8 *part2, uint16 len2)
+{
+ /* If we need to calculate the checksum, do that now */
+
+ if (calc)
+ {
+ part1[3] =
+ ~(part1[0] + part1[1] + part1[2]);
+ }
+
+ /* Setup the header send pointer and size so we can clock this out */
+
+ ubcsp_config.send_ptr = part1;
+ ubcsp_config.send_size = 4;
+
+ /* Setup the payload send pointer and size */
+
+ ubcsp_config.next_send_ptr = part2;
+ ubcsp_config.next_send_size = len2;
+
+#if UBCSP_CRC
+ /* Initialize the crc as required */
+
+ ubcsp_config.send_crc = -1;
+
+ ubcsp_config.need_send_crc = 1;
+#endif
+}
+
+/*****************************************************************************/
+/** **/
+/** ubcsp_sent_packet **/
+/** **/
+/** Called when we have finished sending a packet **/
+/** If this packet was unreliable, then notify caller, and clear the data **/
+/** **/
+/*****************************************************************************/
+
+static uint8 ubcsp_sent_packet (void)
+{
+ if (ubcsp_config.send_packet)
+ {
+ if (!ubcsp_config.send_packet->reliable)
+ {
+ /* We had a packet sent that was unreliable */
+
+ /* Forget about this packet */
+
+ ubcsp_config.send_packet = 0;
+
+ /* Notify caller that they can send another one */
+
+ return UBCSP_PACKET_SENT;
+ }
+ }
+
+ /* We didn't have a packet, or it was reliable
+ Must wait for ACK before allowing another packet to be sent */
+
+ return 0;
+}
+
+/*****************************************************************************/
+/** **/
+/** ubcsp_poll **/
+/** **/
+/** This is the main function for ubcsp **/
+/** It performs a number of tasks **/
+/** **/
+/** 1) Send another octet to the UART - escaping as required **/
+/** 2) Setup the payload to be sent after the header has been sent **/
+/** 3) Send the CRC for the packet if required **/
+/** **/
+/** 4) Calculate the next Link Establishment State **/
+/** 5) Send a Link Establishment packet **/
+/** 6) Send a normal packet if available **/
+/** 7) Send an ACK packet if required **/
+/** **/
+/** 8) Receive octets from UART and deslip them as required **/
+/** 9) Place received octets into receive header or receive payload buffer **/
+/** 10) Process received packet when SLIP_END is received **/
+/** **/
+/** 11) Keep track of ability of caller to delay recalling **/
+/** **/
+/*****************************************************************************/
+
+uint8 ubcsp_poll (uint8 *activity)
+{
+ uint8
+ delay = UBCSP_POLL_TIME_IMMEDIATE;
+
+ uint8
+ value;
+
+ /* Assume no activity to start with */
+
+ *activity = 0;
+
+ /* If we don't have to delay, then send something if we can */
+
+ if (!ubcsp_config.delay)
+ {
+ /* Do we have something we are sending to send */
+
+ if (ubcsp_config.send_size)
+ {
+ /* We have something to send so send it */
+
+ if (ubcsp_config.send_slip_escape)
+ {
+ /* Last time we send a SLIP_ESCAPE octet
+ this time send the second escape code */
+
+ put_uart (ubcsp_config.send_slip_escape);
+
+ ubcsp_config.send_slip_escape = 0;
+ }
+ else
+ {
+#if UBCSP_CRC
+ /* get the value to send, and calculate CRC as we go */
+
+ value = *ubcsp_config.send_ptr ++;
+
+ ubcsp_config.send_crc = ubcsp_calc_crc (value, ubcsp_config.send_crc);
+
+ /* Output the octet */
+
+ ubcsp_put_slip_uart (value);
+#else
+ /* Just output the octet*/
+
+ ubcsp_put_slip_uart (*ubcsp_config.send_ptr ++);
+#endif
+ }
+
+ /* If we did output a SLIP_ESCAPE, then don't process the end of a block */
+
+ if ((!ubcsp_config.send_slip_escape) && ((ubcsp_config.send_size = ubcsp_config.send_size - 1) == 0))
+ {
+ /*** We are at the end of a block - either header or payload ***/
+
+ /* setup the next block */
+
+ ubcsp_config.send_ptr = ubcsp_config.next_send_ptr;
+ ubcsp_config.send_size = ubcsp_config.next_send_size;
+ ubcsp_config.next_send_ptr = 0;
+ ubcsp_config.next_send_size = 0;
+
+#if UBCSP_CRC
+ /* If we have no successor block
+ then we might need to send the CRC */
+
+ if (!ubcsp_config.send_ptr)
+ {
+ if (ubcsp_config.need_send_crc)
+ {
+ /* reverse the CRC from what we computed along the way */
+
+ ubcsp_config.need_send_crc = 0;
+
+ ubcsp_config.send_crc = ubcsp_crc_reverse (ubcsp_config.send_crc);
+
+ /* Save in the send_crc buffer */
+
+ ubcsp_send_crc[0] = (uint8) (ubcsp_config.send_crc >> 8);
+ ubcsp_send_crc[1] = (uint8) ubcsp_config.send_crc;
+
+ /* Setup to send this buffer */
+
+ ubcsp_config.send_ptr = ubcsp_send_crc;
+ ubcsp_config.send_size = 2;
+ }
+ else
+ {
+ /* We don't need to send the crc
+ either we just have, or this packet doesn't include it */
+
+ /* Output the end of FRAME marker */
+
+ put_uart (SLIP_FRAME);
+
+ /* Check if this is an unreliable packet */
+
+ *activity |= ubcsp_sent_packet ();
+
+ /* We've sent the packet, so don't need to have be called quickly soon */
+
+ delay = UBCSP_POLL_TIME_DELAY;
+ }
+ }
+#else
+ /* If we have no successor block
+ then we might need to send the CRC */
+
+ if (!ubcsp_config.send_ptr)
+ {
+ /* Output the end of FRAME marker */
+
+ put_uart (SLIP_FRAME);
+
+ /* Check if this is an unreliable packet */
+
+ *activity |= ubcsp_sent_packet ();
+
+ /* We've sent the packet, so don't need to have be called quickly soon */
+
+ delay = UBCSP_POLL_TIME_DELAY;
+ }
+#endif
+ }
+ }
+ else if (ubcsp_config.link_establishment_packet == ubcsp_le_none)
+ {
+ /* We didn't have something to send
+ AND we have no Link Establishment packet to send */
+
+ if (ubcsp_config.link_establishment_resp & 2)
+ {
+ /* Send the start of FRAME packet */
+
+ put_uart (SLIP_FRAME);
+
+ /* We did require a RESP packet - so setup the send */
+
+ ubcsp_setup_packet ((uint8*) ubcsp_send_le_header, 0, (uint8*) ubcsp_le_buffer[ubcsp_le_conf_resp], 4);
+
+ /* We have now "sent" this packet */
+
+ ubcsp_config.link_establishment_resp = 0;
+ }
+ else if (ubcsp_config.send_packet)
+ {
+ /* There is a packet ready to be sent */
+
+ /* Send the start of FRAME packet */
+
+ put_uart (SLIP_FRAME);
+
+ /* Encode up the packet header using ACK and SEQ numbers */
+
+ ubcsp_send_header[0] =
+ (ubcsp_config.send_packet->reliable << 7) |
+#if UBCSP_CRC
+ 0x40 | /* Always use CRC's */
+#endif
+ (ubcsp_config.ack_number << 3) |
+ (ubcsp_config.sequence_number);
+
+ /* Encode up the packet header's channel and length */
+ ubcsp_send_header[1] =
+ (ubcsp_config.send_packet->channel & 0x0f) |
+ ((ubcsp_config.send_packet->length << 4) & 0xf0);
+
+ ubcsp_send_header[2] =
+ (ubcsp_config.send_packet->length >> 4) & 0xff;
+
+ /* Let the ubcsp_setup_packet function calculate the header checksum */
+
+ ubcsp_setup_packet ((uint8*) ubcsp_send_header, 1, ubcsp_config.send_packet->payload, ubcsp_config.send_packet->length);
+
+ /* Don't need to send an ACK - we just place on in this packet */
+
+ ubcsp_config.send_ack = 0;
+
+#if SHOW_PACKET_ERRORS
+ printf (" : %10d Send %d Ack %d\n",
+ GetTickCount () % 100000,
+ ubcsp_config.sequence_number,
+ ubcsp_config.ack_number);
+#endif
+ }
+ else if (ubcsp_config.send_ack)
+ {
+ /* Send the start of FRAME packet */
+
+ put_uart (SLIP_FRAME);
+
+#if SHOW_PACKET_ERRORS
+ printf (" : %10d Send ACK %d\n",
+ GetTickCount () % 100000,
+ ubcsp_config.ack_number);
+#endif
+
+ /* The ack packet is already computed apart from the first octet */
+
+ ubcsp_send_ack_header[0] =
+#if UBCSP_CRC
+ 0x40 |
+#endif
+ (ubcsp_config.ack_number << 3);
+
+ /* Let the ubcsp_setup_packet function calculate the header checksum */
+
+ ubcsp_setup_packet (ubcsp_send_ack_header, 1, 0, 0);
+
+ /* We've now sent the ack */
+
+ ubcsp_config.send_ack = 0;
+ }
+ else
+ {
+ /* We didn't have a Link Establishment response packet,
+ a normal packet or an ACK packet to send */
+
+ delay = UBCSP_POLL_TIME_DELAY;
+ }
+ }
+ else
+ {
+#if SHOW_PACKET_ERRORS
+// printf (" : %10d Send LE %d\n",
+// GetTickCount () % 100000,
+// ubcsp_config.link_establishment_packet);
+#endif
+
+ /* Send A Link Establishment Message */
+
+ put_uart (SLIP_FRAME);
+
+ /* Send the Link Establishment header followed by the
+ Link Establishment packet */
+
+ ubcsp_setup_packet ((uint8*) ubcsp_send_le_header, 0, (uint8*) ubcsp_le_buffer[ubcsp_config.link_establishment_packet], 4);
+
+ /* start sending immediately */
+
+ ubcsp_config.delay = 0;
+
+ /* workout what the next link establishment packet should be */
+
+ ubcsp_config.link_establishment_packet = next_le_packet[ubcsp_config.link_establishment_state + ubcsp_config.link_establishment_resp * 4];
+
+ /* We have now delt with any response packet that we needed */
+
+ ubcsp_config.link_establishment_resp = 0;
+
+ return 0;
+ }
+ }
+
+ /* We now need to receive any octets from the UART */
+
+ while ((ubcsp_config.receive_packet) && (get_uart (&value)))
+ {
+ /* If the last octet was SLIP_ESCAPE, then special processing is required */
+
+ if (ubcsp_config.receive_slip_escape)
+ {
+ /* WARNING - out of range values are not detected !!!
+ This will probably be caught with the checksum or CRC check */
+
+ value = ubcsp_deslip[value - SLIP_ESCAPE_FRAME];
+
+ ubcsp_config.receive_slip_escape = 0;
+ }
+ else
+ {
+ /* Check for the SLIP_FRAME octet - must be start or end of packet */
+ if (value == SLIP_FRAME)
+ {
+ /* If we had a full header then we have a packet */
+
+ if (ubcsp_config.receive_index >= 0)
+ {
+ /* process the received packet */
+
+ *activity |= ubcsp_recevied_packet ();
+
+ if (*activity & UBCSP_PACKET_ACK)
+ {
+ /* We need to ACK this packet, then don't delay its sending */
+ ubcsp_config.delay = 0;
+ }
+ }
+
+ /* Setup to receive the next packet */
+
+ ubcsp_config.receive_index = -4;
+
+ /* Ok, next octet */
+
+ goto finished_receive;
+ }
+ else if (value == SLIP_ESCAPE)
+ {
+ /* If we receive a SLIP_ESCAPE,
+ then remember to process the next special octet */
+
+ ubcsp_config.receive_slip_escape = 1;
+
+ goto finished_receive;
+ }
+ }
+
+ if (ubcsp_config.receive_index < 0)
+ {
+ /* We are still receiving the header */
+
+ ubcsp_receive_header[ubcsp_config.receive_index + 4] = value;
+
+ ubcsp_config.receive_index ++;
+ }
+ else if (ubcsp_config.receive_index < ubcsp_config.receive_packet->length)
+ {
+ /* We are receiving the payload */
+ /* We might stop comming here if we are receiving a
+ packet which is longer than the receive_packet->length
+ given by the host */
+
+ ubcsp_config.receive_packet->payload[ubcsp_config.receive_index] = value;
+
+ ubcsp_config.receive_index ++;
+ }
+
+finished_receive:
+ {
+ }
+ }
+
+ if (ubcsp_config.delay > 0)
+ {
+ /* We were delayed so delay some more
+ this could be cancelled if we received something */
+
+ ubcsp_config.delay --;
+ }
+ else
+ {
+ /* We had no delay, so use the delay we just decided to us */
+
+ ubcsp_config.delay = delay;
+ }
+
+ /* Report the current delay to the user */
+
+ return ubcsp_config.delay;
+}
diff --git a/tools/ubcsp.h b/tools/ubcsp.h
new file mode 100644
index 0000000..6a74e9a
--- /dev/null
+++ b/tools/ubcsp.h
@@ -0,0 +1,208 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2000-2005 CSR Ltd.
+ *
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef UBCSP_INCLUDE_H
+#define UBCSP_INCLUDE_H
+
+/*****************************************************************************/
+/*****************************************************************************/
+/*****************************************************************************/
+/** **/
+/** ubcsp.h **/
+/** **/
+/** MicroBCSP - a very low cost implementation of the BCSP protocol **/
+/** **/
+/*****************************************************************************/
+
+/* If we wish to use CRC's, then change 0 to 1 in the next line */
+#define UBCSP_CRC 1
+
+/* Define some basic types - change these for your architecture */
+typedef unsigned char uint8;
+typedef unsigned short uint16;
+typedef unsigned int uint32;
+typedef signed char int8;
+typedef signed short int16;
+typedef signed int int32;
+
+/* The defines below require a printf function to be available */
+
+/* Do we want to show packet errors in debug output */
+#define SHOW_PACKET_ERRORS 0
+
+/* Do we want to show Link Establishment State transitions in debug output */
+#define SHOW_LE_STATES 0
+
+/*****************************************************************************/
+/** **/
+/** ubcsp_packet **/
+/** **/
+/** This is description of a bcsp packet for the upper layer **/
+/** **/
+/*****************************************************************************/
+
+struct ubcsp_packet
+{
+ uint8 channel; /* Which Channel this packet is to/from */
+ uint8 reliable; /* Is this packet reliable */
+ uint8 use_crc; /* Does this packet use CRC data protection */
+ uint16 length; /* What is the length of the payload data */
+ uint8 *payload; /* The payload data itself - size of length */
+};
+
+/*****************************************************************************/
+/** **/
+/** ubcsp_configuration **/
+/** **/
+/** This is the main configuration of the ubcsp engine **/
+/** All state variables are stored in this structure **/
+/** **/
+/*****************************************************************************/
+
+enum ubcsp_link_establishment_state
+{
+ ubcsp_le_uninitialized,
+ ubcsp_le_initialized,
+ ubcsp_le_active
+};
+
+enum ubcsp_link_establishment_packet
+{
+ ubcsp_le_sync,
+ ubcsp_le_sync_resp,
+ ubcsp_le_conf,
+ ubcsp_le_conf_resp,
+ ubcsp_le_none
+};
+
+struct ubcsp_configuration
+{
+ uint8 link_establishment_state;
+ uint8 link_establishment_resp;
+ uint8 link_establishment_packet;
+
+ uint8 sequence_number:3;
+ uint8 ack_number:3;
+ uint8 send_ack;
+ struct ubcsp_packet *send_packet;
+ struct ubcsp_packet *receive_packet;
+
+ uint8 receive_header_checksum;
+ uint8 receive_slip_escape;
+ int32 receive_index;
+
+ uint8 send_header_checksum;
+#ifdef UBCSP_CRC
+ uint8 need_send_crc;
+ uint16 send_crc;
+#endif
+ uint8 send_slip_escape;
+
+ uint8 *send_ptr;
+ int32 send_size;
+ uint8 *next_send_ptr;
+ int32 next_send_size;
+
+ int8 delay;
+};
+
+/*****************************************************************************/
+/** **/
+/** ubcsp_poll sets activity from an OR of these flags **/
+/** **/
+/*****************************************************************************/
+
+#define UBCSP_PACKET_SENT 0x01
+#define UBCSP_PACKET_RECEIVED 0x02
+#define UBCSP_PEER_RESET 0x04
+#define UBCSP_PACKET_ACK 0x08
+
+/*****************************************************************************/
+/** **/
+/** This is the functional interface for ucbsp **/
+/** **/
+/*****************************************************************************/
+
+void ubcsp_initialize (void);
+void ubcsp_send_packet (struct ubcsp_packet *send_packet);
+void ubcsp_receive_packet (struct ubcsp_packet *receive_packet);
+uint8 ubcsp_poll (uint8 *activity);
+
+/*****************************************************************************/
+/** **/
+/** Slip Escape Values **/
+/** **/
+/*****************************************************************************/
+
+#define SLIP_FRAME 0xC0
+#define SLIP_ESCAPE 0xDB
+#define SLIP_ESCAPE_FRAME 0xDC
+#define SLIP_ESCAPE_ESCAPE 0xDD
+
+/*****************************************************************************/
+/*****************************************************************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/** **/
+/** These functions need to be linked into your system **/
+/** **/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/** **/
+/** put_uart outputs a single octet over the UART Tx line **/
+/** **/
+/*****************************************************************************/
+
+extern void put_uart (uint8);
+
+/*****************************************************************************/
+/** **/
+/** get_uart receives a single octet over the UART Rx line **/
+/** if no octet is available, then this returns 0 **/
+/** if an octet was read, then this is returned in the argument and **/
+/** the function returns 1 **/
+/** **/
+/*****************************************************************************/
+
+extern uint8 get_uart (uint8 *);
+
+/*****************************************************************************/
+/** **/
+/** These defines should be changed to your systems concept of 100ms **/
+/** **/
+/*****************************************************************************/
+
+#define UBCSP_POLL_TIME_IMMEDIATE 0
+#define UBCSP_POLL_TIME_DELAY 25
+
+/*****************************************************************************/
+/*****************************************************************************/
+/*****************************************************************************/
+#endif