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