root / hw / bt-sdp.c @ 35ef81d6
History | View | Annotate | Download (28.9 kB)
1 |
/*
|
---|---|
2 |
* Service Discover Protocol server for QEMU L2CAP devices
|
3 |
*
|
4 |
* Copyright (C) 2008 Andrzej Zaborowski <balrog@zabor.org>
|
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 |
} |