1/*-
2 * sdp.c
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 *
6 * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
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.
17 *
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
28 * SUCH DAMAGE.
29 *
30 * $Id: sdp.c,v 1.3 2004/02/17 22:14:57 max Exp $
31 */
32
33#include <sys/param.h>
34#include <sys/queue.h>
35#include <sys/sysctl.h>
36#define L2CAP_SOCKET_CHECKED
37#include <bluetooth.h>
38#include <dev/usb/usb.h>
39#include <dev/usb/usbhid.h>
40#include <errno.h>
41#include <sdp.h>
42#include <stdio.h>
43#include <string.h>
44#include <usbhid.h>
45#include "bthid_config.h"
46#include "bthidcontrol.h"
47
48static int32_t hid_sdp_query				(bdaddr_t const *local, struct hid_device *hd, int32_t *error);
49static int32_t hid_sdp_parse_protocol_descriptor_list	(sdp_attr_p a);
50static int32_t hid_sdp_parse_hid_descriptor		(sdp_attr_p a);
51static int32_t hid_sdp_parse_boolean			(sdp_attr_p a);
52
53/*
54 * Hard coded attribute IDs taken from the
55 * DEVICE IDENTIFICATION PROFILE SPECIFICATION V13 p.12
56 */
57
58#define SDP_ATTR_DEVICE_ID_SERVICE_VENDORID  0x0201
59#define SDP_ATTR_DEVICE_ID_SERVICE_PRODUCTID 0x0202
60#define SDP_ATTR_DEVICE_ID_SERVICE_VERSION   0x0203
61#define SDP_ATTR_DEVICE_ID_RANGE SDP_ATTR_RANGE( \
62 SDP_ATTR_DEVICE_ID_SERVICE_VENDORID, SDP_ATTR_DEVICE_ID_SERVICE_VERSION )
63
64static uint16_t		service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE;
65static uint16_t		service_devid = SDP_SERVICE_CLASS_PNP_INFORMATION;
66static uint32_t 	attrs_devid   = SDP_ATTR_DEVICE_ID_RANGE;
67
68static uint32_t		attrs[] = {
69SDP_ATTR_RANGE(	SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
70		SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
71SDP_ATTR_RANGE	(SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS,
72		SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS),
73SDP_ATTR_RANGE(	0x0205,		/* HIDReconnectInitiate */
74		0x0205),
75SDP_ATTR_RANGE(	0x0206,		/* HIDDescriptorList */
76		0x0206),
77SDP_ATTR_RANGE(	0x0209,		/* HIDBatteryPower */
78		0x0209),
79SDP_ATTR_RANGE(	0x020d,		/* HIDNormallyConnectable */
80		0x020d)
81	};
82#define	nattrs	nitems(attrs)
83
84static sdp_attr_t	values[8];
85#define	nvalues	nitems(values)
86
87static uint8_t		buffer[nvalues][512];
88
89/*
90 * Query remote device
91 */
92
93#undef	hid_sdp_query_exit
94#define	hid_sdp_query_exit(e) {		\
95	if (error != NULL)		\
96		*error = (e);		\
97	if (ss != NULL) {		\
98		sdp_close(ss);		\
99		ss = NULL;		\
100	}				\
101	return (((e) == 0)? 0 : -1);	\
102}
103
104static void
105hid_init_return_values() {
106	int i;
107	for (i = 0; i < nvalues; i ++) {
108		values[i].flags = SDP_ATTR_INVALID;
109		values[i].attr = 0;
110		values[i].vlen = sizeof(buffer[i]);
111		values[i].value = buffer[i];
112	}
113}
114
115static int32_t
116hid_sdp_query(bdaddr_t const *local, struct hid_device *hd, int32_t *error)
117{
118	void			*ss = NULL;
119	uint8_t			*hid_descriptor = NULL, *v;
120	int32_t			 i, control_psm = -1, interrupt_psm = -1,
121				 reconnect_initiate = -1,
122				 normally_connectable = 0, battery_power = 0,
123				 hid_descriptor_length = -1, type;
124	int16_t 		 vendor_id = 0, product_id = 0, version = 0;
125	bdaddr_t		 sdp_local;
126	char			 devname[HCI_DEVNAME_SIZE];
127
128	if (local == NULL)
129		local = NG_HCI_BDADDR_ANY;
130	if (hd == NULL)
131		hid_sdp_query_exit(EINVAL);
132
133	hid_init_return_values();
134
135	if ((ss = sdp_open(local, &hd->bdaddr)) == NULL)
136		hid_sdp_query_exit(ENOMEM);
137	if (sdp_error(ss) != 0)
138		hid_sdp_query_exit(sdp_error(ss));
139	if (sdp_search(ss, 1, &service, nattrs, attrs, nvalues, values) != 0)
140                hid_sdp_query_exit(sdp_error(ss));
141
142	for (i = 0; i < nvalues; i ++) {
143		if (values[i].flags != SDP_ATTR_OK)
144			continue;
145
146		switch (values[i].attr) {
147		case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
148			control_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
149			break;
150
151		case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS:
152			interrupt_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
153			break;
154
155		case 0x0205: /* HIDReconnectInitiate */
156			reconnect_initiate = hid_sdp_parse_boolean(&values[i]);
157			break;
158
159		case 0x0206: /* HIDDescriptorList */
160			if (hid_sdp_parse_hid_descriptor(&values[i]) == 0) {
161				hid_descriptor = values[i].value;
162				hid_descriptor_length = values[i].vlen;
163			}
164			break;
165
166		case 0x0209: /* HIDBatteryPower */
167			battery_power = hid_sdp_parse_boolean(&values[i]);
168			break;
169
170		case 0x020d: /* HIDNormallyConnectable */
171			normally_connectable = hid_sdp_parse_boolean(&values[i]);
172			break;
173		}
174	}
175
176	hid_init_return_values();
177
178	if (sdp_search(ss, 1, &service_devid, 1, &attrs_devid, nvalues, values) != 0)
179                hid_sdp_query_exit(sdp_error(ss));
180
181	/* Try extract HCI bdaddr from opened SDP session */
182	if (sdp_get_lcaddr(ss, &sdp_local) != 0 ||
183	    bt_devname(devname, &sdp_local) == 0)
184		hid_sdp_query_exit(ENOATTR);
185
186        sdp_close(ss);
187        ss = NULL;
188
189	/* If search is successful, scan through return vals */
190	for (i = 0; i < 3; i ++ ) {
191		if (values[i].flags == SDP_ATTR_INVALID )
192			continue;
193
194		/* Expecting tag + uint16_t on all 3 attributes */
195		if (values[i].vlen != 3)
196			continue;
197
198		/* Make sure, we're reading a uint16_t */
199		v = values[i].value;
200		SDP_GET8(type, v);
201		if (type != SDP_DATA_UINT16 )
202			continue;
203
204		switch (values[i].attr) {
205			case SDP_ATTR_DEVICE_ID_SERVICE_VENDORID:
206				SDP_GET16(vendor_id, v);
207				break;
208			case SDP_ATTR_DEVICE_ID_SERVICE_PRODUCTID:
209				SDP_GET16(product_id, v);
210				break;
211			case SDP_ATTR_DEVICE_ID_SERVICE_VERSION:
212				SDP_GET16(version, v);
213				break;
214			default:
215				break;
216		}
217	}
218
219	if (control_psm == -1 || interrupt_psm == -1 ||
220	    reconnect_initiate == -1 ||
221	    hid_descriptor == NULL || hid_descriptor_length == -1)
222		hid_sdp_query_exit(ENOATTR);
223	hd->name = bt_devremote_name_gen(devname, &hd->bdaddr);
224	hd->vendor_id = vendor_id;
225	hd->product_id = product_id;
226	hd->version = version;
227	hd->control_psm = control_psm;
228	hd->interrupt_psm = interrupt_psm;
229	hd->reconnect_initiate = reconnect_initiate? 1 : 0;
230	hd->battery_power = battery_power? 1 : 0;
231	hd->normally_connectable = normally_connectable? 1 : 0;
232	hd->desc = hid_use_report_desc(hid_descriptor, hid_descriptor_length);
233	if (hd->desc == NULL)
234		hid_sdp_query_exit(ENOMEM);
235
236	return (0);
237}
238
239/*
240 * seq len				2
241 *	seq len				2
242 *		uuid value		3
243 *		uint16 value		3
244 *		seq len			2
245 *			uuid value	3
246 */
247
248static int32_t
249hid_sdp_parse_protocol_descriptor_list(sdp_attr_p a)
250{
251	uint8_t	*ptr = a->value;
252	uint8_t	*end = a->value + a->vlen;
253	int32_t	 type, len, uuid, psm;
254
255	if (end - ptr < 15)
256		return (-1);
257
258	if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) {
259		SDP_GET8(type, ptr);
260		switch (type) {
261		case SDP_DATA_SEQ8:
262			SDP_GET8(len, ptr);
263			break;
264
265		case SDP_DATA_SEQ16:
266			SDP_GET16(len, ptr);
267			break;
268
269		case SDP_DATA_SEQ32:
270			SDP_GET32(len, ptr);
271			break;
272
273		default:
274			return (-1);
275		}
276		if (ptr + len > end)
277			return (-1);
278	}
279
280	SDP_GET8(type, ptr);
281	switch (type) {
282	case SDP_DATA_SEQ8:
283		SDP_GET8(len, ptr);
284		break;
285
286	case SDP_DATA_SEQ16:
287		SDP_GET16(len, ptr);
288		break;
289
290	case SDP_DATA_SEQ32:
291		SDP_GET32(len, ptr);
292		break;
293
294	default:
295		return (-1);
296	}
297	if (ptr + len > end)
298		return (-1);
299
300	/* Protocol */
301	SDP_GET8(type, ptr);
302	switch (type) {
303	case SDP_DATA_SEQ8:
304		SDP_GET8(len, ptr);
305		break;
306
307	case SDP_DATA_SEQ16:
308		SDP_GET16(len, ptr);
309		break;
310
311	case SDP_DATA_SEQ32:
312		SDP_GET32(len, ptr);
313		break;
314
315	default:
316		return (-1);
317	}
318	if (ptr + len > end)
319		return (-1);
320
321	/* UUID */
322	if (ptr + 3 > end)
323		return (-1);
324	SDP_GET8(type, ptr);
325	switch (type) {
326	case SDP_DATA_UUID16:
327		SDP_GET16(uuid, ptr);
328		if (uuid != SDP_UUID_PROTOCOL_L2CAP)
329			return (-1);
330		break;
331
332	case SDP_DATA_UUID32:  /* XXX FIXME can we have 32-bit UUID */
333	case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
334	default:
335		return (-1);
336	}
337
338	/* PSM */
339	if (ptr + 3 > end)
340		return (-1);
341	SDP_GET8(type, ptr);
342	if (type != SDP_DATA_UINT16)
343		return (-1);
344	SDP_GET16(psm, ptr);
345
346	return (psm);
347}
348
349/*
350 * seq len			2
351 *	seq len			2
352 *		uint8 value8	2
353 * 		str value	3
354 */
355
356static int32_t
357hid_sdp_parse_hid_descriptor(sdp_attr_p a)
358{
359	uint8_t	*ptr = a->value;
360	uint8_t	*end = a->value + a->vlen;
361	int32_t	 type, len, descriptor_type;
362
363	if (end - ptr < 9)
364		return (-1);
365
366	SDP_GET8(type, ptr);
367	switch (type) {
368	case SDP_DATA_SEQ8:
369		SDP_GET8(len, ptr);
370		break;
371
372	case SDP_DATA_SEQ16:
373		SDP_GET16(len, ptr);
374		break;
375
376	case SDP_DATA_SEQ32:
377		SDP_GET32(len, ptr);
378		break;
379
380	default:
381		return (-1);
382	}
383	if (ptr + len > end)
384		return (-1);
385
386	while (ptr < end) {
387		/* Descriptor */
388		SDP_GET8(type, ptr);
389		switch (type) {
390		case SDP_DATA_SEQ8:
391			if (ptr + 1 > end)
392				return (-1);
393			SDP_GET8(len, ptr);
394			break;
395
396		case SDP_DATA_SEQ16:
397			if (ptr + 2 > end)
398				return (-1);
399			SDP_GET16(len, ptr);
400			break;
401
402		case SDP_DATA_SEQ32:
403			if (ptr + 4 > end)
404				return (-1);
405			SDP_GET32(len, ptr);
406			break;
407
408		default:
409			return (-1);
410		}
411
412		/* Descripor type */
413		if (ptr + 1 > end)
414			return (-1);
415		SDP_GET8(type, ptr);
416		if (type != SDP_DATA_UINT8 || ptr + 1 > end)
417			return (-1);
418		SDP_GET8(descriptor_type, ptr);
419
420		/* Descriptor value */
421		if (ptr + 1 > end)
422			return (-1);
423		SDP_GET8(type, ptr);
424		switch (type) {
425		case SDP_DATA_STR8:
426			if (ptr + 1 > end)
427				return (-1);
428			SDP_GET8(len, ptr);
429			break;
430
431		case SDP_DATA_STR16:
432			if (ptr + 2 > end)
433				return (-1);
434			SDP_GET16(len, ptr);
435			break;
436
437		case SDP_DATA_STR32:
438			if (ptr + 4 > end)
439				return (-1);
440			SDP_GET32(len, ptr);
441			break;
442
443		default:
444			return (-1);
445		}
446		if (ptr + len > end)
447			return (-1);
448
449		if (descriptor_type == UDESC_REPORT && len > 0) {
450			a->value = ptr;
451			a->vlen = len;
452
453			return (0);
454		}
455
456		ptr += len;
457	}
458
459	return (-1);
460}
461
462/* bool8 int8 */
463static int32_t
464hid_sdp_parse_boolean(sdp_attr_p a)
465{
466	if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL)
467		return (-1);
468
469	return (a->value[1]);
470}
471
472/* Perform SDP query */
473static int32_t
474hid_query(bdaddr_t *bdaddr, int argc, char **argv)
475{
476	struct hid_device	hd;
477	int			e;
478
479	memcpy(&hd.bdaddr, bdaddr, sizeof(hd.bdaddr));
480	if (hid_sdp_query(NULL, &hd, &e) < 0) {
481		fprintf(stderr, "Could not perform SDP query on the " \
482			"device %s. %s (%d)\n", bt_ntoa(bdaddr, NULL),
483			strerror(e), e);
484		return (FAILED);
485	}
486
487	print_hid_device(&hd, stdout);
488
489	return (OK);
490}
491
492struct bthid_command	sdp_commands[] =
493{
494{
495"Query",
496"Perform SDP query to the specified device and print HID configuration entry\n"\
497"for the device. The configuration entry should be appended to the Bluetooth\n"\
498"HID daemon configuration file and the daemon should be restarted.\n",
499hid_query
500},
501{ NULL, NULL, NULL }
502};
503
504