]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - usr.sbin/bluetooth/bthidcontrol/sdp.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / usr.sbin / bluetooth / bthidcontrol / sdp.c
1 /*
2  * sdp.c
3  *
4  * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
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  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $Id: sdp.c,v 1.3 2004/02/17 22:14:57 max Exp $
29  * $FreeBSD$
30  */
31
32 #include <sys/queue.h>
33 #include <bluetooth.h>
34 #include <dev/usb/usb.h>
35 #include <dev/usb/usbhid.h>
36 #include <errno.h>
37 #include <sdp.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <usbhid.h>
41 #include "bthid_config.h"
42 #include "bthidcontrol.h"
43
44 static int32_t hid_sdp_query                            (bdaddr_t const *local, struct hid_device *hd, int32_t *error);
45 static int32_t hid_sdp_parse_protocol_descriptor_list   (sdp_attr_p a);
46 static int32_t hid_sdp_parse_hid_descriptor             (sdp_attr_p a);
47 static int32_t hid_sdp_parse_boolean                    (sdp_attr_p a);
48
49 static uint16_t         service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE;
50
51 static uint32_t         attrs[] = {
52 SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
53                 SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
54 SDP_ATTR_RANGE  (SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS,
55                 SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS),
56 SDP_ATTR_RANGE( 0x0205,         /* HIDReconnectInitiate */
57                 0x0206),        /* HIDDescriptorList */
58 SDP_ATTR_RANGE( 0x0209,         /* HIDBatteryPower */
59                 0x0209),
60 SDP_ATTR_RANGE( 0x020d,         /* HIDNormallyConnectable */
61                 0x020d)
62         };
63 #define nattrs  (sizeof(attrs)/sizeof(attrs[0]))
64
65 static sdp_attr_t       values[8];
66 #define nvalues (sizeof(values)/sizeof(values[0]))
67
68 static uint8_t          buffer[nvalues][512];
69
70 /*
71  * Query remote device
72  */
73
74 #undef  hid_sdp_query_exit
75 #define hid_sdp_query_exit(e) {         \
76         if (error != NULL)              \
77                 *error = (e);           \
78         if (ss != NULL) {               \
79                 sdp_close(ss);          \
80                 ss = NULL;              \
81         }                               \
82         return (((e) == 0)? 0 : -1);    \
83 }
84
85 static int32_t
86 hid_sdp_query(bdaddr_t const *local, struct hid_device *hd, int32_t *error)
87 {
88         void    *ss = NULL;
89         uint8_t *hid_descriptor = NULL;
90         int32_t  i, control_psm = -1, interrupt_psm = -1,
91                  reconnect_initiate = -1,
92                  normally_connectable = 0, battery_power = 0,
93                  hid_descriptor_length = -1;
94
95         if (local == NULL)
96                 local = NG_HCI_BDADDR_ANY;
97         if (hd == NULL)
98                 hid_sdp_query_exit(EINVAL);
99
100         for (i = 0; i < nvalues; i ++) {
101                 values[i].flags = SDP_ATTR_INVALID;
102                 values[i].attr = 0;
103                 values[i].vlen = sizeof(buffer[i]);
104                 values[i].value = buffer[i];
105         }
106
107         if ((ss = sdp_open(local, &hd->bdaddr)) == NULL)
108                 hid_sdp_query_exit(ENOMEM);
109         if (sdp_error(ss) != 0)
110                 hid_sdp_query_exit(sdp_error(ss));
111         if (sdp_search(ss, 1, &service, nattrs, attrs, nvalues, values) != 0)
112                 hid_sdp_query_exit(sdp_error(ss));
113
114         sdp_close(ss);
115         ss = NULL;
116
117         for (i = 0; i < nvalues; i ++) {
118                 if (values[i].flags != SDP_ATTR_OK)
119                         continue;
120
121                 switch (values[i].attr) {
122                 case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
123                         control_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
124                         break;
125
126                 case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS:
127                         interrupt_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
128                         break;
129
130                 case 0x0205: /* HIDReconnectInitiate */
131                         reconnect_initiate = hid_sdp_parse_boolean(&values[i]);
132                         break;
133
134                 case 0x0206: /* HIDDescriptorList */
135                         if (hid_sdp_parse_hid_descriptor(&values[i]) == 0) {
136                                 hid_descriptor = values[i].value;
137                                 hid_descriptor_length = values[i].vlen;
138                         }
139                         break;
140
141                 case 0x0209: /* HIDBatteryPower */
142                         battery_power = hid_sdp_parse_boolean(&values[i]);
143                         break;
144
145                 case 0x020d: /* HIDNormallyConnectable */
146                         normally_connectable = hid_sdp_parse_boolean(&values[i]);
147                         break;
148                 }
149         }
150
151         if (control_psm == -1 || interrupt_psm == -1 ||
152             reconnect_initiate == -1 || normally_connectable == -1 ||
153             hid_descriptor == NULL || hid_descriptor_length == -1)
154                 hid_sdp_query_exit(ENOATTR);
155
156         hd->control_psm = control_psm;
157         hd->interrupt_psm = interrupt_psm;
158         hd->reconnect_initiate = reconnect_initiate? 1 : 0;
159         hd->battery_power = battery_power? 1 : 0;
160         hd->normally_connectable = normally_connectable? 1 : 0;
161         hd->desc = hid_use_report_desc(hid_descriptor, hid_descriptor_length);
162         if (hd->desc == NULL)
163                 hid_sdp_query_exit(ENOMEM);
164
165         return (0);
166 }
167
168 /*
169  * seq len                              2
170  *      seq len                         2
171  *              uuid value              3
172  *              uint16 value            3
173  *              seq len                 2
174  *                      uuid value      3
175  */
176
177 static int32_t
178 hid_sdp_parse_protocol_descriptor_list(sdp_attr_p a)
179 {
180         uint8_t *ptr = a->value;
181         uint8_t *end = a->value + a->vlen;
182         int32_t  type, len, uuid, psm;
183
184         if (end - ptr < 15)
185                 return (-1);
186
187         if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) {
188                 SDP_GET8(type, ptr);
189                 switch (type) {
190                 case SDP_DATA_SEQ8:
191                         SDP_GET8(len, ptr);
192                         break;
193
194                 case SDP_DATA_SEQ16:
195                         SDP_GET16(len, ptr);
196                         break;
197
198                 case SDP_DATA_SEQ32:
199                         SDP_GET32(len, ptr);
200                         break;
201
202                 default:
203                         return (-1);
204                 }
205                 if (ptr + len > end)
206                         return (-1);
207         }
208
209         SDP_GET8(type, ptr);
210         switch (type) {
211         case SDP_DATA_SEQ8:
212                 SDP_GET8(len, ptr);
213                 break;
214
215         case SDP_DATA_SEQ16:
216                 SDP_GET16(len, ptr);
217                 break;
218
219         case SDP_DATA_SEQ32:
220                 SDP_GET32(len, ptr);
221                 break;
222
223         default:
224                 return (-1);
225         }
226         if (ptr + len > end)
227                 return (-1);
228
229         /* Protocol */
230         SDP_GET8(type, ptr);
231         switch (type) {
232         case SDP_DATA_SEQ8:
233                 SDP_GET8(len, ptr);
234                 break;
235
236         case SDP_DATA_SEQ16:
237                 SDP_GET16(len, ptr);
238                 break;
239
240         case SDP_DATA_SEQ32:
241                 SDP_GET32(len, ptr);
242                 break;
243
244         default:
245                 return (-1);
246         }
247         if (ptr + len > end)
248                 return (-1);
249
250         /* UUID */
251         if (ptr + 3 > end)
252                 return (-1);
253         SDP_GET8(type, ptr);
254         switch (type) {
255         case SDP_DATA_UUID16:
256                 SDP_GET16(uuid, ptr);
257                 if (uuid != SDP_UUID_PROTOCOL_L2CAP)
258                         return (-1);
259                 break;
260
261         case SDP_DATA_UUID32:  /* XXX FIXME can we have 32-bit UUID */
262         case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
263         default:
264                 return (-1);
265         }
266
267         /* PSM */
268         if (ptr + 3 > end)
269                 return (-1);
270         SDP_GET8(type, ptr);
271         if (type != SDP_DATA_UINT16)
272                 return (-1);
273         SDP_GET16(psm, ptr);
274
275         return (psm);
276 }
277
278 /*
279  * seq len                      2
280  *      seq len                 2
281  *              uint8 value8    2
282  *              str value       3
283  */
284
285 static int32_t
286 hid_sdp_parse_hid_descriptor(sdp_attr_p a)
287 {
288         uint8_t *ptr = a->value;
289         uint8_t *end = a->value + a->vlen;
290         int32_t  type, len, descriptor_type;
291
292         if (end - ptr < 9)
293                 return (-1);
294
295         SDP_GET8(type, ptr);
296         switch (type) {
297         case SDP_DATA_SEQ8:
298                 SDP_GET8(len, ptr);
299                 break;
300
301         case SDP_DATA_SEQ16:
302                 SDP_GET16(len, ptr);
303                 break;
304
305         case SDP_DATA_SEQ32:
306                 SDP_GET32(len, ptr);
307                 break;
308
309         default:
310                 return (-1);
311         }
312         if (ptr + len > end)
313                 return (-1);
314
315         while (ptr < end) {
316                 /* Descriptor */
317                 SDP_GET8(type, ptr);
318                 switch (type) {
319                 case SDP_DATA_SEQ8:
320                         if (ptr + 1 > end)
321                                 return (-1);
322                         SDP_GET8(len, ptr);
323                         break;
324
325                 case SDP_DATA_SEQ16:
326                         if (ptr + 2 > end)
327                                 return (-1);
328                         SDP_GET16(len, ptr);
329                         break;
330
331                 case SDP_DATA_SEQ32:
332                         if (ptr + 4 > end)
333                                 return (-1);
334                         SDP_GET32(len, ptr);
335                         break;
336
337                 default:
338                         return (-1);
339                 }
340
341                 /* Descripor type */
342                 if (ptr + 1 > end)
343                         return (-1);
344                 SDP_GET8(type, ptr);
345                 if (type != SDP_DATA_UINT8 || ptr + 1 > end)
346                         return (-1);
347                 SDP_GET8(descriptor_type, ptr);
348
349                 /* Descriptor value */
350                 if (ptr + 1 > end)
351                         return (-1);
352                 SDP_GET8(type, ptr);
353                 switch (type) {
354                 case SDP_DATA_STR8:
355                         if (ptr + 1 > end)
356                                 return (-1);
357                         SDP_GET8(len, ptr);
358                         break;
359
360                 case SDP_DATA_STR16:
361                         if (ptr + 2 > end)
362                                 return (-1);
363                         SDP_GET16(len, ptr);
364                         break;
365
366                 case SDP_DATA_STR32:
367                         if (ptr + 4 > end)
368                                 return (-1);
369                         SDP_GET32(len, ptr);
370                         break;
371
372                 default:
373                         return (-1);
374                 }
375                 if (ptr + len > end)
376                         return (-1);
377
378                 if (descriptor_type == UDESC_REPORT && len > 0) {
379                         a->value = ptr;
380                         a->vlen = len;
381
382                         return (0);
383                 }
384
385                 ptr += len;
386         }
387
388         return (-1);
389 }
390
391 /* bool8 int8 */
392 static int32_t
393 hid_sdp_parse_boolean(sdp_attr_p a)
394 {
395         if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL)
396                 return (-1);
397
398         return (a->value[1]);
399 }
400
401 /* Perform SDP query */
402 static int32_t
403 hid_query(bdaddr_t *bdaddr, int argc, char **argv)
404 {
405         struct hid_device       hd;
406         int                     e;
407
408         memcpy(&hd.bdaddr, bdaddr, sizeof(hd.bdaddr));
409         if (hid_sdp_query(NULL, &hd, &e) < 0) {
410                 fprintf(stderr, "Could not perform SDP query on the " \
411                         "device %s. %s (%d)\n", bt_ntoa(bdaddr, NULL),
412                         strerror(e), e);
413                 return (FAILED);
414         }
415
416         print_hid_device(&hd, stdout);
417
418         return (OK);
419 }
420
421 struct bthid_command    sdp_commands[] =
422 {
423 {
424 "Query",
425 "Perform SDP query to the specified device and print HID configuration entry\n"\
426 "for the device. The configuration entry should be appended to the Bluetooth\n"\
427 "HID daemon configuration file and the daemon should be restarted.\n",
428 hid_query
429 },
430 { NULL, NULL, NULL }
431 };
432