xref: /freebsd/usr.sbin/bluetooth/bthidcontrol/sdp.c (revision 63d1fd5970ec814904aa0f4580b10a0d302d08b2)
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 #define L2CAP_SOCKET_CHECKED
34 #include <bluetooth.h>
35 #include <dev/usb/usb.h>
36 #include <dev/usb/usbhid.h>
37 #include <errno.h>
38 #include <sdp.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <usbhid.h>
42 #include "bthid_config.h"
43 #include "bthidcontrol.h"
44 
45 static int32_t hid_sdp_query				(bdaddr_t const *local, struct hid_device *hd, int32_t *error);
46 static int32_t hid_sdp_parse_protocol_descriptor_list	(sdp_attr_p a);
47 static int32_t hid_sdp_parse_hid_descriptor		(sdp_attr_p a);
48 static int32_t hid_sdp_parse_boolean			(sdp_attr_p a);
49 
50 static uint16_t		service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE;
51 
52 static uint32_t		attrs[] = {
53 SDP_ATTR_RANGE(	SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
54 		SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
55 SDP_ATTR_RANGE	(SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS,
56 		SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS),
57 SDP_ATTR_RANGE(	0x0205,		/* HIDReconnectInitiate */
58 		0x0205),
59 SDP_ATTR_RANGE(	0x0206,		/* HIDDescriptorList */
60 		0x0206),
61 SDP_ATTR_RANGE(	0x0209,		/* HIDBatteryPower */
62 		0x0209),
63 SDP_ATTR_RANGE(	0x020d,		/* HIDNormallyConnectable */
64 		0x020d)
65 	};
66 #define	nattrs	(sizeof(attrs)/sizeof(attrs[0]))
67 
68 static sdp_attr_t	values[8];
69 #define	nvalues	(sizeof(values)/sizeof(values[0]))
70 
71 static uint8_t		buffer[nvalues][512];
72 
73 /*
74  * Query remote device
75  */
76 
77 #undef	hid_sdp_query_exit
78 #define	hid_sdp_query_exit(e) {		\
79 	if (error != NULL)		\
80 		*error = (e);		\
81 	if (ss != NULL) {		\
82 		sdp_close(ss);		\
83 		ss = NULL;		\
84 	}				\
85 	return (((e) == 0)? 0 : -1);	\
86 }
87 
88 static int32_t
89 hid_sdp_query(bdaddr_t const *local, struct hid_device *hd, int32_t *error)
90 {
91 	void	*ss = NULL;
92 	uint8_t	*hid_descriptor = NULL;
93 	int32_t	 i, control_psm = -1, interrupt_psm = -1,
94 		 reconnect_initiate = -1,
95 		 normally_connectable = 0, battery_power = 0,
96 		 hid_descriptor_length = -1;
97 
98 	if (local == NULL)
99 		local = NG_HCI_BDADDR_ANY;
100 	if (hd == NULL)
101 		hid_sdp_query_exit(EINVAL);
102 
103 	for (i = 0; i < nvalues; i ++) {
104 		values[i].flags = SDP_ATTR_INVALID;
105 		values[i].attr = 0;
106 		values[i].vlen = sizeof(buffer[i]);
107 		values[i].value = buffer[i];
108 	}
109 
110 	if ((ss = sdp_open(local, &hd->bdaddr)) == NULL)
111 		hid_sdp_query_exit(ENOMEM);
112 	if (sdp_error(ss) != 0)
113 		hid_sdp_query_exit(sdp_error(ss));
114 	if (sdp_search(ss, 1, &service, nattrs, attrs, nvalues, values) != 0)
115                 hid_sdp_query_exit(sdp_error(ss));
116 
117         sdp_close(ss);
118         ss = NULL;
119 
120 	for (i = 0; i < nvalues; i ++) {
121 		if (values[i].flags != SDP_ATTR_OK)
122 			continue;
123 
124 		switch (values[i].attr) {
125 		case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
126 			control_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
127 			break;
128 
129 		case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS:
130 			interrupt_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
131 			break;
132 
133 		case 0x0205: /* HIDReconnectInitiate */
134 			reconnect_initiate = hid_sdp_parse_boolean(&values[i]);
135 			break;
136 
137 		case 0x0206: /* HIDDescriptorList */
138 			if (hid_sdp_parse_hid_descriptor(&values[i]) == 0) {
139 				hid_descriptor = values[i].value;
140 				hid_descriptor_length = values[i].vlen;
141 			}
142 			break;
143 
144 		case 0x0209: /* HIDBatteryPower */
145 			battery_power = hid_sdp_parse_boolean(&values[i]);
146 			break;
147 
148 		case 0x020d: /* HIDNormallyConnectable */
149 			normally_connectable = hid_sdp_parse_boolean(&values[i]);
150 			break;
151 		}
152 	}
153 
154 	if (control_psm == -1 || interrupt_psm == -1 ||
155 	    reconnect_initiate == -1 ||
156 	    hid_descriptor == NULL || hid_descriptor_length == -1)
157 		hid_sdp_query_exit(ENOATTR);
158 
159 	hd->control_psm = control_psm;
160 	hd->interrupt_psm = interrupt_psm;
161 	hd->reconnect_initiate = reconnect_initiate? 1 : 0;
162 	hd->battery_power = battery_power? 1 : 0;
163 	hd->normally_connectable = normally_connectable? 1 : 0;
164 	hd->desc = hid_use_report_desc(hid_descriptor, hid_descriptor_length);
165 	if (hd->desc == NULL)
166 		hid_sdp_query_exit(ENOMEM);
167 
168 	return (0);
169 }
170 
171 /*
172  * seq len				2
173  *	seq len				2
174  *		uuid value		3
175  *		uint16 value		3
176  *		seq len			2
177  *			uuid value	3
178  */
179 
180 static int32_t
181 hid_sdp_parse_protocol_descriptor_list(sdp_attr_p a)
182 {
183 	uint8_t	*ptr = a->value;
184 	uint8_t	*end = a->value + a->vlen;
185 	int32_t	 type, len, uuid, psm;
186 
187 	if (end - ptr < 15)
188 		return (-1);
189 
190 	if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) {
191 		SDP_GET8(type, ptr);
192 		switch (type) {
193 		case SDP_DATA_SEQ8:
194 			SDP_GET8(len, ptr);
195 			break;
196 
197 		case SDP_DATA_SEQ16:
198 			SDP_GET16(len, ptr);
199 			break;
200 
201 		case SDP_DATA_SEQ32:
202 			SDP_GET32(len, ptr);
203 			break;
204 
205 		default:
206 			return (-1);
207 		}
208 		if (ptr + len > end)
209 			return (-1);
210 	}
211 
212 	SDP_GET8(type, ptr);
213 	switch (type) {
214 	case SDP_DATA_SEQ8:
215 		SDP_GET8(len, ptr);
216 		break;
217 
218 	case SDP_DATA_SEQ16:
219 		SDP_GET16(len, ptr);
220 		break;
221 
222 	case SDP_DATA_SEQ32:
223 		SDP_GET32(len, ptr);
224 		break;
225 
226 	default:
227 		return (-1);
228 	}
229 	if (ptr + len > end)
230 		return (-1);
231 
232 	/* Protocol */
233 	SDP_GET8(type, ptr);
234 	switch (type) {
235 	case SDP_DATA_SEQ8:
236 		SDP_GET8(len, ptr);
237 		break;
238 
239 	case SDP_DATA_SEQ16:
240 		SDP_GET16(len, ptr);
241 		break;
242 
243 	case SDP_DATA_SEQ32:
244 		SDP_GET32(len, ptr);
245 		break;
246 
247 	default:
248 		return (-1);
249 	}
250 	if (ptr + len > end)
251 		return (-1);
252 
253 	/* UUID */
254 	if (ptr + 3 > end)
255 		return (-1);
256 	SDP_GET8(type, ptr);
257 	switch (type) {
258 	case SDP_DATA_UUID16:
259 		SDP_GET16(uuid, ptr);
260 		if (uuid != SDP_UUID_PROTOCOL_L2CAP)
261 			return (-1);
262 		break;
263 
264 	case SDP_DATA_UUID32:  /* XXX FIXME can we have 32-bit UUID */
265 	case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
266 	default:
267 		return (-1);
268 	}
269 
270 	/* PSM */
271 	if (ptr + 3 > end)
272 		return (-1);
273 	SDP_GET8(type, ptr);
274 	if (type != SDP_DATA_UINT16)
275 		return (-1);
276 	SDP_GET16(psm, ptr);
277 
278 	return (psm);
279 }
280 
281 /*
282  * seq len			2
283  *	seq len			2
284  *		uint8 value8	2
285  * 		str value	3
286  */
287 
288 static int32_t
289 hid_sdp_parse_hid_descriptor(sdp_attr_p a)
290 {
291 	uint8_t	*ptr = a->value;
292 	uint8_t	*end = a->value + a->vlen;
293 	int32_t	 type, len, descriptor_type;
294 
295 	if (end - ptr < 9)
296 		return (-1);
297 
298 	SDP_GET8(type, ptr);
299 	switch (type) {
300 	case SDP_DATA_SEQ8:
301 		SDP_GET8(len, ptr);
302 		break;
303 
304 	case SDP_DATA_SEQ16:
305 		SDP_GET16(len, ptr);
306 		break;
307 
308 	case SDP_DATA_SEQ32:
309 		SDP_GET32(len, ptr);
310 		break;
311 
312 	default:
313 		return (-1);
314 	}
315 	if (ptr + len > end)
316 		return (-1);
317 
318 	while (ptr < end) {
319 		/* Descriptor */
320 		SDP_GET8(type, ptr);
321 		switch (type) {
322 		case SDP_DATA_SEQ8:
323 			if (ptr + 1 > end)
324 				return (-1);
325 			SDP_GET8(len, ptr);
326 			break;
327 
328 		case SDP_DATA_SEQ16:
329 			if (ptr + 2 > end)
330 				return (-1);
331 			SDP_GET16(len, ptr);
332 			break;
333 
334 		case SDP_DATA_SEQ32:
335 			if (ptr + 4 > end)
336 				return (-1);
337 			SDP_GET32(len, ptr);
338 			break;
339 
340 		default:
341 			return (-1);
342 		}
343 
344 		/* Descripor type */
345 		if (ptr + 1 > end)
346 			return (-1);
347 		SDP_GET8(type, ptr);
348 		if (type != SDP_DATA_UINT8 || ptr + 1 > end)
349 			return (-1);
350 		SDP_GET8(descriptor_type, ptr);
351 
352 		/* Descriptor value */
353 		if (ptr + 1 > end)
354 			return (-1);
355 		SDP_GET8(type, ptr);
356 		switch (type) {
357 		case SDP_DATA_STR8:
358 			if (ptr + 1 > end)
359 				return (-1);
360 			SDP_GET8(len, ptr);
361 			break;
362 
363 		case SDP_DATA_STR16:
364 			if (ptr + 2 > end)
365 				return (-1);
366 			SDP_GET16(len, ptr);
367 			break;
368 
369 		case SDP_DATA_STR32:
370 			if (ptr + 4 > end)
371 				return (-1);
372 			SDP_GET32(len, ptr);
373 			break;
374 
375 		default:
376 			return (-1);
377 		}
378 		if (ptr + len > end)
379 			return (-1);
380 
381 		if (descriptor_type == UDESC_REPORT && len > 0) {
382 			a->value = ptr;
383 			a->vlen = len;
384 
385 			return (0);
386 		}
387 
388 		ptr += len;
389 	}
390 
391 	return (-1);
392 }
393 
394 /* bool8 int8 */
395 static int32_t
396 hid_sdp_parse_boolean(sdp_attr_p a)
397 {
398 	if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL)
399 		return (-1);
400 
401 	return (a->value[1]);
402 }
403 
404 /* Perform SDP query */
405 static int32_t
406 hid_query(bdaddr_t *bdaddr, int argc, char **argv)
407 {
408 	struct hid_device	hd;
409 	int			e;
410 
411 	memcpy(&hd.bdaddr, bdaddr, sizeof(hd.bdaddr));
412 	if (hid_sdp_query(NULL, &hd, &e) < 0) {
413 		fprintf(stderr, "Could not perform SDP query on the " \
414 			"device %s. %s (%d)\n", bt_ntoa(bdaddr, NULL),
415 			strerror(e), e);
416 		return (FAILED);
417 	}
418 
419 	print_hid_device(&hd, stdout);
420 
421 	return (OK);
422 }
423 
424 struct bthid_command	sdp_commands[] =
425 {
426 {
427 "Query",
428 "Perform SDP query to the specified device and print HID configuration entry\n"\
429 "for the device. The configuration entry should be appended to the Bluetooth\n"\
430 "HID daemon configuration file and the daemon should be restarted.\n",
431 hid_query
432 },
433 { NULL, NULL, NULL }
434 };
435 
436