summaryrefslogtreecommitdiffstats
path: root/usr.sbin/bluetooth/bthidcontrol/sdp.c
diff options
context:
space:
mode:
authoremax <emax@FreeBSD.org>2004-04-10 00:18:00 +0000
committeremax <emax@FreeBSD.org>2004-04-10 00:18:00 +0000
commit043ec21649d9c0e33013b9092b6ea700f0d15ab8 (patch)
tree7d74dd469839b4cadda98c84728a960fffee125a /usr.sbin/bluetooth/bthidcontrol/sdp.c
parent96b75c2267d1d5caf4783f60f78743070ccfa0c8 (diff)
downloadFreeBSD-src-043ec21649d9c0e33013b9092b6ea700f0d15ab8.zip
FreeBSD-src-043ec21649d9c0e33013b9092b6ea700f0d15ab8.tar.gz
Start committing Bluetooth HID (Human Interface Device) support.
Note: bthidd(8) is still not complete. Need to commit kernel support (a-la Linux /dev/input) to feed HID events into kernel. Also need to write bthidd(8) and bthidd.conf(5) man pages.
Diffstat (limited to 'usr.sbin/bluetooth/bthidcontrol/sdp.c')
-rw-r--r--usr.sbin/bluetooth/bthidcontrol/sdp.c432
1 files changed, 432 insertions, 0 deletions
diff --git a/usr.sbin/bluetooth/bthidcontrol/sdp.c b/usr.sbin/bluetooth/bthidcontrol/sdp.c
new file mode 100644
index 0000000..e39c8ca
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidcontrol/sdp.c
@@ -0,0 +1,432 @@
+/*
+ * sdp.c
+ *
+ * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: sdp.c,v 1.3 2004/02/17 22:14:57 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <bluetooth.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+#include <errno.h>
+#include <sdp.h>
+#include <stdio.h>
+#include <string.h>
+#include <usbhid.h>
+#include "bthid_config.h"
+#include "bthidcontrol.h"
+
+static int32_t hid_sdp_query (bdaddr_t const *local, struct hid_device *hd, int32_t *error);
+static int32_t hid_sdp_parse_protocol_descriptor_list (sdp_attr_p a);
+static int32_t hid_sdp_parse_hid_descriptor (sdp_attr_p a);
+static int32_t hid_sdp_parse_boolean (sdp_attr_p a);
+
+static uint16_t service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE;
+
+static uint32_t attrs[] = {
+SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
+ SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
+SDP_ATTR_RANGE (SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS,
+ SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS),
+SDP_ATTR_RANGE( 0x0205, /* HIDReconnectInitiate */
+ 0x0206), /* HIDDesctiptorList */
+SDP_ATTR_RANGE( 0x020a, /* HIDBatteryPower */
+ 0x020a),
+SDP_ATTR_RANGE( 0x020d, /* HIDNormallyConnectable */
+ 0x020d)
+ };
+#define nattrs (sizeof(attrs)/sizeof(attrs[0]))
+
+static sdp_attr_t values[8];
+#define nvalues (sizeof(values)/sizeof(values[0]))
+
+static uint8_t buffer[nvalues][512];
+
+/*
+ * Query remote device
+ */
+
+#undef hid_sdp_query_exit
+#define hid_sdp_query_exit(e) { \
+ if (error != NULL) \
+ *error = (e); \
+ if (ss != NULL) { \
+ sdp_close(ss); \
+ ss = NULL; \
+ } \
+ return (((e) == 0)? 0 : -1); \
+}
+
+static int32_t
+hid_sdp_query(bdaddr_t const *local, struct hid_device *hd, int32_t *error)
+{
+ void *ss = NULL;
+ uint8_t *hid_descriptor = NULL;
+ int32_t i, control_psm = -1, interrupt_psm = -1,
+ reconnect_initiate = -1,
+ normally_connectable = 0, battery_power = 0,
+ hid_descriptor_length = -1;
+
+ if (local == NULL)
+ local = NG_HCI_BDADDR_ANY;
+ if (hd == NULL)
+ hid_sdp_query_exit(EINVAL);
+
+ for (i = 0; i < nvalues; i ++) {
+ values[i].flags = SDP_ATTR_INVALID;
+ values[i].attr = 0;
+ values[i].vlen = sizeof(buffer[i]);
+ values[i].value = buffer[i];
+ }
+
+ if ((ss = sdp_open(local, &hd->bdaddr)) == NULL)
+ hid_sdp_query_exit(ENOMEM);
+ if (sdp_error(ss) != 0)
+ hid_sdp_query_exit(sdp_error(ss));
+ if (sdp_search(ss, 1, &service, nattrs, attrs, nvalues, values) != 0)
+ hid_sdp_query_exit(sdp_error(ss));
+
+ sdp_close(ss);
+ ss = NULL;
+
+ for (i = 0; i < nvalues; i ++) {
+ if (values[i].flags != SDP_ATTR_OK)
+ continue;
+
+ switch (values[i].attr) {
+ case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
+ control_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
+ break;
+
+ case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS:
+ interrupt_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
+ break;
+
+ case 0x0205: /* HIDReconnectInitiate */
+ reconnect_initiate = hid_sdp_parse_boolean(&values[i]);
+ break;
+
+ case 0x0206: /* HIDDesctiptorList */
+ if (hid_sdp_parse_hid_descriptor(&values[i]) == 0) {
+ hid_descriptor = values[i].value;
+ hid_descriptor_length = values[i].vlen;
+ }
+ break;
+
+ case 0x020a: /* HIDBatteryPower */
+ battery_power = hid_sdp_parse_boolean(&values[i]);
+ break;
+
+ case 0x020d: /* HIDNormallyConnectable */
+ normally_connectable = hid_sdp_parse_boolean(&values[i]);
+ break;
+ }
+ }
+
+ if (control_psm == -1 || interrupt_psm == -1 ||
+ reconnect_initiate == -1 || normally_connectable == -1 ||
+ hid_descriptor == NULL || hid_descriptor_length == -1)
+ hid_sdp_query_exit(ENOATTR);
+
+ hd->control_psm = control_psm;
+ hd->interrupt_psm = interrupt_psm;
+ hd->reconnect_initiate = reconnect_initiate? 1 : 0;
+ hd->battery_power = battery_power? 1 : 0;
+ hd->normally_connectable = normally_connectable? 1 : 0;
+ hd->desc = hid_use_report_desc(hid_descriptor, hid_descriptor_length);
+ if (hd->desc == NULL)
+ hid_sdp_query_exit(ENOMEM);
+
+ return (0);
+}
+
+/*
+ * seq len 2
+ * seq len 2
+ * uuid value 3
+ * uint16 value 3
+ * seq len 2
+ * uuid value 3
+ */
+
+static int32_t
+hid_sdp_parse_protocol_descriptor_list(sdp_attr_p a)
+{
+ uint8_t *ptr = a->value;
+ uint8_t *end = a->value + a->vlen;
+ int32_t type, len, uuid, psm;
+
+ if (end - ptr < 15)
+ return (-1);
+
+ if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) {
+ SDP_GET8(type, ptr);
+ switch (type) {
+ case SDP_DATA_SEQ8:
+ SDP_GET8(len, ptr);
+ break;
+
+ case SDP_DATA_SEQ16:
+ SDP_GET16(len, ptr);
+ break;
+
+ case SDP_DATA_SEQ32:
+ SDP_GET32(len, ptr);
+ break;
+
+ default:
+ return (-1);
+ }
+ if (ptr + len > end)
+ return (-1);
+ }
+
+ SDP_GET8(type, ptr);
+ switch (type) {
+ case SDP_DATA_SEQ8:
+ SDP_GET8(len, ptr);
+ break;
+
+ case SDP_DATA_SEQ16:
+ SDP_GET16(len, ptr);
+ break;
+
+ case SDP_DATA_SEQ32:
+ SDP_GET32(len, ptr);
+ break;
+
+ default:
+ return (-1);
+ }
+ if (ptr + len > end)
+ return (-1);
+
+ /* Protocol */
+ SDP_GET8(type, ptr);
+ switch (type) {
+ case SDP_DATA_SEQ8:
+ SDP_GET8(len, ptr);
+ break;
+
+ case SDP_DATA_SEQ16:
+ SDP_GET16(len, ptr);
+ break;
+
+ case SDP_DATA_SEQ32:
+ SDP_GET32(len, ptr);
+ break;
+
+ default:
+ return (-1);
+ }
+ if (ptr + len > end)
+ return (-1);
+
+ /* UUID */
+ if (ptr + 3 > end)
+ return (-1);
+ SDP_GET8(type, ptr);
+ switch (type) {
+ case SDP_DATA_UUID16:
+ SDP_GET16(uuid, ptr);
+ if (uuid != SDP_UUID_PROTOCOL_L2CAP)
+ return (-1);
+ break;
+
+ case SDP_DATA_UUID32: /* XXX FIXME can we have 32-bit UUID */
+ case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
+ default:
+ return (-1);
+ }
+
+ /* PSM */
+ if (ptr + 3 > end)
+ return (-1);
+ SDP_GET8(type, ptr);
+ if (type != SDP_DATA_UINT16)
+ return (-1);
+ SDP_GET16(psm, ptr);
+
+ return (psm);
+}
+
+/*
+ * seq len 2
+ * seq len 2
+ * uint8 value8 2
+ * str value 3
+ */
+
+static int32_t
+hid_sdp_parse_hid_descriptor(sdp_attr_p a)
+{
+ uint8_t *ptr = a->value;
+ uint8_t *end = a->value + a->vlen;
+ int32_t type, len, descriptor_type;
+
+ if (end - ptr < 9)
+ return (-1);
+
+ SDP_GET8(type, ptr);
+ switch (type) {
+ case SDP_DATA_SEQ8:
+ SDP_GET8(len, ptr);
+ break;
+
+ case SDP_DATA_SEQ16:
+ SDP_GET16(len, ptr);
+ break;
+
+ case SDP_DATA_SEQ32:
+ SDP_GET32(len, ptr);
+ break;
+
+ default:
+ return (-1);
+ }
+ if (ptr + len > end)
+ return (-1);
+
+ while (ptr < end) {
+ /* Descriptor */
+ SDP_GET8(type, ptr);
+ switch (type) {
+ case SDP_DATA_SEQ8:
+ if (ptr + 1 > end)
+ return (-1);
+ SDP_GET8(len, ptr);
+ break;
+
+ case SDP_DATA_SEQ16:
+ if (ptr + 2 > end)
+ return (-1);
+ SDP_GET16(len, ptr);
+ break;
+
+ case SDP_DATA_SEQ32:
+ if (ptr + 4 > end)
+ return (-1);
+ SDP_GET32(len, ptr);
+ break;
+
+ default:
+ return (-1);
+ }
+
+ /* Descripor type */
+ if (ptr + 1 > end)
+ return (-1);
+ SDP_GET8(type, ptr);
+ if (type != SDP_DATA_UINT8 || ptr + 1 > end)
+ return (-1);
+ SDP_GET8(descriptor_type, ptr);
+
+ /* Descriptor value */
+ if (ptr + 1 > end)
+ return (-1);
+ SDP_GET8(type, ptr);
+ switch (type) {
+ case SDP_DATA_STR8:
+ if (ptr + 1 > end)
+ return (-1);
+ SDP_GET8(len, ptr);
+ break;
+
+ case SDP_DATA_STR16:
+ if (ptr + 2 > end)
+ return (-1);
+ SDP_GET16(len, ptr);
+ break;
+
+ case SDP_DATA_STR32:
+ if (ptr + 4 > end)
+ return (-1);
+ SDP_GET32(len, ptr);
+ break;
+
+ default:
+ return (-1);
+ }
+ if (ptr + len > end)
+ return (-1);
+
+ if (descriptor_type == UDESC_REPORT && len > 0) {
+ a->value = ptr;
+ a->vlen = len;
+
+ return (0);
+ }
+
+ ptr += len;
+ }
+
+ return (-1);
+}
+
+/* bool8 int8 */
+static int32_t
+hid_sdp_parse_boolean(sdp_attr_p a)
+{
+ if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL)
+ return (-1);
+
+ return (a->value[1]);
+}
+
+/* Perform SDP query */
+static int32_t
+hid_query(bdaddr_t *bdaddr, int argc, char **argv)
+{
+ struct hid_device hd;
+ int e;
+
+ memcpy(&hd.bdaddr, bdaddr, sizeof(hd.bdaddr));
+ if (hid_sdp_query(NULL, &hd, &e) < 0) {
+ fprintf(stderr, "Could not perform SDP query on the " \
+ "device %s. %s (%d)\n", bt_ntoa(bdaddr, NULL),
+ strerror(e), e);
+ return (FAILED);
+ }
+
+ print_hid_device(&hd, stdout);
+
+ return (OK);
+}
+
+struct bthid_command sdp_commands[] =
+{
+{
+"Query",
+"Perform SDP query to the specified device and print HID configuration entry\n"\
+"for the device. The configuration entry should be appended to the Bluetooth\n"\
+"HID daemon configuration file and the daemon should be restarted.\n",
+hid_query
+},
+{ NULL, NULL, NULL }
+};
+
OpenPOWER on IntegriCloud