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