root / hw / bt-sdp.c @ f74ab486
History | View | Annotate | Download (28.8 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, see <http://www.gnu.org/licenses/>.
|
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;
|
162 |
end = (req[0] << 8) | req[1]; |
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 |
|
174 |
if (len > 1); |
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 |
}, |
789 |
#define TRUE { \
|
790 |
.type = SDP_DTYPE_BOOL | SDP_DSIZE_1, \ |
791 |
.value.uint = 1, \
|
792 |
}, |
793 |
#define FALSE { \
|
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))
|
845 |
ATTRIBUTE(VIRTUAL_CABLE, TRUE) |
846 |
ATTRIBUTE(RECONNECT_INITIATE, FALSE) |
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 |
)) |
886 |
ATTRIBUTE(SDP_DISABLE, FALSE) |
887 |
ATTRIBUTE(BATTERY_POWER, TRUE) |
888 |
ATTRIBUTE(REMOTE_WAKEUP, TRUE) |
889 |
ATTRIBUTE(BOOT_DEVICE, TRUE) /* XXX: untested */
|
890 |
ATTRIBUTE(SUPERVISION_TIMEOUT, UINT16(0x0c80))
|
891 |
ATTRIBUTE(NORMALLY_CONNECTABLE, TRUE) |
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))
|
939 |
ATTRIBUTE(PRIMARY_RECORD, TRUE) |
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, |
950 |
NULL,
|
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 |
} |