sdp.c revision 1.4 1 /* $NetBSD: sdp.c,v 1.4 2007/08/17 17:59:16 pavel Exp $ */
2
3 /*-
4 * Copyright (c) 2006 Itronix Inc.
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 * 3. The name of Itronix Inc. may not be used to endorse
16 * or promote products derived from this software without specific
17 * prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31 /*
32 * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin (at) yahoo.com>
33 * All rights reserved.
34 *
35 * Redistribution and use in source and binary forms, with or without
36 * modification, are permitted provided that the following conditions
37 * are met:
38 * 1. Redistributions of source code must retain the above copyright
39 * notice, this list of conditions and the following disclaimer.
40 * 2. Redistributions in binary form must reproduce the above copyright
41 * notice, this list of conditions and the following disclaimer in the
42 * documentation and/or other materials provided with the distribution.
43 *
44 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
45 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
46 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
47 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
48 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
49 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
50 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
51 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
52 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
53 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
54 * SUCH DAMAGE.
55 */
56
57 #include <sys/cdefs.h>
58 __RCSID("$NetBSD: sdp.c,v 1.4 2007/08/17 17:59:16 pavel Exp $");
59
60 #include <sys/types.h>
61
62 #include <dev/bluetooth/btdev.h>
63 #include <dev/bluetooth/bthidev.h>
64 #include <dev/bluetooth/btsco.h>
65 #include <dev/usb/usb.h>
66 #include <dev/usb/usbhid.h>
67
68 #include <prop/proplib.h>
69
70 #include <bluetooth.h>
71 #include <err.h>
72 #include <errno.h>
73 #include <sdp.h>
74 #include <stdlib.h>
75 #include <usbhid.h>
76
77 #include "btdevctl.h"
78
79 static int32_t parse_l2cap_psm(sdp_attr_t *);
80 static int32_t parse_rfcomm_channel(sdp_attr_t *);
81 static int32_t parse_hid_descriptor(sdp_attr_t *);
82 static int32_t parse_boolean(sdp_attr_t *);
83
84 static int config_hid(prop_dictionary_t);
85 static int config_hset(prop_dictionary_t);
86 static int config_hf(prop_dictionary_t);
87
88 uint16_t hid_services[] = {
89 SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE
90 };
91
92 uint32_t hid_attrs[] = {
93 SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
94 SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
95 SDP_ATTR_RANGE( SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS,
96 SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS),
97 SDP_ATTR_RANGE( 0x0205, /* HIDReconnectInitiate */
98 0x0206), /* HIDDescriptorList */
99 SDP_ATTR_RANGE( 0x0209, /* HIDBatteryPower */
100 0x0209),
101 SDP_ATTR_RANGE( 0x020d, /* HIDNormallyConnectable */
102 0x020d)
103 };
104
105 uint16_t hset_services[] = {
106 SDP_SERVICE_CLASS_HEADSET
107 };
108
109 uint32_t hset_attrs[] = {
110 SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
111 SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
112 };
113
114 uint16_t hf_services[] = {
115 SDP_SERVICE_CLASS_HANDSFREE_AUDIO_GATEWAY
116 };
117
118 uint32_t hf_attrs[] = {
119 SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
120 SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
121 };
122
123 #define NUM(v) (sizeof(v) / sizeof(v[0]))
124
125 static struct {
126 const char *name;
127 int (*handler)(prop_dictionary_t);
128 const char *description;
129 uint16_t *services;
130 int nservices;
131 uint32_t *attrs;
132 int nattrs;
133 } cfgtype[] = {
134 {
135 "HID", config_hid, "Human Interface Device",
136 hid_services, NUM(hid_services),
137 hid_attrs, NUM(hid_attrs),
138 },
139 {
140 "HSET", config_hset, "Headset",
141 hset_services, NUM(hset_services),
142 hset_attrs, NUM(hset_attrs),
143 },
144 {
145 "HF", config_hf, "Handsfree",
146 hf_services, NUM(hf_services),
147 hf_attrs, NUM(hf_attrs),
148 },
149 };
150
151 static sdp_attr_t values[8];
152 static uint8_t buffer[NUM(values)][512];
153
154 prop_dictionary_t
155 cfg_query(bdaddr_t *laddr, bdaddr_t *raddr, const char *service)
156 {
157 prop_dictionary_t dict;
158 void *ss;
159 int rv, i;
160
161 dict = prop_dictionary_create();
162 if (dict == NULL)
163 return NULL;
164
165 for (i = 0 ; i < NUM(values) ; i++) {
166 values[i].flags = SDP_ATTR_INVALID;
167 values[i].attr = 0;
168 values[i].vlen = sizeof(buffer[i]);
169 values[i].value = buffer[i];
170 }
171
172 for (i = 0 ; i < NUM(cfgtype) ; i++) {
173 if (strcasecmp(service, cfgtype[i].name) == 0) {
174 ss = sdp_open(laddr, raddr);
175
176 if (ss == NULL || (errno = sdp_error(ss)) != 0)
177 return NULL;
178
179 rv = sdp_search(ss,
180 cfgtype[i].nservices, cfgtype[i].services,
181 cfgtype[i].nattrs, cfgtype[i].attrs,
182 NUM(values), values);
183
184 if (rv != 0) {
185 errno = sdp_error(ss);
186 return NULL;
187 }
188 sdp_close(ss);
189
190 rv = (*cfgtype[i].handler)(dict);
191 if (rv != 0)
192 return NULL;
193
194 return dict;
195 }
196 }
197
198 printf("Known config types:\n");
199 for (i = 0 ; i < NUM(cfgtype) ; i++)
200 printf("\t%s\t%s\n", cfgtype[i].name, cfgtype[i].description);
201
202 exit(EXIT_FAILURE);
203 }
204
205 /*
206 * Configure HID results
207 */
208 static int
209 config_hid(prop_dictionary_t dict)
210 {
211 prop_object_t obj;
212 int32_t control_psm, interrupt_psm,
213 reconnect_initiate, battery_power,
214 normally_connectable, hid_length;
215 uint8_t *hid_descriptor;
216 const char *mode;
217 int i;
218
219 control_psm = -1;
220 interrupt_psm = -1;
221 reconnect_initiate = -1;
222 normally_connectable = 0;
223 battery_power = 0;
224 hid_descriptor = NULL;
225 hid_length = -1;
226
227 for (i = 0; i < NUM(values) ; i++) {
228 if (values[i].flags != SDP_ATTR_OK)
229 continue;
230
231 switch (values[i].attr) {
232 case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
233 control_psm = parse_l2cap_psm(&values[i]);
234 break;
235
236 case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS:
237 interrupt_psm = parse_l2cap_psm(&values[i]);
238 break;
239
240 case 0x0205: /* HIDReconnectInitiate */
241 reconnect_initiate = parse_boolean(&values[i]);
242 break;
243
244 case 0x0206: /* HIDDescriptorList */
245 if (parse_hid_descriptor(&values[i]) == 0) {
246 hid_descriptor = values[i].value;
247 hid_length = values[i].vlen;
248 }
249 break;
250
251 case 0x0209: /* HIDBatteryPower */
252 battery_power = parse_boolean(&values[i]);
253 break;
254
255 case 0x020d: /* HIDNormallyConnectable */
256 normally_connectable = parse_boolean(&values[i]);
257 break;
258 }
259 }
260
261 if (control_psm == -1
262 || interrupt_psm == -1
263 || reconnect_initiate == -1
264 || hid_descriptor == NULL
265 || hid_length == -1)
266 return ENOATTR;
267
268 obj = prop_string_create_cstring_nocopy("bthidev");
269 if (obj == NULL || !prop_dictionary_set(dict, BTDEVtype, obj))
270 return errno;
271
272 prop_object_release(obj);
273
274 obj = prop_number_create_integer(control_psm);
275 if (obj == NULL || !prop_dictionary_set(dict, BTHIDEVcontrolpsm, obj))
276 return errno;
277
278 prop_object_release(obj);
279
280 obj = prop_number_create_integer(interrupt_psm);
281 if (obj == NULL || !prop_dictionary_set(dict, BTHIDEVinterruptpsm, obj))
282 return errno;
283
284 prop_object_release(obj);
285
286 obj = prop_data_create_data(hid_descriptor, hid_length);
287 if (obj == NULL || !prop_dictionary_set(dict, BTHIDEVdescriptor, obj))
288 return errno;
289
290 mode = hid_mode(obj);
291 prop_object_release(obj);
292
293 obj = prop_string_create_cstring_nocopy(mode);
294 if (obj == NULL || !prop_dictionary_set(dict, BTDEVmode, obj))
295 return errno;
296
297 prop_object_release(obj);
298
299 if (!reconnect_initiate) {
300 obj = prop_bool_create(true);
301 if (obj == NULL || !prop_dictionary_set(dict, BTHIDEVreconnect, obj))
302 return errno;
303
304 prop_object_release(obj);
305 }
306
307 return 0;
308 }
309
310 /*
311 * Configure HSET results
312 */
313 static int
314 config_hset(prop_dictionary_t dict)
315 {
316 prop_object_t obj;
317 uint32_t channel;
318 int i;
319
320 channel = -1;
321
322 for (i = 0; i < NUM(values) ; i++) {
323 if (values[i].flags != SDP_ATTR_OK)
324 continue;
325
326 switch (values[i].attr) {
327 case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
328 channel = parse_rfcomm_channel(&values[i]);
329 break;
330 }
331 }
332
333 if (channel == -1)
334 return ENOATTR;
335
336 obj = prop_string_create_cstring_nocopy("btsco");
337 if (obj == NULL || !prop_dictionary_set(dict, BTDEVtype, obj))
338 return errno;
339
340 prop_object_release(obj);
341
342 obj = prop_number_create_integer(channel);
343 if (obj == NULL || !prop_dictionary_set(dict, BTSCOchannel, obj))
344 return errno;
345
346 prop_object_release(obj);
347
348 return 0;
349 }
350
351 /*
352 * Configure HF results
353 */
354 static int
355 config_hf(prop_dictionary_t dict)
356 {
357 prop_object_t obj;
358 uint32_t channel;
359 int i;
360
361 channel = -1;
362
363 for (i = 0 ; i < NUM(values) ; i++) {
364 if (values[i].flags != SDP_ATTR_OK)
365 continue;
366
367 switch (values[i].attr) {
368 case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
369 channel = parse_rfcomm_channel(&values[i]);
370 break;
371 }
372 }
373
374 if (channel == -1)
375 return ENOATTR;
376
377 obj = prop_string_create_cstring_nocopy("btsco");
378 if (obj == NULL || !prop_dictionary_set(dict, BTDEVtype, obj))
379 return errno;
380
381 prop_object_release(obj);
382
383 obj = prop_bool_create(true);
384 if (obj == NULL || !prop_dictionary_set(dict, BTSCOlisten, obj))
385 return errno;
386
387 prop_object_release(obj);
388
389 obj = prop_number_create_integer(channel);
390 if (obj == NULL || !prop_dictionary_set(dict, BTSCOchannel, obj))
391 return errno;
392
393 prop_object_release(obj);
394
395 return 0;
396 }
397
398 /*
399 * Parse [additional] protocol descriptor list for L2CAP PSM
400 *
401 * seq8 len8 2
402 * seq8 len8 2
403 * uuid16 value16 3 L2CAP
404 * uint16 value16 3 PSM
405 * seq8 len8 2
406 * uuid16 value16 3 HID Protocol
407 * ===
408 * 15
409 */
410
411 static int32_t
412 parse_l2cap_psm(sdp_attr_t *a)
413 {
414 uint8_t *ptr = a->value;
415 uint8_t *end = a->value + a->vlen;
416 int32_t type, len, uuid, psm;
417
418 if (end - ptr < 15)
419 return (-1);
420
421 if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) {
422 SDP_GET8(type, ptr);
423 switch (type) {
424 case SDP_DATA_SEQ8:
425 SDP_GET8(len, ptr);
426 break;
427
428 case SDP_DATA_SEQ16:
429 SDP_GET16(len, ptr);
430 break;
431
432 case SDP_DATA_SEQ32:
433 SDP_GET32(len, ptr);
434 break;
435
436 default:
437 return (-1);
438 }
439 if (ptr + len > end)
440 return (-1);
441 }
442
443 SDP_GET8(type, ptr);
444 switch (type) {
445 case SDP_DATA_SEQ8:
446 SDP_GET8(len, ptr);
447 break;
448
449 case SDP_DATA_SEQ16:
450 SDP_GET16(len, ptr);
451 break;
452
453 case SDP_DATA_SEQ32:
454 SDP_GET32(len, ptr);
455 break;
456
457 default:
458 return (-1);
459 }
460 if (ptr + len > end)
461 return (-1);
462
463 /* Protocol */
464 SDP_GET8(type, ptr);
465 switch (type) {
466 case SDP_DATA_SEQ8:
467 SDP_GET8(len, ptr);
468 break;
469
470 case SDP_DATA_SEQ16:
471 SDP_GET16(len, ptr);
472 break;
473
474 case SDP_DATA_SEQ32:
475 SDP_GET32(len, ptr);
476 break;
477
478 default:
479 return (-1);
480 }
481 if (ptr + len > end)
482 return (-1);
483
484 /* UUID */
485 if (ptr + 3 > end)
486 return (-1);
487 SDP_GET8(type, ptr);
488 switch (type) {
489 case SDP_DATA_UUID16:
490 SDP_GET16(uuid, ptr);
491 if (uuid != SDP_UUID_PROTOCOL_L2CAP)
492 return (-1);
493 break;
494
495 case SDP_DATA_UUID32: /* XXX FIXME can we have 32-bit UUID */
496 case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
497 default:
498 return (-1);
499 }
500
501 /* PSM */
502 if (ptr + 3 > end)
503 return (-1);
504 SDP_GET8(type, ptr);
505 if (type != SDP_DATA_UINT16)
506 return (-1);
507 SDP_GET16(psm, ptr);
508
509 return (psm);
510 }
511
512 /*
513 * Parse HID descriptor string
514 *
515 * seq8 len8 2
516 * seq8 len8 2
517 * uint8 value8 2
518 * str value 3
519 * ===
520 * 9
521 */
522
523 static int32_t
524 parse_hid_descriptor(sdp_attr_t *a)
525 {
526 uint8_t *ptr = a->value;
527 uint8_t *end = a->value + a->vlen;
528 int32_t type, len, descriptor_type;
529
530 if (end - ptr < 9)
531 return (-1);
532
533 SDP_GET8(type, ptr);
534 switch (type) {
535 case SDP_DATA_SEQ8:
536 SDP_GET8(len, ptr);
537 break;
538
539 case SDP_DATA_SEQ16:
540 SDP_GET16(len, ptr);
541 break;
542
543 case SDP_DATA_SEQ32:
544 SDP_GET32(len, ptr);
545 break;
546
547 default:
548 return (-1);
549 }
550 if (ptr + len > end)
551 return (-1);
552
553 while (ptr < end) {
554 /* Descriptor */
555 SDP_GET8(type, ptr);
556 switch (type) {
557 case SDP_DATA_SEQ8:
558 if (ptr + 1 > end)
559 return (-1);
560 SDP_GET8(len, ptr);
561 break;
562
563 case SDP_DATA_SEQ16:
564 if (ptr + 2 > end)
565 return (-1);
566 SDP_GET16(len, ptr);
567 break;
568
569 case SDP_DATA_SEQ32:
570 if (ptr + 4 > end)
571 return (-1);
572 SDP_GET32(len, ptr);
573 break;
574
575 default:
576 return (-1);
577 }
578
579 /* Descripor type */
580 if (ptr + 1 > end)
581 return (-1);
582 SDP_GET8(type, ptr);
583 if (type != SDP_DATA_UINT8 || ptr + 1 > end)
584 return (-1);
585 SDP_GET8(descriptor_type, ptr);
586
587 /* Descriptor value */
588 if (ptr + 1 > end)
589 return (-1);
590 SDP_GET8(type, ptr);
591 switch (type) {
592 case SDP_DATA_STR8:
593 if (ptr + 1 > end)
594 return (-1);
595 SDP_GET8(len, ptr);
596 break;
597
598 case SDP_DATA_STR16:
599 if (ptr + 2 > end)
600 return (-1);
601 SDP_GET16(len, ptr);
602 break;
603
604 case SDP_DATA_STR32:
605 if (ptr + 4 > end)
606 return (-1);
607 SDP_GET32(len, ptr);
608 break;
609
610 default:
611 return (-1);
612 }
613 if (ptr + len > end)
614 return (-1);
615
616 if (descriptor_type == UDESC_REPORT && len > 0) {
617 a->value = ptr;
618 a->vlen = len;
619
620 return (0);
621 }
622
623 ptr += len;
624 }
625
626 return (-1);
627 }
628
629 /*
630 * Parse boolean value
631 *
632 * bool8 int8
633 */
634
635 static int32_t
636 parse_boolean(sdp_attr_t *a)
637 {
638 if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL)
639 return (-1);
640
641 return (a->value[1]);
642 }
643
644 /*
645 * Parse protocol descriptor list for the RFCOMM channel
646 *
647 * seq8 len8 2
648 * seq8 len8 2
649 * uuid16 value16 3 L2CAP
650 * seq8 len8 2
651 * uuid16 value16 3 RFCOMM
652 * uint8 value8 2 channel
653 * ===
654 * 14
655 */
656
657 static int32_t
658 parse_rfcomm_channel(sdp_attr_t *a)
659 {
660 uint8_t *ptr = a->value;
661 uint8_t *end = a->value + a->vlen;
662 int32_t type, len, uuid, channel;
663
664 if (end - ptr < 14)
665 return (-1);
666
667 SDP_GET8(type, ptr);
668 switch (type) {
669 case SDP_DATA_SEQ8:
670 SDP_GET8(len, ptr);
671 break;
672
673 case SDP_DATA_SEQ16:
674 SDP_GET16(len, ptr);
675 break;
676
677 case SDP_DATA_SEQ32:
678 SDP_GET32(len, ptr);
679 break;
680
681 default:
682 return (-1);
683 }
684 if (ptr + len > end)
685 return (-1);
686
687 /* Protocol */
688 SDP_GET8(type, ptr);
689 switch (type) {
690 case SDP_DATA_SEQ8:
691 SDP_GET8(len, ptr);
692 break;
693
694 case SDP_DATA_SEQ16:
695 SDP_GET16(len, ptr);
696 break;
697
698 case SDP_DATA_SEQ32:
699 SDP_GET32(len, ptr);
700 break;
701
702 default:
703 return (-1);
704 }
705 if (ptr + len > end)
706 return (-1);
707
708 /* UUID */
709 if (ptr + 3 > end)
710 return (-1);
711 SDP_GET8(type, ptr);
712 switch (type) {
713 case SDP_DATA_UUID16:
714 SDP_GET16(uuid, ptr);
715 if (uuid != SDP_UUID_PROTOCOL_L2CAP)
716 return (-1);
717 break;
718
719 case SDP_DATA_UUID32: /* XXX FIXME can we have 32-bit UUID */
720 case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
721 default:
722 return (-1);
723 }
724
725 /* Protocol */
726 SDP_GET8(type, ptr);
727 switch (type) {
728 case SDP_DATA_SEQ8:
729 SDP_GET8(len, ptr);
730 break;
731
732 case SDP_DATA_SEQ16:
733 SDP_GET16(len, ptr);
734 break;
735
736 case SDP_DATA_SEQ32:
737 SDP_GET32(len, ptr);
738 break;
739
740 default:
741 return (-1);
742 }
743 if (ptr + len > end)
744 return (-1);
745
746 /* UUID */
747 if (ptr + 3 > end)
748 return (-1);
749 SDP_GET8(type, ptr);
750 switch (type) {
751 case SDP_DATA_UUID16:
752 SDP_GET16(uuid, ptr);
753 if (uuid != SDP_UUID_PROTOCOL_RFCOMM)
754 return (-1);
755 break;
756
757 case SDP_DATA_UUID32: /* XXX FIXME can we have 32-bit UUID */
758 case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
759 default:
760 return (-1);
761 }
762
763 /* channel */
764 if (ptr + 2 > end)
765 return (-1);
766
767 SDP_GET8(type, ptr);
768 if (type != SDP_DATA_UINT8)
769 return (-1);
770
771 SDP_GET8(channel, ptr);
772
773 return (channel);
774 }
775
776 /*
777 * return appropriate mode for HID descriptor
778 */
779 const char *
780 hid_mode(prop_data_t desc)
781 {
782 report_desc_t r;
783 hid_data_t d;
784 struct hid_item h;
785 const char *mode;
786
787 hid_init(NULL);
788
789 mode = BTDEVauth; /* default */
790
791 r = hid_use_report_desc(prop_data_data_nocopy(desc),
792 prop_data_size(desc));
793 if (r == NULL)
794 err(EXIT_FAILURE, "hid_use_report_desc");
795
796 d = hid_start_parse(r, ~0, -1);
797 while (hid_get_item(d, &h) > 0) {
798 if (h.kind == hid_collection
799 && HID_PAGE(h.usage) == HUP_GENERIC_DESKTOP
800 && HID_USAGE(h.usage) == HUG_KEYBOARD)
801 mode = BTDEVencrypt;
802 }
803
804 hid_end_parse(d);
805 hid_dispose_report_desc(r);
806
807 return mode;
808 }
809