xref: /freebsd/usr.sbin/bluetooth/bthidcontrol/sdp.c (revision 8d6f425ddd8021ae2257ba9682f8844254ecdde1)
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>
33*8d6f425dSTakanori 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 
506490c2ffSMaksim Yevmenkin static uint16_t		service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE;
516490c2ffSMaksim Yevmenkin 
526490c2ffSMaksim Yevmenkin static uint32_t		attrs[] = {
536490c2ffSMaksim Yevmenkin SDP_ATTR_RANGE(	SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
546490c2ffSMaksim Yevmenkin 		SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
556490c2ffSMaksim Yevmenkin SDP_ATTR_RANGE	(SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS,
566490c2ffSMaksim Yevmenkin 		SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS),
576490c2ffSMaksim Yevmenkin SDP_ATTR_RANGE(	0x0205,		/* HIDReconnectInitiate */
5833e98adeSMaksim Yevmenkin 		0x0205),
5933e98adeSMaksim Yevmenkin SDP_ATTR_RANGE(	0x0206,		/* HIDDescriptorList */
6033e98adeSMaksim Yevmenkin 		0x0206),
61595dedc1SMarkus Brueffer SDP_ATTR_RANGE(	0x0209,		/* HIDBatteryPower */
62595dedc1SMarkus Brueffer 		0x0209),
636490c2ffSMaksim Yevmenkin SDP_ATTR_RANGE(	0x020d,		/* HIDNormallyConnectable */
646490c2ffSMaksim Yevmenkin 		0x020d)
656490c2ffSMaksim Yevmenkin 	};
666490c2ffSMaksim Yevmenkin #define	nattrs	(sizeof(attrs)/sizeof(attrs[0]))
676490c2ffSMaksim Yevmenkin 
686490c2ffSMaksim Yevmenkin static sdp_attr_t	values[8];
696490c2ffSMaksim Yevmenkin #define	nvalues	(sizeof(values)/sizeof(values[0]))
706490c2ffSMaksim Yevmenkin 
716490c2ffSMaksim Yevmenkin static uint8_t		buffer[nvalues][512];
726490c2ffSMaksim Yevmenkin 
736490c2ffSMaksim Yevmenkin /*
746490c2ffSMaksim Yevmenkin  * Query remote device
756490c2ffSMaksim Yevmenkin  */
766490c2ffSMaksim Yevmenkin 
776490c2ffSMaksim Yevmenkin #undef	hid_sdp_query_exit
786490c2ffSMaksim Yevmenkin #define	hid_sdp_query_exit(e) {		\
796490c2ffSMaksim Yevmenkin 	if (error != NULL)		\
806490c2ffSMaksim Yevmenkin 		*error = (e);		\
816490c2ffSMaksim Yevmenkin 	if (ss != NULL) {		\
826490c2ffSMaksim Yevmenkin 		sdp_close(ss);		\
836490c2ffSMaksim Yevmenkin 		ss = NULL;		\
846490c2ffSMaksim Yevmenkin 	}				\
856490c2ffSMaksim Yevmenkin 	return (((e) == 0)? 0 : -1);	\
866490c2ffSMaksim Yevmenkin }
876490c2ffSMaksim Yevmenkin 
886490c2ffSMaksim Yevmenkin static int32_t
896490c2ffSMaksim Yevmenkin hid_sdp_query(bdaddr_t const *local, struct hid_device *hd, int32_t *error)
906490c2ffSMaksim Yevmenkin {
916490c2ffSMaksim Yevmenkin 	void	*ss = NULL;
926490c2ffSMaksim Yevmenkin 	uint8_t	*hid_descriptor = NULL;
936490c2ffSMaksim Yevmenkin 	int32_t	 i, control_psm = -1, interrupt_psm = -1,
946490c2ffSMaksim Yevmenkin 		 reconnect_initiate = -1,
956490c2ffSMaksim Yevmenkin 		 normally_connectable = 0, battery_power = 0,
966490c2ffSMaksim Yevmenkin 		 hid_descriptor_length = -1;
976490c2ffSMaksim Yevmenkin 
986490c2ffSMaksim Yevmenkin 	if (local == NULL)
996490c2ffSMaksim Yevmenkin 		local = NG_HCI_BDADDR_ANY;
1006490c2ffSMaksim Yevmenkin 	if (hd == NULL)
1016490c2ffSMaksim Yevmenkin 		hid_sdp_query_exit(EINVAL);
1026490c2ffSMaksim Yevmenkin 
1036490c2ffSMaksim Yevmenkin 	for (i = 0; i < nvalues; i ++) {
1046490c2ffSMaksim Yevmenkin 		values[i].flags = SDP_ATTR_INVALID;
1056490c2ffSMaksim Yevmenkin 		values[i].attr = 0;
1066490c2ffSMaksim Yevmenkin 		values[i].vlen = sizeof(buffer[i]);
1076490c2ffSMaksim Yevmenkin 		values[i].value = buffer[i];
1086490c2ffSMaksim Yevmenkin 	}
1096490c2ffSMaksim Yevmenkin 
1106490c2ffSMaksim Yevmenkin 	if ((ss = sdp_open(local, &hd->bdaddr)) == NULL)
1116490c2ffSMaksim Yevmenkin 		hid_sdp_query_exit(ENOMEM);
1126490c2ffSMaksim Yevmenkin 	if (sdp_error(ss) != 0)
1136490c2ffSMaksim Yevmenkin 		hid_sdp_query_exit(sdp_error(ss));
1146490c2ffSMaksim Yevmenkin 	if (sdp_search(ss, 1, &service, nattrs, attrs, nvalues, values) != 0)
1156490c2ffSMaksim Yevmenkin                 hid_sdp_query_exit(sdp_error(ss));
1166490c2ffSMaksim Yevmenkin 
1176490c2ffSMaksim Yevmenkin         sdp_close(ss);
1186490c2ffSMaksim Yevmenkin         ss = NULL;
1196490c2ffSMaksim Yevmenkin 
1206490c2ffSMaksim Yevmenkin 	for (i = 0; i < nvalues; i ++) {
1216490c2ffSMaksim Yevmenkin 		if (values[i].flags != SDP_ATTR_OK)
1226490c2ffSMaksim Yevmenkin 			continue;
1236490c2ffSMaksim Yevmenkin 
1246490c2ffSMaksim Yevmenkin 		switch (values[i].attr) {
1256490c2ffSMaksim Yevmenkin 		case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
1266490c2ffSMaksim Yevmenkin 			control_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
1276490c2ffSMaksim Yevmenkin 			break;
1286490c2ffSMaksim Yevmenkin 
1296490c2ffSMaksim Yevmenkin 		case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS:
1306490c2ffSMaksim Yevmenkin 			interrupt_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
1316490c2ffSMaksim Yevmenkin 			break;
1326490c2ffSMaksim Yevmenkin 
1336490c2ffSMaksim Yevmenkin 		case 0x0205: /* HIDReconnectInitiate */
1346490c2ffSMaksim Yevmenkin 			reconnect_initiate = hid_sdp_parse_boolean(&values[i]);
1356490c2ffSMaksim Yevmenkin 			break;
1366490c2ffSMaksim Yevmenkin 
137595dedc1SMarkus Brueffer 		case 0x0206: /* HIDDescriptorList */
1386490c2ffSMaksim Yevmenkin 			if (hid_sdp_parse_hid_descriptor(&values[i]) == 0) {
1396490c2ffSMaksim Yevmenkin 				hid_descriptor = values[i].value;
1406490c2ffSMaksim Yevmenkin 				hid_descriptor_length = values[i].vlen;
1416490c2ffSMaksim Yevmenkin 			}
1426490c2ffSMaksim Yevmenkin 			break;
1436490c2ffSMaksim Yevmenkin 
144595dedc1SMarkus Brueffer 		case 0x0209: /* HIDBatteryPower */
1456490c2ffSMaksim Yevmenkin 			battery_power = hid_sdp_parse_boolean(&values[i]);
1466490c2ffSMaksim Yevmenkin 			break;
1476490c2ffSMaksim Yevmenkin 
1486490c2ffSMaksim Yevmenkin 		case 0x020d: /* HIDNormallyConnectable */
1496490c2ffSMaksim Yevmenkin 			normally_connectable = hid_sdp_parse_boolean(&values[i]);
1506490c2ffSMaksim Yevmenkin 			break;
1516490c2ffSMaksim Yevmenkin 		}
1526490c2ffSMaksim Yevmenkin 	}
1536490c2ffSMaksim Yevmenkin 
1546490c2ffSMaksim Yevmenkin 	if (control_psm == -1 || interrupt_psm == -1 ||
15533e98adeSMaksim Yevmenkin 	    reconnect_initiate == -1 ||
1566490c2ffSMaksim Yevmenkin 	    hid_descriptor == NULL || hid_descriptor_length == -1)
1576490c2ffSMaksim Yevmenkin 		hid_sdp_query_exit(ENOATTR);
1586490c2ffSMaksim Yevmenkin 
1596490c2ffSMaksim Yevmenkin 	hd->control_psm = control_psm;
1606490c2ffSMaksim Yevmenkin 	hd->interrupt_psm = interrupt_psm;
1616490c2ffSMaksim Yevmenkin 	hd->reconnect_initiate = reconnect_initiate? 1 : 0;
1626490c2ffSMaksim Yevmenkin 	hd->battery_power = battery_power? 1 : 0;
1636490c2ffSMaksim Yevmenkin 	hd->normally_connectable = normally_connectable? 1 : 0;
1646490c2ffSMaksim Yevmenkin 	hd->desc = hid_use_report_desc(hid_descriptor, hid_descriptor_length);
1656490c2ffSMaksim Yevmenkin 	if (hd->desc == NULL)
1666490c2ffSMaksim Yevmenkin 		hid_sdp_query_exit(ENOMEM);
1676490c2ffSMaksim Yevmenkin 
1686490c2ffSMaksim Yevmenkin 	return (0);
1696490c2ffSMaksim Yevmenkin }
1706490c2ffSMaksim Yevmenkin 
1716490c2ffSMaksim Yevmenkin /*
1726490c2ffSMaksim Yevmenkin  * seq len				2
1736490c2ffSMaksim Yevmenkin  *	seq len				2
1746490c2ffSMaksim Yevmenkin  *		uuid value		3
1756490c2ffSMaksim Yevmenkin  *		uint16 value		3
1766490c2ffSMaksim Yevmenkin  *		seq len			2
1776490c2ffSMaksim Yevmenkin  *			uuid value	3
1786490c2ffSMaksim Yevmenkin  */
1796490c2ffSMaksim Yevmenkin 
1806490c2ffSMaksim Yevmenkin static int32_t
1816490c2ffSMaksim Yevmenkin hid_sdp_parse_protocol_descriptor_list(sdp_attr_p a)
1826490c2ffSMaksim Yevmenkin {
1836490c2ffSMaksim Yevmenkin 	uint8_t	*ptr = a->value;
1846490c2ffSMaksim Yevmenkin 	uint8_t	*end = a->value + a->vlen;
1856490c2ffSMaksim Yevmenkin 	int32_t	 type, len, uuid, psm;
1866490c2ffSMaksim Yevmenkin 
1876490c2ffSMaksim Yevmenkin 	if (end - ptr < 15)
1886490c2ffSMaksim Yevmenkin 		return (-1);
1896490c2ffSMaksim Yevmenkin 
1906490c2ffSMaksim Yevmenkin 	if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) {
1916490c2ffSMaksim Yevmenkin 		SDP_GET8(type, ptr);
1926490c2ffSMaksim Yevmenkin 		switch (type) {
1936490c2ffSMaksim Yevmenkin 		case SDP_DATA_SEQ8:
1946490c2ffSMaksim Yevmenkin 			SDP_GET8(len, ptr);
1956490c2ffSMaksim Yevmenkin 			break;
1966490c2ffSMaksim Yevmenkin 
1976490c2ffSMaksim Yevmenkin 		case SDP_DATA_SEQ16:
1986490c2ffSMaksim Yevmenkin 			SDP_GET16(len, ptr);
1996490c2ffSMaksim Yevmenkin 			break;
2006490c2ffSMaksim Yevmenkin 
2016490c2ffSMaksim Yevmenkin 		case SDP_DATA_SEQ32:
2026490c2ffSMaksim Yevmenkin 			SDP_GET32(len, ptr);
2036490c2ffSMaksim Yevmenkin 			break;
2046490c2ffSMaksim Yevmenkin 
2056490c2ffSMaksim Yevmenkin 		default:
2066490c2ffSMaksim Yevmenkin 			return (-1);
2076490c2ffSMaksim Yevmenkin 		}
2086490c2ffSMaksim Yevmenkin 		if (ptr + len > end)
2096490c2ffSMaksim Yevmenkin 			return (-1);
2106490c2ffSMaksim Yevmenkin 	}
2116490c2ffSMaksim Yevmenkin 
2126490c2ffSMaksim Yevmenkin 	SDP_GET8(type, ptr);
2136490c2ffSMaksim Yevmenkin 	switch (type) {
2146490c2ffSMaksim Yevmenkin 	case SDP_DATA_SEQ8:
2156490c2ffSMaksim Yevmenkin 		SDP_GET8(len, ptr);
2166490c2ffSMaksim Yevmenkin 		break;
2176490c2ffSMaksim Yevmenkin 
2186490c2ffSMaksim Yevmenkin 	case SDP_DATA_SEQ16:
2196490c2ffSMaksim Yevmenkin 		SDP_GET16(len, ptr);
2206490c2ffSMaksim Yevmenkin 		break;
2216490c2ffSMaksim Yevmenkin 
2226490c2ffSMaksim Yevmenkin 	case SDP_DATA_SEQ32:
2236490c2ffSMaksim Yevmenkin 		SDP_GET32(len, ptr);
2246490c2ffSMaksim Yevmenkin 		break;
2256490c2ffSMaksim Yevmenkin 
2266490c2ffSMaksim Yevmenkin 	default:
2276490c2ffSMaksim Yevmenkin 		return (-1);
2286490c2ffSMaksim Yevmenkin 	}
2296490c2ffSMaksim Yevmenkin 	if (ptr + len > end)
2306490c2ffSMaksim Yevmenkin 		return (-1);
2316490c2ffSMaksim Yevmenkin 
2326490c2ffSMaksim Yevmenkin 	/* Protocol */
2336490c2ffSMaksim Yevmenkin 	SDP_GET8(type, ptr);
2346490c2ffSMaksim Yevmenkin 	switch (type) {
2356490c2ffSMaksim Yevmenkin 	case SDP_DATA_SEQ8:
2366490c2ffSMaksim Yevmenkin 		SDP_GET8(len, ptr);
2376490c2ffSMaksim Yevmenkin 		break;
2386490c2ffSMaksim Yevmenkin 
2396490c2ffSMaksim Yevmenkin 	case SDP_DATA_SEQ16:
2406490c2ffSMaksim Yevmenkin 		SDP_GET16(len, ptr);
2416490c2ffSMaksim Yevmenkin 		break;
2426490c2ffSMaksim Yevmenkin 
2436490c2ffSMaksim Yevmenkin 	case SDP_DATA_SEQ32:
2446490c2ffSMaksim Yevmenkin 		SDP_GET32(len, ptr);
2456490c2ffSMaksim Yevmenkin 		break;
2466490c2ffSMaksim Yevmenkin 
2476490c2ffSMaksim Yevmenkin 	default:
2486490c2ffSMaksim Yevmenkin 		return (-1);
2496490c2ffSMaksim Yevmenkin 	}
2506490c2ffSMaksim Yevmenkin 	if (ptr + len > end)
2516490c2ffSMaksim Yevmenkin 		return (-1);
2526490c2ffSMaksim Yevmenkin 
2536490c2ffSMaksim Yevmenkin 	/* UUID */
2546490c2ffSMaksim Yevmenkin 	if (ptr + 3 > end)
2556490c2ffSMaksim Yevmenkin 		return (-1);
2566490c2ffSMaksim Yevmenkin 	SDP_GET8(type, ptr);
2576490c2ffSMaksim Yevmenkin 	switch (type) {
2586490c2ffSMaksim Yevmenkin 	case SDP_DATA_UUID16:
2596490c2ffSMaksim Yevmenkin 		SDP_GET16(uuid, ptr);
2606490c2ffSMaksim Yevmenkin 		if (uuid != SDP_UUID_PROTOCOL_L2CAP)
2616490c2ffSMaksim Yevmenkin 			return (-1);
2626490c2ffSMaksim Yevmenkin 		break;
2636490c2ffSMaksim Yevmenkin 
2646490c2ffSMaksim Yevmenkin 	case SDP_DATA_UUID32:  /* XXX FIXME can we have 32-bit UUID */
2656490c2ffSMaksim Yevmenkin 	case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
2666490c2ffSMaksim Yevmenkin 	default:
2676490c2ffSMaksim Yevmenkin 		return (-1);
2686490c2ffSMaksim Yevmenkin 	}
2696490c2ffSMaksim Yevmenkin 
2706490c2ffSMaksim Yevmenkin 	/* PSM */
2716490c2ffSMaksim Yevmenkin 	if (ptr + 3 > end)
2726490c2ffSMaksim Yevmenkin 		return (-1);
2736490c2ffSMaksim Yevmenkin 	SDP_GET8(type, ptr);
2746490c2ffSMaksim Yevmenkin 	if (type != SDP_DATA_UINT16)
2756490c2ffSMaksim Yevmenkin 		return (-1);
2766490c2ffSMaksim Yevmenkin 	SDP_GET16(psm, ptr);
2776490c2ffSMaksim Yevmenkin 
2786490c2ffSMaksim Yevmenkin 	return (psm);
2796490c2ffSMaksim Yevmenkin }
2806490c2ffSMaksim Yevmenkin 
2816490c2ffSMaksim Yevmenkin /*
2826490c2ffSMaksim Yevmenkin  * seq len			2
2836490c2ffSMaksim Yevmenkin  *	seq len			2
2846490c2ffSMaksim Yevmenkin  *		uint8 value8	2
2856490c2ffSMaksim Yevmenkin  * 		str value	3
2866490c2ffSMaksim Yevmenkin  */
2876490c2ffSMaksim Yevmenkin 
2886490c2ffSMaksim Yevmenkin static int32_t
2896490c2ffSMaksim Yevmenkin hid_sdp_parse_hid_descriptor(sdp_attr_p a)
2906490c2ffSMaksim Yevmenkin {
2916490c2ffSMaksim Yevmenkin 	uint8_t	*ptr = a->value;
2926490c2ffSMaksim Yevmenkin 	uint8_t	*end = a->value + a->vlen;
2936490c2ffSMaksim Yevmenkin 	int32_t	 type, len, descriptor_type;
2946490c2ffSMaksim Yevmenkin 
2956490c2ffSMaksim Yevmenkin 	if (end - ptr < 9)
2966490c2ffSMaksim Yevmenkin 		return (-1);
2976490c2ffSMaksim Yevmenkin 
2986490c2ffSMaksim Yevmenkin 	SDP_GET8(type, ptr);
2996490c2ffSMaksim Yevmenkin 	switch (type) {
3006490c2ffSMaksim Yevmenkin 	case SDP_DATA_SEQ8:
3016490c2ffSMaksim Yevmenkin 		SDP_GET8(len, ptr);
3026490c2ffSMaksim Yevmenkin 		break;
3036490c2ffSMaksim Yevmenkin 
3046490c2ffSMaksim Yevmenkin 	case SDP_DATA_SEQ16:
3056490c2ffSMaksim Yevmenkin 		SDP_GET16(len, ptr);
3066490c2ffSMaksim Yevmenkin 		break;
3076490c2ffSMaksim Yevmenkin 
3086490c2ffSMaksim Yevmenkin 	case SDP_DATA_SEQ32:
3096490c2ffSMaksim Yevmenkin 		SDP_GET32(len, ptr);
3106490c2ffSMaksim Yevmenkin 		break;
3116490c2ffSMaksim Yevmenkin 
3126490c2ffSMaksim Yevmenkin 	default:
3136490c2ffSMaksim Yevmenkin 		return (-1);
3146490c2ffSMaksim Yevmenkin 	}
3156490c2ffSMaksim Yevmenkin 	if (ptr + len > end)
3166490c2ffSMaksim Yevmenkin 		return (-1);
3176490c2ffSMaksim Yevmenkin 
3186490c2ffSMaksim Yevmenkin 	while (ptr < end) {
3196490c2ffSMaksim Yevmenkin 		/* Descriptor */
3206490c2ffSMaksim Yevmenkin 		SDP_GET8(type, ptr);
3216490c2ffSMaksim Yevmenkin 		switch (type) {
3226490c2ffSMaksim Yevmenkin 		case SDP_DATA_SEQ8:
3236490c2ffSMaksim Yevmenkin 			if (ptr + 1 > end)
3246490c2ffSMaksim Yevmenkin 				return (-1);
3256490c2ffSMaksim Yevmenkin 			SDP_GET8(len, ptr);
3266490c2ffSMaksim Yevmenkin 			break;
3276490c2ffSMaksim Yevmenkin 
3286490c2ffSMaksim Yevmenkin 		case SDP_DATA_SEQ16:
3296490c2ffSMaksim Yevmenkin 			if (ptr + 2 > end)
3306490c2ffSMaksim Yevmenkin 				return (-1);
3316490c2ffSMaksim Yevmenkin 			SDP_GET16(len, ptr);
3326490c2ffSMaksim Yevmenkin 			break;
3336490c2ffSMaksim Yevmenkin 
3346490c2ffSMaksim Yevmenkin 		case SDP_DATA_SEQ32:
3356490c2ffSMaksim Yevmenkin 			if (ptr + 4 > end)
3366490c2ffSMaksim Yevmenkin 				return (-1);
3376490c2ffSMaksim Yevmenkin 			SDP_GET32(len, ptr);
3386490c2ffSMaksim Yevmenkin 			break;
3396490c2ffSMaksim Yevmenkin 
3406490c2ffSMaksim Yevmenkin 		default:
3416490c2ffSMaksim Yevmenkin 			return (-1);
3426490c2ffSMaksim Yevmenkin 		}
3436490c2ffSMaksim Yevmenkin 
3446490c2ffSMaksim Yevmenkin 		/* Descripor type */
3456490c2ffSMaksim Yevmenkin 		if (ptr + 1 > end)
3466490c2ffSMaksim Yevmenkin 			return (-1);
3476490c2ffSMaksim Yevmenkin 		SDP_GET8(type, ptr);
3486490c2ffSMaksim Yevmenkin 		if (type != SDP_DATA_UINT8 || ptr + 1 > end)
3496490c2ffSMaksim Yevmenkin 			return (-1);
3506490c2ffSMaksim Yevmenkin 		SDP_GET8(descriptor_type, ptr);
3516490c2ffSMaksim Yevmenkin 
3526490c2ffSMaksim Yevmenkin 		/* Descriptor value */
3536490c2ffSMaksim Yevmenkin 		if (ptr + 1 > end)
3546490c2ffSMaksim Yevmenkin 			return (-1);
3556490c2ffSMaksim Yevmenkin 		SDP_GET8(type, ptr);
3566490c2ffSMaksim Yevmenkin 		switch (type) {
3576490c2ffSMaksim Yevmenkin 		case SDP_DATA_STR8:
3586490c2ffSMaksim Yevmenkin 			if (ptr + 1 > end)
3596490c2ffSMaksim Yevmenkin 				return (-1);
3606490c2ffSMaksim Yevmenkin 			SDP_GET8(len, ptr);
3616490c2ffSMaksim Yevmenkin 			break;
3626490c2ffSMaksim Yevmenkin 
3636490c2ffSMaksim Yevmenkin 		case SDP_DATA_STR16:
3646490c2ffSMaksim Yevmenkin 			if (ptr + 2 > end)
3656490c2ffSMaksim Yevmenkin 				return (-1);
3666490c2ffSMaksim Yevmenkin 			SDP_GET16(len, ptr);
3676490c2ffSMaksim Yevmenkin 			break;
3686490c2ffSMaksim Yevmenkin 
3696490c2ffSMaksim Yevmenkin 		case SDP_DATA_STR32:
3706490c2ffSMaksim Yevmenkin 			if (ptr + 4 > end)
3716490c2ffSMaksim Yevmenkin 				return (-1);
3726490c2ffSMaksim Yevmenkin 			SDP_GET32(len, ptr);
3736490c2ffSMaksim Yevmenkin 			break;
3746490c2ffSMaksim Yevmenkin 
3756490c2ffSMaksim Yevmenkin 		default:
3766490c2ffSMaksim Yevmenkin 			return (-1);
3776490c2ffSMaksim Yevmenkin 		}
3786490c2ffSMaksim Yevmenkin 		if (ptr + len > end)
3796490c2ffSMaksim Yevmenkin 			return (-1);
3806490c2ffSMaksim Yevmenkin 
3816490c2ffSMaksim Yevmenkin 		if (descriptor_type == UDESC_REPORT && len > 0) {
3826490c2ffSMaksim Yevmenkin 			a->value = ptr;
3836490c2ffSMaksim Yevmenkin 			a->vlen = len;
3846490c2ffSMaksim Yevmenkin 
3856490c2ffSMaksim Yevmenkin 			return (0);
3866490c2ffSMaksim Yevmenkin 		}
3876490c2ffSMaksim Yevmenkin 
3886490c2ffSMaksim Yevmenkin 		ptr += len;
3896490c2ffSMaksim Yevmenkin 	}
3906490c2ffSMaksim Yevmenkin 
3916490c2ffSMaksim Yevmenkin 	return (-1);
3926490c2ffSMaksim Yevmenkin }
3936490c2ffSMaksim Yevmenkin 
3946490c2ffSMaksim Yevmenkin /* bool8 int8 */
3956490c2ffSMaksim Yevmenkin static int32_t
3966490c2ffSMaksim Yevmenkin hid_sdp_parse_boolean(sdp_attr_p a)
3976490c2ffSMaksim Yevmenkin {
3986490c2ffSMaksim Yevmenkin 	if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL)
3996490c2ffSMaksim Yevmenkin 		return (-1);
4006490c2ffSMaksim Yevmenkin 
4016490c2ffSMaksim Yevmenkin 	return (a->value[1]);
4026490c2ffSMaksim Yevmenkin }
4036490c2ffSMaksim Yevmenkin 
4046490c2ffSMaksim Yevmenkin /* Perform SDP query */
4056490c2ffSMaksim Yevmenkin static int32_t
4066490c2ffSMaksim Yevmenkin hid_query(bdaddr_t *bdaddr, int argc, char **argv)
4076490c2ffSMaksim Yevmenkin {
4086490c2ffSMaksim Yevmenkin 	struct hid_device	hd;
4096490c2ffSMaksim Yevmenkin 	int			e;
4106490c2ffSMaksim Yevmenkin 
4116490c2ffSMaksim Yevmenkin 	memcpy(&hd.bdaddr, bdaddr, sizeof(hd.bdaddr));
4126490c2ffSMaksim Yevmenkin 	if (hid_sdp_query(NULL, &hd, &e) < 0) {
4136490c2ffSMaksim Yevmenkin 		fprintf(stderr, "Could not perform SDP query on the " \
4146490c2ffSMaksim Yevmenkin 			"device %s. %s (%d)\n", bt_ntoa(bdaddr, NULL),
4156490c2ffSMaksim Yevmenkin 			strerror(e), e);
4166490c2ffSMaksim Yevmenkin 		return (FAILED);
4176490c2ffSMaksim Yevmenkin 	}
4186490c2ffSMaksim Yevmenkin 
4196490c2ffSMaksim Yevmenkin 	print_hid_device(&hd, stdout);
4206490c2ffSMaksim Yevmenkin 
4216490c2ffSMaksim Yevmenkin 	return (OK);
4226490c2ffSMaksim Yevmenkin }
4236490c2ffSMaksim Yevmenkin 
4246490c2ffSMaksim Yevmenkin struct bthid_command	sdp_commands[] =
4256490c2ffSMaksim Yevmenkin {
4266490c2ffSMaksim Yevmenkin {
4276490c2ffSMaksim Yevmenkin "Query",
4286490c2ffSMaksim Yevmenkin "Perform SDP query to the specified device and print HID configuration entry\n"\
4296490c2ffSMaksim Yevmenkin "for the device. The configuration entry should be appended to the Bluetooth\n"\
4306490c2ffSMaksim Yevmenkin "HID daemon configuration file and the daemon should be restarted.\n",
4316490c2ffSMaksim Yevmenkin hid_query
4326490c2ffSMaksim Yevmenkin },
4336490c2ffSMaksim Yevmenkin { NULL, NULL, NULL }
4346490c2ffSMaksim Yevmenkin };
4356490c2ffSMaksim Yevmenkin 
436