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