1/*
2 * sdp.c
3 *
4 * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * $Id: sdp.c,v 1.3 2004/02/17 22:14:57 max Exp $
29 * $FreeBSD$
30 */
31
32#include <sys/queue.h>
33#include <bluetooth.h>
34#include <dev/usb/usb.h>
35#include <dev/usb/usbhid.h>
36#include <errno.h>
37#include <sdp.h>
38#include <stdio.h>
39#include <string.h>
40#include <usbhid.h>
41#include "bthid_config.h"
42#include "bthidcontrol.h"
43
44static int32_t hid_sdp_query				(bdaddr_t const *local, struct hid_device *hd, int32_t *error);
45static int32_t hid_sdp_parse_protocol_descriptor_list	(sdp_attr_p a);
46static int32_t hid_sdp_parse_hid_descriptor		(sdp_attr_p a);
47static int32_t hid_sdp_parse_boolean			(sdp_attr_p a);
48
49static uint16_t		service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE;
50
51static uint32_t		attrs[] = {
52SDP_ATTR_RANGE(	SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
53		SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
54SDP_ATTR_RANGE	(SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS,
55		SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS),
56SDP_ATTR_RANGE(	0x0205,		/* HIDReconnectInitiate */
57		0x0205),
58SDP_ATTR_RANGE(	0x0206,		/* HIDDescriptorList */
59		0x0206),
60SDP_ATTR_RANGE(	0x0209,		/* HIDBatteryPower */
61		0x0209),
62SDP_ATTR_RANGE(	0x020d,		/* HIDNormallyConnectable */
63		0x020d)
64	};
65#define	nattrs	(sizeof(attrs)/sizeof(attrs[0]))
66
67static sdp_attr_t	values[8];
68#define	nvalues	(sizeof(values)/sizeof(values[0]))
69
70static uint8_t		buffer[nvalues][512];
71
72/*
73 * Query remote device
74 */
75
76#undef	hid_sdp_query_exit
77#define	hid_sdp_query_exit(e) {		\
78	if (error != NULL)		\
79		*error = (e);		\
80	if (ss != NULL) {		\
81		sdp_close(ss);		\
82		ss = NULL;		\
83	}				\
84	return (((e) == 0)? 0 : -1);	\
85}
86
87static int32_t
88hid_sdp_query(bdaddr_t const *local, struct hid_device *hd, int32_t *error)
89{
90	void	*ss = NULL;
91	uint8_t	*hid_descriptor = NULL;
92	int32_t	 i, control_psm = -1, interrupt_psm = -1,
93		 reconnect_initiate = -1,
94		 normally_connectable = 0, battery_power = 0,
95		 hid_descriptor_length = -1;
96
97	if (local == NULL)
98		local = NG_HCI_BDADDR_ANY;
99	if (hd == NULL)
100		hid_sdp_query_exit(EINVAL);
101
102	for (i = 0; i < nvalues; i ++) {
103		values[i].flags = SDP_ATTR_INVALID;
104		values[i].attr = 0;
105		values[i].vlen = sizeof(buffer[i]);
106		values[i].value = buffer[i];
107	}
108
109	if ((ss = sdp_open(local, &hd->bdaddr)) == NULL)
110		hid_sdp_query_exit(ENOMEM);
111	if (sdp_error(ss) != 0)
112		hid_sdp_query_exit(sdp_error(ss));
113	if (sdp_search(ss, 1, &service, nattrs, attrs, nvalues, values) != 0)
114                hid_sdp_query_exit(sdp_error(ss));
115
116        sdp_close(ss);
117        ss = NULL;
118
119	for (i = 0; i < nvalues; i ++) {
120		if (values[i].flags != SDP_ATTR_OK)
121			continue;
122
123		switch (values[i].attr) {
124		case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
125			control_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
126			break;
127
128		case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS:
129			interrupt_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
130			break;
131
132		case 0x0205: /* HIDReconnectInitiate */
133			reconnect_initiate = hid_sdp_parse_boolean(&values[i]);
134			break;
135
136		case 0x0206: /* HIDDescriptorList */
137			if (hid_sdp_parse_hid_descriptor(&values[i]) == 0) {
138				hid_descriptor = values[i].value;
139				hid_descriptor_length = values[i].vlen;
140			}
141			break;
142
143		case 0x0209: /* HIDBatteryPower */
144			battery_power = hid_sdp_parse_boolean(&values[i]);
145			break;
146
147		case 0x020d: /* HIDNormallyConnectable */
148			normally_connectable = hid_sdp_parse_boolean(&values[i]);
149			break;
150		}
151	}
152
153	if (control_psm == -1 || interrupt_psm == -1 ||
154	    reconnect_initiate == -1 ||
155	    hid_descriptor == NULL || hid_descriptor_length == -1)
156		hid_sdp_query_exit(ENOATTR);
157
158	hd->control_psm = control_psm;
159	hd->interrupt_psm = interrupt_psm;
160	hd->reconnect_initiate = reconnect_initiate? 1 : 0;
161	hd->battery_power = battery_power? 1 : 0;
162	hd->normally_connectable = normally_connectable? 1 : 0;
163	hd->desc = hid_use_report_desc(hid_descriptor, hid_descriptor_length);
164	if (hd->desc == NULL)
165		hid_sdp_query_exit(ENOMEM);
166
167	return (0);
168}
169
170/*
171 * seq len				2
172 *	seq len				2
173 *		uuid value		3
174 *		uint16 value		3
175 *		seq len			2
176 *			uuid value	3
177 */
178
179static int32_t
180hid_sdp_parse_protocol_descriptor_list(sdp_attr_p a)
181{
182	uint8_t	*ptr = a->value;
183	uint8_t	*end = a->value + a->vlen;
184	int32_t	 type, len, uuid, psm;
185
186	if (end - ptr < 15)
187		return (-1);
188
189	if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) {
190		SDP_GET8(type, ptr);
191		switch (type) {
192		case SDP_DATA_SEQ8:
193			SDP_GET8(len, ptr);
194			break;
195
196		case SDP_DATA_SEQ16:
197			SDP_GET16(len, ptr);
198			break;
199
200		case SDP_DATA_SEQ32:
201			SDP_GET32(len, ptr);
202			break;
203
204		default:
205			return (-1);
206		}
207		if (ptr + len > end)
208			return (-1);
209	}
210
211	SDP_GET8(type, ptr);
212	switch (type) {
213	case SDP_DATA_SEQ8:
214		SDP_GET8(len, ptr);
215		break;
216
217	case SDP_DATA_SEQ16:
218		SDP_GET16(len, ptr);
219		break;
220
221	case SDP_DATA_SEQ32:
222		SDP_GET32(len, ptr);
223		break;
224
225	default:
226		return (-1);
227	}
228	if (ptr + len > end)
229		return (-1);
230
231	/* Protocol */
232	SDP_GET8(type, ptr);
233	switch (type) {
234	case SDP_DATA_SEQ8:
235		SDP_GET8(len, ptr);
236		break;
237
238	case SDP_DATA_SEQ16:
239		SDP_GET16(len, ptr);
240		break;
241
242	case SDP_DATA_SEQ32:
243		SDP_GET32(len, ptr);
244		break;
245
246	default:
247		return (-1);
248	}
249	if (ptr + len > end)
250		return (-1);
251
252	/* UUID */
253	if (ptr + 3 > end)
254		return (-1);
255	SDP_GET8(type, ptr);
256	switch (type) {
257	case SDP_DATA_UUID16:
258		SDP_GET16(uuid, ptr);
259		if (uuid != SDP_UUID_PROTOCOL_L2CAP)
260			return (-1);
261		break;
262
263	case SDP_DATA_UUID32:  /* XXX FIXME can we have 32-bit UUID */
264	case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
265	default:
266		return (-1);
267	}
268
269	/* PSM */
270	if (ptr + 3 > end)
271		return (-1);
272	SDP_GET8(type, ptr);
273	if (type != SDP_DATA_UINT16)
274		return (-1);
275	SDP_GET16(psm, ptr);
276
277	return (psm);
278}
279
280/*
281 * seq len			2
282 *	seq len			2
283 *		uint8 value8	2
284 * 		str value	3
285 */
286
287static int32_t
288hid_sdp_parse_hid_descriptor(sdp_attr_p a)
289{
290	uint8_t	*ptr = a->value;
291	uint8_t	*end = a->value + a->vlen;
292	int32_t	 type, len, descriptor_type;
293
294	if (end - ptr < 9)
295		return (-1);
296
297	SDP_GET8(type, ptr);
298	switch (type) {
299	case SDP_DATA_SEQ8:
300		SDP_GET8(len, ptr);
301		break;
302
303	case SDP_DATA_SEQ16:
304		SDP_GET16(len, ptr);
305		break;
306
307	case SDP_DATA_SEQ32:
308		SDP_GET32(len, ptr);
309		break;
310
311	default:
312		return (-1);
313	}
314	if (ptr + len > end)
315		return (-1);
316
317	while (ptr < end) {
318		/* Descriptor */
319		SDP_GET8(type, ptr);
320		switch (type) {
321		case SDP_DATA_SEQ8:
322			if (ptr + 1 > end)
323				return (-1);
324			SDP_GET8(len, ptr);
325			break;
326
327		case SDP_DATA_SEQ16:
328			if (ptr + 2 > end)
329				return (-1);
330			SDP_GET16(len, ptr);
331			break;
332
333		case SDP_DATA_SEQ32:
334			if (ptr + 4 > end)
335				return (-1);
336			SDP_GET32(len, ptr);
337			break;
338
339		default:
340			return (-1);
341		}
342
343		/* Descripor type */
344		if (ptr + 1 > end)
345			return (-1);
346		SDP_GET8(type, ptr);
347		if (type != SDP_DATA_UINT8 || ptr + 1 > end)
348			return (-1);
349		SDP_GET8(descriptor_type, ptr);
350
351		/* Descriptor value */
352		if (ptr + 1 > end)
353			return (-1);
354		SDP_GET8(type, ptr);
355		switch (type) {
356		case SDP_DATA_STR8:
357			if (ptr + 1 > end)
358				return (-1);
359			SDP_GET8(len, ptr);
360			break;
361
362		case SDP_DATA_STR16:
363			if (ptr + 2 > end)
364				return (-1);
365			SDP_GET16(len, ptr);
366			break;
367
368		case SDP_DATA_STR32:
369			if (ptr + 4 > end)
370				return (-1);
371			SDP_GET32(len, ptr);
372			break;
373
374		default:
375			return (-1);
376		}
377		if (ptr + len > end)
378			return (-1);
379
380		if (descriptor_type == UDESC_REPORT && len > 0) {
381			a->value = ptr;
382			a->vlen = len;
383
384			return (0);
385		}
386
387		ptr += len;
388	}
389
390	return (-1);
391}
392
393/* bool8 int8 */
394static int32_t
395hid_sdp_parse_boolean(sdp_attr_p a)
396{
397	if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL)
398		return (-1);
399
400	return (a->value[1]);
401}
402
403/* Perform SDP query */
404static int32_t
405hid_query(bdaddr_t *bdaddr, int argc, char **argv)
406{
407	struct hid_device	hd;
408	int			e;
409
410	memcpy(&hd.bdaddr, bdaddr, sizeof(hd.bdaddr));
411	if (hid_sdp_query(NULL, &hd, &e) < 0) {
412		fprintf(stderr, "Could not perform SDP query on the " \
413			"device %s. %s (%d)\n", bt_ntoa(bdaddr, NULL),
414			strerror(e), e);
415		return (FAILED);
416	}
417
418	print_hid_device(&hd, stdout);
419
420	return (OK);
421}
422
423struct bthid_command	sdp_commands[] =
424{
425{
426"Query",
427"Perform SDP query to the specified device and print HID configuration entry\n"\
428"for the device. The configuration entry should be appended to the Bluetooth\n"\
429"HID daemon configuration file and the daemon should be restarted.\n",
430hid_query
431},
432{ NULL, NULL, NULL }
433};
434
435