11556Srgrimes/*-
21556Srgrimes * sdp.c
31556Srgrimes *
41556Srgrimes * SPDX-License-Identifier: BSD-2-Clause
51556Srgrimes *
61556Srgrimes * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
71556Srgrimes * All rights reserved.
81556Srgrimes *
91556Srgrimes * Redistribution and use in source and binary forms, with or without
101556Srgrimes * modification, are permitted provided that the following conditions
111556Srgrimes * are met:
121556Srgrimes * 1. Redistributions of source code must retain the above copyright
131556Srgrimes *    notice, this list of conditions and the following disclaimer.
141556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
151556Srgrimes *    notice, this list of conditions and the following disclaimer in the
161556Srgrimes *    documentation and/or other materials provided with the distribution.
171556Srgrimes *
181556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
191556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
201556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
211556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
221556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
231556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
241556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
251556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
261556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
271556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
281556Srgrimes * SUCH DAMAGE.
291556Srgrimes *
301556Srgrimes * $Id: sdp.c,v 1.3 2004/02/17 22:14:57 max Exp $
311556Srgrimes */
321556Srgrimes
331556Srgrimes#include <sys/param.h>
341556Srgrimes#include <sys/queue.h>
351556Srgrimes#include <sys/sysctl.h>
361556Srgrimes#define L2CAP_SOCKET_CHECKED
3717987Speter#include <bluetooth.h>
3850471Speter#include <dev/usb/usb.h>
391556Srgrimes#include <dev/usb/usbhid.h>
401556Srgrimes#include <errno.h>
411556Srgrimes#include <sdp.h>
421556Srgrimes#include <stdio.h>
431556Srgrimes#include <string.h>
441556Srgrimes#include <usbhid.h>
4517987Speter#include "bthid_config.h"
4617987Speter#include "bthidcontrol.h"
4717987Speter
4817987Speterstatic int32_t hid_sdp_query				(bdaddr_t const *local, struct hid_device *hd, int32_t *error);
497502Sphkstatic int32_t hid_sdp_parse_protocol_descriptor_list	(sdp_attr_p a);
5017987Speterstatic int32_t hid_sdp_parse_hid_descriptor		(sdp_attr_p a);
5117987Speterstatic int32_t hid_sdp_parse_boolean			(sdp_attr_p a);
521556Srgrimes
531556Srgrimes/*
541556Srgrimes * Hard coded attribute IDs taken from the
551556Srgrimes * DEVICE IDENTIFICATION PROFILE SPECIFICATION V13 p.12
561556Srgrimes */
571556Srgrimes
581556Srgrimes#define SDP_ATTR_DEVICE_ID_SERVICE_VENDORID  0x0201
591556Srgrimes#define SDP_ATTR_DEVICE_ID_SERVICE_PRODUCTID 0x0202
601556Srgrimes#define SDP_ATTR_DEVICE_ID_SERVICE_VERSION   0x0203
6117987Speter#define SDP_ATTR_DEVICE_ID_RANGE SDP_ATTR_RANGE( \
6217987Speter SDP_ATTR_DEVICE_ID_SERVICE_VENDORID, SDP_ATTR_DEVICE_ID_SERVICE_VERSION )
631556Srgrimes
641556Srgrimesstatic uint16_t		service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE;
651556Srgrimesstatic uint16_t		service_devid = SDP_SERVICE_CLASS_PNP_INFORMATION;
661556Srgrimesstatic uint32_t 	attrs_devid   = SDP_ATTR_DEVICE_ID_RANGE;
671556Srgrimes
681556Srgrimesstatic uint32_t		attrs[] = {
691556SrgrimesSDP_ATTR_RANGE(	SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
701556Srgrimes		SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
7117987SpeterSDP_ATTR_RANGE	(SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS,
721556Srgrimes		SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS),
7317987SpeterSDP_ATTR_RANGE(	0x0205,		/* HIDReconnectInitiate */
741556Srgrimes		0x0205),
751556SrgrimesSDP_ATTR_RANGE(	0x0206,		/* HIDDescriptorList */
7617987Speter		0x0206),
771556SrgrimesSDP_ATTR_RANGE(	0x0209,		/* HIDBatteryPower */
781556Srgrimes		0x0209),
791556SrgrimesSDP_ATTR_RANGE(	0x020d,		/* HIDNormallyConnectable */
801556Srgrimes		0x020d)
811556Srgrimes	};
821556Srgrimes#define	nattrs	nitems(attrs)
831556Srgrimes
841556Srgrimesstatic sdp_attr_t	values[8];
851556Srgrimes#define	nvalues	nitems(values)
861556Srgrimes
871556Srgrimesstatic uint8_t		buffer[nvalues][512];
881556Srgrimes
891556Srgrimes/*
901556Srgrimes * Query remote device
911556Srgrimes */
921556Srgrimes
931556Srgrimes#undef	hid_sdp_query_exit
941556Srgrimes#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