]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.sbin/bluetooth/bthidcontrol/sdp.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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                 0x0205),
58 SDP_ATTR_RANGE( 0x0206,         /* HIDDescriptorList */
59                 0x0206),
60 SDP_ATTR_RANGE( 0x0209,         /* HIDBatteryPower */
61                 0x0209),
62 SDP_ATTR_RANGE( 0x020d,         /* HIDNormallyConnectable */
63                 0x020d)
64         };
65 #define nattrs  (sizeof(attrs)/sizeof(attrs[0]))
66
67 static sdp_attr_t       values[8];
68 #define nvalues (sizeof(values)/sizeof(values[0]))
69
70 static uint8_t          buffer[nvalues][512];
71
72 /*
73  * Query remote device
74  */
75
76 #undef  hid_sdp_query_exit
77 #define hid_sdp_query_exit(e) {         \
78         if (error != NULL)              \
79                 *error = (e);           \
80         if (ss != NULL) {               \
81                 sdp_close(ss);          \
82                 ss = NULL;              \
83         }                               \
84         return (((e) == 0)? 0 : -1);    \
85 }
86
87 static int32_t
88 hid_sdp_query(bdaddr_t const *local, struct hid_device *hd, int32_t *error)
89 {
90         void    *ss = NULL;
91         uint8_t *hid_descriptor = NULL;
92         int32_t  i, control_psm = -1, interrupt_psm = -1,
93                  reconnect_initiate = -1,
94                  normally_connectable = 0, battery_power = 0,
95                  hid_descriptor_length = -1;
96
97         if (local == NULL)
98                 local = NG_HCI_BDADDR_ANY;
99         if (hd == NULL)
100                 hid_sdp_query_exit(EINVAL);
101
102         for (i = 0; i < nvalues; i ++) {
103                 values[i].flags = SDP_ATTR_INVALID;
104                 values[i].attr = 0;
105                 values[i].vlen = sizeof(buffer[i]);
106                 values[i].value = buffer[i];
107         }
108
109         if ((ss = sdp_open(local, &hd->bdaddr)) == NULL)
110                 hid_sdp_query_exit(ENOMEM);
111         if (sdp_error(ss) != 0)
112                 hid_sdp_query_exit(sdp_error(ss));
113         if (sdp_search(ss, 1, &service, nattrs, attrs, nvalues, values) != 0)
114                 hid_sdp_query_exit(sdp_error(ss));
115
116         sdp_close(ss);
117         ss = NULL;
118
119         for (i = 0; i < nvalues; i ++) {
120                 if (values[i].flags != SDP_ATTR_OK)
121                         continue;
122
123                 switch (values[i].attr) {
124                 case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
125                         control_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
126                         break;
127
128                 case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS:
129                         interrupt_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
130                         break;
131
132                 case 0x0205: /* HIDReconnectInitiate */
133                         reconnect_initiate = hid_sdp_parse_boolean(&values[i]);
134                         break;
135
136                 case 0x0206: /* HIDDescriptorList */
137                         if (hid_sdp_parse_hid_descriptor(&values[i]) == 0) {
138                                 hid_descriptor = values[i].value;
139                                 hid_descriptor_length = values[i].vlen;
140                         }
141                         break;
142
143                 case 0x0209: /* HIDBatteryPower */
144                         battery_power = hid_sdp_parse_boolean(&values[i]);
145                         break;
146
147                 case 0x020d: /* HIDNormallyConnectable */
148                         normally_connectable = hid_sdp_parse_boolean(&values[i]);
149                         break;
150                 }
151         }
152
153         if (control_psm == -1 || interrupt_psm == -1 ||
154             reconnect_initiate == -1 ||
155             hid_descriptor == NULL || hid_descriptor_length == -1)
156                 hid_sdp_query_exit(ENOATTR);
157
158         hd->control_psm = control_psm;
159         hd->interrupt_psm = interrupt_psm;
160         hd->reconnect_initiate = reconnect_initiate? 1 : 0;
161         hd->battery_power = battery_power? 1 : 0;
162         hd->normally_connectable = normally_connectable? 1 : 0;
163         hd->desc = hid_use_report_desc(hid_descriptor, hid_descriptor_length);
164         if (hd->desc == NULL)
165                 hid_sdp_query_exit(ENOMEM);
166
167         return (0);
168 }
169
170 /*
171  * seq len                              2
172  *      seq len                         2
173  *              uuid value              3
174  *              uint16 value            3
175  *              seq len                 2
176  *                      uuid value      3
177  */
178
179 static int32_t
180 hid_sdp_parse_protocol_descriptor_list(sdp_attr_p a)
181 {
182         uint8_t *ptr = a->value;
183         uint8_t *end = a->value + a->vlen;
184         int32_t  type, len, uuid, psm;
185
186         if (end - ptr < 15)
187                 return (-1);
188
189         if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) {
190                 SDP_GET8(type, ptr);
191                 switch (type) {
192                 case SDP_DATA_SEQ8:
193                         SDP_GET8(len, ptr);
194                         break;
195
196                 case SDP_DATA_SEQ16:
197                         SDP_GET16(len, ptr);
198                         break;
199
200                 case SDP_DATA_SEQ32:
201                         SDP_GET32(len, ptr);
202                         break;
203
204                 default:
205                         return (-1);
206                 }
207                 if (ptr + len > end)
208                         return (-1);
209         }
210
211         SDP_GET8(type, ptr);
212         switch (type) {
213         case SDP_DATA_SEQ8:
214                 SDP_GET8(len, ptr);
215                 break;
216
217         case SDP_DATA_SEQ16:
218                 SDP_GET16(len, ptr);
219                 break;
220
221         case SDP_DATA_SEQ32:
222                 SDP_GET32(len, ptr);
223                 break;
224
225         default:
226                 return (-1);
227         }
228         if (ptr + len > end)
229                 return (-1);
230
231         /* Protocol */
232         SDP_GET8(type, ptr);
233         switch (type) {
234         case SDP_DATA_SEQ8:
235                 SDP_GET8(len, ptr);
236                 break;
237
238         case SDP_DATA_SEQ16:
239                 SDP_GET16(len, ptr);
240                 break;
241
242         case SDP_DATA_SEQ32:
243                 SDP_GET32(len, ptr);
244                 break;
245
246         default:
247                 return (-1);
248         }
249         if (ptr + len > end)
250                 return (-1);
251
252         /* UUID */
253         if (ptr + 3 > end)
254                 return (-1);
255         SDP_GET8(type, ptr);
256         switch (type) {
257         case SDP_DATA_UUID16:
258                 SDP_GET16(uuid, ptr);
259                 if (uuid != SDP_UUID_PROTOCOL_L2CAP)
260                         return (-1);
261                 break;
262
263         case SDP_DATA_UUID32:  /* XXX FIXME can we have 32-bit UUID */
264         case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
265         default:
266                 return (-1);
267         }
268
269         /* PSM */
270         if (ptr + 3 > end)
271                 return (-1);
272         SDP_GET8(type, ptr);
273         if (type != SDP_DATA_UINT16)
274                 return (-1);
275         SDP_GET16(psm, ptr);
276
277         return (psm);
278 }
279
280 /*
281  * seq len                      2
282  *      seq len                 2
283  *              uint8 value8    2
284  *              str value       3
285  */
286
287 static int32_t
288 hid_sdp_parse_hid_descriptor(sdp_attr_p a)
289 {
290         uint8_t *ptr = a->value;
291         uint8_t *end = a->value + a->vlen;
292         int32_t  type, len, descriptor_type;
293
294         if (end - ptr < 9)
295                 return (-1);
296
297         SDP_GET8(type, ptr);
298         switch (type) {
299         case SDP_DATA_SEQ8:
300                 SDP_GET8(len, ptr);
301                 break;
302
303         case SDP_DATA_SEQ16:
304                 SDP_GET16(len, ptr);
305                 break;
306
307         case SDP_DATA_SEQ32:
308                 SDP_GET32(len, ptr);
309                 break;
310
311         default:
312                 return (-1);
313         }
314         if (ptr + len > end)
315                 return (-1);
316
317         while (ptr < end) {
318                 /* Descriptor */
319                 SDP_GET8(type, ptr);
320                 switch (type) {
321                 case SDP_DATA_SEQ8:
322                         if (ptr + 1 > end)
323                                 return (-1);
324                         SDP_GET8(len, ptr);
325                         break;
326
327                 case SDP_DATA_SEQ16:
328                         if (ptr + 2 > end)
329                                 return (-1);
330                         SDP_GET16(len, ptr);
331                         break;
332
333                 case SDP_DATA_SEQ32:
334                         if (ptr + 4 > end)
335                                 return (-1);
336                         SDP_GET32(len, ptr);
337                         break;
338
339                 default:
340                         return (-1);
341                 }
342
343                 /* Descripor type */
344                 if (ptr + 1 > end)
345                         return (-1);
346                 SDP_GET8(type, ptr);
347                 if (type != SDP_DATA_UINT8 || ptr + 1 > end)
348                         return (-1);
349                 SDP_GET8(descriptor_type, ptr);
350
351                 /* Descriptor value */
352                 if (ptr + 1 > end)
353                         return (-1);
354                 SDP_GET8(type, ptr);
355                 switch (type) {
356                 case SDP_DATA_STR8:
357                         if (ptr + 1 > end)
358                                 return (-1);
359                         SDP_GET8(len, ptr);
360                         break;
361
362                 case SDP_DATA_STR16:
363                         if (ptr + 2 > end)
364                                 return (-1);
365                         SDP_GET16(len, ptr);
366                         break;
367
368                 case SDP_DATA_STR32:
369                         if (ptr + 4 > end)
370                                 return (-1);
371                         SDP_GET32(len, ptr);
372                         break;
373
374                 default:
375                         return (-1);
376                 }
377                 if (ptr + len > end)
378                         return (-1);
379
380                 if (descriptor_type == UDESC_REPORT && len > 0) {
381                         a->value = ptr;
382                         a->vlen = len;
383
384                         return (0);
385                 }
386
387                 ptr += len;
388         }
389
390         return (-1);
391 }
392
393 /* bool8 int8 */
394 static int32_t
395 hid_sdp_parse_boolean(sdp_attr_p a)
396 {
397         if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL)
398                 return (-1);
399
400         return (a->value[1]);
401 }
402
403 /* Perform SDP query */
404 static int32_t
405 hid_query(bdaddr_t *bdaddr, int argc, char **argv)
406 {
407         struct hid_device       hd;
408         int                     e;
409
410         memcpy(&hd.bdaddr, bdaddr, sizeof(hd.bdaddr));
411         if (hid_sdp_query(NULL, &hd, &e) < 0) {
412                 fprintf(stderr, "Could not perform SDP query on the " \
413                         "device %s. %s (%d)\n", bt_ntoa(bdaddr, NULL),
414                         strerror(e), e);
415                 return (FAILED);
416         }
417
418         print_hid_device(&hd, stdout);
419
420         return (OK);
421 }
422
423 struct bthid_command    sdp_commands[] =
424 {
425 {
426 "Query",
427 "Perform SDP query to the specified device and print HID configuration entry\n"\
428 "for the device. The configuration entry should be appended to the Bluetooth\n"\
429 "HID daemon configuration file and the daemon should be restarted.\n",
430 hid_query
431 },
432 { NULL, NULL, NULL }
433 };
434