sdp.c revision 1.6 1 /* $NetBSD: sdp.c,v 1.6 2009/04/15 00:35:04 lukem 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.6 2009/04/15 00:35:04 lukem 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 static struct {
124 const char *name;
125 int (*handler)(prop_dictionary_t);
126 const char *description;
127 uint16_t *services;
128 int nservices;
129 uint32_t *attrs;
130 int nattrs;
131 } cfgtype[] = {
132 {
133 "HID", config_hid, "Human Interface Device",
134 hid_services, __arraycount(hid_services),
135 hid_attrs, __arraycount(hid_attrs),
136 },
137 {
138 "HSET", config_hset, "Headset",
139 hset_services, __arraycount(hset_services),
140 hset_attrs, __arraycount(hset_attrs),
141 },
142 {
143 "HF", config_hf, "Handsfree",
144 hf_services, __arraycount(hf_services),
145 hf_attrs, __arraycount(hf_attrs),
146 },
147 };
148
149 static sdp_attr_t values[8];
150 static uint8_t buffer[__arraycount(values)][512];
151
152 prop_dictionary_t
153 cfg_query(bdaddr_t *laddr, bdaddr_t *raddr, const char *service)
154 {
155 prop_dictionary_t dict;
156 void *ss;
157 int rv;
158 size_t i;
159
160 dict = prop_dictionary_create();
161 if (dict == NULL)
162 return NULL;
163
164 for (i = 0; i < __arraycount(values); i++) {
165 values[i].flags = SDP_ATTR_INVALID;
166 values[i].attr = 0;
167 values[i].vlen = sizeof(buffer[i]);
168 values[i].value = buffer[i];
169 }
170
171 for (i = 0; i < __arraycount(cfgtype); i++) {
172 if (strcasecmp(service, cfgtype[i].name) == 0) {
173 ss = sdp_open(laddr, raddr);
174
175 if (ss == NULL || (errno = sdp_error(ss)) != 0)
176 return NULL;
177
178 rv = sdp_search(ss,
179 cfgtype[i].nservices, cfgtype[i].services,
180 cfgtype[i].nattrs, cfgtype[i].attrs,
181 __arraycount(values), values);
182
183 if (rv != 0) {
184 errno = sdp_error(ss);
185 return NULL;
186 }
187 sdp_close(ss);
188
189 rv = (*cfgtype[i].handler)(dict);
190 if (rv != 0)
191 return NULL;
192
193 return dict;
194 }
195 }
196
197 printf("Known config types:\n");
198 for (i = 0; i < __arraycount(cfgtype); i++)
199 printf("\t%s\t%s\n", cfgtype[i].name, cfgtype[i].description);
200
201 exit(EXIT_FAILURE);
202 }
203
204 /*
205 * Configure HID results
206 */
207 static int
208 config_hid(prop_dictionary_t dict)
209 {
210 prop_object_t obj;
211 int32_t control_psm, interrupt_psm,
212 reconnect_initiate, battery_power,
213 normally_connectable, hid_length;
214 uint8_t *hid_descriptor;
215 const char *mode;
216 size_t i;
217
218 control_psm = -1;
219 interrupt_psm = -1;
220 reconnect_initiate = -1;
221 normally_connectable = 0;
222 battery_power = 0;
223 hid_descriptor = NULL;
224 hid_length = -1;
225
226 for (i = 0; i < __arraycount(values); i++) {
227 if (values[i].flags != SDP_ATTR_OK)
228 continue;
229
230 switch (values[i].attr) {
231 case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
232 control_psm = parse_l2cap_psm(&values[i]);
233 break;
234
235 case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS:
236 interrupt_psm = parse_l2cap_psm(&values[i]);
237 break;
238
239 case 0x0205: /* HIDReconnectInitiate */
240 reconnect_initiate = parse_boolean(&values[i]);
241 break;
242
243 case 0x0206: /* HIDDescriptorList */
244 if (parse_hid_descriptor(&values[i]) == 0) {
245 hid_descriptor = values[i].value;
246 hid_length = values[i].vlen;
247 }
248 break;
249
250 case 0x0209: /* HIDBatteryPower */
251 battery_power = parse_boolean(&values[i]);
252 break;
253
254 case 0x020d: /* HIDNormallyConnectable */
255 normally_connectable = parse_boolean(&values[i]);
256 break;
257 }
258 }
259
260 if (control_psm == -1
261 || interrupt_psm == -1
262 || reconnect_initiate == -1
263 || hid_descriptor == NULL
264 || hid_length == -1)
265 return ENOATTR;
266
267 obj = prop_string_create_cstring_nocopy("bthidev");
268 if (obj == NULL || !prop_dictionary_set(dict, BTDEVtype, obj))
269 return errno;
270
271 prop_object_release(obj);
272
273 obj = prop_number_create_integer(control_psm);
274 if (obj == NULL || !prop_dictionary_set(dict, BTHIDEVcontrolpsm, obj))
275 return errno;
276
277 prop_object_release(obj);
278
279 obj = prop_number_create_integer(interrupt_psm);
280 if (obj == NULL || !prop_dictionary_set(dict, BTHIDEVinterruptpsm, obj))
281 return errno;
282
283 prop_object_release(obj);
284
285 obj = prop_data_create_data(hid_descriptor, hid_length);
286 if (obj == NULL || !prop_dictionary_set(dict, BTHIDEVdescriptor, obj))
287 return errno;
288
289 mode = hid_mode(obj);
290 prop_object_release(obj);
291
292 obj = prop_string_create_cstring_nocopy(mode);
293 if (obj == NULL || !prop_dictionary_set(dict, BTDEVmode, obj))
294 return errno;
295
296 prop_object_release(obj);
297
298 if (!reconnect_initiate) {
299 obj = prop_bool_create(true);
300 if (obj == NULL || !prop_dictionary_set(dict, BTHIDEVreconnect, obj))
301 return errno;
302
303 prop_object_release(obj);
304 }
305
306 return 0;
307 }
308
309 /*
310 * Configure HSET results
311 */
312 static int
313 config_hset(prop_dictionary_t dict)
314 {
315 prop_object_t obj;
316 uint32_t channel;
317 size_t i;
318
319 channel = -1;
320
321 for (i = 0; i < __arraycount(values); i++) {
322 if (values[i].flags != SDP_ATTR_OK)
323 continue;
324
325 switch (values[i].attr) {
326 case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
327 channel = parse_rfcomm_channel(&values[i]);
328 break;
329 }
330 }
331
332 if (channel == (uint32_t)-1)
333 return ENOATTR;
334
335 obj = prop_string_create_cstring_nocopy("btsco");
336 if (obj == NULL || !prop_dictionary_set(dict, BTDEVtype, obj))
337 return errno;
338
339 prop_object_release(obj);
340
341 obj = prop_number_create_integer(channel);
342 if (obj == NULL || !prop_dictionary_set(dict, BTSCOchannel, obj))
343 return errno;
344
345 prop_object_release(obj);
346
347 return 0;
348 }
349
350 /*
351 * Configure HF results
352 */
353 static int
354 config_hf(prop_dictionary_t dict)
355 {
356 prop_object_t obj;
357 uint32_t channel;
358 size_t i;
359
360 channel = -1;
361
362 for (i = 0; i < __arraycount(values); i++) {
363 if (values[i].flags != SDP_ATTR_OK)
364 continue;
365
366 switch (values[i].attr) {
367 case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
368 channel = parse_rfcomm_channel(&values[i]);
369 break;
370 }
371 }
372
373 if (channel == (uint32_t)-1)
374 return ENOATTR;
375
376 obj = prop_string_create_cstring_nocopy("btsco");
377 if (obj == NULL || !prop_dictionary_set(dict, BTDEVtype, obj))
378 return errno;
379
380 prop_object_release(obj);
381
382 obj = prop_bool_create(true);
383 if (obj == NULL || !prop_dictionary_set(dict, BTSCOlisten, obj))
384 return errno;
385
386 prop_object_release(obj);
387
388 obj = prop_number_create_integer(channel);
389 if (obj == NULL || !prop_dictionary_set(dict, BTSCOchannel, obj))
390 return errno;
391
392 prop_object_release(obj);
393
394 return 0;
395 }
396
397 /*
398 * Parse [additional] protocol descriptor list for L2CAP PSM
399 *
400 * seq8 len8 2
401 * seq8 len8 2
402 * uuid16 value16 3 L2CAP
403 * uint16 value16 3 PSM
404 * seq8 len8 2
405 * uuid16 value16 3 HID Protocol
406 * ===
407 * 15
408 */
409
410 static int32_t
411 parse_l2cap_psm(sdp_attr_t *a)
412 {
413 uint8_t *ptr = a->value;
414 uint8_t *end = a->value + a->vlen;
415 int32_t type, len, uuid, psm;
416
417 if (end - ptr < 15)
418 return (-1);
419
420 if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) {
421 SDP_GET8(type, ptr);
422 switch (type) {
423 case SDP_DATA_SEQ8:
424 SDP_GET8(len, ptr);
425 break;
426
427 case SDP_DATA_SEQ16:
428 SDP_GET16(len, ptr);
429 break;
430
431 case SDP_DATA_SEQ32:
432 SDP_GET32(len, ptr);
433 break;
434
435 default:
436 return (-1);
437 }
438 if (ptr + len > end)
439 return (-1);
440 }
441
442 SDP_GET8(type, ptr);
443 switch (type) {
444 case SDP_DATA_SEQ8:
445 SDP_GET8(len, ptr);
446 break;
447
448 case SDP_DATA_SEQ16:
449 SDP_GET16(len, ptr);
450 break;
451
452 case SDP_DATA_SEQ32:
453 SDP_GET32(len, ptr);
454 break;
455
456 default:
457 return (-1);
458 }
459 if (ptr + len > end)
460 return (-1);
461
462 /* Protocol */
463 SDP_GET8(type, ptr);
464 switch (type) {
465 case SDP_DATA_SEQ8:
466 SDP_GET8(len, ptr);
467 break;
468
469 case SDP_DATA_SEQ16:
470 SDP_GET16(len, ptr);
471 break;
472
473 case SDP_DATA_SEQ32:
474 SDP_GET32(len, ptr);
475 break;
476
477 default:
478 return (-1);
479 }
480 if (ptr + len > end)
481 return (-1);
482
483 /* UUID */
484 if (ptr + 3 > end)
485 return (-1);
486 SDP_GET8(type, ptr);
487 switch (type) {
488 case SDP_DATA_UUID16:
489 SDP_GET16(uuid, ptr);
490 if (uuid != SDP_UUID_PROTOCOL_L2CAP)
491 return (-1);
492 break;
493
494 case SDP_DATA_UUID32: /* XXX FIXME can we have 32-bit UUID */
495 case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
496 default:
497 return (-1);
498 }
499
500 /* PSM */
501 if (ptr + 3 > end)
502 return (-1);
503 SDP_GET8(type, ptr);
504 if (type != SDP_DATA_UINT16)
505 return (-1);
506 SDP_GET16(psm, ptr);
507
508 return (psm);
509 }
510
511 /*
512 * Parse HID descriptor string
513 *
514 * seq8 len8 2
515 * seq8 len8 2
516 * uint8 value8 2
517 * str value 3
518 * ===
519 * 9
520 */
521
522 static int32_t
523 parse_hid_descriptor(sdp_attr_t *a)
524 {
525 uint8_t *ptr = a->value;
526 uint8_t *end = a->value + a->vlen;
527 int32_t type, len, descriptor_type;
528
529 if (end - ptr < 9)
530 return (-1);
531
532 SDP_GET8(type, ptr);
533 switch (type) {
534 case SDP_DATA_SEQ8:
535 SDP_GET8(len, ptr);
536 break;
537
538 case SDP_DATA_SEQ16:
539 SDP_GET16(len, ptr);
540 break;
541
542 case SDP_DATA_SEQ32:
543 SDP_GET32(len, ptr);
544 break;
545
546 default:
547 return (-1);
548 }
549 if (ptr + len > end)
550 return (-1);
551
552 while (ptr < end) {
553 /* Descriptor */
554 SDP_GET8(type, ptr);
555 switch (type) {
556 case SDP_DATA_SEQ8:
557 if (ptr + 1 > end)
558 return (-1);
559 SDP_GET8(len, ptr);
560 break;
561
562 case SDP_DATA_SEQ16:
563 if (ptr + 2 > end)
564 return (-1);
565 SDP_GET16(len, ptr);
566 break;
567
568 case SDP_DATA_SEQ32:
569 if (ptr + 4 > end)
570 return (-1);
571 SDP_GET32(len, ptr);
572 break;
573
574 default:
575 return (-1);
576 }
577
578 /* Descripor type */
579 if (ptr + 1 > end)
580 return (-1);
581 SDP_GET8(type, ptr);
582 if (type != SDP_DATA_UINT8 || ptr + 1 > end)
583 return (-1);
584 SDP_GET8(descriptor_type, ptr);
585
586 /* Descriptor value */
587 if (ptr + 1 > end)
588 return (-1);
589 SDP_GET8(type, ptr);
590 switch (type) {
591 case SDP_DATA_STR8:
592 if (ptr + 1 > end)
593 return (-1);
594 SDP_GET8(len, ptr);
595 break;
596
597 case SDP_DATA_STR16:
598 if (ptr + 2 > end)
599 return (-1);
600 SDP_GET16(len, ptr);
601 break;
602
603 case SDP_DATA_STR32:
604 if (ptr + 4 > end)
605 return (-1);
606 SDP_GET32(len, ptr);
607 break;
608
609 default:
610 return (-1);
611 }
612 if (ptr + len > end)
613 return (-1);
614
615 if (descriptor_type == UDESC_REPORT && len > 0) {
616 a->value = ptr;
617 a->vlen = len;
618
619 return (0);
620 }
621
622 ptr += len;
623 }
624
625 return (-1);
626 }
627
628 /*
629 * Parse boolean value
630 *
631 * bool8 int8
632 */
633
634 static int32_t
635 parse_boolean(sdp_attr_t *a)
636 {
637 if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL)
638 return (-1);
639
640 return (a->value[1]);
641 }
642
643 /*
644 * Parse protocol descriptor list for the RFCOMM channel
645 *
646 * seq8 len8 2
647 * seq8 len8 2
648 * uuid16 value16 3 L2CAP
649 * seq8 len8 2
650 * uuid16 value16 3 RFCOMM
651 * uint8 value8 2 channel
652 * ===
653 * 14
654 */
655
656 static int32_t
657 parse_rfcomm_channel(sdp_attr_t *a)
658 {
659 uint8_t *ptr = a->value;
660 uint8_t *end = a->value + a->vlen;
661 int32_t type, len, uuid, channel;
662
663 if (end - ptr < 14)
664 return (-1);
665
666 SDP_GET8(type, ptr);
667 switch (type) {
668 case SDP_DATA_SEQ8:
669 SDP_GET8(len, ptr);
670 break;
671
672 case SDP_DATA_SEQ16:
673 SDP_GET16(len, ptr);
674 break;
675
676 case SDP_DATA_SEQ32:
677 SDP_GET32(len, ptr);
678 break;
679
680 default:
681 return (-1);
682 }
683 if (ptr + len > end)
684 return (-1);
685
686 /* Protocol */
687 SDP_GET8(type, ptr);
688 switch (type) {
689 case SDP_DATA_SEQ8:
690 SDP_GET8(len, ptr);
691 break;
692
693 case SDP_DATA_SEQ16:
694 SDP_GET16(len, ptr);
695 break;
696
697 case SDP_DATA_SEQ32:
698 SDP_GET32(len, ptr);
699 break;
700
701 default:
702 return (-1);
703 }
704 if (ptr + len > end)
705 return (-1);
706
707 /* UUID */
708 if (ptr + 3 > end)
709 return (-1);
710 SDP_GET8(type, ptr);
711 switch (type) {
712 case SDP_DATA_UUID16:
713 SDP_GET16(uuid, ptr);
714 if (uuid != SDP_UUID_PROTOCOL_L2CAP)
715 return (-1);
716 break;
717
718 case SDP_DATA_UUID32: /* XXX FIXME can we have 32-bit UUID */
719 case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
720 default:
721 return (-1);
722 }
723
724 /* Protocol */
725 SDP_GET8(type, ptr);
726 switch (type) {
727 case SDP_DATA_SEQ8:
728 SDP_GET8(len, ptr);
729 break;
730
731 case SDP_DATA_SEQ16:
732 SDP_GET16(len, ptr);
733 break;
734
735 case SDP_DATA_SEQ32:
736 SDP_GET32(len, ptr);
737 break;
738
739 default:
740 return (-1);
741 }
742 if (ptr + len > end)
743 return (-1);
744
745 /* UUID */
746 if (ptr + 3 > end)
747 return (-1);
748 SDP_GET8(type, ptr);
749 switch (type) {
750 case SDP_DATA_UUID16:
751 SDP_GET16(uuid, ptr);
752 if (uuid != SDP_UUID_PROTOCOL_RFCOMM)
753 return (-1);
754 break;
755
756 case SDP_DATA_UUID32: /* XXX FIXME can we have 32-bit UUID */
757 case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
758 default:
759 return (-1);
760 }
761
762 /* channel */
763 if (ptr + 2 > end)
764 return (-1);
765
766 SDP_GET8(type, ptr);
767 if (type != SDP_DATA_UINT8)
768 return (-1);
769
770 SDP_GET8(channel, ptr);
771
772 return (channel);
773 }
774
775 /*
776 * return appropriate mode for HID descriptor
777 */
778 const char *
779 hid_mode(prop_data_t desc)
780 {
781 report_desc_t r;
782 hid_data_t d;
783 struct hid_item h;
784 const char *mode;
785
786 hid_init(NULL);
787
788 mode = BTDEVauth; /* default */
789
790 r = hid_use_report_desc(prop_data_data_nocopy(desc),
791 prop_data_size(desc));
792 if (r == NULL)
793 err(EXIT_FAILURE, "hid_use_report_desc");
794
795 d = hid_start_parse(r, ~0, -1);
796 while (hid_get_item(d, &h) > 0) {
797 if (h.kind == hid_collection
798 && HID_PAGE(h.usage) == HUP_GENERIC_DESKTOP
799 && HID_USAGE(h.usage) == HUG_KEYBOARD)
800 mode = BTDEVencrypt;
801 }
802
803 hid_end_parse(d);
804 hid_dispose_report_desc(r);
805
806 return mode;
807 }
808