diff options
author | Marcel Holtmann <marcel@holtmann.org> | 2008-07-26 19:00:53 +0200 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2008-07-26 19:00:53 +0200 |
commit | d6ae1c3f777832f8e32702f81fe64e33a1396928 (patch) | |
tree | 159a1e59f3929c9d795dbd1f3edd84d9dccba048 /tools | |
parent | b8e5fea8d31fbcd3d1c044385f8217dbf39892bb (diff) | |
parent | 3382af9114a9b2e657c7ddd0a5511edda6a37a90 (diff) |
Import bluez-utils-3.36 revision history
Diffstat (limited to 'tools')
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 |