1 /*  Bluetooth Audio Content Control */
2 
3 /*
4  * Copyright (c) 2020 Bose Corporation
5  * Copyright (c) 2021-2024 Nordic Semiconductor ASA
6  *
7  * SPDX-License-Identifier: Apache-2.0
8  */
9 #include <errno.h>
10 #include <stddef.h>
11 #include <stdint.h>
12 #include <sys/types.h>
13 
14 #include <zephyr/bluetooth/att.h>
15 #include <zephyr/bluetooth/audio/ccid.h>
16 #include <zephyr/bluetooth/gatt.h>
17 #include <zephyr/bluetooth/uuid.h>
18 #include <zephyr/sys/__assert.h>
19 
20 struct ccid_search_param {
21 	const struct bt_gatt_attr *attr;
22 	uint8_t ccid;
23 };
24 
ccid_attr_cb(const struct bt_gatt_attr * attr,uint16_t handle,void * user_data)25 static uint8_t ccid_attr_cb(const struct bt_gatt_attr *attr, uint16_t handle, void *user_data)
26 {
27 	struct ccid_search_param *search_param = user_data;
28 
29 	if (attr->read != NULL) {
30 		uint8_t ccid = 0U;
31 		ssize_t res;
32 
33 		res = attr->read(NULL, attr, &ccid, sizeof(ccid), 0);
34 
35 		if (res == sizeof(ccid) && search_param->ccid == ccid) {
36 			search_param->attr = attr;
37 
38 			return BT_GATT_ITER_STOP;
39 		}
40 	}
41 
42 	return BT_GATT_ITER_CONTINUE;
43 }
44 
bt_ccid_find_attr(uint8_t ccid)45 const struct bt_gatt_attr *bt_ccid_find_attr(uint8_t ccid)
46 {
47 	struct ccid_search_param search_param = {
48 		.attr = NULL,
49 		.ccid = ccid,
50 	};
51 
52 	bt_gatt_foreach_attr_type(BT_ATT_FIRST_ATTRIBUTE_HANDLE, BT_ATT_LAST_ATTRIBUTE_HANDLE,
53 				  BT_UUID_CCID, NULL, 0, ccid_attr_cb, &search_param);
54 
55 	return search_param.attr;
56 }
57 
bt_ccid_alloc_value(void)58 int bt_ccid_alloc_value(void)
59 {
60 	static uint8_t next_ccid_value;
61 	const uint8_t tmp = next_ccid_value;
62 
63 	/* Verify that the CCID is unused and increment until we reach an unused value or until we
64 	 * reach our starting point
65 	 * This is not a perfect check as there may be services that are not registered that have
66 	 * allocated a CCID.
67 	 * Implementing a perfect solution would require a alloc and free API where we need to
68 	 * keep track of all allocated CCIDs, which requires additional memory for something that's
69 	 * very unlikely to be an issue.
70 	 */
71 	while (bt_ccid_find_attr(next_ccid_value) != NULL) {
72 		next_ccid_value++;
73 		if (tmp == next_ccid_value) {
74 			return -ENOMEM;
75 		}
76 	}
77 
78 	return next_ccid_value++;
79 }
80