Merge remote-tracking branch 'qemu/master'
[qemu/ar7.git] / hw / bt / sdp.c
blobf347dc9db80b8a26ec40e8f930382979b9bdbc6b
1 /*
2 * Service Discover Protocol server for QEMU L2CAP devices
4 * Copyright (C) 2008 Andrzej Zaborowski <balrog@zabor.org>
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.
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.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, see <http://www.gnu.org/licenses/>.
20 #include "qemu/osdep.h"
21 #include "qemu/error-report.h"
22 #include "qemu-common.h"
23 #include "qemu/host-utils.h"
24 #include "hw/bt.h"
26 /* windef.h also defines FALSE, TRUE, so undef it here. */
27 #undef FALSE
28 #undef TRUE
30 struct bt_l2cap_sdp_state_s {
31 struct bt_l2cap_conn_params_s *channel;
33 struct sdp_service_record_s {
34 int match;
36 int *uuid;
37 int uuids;
38 struct sdp_service_attribute_s {
39 int match;
41 int attribute_id;
42 int len;
43 void *pair;
44 } *attribute_list;
45 int attributes;
46 } *service_list;
47 int services;
50 static ssize_t sdp_datalen(const uint8_t **element, ssize_t *left)
52 uint32_t len = *(*element) ++ & SDP_DSIZE_MASK;
54 if (!*left)
55 return -1;
56 (*left) --;
58 if (len < SDP_DSIZE_NEXT1)
59 return 1 << len;
60 else if (len == SDP_DSIZE_NEXT1) {
61 if (*left < 1)
62 return -1;
63 (*left) --;
65 return *(*element) ++;
66 } else if (len == SDP_DSIZE_NEXT2) {
67 if (*left < 2)
68 return -1;
69 (*left) -= 2;
71 len = (*(*element) ++) << 8;
72 return len | (*(*element) ++);
73 } else {
74 if (*left < 4)
75 return -1;
76 (*left) -= 4;
78 len = (*(*element) ++) << 24;
79 len |= (*(*element) ++) << 16;
80 len |= (*(*element) ++) << 8;
81 return len | (*(*element) ++);
85 static const uint8_t bt_base_uuid[12] = {
86 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
89 static int sdp_uuid_match(struct sdp_service_record_s *record,
90 const uint8_t *uuid, ssize_t datalen)
92 int *lo, hi, val;
94 if (datalen == 16 || datalen == 4) {
95 if (datalen == 16 && memcmp(uuid + 4, bt_base_uuid, 12))
96 return 0;
98 if (uuid[0] | uuid[1])
99 return 0;
100 uuid += 2;
103 val = (uuid[0] << 8) | uuid[1];
104 lo = record->uuid;
105 hi = record->uuids;
106 while (hi >>= 1)
107 if (lo[hi] <= val)
108 lo += hi;
110 return *lo == val;
113 #define CONTINUATION_PARAM_SIZE (1 + sizeof(int))
114 #define MAX_PDU_OUT_SIZE 96 /* Arbitrary */
115 #define PDU_HEADER_SIZE 5
116 #define MAX_RSP_PARAM_SIZE (MAX_PDU_OUT_SIZE - PDU_HEADER_SIZE - \
117 CONTINUATION_PARAM_SIZE)
119 static int sdp_svc_match(struct bt_l2cap_sdp_state_s *sdp,
120 const uint8_t **req, ssize_t *len)
122 size_t datalen;
123 int i;
125 if ((**req & ~SDP_DSIZE_MASK) != SDP_DTYPE_UUID)
126 return 1;
128 datalen = sdp_datalen(req, len);
129 if (datalen != 2 && datalen != 4 && datalen != 16)
130 return 1;
132 for (i = 0; i < sdp->services; i ++)
133 if (sdp_uuid_match(&sdp->service_list[i], *req, datalen))
134 sdp->service_list[i].match = 1;
136 (*req) += datalen;
137 (*len) -= datalen;
139 return 0;
142 static ssize_t sdp_svc_search(struct bt_l2cap_sdp_state_s *sdp,
143 uint8_t *rsp, const uint8_t *req, ssize_t len)
145 ssize_t seqlen;
146 int i, count, start, end, max;
147 int32_t handle;
149 /* Perform the search */
150 for (i = 0; i < sdp->services; i ++)
151 sdp->service_list[i].match = 0;
153 if (len < 1)
154 return -SDP_INVALID_SYNTAX;
155 if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) {
156 seqlen = sdp_datalen(&req, &len);
157 if (seqlen < 3 || len < seqlen)
158 return -SDP_INVALID_SYNTAX;
159 len -= seqlen;
160 while (seqlen)
161 if (sdp_svc_match(sdp, &req, &seqlen))
162 return -SDP_INVALID_SYNTAX;
163 } else {
164 if (sdp_svc_match(sdp, &req, &len)) {
165 return -SDP_INVALID_SYNTAX;
169 if (len < 3)
170 return -SDP_INVALID_SYNTAX;
171 max = (req[0] << 8) | req[1];
172 req += 2;
173 len -= 2;
175 if (*req) {
176 if (len <= sizeof(int))
177 return -SDP_INVALID_SYNTAX;
178 len -= sizeof(int);
179 memcpy(&start, req + 1, sizeof(int));
180 } else
181 start = 0;
183 if (len > 1)
184 return -SDP_INVALID_SYNTAX;
186 /* Output the results */
187 len = 4;
188 count = 0;
189 end = start;
190 for (i = 0; i < sdp->services; i ++)
191 if (sdp->service_list[i].match) {
192 if (count >= start && count < max && len + 4 < MAX_RSP_PARAM_SIZE) {
193 handle = i;
194 memcpy(rsp + len, &handle, 4);
195 len += 4;
196 end = count + 1;
199 count ++;
202 rsp[0] = count >> 8;
203 rsp[1] = count & 0xff;
204 rsp[2] = (end - start) >> 8;
205 rsp[3] = (end - start) & 0xff;
207 if (end < count) {
208 rsp[len ++] = sizeof(int);
209 memcpy(rsp + len, &end, sizeof(int));
210 len += 4;
211 } else
212 rsp[len ++] = 0;
214 return len;
217 static int sdp_attr_match(struct sdp_service_record_s *record,
218 const uint8_t **req, ssize_t *len)
220 int i, start, end;
222 if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_2)) {
223 (*req) ++;
224 if (*len < 3)
225 return 1;
227 start = (*(*req) ++) << 8;
228 start |= *(*req) ++;
229 end = start;
230 *len -= 3;
231 } else if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_4)) {
232 (*req) ++;
233 if (*len < 5)
234 return 1;
236 start = (*(*req) ++) << 8;
237 start |= *(*req) ++;
238 end = (*(*req) ++) << 8;
239 end |= *(*req) ++;
240 *len -= 5;
241 } else
242 return 1;
244 for (i = 0; i < record->attributes; i ++)
245 if (record->attribute_list[i].attribute_id >= start &&
246 record->attribute_list[i].attribute_id <= end)
247 record->attribute_list[i].match = 1;
249 return 0;
252 static ssize_t sdp_attr_get(struct bt_l2cap_sdp_state_s *sdp,
253 uint8_t *rsp, const uint8_t *req, ssize_t len)
255 ssize_t seqlen;
256 int i, start, end, max;
257 int32_t handle;
258 struct sdp_service_record_s *record;
259 uint8_t *lst;
261 /* Perform the search */
262 if (len < 7)
263 return -SDP_INVALID_SYNTAX;
264 memcpy(&handle, req, 4);
265 req += 4;
266 len -= 4;
268 if (handle < 0 || handle > sdp->services)
269 return -SDP_INVALID_RECORD_HANDLE;
270 record = &sdp->service_list[handle];
272 for (i = 0; i < record->attributes; i ++)
273 record->attribute_list[i].match = 0;
275 max = (req[0] << 8) | req[1];
276 req += 2;
277 len -= 2;
278 if (max < 0x0007)
279 return -SDP_INVALID_SYNTAX;
281 if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) {
282 seqlen = sdp_datalen(&req, &len);
283 if (seqlen < 3 || len < seqlen)
284 return -SDP_INVALID_SYNTAX;
285 len -= seqlen;
287 while (seqlen)
288 if (sdp_attr_match(record, &req, &seqlen))
289 return -SDP_INVALID_SYNTAX;
290 } else {
291 if (sdp_attr_match(record, &req, &len)) {
292 return -SDP_INVALID_SYNTAX;
296 if (len < 1)
297 return -SDP_INVALID_SYNTAX;
299 if (*req) {
300 if (len <= sizeof(int))
301 return -SDP_INVALID_SYNTAX;
302 len -= sizeof(int);
303 memcpy(&start, req + 1, sizeof(int));
304 } else
305 start = 0;
307 if (len > 1)
308 return -SDP_INVALID_SYNTAX;
310 /* Output the results */
311 lst = rsp + 2;
312 max = MIN(max, MAX_RSP_PARAM_SIZE);
313 len = 3 - start;
314 end = 0;
315 for (i = 0; i < record->attributes; i ++)
316 if (record->attribute_list[i].match) {
317 if (len >= 0 && len + record->attribute_list[i].len < max) {
318 memcpy(lst + len, record->attribute_list[i].pair,
319 record->attribute_list[i].len);
320 end = len + record->attribute_list[i].len;
322 len += record->attribute_list[i].len;
324 if (0 >= start) {
325 lst[0] = SDP_DTYPE_SEQ | SDP_DSIZE_NEXT2;
326 lst[1] = (len + start - 3) >> 8;
327 lst[2] = (len + start - 3) & 0xff;
330 rsp[0] = end >> 8;
331 rsp[1] = end & 0xff;
333 if (end < len) {
334 len = end + start;
335 lst[end ++] = sizeof(int);
336 memcpy(lst + end, &len, sizeof(int));
337 end += sizeof(int);
338 } else
339 lst[end ++] = 0;
341 return end + 2;
344 static int sdp_svc_attr_match(struct bt_l2cap_sdp_state_s *sdp,
345 const uint8_t **req, ssize_t *len)
347 int i, j, start, end;
348 struct sdp_service_record_s *record;
350 if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_2)) {
351 (*req) ++;
352 if (*len < 3)
353 return 1;
355 start = (*(*req) ++) << 8;
356 start |= *(*req) ++;
357 end = start;
358 *len -= 3;
359 } else if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_4)) {
360 (*req) ++;
361 if (*len < 5)
362 return 1;
364 start = (*(*req) ++) << 8;
365 start |= *(*req) ++;
366 end = (*(*req) ++) << 8;
367 end |= *(*req) ++;
368 *len -= 5;
369 } else
370 return 1;
372 for (i = 0; i < sdp->services; i ++)
373 if ((record = &sdp->service_list[i])->match)
374 for (j = 0; j < record->attributes; j ++)
375 if (record->attribute_list[j].attribute_id >= start &&
376 record->attribute_list[j].attribute_id <= end)
377 record->attribute_list[j].match = 1;
379 return 0;
382 static ssize_t sdp_svc_search_attr_get(struct bt_l2cap_sdp_state_s *sdp,
383 uint8_t *rsp, const uint8_t *req, ssize_t len)
385 ssize_t seqlen;
386 int i, j, start, end, max;
387 struct sdp_service_record_s *record;
388 uint8_t *lst;
390 /* Perform the search */
391 for (i = 0; i < sdp->services; i ++) {
392 sdp->service_list[i].match = 0;
393 for (j = 0; j < sdp->service_list[i].attributes; j ++)
394 sdp->service_list[i].attribute_list[j].match = 0;
397 if (len < 1)
398 return -SDP_INVALID_SYNTAX;
399 if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) {
400 seqlen = sdp_datalen(&req, &len);
401 if (seqlen < 3 || len < seqlen)
402 return -SDP_INVALID_SYNTAX;
403 len -= seqlen;
405 while (seqlen)
406 if (sdp_svc_match(sdp, &req, &seqlen))
407 return -SDP_INVALID_SYNTAX;
408 } else {
409 if (sdp_svc_match(sdp, &req, &len)) {
410 return -SDP_INVALID_SYNTAX;
414 if (len < 3)
415 return -SDP_INVALID_SYNTAX;
416 max = (req[0] << 8) | req[1];
417 req += 2;
418 len -= 2;
419 if (max < 0x0007)
420 return -SDP_INVALID_SYNTAX;
422 if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) {
423 seqlen = sdp_datalen(&req, &len);
424 if (seqlen < 3 || len < seqlen)
425 return -SDP_INVALID_SYNTAX;
426 len -= seqlen;
428 while (seqlen)
429 if (sdp_svc_attr_match(sdp, &req, &seqlen))
430 return -SDP_INVALID_SYNTAX;
431 } else {
432 if (sdp_svc_attr_match(sdp, &req, &len)) {
433 return -SDP_INVALID_SYNTAX;
437 if (len < 1)
438 return -SDP_INVALID_SYNTAX;
440 if (*req) {
441 if (len <= sizeof(int))
442 return -SDP_INVALID_SYNTAX;
443 len -= sizeof(int);
444 memcpy(&start, req + 1, sizeof(int));
445 } else
446 start = 0;
448 if (len > 1)
449 return -SDP_INVALID_SYNTAX;
451 /* Output the results */
452 /* This assumes empty attribute lists are never to be returned even
453 * for matching Service Records. In practice this shouldn't happen
454 * as the requestor will usually include the always present
455 * ServiceRecordHandle AttributeID in AttributeIDList. */
456 lst = rsp + 2;
457 max = MIN(max, MAX_RSP_PARAM_SIZE);
458 len = 3 - start;
459 end = 0;
460 for (i = 0; i < sdp->services; i ++)
461 if ((record = &sdp->service_list[i])->match) {
462 len += 3;
463 seqlen = len;
464 for (j = 0; j < record->attributes; j ++)
465 if (record->attribute_list[j].match) {
466 if (len >= 0)
467 if (len + record->attribute_list[j].len < max) {
468 memcpy(lst + len, record->attribute_list[j].pair,
469 record->attribute_list[j].len);
470 end = len + record->attribute_list[j].len;
472 len += record->attribute_list[j].len;
474 if (seqlen == len)
475 len -= 3;
476 else if (seqlen >= 3 && seqlen < max) {
477 lst[seqlen - 3] = SDP_DTYPE_SEQ | SDP_DSIZE_NEXT2;
478 lst[seqlen - 2] = (len - seqlen) >> 8;
479 lst[seqlen - 1] = (len - seqlen) & 0xff;
482 if (len == 3 - start)
483 len -= 3;
484 else if (0 >= start) {
485 lst[0] = SDP_DTYPE_SEQ | SDP_DSIZE_NEXT2;
486 lst[1] = (len + start - 3) >> 8;
487 lst[2] = (len + start - 3) & 0xff;
490 rsp[0] = end >> 8;
491 rsp[1] = end & 0xff;
493 if (end < len) {
494 len = end + start;
495 lst[end ++] = sizeof(int);
496 memcpy(lst + end, &len, sizeof(int));
497 end += sizeof(int);
498 } else
499 lst[end ++] = 0;
501 return end + 2;
504 static void bt_l2cap_sdp_sdu_in(void *opaque, const uint8_t *data, int len)
506 struct bt_l2cap_sdp_state_s *sdp = opaque;
507 enum bt_sdp_cmd pdu_id;
508 uint8_t rsp[MAX_PDU_OUT_SIZE - PDU_HEADER_SIZE], *sdu_out;
509 int transaction_id, plen;
510 int err = 0;
511 int rsp_len = 0;
513 if (len < 5) {
514 error_report("%s: short SDP PDU (%iB).", __func__, len);
515 return;
518 pdu_id = *data ++;
519 transaction_id = (data[0] << 8) | data[1];
520 plen = (data[2] << 8) | data[3];
521 data += 4;
522 len -= 5;
524 if (len != plen) {
525 error_report("%s: wrong SDP PDU length (%iB != %iB).",
526 __func__, plen, len);
527 err = SDP_INVALID_PDU_SIZE;
528 goto respond;
531 switch (pdu_id) {
532 case SDP_SVC_SEARCH_REQ:
533 rsp_len = sdp_svc_search(sdp, rsp, data, len);
534 pdu_id = SDP_SVC_SEARCH_RSP;
535 break;
537 case SDP_SVC_ATTR_REQ:
538 rsp_len = sdp_attr_get(sdp, rsp, data, len);
539 pdu_id = SDP_SVC_ATTR_RSP;
540 break;
542 case SDP_SVC_SEARCH_ATTR_REQ:
543 rsp_len = sdp_svc_search_attr_get(sdp, rsp, data, len);
544 pdu_id = SDP_SVC_SEARCH_ATTR_RSP;
545 break;
547 case SDP_ERROR_RSP:
548 case SDP_SVC_ATTR_RSP:
549 case SDP_SVC_SEARCH_RSP:
550 case SDP_SVC_SEARCH_ATTR_RSP:
551 default:
552 error_report("%s: unexpected SDP PDU ID %02x.",
553 __func__, pdu_id);
554 err = SDP_INVALID_SYNTAX;
555 break;
558 if (rsp_len < 0) {
559 err = -rsp_len;
560 rsp_len = 0;
563 respond:
564 if (err) {
565 pdu_id = SDP_ERROR_RSP;
566 rsp[rsp_len ++] = err >> 8;
567 rsp[rsp_len ++] = err & 0xff;
570 sdu_out = sdp->channel->sdu_out(sdp->channel, rsp_len + PDU_HEADER_SIZE);
572 sdu_out[0] = pdu_id;
573 sdu_out[1] = transaction_id >> 8;
574 sdu_out[2] = transaction_id & 0xff;
575 sdu_out[3] = rsp_len >> 8;
576 sdu_out[4] = rsp_len & 0xff;
577 memcpy(sdu_out + PDU_HEADER_SIZE, rsp, rsp_len);
579 sdp->channel->sdu_submit(sdp->channel);
582 static void bt_l2cap_sdp_close_ch(void *opaque)
584 struct bt_l2cap_sdp_state_s *sdp = opaque;
585 int i;
587 for (i = 0; i < sdp->services; i ++) {
588 g_free(sdp->service_list[i].attribute_list[0].pair);
589 g_free(sdp->service_list[i].attribute_list);
590 g_free(sdp->service_list[i].uuid);
592 g_free(sdp->service_list);
593 g_free(sdp);
596 struct sdp_def_service_s {
597 uint16_t class_uuid;
598 struct sdp_def_attribute_s {
599 uint16_t id;
600 struct sdp_def_data_element_s {
601 uint8_t type;
602 union {
603 uint32_t uint;
604 const char *str;
605 struct sdp_def_data_element_s *list;
606 } value;
607 } data;
608 } attributes[];
611 /* Calculate a safe byte count to allocate that will store the given
612 * element, at the same time count elements of a UUID type. */
613 static int sdp_attr_max_size(struct sdp_def_data_element_s *element,
614 int *uuids)
616 int type = element->type & ~SDP_DSIZE_MASK;
617 int len;
619 if (type == SDP_DTYPE_UINT || type == SDP_DTYPE_UUID ||
620 type == SDP_DTYPE_BOOL) {
621 if (type == SDP_DTYPE_UUID)
622 (*uuids) ++;
623 return 1 + (1 << (element->type & SDP_DSIZE_MASK));
626 if (type == SDP_DTYPE_STRING || type == SDP_DTYPE_URL) {
627 if (element->type & SDP_DSIZE_MASK) {
628 for (len = 0; element->value.str[len] |
629 element->value.str[len + 1]; len ++);
630 return len;
631 } else
632 return 2 + strlen(element->value.str);
635 if (type != SDP_DTYPE_SEQ)
636 exit(-1);
637 len = 2;
638 element = element->value.list;
639 while (element->type)
640 len += sdp_attr_max_size(element ++, uuids);
641 if (len > 255)
642 exit (-1);
644 return len;
647 static int sdp_attr_write(uint8_t *data,
648 struct sdp_def_data_element_s *element, int **uuid)
650 int type = element->type & ~SDP_DSIZE_MASK;
651 int len = 0;
653 if (type == SDP_DTYPE_UINT || type == SDP_DTYPE_BOOL) {
654 data[len ++] = element->type;
655 if ((element->type & SDP_DSIZE_MASK) == SDP_DSIZE_1)
656 data[len ++] = (element->value.uint >> 0) & 0xff;
657 else if ((element->type & SDP_DSIZE_MASK) == SDP_DSIZE_2) {
658 data[len ++] = (element->value.uint >> 8) & 0xff;
659 data[len ++] = (element->value.uint >> 0) & 0xff;
660 } else if ((element->type & SDP_DSIZE_MASK) == SDP_DSIZE_4) {
661 data[len ++] = (element->value.uint >> 24) & 0xff;
662 data[len ++] = (element->value.uint >> 16) & 0xff;
663 data[len ++] = (element->value.uint >> 8) & 0xff;
664 data[len ++] = (element->value.uint >> 0) & 0xff;
667 return len;
670 if (type == SDP_DTYPE_UUID) {
671 *(*uuid) ++ = element->value.uint;
673 data[len ++] = element->type;
674 data[len ++] = (element->value.uint >> 24) & 0xff;
675 data[len ++] = (element->value.uint >> 16) & 0xff;
676 data[len ++] = (element->value.uint >> 8) & 0xff;
677 data[len ++] = (element->value.uint >> 0) & 0xff;
678 memcpy(data + len, bt_base_uuid, 12);
680 return len + 12;
683 data[0] = type | SDP_DSIZE_NEXT1;
684 if (type == SDP_DTYPE_STRING || type == SDP_DTYPE_URL) {
685 if (element->type & SDP_DSIZE_MASK)
686 for (len = 0; element->value.str[len] |
687 element->value.str[len + 1]; len ++);
688 else
689 len = strlen(element->value.str);
690 memcpy(data + 2, element->value.str, data[1] = len);
692 return len + 2;
695 len = 2;
696 element = element->value.list;
697 while (element->type)
698 len += sdp_attr_write(data + len, element ++, uuid);
699 data[1] = len - 2;
701 return len;
704 static int sdp_attributeid_compare(const struct sdp_service_attribute_s *a,
705 const struct sdp_service_attribute_s *b)
707 return (int) b->attribute_id - a->attribute_id;
710 static int sdp_uuid_compare(const int *a, const int *b)
712 return *a - *b;
715 static void sdp_service_record_build(struct sdp_service_record_s *record,
716 struct sdp_def_service_s *def, int handle)
718 int len = 0;
719 uint8_t *data;
720 int *uuid;
722 record->uuids = 0;
723 while (def->attributes[record->attributes].data.type) {
724 len += 3;
725 len += sdp_attr_max_size(&def->attributes[record->attributes ++].data,
726 &record->uuids);
729 assert(len > 0);
730 record->uuids = pow2ceil(record->uuids);
731 record->attribute_list =
732 g_malloc0(record->attributes * sizeof(*record->attribute_list));
733 record->uuid =
734 g_malloc0(record->uuids * sizeof(*record->uuid));
735 data = g_malloc(len);
737 record->attributes = 0;
738 uuid = record->uuid;
739 while (def->attributes[record->attributes].data.type) {
740 int attribute_id = def->attributes[record->attributes].id;
741 record->attribute_list[record->attributes].pair = data;
742 record->attribute_list[record->attributes].attribute_id = attribute_id;
744 len = 0;
745 data[len ++] = SDP_DTYPE_UINT | SDP_DSIZE_2;
746 data[len ++] = attribute_id >> 8;
747 data[len ++] = attribute_id & 0xff;
748 len += sdp_attr_write(data + len,
749 &def->attributes[record->attributes].data, &uuid);
751 /* Special case: assign a ServiceRecordHandle in sequence */
752 if (def->attributes[record->attributes].id == SDP_ATTR_RECORD_HANDLE)
753 def->attributes[record->attributes].data.value.uint = handle;
754 /* Note: we could also assign a ServiceDescription based on
755 * sdp->device.device->lmp_name. */
757 record->attribute_list[record->attributes ++].len = len;
758 data += len;
761 /* Sort the attribute list by the AttributeID. The first must be
762 * SDP_ATTR_RECORD_HANDLE so that bt_l2cap_sdp_close_ch can free
763 * the buffer.
765 qsort(record->attribute_list, record->attributes,
766 sizeof(*record->attribute_list),
767 (void *) sdp_attributeid_compare);
768 assert(record->attribute_list[0].pair == data);
770 /* Sort the searchable UUIDs list for bisection */
771 qsort(record->uuid, record->uuids,
772 sizeof(*record->uuid),
773 (void *) sdp_uuid_compare);
776 static void sdp_service_db_build(struct bt_l2cap_sdp_state_s *sdp,
777 struct sdp_def_service_s **service)
779 sdp->services = 0;
780 while (service[sdp->services])
781 sdp->services ++;
782 sdp->service_list =
783 g_malloc0(sdp->services * sizeof(*sdp->service_list));
785 sdp->services = 0;
786 while (*service) {
787 sdp_service_record_build(&sdp->service_list[sdp->services],
788 *service, sdp->services);
789 service ++;
790 sdp->services ++;
794 #define LAST { .type = 0 }
795 #define SERVICE(name, attrs) \
796 static struct sdp_def_service_s glue(glue(sdp_service_, name), _s) = { \
797 .attributes = { attrs { .data = LAST } }, \
799 #define ATTRIBUTE(attrid, val) { .id = glue(SDP_ATTR_, attrid), .data = val },
800 #define UINT8(val) { \
801 .type = SDP_DTYPE_UINT | SDP_DSIZE_1, \
802 .value.uint = val, \
804 #define UINT16(val) { \
805 .type = SDP_DTYPE_UINT | SDP_DSIZE_2, \
806 .value.uint = val, \
808 #define UINT32(val) { \
809 .type = SDP_DTYPE_UINT | SDP_DSIZE_4, \
810 .value.uint = val, \
812 #define UUID128(val) { \
813 .type = SDP_DTYPE_UUID | SDP_DSIZE_16, \
814 .value.uint = val, \
816 #define SDP_TRUE { \
817 .type = SDP_DTYPE_BOOL | SDP_DSIZE_1, \
818 .value.uint = 1, \
820 #define SDP_FALSE { \
821 .type = SDP_DTYPE_BOOL | SDP_DSIZE_1, \
822 .value.uint = 0, \
824 #define STRING(val) { \
825 .type = SDP_DTYPE_STRING, \
826 .value.str = val, \
828 #define ARRAY(...) { \
829 .type = SDP_DTYPE_STRING | SDP_DSIZE_2, \
830 .value.str = (char []) { __VA_ARGS__, 0, 0 }, \
832 #define URL(val) { \
833 .type = SDP_DTYPE_URL, \
834 .value.str = val, \
836 #if 1
837 #define LIST(val) { \
838 .type = SDP_DTYPE_SEQ, \
839 .value.list = (struct sdp_def_data_element_s []) { val LAST }, \
841 #endif
843 /* Try to keep each single attribute below MAX_PDU_OUT_SIZE bytes
844 * in resulting SDP data representation size. */
846 SERVICE(hid,
847 ATTRIBUTE(RECORD_HANDLE, UINT32(0)) /* Filled in later */
848 ATTRIBUTE(SVCLASS_ID_LIST, LIST(UUID128(HID_SVCLASS_ID)))
849 ATTRIBUTE(RECORD_STATE, UINT32(1))
850 ATTRIBUTE(PROTO_DESC_LIST, LIST(
851 LIST(UUID128(L2CAP_UUID) UINT16(BT_PSM_HID_CTRL))
852 LIST(UUID128(HIDP_UUID))
854 ATTRIBUTE(BROWSE_GRP_LIST, LIST(UUID128(0x1002)))
855 ATTRIBUTE(LANG_BASE_ATTR_ID_LIST, LIST(
856 UINT16(0x656e) UINT16(0x006a) UINT16(0x0100)
858 ATTRIBUTE(PFILE_DESC_LIST, LIST(
859 LIST(UUID128(HID_PROFILE_ID) UINT16(0x0100))
861 ATTRIBUTE(DOC_URL, URL("http://bellard.org/qemu/user-doc.html"))
862 ATTRIBUTE(SVCNAME_PRIMARY, STRING("QEMU Bluetooth HID"))
863 ATTRIBUTE(SVCDESC_PRIMARY, STRING("QEMU Keyboard/Mouse"))
864 ATTRIBUTE(SVCPROV_PRIMARY, STRING("QEMU"))
866 /* Profile specific */
867 ATTRIBUTE(DEVICE_RELEASE_NUMBER, UINT16(0x0091)) /* Deprecated, remove */
868 ATTRIBUTE(PARSER_VERSION, UINT16(0x0111))
869 /* TODO: extract from l2cap_device->device.class[0] */
870 ATTRIBUTE(DEVICE_SUBCLASS, UINT8(0x40))
871 ATTRIBUTE(COUNTRY_CODE, UINT8(0x15))
872 ATTRIBUTE(VIRTUAL_CABLE, SDP_TRUE)
873 ATTRIBUTE(RECONNECT_INITIATE, SDP_FALSE)
874 /* TODO: extract from hid->usbdev->report_desc */
875 ATTRIBUTE(DESCRIPTOR_LIST, LIST(
876 LIST(UINT8(0x22) ARRAY(
877 0x05, 0x01, /* Usage Page (Generic Desktop) */
878 0x09, 0x06, /* Usage (Keyboard) */
879 0xa1, 0x01, /* Collection (Application) */
880 0x75, 0x01, /* Report Size (1) */
881 0x95, 0x08, /* Report Count (8) */
882 0x05, 0x07, /* Usage Page (Key Codes) */
883 0x19, 0xe0, /* Usage Minimum (224) */
884 0x29, 0xe7, /* Usage Maximum (231) */
885 0x15, 0x00, /* Logical Minimum (0) */
886 0x25, 0x01, /* Logical Maximum (1) */
887 0x81, 0x02, /* Input (Data, Variable, Absolute) */
888 0x95, 0x01, /* Report Count (1) */
889 0x75, 0x08, /* Report Size (8) */
890 0x81, 0x01, /* Input (Constant) */
891 0x95, 0x05, /* Report Count (5) */
892 0x75, 0x01, /* Report Size (1) */
893 0x05, 0x08, /* Usage Page (LEDs) */
894 0x19, 0x01, /* Usage Minimum (1) */
895 0x29, 0x05, /* Usage Maximum (5) */
896 0x91, 0x02, /* Output (Data, Variable, Absolute) */
897 0x95, 0x01, /* Report Count (1) */
898 0x75, 0x03, /* Report Size (3) */
899 0x91, 0x01, /* Output (Constant) */
900 0x95, 0x06, /* Report Count (6) */
901 0x75, 0x08, /* Report Size (8) */
902 0x15, 0x00, /* Logical Minimum (0) */
903 0x25, 0xff, /* Logical Maximum (255) */
904 0x05, 0x07, /* Usage Page (Key Codes) */
905 0x19, 0x00, /* Usage Minimum (0) */
906 0x29, 0xff, /* Usage Maximum (255) */
907 0x81, 0x00, /* Input (Data, Array) */
908 0xc0 /* End Collection */
909 ))))
910 ATTRIBUTE(LANG_ID_BASE_LIST, LIST(
911 LIST(UINT16(0x0409) UINT16(0x0100))
913 ATTRIBUTE(SDP_DISABLE, SDP_FALSE)
914 ATTRIBUTE(BATTERY_POWER, SDP_TRUE)
915 ATTRIBUTE(REMOTE_WAKEUP, SDP_TRUE)
916 ATTRIBUTE(BOOT_DEVICE, SDP_TRUE) /* XXX: untested */
917 ATTRIBUTE(SUPERVISION_TIMEOUT, UINT16(0x0c80))
918 ATTRIBUTE(NORMALLY_CONNECTABLE, SDP_TRUE)
919 ATTRIBUTE(PROFILE_VERSION, UINT16(0x0100))
922 SERVICE(sdp,
923 ATTRIBUTE(RECORD_HANDLE, UINT32(0)) /* Filled in later */
924 ATTRIBUTE(SVCLASS_ID_LIST, LIST(UUID128(SDP_SERVER_SVCLASS_ID)))
925 ATTRIBUTE(RECORD_STATE, UINT32(1))
926 ATTRIBUTE(PROTO_DESC_LIST, LIST(
927 LIST(UUID128(L2CAP_UUID) UINT16(BT_PSM_SDP))
928 LIST(UUID128(SDP_UUID))
930 ATTRIBUTE(BROWSE_GRP_LIST, LIST(UUID128(0x1002)))
931 ATTRIBUTE(LANG_BASE_ATTR_ID_LIST, LIST(
932 UINT16(0x656e) UINT16(0x006a) UINT16(0x0100)
934 ATTRIBUTE(PFILE_DESC_LIST, LIST(
935 LIST(UUID128(SDP_SERVER_PROFILE_ID) UINT16(0x0100))
937 ATTRIBUTE(DOC_URL, URL("http://bellard.org/qemu/user-doc.html"))
938 ATTRIBUTE(SVCPROV_PRIMARY, STRING("QEMU"))
940 /* Profile specific */
941 ATTRIBUTE(VERSION_NUM_LIST, LIST(UINT16(0x0100)))
942 ATTRIBUTE(SVCDB_STATE , UINT32(1))
945 SERVICE(pnp,
946 ATTRIBUTE(RECORD_HANDLE, UINT32(0)) /* Filled in later */
947 ATTRIBUTE(SVCLASS_ID_LIST, LIST(UUID128(PNP_INFO_SVCLASS_ID)))
948 ATTRIBUTE(RECORD_STATE, UINT32(1))
949 ATTRIBUTE(PROTO_DESC_LIST, LIST(
950 LIST(UUID128(L2CAP_UUID) UINT16(BT_PSM_SDP))
951 LIST(UUID128(SDP_UUID))
953 ATTRIBUTE(BROWSE_GRP_LIST, LIST(UUID128(0x1002)))
954 ATTRIBUTE(LANG_BASE_ATTR_ID_LIST, LIST(
955 UINT16(0x656e) UINT16(0x006a) UINT16(0x0100)
957 ATTRIBUTE(PFILE_DESC_LIST, LIST(
958 LIST(UUID128(PNP_INFO_PROFILE_ID) UINT16(0x0100))
960 ATTRIBUTE(DOC_URL, URL("http://bellard.org/qemu/user-doc.html"))
961 ATTRIBUTE(SVCPROV_PRIMARY, STRING("QEMU"))
963 /* Profile specific */
964 ATTRIBUTE(SPECIFICATION_ID, UINT16(0x0100))
965 ATTRIBUTE(VERSION, UINT16(0x0100))
966 ATTRIBUTE(PRIMARY_RECORD, SDP_TRUE)
969 static int bt_l2cap_sdp_new_ch(struct bt_l2cap_device_s *dev,
970 struct bt_l2cap_conn_params_s *params)
972 struct bt_l2cap_sdp_state_s *sdp = g_malloc0(sizeof(*sdp));
973 struct sdp_def_service_s *services[] = {
974 &sdp_service_sdp_s,
975 &sdp_service_hid_s,
976 &sdp_service_pnp_s,
977 NULL,
980 sdp->channel = params;
981 sdp->channel->opaque = sdp;
982 sdp->channel->close = bt_l2cap_sdp_close_ch;
983 sdp->channel->sdu_in = bt_l2cap_sdp_sdu_in;
985 sdp_service_db_build(sdp, services);
987 return 0;
990 void bt_l2cap_sdp_init(struct bt_l2cap_device_s *dev)
992 bt_l2cap_psm_register(dev, BT_PSM_SDP,
993 MAX_PDU_OUT_SIZE, bt_l2cap_sdp_new_ch);