1/*-
2 * sdp.c
3 *
4 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 * $FreeBSD$
32 */
33
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>
41#include <errno.h>
42#include <sdp.h>
43#include <stdio.h>
44#include <string.h>
45#include <usbhid.h>
46#include "bthid_config.h"
47#include "bthidcontrol.h"
48
49static int32_t hid_sdp_query				(bdaddr_t const *local, struct hid_device *hd, int32_t *error);
50static int32_t hid_sdp_parse_protocol_descriptor_list	(sdp_attr_p a);
51static int32_t hid_sdp_parse_hid_descriptor		(sdp_attr_p a);
52static int32_t hid_sdp_parse_boolean			(sdp_attr_p a);
53
54/*
55 * Hard coded attibute IDs taken from the
56 * DEVICE IDENTIFICATION PROFILE SPECIFICATION V13 p.12
57 */
58
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 )
64
65static uint16_t		service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE;
66static uint16_t		service_devid = SDP_SERVICE_CLASS_PNP_INFORMATION;
67static uint32_t 	attrs_devid   = SDP_ATTR_DEVICE_ID_RANGE;
68
69static uint32_t		attrs[] = {
70SDP_ATTR_RANGE(	SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
71		SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
72SDP_ATTR_RANGE	(SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS,
73		SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS),
74SDP_ATTR_RANGE(	0x0205,		/* HIDReconnectInitiate */
75		0x0205),
76SDP_ATTR_RANGE(	0x0206,		/* HIDDescriptorList */
77		0x0206),
78SDP_ATTR_RANGE(	0x0209,		/* HIDBatteryPower */
79		0x0209),
80SDP_ATTR_RANGE(	0x020d,		/* HIDNormallyConnectable */
81		0x020d)
82	};
83#define	nattrs	(sizeof(attrs)/sizeof(attrs[0]))
84
85static sdp_attr_t	values[8];
86#define	nvalues	(sizeof(values)/sizeof(values[0]))
87
88static uint8_t		buffer[nvalues][512];
89
90/*
91 * Query remote device
92 */
93
94#undef	hid_sdp_query_exit
95#define	hid_sdp_query_exit(e) {		\
96	if (error != NULL)		\
97		*error = (e);		\
98	if (ss != NULL) {		\
99		sdp_close(ss);		\
100		ss = NULL;		\
101	}				\
102	return (((e) == 0)? 0 : -1);	\
103}
104
105static void
106hid_init_return_values() {
107	int i;
108	for (i = 0; i < nvalues; i ++) {
109		values[i].flags = SDP_ATTR_INVALID;
110		values[i].attr = 0;
111		values[i].vlen = sizeof(buffer[i]);
112		values[i].value = buffer[i];
113	}
114}
115
116static int32_t
117hid_sdp_query(bdaddr_t const *local, struct hid_device *hd, int32_t *error)
118{
119	void			*ss = NULL;
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;
126	bdaddr_t		 sdp_local;
127	char			 devname[HCI_DEVNAME_SIZE];
128
129	if (local == NULL)
130		local = NG_HCI_BDADDR_ANY;
131	if (hd == NULL)
132		hid_sdp_query_exit(EINVAL);
133
134	hid_init_return_values();
135
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));
142
143	for (i = 0; i < nvalues; i ++) {
144		if (values[i].flags != SDP_ATTR_OK)
145			continue;
146
147		switch (values[i].attr) {
148		case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
149			control_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
150			break;
151
152		case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS:
153			interrupt_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
154			break;
155
156		case 0x0205: /* HIDReconnectInitiate */
157			reconnect_initiate = hid_sdp_parse_boolean(&values[i]);
158			break;
159
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;
164			}
165			break;
166
167		case 0x0209: /* HIDBatteryPower */
168			battery_power = hid_sdp_parse_boolean(&values[i]);
169			break;
170
171		case 0x020d: /* HIDNormallyConnectable */
172			normally_connectable = hid_sdp_parse_boolean(&values[i]);
173			break;
174		}
175	}
176
177	hid_init_return_values();
178
179	if (sdp_search(ss, 1, &service_devid, 1, &attrs_devid, nvalues, values) != 0)
180                hid_sdp_query_exit(sdp_error(ss));
181
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);
186
187        sdp_close(ss);
188        ss = NULL;
189
190	/* If search is successful, scan through return vals */
191	for (i = 0; i < 3; i ++ ) {
192		if (values[i].flags == SDP_ATTR_INVALID )
193			continue;
194
195		/* Expecting tag + uint16_t on all 3 attributes */
196		if (values[i].vlen != 3)
197			continue;
198
199		/* Make sure, we're reading a uint16_t */
200		v = values[i].value;
201		SDP_GET8(type, v);
202		if (type != SDP_DATA_UINT16 )
203			continue;
204
205		switch (values[i].attr) {
206			case SDP_ATTR_DEVICE_ID_SERVICE_VENDORID:
207				SDP_GET16(vendor_id, v);
208				break;
209			case SDP_ATTR_DEVICE_ID_SERVICE_PRODUCTID:
210				SDP_GET16(product_id, v);
211				break;
212			case SDP_ATTR_DEVICE_ID_SERVICE_VERSION:
213				SDP_GET16(version, v);
214				break;
215			default:
216				break;
217		}
218	}
219
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);
236
237	return (0);
238}
239
240/*
241 * seq len				2
242 *	seq len				2
243 *		uuid value		3
244 *		uint16 value		3
245 *		seq len			2
246 *			uuid value	3
247 */
248
249static int32_t
250hid_sdp_parse_protocol_descriptor_list(sdp_attr_p a)
251{
252	uint8_t	*ptr = a->value;
253	uint8_t	*end = a->value + a->vlen;
254	int32_t	 type, len, uuid, psm;
255
256	if (end - ptr < 15)
257		return (-1);
258
259	if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) {
260		SDP_GET8(type, ptr);
261		switch (type) {
262		case SDP_DATA_SEQ8:
263			SDP_GET8(len, ptr);
264			break;
265
266		case SDP_DATA_SEQ16:
267			SDP_GET16(len, ptr);
268			break;
269
270		case SDP_DATA_SEQ32:
271			SDP_GET32(len, ptr);
272			break;
273
274		default:
275			return (-1);
276		}
277		if (ptr + len > end)
278			return (-1);
279	}
280
281	SDP_GET8(type, ptr);
282	switch (type) {
283	case SDP_DATA_SEQ8:
284		SDP_GET8(len, ptr);
285		break;
286
287	case SDP_DATA_SEQ16:
288		SDP_GET16(len, ptr);
289		break;
290
291	case SDP_DATA_SEQ32:
292		SDP_GET32(len, ptr);
293		break;
294
295	default:
296		return (-1);
297	}
298	if (ptr + len > end)
299		return (-1);
300
301	/* Protocol */
302	SDP_GET8(type, ptr);
303	switch (type) {
304	case SDP_DATA_SEQ8:
305		SDP_GET8(len, ptr);
306		break;
307
308	case SDP_DATA_SEQ16:
309		SDP_GET16(len, ptr);
310		break;
311
312	case SDP_DATA_SEQ32:
313		SDP_GET32(len, ptr);
314		break;
315
316	default:
317		return (-1);
318	}
319	if (ptr + len > end)
320		return (-1);
321
322	/* UUID */
323	if (ptr + 3 > end)
324		return (-1);
325	SDP_GET8(type, ptr);
326	switch (type) {
327	case SDP_DATA_UUID16:
328		SDP_GET16(uuid, ptr);
329		if (uuid != SDP_UUID_PROTOCOL_L2CAP)
330			return (-1);
331		break;
332
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 */
335	default:
336		return (-1);
337	}
338
339	/* PSM */
340	if (ptr + 3 > end)
341		return (-1);
342	SDP_GET8(type, ptr);
343	if (type != SDP_DATA_UINT16)
344		return (-1);
345	SDP_GET16(psm, ptr);
346
347	return (psm);
348}
349
350/*
351 * seq len			2
352 *	seq len			2
353 *		uint8 value8	2
354 * 		str value	3
355 */
356
357static int32_t
358hid_sdp_parse_hid_descriptor(sdp_attr_p a)
359{
360	uint8_t	*ptr = a->value;
361	uint8_t	*end = a->value + a->vlen;
362	int32_t	 type, len, descriptor_type;
363
364	if (end - ptr < 9)
365		return (-1);
366
367	SDP_GET8(type, ptr);
368	switch (type) {
369	case SDP_DATA_SEQ8:
370		SDP_GET8(len, ptr);
371		break;
372
373	case SDP_DATA_SEQ16:
374		SDP_GET16(len, ptr);
375		break;
376
377	case SDP_DATA_SEQ32:
378		SDP_GET32(len, ptr);
379		break;
380
381	default:
382		return (-1);
383	}
384	if (ptr + len > end)
385		return (-1);
386
387	while (ptr < end) {
388		/* Descriptor */
389		SDP_GET8(type, ptr);
390		switch (type) {
391		case SDP_DATA_SEQ8:
392			if (ptr + 1 > end)
393				return (-1);
394			SDP_GET8(len, ptr);
395			break;
396
397		case SDP_DATA_SEQ16:
398			if (ptr + 2 > end)
399				return (-1);
400			SDP_GET16(len, ptr);
401			break;
402
403		case SDP_DATA_SEQ32:
404			if (ptr + 4 > end)
405				return (-1);
406			SDP_GET32(len, ptr);
407			break;
408
409		default:
410			return (-1);
411		}
412
413		/* Descripor type */
414		if (ptr + 1 > end)
415			return (-1);
416		SDP_GET8(type, ptr);
417		if (type != SDP_DATA_UINT8 || ptr + 1 > end)
418			return (-1);
419		SDP_GET8(descriptor_type, ptr);
420
421		/* Descriptor value */
422		if (ptr + 1 > end)
423			return (-1);
424		SDP_GET8(type, ptr);
425		switch (type) {
426		case SDP_DATA_STR8:
427			if (ptr + 1 > end)
428				return (-1);
429			SDP_GET8(len, ptr);
430			break;
431
432		case SDP_DATA_STR16:
433			if (ptr + 2 > end)
434				return (-1);
435			SDP_GET16(len, ptr);
436			break;
437
438		case SDP_DATA_STR32:
439			if (ptr + 4 > end)
440				return (-1);
441			SDP_GET32(len, ptr);
442			break;
443
444		default:
445			return (-1);
446		}
447		if (ptr + len > end)
448			return (-1);
449
450		if (descriptor_type == UDESC_REPORT && len > 0) {
451			a->value = ptr;
452			a->vlen = len;
453
454			return (0);
455		}
456
457		ptr += len;
458	}
459
460	return (-1);
461}
462
463/* bool8 int8 */
464static int32_t
465hid_sdp_parse_boolean(sdp_attr_p a)
466{
467	if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL)
468		return (-1);
469
470	return (a->value[1]);
471}
472
473/* Perform SDP query */
474static int32_t
475hid_query(bdaddr_t *bdaddr, int argc, char **argv)
476{
477	struct hid_device	hd;
478	int			e;
479
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),
484			strerror(e), e);
485		return (FAILED);
486	}
487
488	print_hid_device(&hd, stdout);
489
490	return (OK);
491}
492
493struct bthid_command	sdp_commands[] =
494{
495{
496"Query",
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",
500hid_query
501},
502{ NULL, NULL, NULL }
503};
504
505