4 * Copyright (c) 2009 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/cdefs.h>
33 __RCSID("$NetBSD: db.c$");
35 #include <bluetooth.h>
45 * Using a prebuilt service record means that providing ServerState
46 * and a non-hardcoded ProviderName are difficult. Look into that later.
49 /* ServiceDiscoveryServer service record */
50 static uint8_t sds_data
[] = {
51 0x09, 0x00, 0x00, // uint16 ServiceRecordHandle
52 0x0a, 0x00, 0x00, 0x00, // uint32 0x00000000
55 0x09, 0x00, 0x01, // uint16 ServiceClassIDList
56 0x35, 0x03, // seq8(3)
57 0x19, 0x10, 0x00, // uuid16 ServiceDiscoveryServer
59 0x09, 0x00, 0x04, // uint16 ProtocolDescriptorList
60 0x35, 0x0d, // seq8(13)
61 0x35, 0x06, // seq8(6)
62 0x19, 0x01, 0x00, // uuid16 L2CAP
63 0x09, 0x00, 0x01, // uint16 L2CAP_PSM_SDP
64 0x35, 0x03, // seq8(3)
65 0x19, 0x00, 0x01, // uuid16 SDP
67 0x09, 0x00, 0x05, // uint16 BrowseGroupList
68 0x35, 0x03, // seq8(3)
69 0x19, 0x10, 0x02, // uuid16 PublicBrowseGroup
71 0x09, 0x00, 0x06, // uint16 LanguageBaseAttributeIDList
72 0x35, 0x09, // seq8(9)
73 0x09, 0x65, 0x6e, // uint16 0x656e ("en")
74 0x09, 0x00, 0x6a, // uint16 106 (UTF-8)
75 0x09, 0x01, 0x00, // uint16 PrimaryLanguageBaseID
77 0x09, 0x01, 0x00, // uint16 PrimaryLanguageBaseID + ServiceNameOffset
78 0x25, 0x1b, 0x42, 0x6c, // str8(27) "Bluetooth service discovery"
79 0x75, 0x65, 0x74, 0x6f,
80 0x6f, 0x74, 0x68, 0x20,
81 0x73, 0x65, 0x72, 0x76,
82 0x69, 0x63, 0x65, 0x20,
83 0x64, 0x69, 0x73, 0x63,
84 0x6f, 0x76, 0x65, 0x72,
87 0x09, 0x01, 0x02, // uint16 PrimaryLanguageBaseID + ProviderNameOffset
88 0x25, 0x06, 0x4e, 0x65, // str8(6) "NetBSD"
89 0x74, 0x42, 0x53, 0x44,
91 0x09, 0x02, 0x00, // uint16 VersionNumberList
92 0x35, 0x03, // seq8(3)
93 0x09, 0x01, 0x00, // uint16 v1.0
96 /* BrowseGroupDescriptor service record */
97 static uint8_t bgd_data
[] = {
98 0x09, 0x00, 0x00, // uint16 ServiceRecordHandle
99 0x0a, 0x00, 0x00, 0x00, // uint32 0x00000001
102 0x09, 0x00, 0x01, // uint16 ServiceClassIDList
103 0x35, 0x03, // seq8(3)
104 0x19, 0x10, 0x01, // uuid16 BrowseGroupDescriptor
106 0x09, 0x00, 0x06, // uint16 LanguageBaseAttributeIDList
107 0x35, 0x09, // seq8(9)
108 0x09, 0x65, 0x6e, // uint16 0x656e ("en")
109 0x09, 0x00, 0x6a, // uint16 106 (UTF-8)
110 0x09, 0x01, 0x00, // uint16 PrimaryLanguageBaseID
112 0x09, 0x01, 0x00, // uint16 PrimaryLanguageBaseID + ServiceNameOffset
113 0x25, 0x12, 0x50, 0x75, // str8(18) "Public Browse Root"
114 0x62, 0x6c, 0x69, 0x63,
115 0x20, 0x42, 0x72, 0x6f,
116 0x77, 0x73, 0x65, 0x20,
117 0x52, 0x6f, 0x6f, 0x74,
119 0x09, 0x02, 0x00, // uint16 GroupID
120 0x19, 0x10, 0x02, // uuid16 PublicBrowseRoot
124 * Initialise the record database with the ServiceDiscoveryServer
125 * and BrowseGroupDescriptor records
128 db_init(server_t
*srv
)
132 LIST_INIT(&srv
->rlist
);
136 d
.end
= sds_data
+ sizeof(sds_data
);
137 if (!db_create(srv
, -1, BDADDR_ANY
, srv
->handle
++, &d
))
141 d
.end
= bgd_data
+ sizeof(bgd_data
);
142 if (!db_create(srv
, -1, BDADDR_ANY
, srv
->handle
++, &d
))
149 * Iterate through records selected by fd. rec should point to a NULL
150 * value to start the iteration, and false will be returned when there
151 * are no more records to return.
154 db_next(server_t
*srv
, int fd
, record_t
**rec
)
159 r
= LIST_FIRST(&srv
->rlist
);
161 r
= LIST_NEXT(*rec
, next
);
163 while (r
!= NULL
&& !FD_ISSET(fd
, &r
->refset
))
164 r
= LIST_NEXT(r
, next
);
167 return (r
== NULL
) ? false : true;
171 * Match a ServiceRecord against a UUID. Note that because we already
172 * know that the record data is valid, we don't need to recurse here
173 * and can just skip over SEQ and ALT headers. Return true if equivalent
177 db_match_uuid(record_t
*rec
, uuid_t
*uuid
)
179 uint8_t *p
= rec
->data
.next
;
182 while (p
< rec
->data
.end
) {
196 case SDP_DATA_UINT16
:
203 case SDP_DATA_UINT32
:
210 case SDP_DATA_UINT64
:
214 case SDP_DATA_INT128
:
215 case SDP_DATA_UINT128
:
234 case SDP_DATA_UUID16
:
235 u
= BLUETOOTH_BASE_UUID
;
236 u
.time_low
= be16dec(p
);
238 if (uuid_equal(&u
, uuid
, NULL
))
244 case SDP_DATA_UUID32
:
245 u
= BLUETOOTH_BASE_UUID
;
246 u
.time_low
= be32dec(p
);
248 if (uuid_equal(&u
, uuid
, NULL
))
254 case SDP_DATA_UUID128
:
257 if (uuid_equal(&u
, uuid
, NULL
))
272 * Select ServiceRecords matching ServiceSearchPattern
274 * A record is selected when it is visible to the client and
275 * contains each and every UUID from the ServiceSearchPattern
278 db_select_ssp(server_t
*srv
, int fd
, sdp_data_t
*ssp
)
284 LIST_FOREACH(r
, &srv
->rlist
, next
) {
288 if (!srv
->fdidx
[fd
].control
289 && !bdaddr_any(&r
->bdaddr
)
290 && !bdaddr_same(&r
->bdaddr
, &srv
->fdidx
[fd
].bdaddr
))
295 if (!sdp_get_uuid(&s
, &u
)) {
296 /* matched all UUIDs */
297 FD_SET(fd
, &r
->refset
);
302 if (!db_match_uuid(r
, &u
)) {
303 /* does not match UUID */
311 * Select a ServiceRecord given the RecordHandle.
314 db_select_handle(server_t
*srv
, int fd
, uint32_t handle
)
318 LIST_FOREACH(r
, &srv
->rlist
, next
) {
322 if (!srv
->fdidx
[fd
].control
323 && !bdaddr_any(&r
->bdaddr
)
324 && !bdaddr_same(&r
->bdaddr
, &srv
->fdidx
[fd
].bdaddr
))
327 if (handle
== r
->handle
) {
328 FD_SET(fd
, &r
->refset
);
336 * Create a record and insert in server record list in ascending handle
337 * order. Where a selectable record exists with the same handle number,
338 * it will be expired.
341 db_create(server_t
*srv
, int fd
, const bdaddr_t
*bdaddr
, uint32_t handle
, sdp_data_t
*data
)
343 record_t
*n
, *r
, *rec
;
349 if (!sdp_get_attr(&d
, &a
, &v
)
350 || a
!= SDP_ATTR_SERVICE_RECORD_HANDLE
351 || sdp_data_type(&v
) != SDP_DATA_UINT32
)
354 sdp_set_uint(&v
, handle
);
356 len
= data
->end
- data
->next
;
357 rec
= malloc(sizeof(record_t
) + len
);
361 memset(rec
, 0, sizeof(record_t
));
362 FD_ZERO(&rec
->refset
);
363 rec
->handle
= handle
;
366 bdaddr_copy(&rec
->bdaddr
, bdaddr
);
367 rec
->data
.next
= rec
->ext
;
368 rec
->data
.end
= rec
->ext
+ len
;
369 memcpy(rec
->ext
, data
->next
, len
);
372 * Note, this does not handle the case where we expire
373 * the first record on the list, as that won't happen.
375 n
= LIST_FIRST(&srv
->rlist
);
379 n
= LIST_NEXT(r
, next
);
380 } while (n
!= NULL
&& n
->handle
< handle
);
382 if (n
!= NULL
&& n
->valid
&& n
->handle
== handle
) {
383 if (n
->refcnt
-- == 0) {
384 LIST_REMOVE(n
, next
);
392 LIST_INSERT_AFTER(r
, rec
, next
);
394 LIST_INSERT_HEAD(&srv
->rlist
, rec
, next
);
401 * Unselect any ServiceRecords selected by fd
404 db_unselect(server_t
*srv
, int fd
)
408 n
= LIST_FIRST(&srv
->rlist
);
411 n
= LIST_NEXT(r
, next
);
413 if (FD_ISSET(fd
, &r
->refset
)) {
414 if (r
->refcnt
-- == 0) {
415 LIST_REMOVE(r
, next
);
418 FD_CLR(fd
, &r
->refset
);
425 * Invalidate or release all records owned by fd
428 db_release(server_t
*srv
, int fd
)
432 n
= LIST_FIRST(&srv
->rlist
);
435 n
= LIST_NEXT(r
, next
);
438 if (r
->refcnt
-- == 0) {
439 LIST_REMOVE(r
, next
);