Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/usr.sbin/bluetooth/bthidcontrol/sdp.c
103829 views
1
/*-
2
* sdp.c
3
*
4
* SPDX-License-Identifier: BSD-2-Clause
5
*
6
* Copyright (c) 2004 Maksim Yevmenkin <[email protected]>
7
* All rights reserved.
8
*
9
* Redistribution and use in source and binary forms, with or without
10
* modification, are permitted provided that the following conditions
11
* are met:
12
* 1. Redistributions of source code must retain the above copyright
13
* notice, this list of conditions and the following disclaimer.
14
* 2. Redistributions in binary form must reproduce the above copyright
15
* notice, this list of conditions and the following disclaimer in the
16
* documentation and/or other materials provided with the distribution.
17
*
18
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28
* SUCH DAMAGE.
29
*
30
* $Id: sdp.c,v 1.3 2004/02/17 22:14:57 max Exp $
31
*/
32
33
#include <sys/param.h>
34
#include <sys/queue.h>
35
#include <sys/sysctl.h>
36
#define L2CAP_SOCKET_CHECKED
37
#include <bluetooth.h>
38
#include <dev/usb/usb.h>
39
#include <dev/usb/usbhid.h>
40
#include <errno.h>
41
#include <sdp.h>
42
#include <stdio.h>
43
#include <string.h>
44
#include <usbhid.h>
45
#include "bthid_config.h"
46
#include "bthidcontrol.h"
47
48
static int32_t hid_sdp_query (bdaddr_t const *local, struct hid_device *hd, int32_t *error);
49
static int32_t hid_sdp_parse_protocol_descriptor_list (sdp_attr_p a);
50
static int32_t hid_sdp_parse_hid_descriptor (sdp_attr_p a);
51
static int32_t hid_sdp_parse_boolean (sdp_attr_p a);
52
53
/*
54
* Hard coded attribute IDs taken from the
55
* DEVICE IDENTIFICATION PROFILE SPECIFICATION V13 p.12
56
*/
57
58
#define SDP_ATTR_DEVICE_ID_SERVICE_VENDORID 0x0201
59
#define SDP_ATTR_DEVICE_ID_SERVICE_PRODUCTID 0x0202
60
#define SDP_ATTR_DEVICE_ID_SERVICE_VERSION 0x0203
61
#define SDP_ATTR_DEVICE_ID_RANGE SDP_ATTR_RANGE( \
62
SDP_ATTR_DEVICE_ID_SERVICE_VENDORID, SDP_ATTR_DEVICE_ID_SERVICE_VERSION )
63
64
static uint16_t service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE;
65
static uint16_t service_devid = SDP_SERVICE_CLASS_PNP_INFORMATION;
66
static uint32_t attrs_devid = SDP_ATTR_DEVICE_ID_RANGE;
67
68
static uint32_t attrs[] = {
69
SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
70
SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
71
SDP_ATTR_RANGE (SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS,
72
SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS),
73
SDP_ATTR_RANGE( 0x0205, /* HIDReconnectInitiate */
74
0x0205),
75
SDP_ATTR_RANGE( 0x0206, /* HIDDescriptorList */
76
0x0206),
77
SDP_ATTR_RANGE( 0x0209, /* HIDBatteryPower */
78
0x0209),
79
SDP_ATTR_RANGE( 0x020d, /* HIDNormallyConnectable */
80
0x020d)
81
};
82
#define nattrs nitems(attrs)
83
84
static sdp_attr_t values[8];
85
#define nvalues nitems(values)
86
87
static uint8_t buffer[nvalues][512];
88
89
/*
90
* Query remote device
91
*/
92
93
#undef hid_sdp_query_exit
94
#define hid_sdp_query_exit(e) { \
95
if (error != NULL) \
96
*error = (e); \
97
if (ss != NULL) { \
98
sdp_close(ss); \
99
ss = NULL; \
100
} \
101
return (((e) == 0)? 0 : -1); \
102
}
103
104
static void
105
hid_init_return_values() {
106
int i;
107
for (i = 0; i < nvalues; i ++) {
108
values[i].flags = SDP_ATTR_INVALID;
109
values[i].attr = 0;
110
values[i].vlen = sizeof(buffer[i]);
111
values[i].value = buffer[i];
112
}
113
}
114
115
static int32_t
116
hid_sdp_query(bdaddr_t const *local, struct hid_device *hd, int32_t *error)
117
{
118
void *ss = NULL;
119
uint8_t *hid_descriptor = NULL, *v;
120
int32_t i, control_psm = -1, interrupt_psm = -1,
121
reconnect_initiate = -1,
122
normally_connectable = 0, battery_power = 0,
123
hid_descriptor_length = -1, type;
124
int16_t vendor_id = 0, product_id = 0, version = 0;
125
bdaddr_t sdp_local;
126
char devname[HCI_DEVNAME_SIZE];
127
128
if (local == NULL)
129
local = NG_HCI_BDADDR_ANY;
130
if (hd == NULL)
131
hid_sdp_query_exit(EINVAL);
132
133
hid_init_return_values();
134
135
if ((ss = sdp_open(local, &hd->bdaddr)) == NULL)
136
hid_sdp_query_exit(ENOMEM);
137
if (sdp_error(ss) != 0)
138
hid_sdp_query_exit(sdp_error(ss));
139
if (sdp_search(ss, 1, &service, nattrs, attrs, nvalues, values) != 0)
140
hid_sdp_query_exit(sdp_error(ss));
141
142
for (i = 0; i < nvalues; i ++) {
143
if (values[i].flags != SDP_ATTR_OK)
144
continue;
145
146
switch (values[i].attr) {
147
case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
148
control_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
149
break;
150
151
case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS:
152
interrupt_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
153
break;
154
155
case 0x0205: /* HIDReconnectInitiate */
156
reconnect_initiate = hid_sdp_parse_boolean(&values[i]);
157
break;
158
159
case 0x0206: /* HIDDescriptorList */
160
if (hid_sdp_parse_hid_descriptor(&values[i]) == 0) {
161
hid_descriptor = values[i].value;
162
hid_descriptor_length = values[i].vlen;
163
}
164
break;
165
166
case 0x0209: /* HIDBatteryPower */
167
battery_power = hid_sdp_parse_boolean(&values[i]);
168
break;
169
170
case 0x020d: /* HIDNormallyConnectable */
171
normally_connectable = hid_sdp_parse_boolean(&values[i]);
172
break;
173
}
174
}
175
176
hid_init_return_values();
177
178
if (sdp_search(ss, 1, &service_devid, 1, &attrs_devid, nvalues, values) != 0)
179
hid_sdp_query_exit(sdp_error(ss));
180
181
/* Try extract HCI bdaddr from opened SDP session */
182
if (sdp_get_lcaddr(ss, &sdp_local) != 0 ||
183
bt_devname(devname, &sdp_local) == 0)
184
hid_sdp_query_exit(ENOATTR);
185
186
sdp_close(ss);
187
ss = NULL;
188
189
/* If search is successful, scan through return vals */
190
for (i = 0; i < 3; i ++ ) {
191
if (values[i].flags == SDP_ATTR_INVALID )
192
continue;
193
194
/* Expecting tag + uint16_t on all 3 attributes */
195
if (values[i].vlen != 3)
196
continue;
197
198
/* Make sure, we're reading a uint16_t */
199
v = values[i].value;
200
SDP_GET8(type, v);
201
if (type != SDP_DATA_UINT16 )
202
continue;
203
204
switch (values[i].attr) {
205
case SDP_ATTR_DEVICE_ID_SERVICE_VENDORID:
206
SDP_GET16(vendor_id, v);
207
break;
208
case SDP_ATTR_DEVICE_ID_SERVICE_PRODUCTID:
209
SDP_GET16(product_id, v);
210
break;
211
case SDP_ATTR_DEVICE_ID_SERVICE_VERSION:
212
SDP_GET16(version, v);
213
break;
214
default:
215
break;
216
}
217
}
218
219
if (control_psm == -1 || interrupt_psm == -1 ||
220
reconnect_initiate == -1 ||
221
hid_descriptor == NULL || hid_descriptor_length == -1)
222
hid_sdp_query_exit(ENOATTR);
223
hd->name = bt_devremote_name_gen(devname, &hd->bdaddr);
224
hd->vendor_id = vendor_id;
225
hd->product_id = product_id;
226
hd->version = version;
227
hd->control_psm = control_psm;
228
hd->interrupt_psm = interrupt_psm;
229
hd->reconnect_initiate = reconnect_initiate? 1 : 0;
230
hd->battery_power = battery_power? 1 : 0;
231
hd->normally_connectable = normally_connectable? 1 : 0;
232
hd->desc = hid_use_report_desc(hid_descriptor, hid_descriptor_length);
233
if (hd->desc == NULL)
234
hid_sdp_query_exit(ENOMEM);
235
236
return (0);
237
}
238
239
/*
240
* seq len 2
241
* seq len 2
242
* uuid value 3
243
* uint16 value 3
244
* seq len 2
245
* uuid value 3
246
*/
247
248
static int32_t
249
hid_sdp_parse_protocol_descriptor_list(sdp_attr_p a)
250
{
251
uint8_t *ptr = a->value;
252
uint8_t *end = a->value + a->vlen;
253
int32_t type, len, uuid, psm;
254
255
if (end - ptr < 15)
256
return (-1);
257
258
if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) {
259
SDP_GET8(type, ptr);
260
switch (type) {
261
case SDP_DATA_SEQ8:
262
SDP_GET8(len, ptr);
263
break;
264
265
case SDP_DATA_SEQ16:
266
SDP_GET16(len, ptr);
267
break;
268
269
case SDP_DATA_SEQ32:
270
SDP_GET32(len, ptr);
271
break;
272
273
default:
274
return (-1);
275
}
276
if (ptr + len > end)
277
return (-1);
278
}
279
280
SDP_GET8(type, ptr);
281
switch (type) {
282
case SDP_DATA_SEQ8:
283
SDP_GET8(len, ptr);
284
break;
285
286
case SDP_DATA_SEQ16:
287
SDP_GET16(len, ptr);
288
break;
289
290
case SDP_DATA_SEQ32:
291
SDP_GET32(len, ptr);
292
break;
293
294
default:
295
return (-1);
296
}
297
if (ptr + len > end)
298
return (-1);
299
300
/* Protocol */
301
SDP_GET8(type, ptr);
302
switch (type) {
303
case SDP_DATA_SEQ8:
304
SDP_GET8(len, ptr);
305
break;
306
307
case SDP_DATA_SEQ16:
308
SDP_GET16(len, ptr);
309
break;
310
311
case SDP_DATA_SEQ32:
312
SDP_GET32(len, ptr);
313
break;
314
315
default:
316
return (-1);
317
}
318
if (ptr + len > end)
319
return (-1);
320
321
/* UUID */
322
if (ptr + 3 > end)
323
return (-1);
324
SDP_GET8(type, ptr);
325
switch (type) {
326
case SDP_DATA_UUID16:
327
SDP_GET16(uuid, ptr);
328
if (uuid != SDP_UUID_PROTOCOL_L2CAP)
329
return (-1);
330
break;
331
332
case SDP_DATA_UUID32: /* XXX FIXME can we have 32-bit UUID */
333
case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
334
default:
335
return (-1);
336
}
337
338
/* PSM */
339
if (ptr + 3 > end)
340
return (-1);
341
SDP_GET8(type, ptr);
342
if (type != SDP_DATA_UINT16)
343
return (-1);
344
SDP_GET16(psm, ptr);
345
346
return (psm);
347
}
348
349
/*
350
* seq len 2
351
* seq len 2
352
* uint8 value8 2
353
* str value 3
354
*/
355
356
static int32_t
357
hid_sdp_parse_hid_descriptor(sdp_attr_p a)
358
{
359
uint8_t *ptr = a->value;
360
uint8_t *end = a->value + a->vlen;
361
int32_t type, len, descriptor_type;
362
363
if (end - ptr < 9)
364
return (-1);
365
366
SDP_GET8(type, ptr);
367
switch (type) {
368
case SDP_DATA_SEQ8:
369
SDP_GET8(len, ptr);
370
break;
371
372
case SDP_DATA_SEQ16:
373
SDP_GET16(len, ptr);
374
break;
375
376
case SDP_DATA_SEQ32:
377
SDP_GET32(len, ptr);
378
break;
379
380
default:
381
return (-1);
382
}
383
if (ptr + len > end)
384
return (-1);
385
386
while (ptr < end) {
387
/* Descriptor */
388
SDP_GET8(type, ptr);
389
switch (type) {
390
case SDP_DATA_SEQ8:
391
if (ptr + 1 > end)
392
return (-1);
393
SDP_GET8(len, ptr);
394
break;
395
396
case SDP_DATA_SEQ16:
397
if (ptr + 2 > end)
398
return (-1);
399
SDP_GET16(len, ptr);
400
break;
401
402
case SDP_DATA_SEQ32:
403
if (ptr + 4 > end)
404
return (-1);
405
SDP_GET32(len, ptr);
406
break;
407
408
default:
409
return (-1);
410
}
411
412
/* Descripor type */
413
if (ptr + 1 > end)
414
return (-1);
415
SDP_GET8(type, ptr);
416
if (type != SDP_DATA_UINT8 || ptr + 1 > end)
417
return (-1);
418
SDP_GET8(descriptor_type, ptr);
419
420
/* Descriptor value */
421
if (ptr + 1 > end)
422
return (-1);
423
SDP_GET8(type, ptr);
424
switch (type) {
425
case SDP_DATA_STR8:
426
if (ptr + 1 > end)
427
return (-1);
428
SDP_GET8(len, ptr);
429
break;
430
431
case SDP_DATA_STR16:
432
if (ptr + 2 > end)
433
return (-1);
434
SDP_GET16(len, ptr);
435
break;
436
437
case SDP_DATA_STR32:
438
if (ptr + 4 > end)
439
return (-1);
440
SDP_GET32(len, ptr);
441
break;
442
443
default:
444
return (-1);
445
}
446
if (ptr + len > end)
447
return (-1);
448
449
if (descriptor_type == UDESC_REPORT && len > 0) {
450
a->value = ptr;
451
a->vlen = len;
452
453
return (0);
454
}
455
456
ptr += len;
457
}
458
459
return (-1);
460
}
461
462
/* bool8 int8 */
463
static int32_t
464
hid_sdp_parse_boolean(sdp_attr_p a)
465
{
466
if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL)
467
return (-1);
468
469
return (a->value[1]);
470
}
471
472
/* Perform SDP query */
473
static int32_t
474
hid_query(bdaddr_t *bdaddr, int argc, char **argv)
475
{
476
struct hid_device hd;
477
int e;
478
479
memcpy(&hd.bdaddr, bdaddr, sizeof(hd.bdaddr));
480
if (hid_sdp_query(NULL, &hd, &e) < 0) {
481
fprintf(stderr, "Could not perform SDP query on the " \
482
"device %s. %s (%d)\n", bt_ntoa(bdaddr, NULL),
483
strerror(e), e);
484
return (FAILED);
485
}
486
487
print_hid_device(&hd, stdout);
488
489
return (OK);
490
}
491
492
struct bthid_command sdp_commands[] =
493
{
494
{
495
"Query",
496
"Perform SDP query to the specified device and print HID configuration entry\n"\
497
"for the device. The configuration entry should be appended to the Bluetooth\n"\
498
"HID daemon configuration file and the daemon should be restarted.\n",
499
hid_query
500
},
501
{ NULL, NULL, NULL }
502
};
503
504
505