|
|
1.1 ! root 1: /* ! 2: * Service Discover Protocol server for QEMU L2CAP devices ! 3: * ! 4: * Copyright (C) 2008 Andrzej Zaborowski <[email protected]> ! 5: * ! 6: * This program is free software; you can redistribute it and/or ! 7: * modify it under the terms of the GNU General Public License as ! 8: * published by the Free Software Foundation; either version 2 of ! 9: * the License, or (at your option) any later version. ! 10: * ! 11: * This program is distributed in the hope that it will be useful, ! 12: * but WITHOUT ANY WARRANTY; without even the implied warranty of ! 13: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! 14: * GNU General Public License for more details. ! 15: * ! 16: * You should have received a copy of the GNU General Public License along ! 17: * with this program; if not, write to the Free Software Foundation, Inc., ! 18: * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ! 19: */ ! 20: ! 21: #include "qemu-common.h" ! 22: #include "bt.h" ! 23: ! 24: struct bt_l2cap_sdp_state_s { ! 25: struct bt_l2cap_conn_params_s *channel; ! 26: ! 27: struct sdp_service_record_s { ! 28: int match; ! 29: ! 30: int *uuid; ! 31: int uuids; ! 32: struct sdp_service_attribute_s { ! 33: int match; ! 34: ! 35: int attribute_id; ! 36: int len; ! 37: void *pair; ! 38: } *attribute_list; ! 39: int attributes; ! 40: } *service_list; ! 41: int services; ! 42: }; ! 43: ! 44: static ssize_t sdp_datalen(const uint8_t **element, ssize_t *left) ! 45: { ! 46: size_t len = *(*element) ++ & SDP_DSIZE_MASK; ! 47: ! 48: if (!*left) ! 49: return -1; ! 50: (*left) --; ! 51: ! 52: if (len < SDP_DSIZE_NEXT1) ! 53: return 1 << len; ! 54: else if (len == SDP_DSIZE_NEXT1) { ! 55: if (*left < 1) ! 56: return -1; ! 57: (*left) --; ! 58: ! 59: return *(*element) ++; ! 60: } else if (len == SDP_DSIZE_NEXT2) { ! 61: if (*left < 2) ! 62: return -1; ! 63: (*left) -= 2; ! 64: ! 65: len = (*(*element) ++) << 8; ! 66: return len | (*(*element) ++); ! 67: } else { ! 68: if (*left < 4) ! 69: return -1; ! 70: (*left) -= 4; ! 71: ! 72: len = (*(*element) ++) << 24; ! 73: len |= (*(*element) ++) << 16; ! 74: len |= (*(*element) ++) << 8; ! 75: return len | (*(*element) ++); ! 76: } ! 77: } ! 78: ! 79: static const uint8_t bt_base_uuid[12] = { ! 80: 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb, ! 81: }; ! 82: ! 83: static int sdp_uuid_match(struct sdp_service_record_s *record, ! 84: const uint8_t *uuid, ssize_t datalen) ! 85: { ! 86: int *lo, hi, val; ! 87: ! 88: if (datalen == 16 || datalen == 4) { ! 89: if (datalen == 16 && memcmp(uuid + 4, bt_base_uuid, 12)) ! 90: return 0; ! 91: ! 92: if (uuid[0] | uuid[1]) ! 93: return 0; ! 94: uuid += 2; ! 95: } ! 96: ! 97: val = (uuid[0] << 8) | uuid[1]; ! 98: lo = record->uuid; ! 99: hi = record->uuids; ! 100: while (hi >>= 1) ! 101: if (lo[hi] <= val) ! 102: lo += hi; ! 103: ! 104: return *lo == val; ! 105: } ! 106: ! 107: #define CONTINUATION_PARAM_SIZE (1 + sizeof(int)) ! 108: #define MAX_PDU_OUT_SIZE 96 /* Arbitrary */ ! 109: #define PDU_HEADER_SIZE 5 ! 110: #define MAX_RSP_PARAM_SIZE (MAX_PDU_OUT_SIZE - PDU_HEADER_SIZE - \ ! 111: CONTINUATION_PARAM_SIZE) ! 112: ! 113: static int sdp_svc_match(struct bt_l2cap_sdp_state_s *sdp, ! 114: const uint8_t **req, ssize_t *len) ! 115: { ! 116: size_t datalen; ! 117: int i; ! 118: ! 119: if ((**req & ~SDP_DSIZE_MASK) != SDP_DTYPE_UUID) ! 120: return 1; ! 121: ! 122: datalen = sdp_datalen(req, len); ! 123: if (datalen != 2 && datalen != 4 && datalen != 16) ! 124: return 1; ! 125: ! 126: for (i = 0; i < sdp->services; i ++) ! 127: if (sdp_uuid_match(&sdp->service_list[i], *req, datalen)) ! 128: sdp->service_list[i].match = 1; ! 129: ! 130: (*req) += datalen; ! 131: (*len) -= datalen; ! 132: ! 133: return 0; ! 134: } ! 135: ! 136: static ssize_t sdp_svc_search(struct bt_l2cap_sdp_state_s *sdp, ! 137: uint8_t *rsp, const uint8_t *req, ssize_t len) ! 138: { ! 139: ssize_t seqlen; ! 140: int i, count, start, end, max; ! 141: int32_t handle; ! 142: ! 143: /* Perform the search */ ! 144: for (i = 0; i < sdp->services; i ++) ! 145: sdp->service_list[i].match = 0; ! 146: ! 147: if (len < 1) ! 148: return -SDP_INVALID_SYNTAX; ! 149: if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) { ! 150: seqlen = sdp_datalen(&req, &len); ! 151: if (seqlen < 3 || len < seqlen) ! 152: return -SDP_INVALID_SYNTAX; ! 153: len -= seqlen; ! 154: ! 155: while (seqlen) ! 156: if (sdp_svc_match(sdp, &req, &seqlen)) ! 157: return -SDP_INVALID_SYNTAX; ! 158: } else if (sdp_svc_match(sdp, &req, &seqlen)) ! 159: return -SDP_INVALID_SYNTAX; ! 160: ! 161: if (len < 3) ! 162: return -SDP_INVALID_SYNTAX; ! 163: end = (req[0] << 8) | req[1]; ! 164: req += 2; ! 165: len -= 2; ! 166: ! 167: if (*req) { ! 168: if (len <= sizeof(int)) ! 169: return -SDP_INVALID_SYNTAX; ! 170: len -= sizeof(int); ! 171: memcpy(&start, req + 1, sizeof(int)); ! 172: } else ! 173: start = 0; ! 174: ! 175: if (len > 1); ! 176: return -SDP_INVALID_SYNTAX; ! 177: ! 178: /* Output the results */ ! 179: len = 4; ! 180: count = 0; ! 181: end = start; ! 182: for (i = 0; i < sdp->services; i ++) ! 183: if (sdp->service_list[i].match) { ! 184: if (count >= start && count < max && len + 4 < MAX_RSP_PARAM_SIZE) { ! 185: handle = i; ! 186: memcpy(rsp + len, &handle, 4); ! 187: len += 4; ! 188: end = count + 1; ! 189: } ! 190: ! 191: count ++; ! 192: } ! 193: ! 194: rsp[0] = count >> 8; ! 195: rsp[1] = count & 0xff; ! 196: rsp[2] = (end - start) >> 8; ! 197: rsp[3] = (end - start) & 0xff; ! 198: ! 199: if (end < count) { ! 200: rsp[len ++] = sizeof(int); ! 201: memcpy(rsp + len, &end, sizeof(int)); ! 202: len += 4; ! 203: } else ! 204: rsp[len ++] = 0; ! 205: ! 206: return len; ! 207: } ! 208: ! 209: static int sdp_attr_match(struct sdp_service_record_s *record, ! 210: const uint8_t **req, ssize_t *len) ! 211: { ! 212: int i, start, end; ! 213: ! 214: if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_2)) { ! 215: (*req) ++; ! 216: if (*len < 3) ! 217: return 1; ! 218: ! 219: start = (*(*req) ++) << 8; ! 220: start |= *(*req) ++; ! 221: end = start; ! 222: *len -= 3; ! 223: } else if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_4)) { ! 224: (*req) ++; ! 225: if (*len < 5) ! 226: return 1; ! 227: ! 228: start = (*(*req) ++) << 8; ! 229: start |= *(*req) ++; ! 230: end = (*(*req) ++) << 8; ! 231: end |= *(*req) ++; ! 232: *len -= 5; ! 233: } else ! 234: return 1; ! 235: ! 236: for (i = 0; i < record->attributes; i ++) ! 237: if (record->attribute_list[i].attribute_id >= start && ! 238: record->attribute_list[i].attribute_id <= end) ! 239: record->attribute_list[i].match = 1; ! 240: ! 241: return 0; ! 242: } ! 243: ! 244: static ssize_t sdp_attr_get(struct bt_l2cap_sdp_state_s *sdp, ! 245: uint8_t *rsp, const uint8_t *req, ssize_t len) ! 246: { ! 247: ssize_t seqlen; ! 248: int i, start, end, max; ! 249: int32_t handle; ! 250: struct sdp_service_record_s *record; ! 251: uint8_t *lst; ! 252: ! 253: /* Perform the search */ ! 254: if (len < 7) ! 255: return -SDP_INVALID_SYNTAX; ! 256: memcpy(&handle, req, 4); ! 257: req += 4; ! 258: len -= 4; ! 259: ! 260: if (handle < 0 || handle > sdp->services) ! 261: return -SDP_INVALID_RECORD_HANDLE; ! 262: record = &sdp->service_list[handle]; ! 263: ! 264: for (i = 0; i < record->attributes; i ++) ! 265: record->attribute_list[i].match = 0; ! 266: ! 267: max = (req[0] << 8) | req[1]; ! 268: req += 2; ! 269: len -= 2; ! 270: if (max < 0x0007) ! 271: return -SDP_INVALID_SYNTAX; ! 272: ! 273: if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) { ! 274: seqlen = sdp_datalen(&req, &len); ! 275: if (seqlen < 3 || len < seqlen) ! 276: return -SDP_INVALID_SYNTAX; ! 277: len -= seqlen; ! 278: ! 279: while (seqlen) ! 280: if (sdp_attr_match(record, &req, &seqlen)) ! 281: return -SDP_INVALID_SYNTAX; ! 282: } else if (sdp_attr_match(record, &req, &seqlen)) ! 283: return -SDP_INVALID_SYNTAX; ! 284: ! 285: if (len < 1) ! 286: return -SDP_INVALID_SYNTAX; ! 287: ! 288: if (*req) { ! 289: if (len <= sizeof(int)) ! 290: return -SDP_INVALID_SYNTAX; ! 291: len -= sizeof(int); ! 292: memcpy(&start, req + 1, sizeof(int)); ! 293: } else ! 294: start = 0; ! 295: ! 296: if (len > 1) ! 297: return -SDP_INVALID_SYNTAX; ! 298: ! 299: /* Output the results */ ! 300: lst = rsp + 2; ! 301: max = MIN(max, MAX_RSP_PARAM_SIZE); ! 302: len = 3 - start; ! 303: end = 0; ! 304: for (i = 0; i < record->attributes; i ++) ! 305: if (record->attribute_list[i].match) { ! 306: if (len >= 0 && len + record->attribute_list[i].len < max) { ! 307: memcpy(lst + len, record->attribute_list[i].pair, ! 308: record->attribute_list[i].len); ! 309: end = len + record->attribute_list[i].len; ! 310: } ! 311: len += record->attribute_list[i].len; ! 312: } ! 313: if (0 >= start) { ! 314: lst[0] = SDP_DTYPE_SEQ | SDP_DSIZE_NEXT2; ! 315: lst[1] = (len + start - 3) >> 8; ! 316: lst[2] = (len + start - 3) & 0xff; ! 317: } ! 318: ! 319: rsp[0] = end >> 8; ! 320: rsp[1] = end & 0xff; ! 321: ! 322: if (end < len) { ! 323: len = end + start; ! 324: lst[end ++] = sizeof(int); ! 325: memcpy(lst + end, &len, sizeof(int)); ! 326: end += sizeof(int); ! 327: } else ! 328: lst[end ++] = 0; ! 329: ! 330: return end + 2; ! 331: } ! 332: ! 333: static int sdp_svc_attr_match(struct bt_l2cap_sdp_state_s *sdp, ! 334: const uint8_t **req, ssize_t *len) ! 335: { ! 336: int i, j, start, end; ! 337: struct sdp_service_record_s *record; ! 338: ! 339: if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_2)) { ! 340: (*req) ++; ! 341: if (*len < 3) ! 342: return 1; ! 343: ! 344: start = (*(*req) ++) << 8; ! 345: start |= *(*req) ++; ! 346: end = start; ! 347: *len -= 3; ! 348: } else if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_4)) { ! 349: (*req) ++; ! 350: if (*len < 5) ! 351: return 1; ! 352: ! 353: start = (*(*req) ++) << 8; ! 354: start |= *(*req) ++; ! 355: end = (*(*req) ++) << 8; ! 356: end |= *(*req) ++; ! 357: *len -= 5; ! 358: } else ! 359: return 1; ! 360: ! 361: for (i = 0; i < sdp->services; i ++) ! 362: if ((record = &sdp->service_list[i])->match) ! 363: for (j = 0; j < record->attributes; j ++) ! 364: if (record->attribute_list[j].attribute_id >= start && ! 365: record->attribute_list[j].attribute_id <= end) ! 366: record->attribute_list[j].match = 1; ! 367: ! 368: return 0; ! 369: } ! 370: ! 371: static ssize_t sdp_svc_search_attr_get(struct bt_l2cap_sdp_state_s *sdp, ! 372: uint8_t *rsp, const uint8_t *req, ssize_t len) ! 373: { ! 374: ssize_t seqlen; ! 375: int i, j, start, end, max; ! 376: struct sdp_service_record_s *record; ! 377: uint8_t *lst; ! 378: ! 379: /* Perform the search */ ! 380: for (i = 0; i < sdp->services; i ++) { ! 381: sdp->service_list[i].match = 0; ! 382: for (j = 0; j < sdp->service_list[i].attributes; j ++) ! 383: sdp->service_list[i].attribute_list[j].match = 0; ! 384: } ! 385: ! 386: if (len < 1) ! 387: return -SDP_INVALID_SYNTAX; ! 388: if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) { ! 389: seqlen = sdp_datalen(&req, &len); ! 390: if (seqlen < 3 || len < seqlen) ! 391: return -SDP_INVALID_SYNTAX; ! 392: len -= seqlen; ! 393: ! 394: while (seqlen) ! 395: if (sdp_svc_match(sdp, &req, &seqlen)) ! 396: return -SDP_INVALID_SYNTAX; ! 397: } else if (sdp_svc_match(sdp, &req, &seqlen)) ! 398: return -SDP_INVALID_SYNTAX; ! 399: ! 400: if (len < 3) ! 401: return -SDP_INVALID_SYNTAX; ! 402: max = (req[0] << 8) | req[1]; ! 403: req += 2; ! 404: len -= 2; ! 405: if (max < 0x0007) ! 406: return -SDP_INVALID_SYNTAX; ! 407: ! 408: if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) { ! 409: seqlen = sdp_datalen(&req, &len); ! 410: if (seqlen < 3 || len < seqlen) ! 411: return -SDP_INVALID_SYNTAX; ! 412: len -= seqlen; ! 413: ! 414: while (seqlen) ! 415: if (sdp_svc_attr_match(sdp, &req, &seqlen)) ! 416: return -SDP_INVALID_SYNTAX; ! 417: } else if (sdp_svc_attr_match(sdp, &req, &seqlen)) ! 418: return -SDP_INVALID_SYNTAX; ! 419: ! 420: if (len < 1) ! 421: return -SDP_INVALID_SYNTAX; ! 422: ! 423: if (*req) { ! 424: if (len <= sizeof(int)) ! 425: return -SDP_INVALID_SYNTAX; ! 426: len -= sizeof(int); ! 427: memcpy(&start, req + 1, sizeof(int)); ! 428: } else ! 429: start = 0; ! 430: ! 431: if (len > 1) ! 432: return -SDP_INVALID_SYNTAX; ! 433: ! 434: /* Output the results */ ! 435: /* This assumes empty attribute lists are never to be returned even ! 436: * for matching Service Records. In practice this shouldn't happen ! 437: * as the requestor will usually include the always present ! 438: * ServiceRecordHandle AttributeID in AttributeIDList. */ ! 439: lst = rsp + 2; ! 440: max = MIN(max, MAX_RSP_PARAM_SIZE); ! 441: len = 3 - start; ! 442: end = 0; ! 443: for (i = 0; i < sdp->services; i ++) ! 444: if ((record = &sdp->service_list[i])->match) { ! 445: len += 3; ! 446: seqlen = len; ! 447: for (j = 0; j < record->attributes; j ++) ! 448: if (record->attribute_list[j].match) { ! 449: if (len >= 0) ! 450: if (len + record->attribute_list[j].len < max) { ! 451: memcpy(lst + len, record->attribute_list[j].pair, ! 452: record->attribute_list[j].len); ! 453: end = len + record->attribute_list[j].len; ! 454: } ! 455: len += record->attribute_list[j].len; ! 456: } ! 457: if (seqlen == len) ! 458: len -= 3; ! 459: else if (seqlen >= 3 && seqlen < max) { ! 460: lst[seqlen - 3] = SDP_DTYPE_SEQ | SDP_DSIZE_NEXT2; ! 461: lst[seqlen - 2] = (len - seqlen) >> 8; ! 462: lst[seqlen - 1] = (len - seqlen) & 0xff; ! 463: } ! 464: } ! 465: if (len == 3 - start) ! 466: len -= 3; ! 467: else if (0 >= start) { ! 468: lst[0] = SDP_DTYPE_SEQ | SDP_DSIZE_NEXT2; ! 469: lst[1] = (len + start - 3) >> 8; ! 470: lst[2] = (len + start - 3) & 0xff; ! 471: } ! 472: ! 473: rsp[0] = end >> 8; ! 474: rsp[1] = end & 0xff; ! 475: ! 476: if (end < len) { ! 477: len = end + start; ! 478: lst[end ++] = sizeof(int); ! 479: memcpy(lst + end, &len, sizeof(int)); ! 480: end += sizeof(int); ! 481: } else ! 482: lst[end ++] = 0; ! 483: ! 484: return end + 2; ! 485: } ! 486: ! 487: static void bt_l2cap_sdp_sdu_in(void *opaque, const uint8_t *data, int len) ! 488: { ! 489: struct bt_l2cap_sdp_state_s *sdp = opaque; ! 490: enum bt_sdp_cmd pdu_id; ! 491: uint8_t rsp[MAX_PDU_OUT_SIZE - PDU_HEADER_SIZE], *sdu_out; ! 492: int transaction_id, plen; ! 493: int err = 0; ! 494: int rsp_len = 0; ! 495: ! 496: if (len < 5) { ! 497: fprintf(stderr, "%s: short SDP PDU (%iB).\n", __FUNCTION__, len); ! 498: return; ! 499: } ! 500: ! 501: pdu_id = *data ++; ! 502: transaction_id = (data[0] << 8) | data[1]; ! 503: plen = (data[2] << 8) | data[3]; ! 504: data += 4; ! 505: len -= 5; ! 506: ! 507: if (len != plen) { ! 508: fprintf(stderr, "%s: wrong SDP PDU length (%iB != %iB).\n", ! 509: __FUNCTION__, plen, len); ! 510: err = SDP_INVALID_PDU_SIZE; ! 511: goto respond; ! 512: } ! 513: ! 514: switch (pdu_id) { ! 515: case SDP_SVC_SEARCH_REQ: ! 516: rsp_len = sdp_svc_search(sdp, rsp, data, len); ! 517: pdu_id = SDP_SVC_SEARCH_RSP; ! 518: break; ! 519: ! 520: case SDP_SVC_ATTR_REQ: ! 521: rsp_len = sdp_attr_get(sdp, rsp, data, len); ! 522: pdu_id = SDP_SVC_ATTR_RSP; ! 523: break; ! 524: ! 525: case SDP_SVC_SEARCH_ATTR_REQ: ! 526: rsp_len = sdp_svc_search_attr_get(sdp, rsp, data, len); ! 527: pdu_id = SDP_SVC_SEARCH_ATTR_RSP; ! 528: break; ! 529: ! 530: case SDP_ERROR_RSP: ! 531: case SDP_SVC_ATTR_RSP: ! 532: case SDP_SVC_SEARCH_RSP: ! 533: case SDP_SVC_SEARCH_ATTR_RSP: ! 534: default: ! 535: fprintf(stderr, "%s: unexpected SDP PDU ID %02x.\n", ! 536: __FUNCTION__, pdu_id); ! 537: err = SDP_INVALID_SYNTAX; ! 538: break; ! 539: } ! 540: ! 541: if (rsp_len < 0) { ! 542: err = -rsp_len; ! 543: rsp_len = 0; ! 544: } ! 545: ! 546: respond: ! 547: if (err) { ! 548: pdu_id = SDP_ERROR_RSP; ! 549: rsp[rsp_len ++] = err >> 8; ! 550: rsp[rsp_len ++] = err & 0xff; ! 551: } ! 552: ! 553: sdu_out = sdp->channel->sdu_out(sdp->channel, rsp_len + PDU_HEADER_SIZE); ! 554: ! 555: sdu_out[0] = pdu_id; ! 556: sdu_out[1] = transaction_id >> 8; ! 557: sdu_out[2] = transaction_id & 0xff; ! 558: sdu_out[3] = rsp_len >> 8; ! 559: sdu_out[4] = rsp_len & 0xff; ! 560: memcpy(sdu_out + PDU_HEADER_SIZE, rsp, rsp_len); ! 561: ! 562: sdp->channel->sdu_submit(sdp->channel); ! 563: } ! 564: ! 565: static void bt_l2cap_sdp_close_ch(void *opaque) ! 566: { ! 567: struct bt_l2cap_sdp_state_s *sdp = opaque; ! 568: int i; ! 569: ! 570: for (i = 0; i < sdp->services; i ++) { ! 571: qemu_free(sdp->service_list[i].attribute_list->pair); ! 572: qemu_free(sdp->service_list[i].attribute_list); ! 573: qemu_free(sdp->service_list[i].uuid); ! 574: } ! 575: qemu_free(sdp->service_list); ! 576: qemu_free(sdp); ! 577: } ! 578: ! 579: struct sdp_def_service_s { ! 580: uint16_t class_uuid; ! 581: struct sdp_def_attribute_s { ! 582: uint16_t id; ! 583: struct sdp_def_data_element_s { ! 584: uint8_t type; ! 585: union { ! 586: uint32_t uint; ! 587: const char *str; ! 588: struct sdp_def_data_element_s *list; ! 589: } value; ! 590: } data; ! 591: } attributes[]; ! 592: }; ! 593: ! 594: /* Calculate a safe byte count to allocate that will store the given ! 595: * element, at the same time count elements of a UUID type. */ ! 596: static int sdp_attr_max_size(struct sdp_def_data_element_s *element, ! 597: int *uuids) ! 598: { ! 599: int type = element->type & ~SDP_DSIZE_MASK; ! 600: int len; ! 601: ! 602: if (type == SDP_DTYPE_UINT || type == SDP_DTYPE_UUID || ! 603: type == SDP_DTYPE_BOOL) { ! 604: if (type == SDP_DTYPE_UUID) ! 605: (*uuids) ++; ! 606: return 1 + (1 << (element->type & SDP_DSIZE_MASK)); ! 607: } ! 608: ! 609: if (type == SDP_DTYPE_STRING || type == SDP_DTYPE_URL) { ! 610: if (element->type & SDP_DSIZE_MASK) { ! 611: for (len = 0; element->value.str[len] | ! 612: element->value.str[len + 1]; len ++); ! 613: return len; ! 614: } else ! 615: return 2 + strlen(element->value.str); ! 616: } ! 617: ! 618: if (type != SDP_DTYPE_SEQ) ! 619: exit(-1); ! 620: len = 2; ! 621: element = element->value.list; ! 622: while (element->type) ! 623: len += sdp_attr_max_size(element ++, uuids); ! 624: if (len > 255) ! 625: exit (-1); ! 626: ! 627: return len; ! 628: } ! 629: ! 630: static int sdp_attr_write(uint8_t *data, ! 631: struct sdp_def_data_element_s *element, int **uuid) ! 632: { ! 633: int type = element->type & ~SDP_DSIZE_MASK; ! 634: int len = 0; ! 635: ! 636: if (type == SDP_DTYPE_UINT || type == SDP_DTYPE_BOOL) { ! 637: data[len ++] = element->type; ! 638: if ((element->type & SDP_DSIZE_MASK) == SDP_DSIZE_1) ! 639: data[len ++] = (element->value.uint >> 0) & 0xff; ! 640: else if ((element->type & SDP_DSIZE_MASK) == SDP_DSIZE_2) { ! 641: data[len ++] = (element->value.uint >> 8) & 0xff; ! 642: data[len ++] = (element->value.uint >> 0) & 0xff; ! 643: } else if ((element->type & SDP_DSIZE_MASK) == SDP_DSIZE_4) { ! 644: data[len ++] = (element->value.uint >> 24) & 0xff; ! 645: data[len ++] = (element->value.uint >> 16) & 0xff; ! 646: data[len ++] = (element->value.uint >> 8) & 0xff; ! 647: data[len ++] = (element->value.uint >> 0) & 0xff; ! 648: } ! 649: ! 650: return len; ! 651: } ! 652: ! 653: if (type == SDP_DTYPE_UUID) { ! 654: *(*uuid) ++ = element->value.uint; ! 655: ! 656: data[len ++] = element->type; ! 657: data[len ++] = (element->value.uint >> 24) & 0xff; ! 658: data[len ++] = (element->value.uint >> 16) & 0xff; ! 659: data[len ++] = (element->value.uint >> 8) & 0xff; ! 660: data[len ++] = (element->value.uint >> 0) & 0xff; ! 661: memcpy(data + len, bt_base_uuid, 12); ! 662: ! 663: return len + 12; ! 664: } ! 665: ! 666: data[0] = type | SDP_DSIZE_NEXT1; ! 667: if (type == SDP_DTYPE_STRING || type == SDP_DTYPE_URL) { ! 668: if (element->type & SDP_DSIZE_MASK) ! 669: for (len = 0; element->value.str[len] | ! 670: element->value.str[len + 1]; len ++); ! 671: else ! 672: len = strlen(element->value.str); ! 673: memcpy(data + 2, element->value.str, data[1] = len); ! 674: ! 675: return len + 2; ! 676: } ! 677: ! 678: len = 2; ! 679: element = element->value.list; ! 680: while (element->type) ! 681: len += sdp_attr_write(data + len, element ++, uuid); ! 682: data[1] = len - 2; ! 683: ! 684: return len; ! 685: } ! 686: ! 687: static int sdp_attributeid_compare(const struct sdp_service_attribute_s *a, ! 688: const struct sdp_service_attribute_s *b) ! 689: { ! 690: return (int) b->attribute_id - a->attribute_id; ! 691: } ! 692: ! 693: static int sdp_uuid_compare(const int *a, const int *b) ! 694: { ! 695: return *a - *b; ! 696: } ! 697: ! 698: static void sdp_service_record_build(struct sdp_service_record_s *record, ! 699: struct sdp_def_service_s *def, int handle) ! 700: { ! 701: int len = 0; ! 702: uint8_t *data; ! 703: int *uuid; ! 704: ! 705: record->uuids = 0; ! 706: while (def->attributes[record->attributes].data.type) { ! 707: len += 3; ! 708: len += sdp_attr_max_size(&def->attributes[record->attributes ++].data, ! 709: &record->uuids); ! 710: } ! 711: record->uuids = 1 << ffs(record->uuids - 1); ! 712: record->attribute_list = ! 713: qemu_mallocz(record->attributes * sizeof(*record->attribute_list)); ! 714: record->uuid = ! 715: qemu_mallocz(record->uuids * sizeof(*record->uuid)); ! 716: data = qemu_malloc(len); ! 717: ! 718: record->attributes = 0; ! 719: uuid = record->uuid; ! 720: while (def->attributes[record->attributes].data.type) { ! 721: record->attribute_list[record->attributes].pair = data; ! 722: ! 723: len = 0; ! 724: data[len ++] = SDP_DTYPE_UINT | SDP_DSIZE_2; ! 725: data[len ++] = def->attributes[record->attributes].id >> 8; ! 726: data[len ++] = def->attributes[record->attributes].id & 0xff; ! 727: len += sdp_attr_write(data + len, ! 728: &def->attributes[record->attributes].data, &uuid); ! 729: ! 730: /* Special case: assign a ServiceRecordHandle in sequence */ ! 731: if (def->attributes[record->attributes].id == SDP_ATTR_RECORD_HANDLE) ! 732: def->attributes[record->attributes].data.value.uint = handle; ! 733: /* Note: we could also assign a ServiceDescription based on ! 734: * sdp->device.device->lmp_name. */ ! 735: ! 736: record->attribute_list[record->attributes ++].len = len; ! 737: data += len; ! 738: } ! 739: ! 740: /* Sort the attribute list by the AttributeID */ ! 741: qsort(record->attribute_list, record->attributes, ! 742: sizeof(*record->attribute_list), ! 743: (void *) sdp_attributeid_compare); ! 744: /* Sort the searchable UUIDs list for bisection */ ! 745: qsort(record->uuid, record->uuids, ! 746: sizeof(*record->uuid), ! 747: (void *) sdp_uuid_compare); ! 748: } ! 749: ! 750: static void sdp_service_db_build(struct bt_l2cap_sdp_state_s *sdp, ! 751: struct sdp_def_service_s **service) ! 752: { ! 753: sdp->services = 0; ! 754: while (service[sdp->services]) ! 755: sdp->services ++; ! 756: sdp->service_list = ! 757: qemu_mallocz(sdp->services * sizeof(*sdp->service_list)); ! 758: ! 759: sdp->services = 0; ! 760: while (*service) { ! 761: sdp_service_record_build(&sdp->service_list[sdp->services], ! 762: *service, sdp->services); ! 763: service ++; ! 764: sdp->services ++; ! 765: } ! 766: } ! 767: ! 768: #define LAST { .type = 0 } ! 769: #define SERVICE(name, attrs) \ ! 770: static struct sdp_def_service_s glue(glue(sdp_service_, name), _s) = { \ ! 771: .attributes = { attrs { .data = LAST } }, \ ! 772: }; ! 773: #define ATTRIBUTE(attrid, val) { .id = glue(SDP_ATTR_, attrid), .data = val }, ! 774: #define UINT8(val) { \ ! 775: .type = SDP_DTYPE_UINT | SDP_DSIZE_1, \ ! 776: .value.uint = val, \ ! 777: }, ! 778: #define UINT16(val) { \ ! 779: .type = SDP_DTYPE_UINT | SDP_DSIZE_2, \ ! 780: .value.uint = val, \ ! 781: }, ! 782: #define UINT32(val) { \ ! 783: .type = SDP_DTYPE_UINT | SDP_DSIZE_4, \ ! 784: .value.uint = val, \ ! 785: }, ! 786: #define UUID128(val) { \ ! 787: .type = SDP_DTYPE_UUID | SDP_DSIZE_16, \ ! 788: .value.uint = val, \ ! 789: }, ! 790: #define TRUE { \ ! 791: .type = SDP_DTYPE_BOOL | SDP_DSIZE_1, \ ! 792: .value.uint = 1, \ ! 793: }, ! 794: #define FALSE { \ ! 795: .type = SDP_DTYPE_BOOL | SDP_DSIZE_1, \ ! 796: .value.uint = 0, \ ! 797: }, ! 798: #define STRING(val) { \ ! 799: .type = SDP_DTYPE_STRING, \ ! 800: .value.str = val, \ ! 801: }, ! 802: #define ARRAY(...) { \ ! 803: .type = SDP_DTYPE_STRING | SDP_DSIZE_2, \ ! 804: .value.str = (char []) { __VA_ARGS__, 0, 0 }, \ ! 805: }, ! 806: #define URL(val) { \ ! 807: .type = SDP_DTYPE_URL, \ ! 808: .value.str = val, \ ! 809: }, ! 810: #if 1 ! 811: #define LIST(val) { \ ! 812: .type = SDP_DTYPE_SEQ, \ ! 813: .value.list = (struct sdp_def_data_element_s []) { val LAST }, \ ! 814: }, ! 815: #endif ! 816: ! 817: /* Try to keep each single attribute below MAX_PDU_OUT_SIZE bytes ! 818: * in resulting SDP data representation size. */ ! 819: ! 820: SERVICE(hid, ! 821: ATTRIBUTE(RECORD_HANDLE, UINT32(0)) /* Filled in later */ ! 822: ATTRIBUTE(SVCLASS_ID_LIST, LIST(UUID128(HID_SVCLASS_ID))) ! 823: ATTRIBUTE(RECORD_STATE, UINT32(1)) ! 824: ATTRIBUTE(PROTO_DESC_LIST, LIST( ! 825: LIST(UUID128(L2CAP_UUID) UINT16(BT_PSM_HID_CTRL)) ! 826: LIST(UUID128(HIDP_UUID)) ! 827: )) ! 828: ATTRIBUTE(BROWSE_GRP_LIST, LIST(UUID128(0x1002))) ! 829: ATTRIBUTE(LANG_BASE_ATTR_ID_LIST, LIST( ! 830: UINT16(0x656e) UINT16(0x006a) UINT16(0x0100) ! 831: )) ! 832: ATTRIBUTE(PFILE_DESC_LIST, LIST( ! 833: LIST(UUID128(HID_PROFILE_ID) UINT16(0x0100)) ! 834: )) ! 835: ATTRIBUTE(DOC_URL, URL("http://bellard.org/qemu/user-doc.html")) ! 836: ATTRIBUTE(SVCNAME_PRIMARY, STRING("QEMU Bluetooth HID")) ! 837: ATTRIBUTE(SVCDESC_PRIMARY, STRING("QEMU Keyboard/Mouse")) ! 838: ATTRIBUTE(SVCPROV_PRIMARY, STRING("QEMU " QEMU_VERSION)) ! 839: ! 840: /* Profile specific */ ! 841: ATTRIBUTE(DEVICE_RELEASE_NUMBER, UINT16(0x0091)) /* Deprecated, remove */ ! 842: ATTRIBUTE(PARSER_VERSION, UINT16(0x0111)) ! 843: /* TODO: extract from l2cap_device->device.class[0] */ ! 844: ATTRIBUTE(DEVICE_SUBCLASS, UINT8(0x40)) ! 845: ATTRIBUTE(COUNTRY_CODE, UINT8(0x15)) ! 846: ATTRIBUTE(VIRTUAL_CABLE, TRUE) ! 847: ATTRIBUTE(RECONNECT_INITIATE, FALSE) ! 848: /* TODO: extract from hid->usbdev->report_desc */ ! 849: ATTRIBUTE(DESCRIPTOR_LIST, LIST( ! 850: LIST(UINT8(0x22) ARRAY( ! 851: 0x05, 0x01, /* Usage Page (Generic Desktop) */ ! 852: 0x09, 0x06, /* Usage (Keyboard) */ ! 853: 0xa1, 0x01, /* Collection (Application) */ ! 854: 0x75, 0x01, /* Report Size (1) */ ! 855: 0x95, 0x08, /* Report Count (8) */ ! 856: 0x05, 0x07, /* Usage Page (Key Codes) */ ! 857: 0x19, 0xe0, /* Usage Minimum (224) */ ! 858: 0x29, 0xe7, /* Usage Maximum (231) */ ! 859: 0x15, 0x00, /* Logical Minimum (0) */ ! 860: 0x25, 0x01, /* Logical Maximum (1) */ ! 861: 0x81, 0x02, /* Input (Data, Variable, Absolute) */ ! 862: 0x95, 0x01, /* Report Count (1) */ ! 863: 0x75, 0x08, /* Report Size (8) */ ! 864: 0x81, 0x01, /* Input (Constant) */ ! 865: 0x95, 0x05, /* Report Count (5) */ ! 866: 0x75, 0x01, /* Report Size (1) */ ! 867: 0x05, 0x08, /* Usage Page (LEDs) */ ! 868: 0x19, 0x01, /* Usage Minimum (1) */ ! 869: 0x29, 0x05, /* Usage Maximum (5) */ ! 870: 0x91, 0x02, /* Output (Data, Variable, Absolute) */ ! 871: 0x95, 0x01, /* Report Count (1) */ ! 872: 0x75, 0x03, /* Report Size (3) */ ! 873: 0x91, 0x01, /* Output (Constant) */ ! 874: 0x95, 0x06, /* Report Count (6) */ ! 875: 0x75, 0x08, /* Report Size (8) */ ! 876: 0x15, 0x00, /* Logical Minimum (0) */ ! 877: 0x25, 0xff, /* Logical Maximum (255) */ ! 878: 0x05, 0x07, /* Usage Page (Key Codes) */ ! 879: 0x19, 0x00, /* Usage Minimum (0) */ ! 880: 0x29, 0xff, /* Usage Maximum (255) */ ! 881: 0x81, 0x00, /* Input (Data, Array) */ ! 882: 0xc0 /* End Collection */ ! 883: )))) ! 884: ATTRIBUTE(LANG_ID_BASE_LIST, LIST( ! 885: LIST(UINT16(0x0409) UINT16(0x0100)) ! 886: )) ! 887: ATTRIBUTE(SDP_DISABLE, FALSE) ! 888: ATTRIBUTE(BATTERY_POWER, TRUE) ! 889: ATTRIBUTE(REMOTE_WAKEUP, TRUE) ! 890: ATTRIBUTE(BOOT_DEVICE, TRUE) /* XXX: untested */ ! 891: ATTRIBUTE(SUPERVISION_TIMEOUT, UINT16(0x0c80)) ! 892: ATTRIBUTE(NORMALLY_CONNECTABLE, TRUE) ! 893: ATTRIBUTE(PROFILE_VERSION, UINT16(0x0100)) ! 894: ) ! 895: ! 896: SERVICE(sdp, ! 897: ATTRIBUTE(RECORD_HANDLE, UINT32(0)) /* Filled in later */ ! 898: ATTRIBUTE(SVCLASS_ID_LIST, LIST(UUID128(SDP_SERVER_SVCLASS_ID))) ! 899: ATTRIBUTE(RECORD_STATE, UINT32(1)) ! 900: ATTRIBUTE(PROTO_DESC_LIST, LIST( ! 901: LIST(UUID128(L2CAP_UUID) UINT16(BT_PSM_SDP)) ! 902: LIST(UUID128(SDP_UUID)) ! 903: )) ! 904: ATTRIBUTE(BROWSE_GRP_LIST, LIST(UUID128(0x1002))) ! 905: ATTRIBUTE(LANG_BASE_ATTR_ID_LIST, LIST( ! 906: UINT16(0x656e) UINT16(0x006a) UINT16(0x0100) ! 907: )) ! 908: ATTRIBUTE(PFILE_DESC_LIST, LIST( ! 909: LIST(UUID128(SDP_SERVER_PROFILE_ID) UINT16(0x0100)) ! 910: )) ! 911: ATTRIBUTE(DOC_URL, URL("http://bellard.org/qemu/user-doc.html")) ! 912: ATTRIBUTE(SVCPROV_PRIMARY, STRING("QEMU " QEMU_VERSION)) ! 913: ! 914: /* Profile specific */ ! 915: ATTRIBUTE(VERSION_NUM_LIST, LIST(UINT16(0x0100))) ! 916: ATTRIBUTE(SVCDB_STATE , UINT32(1)) ! 917: ) ! 918: ! 919: SERVICE(pnp, ! 920: ATTRIBUTE(RECORD_HANDLE, UINT32(0)) /* Filled in later */ ! 921: ATTRIBUTE(SVCLASS_ID_LIST, LIST(UUID128(PNP_INFO_SVCLASS_ID))) ! 922: ATTRIBUTE(RECORD_STATE, UINT32(1)) ! 923: ATTRIBUTE(PROTO_DESC_LIST, LIST( ! 924: LIST(UUID128(L2CAP_UUID) UINT16(BT_PSM_SDP)) ! 925: LIST(UUID128(SDP_UUID)) ! 926: )) ! 927: ATTRIBUTE(BROWSE_GRP_LIST, LIST(UUID128(0x1002))) ! 928: ATTRIBUTE(LANG_BASE_ATTR_ID_LIST, LIST( ! 929: UINT16(0x656e) UINT16(0x006a) UINT16(0x0100) ! 930: )) ! 931: ATTRIBUTE(PFILE_DESC_LIST, LIST( ! 932: LIST(UUID128(PNP_INFO_PROFILE_ID) UINT16(0x0100)) ! 933: )) ! 934: ATTRIBUTE(DOC_URL, URL("http://bellard.org/qemu/user-doc.html")) ! 935: ATTRIBUTE(SVCPROV_PRIMARY, STRING("QEMU " QEMU_VERSION)) ! 936: ! 937: /* Profile specific */ ! 938: ATTRIBUTE(SPECIFICATION_ID, UINT16(0x0100)) ! 939: ATTRIBUTE(VERSION, UINT16(0x0100)) ! 940: ATTRIBUTE(PRIMARY_RECORD, TRUE) ! 941: ) ! 942: ! 943: static int bt_l2cap_sdp_new_ch(struct bt_l2cap_device_s *dev, ! 944: struct bt_l2cap_conn_params_s *params) ! 945: { ! 946: struct bt_l2cap_sdp_state_s *sdp = qemu_mallocz(sizeof(*sdp)); ! 947: struct sdp_def_service_s *services[] = { ! 948: &sdp_service_sdp_s, ! 949: &sdp_service_hid_s, ! 950: &sdp_service_pnp_s, ! 951: 0, ! 952: }; ! 953: ! 954: sdp->channel = params; ! 955: sdp->channel->opaque = sdp; ! 956: sdp->channel->close = bt_l2cap_sdp_close_ch; ! 957: sdp->channel->sdu_in = bt_l2cap_sdp_sdu_in; ! 958: ! 959: sdp_service_db_build(sdp, services); ! 960: ! 961: return 0; ! 962: } ! 963: ! 964: void bt_l2cap_sdp_init(struct bt_l2cap_device_s *dev) ! 965: { ! 966: bt_l2cap_psm_register(dev, BT_PSM_SDP, ! 967: MAX_PDU_OUT_SIZE, bt_l2cap_sdp_new_ch); ! 968: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.