1 /* $NetBSD: sdp.c,v 1.6 2009/04/15 00:35:04 lukem Exp $ */
4 * Copyright (c) 2006 Itronix Inc.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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.
32 * Copyright (c) 2009 The NetBSD Foundation, Inc.
33 * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
34 * All rights reserved.
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
45 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58 #include <sys/cdefs.h>
59 __RCSID("$NetBSD: sdp.c,v 1.6 2009/04/15 00:35:04 lukem Exp $");
61 #include <sys/types.h>
63 #include <dev/bluetooth/btdev.h>
64 #include <dev/bluetooth/bthidev.h>
65 #include <dev/bluetooth/btsco.h>
66 #include <dev/usb/usb.h>
67 #include <dev/usb/usbhid.h>
69 #include <prop/proplib.h>
71 #include <bluetooth.h>
81 static bool parse_hid_descriptor(sdp_data_t
*);
82 static int32_t parse_boolean(sdp_data_t
*);
83 static int32_t parse_pdl_param(sdp_data_t
*, uint16_t);
84 static int32_t parse_pdl(sdp_data_t
*, uint16_t);
85 static int32_t parse_apdl(sdp_data_t
*, uint16_t);
87 static int config_hid(prop_dictionary_t
, sdp_data_t
*);
88 static int config_hset(prop_dictionary_t
, sdp_data_t
*);
89 static int config_hf(prop_dictionary_t
, sdp_data_t
*);
91 uint16_t hid_services
[] = {
92 SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE
,
95 uint16_t hset_services
[] = {
96 SDP_SERVICE_CLASS_HEADSET
,
99 uint16_t hf_services
[] = {
100 SDP_SERVICE_CLASS_HANDSFREE_AUDIO_GATEWAY
,
105 int (*handler
)(prop_dictionary_t
, sdp_data_t
*);
106 const char *description
;
111 "HID", config_hid
, "Human Interface Device",
112 hid_services
, __arraycount(hid_services
),
115 "HSET", config_hset
, "Headset",
116 hset_services
, __arraycount(hset_services
),
119 "HF", config_hf
, "Handsfree",
120 hf_services
, __arraycount(hf_services
),
124 #define MAX_SSP (2 + 1 * 3) /* largest nservices is 1 */
127 cfg_query(bdaddr_t
*laddr
, bdaddr_t
*raddr
, const char *service
)
129 prop_dictionary_t dict
;
131 uint8_t buf
[MAX_SSP
];
132 sdp_data_t ssp
, rsp
, rec
;
136 dict
= prop_dictionary_create();
140 for (i
= 0; i
< __arraycount(cfgtype
); i
++) {
141 if (strcasecmp(service
, cfgtype
[i
].name
) == 0) {
142 ss
= sdp_open(laddr
, raddr
);
146 /* build ServiceSearchPattern */
148 ssp
.end
= buf
+ sizeof(buf
);
150 for (n
= 0; n
< cfgtype
[i
].nservices
; n
++)
151 sdp_put_uuid16(&ssp
, cfgtype
[i
].services
[n
]);
156 rv
= sdp_service_search_attribute(ss
, &ssp
, NULL
, &rsp
);
158 prop_object_release(dict
);
163 while (sdp_get_seq(&rsp
, &rec
)) {
164 errno
= (*cfgtype
[i
].handler
)(dict
, &rec
);
172 prop_object_release(dict
);
177 printf("Known config types:\n");
178 for (i
= 0; i
< __arraycount(cfgtype
); i
++)
179 printf("\t%s\t%s\n", cfgtype
[i
].name
, cfgtype
[i
].description
);
185 * Configure HID results
188 config_hid(prop_dictionary_t dict
, sdp_data_t
*rec
)
191 int32_t control_psm
, interrupt_psm
,
192 reconnect_initiate
, hid_length
;
193 uint8_t *hid_descriptor
;
200 reconnect_initiate
= -1;
201 hid_descriptor
= NULL
;
204 while (sdp_get_attr(rec
, &attr
, &value
)) {
206 case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST
:
207 control_psm
= parse_pdl(&value
, SDP_UUID_PROTOCOL_L2CAP
);
210 case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS
:
211 interrupt_psm
= parse_apdl(&value
, SDP_UUID_PROTOCOL_L2CAP
);
214 case 0x0205: /* HIDReconnectInitiate */
215 reconnect_initiate
= parse_boolean(&value
);
218 case 0x0206: /* HIDDescriptorList */
219 if (parse_hid_descriptor(&value
)) {
220 hid_descriptor
= value
.next
;
221 hid_length
= value
.end
- value
.next
;
230 if (control_psm
== -1
231 || interrupt_psm
== -1
232 || reconnect_initiate
== -1
233 || hid_descriptor
== NULL
237 obj
= prop_string_create_cstring_nocopy("bthidev");
238 if (obj
== NULL
|| !prop_dictionary_set(dict
, BTDEVtype
, obj
))
241 prop_object_release(obj
);
243 obj
= prop_number_create_integer(control_psm
);
244 if (obj
== NULL
|| !prop_dictionary_set(dict
, BTHIDEVcontrolpsm
, obj
))
247 prop_object_release(obj
);
249 obj
= prop_number_create_integer(interrupt_psm
);
250 if (obj
== NULL
|| !prop_dictionary_set(dict
, BTHIDEVinterruptpsm
, obj
))
253 prop_object_release(obj
);
255 obj
= prop_data_create_data(hid_descriptor
, hid_length
);
256 if (obj
== NULL
|| !prop_dictionary_set(dict
, BTHIDEVdescriptor
, obj
))
259 mode
= hid_mode(obj
);
260 prop_object_release(obj
);
262 obj
= prop_string_create_cstring_nocopy(mode
);
263 if (obj
== NULL
|| !prop_dictionary_set(dict
, BTDEVmode
, obj
))
266 prop_object_release(obj
);
268 if (!reconnect_initiate
) {
269 obj
= prop_bool_create(true);
270 if (obj
== NULL
|| !prop_dictionary_set(dict
, BTHIDEVreconnect
, obj
))
273 prop_object_release(obj
);
280 * Configure HSET results
283 config_hset(prop_dictionary_t dict
, sdp_data_t
*rec
)
292 while (sdp_get_attr(rec
, &attr
, &value
)) {
294 case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST
:
295 channel
= parse_pdl(&value
, SDP_UUID_PROTOCOL_RFCOMM
);
306 obj
= prop_string_create_cstring_nocopy("btsco");
307 if (obj
== NULL
|| !prop_dictionary_set(dict
, BTDEVtype
, obj
))
310 prop_object_release(obj
);
312 obj
= prop_number_create_integer(channel
);
313 if (obj
== NULL
|| !prop_dictionary_set(dict
, BTSCOchannel
, obj
))
316 prop_object_release(obj
);
322 * Configure HF results
325 config_hf(prop_dictionary_t dict
, sdp_data_t
*rec
)
334 while (sdp_get_attr(rec
, &attr
, &value
)) {
336 case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST
:
337 channel
= parse_pdl(&value
, SDP_UUID_PROTOCOL_RFCOMM
);
348 obj
= prop_string_create_cstring_nocopy("btsco");
349 if (obj
== NULL
|| !prop_dictionary_set(dict
, BTDEVtype
, obj
))
352 prop_object_release(obj
);
354 obj
= prop_bool_create(true);
355 if (obj
== NULL
|| !prop_dictionary_set(dict
, BTSCOlisten
, obj
))
358 prop_object_release(obj
);
360 obj
= prop_number_create_integer(channel
);
361 if (obj
== NULL
|| !prop_dictionary_set(dict
, BTSCOchannel
, obj
))
364 prop_object_release(obj
);
370 * Parse HIDDescriptorList . This is a sequence of HIDDescriptors, of which
371 * each is a data element sequence containing, minimally, a ClassDescriptorType
372 * and ClassDescriptorData containing a byte array of data. Any extra elements
375 * If a ClassDescriptorType "Report" is found, set SDP data value to the
376 * ClassDescriptorData content and return true. Note that we don't need to
377 * extract the actual length as the SDP data is guaranteed valid.
381 parse_hid_descriptor(sdp_data_t
*value
)
383 sdp_data_t list
, desc
;
388 if (!sdp_get_seq(value
, &list
))
391 while (sdp_get_seq(&list
, &desc
)) {
392 if (sdp_get_uint(&desc
, &type
)
393 && type
== UDESC_REPORT
394 && sdp_get_str(&desc
, &str
, &len
)) {
395 value
->next
= (uint8_t *)str
;
396 value
->end
= (uint8_t *)(str
+ len
);
405 parse_boolean(sdp_data_t
*value
)
409 if (!sdp_get_bool(value
, &bv
))
416 * The ProtocolDescriptorList attribute describes one or
417 * more protocol stacks that may be used to gain access to
418 * the service dscribed by the service record.
420 * If the ProtocolDescriptorList describes a single stack,
421 * the attribute value takes the form of a data element
422 * sequence in which each element of the sequence is a
423 * protocol descriptor.
428 * If it is possible for more than one kind of protocol
429 * stack to be used to gain access to the service, the
430 * ProtocolDescriptorList takes the form of a data element
431 * alternative where each member is a data element sequence
432 * consisting of a list of sequences describing each protocol
440 * Each ProtocolDescriptorList is a list containing a sequence for
441 * each protocol, where each sequence contains the protocol UUUID
442 * and any protocol specific parameters.
451 * We want to extract the ProtocolSpecificParameter#1 for the
452 * given protocol, which will be an unsigned int.
455 parse_pdl_param(sdp_data_t
*pdl
, uint16_t proto
)
460 while (sdp_get_seq(pdl
, &seq
)) {
461 if (!sdp_match_uuid16(&seq
, proto
))
464 if (sdp_get_uint(&seq
, ¶m
))
474 parse_pdl(sdp_data_t
*value
, uint16_t proto
)
479 sdp_get_alt(value
, value
); /* strip any alt header */
481 while (param
== -1 && sdp_get_seq(value
, &seq
))
482 param
= parse_pdl_param(&seq
, proto
);
488 * Parse AdditionalProtocolDescriptorList
491 parse_apdl(sdp_data_t
*value
, uint16_t proto
)
496 sdp_get_seq(value
, value
); /* strip seq header */
498 while (param
== -1 && sdp_get_seq(value
, &seq
))
499 param
= parse_pdl_param(&seq
, proto
);
505 * return appropriate mode for HID descriptor
508 hid_mode(prop_data_t desc
)
517 mode
= BTDEVauth
; /* default */
519 r
= hid_use_report_desc(prop_data_data_nocopy(desc
),
520 prop_data_size(desc
));
522 err(EXIT_FAILURE
, "hid_use_report_desc");
524 d
= hid_start_parse(r
, ~0, -1);
525 while (hid_get_item(d
, &h
) > 0) {
526 if (h
.kind
== hid_collection
527 && HID_PAGE(h
.usage
) == HUP_GENERIC_DESKTOP
528 && HID_USAGE(h
.usage
) == HUG_KEYBOARD
)
533 hid_dispose_report_desc(r
);