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), /* HIDDesctiptorList */ 58 SDP_ATTR_RANGE( 0x020a, /* HIDBatteryPower */ 59 0x020a), 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: /* HIDDesctiptorList */ 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 0x020a: /* 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 433