4 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
6 * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * $Id: sdp.c,v 1.3 2004/02/17 22:14:57 max Exp $
34 #include <sys/types.h>
35 #include <sys/queue.h>
36 #include <sys/sysctl.h>
37 #define L2CAP_SOCKET_CHECKED
38 #include <bluetooth.h>
39 #include <dev/usb/usb.h>
40 #include <dev/usb/usbhid.h>
46 #include "bthid_config.h"
47 #include "bthidcontrol.h"
49 static int32_t hid_sdp_query (bdaddr_t const *local, struct hid_device *hd, int32_t *error);
50 static int32_t hid_sdp_parse_protocol_descriptor_list (sdp_attr_p a);
51 static int32_t hid_sdp_parse_hid_descriptor (sdp_attr_p a);
52 static int32_t hid_sdp_parse_boolean (sdp_attr_p a);
55 * Hard coded attibute IDs taken from the
56 * DEVICE IDENTIFICATION PROFILE SPECIFICATION V13 p.12
59 #define SDP_ATTR_DEVICE_ID_SERVICE_VENDORID 0x0201
60 #define SDP_ATTR_DEVICE_ID_SERVICE_PRODUCTID 0x0202
61 #define SDP_ATTR_DEVICE_ID_SERVICE_VERSION 0x0203
62 #define SDP_ATTR_DEVICE_ID_RANGE SDP_ATTR_RANGE( \
63 SDP_ATTR_DEVICE_ID_SERVICE_VENDORID, SDP_ATTR_DEVICE_ID_SERVICE_VERSION )
65 static uint16_t service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE;
66 static uint16_t service_devid = SDP_SERVICE_CLASS_PNP_INFORMATION;
67 static uint32_t attrs_devid = SDP_ATTR_DEVICE_ID_RANGE;
69 static uint32_t attrs[] = {
70 SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
71 SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
72 SDP_ATTR_RANGE (SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS,
73 SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS),
74 SDP_ATTR_RANGE( 0x0205, /* HIDReconnectInitiate */
76 SDP_ATTR_RANGE( 0x0206, /* HIDDescriptorList */
78 SDP_ATTR_RANGE( 0x0209, /* HIDBatteryPower */
80 SDP_ATTR_RANGE( 0x020d, /* HIDNormallyConnectable */
83 #define nattrs (sizeof(attrs)/sizeof(attrs[0]))
85 static sdp_attr_t values[8];
86 #define nvalues (sizeof(values)/sizeof(values[0]))
88 static uint8_t buffer[nvalues][512];
94 #undef hid_sdp_query_exit
95 #define hid_sdp_query_exit(e) { \
102 return (((e) == 0)? 0 : -1); \
106 hid_init_return_values() {
108 for (i = 0; i < nvalues; i ++) {
109 values[i].flags = SDP_ATTR_INVALID;
111 values[i].vlen = sizeof(buffer[i]);
112 values[i].value = buffer[i];
117 hid_sdp_query(bdaddr_t const *local, struct hid_device *hd, int32_t *error)
120 uint8_t *hid_descriptor = NULL, *v;
121 int32_t i, control_psm = -1, interrupt_psm = -1,
122 reconnect_initiate = -1,
123 normally_connectable = 0, battery_power = 0,
124 hid_descriptor_length = -1, type;
125 int16_t vendor_id = 0, product_id = 0, version = 0;
127 char devname[HCI_DEVNAME_SIZE];
130 local = NG_HCI_BDADDR_ANY;
132 hid_sdp_query_exit(EINVAL);
134 hid_init_return_values();
136 if ((ss = sdp_open(local, &hd->bdaddr)) == NULL)
137 hid_sdp_query_exit(ENOMEM);
138 if (sdp_error(ss) != 0)
139 hid_sdp_query_exit(sdp_error(ss));
140 if (sdp_search(ss, 1, &service, nattrs, attrs, nvalues, values) != 0)
141 hid_sdp_query_exit(sdp_error(ss));
143 for (i = 0; i < nvalues; i ++) {
144 if (values[i].flags != SDP_ATTR_OK)
147 switch (values[i].attr) {
148 case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
149 control_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
152 case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS:
153 interrupt_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
156 case 0x0205: /* HIDReconnectInitiate */
157 reconnect_initiate = hid_sdp_parse_boolean(&values[i]);
160 case 0x0206: /* HIDDescriptorList */
161 if (hid_sdp_parse_hid_descriptor(&values[i]) == 0) {
162 hid_descriptor = values[i].value;
163 hid_descriptor_length = values[i].vlen;
167 case 0x0209: /* HIDBatteryPower */
168 battery_power = hid_sdp_parse_boolean(&values[i]);
171 case 0x020d: /* HIDNormallyConnectable */
172 normally_connectable = hid_sdp_parse_boolean(&values[i]);
177 hid_init_return_values();
179 if (sdp_search(ss, 1, &service_devid, 1, &attrs_devid, nvalues, values) != 0)
180 hid_sdp_query_exit(sdp_error(ss));
182 /* Try extract HCI bdaddr from opened SDP session */
183 if (sdp_get_lcaddr(ss, &sdp_local) != 0 ||
184 bt_devname(devname, &sdp_local) == 0)
185 hid_sdp_query_exit(ENOATTR);
190 /* If search is successful, scan through return vals */
191 for (i = 0; i < 3; i ++ ) {
192 if (values[i].flags == SDP_ATTR_INVALID )
195 /* Expecting tag + uint16_t on all 3 attributes */
196 if (values[i].vlen != 3)
199 /* Make sure, we're reading a uint16_t */
202 if (type != SDP_DATA_UINT16 )
205 switch (values[i].attr) {
206 case SDP_ATTR_DEVICE_ID_SERVICE_VENDORID:
207 SDP_GET16(vendor_id, v);
209 case SDP_ATTR_DEVICE_ID_SERVICE_PRODUCTID:
210 SDP_GET16(product_id, v);
212 case SDP_ATTR_DEVICE_ID_SERVICE_VERSION:
213 SDP_GET16(version, v);
220 if (control_psm == -1 || interrupt_psm == -1 ||
221 reconnect_initiate == -1 ||
222 hid_descriptor == NULL || hid_descriptor_length == -1)
223 hid_sdp_query_exit(ENOATTR);
224 hd->name = bt_devremote_name_gen(devname, &hd->bdaddr);
225 hd->vendor_id = vendor_id;
226 hd->product_id = product_id;
227 hd->version = version;
228 hd->control_psm = control_psm;
229 hd->interrupt_psm = interrupt_psm;
230 hd->reconnect_initiate = reconnect_initiate? 1 : 0;
231 hd->battery_power = battery_power? 1 : 0;
232 hd->normally_connectable = normally_connectable? 1 : 0;
233 hd->desc = hid_use_report_desc(hid_descriptor, hid_descriptor_length);
234 if (hd->desc == NULL)
235 hid_sdp_query_exit(ENOMEM);
250 hid_sdp_parse_protocol_descriptor_list(sdp_attr_p a)
252 uint8_t *ptr = a->value;
253 uint8_t *end = a->value + a->vlen;
254 int32_t type, len, uuid, psm;
259 if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) {
327 case SDP_DATA_UUID16:
328 SDP_GET16(uuid, ptr);
329 if (uuid != SDP_UUID_PROTOCOL_L2CAP)
333 case SDP_DATA_UUID32: /* XXX FIXME can we have 32-bit UUID */
334 case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
343 if (type != SDP_DATA_UINT16)
358 hid_sdp_parse_hid_descriptor(sdp_attr_p a)
360 uint8_t *ptr = a->value;
361 uint8_t *end = a->value + a->vlen;
362 int32_t type, len, descriptor_type;
417 if (type != SDP_DATA_UINT8 || ptr + 1 > end)
419 SDP_GET8(descriptor_type, ptr);
421 /* Descriptor value */
450 if (descriptor_type == UDESC_REPORT && len > 0) {
465 hid_sdp_parse_boolean(sdp_attr_p a)
467 if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL)
470 return (a->value[1]);
473 /* Perform SDP query */
475 hid_query(bdaddr_t *bdaddr, int argc, char **argv)
477 struct hid_device hd;
480 memcpy(&hd.bdaddr, bdaddr, sizeof(hd.bdaddr));
481 if (hid_sdp_query(NULL, &hd, &e) < 0) {
482 fprintf(stderr, "Could not perform SDP query on the " \
483 "device %s. %s (%d)\n", bt_ntoa(bdaddr, NULL),
488 print_hid_device(&hd, stdout);
493 struct bthid_command sdp_commands[] =
497 "Perform SDP query to the specified device and print HID configuration entry\n"\
498 "for the device. The configuration entry should be appended to the Bluetooth\n"\
499 "HID daemon configuration file and the daemon should be restarted.\n",