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