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