xref: /freebsd/usr.sbin/bluetooth/bthidcontrol/sdp.c (revision 6032284eb9cbdf7cf20bb465b2de3a3e5bfaab46)
16490c2ffSMaksim Yevmenkin /*
26490c2ffSMaksim Yevmenkin  * sdp.c
36490c2ffSMaksim Yevmenkin  *
46490c2ffSMaksim Yevmenkin  * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
56490c2ffSMaksim Yevmenkin  * All rights reserved.
66490c2ffSMaksim Yevmenkin  *
76490c2ffSMaksim Yevmenkin  * Redistribution and use in source and binary forms, with or without
86490c2ffSMaksim Yevmenkin  * modification, are permitted provided that the following conditions
96490c2ffSMaksim Yevmenkin  * are met:
106490c2ffSMaksim Yevmenkin  * 1. Redistributions of source code must retain the above copyright
116490c2ffSMaksim Yevmenkin  *    notice, this list of conditions and the following disclaimer.
126490c2ffSMaksim Yevmenkin  * 2. Redistributions in binary form must reproduce the above copyright
136490c2ffSMaksim Yevmenkin  *    notice, this list of conditions and the following disclaimer in the
146490c2ffSMaksim Yevmenkin  *    documentation and/or other materials provided with the distribution.
156490c2ffSMaksim Yevmenkin  *
166490c2ffSMaksim Yevmenkin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
176490c2ffSMaksim Yevmenkin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
186490c2ffSMaksim Yevmenkin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
196490c2ffSMaksim Yevmenkin  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
206490c2ffSMaksim Yevmenkin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
216490c2ffSMaksim Yevmenkin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
226490c2ffSMaksim Yevmenkin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
236490c2ffSMaksim Yevmenkin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
246490c2ffSMaksim Yevmenkin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
256490c2ffSMaksim Yevmenkin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
266490c2ffSMaksim Yevmenkin  * SUCH DAMAGE.
276490c2ffSMaksim Yevmenkin  *
286490c2ffSMaksim Yevmenkin  * $Id: sdp.c,v 1.3 2004/02/17 22:14:57 max Exp $
296490c2ffSMaksim Yevmenkin  * $FreeBSD$
306490c2ffSMaksim Yevmenkin  */
316490c2ffSMaksim Yevmenkin 
326490c2ffSMaksim Yevmenkin #include <sys/queue.h>
338d6f425dSTakanori Watanabe #define L2CAP_SOCKET_CHECKED
346490c2ffSMaksim Yevmenkin #include <bluetooth.h>
356490c2ffSMaksim Yevmenkin #include <dev/usb/usb.h>
366490c2ffSMaksim Yevmenkin #include <dev/usb/usbhid.h>
376490c2ffSMaksim Yevmenkin #include <errno.h>
386490c2ffSMaksim Yevmenkin #include <sdp.h>
396490c2ffSMaksim Yevmenkin #include <stdio.h>
406490c2ffSMaksim Yevmenkin #include <string.h>
416490c2ffSMaksim Yevmenkin #include <usbhid.h>
426490c2ffSMaksim Yevmenkin #include "bthid_config.h"
436490c2ffSMaksim Yevmenkin #include "bthidcontrol.h"
446490c2ffSMaksim Yevmenkin 
456490c2ffSMaksim Yevmenkin static int32_t hid_sdp_query				(bdaddr_t const *local, struct hid_device *hd, int32_t *error);
466490c2ffSMaksim Yevmenkin static int32_t hid_sdp_parse_protocol_descriptor_list	(sdp_attr_p a);
476490c2ffSMaksim Yevmenkin static int32_t hid_sdp_parse_hid_descriptor		(sdp_attr_p a);
486490c2ffSMaksim Yevmenkin static int32_t hid_sdp_parse_boolean			(sdp_attr_p a);
496490c2ffSMaksim Yevmenkin 
50*6032284eSVladimir Kondratyev /*
51*6032284eSVladimir Kondratyev  * Hard coded attibute IDs taken from the
52*6032284eSVladimir Kondratyev  * DEVICE IDENTIFICATION PROFILE SPECIFICATION V13 p.12
53*6032284eSVladimir Kondratyev  */
54*6032284eSVladimir Kondratyev 
55*6032284eSVladimir Kondratyev #define SDP_ATTR_DEVICE_ID_SERVICE_VENDORID  0x0201
56*6032284eSVladimir Kondratyev #define SDP_ATTR_DEVICE_ID_SERVICE_PRODUCTID 0x0202
57*6032284eSVladimir Kondratyev #define SDP_ATTR_DEVICE_ID_SERVICE_VERSION   0x0203
58*6032284eSVladimir Kondratyev #define SDP_ATTR_DEVICE_ID_RANGE SDP_ATTR_RANGE( \
59*6032284eSVladimir Kondratyev  SDP_ATTR_DEVICE_ID_SERVICE_VENDORID, SDP_ATTR_DEVICE_ID_SERVICE_VERSION )
60*6032284eSVladimir Kondratyev 
616490c2ffSMaksim Yevmenkin static uint16_t		service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE;
62*6032284eSVladimir Kondratyev static uint16_t		service_devid = SDP_SERVICE_CLASS_PNP_INFORMATION;
63*6032284eSVladimir Kondratyev static uint32_t 	attrs_devid   = SDP_ATTR_DEVICE_ID_RANGE;
646490c2ffSMaksim Yevmenkin 
656490c2ffSMaksim Yevmenkin static uint32_t		attrs[] = {
666490c2ffSMaksim Yevmenkin SDP_ATTR_RANGE(	SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
676490c2ffSMaksim Yevmenkin 		SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
686490c2ffSMaksim Yevmenkin SDP_ATTR_RANGE	(SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS,
696490c2ffSMaksim Yevmenkin 		SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS),
706490c2ffSMaksim Yevmenkin SDP_ATTR_RANGE(	0x0205,		/* HIDReconnectInitiate */
7133e98adeSMaksim Yevmenkin 		0x0205),
7233e98adeSMaksim Yevmenkin SDP_ATTR_RANGE(	0x0206,		/* HIDDescriptorList */
7333e98adeSMaksim Yevmenkin 		0x0206),
74595dedc1SMarkus Brueffer SDP_ATTR_RANGE(	0x0209,		/* HIDBatteryPower */
75595dedc1SMarkus Brueffer 		0x0209),
766490c2ffSMaksim Yevmenkin SDP_ATTR_RANGE(	0x020d,		/* HIDNormallyConnectable */
776490c2ffSMaksim Yevmenkin 		0x020d)
786490c2ffSMaksim Yevmenkin 	};
796490c2ffSMaksim Yevmenkin #define	nattrs	(sizeof(attrs)/sizeof(attrs[0]))
806490c2ffSMaksim Yevmenkin 
816490c2ffSMaksim Yevmenkin static sdp_attr_t	values[8];
826490c2ffSMaksim Yevmenkin #define	nvalues	(sizeof(values)/sizeof(values[0]))
836490c2ffSMaksim Yevmenkin 
846490c2ffSMaksim Yevmenkin static uint8_t		buffer[nvalues][512];
856490c2ffSMaksim Yevmenkin 
866490c2ffSMaksim Yevmenkin /*
876490c2ffSMaksim Yevmenkin  * Query remote device
886490c2ffSMaksim Yevmenkin  */
896490c2ffSMaksim Yevmenkin 
906490c2ffSMaksim Yevmenkin #undef	hid_sdp_query_exit
916490c2ffSMaksim Yevmenkin #define	hid_sdp_query_exit(e) {		\
926490c2ffSMaksim Yevmenkin 	if (error != NULL)		\
936490c2ffSMaksim Yevmenkin 		*error = (e);		\
946490c2ffSMaksim Yevmenkin 	if (ss != NULL) {		\
956490c2ffSMaksim Yevmenkin 		sdp_close(ss);		\
966490c2ffSMaksim Yevmenkin 		ss = NULL;		\
976490c2ffSMaksim Yevmenkin 	}				\
986490c2ffSMaksim Yevmenkin 	return (((e) == 0)? 0 : -1);	\
996490c2ffSMaksim Yevmenkin }
1006490c2ffSMaksim Yevmenkin 
101*6032284eSVladimir Kondratyev static void
102*6032284eSVladimir Kondratyev hid_init_return_values() {
103*6032284eSVladimir Kondratyev 	int i;
1046490c2ffSMaksim Yevmenkin 	for (i = 0; i < nvalues; i ++) {
1056490c2ffSMaksim Yevmenkin 		values[i].flags = SDP_ATTR_INVALID;
1066490c2ffSMaksim Yevmenkin 		values[i].attr = 0;
1076490c2ffSMaksim Yevmenkin 		values[i].vlen = sizeof(buffer[i]);
1086490c2ffSMaksim Yevmenkin 		values[i].value = buffer[i];
1096490c2ffSMaksim Yevmenkin 	}
110*6032284eSVladimir Kondratyev }
111*6032284eSVladimir Kondratyev 
112*6032284eSVladimir Kondratyev static int32_t
113*6032284eSVladimir Kondratyev hid_sdp_query(bdaddr_t const *local, struct hid_device *hd, int32_t *error)
114*6032284eSVladimir Kondratyev {
115*6032284eSVladimir Kondratyev 	void	*ss = NULL;
116*6032284eSVladimir Kondratyev 	uint8_t	*hid_descriptor = NULL, *v;
117*6032284eSVladimir Kondratyev 	int32_t	 i, control_psm = -1, interrupt_psm = -1,
118*6032284eSVladimir Kondratyev 		 reconnect_initiate = -1,
119*6032284eSVladimir Kondratyev 		 normally_connectable = 0, battery_power = 0,
120*6032284eSVladimir Kondratyev 		 hid_descriptor_length = -1, type;
121*6032284eSVladimir Kondratyev 	int16_t  vendor_id = 0, product_id = 0, version = 0;
122*6032284eSVladimir Kondratyev 
123*6032284eSVladimir Kondratyev 	if (local == NULL)
124*6032284eSVladimir Kondratyev 		local = NG_HCI_BDADDR_ANY;
125*6032284eSVladimir Kondratyev 	if (hd == NULL)
126*6032284eSVladimir Kondratyev 		hid_sdp_query_exit(EINVAL);
127*6032284eSVladimir Kondratyev 
128*6032284eSVladimir Kondratyev 	hid_init_return_values();
1296490c2ffSMaksim Yevmenkin 
1306490c2ffSMaksim Yevmenkin 	if ((ss = sdp_open(local, &hd->bdaddr)) == NULL)
1316490c2ffSMaksim Yevmenkin 		hid_sdp_query_exit(ENOMEM);
1326490c2ffSMaksim Yevmenkin 	if (sdp_error(ss) != 0)
1336490c2ffSMaksim Yevmenkin 		hid_sdp_query_exit(sdp_error(ss));
1346490c2ffSMaksim Yevmenkin 	if (sdp_search(ss, 1, &service, nattrs, attrs, nvalues, values) != 0)
1356490c2ffSMaksim Yevmenkin                 hid_sdp_query_exit(sdp_error(ss));
1366490c2ffSMaksim Yevmenkin 
1376490c2ffSMaksim Yevmenkin 	for (i = 0; i < nvalues; i ++) {
1386490c2ffSMaksim Yevmenkin 		if (values[i].flags != SDP_ATTR_OK)
1396490c2ffSMaksim Yevmenkin 			continue;
1406490c2ffSMaksim Yevmenkin 
1416490c2ffSMaksim Yevmenkin 		switch (values[i].attr) {
1426490c2ffSMaksim Yevmenkin 		case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
1436490c2ffSMaksim Yevmenkin 			control_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
1446490c2ffSMaksim Yevmenkin 			break;
1456490c2ffSMaksim Yevmenkin 
1466490c2ffSMaksim Yevmenkin 		case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS:
1476490c2ffSMaksim Yevmenkin 			interrupt_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
1486490c2ffSMaksim Yevmenkin 			break;
1496490c2ffSMaksim Yevmenkin 
1506490c2ffSMaksim Yevmenkin 		case 0x0205: /* HIDReconnectInitiate */
1516490c2ffSMaksim Yevmenkin 			reconnect_initiate = hid_sdp_parse_boolean(&values[i]);
1526490c2ffSMaksim Yevmenkin 			break;
1536490c2ffSMaksim Yevmenkin 
154595dedc1SMarkus Brueffer 		case 0x0206: /* HIDDescriptorList */
1556490c2ffSMaksim Yevmenkin 			if (hid_sdp_parse_hid_descriptor(&values[i]) == 0) {
1566490c2ffSMaksim Yevmenkin 				hid_descriptor = values[i].value;
1576490c2ffSMaksim Yevmenkin 				hid_descriptor_length = values[i].vlen;
1586490c2ffSMaksim Yevmenkin 			}
1596490c2ffSMaksim Yevmenkin 			break;
1606490c2ffSMaksim Yevmenkin 
161595dedc1SMarkus Brueffer 		case 0x0209: /* HIDBatteryPower */
1626490c2ffSMaksim Yevmenkin 			battery_power = hid_sdp_parse_boolean(&values[i]);
1636490c2ffSMaksim Yevmenkin 			break;
1646490c2ffSMaksim Yevmenkin 
1656490c2ffSMaksim Yevmenkin 		case 0x020d: /* HIDNormallyConnectable */
1666490c2ffSMaksim Yevmenkin 			normally_connectable = hid_sdp_parse_boolean(&values[i]);
1676490c2ffSMaksim Yevmenkin 			break;
1686490c2ffSMaksim Yevmenkin 		}
1696490c2ffSMaksim Yevmenkin 	}
1706490c2ffSMaksim Yevmenkin 
171*6032284eSVladimir Kondratyev 	hid_init_return_values();
172*6032284eSVladimir Kondratyev 
173*6032284eSVladimir Kondratyev 	if (sdp_search(ss, 1, &service_devid, 1, &attrs_devid, nvalues, values) != 0)
174*6032284eSVladimir Kondratyev                 hid_sdp_query_exit(sdp_error(ss));
175*6032284eSVladimir Kondratyev 
176*6032284eSVladimir Kondratyev         sdp_close(ss);
177*6032284eSVladimir Kondratyev         ss = NULL;
178*6032284eSVladimir Kondratyev 
179*6032284eSVladimir Kondratyev 	/* If search is successful, scan through return vals */
180*6032284eSVladimir Kondratyev 	for (i = 0; i < 3; i ++ ) {
181*6032284eSVladimir Kondratyev 		if (values[i].flags == SDP_ATTR_INVALID )
182*6032284eSVladimir Kondratyev 			continue;
183*6032284eSVladimir Kondratyev 
184*6032284eSVladimir Kondratyev 		/* Expecting tag + uint16_t on all 3 attributes */
185*6032284eSVladimir Kondratyev 		if (values[i].vlen != 3)
186*6032284eSVladimir Kondratyev 			continue;
187*6032284eSVladimir Kondratyev 
188*6032284eSVladimir Kondratyev 		/* Make sure, we're reading a uint16_t */
189*6032284eSVladimir Kondratyev 		v = values[i].value;
190*6032284eSVladimir Kondratyev 		SDP_GET8(type, v);
191*6032284eSVladimir Kondratyev 		if (type != SDP_DATA_UINT16 )
192*6032284eSVladimir Kondratyev 			continue;
193*6032284eSVladimir Kondratyev 
194*6032284eSVladimir Kondratyev 		switch (values[i].attr) {
195*6032284eSVladimir Kondratyev 			case SDP_ATTR_DEVICE_ID_SERVICE_VENDORID:
196*6032284eSVladimir Kondratyev 				SDP_GET16(vendor_id, v);
197*6032284eSVladimir Kondratyev 				break;
198*6032284eSVladimir Kondratyev 			case SDP_ATTR_DEVICE_ID_SERVICE_PRODUCTID:
199*6032284eSVladimir Kondratyev 				SDP_GET16(product_id, v);
200*6032284eSVladimir Kondratyev 				break;
201*6032284eSVladimir Kondratyev 			case SDP_ATTR_DEVICE_ID_SERVICE_VERSION:
202*6032284eSVladimir Kondratyev 				SDP_GET16(version, v);
203*6032284eSVladimir Kondratyev 				break;
204*6032284eSVladimir Kondratyev 			default:
205*6032284eSVladimir Kondratyev 				break;
206*6032284eSVladimir Kondratyev 		}
207*6032284eSVladimir Kondratyev 	}
208*6032284eSVladimir Kondratyev 
2096490c2ffSMaksim Yevmenkin 	if (control_psm == -1 || interrupt_psm == -1 ||
21033e98adeSMaksim Yevmenkin 	    reconnect_initiate == -1 ||
2116490c2ffSMaksim Yevmenkin 	    hid_descriptor == NULL || hid_descriptor_length == -1)
2126490c2ffSMaksim Yevmenkin 		hid_sdp_query_exit(ENOATTR);
213*6032284eSVladimir Kondratyev 	hd->vendor_id = vendor_id;
214*6032284eSVladimir Kondratyev 	hd->product_id = product_id;
215*6032284eSVladimir Kondratyev 	hd->version = version;
2166490c2ffSMaksim Yevmenkin 	hd->control_psm = control_psm;
2176490c2ffSMaksim Yevmenkin 	hd->interrupt_psm = interrupt_psm;
2186490c2ffSMaksim Yevmenkin 	hd->reconnect_initiate = reconnect_initiate? 1 : 0;
2196490c2ffSMaksim Yevmenkin 	hd->battery_power = battery_power? 1 : 0;
2206490c2ffSMaksim Yevmenkin 	hd->normally_connectable = normally_connectable? 1 : 0;
2216490c2ffSMaksim Yevmenkin 	hd->desc = hid_use_report_desc(hid_descriptor, hid_descriptor_length);
2226490c2ffSMaksim Yevmenkin 	if (hd->desc == NULL)
2236490c2ffSMaksim Yevmenkin 		hid_sdp_query_exit(ENOMEM);
2246490c2ffSMaksim Yevmenkin 
2256490c2ffSMaksim Yevmenkin 	return (0);
2266490c2ffSMaksim Yevmenkin }
2276490c2ffSMaksim Yevmenkin 
2286490c2ffSMaksim Yevmenkin /*
2296490c2ffSMaksim Yevmenkin  * seq len				2
2306490c2ffSMaksim Yevmenkin  *	seq len				2
2316490c2ffSMaksim Yevmenkin  *		uuid value		3
2326490c2ffSMaksim Yevmenkin  *		uint16 value		3
2336490c2ffSMaksim Yevmenkin  *		seq len			2
2346490c2ffSMaksim Yevmenkin  *			uuid value	3
2356490c2ffSMaksim Yevmenkin  */
2366490c2ffSMaksim Yevmenkin 
2376490c2ffSMaksim Yevmenkin static int32_t
2386490c2ffSMaksim Yevmenkin hid_sdp_parse_protocol_descriptor_list(sdp_attr_p a)
2396490c2ffSMaksim Yevmenkin {
2406490c2ffSMaksim Yevmenkin 	uint8_t	*ptr = a->value;
2416490c2ffSMaksim Yevmenkin 	uint8_t	*end = a->value + a->vlen;
2426490c2ffSMaksim Yevmenkin 	int32_t	 type, len, uuid, psm;
2436490c2ffSMaksim Yevmenkin 
2446490c2ffSMaksim Yevmenkin 	if (end - ptr < 15)
2456490c2ffSMaksim Yevmenkin 		return (-1);
2466490c2ffSMaksim Yevmenkin 
2476490c2ffSMaksim Yevmenkin 	if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) {
2486490c2ffSMaksim Yevmenkin 		SDP_GET8(type, ptr);
2496490c2ffSMaksim Yevmenkin 		switch (type) {
2506490c2ffSMaksim Yevmenkin 		case SDP_DATA_SEQ8:
2516490c2ffSMaksim Yevmenkin 			SDP_GET8(len, ptr);
2526490c2ffSMaksim Yevmenkin 			break;
2536490c2ffSMaksim Yevmenkin 
2546490c2ffSMaksim Yevmenkin 		case SDP_DATA_SEQ16:
2556490c2ffSMaksim Yevmenkin 			SDP_GET16(len, ptr);
2566490c2ffSMaksim Yevmenkin 			break;
2576490c2ffSMaksim Yevmenkin 
2586490c2ffSMaksim Yevmenkin 		case SDP_DATA_SEQ32:
2596490c2ffSMaksim Yevmenkin 			SDP_GET32(len, ptr);
2606490c2ffSMaksim Yevmenkin 			break;
2616490c2ffSMaksim Yevmenkin 
2626490c2ffSMaksim Yevmenkin 		default:
2636490c2ffSMaksim Yevmenkin 			return (-1);
2646490c2ffSMaksim Yevmenkin 		}
2656490c2ffSMaksim Yevmenkin 		if (ptr + len > end)
2666490c2ffSMaksim Yevmenkin 			return (-1);
2676490c2ffSMaksim Yevmenkin 	}
2686490c2ffSMaksim Yevmenkin 
2696490c2ffSMaksim Yevmenkin 	SDP_GET8(type, ptr);
2706490c2ffSMaksim Yevmenkin 	switch (type) {
2716490c2ffSMaksim Yevmenkin 	case SDP_DATA_SEQ8:
2726490c2ffSMaksim Yevmenkin 		SDP_GET8(len, ptr);
2736490c2ffSMaksim Yevmenkin 		break;
2746490c2ffSMaksim Yevmenkin 
2756490c2ffSMaksim Yevmenkin 	case SDP_DATA_SEQ16:
2766490c2ffSMaksim Yevmenkin 		SDP_GET16(len, ptr);
2776490c2ffSMaksim Yevmenkin 		break;
2786490c2ffSMaksim Yevmenkin 
2796490c2ffSMaksim Yevmenkin 	case SDP_DATA_SEQ32:
2806490c2ffSMaksim Yevmenkin 		SDP_GET32(len, ptr);
2816490c2ffSMaksim Yevmenkin 		break;
2826490c2ffSMaksim Yevmenkin 
2836490c2ffSMaksim Yevmenkin 	default:
2846490c2ffSMaksim Yevmenkin 		return (-1);
2856490c2ffSMaksim Yevmenkin 	}
2866490c2ffSMaksim Yevmenkin 	if (ptr + len > end)
2876490c2ffSMaksim Yevmenkin 		return (-1);
2886490c2ffSMaksim Yevmenkin 
2896490c2ffSMaksim Yevmenkin 	/* Protocol */
2906490c2ffSMaksim Yevmenkin 	SDP_GET8(type, ptr);
2916490c2ffSMaksim Yevmenkin 	switch (type) {
2926490c2ffSMaksim Yevmenkin 	case SDP_DATA_SEQ8:
2936490c2ffSMaksim Yevmenkin 		SDP_GET8(len, ptr);
2946490c2ffSMaksim Yevmenkin 		break;
2956490c2ffSMaksim Yevmenkin 
2966490c2ffSMaksim Yevmenkin 	case SDP_DATA_SEQ16:
2976490c2ffSMaksim Yevmenkin 		SDP_GET16(len, ptr);
2986490c2ffSMaksim Yevmenkin 		break;
2996490c2ffSMaksim Yevmenkin 
3006490c2ffSMaksim Yevmenkin 	case SDP_DATA_SEQ32:
3016490c2ffSMaksim Yevmenkin 		SDP_GET32(len, ptr);
3026490c2ffSMaksim Yevmenkin 		break;
3036490c2ffSMaksim Yevmenkin 
3046490c2ffSMaksim Yevmenkin 	default:
3056490c2ffSMaksim Yevmenkin 		return (-1);
3066490c2ffSMaksim Yevmenkin 	}
3076490c2ffSMaksim Yevmenkin 	if (ptr + len > end)
3086490c2ffSMaksim Yevmenkin 		return (-1);
3096490c2ffSMaksim Yevmenkin 
3106490c2ffSMaksim Yevmenkin 	/* UUID */
3116490c2ffSMaksim Yevmenkin 	if (ptr + 3 > end)
3126490c2ffSMaksim Yevmenkin 		return (-1);
3136490c2ffSMaksim Yevmenkin 	SDP_GET8(type, ptr);
3146490c2ffSMaksim Yevmenkin 	switch (type) {
3156490c2ffSMaksim Yevmenkin 	case SDP_DATA_UUID16:
3166490c2ffSMaksim Yevmenkin 		SDP_GET16(uuid, ptr);
3176490c2ffSMaksim Yevmenkin 		if (uuid != SDP_UUID_PROTOCOL_L2CAP)
3186490c2ffSMaksim Yevmenkin 			return (-1);
3196490c2ffSMaksim Yevmenkin 		break;
3206490c2ffSMaksim Yevmenkin 
3216490c2ffSMaksim Yevmenkin 	case SDP_DATA_UUID32:  /* XXX FIXME can we have 32-bit UUID */
3226490c2ffSMaksim Yevmenkin 	case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
3236490c2ffSMaksim Yevmenkin 	default:
3246490c2ffSMaksim Yevmenkin 		return (-1);
3256490c2ffSMaksim Yevmenkin 	}
3266490c2ffSMaksim Yevmenkin 
3276490c2ffSMaksim Yevmenkin 	/* PSM */
3286490c2ffSMaksim Yevmenkin 	if (ptr + 3 > end)
3296490c2ffSMaksim Yevmenkin 		return (-1);
3306490c2ffSMaksim Yevmenkin 	SDP_GET8(type, ptr);
3316490c2ffSMaksim Yevmenkin 	if (type != SDP_DATA_UINT16)
3326490c2ffSMaksim Yevmenkin 		return (-1);
3336490c2ffSMaksim Yevmenkin 	SDP_GET16(psm, ptr);
3346490c2ffSMaksim Yevmenkin 
3356490c2ffSMaksim Yevmenkin 	return (psm);
3366490c2ffSMaksim Yevmenkin }
3376490c2ffSMaksim Yevmenkin 
3386490c2ffSMaksim Yevmenkin /*
3396490c2ffSMaksim Yevmenkin  * seq len			2
3406490c2ffSMaksim Yevmenkin  *	seq len			2
3416490c2ffSMaksim Yevmenkin  *		uint8 value8	2
3426490c2ffSMaksim Yevmenkin  * 		str value	3
3436490c2ffSMaksim Yevmenkin  */
3446490c2ffSMaksim Yevmenkin 
3456490c2ffSMaksim Yevmenkin static int32_t
3466490c2ffSMaksim Yevmenkin hid_sdp_parse_hid_descriptor(sdp_attr_p a)
3476490c2ffSMaksim Yevmenkin {
3486490c2ffSMaksim Yevmenkin 	uint8_t	*ptr = a->value;
3496490c2ffSMaksim Yevmenkin 	uint8_t	*end = a->value + a->vlen;
3506490c2ffSMaksim Yevmenkin 	int32_t	 type, len, descriptor_type;
3516490c2ffSMaksim Yevmenkin 
3526490c2ffSMaksim Yevmenkin 	if (end - ptr < 9)
3536490c2ffSMaksim Yevmenkin 		return (-1);
3546490c2ffSMaksim Yevmenkin 
3556490c2ffSMaksim Yevmenkin 	SDP_GET8(type, ptr);
3566490c2ffSMaksim Yevmenkin 	switch (type) {
3576490c2ffSMaksim Yevmenkin 	case SDP_DATA_SEQ8:
3586490c2ffSMaksim Yevmenkin 		SDP_GET8(len, ptr);
3596490c2ffSMaksim Yevmenkin 		break;
3606490c2ffSMaksim Yevmenkin 
3616490c2ffSMaksim Yevmenkin 	case SDP_DATA_SEQ16:
3626490c2ffSMaksim Yevmenkin 		SDP_GET16(len, ptr);
3636490c2ffSMaksim Yevmenkin 		break;
3646490c2ffSMaksim Yevmenkin 
3656490c2ffSMaksim Yevmenkin 	case SDP_DATA_SEQ32:
3666490c2ffSMaksim Yevmenkin 		SDP_GET32(len, ptr);
3676490c2ffSMaksim Yevmenkin 		break;
3686490c2ffSMaksim Yevmenkin 
3696490c2ffSMaksim Yevmenkin 	default:
3706490c2ffSMaksim Yevmenkin 		return (-1);
3716490c2ffSMaksim Yevmenkin 	}
3726490c2ffSMaksim Yevmenkin 	if (ptr + len > end)
3736490c2ffSMaksim Yevmenkin 		return (-1);
3746490c2ffSMaksim Yevmenkin 
3756490c2ffSMaksim Yevmenkin 	while (ptr < end) {
3766490c2ffSMaksim Yevmenkin 		/* Descriptor */
3776490c2ffSMaksim Yevmenkin 		SDP_GET8(type, ptr);
3786490c2ffSMaksim Yevmenkin 		switch (type) {
3796490c2ffSMaksim Yevmenkin 		case SDP_DATA_SEQ8:
3806490c2ffSMaksim Yevmenkin 			if (ptr + 1 > end)
3816490c2ffSMaksim Yevmenkin 				return (-1);
3826490c2ffSMaksim Yevmenkin 			SDP_GET8(len, ptr);
3836490c2ffSMaksim Yevmenkin 			break;
3846490c2ffSMaksim Yevmenkin 
3856490c2ffSMaksim Yevmenkin 		case SDP_DATA_SEQ16:
3866490c2ffSMaksim Yevmenkin 			if (ptr + 2 > end)
3876490c2ffSMaksim Yevmenkin 				return (-1);
3886490c2ffSMaksim Yevmenkin 			SDP_GET16(len, ptr);
3896490c2ffSMaksim Yevmenkin 			break;
3906490c2ffSMaksim Yevmenkin 
3916490c2ffSMaksim Yevmenkin 		case SDP_DATA_SEQ32:
3926490c2ffSMaksim Yevmenkin 			if (ptr + 4 > end)
3936490c2ffSMaksim Yevmenkin 				return (-1);
3946490c2ffSMaksim Yevmenkin 			SDP_GET32(len, ptr);
3956490c2ffSMaksim Yevmenkin 			break;
3966490c2ffSMaksim Yevmenkin 
3976490c2ffSMaksim Yevmenkin 		default:
3986490c2ffSMaksim Yevmenkin 			return (-1);
3996490c2ffSMaksim Yevmenkin 		}
4006490c2ffSMaksim Yevmenkin 
4016490c2ffSMaksim Yevmenkin 		/* Descripor type */
4026490c2ffSMaksim Yevmenkin 		if (ptr + 1 > end)
4036490c2ffSMaksim Yevmenkin 			return (-1);
4046490c2ffSMaksim Yevmenkin 		SDP_GET8(type, ptr);
4056490c2ffSMaksim Yevmenkin 		if (type != SDP_DATA_UINT8 || ptr + 1 > end)
4066490c2ffSMaksim Yevmenkin 			return (-1);
4076490c2ffSMaksim Yevmenkin 		SDP_GET8(descriptor_type, ptr);
4086490c2ffSMaksim Yevmenkin 
4096490c2ffSMaksim Yevmenkin 		/* Descriptor value */
4106490c2ffSMaksim Yevmenkin 		if (ptr + 1 > end)
4116490c2ffSMaksim Yevmenkin 			return (-1);
4126490c2ffSMaksim Yevmenkin 		SDP_GET8(type, ptr);
4136490c2ffSMaksim Yevmenkin 		switch (type) {
4146490c2ffSMaksim Yevmenkin 		case SDP_DATA_STR8:
4156490c2ffSMaksim Yevmenkin 			if (ptr + 1 > end)
4166490c2ffSMaksim Yevmenkin 				return (-1);
4176490c2ffSMaksim Yevmenkin 			SDP_GET8(len, ptr);
4186490c2ffSMaksim Yevmenkin 			break;
4196490c2ffSMaksim Yevmenkin 
4206490c2ffSMaksim Yevmenkin 		case SDP_DATA_STR16:
4216490c2ffSMaksim Yevmenkin 			if (ptr + 2 > end)
4226490c2ffSMaksim Yevmenkin 				return (-1);
4236490c2ffSMaksim Yevmenkin 			SDP_GET16(len, ptr);
4246490c2ffSMaksim Yevmenkin 			break;
4256490c2ffSMaksim Yevmenkin 
4266490c2ffSMaksim Yevmenkin 		case SDP_DATA_STR32:
4276490c2ffSMaksim Yevmenkin 			if (ptr + 4 > end)
4286490c2ffSMaksim Yevmenkin 				return (-1);
4296490c2ffSMaksim Yevmenkin 			SDP_GET32(len, ptr);
4306490c2ffSMaksim Yevmenkin 			break;
4316490c2ffSMaksim Yevmenkin 
4326490c2ffSMaksim Yevmenkin 		default:
4336490c2ffSMaksim Yevmenkin 			return (-1);
4346490c2ffSMaksim Yevmenkin 		}
4356490c2ffSMaksim Yevmenkin 		if (ptr + len > end)
4366490c2ffSMaksim Yevmenkin 			return (-1);
4376490c2ffSMaksim Yevmenkin 
4386490c2ffSMaksim Yevmenkin 		if (descriptor_type == UDESC_REPORT && len > 0) {
4396490c2ffSMaksim Yevmenkin 			a->value = ptr;
4406490c2ffSMaksim Yevmenkin 			a->vlen = len;
4416490c2ffSMaksim Yevmenkin 
4426490c2ffSMaksim Yevmenkin 			return (0);
4436490c2ffSMaksim Yevmenkin 		}
4446490c2ffSMaksim Yevmenkin 
4456490c2ffSMaksim Yevmenkin 		ptr += len;
4466490c2ffSMaksim Yevmenkin 	}
4476490c2ffSMaksim Yevmenkin 
4486490c2ffSMaksim Yevmenkin 	return (-1);
4496490c2ffSMaksim Yevmenkin }
4506490c2ffSMaksim Yevmenkin 
4516490c2ffSMaksim Yevmenkin /* bool8 int8 */
4526490c2ffSMaksim Yevmenkin static int32_t
4536490c2ffSMaksim Yevmenkin hid_sdp_parse_boolean(sdp_attr_p a)
4546490c2ffSMaksim Yevmenkin {
4556490c2ffSMaksim Yevmenkin 	if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL)
4566490c2ffSMaksim Yevmenkin 		return (-1);
4576490c2ffSMaksim Yevmenkin 
4586490c2ffSMaksim Yevmenkin 	return (a->value[1]);
4596490c2ffSMaksim Yevmenkin }
4606490c2ffSMaksim Yevmenkin 
4616490c2ffSMaksim Yevmenkin /* Perform SDP query */
4626490c2ffSMaksim Yevmenkin static int32_t
4636490c2ffSMaksim Yevmenkin hid_query(bdaddr_t *bdaddr, int argc, char **argv)
4646490c2ffSMaksim Yevmenkin {
4656490c2ffSMaksim Yevmenkin 	struct hid_device	hd;
4666490c2ffSMaksim Yevmenkin 	int			e;
4676490c2ffSMaksim Yevmenkin 
4686490c2ffSMaksim Yevmenkin 	memcpy(&hd.bdaddr, bdaddr, sizeof(hd.bdaddr));
4696490c2ffSMaksim Yevmenkin 	if (hid_sdp_query(NULL, &hd, &e) < 0) {
4706490c2ffSMaksim Yevmenkin 		fprintf(stderr, "Could not perform SDP query on the " \
4716490c2ffSMaksim Yevmenkin 			"device %s. %s (%d)\n", bt_ntoa(bdaddr, NULL),
4726490c2ffSMaksim Yevmenkin 			strerror(e), e);
4736490c2ffSMaksim Yevmenkin 		return (FAILED);
4746490c2ffSMaksim Yevmenkin 	}
4756490c2ffSMaksim Yevmenkin 
4766490c2ffSMaksim Yevmenkin 	print_hid_device(&hd, stdout);
4776490c2ffSMaksim Yevmenkin 
4786490c2ffSMaksim Yevmenkin 	return (OK);
4796490c2ffSMaksim Yevmenkin }
4806490c2ffSMaksim Yevmenkin 
4816490c2ffSMaksim Yevmenkin struct bthid_command	sdp_commands[] =
4826490c2ffSMaksim Yevmenkin {
4836490c2ffSMaksim Yevmenkin {
4846490c2ffSMaksim Yevmenkin "Query",
4856490c2ffSMaksim Yevmenkin "Perform SDP query to the specified device and print HID configuration entry\n"\
4866490c2ffSMaksim Yevmenkin "for the device. The configuration entry should be appended to the Bluetooth\n"\
4876490c2ffSMaksim Yevmenkin "HID daemon configuration file and the daemon should be restarted.\n",
4886490c2ffSMaksim Yevmenkin hid_query
4896490c2ffSMaksim Yevmenkin },
4906490c2ffSMaksim Yevmenkin { NULL, NULL, NULL }
4916490c2ffSMaksim Yevmenkin };
4926490c2ffSMaksim Yevmenkin 
493