]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bluetooth/bthidcontrol/sdp.c
MFC r326276:
[FreeBSD/FreeBSD.git] / usr.sbin / bluetooth / bthidcontrol / sdp.c
1 /*-
2  * sdp.c
3  *
4  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
5  *
6  * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * $Id: sdp.c,v 1.3 2004/02/17 22:14:57 max Exp $
31  * $FreeBSD$
32  */
33
34 #include <sys/queue.h>
35 #define L2CAP_SOCKET_CHECKED
36 #include <bluetooth.h>
37 #include <dev/usb/usb.h>
38 #include <dev/usb/usbhid.h>
39 #include <errno.h>
40 #include <sdp.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <usbhid.h>
44 #include "bthid_config.h"
45 #include "bthidcontrol.h"
46
47 static int32_t hid_sdp_query                            (bdaddr_t const *local, struct hid_device *hd, int32_t *error);
48 static int32_t hid_sdp_parse_protocol_descriptor_list   (sdp_attr_p a);
49 static int32_t hid_sdp_parse_hid_descriptor             (sdp_attr_p a);
50 static int32_t hid_sdp_parse_boolean                    (sdp_attr_p a);
51
52 static uint16_t         service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE;
53
54 static uint32_t         attrs[] = {
55 SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
56                 SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
57 SDP_ATTR_RANGE  (SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS,
58                 SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS),
59 SDP_ATTR_RANGE( 0x0205,         /* HIDReconnectInitiate */
60                 0x0205),
61 SDP_ATTR_RANGE( 0x0206,         /* HIDDescriptorList */
62                 0x0206),
63 SDP_ATTR_RANGE( 0x0209,         /* HIDBatteryPower */
64                 0x0209),
65 SDP_ATTR_RANGE( 0x020d,         /* HIDNormallyConnectable */
66                 0x020d)
67         };
68 #define nattrs  (sizeof(attrs)/sizeof(attrs[0]))
69
70 static sdp_attr_t       values[8];
71 #define nvalues (sizeof(values)/sizeof(values[0]))
72
73 static uint8_t          buffer[nvalues][512];
74
75 /*
76  * Query remote device
77  */
78
79 #undef  hid_sdp_query_exit
80 #define hid_sdp_query_exit(e) {         \
81         if (error != NULL)              \
82                 *error = (e);           \
83         if (ss != NULL) {               \
84                 sdp_close(ss);          \
85                 ss = NULL;              \
86         }                               \
87         return (((e) == 0)? 0 : -1);    \
88 }
89
90 static int32_t
91 hid_sdp_query(bdaddr_t const *local, struct hid_device *hd, int32_t *error)
92 {
93         void    *ss = NULL;
94         uint8_t *hid_descriptor = NULL;
95         int32_t  i, control_psm = -1, interrupt_psm = -1,
96                  reconnect_initiate = -1,
97                  normally_connectable = 0, battery_power = 0,
98                  hid_descriptor_length = -1;
99
100         if (local == NULL)
101                 local = NG_HCI_BDADDR_ANY;
102         if (hd == NULL)
103                 hid_sdp_query_exit(EINVAL);
104
105         for (i = 0; i < nvalues; i ++) {
106                 values[i].flags = SDP_ATTR_INVALID;
107                 values[i].attr = 0;
108                 values[i].vlen = sizeof(buffer[i]);
109                 values[i].value = buffer[i];
110         }
111
112         if ((ss = sdp_open(local, &hd->bdaddr)) == NULL)
113                 hid_sdp_query_exit(ENOMEM);
114         if (sdp_error(ss) != 0)
115                 hid_sdp_query_exit(sdp_error(ss));
116         if (sdp_search(ss, 1, &service, nattrs, attrs, nvalues, values) != 0)
117                 hid_sdp_query_exit(sdp_error(ss));
118
119         sdp_close(ss);
120         ss = NULL;
121
122         for (i = 0; i < nvalues; i ++) {
123                 if (values[i].flags != SDP_ATTR_OK)
124                         continue;
125
126                 switch (values[i].attr) {
127                 case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
128                         control_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
129                         break;
130
131                 case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS:
132                         interrupt_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
133                         break;
134
135                 case 0x0205: /* HIDReconnectInitiate */
136                         reconnect_initiate = hid_sdp_parse_boolean(&values[i]);
137                         break;
138
139                 case 0x0206: /* HIDDescriptorList */
140                         if (hid_sdp_parse_hid_descriptor(&values[i]) == 0) {
141                                 hid_descriptor = values[i].value;
142                                 hid_descriptor_length = values[i].vlen;
143                         }
144                         break;
145
146                 case 0x0209: /* HIDBatteryPower */
147                         battery_power = hid_sdp_parse_boolean(&values[i]);
148                         break;
149
150                 case 0x020d: /* HIDNormallyConnectable */
151                         normally_connectable = hid_sdp_parse_boolean(&values[i]);
152                         break;
153                 }
154         }
155
156         if (control_psm == -1 || interrupt_psm == -1 ||
157             reconnect_initiate == -1 ||
158             hid_descriptor == NULL || hid_descriptor_length == -1)
159                 hid_sdp_query_exit(ENOATTR);
160
161         hd->control_psm = control_psm;
162         hd->interrupt_psm = interrupt_psm;
163         hd->reconnect_initiate = reconnect_initiate? 1 : 0;
164         hd->battery_power = battery_power? 1 : 0;
165         hd->normally_connectable = normally_connectable? 1 : 0;
166         hd->desc = hid_use_report_desc(hid_descriptor, hid_descriptor_length);
167         if (hd->desc == NULL)
168                 hid_sdp_query_exit(ENOMEM);
169
170         return (0);
171 }
172
173 /*
174  * seq len                              2
175  *      seq len                         2
176  *              uuid value              3
177  *              uint16 value            3
178  *              seq len                 2
179  *                      uuid value      3
180  */
181
182 static int32_t
183 hid_sdp_parse_protocol_descriptor_list(sdp_attr_p a)
184 {
185         uint8_t *ptr = a->value;
186         uint8_t *end = a->value + a->vlen;
187         int32_t  type, len, uuid, psm;
188
189         if (end - ptr < 15)
190                 return (-1);
191
192         if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) {
193                 SDP_GET8(type, ptr);
194                 switch (type) {
195                 case SDP_DATA_SEQ8:
196                         SDP_GET8(len, ptr);
197                         break;
198
199                 case SDP_DATA_SEQ16:
200                         SDP_GET16(len, ptr);
201                         break;
202
203                 case SDP_DATA_SEQ32:
204                         SDP_GET32(len, ptr);
205                         break;
206
207                 default:
208                         return (-1);
209                 }
210                 if (ptr + len > end)
211                         return (-1);
212         }
213
214         SDP_GET8(type, ptr);
215         switch (type) {
216         case SDP_DATA_SEQ8:
217                 SDP_GET8(len, ptr);
218                 break;
219
220         case SDP_DATA_SEQ16:
221                 SDP_GET16(len, ptr);
222                 break;
223
224         case SDP_DATA_SEQ32:
225                 SDP_GET32(len, ptr);
226                 break;
227
228         default:
229                 return (-1);
230         }
231         if (ptr + len > end)
232                 return (-1);
233
234         /* Protocol */
235         SDP_GET8(type, ptr);
236         switch (type) {
237         case SDP_DATA_SEQ8:
238                 SDP_GET8(len, ptr);
239                 break;
240
241         case SDP_DATA_SEQ16:
242                 SDP_GET16(len, ptr);
243                 break;
244
245         case SDP_DATA_SEQ32:
246                 SDP_GET32(len, ptr);
247                 break;
248
249         default:
250                 return (-1);
251         }
252         if (ptr + len > end)
253                 return (-1);
254
255         /* UUID */
256         if (ptr + 3 > end)
257                 return (-1);
258         SDP_GET8(type, ptr);
259         switch (type) {
260         case SDP_DATA_UUID16:
261                 SDP_GET16(uuid, ptr);
262                 if (uuid != SDP_UUID_PROTOCOL_L2CAP)
263                         return (-1);
264                 break;
265
266         case SDP_DATA_UUID32:  /* XXX FIXME can we have 32-bit UUID */
267         case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
268         default:
269                 return (-1);
270         }
271
272         /* PSM */
273         if (ptr + 3 > end)
274                 return (-1);
275         SDP_GET8(type, ptr);
276         if (type != SDP_DATA_UINT16)
277                 return (-1);
278         SDP_GET16(psm, ptr);
279
280         return (psm);
281 }
282
283 /*
284  * seq len                      2
285  *      seq len                 2
286  *              uint8 value8    2
287  *              str value       3
288  */
289
290 static int32_t
291 hid_sdp_parse_hid_descriptor(sdp_attr_p a)
292 {
293         uint8_t *ptr = a->value;
294         uint8_t *end = a->value + a->vlen;
295         int32_t  type, len, descriptor_type;
296
297         if (end - ptr < 9)
298                 return (-1);
299
300         SDP_GET8(type, ptr);
301         switch (type) {
302         case SDP_DATA_SEQ8:
303                 SDP_GET8(len, ptr);
304                 break;
305
306         case SDP_DATA_SEQ16:
307                 SDP_GET16(len, ptr);
308                 break;
309
310         case SDP_DATA_SEQ32:
311                 SDP_GET32(len, ptr);
312                 break;
313
314         default:
315                 return (-1);
316         }
317         if (ptr + len > end)
318                 return (-1);
319
320         while (ptr < end) {
321                 /* Descriptor */
322                 SDP_GET8(type, ptr);
323                 switch (type) {
324                 case SDP_DATA_SEQ8:
325                         if (ptr + 1 > end)
326                                 return (-1);
327                         SDP_GET8(len, ptr);
328                         break;
329
330                 case SDP_DATA_SEQ16:
331                         if (ptr + 2 > end)
332                                 return (-1);
333                         SDP_GET16(len, ptr);
334                         break;
335
336                 case SDP_DATA_SEQ32:
337                         if (ptr + 4 > end)
338                                 return (-1);
339                         SDP_GET32(len, ptr);
340                         break;
341
342                 default:
343                         return (-1);
344                 }
345
346                 /* Descripor type */
347                 if (ptr + 1 > end)
348                         return (-1);
349                 SDP_GET8(type, ptr);
350                 if (type != SDP_DATA_UINT8 || ptr + 1 > end)
351                         return (-1);
352                 SDP_GET8(descriptor_type, ptr);
353
354                 /* Descriptor value */
355                 if (ptr + 1 > end)
356                         return (-1);
357                 SDP_GET8(type, ptr);
358                 switch (type) {
359                 case SDP_DATA_STR8:
360                         if (ptr + 1 > end)
361                                 return (-1);
362                         SDP_GET8(len, ptr);
363                         break;
364
365                 case SDP_DATA_STR16:
366                         if (ptr + 2 > end)
367                                 return (-1);
368                         SDP_GET16(len, ptr);
369                         break;
370
371                 case SDP_DATA_STR32:
372                         if (ptr + 4 > end)
373                                 return (-1);
374                         SDP_GET32(len, ptr);
375                         break;
376
377                 default:
378                         return (-1);
379                 }
380                 if (ptr + len > end)
381                         return (-1);
382
383                 if (descriptor_type == UDESC_REPORT && len > 0) {
384                         a->value = ptr;
385                         a->vlen = len;
386
387                         return (0);
388                 }
389
390                 ptr += len;
391         }
392
393         return (-1);
394 }
395
396 /* bool8 int8 */
397 static int32_t
398 hid_sdp_parse_boolean(sdp_attr_p a)
399 {
400         if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL)
401                 return (-1);
402
403         return (a->value[1]);
404 }
405
406 /* Perform SDP query */
407 static int32_t
408 hid_query(bdaddr_t *bdaddr, int argc, char **argv)
409 {
410         struct hid_device       hd;
411         int                     e;
412
413         memcpy(&hd.bdaddr, bdaddr, sizeof(hd.bdaddr));
414         if (hid_sdp_query(NULL, &hd, &e) < 0) {
415                 fprintf(stderr, "Could not perform SDP query on the " \
416                         "device %s. %s (%d)\n", bt_ntoa(bdaddr, NULL),
417                         strerror(e), e);
418                 return (FAILED);
419         }
420
421         print_hid_device(&hd, stdout);
422
423         return (OK);
424 }
425
426 struct bthid_command    sdp_commands[] =
427 {
428 {
429 "Query",
430 "Perform SDP query to the specified device and print HID configuration entry\n"\
431 "for the device. The configuration entry should be appended to the Bluetooth\n"\
432 "HID daemon configuration file and the daemon should be restarted.\n",
433 hid_query
434 },
435 { NULL, NULL, NULL }
436 };
437