xref: /freebsd/usr.sbin/bluetooth/bthidcontrol/sdp.c (revision e65080696611e52ddc84103f4314122d0a336fa5)
11de7b4b8SPedro F. Giffuni /*-
26490c2ffSMaksim Yevmenkin  * sdp.c
36490c2ffSMaksim Yevmenkin  *
41de7b4b8SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
51de7b4b8SPedro F. Giffuni  *
66490c2ffSMaksim Yevmenkin  * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
76490c2ffSMaksim Yevmenkin  * All rights reserved.
86490c2ffSMaksim Yevmenkin  *
96490c2ffSMaksim Yevmenkin  * Redistribution and use in source and binary forms, with or without
106490c2ffSMaksim Yevmenkin  * modification, are permitted provided that the following conditions
116490c2ffSMaksim Yevmenkin  * are met:
126490c2ffSMaksim Yevmenkin  * 1. Redistributions of source code must retain the above copyright
136490c2ffSMaksim Yevmenkin  *    notice, this list of conditions and the following disclaimer.
146490c2ffSMaksim Yevmenkin  * 2. Redistributions in binary form must reproduce the above copyright
156490c2ffSMaksim Yevmenkin  *    notice, this list of conditions and the following disclaimer in the
166490c2ffSMaksim Yevmenkin  *    documentation and/or other materials provided with the distribution.
176490c2ffSMaksim Yevmenkin  *
186490c2ffSMaksim Yevmenkin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
196490c2ffSMaksim Yevmenkin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
206490c2ffSMaksim Yevmenkin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
216490c2ffSMaksim Yevmenkin  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
226490c2ffSMaksim Yevmenkin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
236490c2ffSMaksim Yevmenkin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
246490c2ffSMaksim Yevmenkin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
256490c2ffSMaksim Yevmenkin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
266490c2ffSMaksim Yevmenkin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
276490c2ffSMaksim Yevmenkin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
286490c2ffSMaksim Yevmenkin  * SUCH DAMAGE.
296490c2ffSMaksim Yevmenkin  *
306490c2ffSMaksim Yevmenkin  * $Id: sdp.c,v 1.3 2004/02/17 22:14:57 max Exp $
316490c2ffSMaksim Yevmenkin  * $FreeBSD$
326490c2ffSMaksim Yevmenkin  */
336490c2ffSMaksim Yevmenkin 
34*e6508069SVladimir Kondratyev #include <sys/types.h>
356490c2ffSMaksim Yevmenkin #include <sys/queue.h>
36*e6508069SVladimir Kondratyev #include <sys/sysctl.h>
378d6f425dSTakanori Watanabe #define L2CAP_SOCKET_CHECKED
386490c2ffSMaksim Yevmenkin #include <bluetooth.h>
396490c2ffSMaksim Yevmenkin #include <dev/usb/usb.h>
406490c2ffSMaksim Yevmenkin #include <dev/usb/usbhid.h>
416490c2ffSMaksim Yevmenkin #include <errno.h>
426490c2ffSMaksim Yevmenkin #include <sdp.h>
436490c2ffSMaksim Yevmenkin #include <stdio.h>
446490c2ffSMaksim Yevmenkin #include <string.h>
456490c2ffSMaksim Yevmenkin #include <usbhid.h>
466490c2ffSMaksim Yevmenkin #include "bthid_config.h"
476490c2ffSMaksim Yevmenkin #include "bthidcontrol.h"
486490c2ffSMaksim Yevmenkin 
496490c2ffSMaksim Yevmenkin static int32_t hid_sdp_query				(bdaddr_t const *local, struct hid_device *hd, int32_t *error);
506490c2ffSMaksim Yevmenkin static int32_t hid_sdp_parse_protocol_descriptor_list	(sdp_attr_p a);
516490c2ffSMaksim Yevmenkin static int32_t hid_sdp_parse_hid_descriptor		(sdp_attr_p a);
526490c2ffSMaksim Yevmenkin static int32_t hid_sdp_parse_boolean			(sdp_attr_p a);
536490c2ffSMaksim Yevmenkin 
546032284eSVladimir Kondratyev /*
556032284eSVladimir Kondratyev  * Hard coded attibute IDs taken from the
566032284eSVladimir Kondratyev  * DEVICE IDENTIFICATION PROFILE SPECIFICATION V13 p.12
576032284eSVladimir Kondratyev  */
586032284eSVladimir Kondratyev 
596032284eSVladimir Kondratyev #define SDP_ATTR_DEVICE_ID_SERVICE_VENDORID  0x0201
606032284eSVladimir Kondratyev #define SDP_ATTR_DEVICE_ID_SERVICE_PRODUCTID 0x0202
616032284eSVladimir Kondratyev #define SDP_ATTR_DEVICE_ID_SERVICE_VERSION   0x0203
626032284eSVladimir Kondratyev #define SDP_ATTR_DEVICE_ID_RANGE SDP_ATTR_RANGE( \
636032284eSVladimir Kondratyev  SDP_ATTR_DEVICE_ID_SERVICE_VENDORID, SDP_ATTR_DEVICE_ID_SERVICE_VERSION )
646032284eSVladimir Kondratyev 
656490c2ffSMaksim Yevmenkin static uint16_t		service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE;
666032284eSVladimir Kondratyev static uint16_t		service_devid = SDP_SERVICE_CLASS_PNP_INFORMATION;
676032284eSVladimir Kondratyev static uint32_t 	attrs_devid   = SDP_ATTR_DEVICE_ID_RANGE;
686490c2ffSMaksim Yevmenkin 
696490c2ffSMaksim Yevmenkin static uint32_t		attrs[] = {
706490c2ffSMaksim Yevmenkin SDP_ATTR_RANGE(	SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
716490c2ffSMaksim Yevmenkin 		SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
726490c2ffSMaksim Yevmenkin SDP_ATTR_RANGE	(SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS,
736490c2ffSMaksim Yevmenkin 		SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS),
746490c2ffSMaksim Yevmenkin SDP_ATTR_RANGE(	0x0205,		/* HIDReconnectInitiate */
7533e98adeSMaksim Yevmenkin 		0x0205),
7633e98adeSMaksim Yevmenkin SDP_ATTR_RANGE(	0x0206,		/* HIDDescriptorList */
7733e98adeSMaksim Yevmenkin 		0x0206),
78595dedc1SMarkus Brueffer SDP_ATTR_RANGE(	0x0209,		/* HIDBatteryPower */
79595dedc1SMarkus Brueffer 		0x0209),
806490c2ffSMaksim Yevmenkin SDP_ATTR_RANGE(	0x020d,		/* HIDNormallyConnectable */
816490c2ffSMaksim Yevmenkin 		0x020d)
826490c2ffSMaksim Yevmenkin 	};
836490c2ffSMaksim Yevmenkin #define	nattrs	(sizeof(attrs)/sizeof(attrs[0]))
846490c2ffSMaksim Yevmenkin 
856490c2ffSMaksim Yevmenkin static sdp_attr_t	values[8];
866490c2ffSMaksim Yevmenkin #define	nvalues	(sizeof(values)/sizeof(values[0]))
876490c2ffSMaksim Yevmenkin 
886490c2ffSMaksim Yevmenkin static uint8_t		buffer[nvalues][512];
896490c2ffSMaksim Yevmenkin 
906490c2ffSMaksim Yevmenkin /*
916490c2ffSMaksim Yevmenkin  * Query remote device
926490c2ffSMaksim Yevmenkin  */
936490c2ffSMaksim Yevmenkin 
946490c2ffSMaksim Yevmenkin #undef	hid_sdp_query_exit
956490c2ffSMaksim Yevmenkin #define	hid_sdp_query_exit(e) {		\
966490c2ffSMaksim Yevmenkin 	if (error != NULL)		\
976490c2ffSMaksim Yevmenkin 		*error = (e);		\
986490c2ffSMaksim Yevmenkin 	if (ss != NULL) {		\
996490c2ffSMaksim Yevmenkin 		sdp_close(ss);		\
1006490c2ffSMaksim Yevmenkin 		ss = NULL;		\
1016490c2ffSMaksim Yevmenkin 	}				\
1026490c2ffSMaksim Yevmenkin 	return (((e) == 0)? 0 : -1);	\
1036490c2ffSMaksim Yevmenkin }
1046490c2ffSMaksim Yevmenkin 
1056032284eSVladimir Kondratyev static void
1066032284eSVladimir Kondratyev hid_init_return_values() {
1076032284eSVladimir Kondratyev 	int i;
1086490c2ffSMaksim Yevmenkin 	for (i = 0; i < nvalues; i ++) {
1096490c2ffSMaksim Yevmenkin 		values[i].flags = SDP_ATTR_INVALID;
1106490c2ffSMaksim Yevmenkin 		values[i].attr = 0;
1116490c2ffSMaksim Yevmenkin 		values[i].vlen = sizeof(buffer[i]);
1126490c2ffSMaksim Yevmenkin 		values[i].value = buffer[i];
1136490c2ffSMaksim Yevmenkin 	}
1146032284eSVladimir Kondratyev }
1156032284eSVladimir Kondratyev 
1166032284eSVladimir Kondratyev static int32_t
1176032284eSVladimir Kondratyev hid_sdp_query(bdaddr_t const *local, struct hid_device *hd, int32_t *error)
1186032284eSVladimir Kondratyev {
1196032284eSVladimir Kondratyev 	void			*ss = NULL;
1206032284eSVladimir Kondratyev 	uint8_t			*hid_descriptor = NULL, *v;
1216032284eSVladimir Kondratyev 	int32_t			 i, control_psm = -1, interrupt_psm = -1,
1226032284eSVladimir Kondratyev 				 reconnect_initiate = -1,
1236032284eSVladimir Kondratyev 				 normally_connectable = 0, battery_power = 0,
1246032284eSVladimir Kondratyev 				 hid_descriptor_length = -1, type;
1256032284eSVladimir Kondratyev 	int16_t 		 vendor_id = 0, product_id = 0, version = 0;
126*e6508069SVladimir Kondratyev 	bdaddr_t		 sdp_local;
127*e6508069SVladimir Kondratyev 	char			 devname[HCI_DEVNAME_SIZE];
1286032284eSVladimir Kondratyev 
1296032284eSVladimir Kondratyev 	if (local == NULL)
1306032284eSVladimir Kondratyev 		local = NG_HCI_BDADDR_ANY;
1316032284eSVladimir Kondratyev 	if (hd == NULL)
1326032284eSVladimir Kondratyev 		hid_sdp_query_exit(EINVAL);
1336032284eSVladimir Kondratyev 
1346032284eSVladimir Kondratyev 	hid_init_return_values();
1356490c2ffSMaksim Yevmenkin 
1366490c2ffSMaksim Yevmenkin 	if ((ss = sdp_open(local, &hd->bdaddr)) == NULL)
1376490c2ffSMaksim Yevmenkin 		hid_sdp_query_exit(ENOMEM);
1386490c2ffSMaksim Yevmenkin 	if (sdp_error(ss) != 0)
1396490c2ffSMaksim Yevmenkin 		hid_sdp_query_exit(sdp_error(ss));
1406490c2ffSMaksim Yevmenkin 	if (sdp_search(ss, 1, &service, nattrs, attrs, nvalues, values) != 0)
1416490c2ffSMaksim Yevmenkin                 hid_sdp_query_exit(sdp_error(ss));
1426490c2ffSMaksim Yevmenkin 
1436490c2ffSMaksim Yevmenkin 	for (i = 0; i < nvalues; i ++) {
1446490c2ffSMaksim Yevmenkin 		if (values[i].flags != SDP_ATTR_OK)
1456490c2ffSMaksim Yevmenkin 			continue;
1466490c2ffSMaksim Yevmenkin 
1476490c2ffSMaksim Yevmenkin 		switch (values[i].attr) {
1486490c2ffSMaksim Yevmenkin 		case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
1496490c2ffSMaksim Yevmenkin 			control_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
1506490c2ffSMaksim Yevmenkin 			break;
1516490c2ffSMaksim Yevmenkin 
1526490c2ffSMaksim Yevmenkin 		case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS:
1536490c2ffSMaksim Yevmenkin 			interrupt_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
1546490c2ffSMaksim Yevmenkin 			break;
1556490c2ffSMaksim Yevmenkin 
1566490c2ffSMaksim Yevmenkin 		case 0x0205: /* HIDReconnectInitiate */
1576490c2ffSMaksim Yevmenkin 			reconnect_initiate = hid_sdp_parse_boolean(&values[i]);
1586490c2ffSMaksim Yevmenkin 			break;
1596490c2ffSMaksim Yevmenkin 
160595dedc1SMarkus Brueffer 		case 0x0206: /* HIDDescriptorList */
1616490c2ffSMaksim Yevmenkin 			if (hid_sdp_parse_hid_descriptor(&values[i]) == 0) {
1626490c2ffSMaksim Yevmenkin 				hid_descriptor = values[i].value;
1636490c2ffSMaksim Yevmenkin 				hid_descriptor_length = values[i].vlen;
1646490c2ffSMaksim Yevmenkin 			}
1656490c2ffSMaksim Yevmenkin 			break;
1666490c2ffSMaksim Yevmenkin 
167595dedc1SMarkus Brueffer 		case 0x0209: /* HIDBatteryPower */
1686490c2ffSMaksim Yevmenkin 			battery_power = hid_sdp_parse_boolean(&values[i]);
1696490c2ffSMaksim Yevmenkin 			break;
1706490c2ffSMaksim Yevmenkin 
1716490c2ffSMaksim Yevmenkin 		case 0x020d: /* HIDNormallyConnectable */
1726490c2ffSMaksim Yevmenkin 			normally_connectable = hid_sdp_parse_boolean(&values[i]);
1736490c2ffSMaksim Yevmenkin 			break;
1746490c2ffSMaksim Yevmenkin 		}
1756490c2ffSMaksim Yevmenkin 	}
1766490c2ffSMaksim Yevmenkin 
1776032284eSVladimir Kondratyev 	hid_init_return_values();
1786032284eSVladimir Kondratyev 
1796032284eSVladimir Kondratyev 	if (sdp_search(ss, 1, &service_devid, 1, &attrs_devid, nvalues, values) != 0)
1806032284eSVladimir Kondratyev                 hid_sdp_query_exit(sdp_error(ss));
1816032284eSVladimir Kondratyev 
182*e6508069SVladimir Kondratyev 	/* Try extract HCI bdaddr from opened SDP session */
183*e6508069SVladimir Kondratyev 	if (sdp_get_lcaddr(ss, &sdp_local) != 0 ||
184*e6508069SVladimir Kondratyev 	    bt_devname(devname, &sdp_local) == 0)
185*e6508069SVladimir Kondratyev 		hid_sdp_query_exit(ENOATTR);
186*e6508069SVladimir Kondratyev 
1876032284eSVladimir Kondratyev         sdp_close(ss);
1886032284eSVladimir Kondratyev         ss = NULL;
1896032284eSVladimir Kondratyev 
1906032284eSVladimir Kondratyev 	/* If search is successful, scan through return vals */
1916032284eSVladimir Kondratyev 	for (i = 0; i < 3; i ++ ) {
1926032284eSVladimir Kondratyev 		if (values[i].flags == SDP_ATTR_INVALID )
1936032284eSVladimir Kondratyev 			continue;
1946032284eSVladimir Kondratyev 
1956032284eSVladimir Kondratyev 		/* Expecting tag + uint16_t on all 3 attributes */
1966032284eSVladimir Kondratyev 		if (values[i].vlen != 3)
1976032284eSVladimir Kondratyev 			continue;
1986032284eSVladimir Kondratyev 
1996032284eSVladimir Kondratyev 		/* Make sure, we're reading a uint16_t */
2006032284eSVladimir Kondratyev 		v = values[i].value;
2016032284eSVladimir Kondratyev 		SDP_GET8(type, v);
2026032284eSVladimir Kondratyev 		if (type != SDP_DATA_UINT16 )
2036032284eSVladimir Kondratyev 			continue;
2046032284eSVladimir Kondratyev 
2056032284eSVladimir Kondratyev 		switch (values[i].attr) {
2066032284eSVladimir Kondratyev 			case SDP_ATTR_DEVICE_ID_SERVICE_VENDORID:
2076032284eSVladimir Kondratyev 				SDP_GET16(vendor_id, v);
2086032284eSVladimir Kondratyev 				break;
2096032284eSVladimir Kondratyev 			case SDP_ATTR_DEVICE_ID_SERVICE_PRODUCTID:
2106032284eSVladimir Kondratyev 				SDP_GET16(product_id, v);
2116032284eSVladimir Kondratyev 				break;
2126032284eSVladimir Kondratyev 			case SDP_ATTR_DEVICE_ID_SERVICE_VERSION:
2136032284eSVladimir Kondratyev 				SDP_GET16(version, v);
2146032284eSVladimir Kondratyev 				break;
2156032284eSVladimir Kondratyev 			default:
2166032284eSVladimir Kondratyev 				break;
2176032284eSVladimir Kondratyev 		}
2186032284eSVladimir Kondratyev 	}
2196032284eSVladimir Kondratyev 
2206490c2ffSMaksim Yevmenkin 	if (control_psm == -1 || interrupt_psm == -1 ||
22133e98adeSMaksim Yevmenkin 	    reconnect_initiate == -1 ||
2226490c2ffSMaksim Yevmenkin 	    hid_descriptor == NULL || hid_descriptor_length == -1)
2236490c2ffSMaksim Yevmenkin 		hid_sdp_query_exit(ENOATTR);
224*e6508069SVladimir Kondratyev 	hd->name = bt_devremote_name_gen(devname, &hd->bdaddr);
2256032284eSVladimir Kondratyev 	hd->vendor_id = vendor_id;
2266032284eSVladimir Kondratyev 	hd->product_id = product_id;
2276032284eSVladimir Kondratyev 	hd->version = version;
2286490c2ffSMaksim Yevmenkin 	hd->control_psm = control_psm;
2296490c2ffSMaksim Yevmenkin 	hd->interrupt_psm = interrupt_psm;
2306490c2ffSMaksim Yevmenkin 	hd->reconnect_initiate = reconnect_initiate? 1 : 0;
2316490c2ffSMaksim Yevmenkin 	hd->battery_power = battery_power? 1 : 0;
2326490c2ffSMaksim Yevmenkin 	hd->normally_connectable = normally_connectable? 1 : 0;
2336490c2ffSMaksim Yevmenkin 	hd->desc = hid_use_report_desc(hid_descriptor, hid_descriptor_length);
2346490c2ffSMaksim Yevmenkin 	if (hd->desc == NULL)
2356490c2ffSMaksim Yevmenkin 		hid_sdp_query_exit(ENOMEM);
2366490c2ffSMaksim Yevmenkin 
2376490c2ffSMaksim Yevmenkin 	return (0);
2386490c2ffSMaksim Yevmenkin }
2396490c2ffSMaksim Yevmenkin 
2406490c2ffSMaksim Yevmenkin /*
2416490c2ffSMaksim Yevmenkin  * seq len				2
2426490c2ffSMaksim Yevmenkin  *	seq len				2
2436490c2ffSMaksim Yevmenkin  *		uuid value		3
2446490c2ffSMaksim Yevmenkin  *		uint16 value		3
2456490c2ffSMaksim Yevmenkin  *		seq len			2
2466490c2ffSMaksim Yevmenkin  *			uuid value	3
2476490c2ffSMaksim Yevmenkin  */
2486490c2ffSMaksim Yevmenkin 
2496490c2ffSMaksim Yevmenkin static int32_t
2506490c2ffSMaksim Yevmenkin hid_sdp_parse_protocol_descriptor_list(sdp_attr_p a)
2516490c2ffSMaksim Yevmenkin {
2526490c2ffSMaksim Yevmenkin 	uint8_t	*ptr = a->value;
2536490c2ffSMaksim Yevmenkin 	uint8_t	*end = a->value + a->vlen;
2546490c2ffSMaksim Yevmenkin 	int32_t	 type, len, uuid, psm;
2556490c2ffSMaksim Yevmenkin 
2566490c2ffSMaksim Yevmenkin 	if (end - ptr < 15)
2576490c2ffSMaksim Yevmenkin 		return (-1);
2586490c2ffSMaksim Yevmenkin 
2596490c2ffSMaksim Yevmenkin 	if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) {
2606490c2ffSMaksim Yevmenkin 		SDP_GET8(type, ptr);
2616490c2ffSMaksim Yevmenkin 		switch (type) {
2626490c2ffSMaksim Yevmenkin 		case SDP_DATA_SEQ8:
2636490c2ffSMaksim Yevmenkin 			SDP_GET8(len, ptr);
2646490c2ffSMaksim Yevmenkin 			break;
2656490c2ffSMaksim Yevmenkin 
2666490c2ffSMaksim Yevmenkin 		case SDP_DATA_SEQ16:
2676490c2ffSMaksim Yevmenkin 			SDP_GET16(len, ptr);
2686490c2ffSMaksim Yevmenkin 			break;
2696490c2ffSMaksim Yevmenkin 
2706490c2ffSMaksim Yevmenkin 		case SDP_DATA_SEQ32:
2716490c2ffSMaksim Yevmenkin 			SDP_GET32(len, ptr);
2726490c2ffSMaksim Yevmenkin 			break;
2736490c2ffSMaksim Yevmenkin 
2746490c2ffSMaksim Yevmenkin 		default:
2756490c2ffSMaksim Yevmenkin 			return (-1);
2766490c2ffSMaksim Yevmenkin 		}
2776490c2ffSMaksim Yevmenkin 		if (ptr + len > end)
2786490c2ffSMaksim Yevmenkin 			return (-1);
2796490c2ffSMaksim Yevmenkin 	}
2806490c2ffSMaksim Yevmenkin 
2816490c2ffSMaksim Yevmenkin 	SDP_GET8(type, ptr);
2826490c2ffSMaksim Yevmenkin 	switch (type) {
2836490c2ffSMaksim Yevmenkin 	case SDP_DATA_SEQ8:
2846490c2ffSMaksim Yevmenkin 		SDP_GET8(len, ptr);
2856490c2ffSMaksim Yevmenkin 		break;
2866490c2ffSMaksim Yevmenkin 
2876490c2ffSMaksim Yevmenkin 	case SDP_DATA_SEQ16:
2886490c2ffSMaksim Yevmenkin 		SDP_GET16(len, ptr);
2896490c2ffSMaksim Yevmenkin 		break;
2906490c2ffSMaksim Yevmenkin 
2916490c2ffSMaksim Yevmenkin 	case SDP_DATA_SEQ32:
2926490c2ffSMaksim Yevmenkin 		SDP_GET32(len, ptr);
2936490c2ffSMaksim Yevmenkin 		break;
2946490c2ffSMaksim Yevmenkin 
2956490c2ffSMaksim Yevmenkin 	default:
2966490c2ffSMaksim Yevmenkin 		return (-1);
2976490c2ffSMaksim Yevmenkin 	}
2986490c2ffSMaksim Yevmenkin 	if (ptr + len > end)
2996490c2ffSMaksim Yevmenkin 		return (-1);
3006490c2ffSMaksim Yevmenkin 
3016490c2ffSMaksim Yevmenkin 	/* Protocol */
3026490c2ffSMaksim Yevmenkin 	SDP_GET8(type, ptr);
3036490c2ffSMaksim Yevmenkin 	switch (type) {
3046490c2ffSMaksim Yevmenkin 	case SDP_DATA_SEQ8:
3056490c2ffSMaksim Yevmenkin 		SDP_GET8(len, ptr);
3066490c2ffSMaksim Yevmenkin 		break;
3076490c2ffSMaksim Yevmenkin 
3086490c2ffSMaksim Yevmenkin 	case SDP_DATA_SEQ16:
3096490c2ffSMaksim Yevmenkin 		SDP_GET16(len, ptr);
3106490c2ffSMaksim Yevmenkin 		break;
3116490c2ffSMaksim Yevmenkin 
3126490c2ffSMaksim Yevmenkin 	case SDP_DATA_SEQ32:
3136490c2ffSMaksim Yevmenkin 		SDP_GET32(len, ptr);
3146490c2ffSMaksim Yevmenkin 		break;
3156490c2ffSMaksim Yevmenkin 
3166490c2ffSMaksim Yevmenkin 	default:
3176490c2ffSMaksim Yevmenkin 		return (-1);
3186490c2ffSMaksim Yevmenkin 	}
3196490c2ffSMaksim Yevmenkin 	if (ptr + len > end)
3206490c2ffSMaksim Yevmenkin 		return (-1);
3216490c2ffSMaksim Yevmenkin 
3226490c2ffSMaksim Yevmenkin 	/* UUID */
3236490c2ffSMaksim Yevmenkin 	if (ptr + 3 > end)
3246490c2ffSMaksim Yevmenkin 		return (-1);
3256490c2ffSMaksim Yevmenkin 	SDP_GET8(type, ptr);
3266490c2ffSMaksim Yevmenkin 	switch (type) {
3276490c2ffSMaksim Yevmenkin 	case SDP_DATA_UUID16:
3286490c2ffSMaksim Yevmenkin 		SDP_GET16(uuid, ptr);
3296490c2ffSMaksim Yevmenkin 		if (uuid != SDP_UUID_PROTOCOL_L2CAP)
3306490c2ffSMaksim Yevmenkin 			return (-1);
3316490c2ffSMaksim Yevmenkin 		break;
3326490c2ffSMaksim Yevmenkin 
3336490c2ffSMaksim Yevmenkin 	case SDP_DATA_UUID32:  /* XXX FIXME can we have 32-bit UUID */
3346490c2ffSMaksim Yevmenkin 	case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
3356490c2ffSMaksim Yevmenkin 	default:
3366490c2ffSMaksim Yevmenkin 		return (-1);
3376490c2ffSMaksim Yevmenkin 	}
3386490c2ffSMaksim Yevmenkin 
3396490c2ffSMaksim Yevmenkin 	/* PSM */
3406490c2ffSMaksim Yevmenkin 	if (ptr + 3 > end)
3416490c2ffSMaksim Yevmenkin 		return (-1);
3426490c2ffSMaksim Yevmenkin 	SDP_GET8(type, ptr);
3436490c2ffSMaksim Yevmenkin 	if (type != SDP_DATA_UINT16)
3446490c2ffSMaksim Yevmenkin 		return (-1);
3456490c2ffSMaksim Yevmenkin 	SDP_GET16(psm, ptr);
3466490c2ffSMaksim Yevmenkin 
3476490c2ffSMaksim Yevmenkin 	return (psm);
3486490c2ffSMaksim Yevmenkin }
3496490c2ffSMaksim Yevmenkin 
3506490c2ffSMaksim Yevmenkin /*
3516490c2ffSMaksim Yevmenkin  * seq len			2
3526490c2ffSMaksim Yevmenkin  *	seq len			2
3536490c2ffSMaksim Yevmenkin  *		uint8 value8	2
3546490c2ffSMaksim Yevmenkin  * 		str value	3
3556490c2ffSMaksim Yevmenkin  */
3566490c2ffSMaksim Yevmenkin 
3576490c2ffSMaksim Yevmenkin static int32_t
3586490c2ffSMaksim Yevmenkin hid_sdp_parse_hid_descriptor(sdp_attr_p a)
3596490c2ffSMaksim Yevmenkin {
3606490c2ffSMaksim Yevmenkin 	uint8_t	*ptr = a->value;
3616490c2ffSMaksim Yevmenkin 	uint8_t	*end = a->value + a->vlen;
3626490c2ffSMaksim Yevmenkin 	int32_t	 type, len, descriptor_type;
3636490c2ffSMaksim Yevmenkin 
3646490c2ffSMaksim Yevmenkin 	if (end - ptr < 9)
3656490c2ffSMaksim Yevmenkin 		return (-1);
3666490c2ffSMaksim Yevmenkin 
3676490c2ffSMaksim Yevmenkin 	SDP_GET8(type, ptr);
3686490c2ffSMaksim Yevmenkin 	switch (type) {
3696490c2ffSMaksim Yevmenkin 	case SDP_DATA_SEQ8:
3706490c2ffSMaksim Yevmenkin 		SDP_GET8(len, ptr);
3716490c2ffSMaksim Yevmenkin 		break;
3726490c2ffSMaksim Yevmenkin 
3736490c2ffSMaksim Yevmenkin 	case SDP_DATA_SEQ16:
3746490c2ffSMaksim Yevmenkin 		SDP_GET16(len, ptr);
3756490c2ffSMaksim Yevmenkin 		break;
3766490c2ffSMaksim Yevmenkin 
3776490c2ffSMaksim Yevmenkin 	case SDP_DATA_SEQ32:
3786490c2ffSMaksim Yevmenkin 		SDP_GET32(len, ptr);
3796490c2ffSMaksim Yevmenkin 		break;
3806490c2ffSMaksim Yevmenkin 
3816490c2ffSMaksim Yevmenkin 	default:
3826490c2ffSMaksim Yevmenkin 		return (-1);
3836490c2ffSMaksim Yevmenkin 	}
3846490c2ffSMaksim Yevmenkin 	if (ptr + len > end)
3856490c2ffSMaksim Yevmenkin 		return (-1);
3866490c2ffSMaksim Yevmenkin 
3876490c2ffSMaksim Yevmenkin 	while (ptr < end) {
3886490c2ffSMaksim Yevmenkin 		/* Descriptor */
3896490c2ffSMaksim Yevmenkin 		SDP_GET8(type, ptr);
3906490c2ffSMaksim Yevmenkin 		switch (type) {
3916490c2ffSMaksim Yevmenkin 		case SDP_DATA_SEQ8:
3926490c2ffSMaksim Yevmenkin 			if (ptr + 1 > end)
3936490c2ffSMaksim Yevmenkin 				return (-1);
3946490c2ffSMaksim Yevmenkin 			SDP_GET8(len, ptr);
3956490c2ffSMaksim Yevmenkin 			break;
3966490c2ffSMaksim Yevmenkin 
3976490c2ffSMaksim Yevmenkin 		case SDP_DATA_SEQ16:
3986490c2ffSMaksim Yevmenkin 			if (ptr + 2 > end)
3996490c2ffSMaksim Yevmenkin 				return (-1);
4006490c2ffSMaksim Yevmenkin 			SDP_GET16(len, ptr);
4016490c2ffSMaksim Yevmenkin 			break;
4026490c2ffSMaksim Yevmenkin 
4036490c2ffSMaksim Yevmenkin 		case SDP_DATA_SEQ32:
4046490c2ffSMaksim Yevmenkin 			if (ptr + 4 > end)
4056490c2ffSMaksim Yevmenkin 				return (-1);
4066490c2ffSMaksim Yevmenkin 			SDP_GET32(len, ptr);
4076490c2ffSMaksim Yevmenkin 			break;
4086490c2ffSMaksim Yevmenkin 
4096490c2ffSMaksim Yevmenkin 		default:
4106490c2ffSMaksim Yevmenkin 			return (-1);
4116490c2ffSMaksim Yevmenkin 		}
4126490c2ffSMaksim Yevmenkin 
4136490c2ffSMaksim Yevmenkin 		/* Descripor type */
4146490c2ffSMaksim Yevmenkin 		if (ptr + 1 > end)
4156490c2ffSMaksim Yevmenkin 			return (-1);
4166490c2ffSMaksim Yevmenkin 		SDP_GET8(type, ptr);
4176490c2ffSMaksim Yevmenkin 		if (type != SDP_DATA_UINT8 || ptr + 1 > end)
4186490c2ffSMaksim Yevmenkin 			return (-1);
4196490c2ffSMaksim Yevmenkin 		SDP_GET8(descriptor_type, ptr);
4206490c2ffSMaksim Yevmenkin 
4216490c2ffSMaksim Yevmenkin 		/* Descriptor value */
4226490c2ffSMaksim Yevmenkin 		if (ptr + 1 > end)
4236490c2ffSMaksim Yevmenkin 			return (-1);
4246490c2ffSMaksim Yevmenkin 		SDP_GET8(type, ptr);
4256490c2ffSMaksim Yevmenkin 		switch (type) {
4266490c2ffSMaksim Yevmenkin 		case SDP_DATA_STR8:
4276490c2ffSMaksim Yevmenkin 			if (ptr + 1 > end)
4286490c2ffSMaksim Yevmenkin 				return (-1);
4296490c2ffSMaksim Yevmenkin 			SDP_GET8(len, ptr);
4306490c2ffSMaksim Yevmenkin 			break;
4316490c2ffSMaksim Yevmenkin 
4326490c2ffSMaksim Yevmenkin 		case SDP_DATA_STR16:
4336490c2ffSMaksim Yevmenkin 			if (ptr + 2 > end)
4346490c2ffSMaksim Yevmenkin 				return (-1);
4356490c2ffSMaksim Yevmenkin 			SDP_GET16(len, ptr);
4366490c2ffSMaksim Yevmenkin 			break;
4376490c2ffSMaksim Yevmenkin 
4386490c2ffSMaksim Yevmenkin 		case SDP_DATA_STR32:
4396490c2ffSMaksim Yevmenkin 			if (ptr + 4 > end)
4406490c2ffSMaksim Yevmenkin 				return (-1);
4416490c2ffSMaksim Yevmenkin 			SDP_GET32(len, ptr);
4426490c2ffSMaksim Yevmenkin 			break;
4436490c2ffSMaksim Yevmenkin 
4446490c2ffSMaksim Yevmenkin 		default:
4456490c2ffSMaksim Yevmenkin 			return (-1);
4466490c2ffSMaksim Yevmenkin 		}
4476490c2ffSMaksim Yevmenkin 		if (ptr + len > end)
4486490c2ffSMaksim Yevmenkin 			return (-1);
4496490c2ffSMaksim Yevmenkin 
4506490c2ffSMaksim Yevmenkin 		if (descriptor_type == UDESC_REPORT && len > 0) {
4516490c2ffSMaksim Yevmenkin 			a->value = ptr;
4526490c2ffSMaksim Yevmenkin 			a->vlen = len;
4536490c2ffSMaksim Yevmenkin 
4546490c2ffSMaksim Yevmenkin 			return (0);
4556490c2ffSMaksim Yevmenkin 		}
4566490c2ffSMaksim Yevmenkin 
4576490c2ffSMaksim Yevmenkin 		ptr += len;
4586490c2ffSMaksim Yevmenkin 	}
4596490c2ffSMaksim Yevmenkin 
4606490c2ffSMaksim Yevmenkin 	return (-1);
4616490c2ffSMaksim Yevmenkin }
4626490c2ffSMaksim Yevmenkin 
4636490c2ffSMaksim Yevmenkin /* bool8 int8 */
4646490c2ffSMaksim Yevmenkin static int32_t
4656490c2ffSMaksim Yevmenkin hid_sdp_parse_boolean(sdp_attr_p a)
4666490c2ffSMaksim Yevmenkin {
4676490c2ffSMaksim Yevmenkin 	if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL)
4686490c2ffSMaksim Yevmenkin 		return (-1);
4696490c2ffSMaksim Yevmenkin 
4706490c2ffSMaksim Yevmenkin 	return (a->value[1]);
4716490c2ffSMaksim Yevmenkin }
4726490c2ffSMaksim Yevmenkin 
4736490c2ffSMaksim Yevmenkin /* Perform SDP query */
4746490c2ffSMaksim Yevmenkin static int32_t
4756490c2ffSMaksim Yevmenkin hid_query(bdaddr_t *bdaddr, int argc, char **argv)
4766490c2ffSMaksim Yevmenkin {
4776490c2ffSMaksim Yevmenkin 	struct hid_device	hd;
4786490c2ffSMaksim Yevmenkin 	int			e;
4796490c2ffSMaksim Yevmenkin 
4806490c2ffSMaksim Yevmenkin 	memcpy(&hd.bdaddr, bdaddr, sizeof(hd.bdaddr));
4816490c2ffSMaksim Yevmenkin 	if (hid_sdp_query(NULL, &hd, &e) < 0) {
4826490c2ffSMaksim Yevmenkin 		fprintf(stderr, "Could not perform SDP query on the " \
4836490c2ffSMaksim Yevmenkin 			"device %s. %s (%d)\n", bt_ntoa(bdaddr, NULL),
4846490c2ffSMaksim Yevmenkin 			strerror(e), e);
4856490c2ffSMaksim Yevmenkin 		return (FAILED);
4866490c2ffSMaksim Yevmenkin 	}
4876490c2ffSMaksim Yevmenkin 
4886490c2ffSMaksim Yevmenkin 	print_hid_device(&hd, stdout);
4896490c2ffSMaksim Yevmenkin 
4906490c2ffSMaksim Yevmenkin 	return (OK);
4916490c2ffSMaksim Yevmenkin }
4926490c2ffSMaksim Yevmenkin 
4936490c2ffSMaksim Yevmenkin struct bthid_command	sdp_commands[] =
4946490c2ffSMaksim Yevmenkin {
4956490c2ffSMaksim Yevmenkin {
4966490c2ffSMaksim Yevmenkin "Query",
4976490c2ffSMaksim Yevmenkin "Perform SDP query to the specified device and print HID configuration entry\n"\
4986490c2ffSMaksim Yevmenkin "for the device. The configuration entry should be appended to the Bluetooth\n"\
4996490c2ffSMaksim Yevmenkin "HID daemon configuration file and the daemon should be restarted.\n",
5006490c2ffSMaksim Yevmenkin hid_query
5016490c2ffSMaksim Yevmenkin },
5026490c2ffSMaksim Yevmenkin { NULL, NULL, NULL }
5036490c2ffSMaksim Yevmenkin };
5046490c2ffSMaksim Yevmenkin 
505