xref: /freebsd/usr.sbin/bluetooth/bthidcontrol/sdp.c (revision 57718be8fa0bd5edc11ab9a72e68cc71982939a6)
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 
44 static int32_t hid_sdp_query				(bdaddr_t const *local, struct hid_device *hd, int32_t *error);
45 static int32_t hid_sdp_parse_protocol_descriptor_list	(sdp_attr_p a);
46 static int32_t hid_sdp_parse_hid_descriptor		(sdp_attr_p a);
47 static int32_t hid_sdp_parse_boolean			(sdp_attr_p a);
48 
49 static uint16_t		service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE;
50 
51 static uint32_t		attrs[] = {
52 SDP_ATTR_RANGE(	SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
53 		SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
54 SDP_ATTR_RANGE	(SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS,
55 		SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS),
56 SDP_ATTR_RANGE(	0x0205,		/* HIDReconnectInitiate */
57 		0x0205),
58 SDP_ATTR_RANGE(	0x0206,		/* HIDDescriptorList */
59 		0x0206),
60 SDP_ATTR_RANGE(	0x0209,		/* HIDBatteryPower */
61 		0x0209),
62 SDP_ATTR_RANGE(	0x020d,		/* HIDNormallyConnectable */
63 		0x020d)
64 	};
65 #define	nattrs	(sizeof(attrs)/sizeof(attrs[0]))
66 
67 static sdp_attr_t	values[8];
68 #define	nvalues	(sizeof(values)/sizeof(values[0]))
69 
70 static 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 
87 static int32_t
88 hid_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 
179 static int32_t
180 hid_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 
287 static int32_t
288 hid_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 */
394 static int32_t
395 hid_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 */
404 static int32_t
405 hid_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 
423 struct 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",
430 hid_query
431 },
432 { NULL, NULL, NULL }
433 };
434 
435