summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorMarcel Holtmann <marcel@holtmann.org>2008-07-26 19:00:53 +0200
committerMarcel Holtmann <marcel@holtmann.org>2008-07-26 19:00:53 +0200
commitd6ae1c3f777832f8e32702f81fe64e33a1396928 (patch)
tree159a1e59f3929c9d795dbd1f3edd84d9dccba048 /tools
parentb8e5fea8d31fbcd3d1c044385f8217dbf39892bb (diff)
parent3382af9114a9b2e657c7ddd0a5511edda6a37a90 (diff)
Import bluez-utils-3.36 revision history
Diffstat (limited to 'tools')
-rw-r--r--tools/Makefile.am103
-rw-r--r--tools/avctrl.839
-rw-r--r--tools/avctrl.c248
-rw-r--r--tools/avinfo.c660
-rw-r--r--tools/bccmd.8130
-rw-r--r--tools/bccmd.c1218
-rw-r--r--tools/ciptool.168
-rw-r--r--tools/ciptool.c498
-rw-r--r--tools/csr.c2852
-rw-r--r--tools/csr.h552
-rw-r--r--tools/csr_3wire.c60
-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.c788
-rw-r--r--tools/example.psr12
-rw-r--r--tools/hciattach.8122
-rw-r--r--tools/hciattach.c1373
-rw-r--r--tools/hciattach_st.c276
-rw-r--r--tools/hciattach_ti.c523
-rw-r--r--tools/hciattach_tialt.c272
-rw-r--r--tools/hciconfig.8276
-rw-r--r--tools/hciconfig.c1749
-rw-r--r--tools/hcisecfilter.c155
-rw-r--r--tools/hcitool.1206
-rw-r--r--tools/hcitool.c2435
-rw-r--r--tools/hid2hci.845
-rw-r--r--tools/hid2hci.c390
-rw-r--r--tools/l2ping.167
-rw-r--r--tools/l2ping.c290
-rw-r--r--tools/ppporc.c273
-rw-r--r--tools/sdptool.1130
-rw-r--r--tools/sdptool.c4134
-rw-r--r--tools/ubcsp.c1193
-rw-r--r--tools/ubcsp.h208
41 files changed, 22682 insertions, 0 deletions
diff --git a/tools/Makefile.am b/tools/Makefile.am
new file mode 100644
index 00000000..eb4dcc74
--- /dev/null
+++ b/tools/Makefile.am
@@ -0,0 +1,103 @@
+
+if TOOLS
+tools_programs = hcitool l2ping sdptool ciptool
+tools_manfiles = hcitool.1 l2ping.1 sdptool.1 ciptool.1
+else
+tools_programs =
+tools_manfiles =
+endif
+
+if BCCMD
+bccmd_programs = bccmd
+bccmd_manfiles = bccmd.8
+else
+bccmd_programs =
+bccmd_manfiles =
+endif
+
+if HID2HCI
+hid2hci_programs = hid2hci
+hid2hci_manfiles = hid2hci.8
+else
+hid2hci_programs =
+hid2hci_manfiles =
+endif
+
+if DFUTOOL
+dfutool_programs = dfutool
+dfutool_manfiles = dfutool.1
+else
+dfutool_programs =
+dfutool_manfiles =
+endif
+
+if USB
+usb_programs = dfubabel avctrl
+else
+usb_programs =
+endif
+
+sbin_PROGRAMS = hciattach hciconfig $(bccmd_programs) $(avctrl_programs) $(hid2hci_programs)
+
+bin_PROGRAMS = $(tools_programs) $(dfutool_programs) $(dfubabel_programs)
+
+noinst_PROGRAMS = hcisecfilter ppporc avinfo $(usb_programs)
+
+hciattach_SOURCES = hciattach.c hciattach_st.c hciattach_ti.c hciattach_tialt.c
+hciattach_LDADD = @BLUEZ_LIBS@
+
+hciconfig_SOURCES = hciconfig.c csr.h csr.c
+hciconfig_LDADD = @BLUEZ_LIBS@ $(top_builddir)/common/libhelper.a
+
+if TOOLS
+hcitool_SOURCES = hcitool.c
+hcitool_LDADD = @BLUEZ_LIBS@ $(top_builddir)/common/libhelper.a
+
+l2ping_LDADD = @BLUEZ_LIBS@
+
+sdptool_LDADD = @BLUEZ_LIBS@ $(top_builddir)/common/libhelper.a
+
+ciptool_LDADD = @BLUEZ_LIBS@
+
+avinfo_LDADD = @BLUEZ_LIBS@
+endif
+
+ppporc_LDADD = @BLUEZ_LIBS@
+
+if BCCMD
+bccmd_SOURCES = bccmd.c csr.h csr.c csr_hci.c \
+ csr_bcsp.c csr_h4.c csr_3wire.c ubcsp.h ubcsp.c
+bccmd_LDADD = @BLUEZ_LIBS@
+if USB
+bccmd_SOURCES += csr_usb.c
+bccmd_LDADD += @USB_LIBS@
+endif
+endif
+
+if HID2HCI
+hid2hci_LDADD = @USB_LIBS@
+endif
+
+if DFUTOOL
+dfutool_SOURCES = dfutool.c dfu.h dfu.c
+dfutool_LDADD = @USB_LIBS@
+endif
+
+if USB
+dfubabel_LDADD = @USB_LIBS@
+avctrl_LDADD = @USB_LIBS@
+endif
+
+AM_CFLAGS = @BLUEZ_CFLAGS@ @USB_CFLAGS@
+
+INCLUDES = -I$(top_srcdir)/common
+
+if MANPAGES
+man_MANS = hciattach.8 hciconfig.8 $(tools_manfiles) \
+ $(bccmd_manfiles) $(hid2hci_manfiles) $(dfutool_manfiles)
+endif
+
+EXTRA_DIST = hciattach.8 hciconfig.8 hcitool.1 l2ping.1 sdptool.1 ciptool.1 \
+ bccmd.8 avctrl.8 hid2hci.8 dfutool.1 dfubabel.1 example.psr
+
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/tools/avctrl.8 b/tools/avctrl.8
new file mode 100644
index 00000000..7c3759d6
--- /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 00000000..61d0180d
--- /dev/null
+++ b/tools/avctrl.c
@@ -0,0 +1,248 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 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;
+ 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 00000000..76d29c0c
--- /dev/null
+++ b/tools/avinfo.c
@@ -0,0 +1,660 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2006-2007 Nokia Corporation
+ * Copyright (C) 2004-2008 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 int avdtp_send(int sk, void *data, int len)
+{
+ int 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 (%d)\n",
+ strerror(-ret), -ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int avdtp_receive(int sk, void *data, int len)
+{
+ int 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;
+}
+
+int avdtp_get_caps(int sk, int seid)
+{
+ struct seid_req req;
+ char buffer[1024];
+ struct getcap_resp *caps = (void *) buffer;
+ int 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 (ret < (sizeof(struct getcap_resp) + 4 +
+ sizeof(struct avdtp_media_codec_capability))) {
+ printf("Invalid capabilities\n");
+ return -1;
+ }
+
+ print_caps(caps, ret);
+
+ return 0;
+}
+
+int avdtp_discover(int sk)
+{
+ struct avdtp_header req;
+ char buffer[256];
+ struct discover_resp *discover = (void *) buffer;
+ int ret, seps, i;
+
+ 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 <remote address>\n");
+}
+
+static struct option main_options[] = {
+ { "help", 0, 0, 'h' },
+ { 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);
+ }
+
+ while ((opt = getopt_long(argc, argv, "h", main_options, NULL)) != -1) {
+ switch (opt) {
+ case 'h':
+ default:
+ 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);
+ }
+
+ printf("Connecting ... \n");
+
+ if (bachk(argv[1]) < 0) {
+ printf("Invalid argument\n");
+ exit(1);
+ }
+
+ str2ba(argv[1], &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 00000000..28cbe880
--- /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 00000000..95cb37bb
--- /dev/null
+++ b/tools/bccmd.c
@@ -0,0 +1,1218 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 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)
+{
+ 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);
+ 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];
+ int i, 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 > 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 > 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 > 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-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' },
+ { "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;
+
+ while ((opt=getopt_long(argc, argv, "+t:d:i: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 'h':
+ default:
+ usage();
+ exit(0);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (argc < 1) {
+ usage();
+ exit(1);
+ }
+
+ if (transport_open(transport, device) < 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 00000000..982f414b
--- /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 00000000..886e20aa
--- /dev/null
+++ b/tools/ciptool.c
@@ -0,0 +1,498 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2008 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];
+ 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 00000000..b32e4162
--- /dev/null
+++ b/tools/csr.c
@@ -0,0 +1,2852 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2003-2008 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)" },
+ { 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 00000000..5df3cb5a
--- /dev/null
+++ b/tools/csr.h
@@ -0,0 +1,552 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2003-2008 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>
+
+#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);
+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 00000000..ed2064cd
--- /dev/null
+++ b/tools/csr_3wire.c
@@ -0,0 +1,60 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 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>
+
+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 00000000..bcf56a35
--- /dev/null
+++ b/tools/csr_bcsp.c
@@ -0,0 +1,255 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 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)
+{
+ 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, 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);
+
+ 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++ > 100) {
+ 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++ > 100) {
+ 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 00000000..27ea3e3c
--- /dev/null
+++ b/tools/csr_h4.c
@@ -0,0 +1,165 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 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 00000000..454cbfb2
--- /dev/null
+++ b/tools/csr_hci.c
@@ -0,0 +1,160 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 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 00000000..b7703dcf
--- /dev/null
+++ b/tools/csr_usb.c
@@ -0,0 +1,180 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 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 00000000..069cbb89
--- /dev/null
+++ b/tools/dfu.c
@@ -0,0 +1,168 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2003-2008 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 00000000..39d25250
--- /dev/null
+++ b/tools/dfu.h
@@ -0,0 +1,107 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2003-2008 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 00000000..5e0f805f
--- /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 00000000..78dfa9f9
--- /dev/null
+++ b/tools/dfubabel.c
@@ -0,0 +1,211 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 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;
+ 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 00000000..78b1fe7f
--- /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 00000000..246d92a4
--- /dev/null
+++ b/tools/dfutool.c
@@ -0,0 +1,788 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2003-2008 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, 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;
+ unsigned long size;
+ char *filename;
+ int i, fd, len;
+
+ 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) < size) {
+ perror("Can't load firmware");
+ free(buf);
+ close(fd);
+ exit(1);
+ }
+
+ printf("Filename\t%s\n", basename(filename));
+ printf("Filesize\t%ld\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;
+ unsigned long filesize, count, timeout = 0;
+ char *filename;
+ uint32_t crc, dwCRC;
+ int fd, i, 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) < 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%ld\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/example.psr b/tools/example.psr
new file mode 100644
index 00000000..bbbec73a
--- /dev/null
+++ b/tools/example.psr
@@ -0,0 +1,12 @@
+// PSKEY_BDADDR
+&0001 = 0001 2821 005b 6789
+// PSKEY_ANA_FTRIM
+&01f6 = 0025
+// PSKEY_HOST_INTERFACE
+&01f9 = 0001
+// PSKEY_UART_BAUD_RATE
+&0204 = 01d8
+// PSKEY_ANA_FREQ
+&01fe = 0004
+// PSKEY_UART_CONFIG
+&0205 = 0006
diff --git a/tools/hciattach.8 b/tools/hciattach.8
new file mode 100644
index 00000000..f9d295eb
--- /dev/null
+++ b/tools/hciattach.8
@@ -0,0 +1,122 @@
+.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 [\| \-n \|]
+.RB [\| \-p \|]
+.RB [\| \-t
+.IR timeout \|]
+.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 \-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
+.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
+.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 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 00000000..439b6658
--- /dev/null
+++ b/tools/hciattach.c
@@ -0,0 +1,1373 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2008 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>
+
+#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 HCI_UART_H4 0
+#define HCI_UART_BCSP 1
+#define HCI_UART_3WIRE 2
+#define HCI_UART_H4DS 3
+#define HCI_UART_LL 4
+
+struct uart_t {
+ char *type;
+ int m_id;
+ int p_id;
+ int proto;
+ int init_speed;
+ int speed;
+ int flags;
+ 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
+
+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)
+{
+ cfsetospeed(ti, uart_speed(speed));
+ cfsetispeed(ti, uart_speed(speed));
+ return tcsetattr(fd, TCSANOW, ti);
+}
+
+/*
+ * 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;
+}
+
+extern int texas_init(int fd, struct termios *ti);
+extern int texas_post(int fd, struct termios *ti);
+
+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);
+}
+
+extern int texasalt_init(int fd, int speed);
+
+static int texasalt(int fd, struct uart_t *u, struct termios *ti)
+{
+ return texasalt_init(fd, u->speed);
+}
+
+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
+ */
+int serial_fd;
+
+static void bcsp_tshy_sig_alarm(int sig)
+{
+ static int retries=0;
+ unsigned char bcsp_sync_pkt[10] = {0xc0,0x00,0x41,0x00,0xbe,0xda,0xdc,0xed,0xed,0xc0};
+ int len;
+
+ if (retries < 10) {
+ 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)
+{
+ static int retries=0;
+ unsigned char bcsp_conf_pkt[10] = {0xc0,0x00,0x41,0x00,0xbe,0xad,0xef,0xac,0xed,0xc0};
+ int len;
+
+ if (retries < 10){
+ 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;
+}
+
+extern int stlc2500_init(int fd, bdaddr_t *bdaddr);
+
+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_init(int fd, bdaddr_t *bdaddr)
+{
+ /* This is broken and the routine got lost somewhere */
+ return -EIO;
+}
+
+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, NULL, NULL },
+ { "ericsson", 0x0000, 0x0000, HCI_UART_H4, 57600, 115200, FLOW_CTL, NULL, ericsson },
+ { "digi", 0x0000, 0x0000, HCI_UART_H4, 9600, 115200, FLOW_CTL, NULL, digi },
+
+ { "bcsp", 0x0000, 0x0000, HCI_UART_BCSP, 115200, 115200, 0, NULL, bcsp },
+
+ /* Xircom PCMCIA cards: Credit Card Adapter and Real Port Adapter */
+ { "xircom", 0x0105, 0x080a, HCI_UART_H4, 115200, 115200, FLOW_CTL, NULL, NULL },
+
+ /* CSR Casira serial adapter or BrainBoxes serial dongle (BL642) */
+ { "csr", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200, FLOW_CTL, NULL, csr },
+
+ /* BrainBoxes PCMCIA card (BL620) */
+ { "bboxes", 0x0160, 0x0002, HCI_UART_H4, 115200, 460800, FLOW_CTL, NULL, csr },
+
+ /* Silicon Wave kits */
+ { "swave", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200, FLOW_CTL, NULL, swave },
+
+ /* Texas Instruments Bluelink (BRF) modules */
+ { "texas", 0x0000, 0x0000, HCI_UART_LL, 115200, 115200, FLOW_CTL, NULL, texas, texas2 },
+ { "texasalt", 0x0000, 0x0000, HCI_UART_LL, 115200, 115200, FLOW_CTL, NULL, texasalt, NULL },
+
+ /* ST Microelectronics minikits based on STLC2410/STLC2415 */
+ { "st", 0x0000, 0x0000, HCI_UART_H4, 57600, 115200, FLOW_CTL, NULL, st },
+
+ /* ST Microelectronics minikits based on STLC2500 */
+ { "stlc2500", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200, FLOW_CTL, "00:80:E1:00:AB:BA", stlc2500 },
+
+ /* Philips generic Ericsson IP core based */
+ { "philips", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200, FLOW_CTL, NULL, NULL },
+
+ /* Philips BGB2xx Module */
+ { "bgb2xx", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200, FLOW_CTL, "BD:B2:10:00:AB:BA", bgb2xx },
+
+ /* Sphinx Electronics PICO Card */
+ { "picocard", 0x025e, 0x1000, HCI_UART_H4, 115200, 115200, FLOW_CTL, NULL, NULL },
+
+ /* Inventel BlueBird Module */
+ { "inventel", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200, FLOW_CTL, NULL, NULL },
+
+ /* COM One Platinium Bluetooth PC Card */
+ { "comone", 0xffff, 0x0101, HCI_UART_BCSP, 115200, 115200, 0, NULL, bcsp },
+
+ /* TDK Bluetooth PC Card and IBM Bluetooth PC Card II */
+ { "tdk", 0x0105, 0x4254, HCI_UART_BCSP, 115200, 115200, 0, NULL, bcsp },
+
+ /* Socket Bluetooth CF Card (Rev G) */
+ { "socket", 0x0104, 0x0096, HCI_UART_BCSP, 230400, 230400, 0, NULL, bcsp },
+
+ /* 3Com Bluetooth Card (Version 3.0) */
+ { "3com", 0x0101, 0x0041, HCI_UART_H4, 115200, 115200, FLOW_CTL, NULL, csr },
+
+ /* AmbiCom BT2000C Bluetooth PC/CF Card */
+ { "bt2000c", 0x022d, 0x2000, HCI_UART_H4, 57600, 460800, FLOW_CTL, NULL, csr },
+
+ /* Zoom Bluetooth PCMCIA Card */
+ { "zoom", 0x0279, 0x950b, HCI_UART_BCSP, 115200, 115200, 0, NULL, bcsp },
+
+ /* Sitecom CN-504 PCMCIA Card */
+ { "sitecom", 0x0279, 0x950b, HCI_UART_BCSP, 115200, 115200, 0, NULL, bcsp },
+
+ /* Billionton PCBTC1 PCMCIA Card */
+ { "billionton", 0x0279, 0x950b, HCI_UART_BCSP, 115200, 115200, 0, NULL, bcsp },
+
+ /* Broadcom BCM2035 */
+ { "bcm2035", 0x0A5C, 0x2035, HCI_UART_H4, 115200, 460800, FLOW_CTL, NULL, bcm2035 },
+
+ { NULL, 0 }
+};
+
+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;
+}
+
+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 */
+int init_uart(char *dev, struct uart_t *u, int send_break)
+{
+ struct termios ti;
+ int fd, i;
+
+ 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 (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] [-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, 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;
+ char dev[PATH_MAX];
+
+ detach = 1;
+ printpid = 0;
+
+ while ((opt=getopt(argc, argv, "bnpt:s:l")) != 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);
+
+ 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:
+ 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);
+
+ n = init_uart(dev, u, send_break);
+ if (n < 0) {
+ perror("Can't initialize device");
+ exit(1);
+ }
+
+ 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;
+
+ while (!__io_canceled) {
+ p.revents = 0;
+ err = poll(&p, 1, 500);
+ 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_st.c b/tools/hciattach_st.c
new file mode 100644
index 00000000..2c7d7438
--- /dev/null
+++ b/tools/hciattach_st.c
@@ -0,0 +1,276 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2005-2008 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>
+
+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 00000000..7c1d58f9
--- /dev/null
+++ b/tools/hciattach_ti.c
@@ -0,0 +1,523 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2007-2008 Texas Instruments, Inc.
+ * Copyright (C) 2005-2008 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>
+
+#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));
+
+extern int set_speed(int fd, struct termios *ti, int speed);
+extern int read_hci_event(int fd, unsigned char* buf, int size);
+
+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[256];
+ 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;
+ }
+
+ 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 00000000..39292c04
--- /dev/null
+++ b/tools/hciattach_tialt.c
@@ -0,0 +1,272 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2005-2008 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>
+
+#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;
+
+extern int read_hci_event(int fd, unsigned char* buf, int size);
+
+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;
+
+ /* Send a speed-change request */
+
+ texas_speed_change_cmd_t cmd;
+ int n;
+
+ cmd.uart_prefix = HCI_COMMAND_PKT;
+ cmd.hci_hdr.opcode = 0xff36;
+ cmd.hci_hdr.plen = sizeof(uint32_t);
+ cmd.speed = speed;
+
+ fprintf(stdout, "Setting speed to %d\n", speed);
+ n = write(fd, &cmd, sizeof(cmd));
+ if (n < 0) {
+ perror("Failed to write speed-set command");
+ return -1;
+ }
+
+ n = write(fd, &cmd, sizeof(cmd));
+ if (n < 0) {
+ perror("Failed to write command.");
+ return -1;
+ }
+ if (n < (int)sizeof(cmd)) {
+ fprintf(stderr, "Wanted to write %d bytes, could only write %d. "
+ "Stop\n", (int)sizeof(cmd), n);
+ return -1;
+ }
+
+ /* Parse speed-change reply */
+ if (read_command_complete(fd, 0xff36, 4) < 0) {
+ return -1;
+ }
+ fprintf(stdout, "Speed changed to %d.\n", speed);
+}
+
+static int texas_load_firmware(int fd, const char *firmware) {
+
+ fprintf(stdout, "Opening firmware file: %s\n", firmware);
+ int fw = open(firmware, O_RDONLY);
+ FAILIF(fw < 0,
+ "Could not open firmware file %s: %s (%d).\n",
+ firmware, strerror(errno), errno);
+
+ fprintf(stdout, "Uploading firmware...\n");
+ do {
+ int nr;
+ /* Read each command and wait for a response. */
+ unsigned char cmdp[1 + sizeof(hci_command_hdr)];
+ nr = read(fw, cmdp, sizeof(cmdp));
+ if (!nr)
+ break;
+ hci_command_hdr *cmd = (hci_command_hdr *)(cmdp + 1);
+ FAILIF(nr != sizeof(cmdp), "Could not read H4 + HCI header!\n");
+ FAILIF(*cmdp != HCI_COMMAND_PKT, "Command is not an H4 command packet!\n");
+
+ unsigned char data[1024];
+ 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 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;
+ int nw = writev(fd, iov_cmd, 2);
+ FAILIF(nw != 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[5] = {
+ "unknown",
+ "unknown",
+ "brf6100",
+ "brf6150",
+ "brf6300"
+ };
+
+ 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 > 4) ? "unknown" : c_brf_chip[brf_chip]),
+ brf_chip);
+
+ char fw[100];
+ 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 00000000..f03b1c16
--- /dev/null
+++ b/tools/hciconfig.8
@@ -0,0 +1,276 @@
+.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 \|]
+.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 00000000..a01937b9
--- /dev/null
+++ b/tools/hciconfig.c
@@ -0,0 +1,1749 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2008 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)
+{
+ printf("\tPacket type: %s\n", hci_ptypetostr(di->pkt_type));
+}
+
+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)
+{
+ printf("\tLink mode: %s\n", hci_lmtostr(di->link_mode));
+}
+
+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 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_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 max_page, features[8];
+ 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);
+ tmp = lmp_featurestostr(di.features, "\t\t", 63);
+ 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]);
+ printf("%s\n", tmp);
+ bt_free(tmp);
+
+ for (i = 1; i <= max_page; i++) {
+ if (hci_read_local_ext_features(dd, 1, &max_page, 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 char *services[] = { "Positioning",
+ "Networking",
+ "Rendering",
+ "Capturing",
+ "Object Transfer",
+ "Audio",
+ "Telephony",
+ "Information" };
+ static char *major_devices[] = { "Miscellaneous",
+ "Computer",
+ "Phone",
+ "LAN Access",
+ "Audio/Video",
+ "Peripheral",
+ "Imaging",
+ "Uncategorized" };
+ 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]) {
+ int first = 1;
+ for (s = 0; s < (sizeof(services) / sizeof(*services)); s++)
+ if (cls[2] & (1 << s)) {
+ if (!first)
+ printf(", ");
+ printf(services[s]);
+ first = 0;
+ }
+ } else
+ printf("Unspecified");
+ printf("\n\tDevice Class: ");
+ if ((cls[1] & 0x1f) >= sizeof(major_devices) / 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.hci_ver);
+
+ print_dev_hdr(&di);
+ printf("\tHCI Ver: %s (0x%x) HCI Rev: 0x%x LMP Ver: %s (0x%x) LMP Subver: 0x%x\n"
+ "\tManufacturer: %s (%d)\n",
+ 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_inquiry_transmit_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", *((uint8_t *) ptr));
+ 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 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\n", di->name, hci_dtypetostr(di->type) );
+ 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;
+
+ print_dev_hdr(di);
+
+ printf("\t%s\n", hci_dflagstostr(di->flags) );
+
+ printf("\tRX bytes:%d acl:%d sco:%d events:%d errors:%d\n",
+ st->byte_rx, st->acl_rx, st->sco_rx, st->evt_rx, st->err_rx);
+
+ printf("\tTX bytes:%d acl:%d sco:%d commands:%d errors:%d\n",
+ st->byte_tx, st->acl_tx, st->sco_tx, st->cmd_tx, st->err_tx);
+
+ if (all && !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" },
+ { 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, 5))
+ continue;
+
+ if (command[i].opt) {
+ argc--; argv++;
+ }
+
+ command[i].func(ctl, di.dev_id, *argv);
+ cmd = 1;
+ break;
+ }
+ argc--; argv++;
+ }
+
+ if (!cmd)
+ print_dev_info(ctl, &di);
+
+ close(ctl);
+ return 0;
+}
diff --git a/tools/hcisecfilter.c b/tools/hcisecfilter.c
new file mode 100644
index 00000000..81a2fca4
--- /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-2008 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_INQUIRY_TRANSMIT_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 00000000..8a7131d2
--- /dev/null
+++ b/tools/hcitool.1
@@ -0,0 +1,206 @@
+.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>"
+Delete baseband connection from remote device with Bluetooth address
+.IR bdaddr .
+.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 00000000..02f56a43
--- /dev/null
+++ b/tools/hcitool.c
@@ -0,0 +1,2435 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2008 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"
+
+#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 char *type2str(uint8_t type)
+{
+ switch (type) {
+ case SCO_LINK:
+ return "SCO";
+ case ACL_LINK:
+ return "ACL";
+ case ESCO_LINK:
+ return "eSCO";
+ 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];
+ ba2str(&ci->bdaddr, addr);
+ printf("\t%s %s %s handle %d state %d lm %s\n",
+ ci->out ? "<" : ">", type2str(ci->type),
+ addr, ci->handle, ci->state,
+ hci_lmtostr(ci->link_mode));
+ }
+
+ 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))
+ return 1;
+
+ 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 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(dev_help);
+ return;
+ }
+ }
+
+ 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 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(inq_help);
+ return;
+ }
+ }
+
+ 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 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];
+ uint16_t handle;
+ 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(scan_help);
+ return;
+ }
+ }
+
+ 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++) {
+ 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 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(name_help);
+ return;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ printf(name_help);
+ return;
+ }
+
+ 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 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 max_page, features[8];
+ char name[249], oui[9], *comp;
+ struct hci_version version;
+ struct hci_dev_info di;
+ struct hci_conn_info_req *cr;
+ int opt, dd, cc = 0;
+
+ for_each_opt(opt, info_options, NULL) {
+ switch (opt) {
+ default:
+ printf(info_help);
+ return;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ printf(info_help);
+ return;
+ }
+
+ 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);
+ }
+ 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);
+ }
+
+ if (hci_read_remote_features(dd, handle, features, 20000) == 0) {
+ char *tmp = lmp_featurestostr(features, "\t\t", 63);
+ printf("\tFeatures: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n%s\n",
+ features[0], features[1], features[2], features[3],
+ features[4], features[5], features[6], features[7], tmp);
+ bt_free(tmp);
+ }
+
+ if ((di.features[7] & LMP_EXT_FEAT) && (features[7] & LMP_EXT_FEAT)) {
+ if (hci_read_remote_ext_features(dd, handle, 0,
+ &max_page, features, 20000) == 0)
+ if (max_page > 0)
+ printf("\tExtended features: %d page%s\n",
+ max_page, max_page > 1 ? "s" : "");
+ }
+
+ 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 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(spinq_help);
+ return;
+ }
+ }
+
+ 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 char *epinq_help =
+ "Usage:\n"
+ "\tspinq\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(epinq_help);
+ return;
+ }
+ }
+
+ 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 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(cmd_help);
+ return;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 2) {
+ printf(cmd_help);
+ return;
+ }
+
+ 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(cmd_help);
+ return;
+ }
+
+ for (i = 2, len = 0; i < argc && len < 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 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(con_help);
+ return;
+ }
+ }
+
+ 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 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(cc_help);
+ return;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ printf(cc_help);
+ return;
+ }
+
+ 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 char *dc_help =
+ "Usage:\n"
+ "\tdc <bdaddr>\n";
+
+static void cmd_dc(int dev_id, int argc, char **argv)
+{
+ struct hci_conn_info_req *cr;
+ bdaddr_t bdaddr;
+ int opt, dd;
+
+ for_each_opt(opt, dc_options, NULL) {
+ switch (opt) {
+ default:
+ printf(dc_help);
+ return;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ printf(dc_help);
+ return;
+ }
+
+ 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_disconnect(dd, htobs(cr->conn_info->handle),
+ HCI_OE_USER_ENDED_CONNECTION, 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 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(sr_help);
+ return;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 2) {
+ printf(sr_help);
+ return;
+ }
+
+ 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 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(rssi_help);
+ return;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ printf(rssi_help);
+ return;
+ }
+
+ 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 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(lq_help);
+ return;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ printf(lq_help);
+ return;
+ }
+
+ 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 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(tpl_help);
+ return;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ printf(tpl_help);
+ return;
+ }
+
+ 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 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(afh_help);
+ return;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ printf(afh_help);
+ return;
+ }
+
+ 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 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(cpt_help);
+ return;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 2) {
+ printf(cpt_help);
+ return;
+ }
+
+ 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 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(lp_help);
+ return;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ printf(lp_help);
+ return;
+ }
+
+ 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 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(lst_help);
+ return;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ printf(lst_help);
+ return;
+ }
+
+ 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 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(auth_help);
+ return;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ printf(auth_help);
+ return;
+ }
+
+ 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 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(enc_help);
+ return;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ printf(enc_help);
+ return;
+ }
+
+ 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 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(key_help);
+ return;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ printf(key_help);
+ return;
+ }
+
+ 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 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(clkoff_help);
+ return;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ printf(clkoff_help);
+ return;
+ }
+
+ 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 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(clock_help);
+ return;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ 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 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" },
+ { 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], 3))
+ continue;
+ command[i].func(dev_id, argc, argv);
+ break;
+ }
+ return 0;
+}
diff --git a/tools/hid2hci.8 b/tools/hid2hci.8
new file mode 100644
index 00000000..6aa9be29
--- /dev/null
+++ b/tools/hid2hci.8
@@ -0,0 +1,45 @@
+.\"
+.\" 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 "JUNE 6, 2003" "" ""
+
+.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 HID proxy Bluetooth dongle 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 -0
+Switches the device into HCI mode.
+.TP
+.BI -1
+Switches the device into HID mode.
+.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 00000000..eee6da8f
--- /dev/null
+++ b/tools/hid2hci.c
@@ -0,0 +1,390 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2003-2008 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 device_id {
+ int mode;
+ uint16_t vendor;
+ uint16_t product;
+ int (*func)(struct device_info *dev);
+};
+
+struct device_info {
+ struct usb_device *dev;
+ struct device_id *id;
+};
+
+static int switch_hidproxy(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->id->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;
+ int i, 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 || dinfo.busnum != atoi(devinfo->dev->bus->dirname) ||
+ 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, 0x13, 0x00, 0x00 };
+
+ struct usb_dev_handle *handle;
+ int err;
+
+ handle = usb_open(devinfo->dev);
+ if (handle) {
+ usb_claim_interface(handle, 0);
+ usb_detach_kernel_driver_np(handle, 0);
+ }
+
+ err = usb_control_msg(handle,
+ USB_ENDPOINT_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0x09, 0x7f | (0x03 << 8), 0,
+ report, sizeof(report), 10000);
+
+ if (err == 0) {
+ err = -1;
+ errno = EALREADY;
+ } else {
+ if (errno == ETIMEDOUT)
+ err = 0;
+ }
+
+ usb_close(handle);
+
+ return err;
+}
+
+static struct device_id device_list[] = {
+ { HCI, 0x0a12, 0x1000, switch_hidproxy },
+ { HID, 0x0a12, 0x0001, switch_hidproxy },
+ { HCI, 0x0458, 0x1000, switch_hidproxy },
+ { HID, 0x0458, 0x003f, switch_hidproxy },
+ { HCI, 0x05ac, 0x1000, switch_hidproxy },
+ { HID, 0x05ac, 0x8203, switch_hidproxy },
+ { HID, 0x05ac, 0x8204, switch_hidproxy }, /* Apple Mac mini */
+ { HID, 0x05ac, 0x8207, switch_hidproxy }, /* Apple Power Mac G5 */
+ { HCI, 0x046d, 0xc703, switch_logitech },
+ { HCI, 0x046d, 0xc704, switch_logitech },
+ { HCI, 0x046d, 0xc705, switch_logitech },
+ { HCI, 0x046d, 0xc70a, switch_logitech }, /* Logitech diNovo mouse */
+ { HCI, 0x046d, 0xc70b, switch_logitech }, /* Logitech diNovo Laser keyboard */
+ { HCI, 0x046d, 0xc70c, switch_logitech }, /* Logitech diNovo Laser mouse */
+ { HCI, 0x046d, 0xc70e, switch_logitech }, /* Logitech diNovo keyboard */
+ { HCI, 0x046d, 0xc713, switch_logitech }, /* Logitech diNovo Edge */
+ { HCI, 0x046d, 0xc714, switch_logitech }, /* Logitech diNovo Edge */
+ { HCI, 0x413c, 0x8158, switch_dell }, /* Dell Wireless 370 */
+ { HCI, 0x413c, 0x8154, switch_dell }, /* Dell Wireless 410 */
+ { -1 }
+};
+
+static struct device_id *match_device(int mode, uint16_t vendor, uint16_t product)
+{
+ int i;
+
+ for (i = 0; device_list[i].mode >= 0; i++) {
+ if (mode != device_list[i].mode)
+ continue;
+ if (vendor == device_list[i].vendor &&
+ product == device_list[i].product)
+ return &device_list[i];
+ }
+
+ return NULL;
+}
+
+static int find_devices(int mode, struct device_info *devinfo, size_t size)
+{
+ struct usb_bus *bus;
+ struct usb_device *dev;
+ struct device_id *id;
+ 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(mode, 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("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-0, --tohci Switch to HCI mode (default)\n"
+ "\t-1, --tohid Switch to HID mode\n"
+ "\n");
+}
+
+static struct option main_options[] = {
+ { "help", 0, 0, 'h' },
+ { "quiet", 0, 0, 'q' },
+ { "tohci", 0, 0, '0' },
+ { "tohid", 0, 0, '1' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ struct device_info dev[16];
+ int i, opt, num, quiet = 0, mode = HCI;
+
+ while ((opt = getopt_long(argc, argv, "+01qh", main_options, NULL)) != -1) {
+ switch (opt) {
+ case '0':
+ mode = HCI;
+ break;
+ case '1':
+ mode = HID;
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 'h':
+ usage();
+ exit(0);
+ default:
+ exit(0);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ usb_init();
+
+ num = find_devices(mode, dev, sizeof(dev) / sizeof(dev[0]));
+ if (num <= 0) {
+ if (!quiet)
+ fprintf(stderr, "No devices in %s mode found\n",
+ mode ? "HCI" : "HID");
+ exit(1);
+ }
+
+ for (i = 0; i < num; i++) {
+ struct device_id *id = dev[i].id;
+
+ if (!quiet)
+ printf("Switching device %04x:%04x to %s mode ",
+ id->vendor, id->product, mode ? "HID" : "HCI");
+ fflush(stdout);
+
+ if (id->func(&dev[i]) < 0) {
+ if (!quiet)
+ printf("failed (%s)\n", strerror(errno));
+ } else {
+ if (!quiet)
+ printf("was successful\n");
+ }
+ }
+
+ return 0;
+}
diff --git a/tools/l2ping.1 b/tools/l2ping.1
new file mode 100644
index 00000000..8f56fb5e
--- /dev/null
+++ b/tools/l2ping.1
@@ -0,0 +1,67 @@
+.TH L2PING 1 "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 [\| \-f \|]
+.RB [\| \-r \|]
+.I bd_addr
+
+.SH DESCRIPTION
+.LP
+.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
+.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
+.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 00000000..1e2aab6d
--- /dev/null
+++ b/tools/l2ping.c
@@ -0,0 +1,290 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2008 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;
+
+/* 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 *buf;
+ char str[18];
+ int i, sk, lost;
+ uint8_t id;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = stat;
+ sigaction(SIGINT, &sa, NULL);
+
+ buf = malloc(L2CAP_CMD_HDR_SIZE + size);
+ if (!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");
+ free(buf);
+ exit(1);
+ }
+
+ /* 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");
+ close(sk);
+ free(buf);
+ exit(1);
+ }
+
+ /* 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");
+ close(sk);
+ free(buf);
+ exit(1);
+ }
+
+ /* 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");
+ close(sk);
+ free(buf);
+ exit(1);
+ }
+
+ ba2str(&addr.l2_bdaddr, str);
+ printf("Ping: %s from %s (data size %d) ...\n", svr, str, size);
+
+ /* Initialize buffer */
+ for (i = 0; i < size; i++)
+ 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 *cmd = (l2cap_cmd_hdr *) buf;
+
+ /* Build command header */
+ cmd->ident = id;
+ cmd->len = htobs(size);
+
+ if (reverse)
+ cmd->code = L2CAP_ECHO_RSP;
+ else
+ cmd->code = L2CAP_ECHO_REQ;
+
+ gettimeofday(&tv_send, NULL);
+
+ /* Send Echo Command */
+ if (send(sk, buf, L2CAP_CMD_HDR_SIZE + size, 0) <= 0) {
+ perror("Send failed");
+ exit(1);
+ }
+
+ /* 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");
+ exit(1);
+ }
+
+ if (!err) {
+ lost = 1;
+ break;
+ }
+
+ if ((err = recv(sk, buf, L2CAP_CMD_HDR_SIZE + size, 0)) < 0) {
+ perror("Recv failed");
+ exit(1);
+ }
+
+ if (!err){
+ printf("Disconnected\n");
+ exit(1);
+ }
+
+ cmd->len = btohs(cmd->len);
+
+ /* Check for our id */
+ if (cmd->ident != id)
+ continue;
+
+ /* Check type */
+ if (!reverse && cmd->code == L2CAP_ECHO_RSP)
+ break;
+
+ if (cmd->code == L2CAP_COMMAND_REJ) {
+ printf("Peer doesn't support Echo packets\n");
+ exit(1);
+ }
+
+ }
+ sent_pkt++;
+
+ if (!lost) {
+ recv_pkt++;
+
+ gettimeofday(&tv_recv, NULL);
+ timersub(&tv_recv, &tv_send, &tv_diff);
+
+ printf("%d bytes from %s id %d time %.2fms\n", 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);
+}
+
+static void usage(void)
+{
+ printf("l2ping - L2CAP ping\n");
+ printf("Usage:\n");
+ printf("\tl2ping [-i device] [-s size] [-c count] [-t timeout] [-f] [-r] <bdaddr>\n");
+}
+
+int main(int argc, char *argv[])
+{
+ int opt;
+
+ /* Default options */
+ bacpy(&bdaddr, BDADDR_ANY);
+
+ while ((opt=getopt(argc,argv,"i:s:c:t:fr")) != EOF) {
+ switch(opt) {
+ case 'i':
+ if (!strncasecmp(optarg, "hci", 3))
+ hci_devba(atoi(optarg + 3), &bdaddr);
+ else
+ str2ba(optarg, &bdaddr);
+ break;
+
+ case 'f':
+ /* Kinda flood ping */
+ delay = 0;
+ break;
+
+ case 'r':
+ /* Use responses instead of requests */
+ reverse = 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/ppporc.c b/tools/ppporc.c
new file mode 100644
index 00000000..2d8b034d
--- /dev/null
+++ b/tools/ppporc.c
@@ -0,0 +1,273 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2008 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 <sys/poll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+
+extern int optind, opterr, optopt;
+extern char *optarg;
+
+/* 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/sdptool.1 b/tools/sdptool.1
new file mode 100644
index 00000000..686ec95a
--- /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 00000000..003d8875
--- /dev/null
+++ b/tools/sdptool.c
@@ -0,0 +1,4134 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2001-2002 Nokia Corporation
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2008 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, "MDP", NULL, 0 },
+ { 0x1401, "MDPSource", NULL, 0 },
+ { 0x1402, "MDPSink", 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 > 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 = NULL;
+ uint16_t attrId;
+ struct service_context *service = (struct service_context *) userData;
+ struct attrib_context context;
+ struct attrib_def *attrDef = NULL;
+ int i;
+
+ sdpdata = (sdp_data_t *)value;
+ 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 */
+ if (sdpdata)
+ sdp_data_printf(sdpdata, &context, 2);
+ else
+ printf(" NULL value\n");
+ /* 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;
+
+ /* 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);
+
+ if (data)
+ print_raw_data(data, 2);
+ else
+ printf(" NULL value\n");
+}
+
+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 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(set_help);
+ return -1;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 3) {
+ printf(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 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(seq_help);
+ return -1;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 3) {
+ printf(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;
+ 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, 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]);
+
+ 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("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 };
+ //uint8_t formats[] = { 0xff };
+ void *dtds[sizeof(formats)], *values[sizeof(formats)];
+ 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_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;
+ 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;
+ 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;
+}
+
+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 },
+
+ { "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, },
+
+ { 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 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(add_help);
+ return -1;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ printf(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 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(del_help);
+ return -1;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ printf(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(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 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(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 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(search_help);
+ return -1;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ printf(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 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 };
+ bdaddr_t bdaddr;
+ int i, n, opt, err = 0, num = 32;
+
+ /* 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(records_help);
+ return -1;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ printf(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 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(get_help);
+ return -1;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ printf(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 00000000..6928da95
--- /dev/null
+++ b/tools/ubcsp.c
@@ -0,0 +1,1193 @@
+/*
+ *
+ * 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;
+}
+
+/*****************************************************************************/
+/** **/
+/** ubcsp_end_of_functions **/
+/** **/
+/*****************************************************************************/
+
+/* This function is only included to allow for the size
+ of the code to be determined */
+
+void ubcsp_end_of_functions (void)
+{
+}
diff --git a/tools/ubcsp.h b/tools/ubcsp.h
new file mode 100644
index 00000000..6a74e9a1
--- /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