1 /* btp_pbp.c - Bluetooth PBP Tester */
2 
3 /*
4  * Copyright (c) 2025 Nordic Semiconductor ASA
5  *
6  * SPDX-License-Identifier: Apache-2.0
7  */
8 
9 #include "btp/btp.h"
10 
11 #include <zephyr/bluetooth/audio/bap.h>
12 #include <zephyr/bluetooth/audio/pbp.h>
13 #include <zephyr/bluetooth/bluetooth.h>
14 #include <zephyr/logging/log.h>
15 #include <zephyr/sys/util.h>
16 #define LOG_MODULE_NAME bttester_pbp
17 LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_BTTESTER_LOG_LEVEL);
18 
19 #include "btp_bap_broadcast.h"
20 
21 #define PBP_EXT_ADV_METADATA_LEN_MAX 128
22 
23 static uint8_t pbp_features_cached;
24 static uint8_t pbp_metadata_cached[PBP_EXT_ADV_METADATA_LEN_MAX];
25 static uint8_t pbp_metadata_cached_len;
26 static uint8_t pbp_broadcast_name_cached[BT_AUDIO_BROADCAST_NAME_LEN_MAX];
27 static uint8_t pbp_name_cached_len;
28 
scan_get_broadcast_name_len(struct bt_data * data,void * user_data)29 static bool scan_get_broadcast_name_len(struct bt_data *data, void *user_data)
30 {
31 	uint8_t *broadcast_name_len = user_data;
32 
33 	switch (data->type) {
34 	case BT_DATA_BROADCAST_NAME:
35 		*broadcast_name_len = data->data_len;
36 		return false;
37 	default:
38 		return true;
39 	}
40 }
41 
scan_get_data(struct bt_data * data,void * user_data)42 static bool scan_get_data(struct bt_data *data, void *user_data)
43 {
44 	enum bt_pbp_announcement_feature source_features;
45 	uint32_t broadcast_id;
46 	uint8_t *metadata;
47 	struct bt_uuid_16 adv_uuid;
48 	struct btp_pbp_ev_public_broadcast_anouncement_found_rp *ev = user_data;
49 
50 	switch (data->type) {
51 	case BT_DATA_BROADCAST_NAME:
52 		ev->broadcast_name_len = data->data_len;
53 		memcpy(ev->broadcast_name, data->data, data->data_len);
54 		return true;
55 	case BT_DATA_SVC_DATA16:
56 		if (!bt_uuid_create(&adv_uuid.uuid, data->data, BT_UUID_SIZE_16)) {
57 			return true;
58 		}
59 		if (!bt_uuid_cmp(&adv_uuid.uuid, BT_UUID_BROADCAST_AUDIO)) {
60 			broadcast_id = sys_get_le24(data->data + BT_UUID_SIZE_16);
61 			sys_put_le24(broadcast_id, ev->broadcast_id);
62 			return true;
63 		}
64 
65 		int ret = bt_pbp_parse_announcement(data, &source_features, &metadata);
66 
67 		if (ret >= 0) {
68 			ev->pba_features = source_features;
69 			return true;
70 		}
71 
72 		return true;
73 	default:
74 		return true;
75 	}
76 }
77 
pbp_scan_recv(const struct bt_le_scan_recv_info * info,struct net_buf_simple * ad)78 static void pbp_scan_recv(const struct bt_le_scan_recv_info *info, struct net_buf_simple *ad)
79 {
80 	if ((info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) ||
81 	    !(info->adv_props & BT_GAP_ADV_PROP_EXT_ADV) || info->interval == 0) {
82 		return;
83 	}
84 
85 	uint8_t broadcast_name_len = 0;
86 	struct net_buf_simple ad_copy;
87 
88 	/* Initial parse to determine broadcast_name_len before allocating memory */
89 	net_buf_simple_clone(ad, &ad_copy);
90 	bt_data_parse(&ad_copy, scan_get_broadcast_name_len, &broadcast_name_len);
91 
92 	struct btp_pbp_ev_public_broadcast_anouncement_found_rp *ev_ptr;
93 
94 	tester_rsp_buffer_lock();
95 	tester_rsp_buffer_allocate(sizeof(*ev_ptr) + broadcast_name_len, (uint8_t **)&ev_ptr);
96 
97 	sys_put_le24(BT_BAP_INVALID_BROADCAST_ID, ev_ptr->broadcast_id);
98 	ev_ptr->pba_features = 0U;
99 	ev_ptr->broadcast_name_len = 0U;
100 
101 	bt_addr_le_copy(&ev_ptr->address, info->addr);
102 	ev_ptr->advertiser_sid = info->sid;
103 	ev_ptr->padv_interval = info->interval;
104 	bt_data_parse(ad, scan_get_data, ev_ptr);
105 
106 	if (sys_get_le24(ev_ptr->broadcast_id) != BT_BAP_INVALID_BROADCAST_ID &&
107 	    ev_ptr->pba_features != 0U && ev_ptr->broadcast_name_len > 0) {
108 		tester_event(BTP_SERVICE_ID_PBP, BTP_PBP_EV_PUBLIC_BROADCAST_ANOUNCEMENT_FOUND,
109 			     ev_ptr, sizeof(*ev_ptr) + broadcast_name_len);
110 	}
111 
112 	tester_rsp_buffer_free();
113 	tester_rsp_buffer_unlock();
114 }
115 
116 static struct bt_le_scan_cb pbp_scan_cb = {
117 	.recv = pbp_scan_recv,
118 };
119 
pbp_read_supported_commands(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)120 static uint8_t pbp_read_supported_commands(const void *cmd, uint16_t cmd_len, void *rsp,
121 					   uint16_t *rsp_len)
122 {
123 	struct btp_pbp_read_supported_commands_rp *rp = rsp;
124 
125 	tester_set_bit(rp->data, BTP_PBP_READ_SUPPORTED_COMMANDS);
126 	tester_set_bit(rp->data, BTP_PBP_SET_PUBLIC_BROADCAST_ANNOUNCEMENT);
127 	tester_set_bit(rp->data, BTP_PBP_SET_BROADCAST_NAME);
128 	tester_set_bit(rp->data, BTP_PBP_BROADCAST_SCAN_START);
129 	tester_set_bit(rp->data, BTP_PBP_BROADCAST_SCAN_STOP);
130 
131 	*rsp_len = sizeof(*rp) + 1;
132 
133 	return BTP_STATUS_SUCCESS;
134 }
135 
pbp_broadcast_source_adv_setup(void)136 static int pbp_broadcast_source_adv_setup(void)
137 {
138 	struct bt_le_adv_param param =
139 		BT_LE_ADV_PARAM_INIT(0, BT_GAP_ADV_FAST_INT_MIN_2, BT_GAP_ADV_FAST_INT_MAX_2, NULL);
140 	uint32_t gap_settings = BIT(BTP_GAP_SETTINGS_DISCOVERABLE) |
141 				BIT(BTP_GAP_SETTINGS_EXTENDED_ADVERTISING);
142 	uint32_t broadcast_id;
143 
144 	NET_BUF_SIMPLE_DEFINE(ad_buf, BT_UUID_SIZE_16 + BT_AUDIO_BROADCAST_ID_SIZE);
145 	NET_BUF_SIMPLE_DEFINE(pba_buf, BT_PBP_MIN_PBA_SIZE + pbp_metadata_cached_len);
146 
147 	int err = bt_rand(&broadcast_id, BT_AUDIO_BROADCAST_ID_SIZE);
148 	struct bt_data ext_ad[3];
149 
150 	if (err) {
151 		LOG_ERR("Unable to generate broadcast ID: %d\n", err);
152 		return -EINVAL;
153 	}
154 
155 	ext_ad[0].type = BT_DATA_BROADCAST_NAME;
156 	ext_ad[0].data_len = pbp_name_cached_len;
157 	ext_ad[0].data = pbp_broadcast_name_cached;
158 	net_buf_simple_add_le16(&ad_buf, BT_UUID_BROADCAST_AUDIO_VAL);
159 	net_buf_simple_add_le24(&ad_buf, broadcast_id);
160 	ext_ad[1].type = BT_DATA_SVC_DATA16;
161 	ext_ad[1].data_len = ad_buf.len;
162 	ext_ad[1].data = ad_buf.data;
163 	net_buf_simple_add_le16(&pba_buf, BT_UUID_PBA_VAL);
164 	net_buf_simple_add_u8(&pba_buf, pbp_features_cached);
165 	net_buf_simple_add_u8(&pba_buf, pbp_metadata_cached_len);
166 	net_buf_simple_add_mem(&pba_buf, pbp_metadata_cached, pbp_metadata_cached_len);
167 	ext_ad[2].type = BT_DATA_SVC_DATA16;
168 	ext_ad[2].data_len = pba_buf.len;
169 	ext_ad[2].data = pba_buf.data;
170 
171 	struct btp_bap_broadcast_local_source *source;
172 
173 	source = btp_bap_broadcast_local_source_from_src_id_get(0);
174 
175 	if (source->ext_adv == NULL) {
176 		err = tester_gap_create_adv_instance(&param, BTP_GAP_ADDR_TYPE_IDENTITY, ext_ad,
177 						     ARRAY_SIZE(ext_ad), NULL, 0, &gap_settings,
178 						     &source->ext_adv);
179 		if (err != 0) {
180 			LOG_ERR("Could not set up extended advertisement: %d", err);
181 			return -EINVAL;
182 		}
183 	} else {
184 		err = bt_le_ext_adv_set_data(source->ext_adv, ext_ad, ARRAY_SIZE(ext_ad), NULL, 0);
185 
186 		if (err != 0) {
187 			LOG_ERR("Could not set extended advertisement data: %d", err);
188 			return -EINVAL;
189 		}
190 	}
191 
192 	source->broadcast_id = broadcast_id;
193 	source->allocated = true;
194 
195 	return 0;
196 }
197 
pbp_set_public_broadcast_announcement(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)198 static uint8_t pbp_set_public_broadcast_announcement(const void *cmd, uint16_t cmd_len,
199 						     void *rsp, uint16_t *rsp_len)
200 {
201 	const struct btp_pbp_set_public_broadcast_announcement_cmd *cp = cmd;
202 	int err = -EINVAL;
203 
204 	if (cp->metadata_len <= PBP_EXT_ADV_METADATA_LEN_MAX) {
205 		pbp_features_cached = cp->features;
206 		pbp_metadata_cached_len = cp->metadata_len;
207 		memcpy(pbp_metadata_cached, cp->metadata, cp->metadata_len);
208 		err = pbp_broadcast_source_adv_setup();
209 	} else {
210 		LOG_ERR("Metadata too long: %d > %d", cp->metadata_len,
211 			PBP_EXT_ADV_METADATA_LEN_MAX);
212 	}
213 
214 	return BTP_STATUS_VAL(err);
215 }
216 
pbp_set_broadcast_name(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)217 static uint8_t pbp_set_broadcast_name(const void *cmd, uint16_t cmd_len, void *rsp,
218 				      uint16_t *rsp_len)
219 {
220 	const struct btp_pbp_set_broadcast_name_cmd *cp = cmd;
221 	int err = -EINVAL;
222 
223 	if (cp->name_len <= BT_AUDIO_BROADCAST_NAME_LEN_MAX) {
224 		pbp_name_cached_len = cp->name_len;
225 		memcpy(pbp_broadcast_name_cached, cp->name, cp->name_len);
226 		err = pbp_broadcast_source_adv_setup();
227 	} else {
228 		LOG_ERR("Broadcast name too long: %d > %d", cp->name_len,
229 			BT_AUDIO_BROADCAST_NAME_LEN_MAX);
230 	}
231 
232 	return BTP_STATUS_VAL(err);
233 }
234 
pbp_broadcast_scan_start(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)235 static uint8_t pbp_broadcast_scan_start(const void *cmd, uint16_t cmd_len, void *rsp,
236 					uint16_t *rsp_len)
237 {
238 	int err;
239 
240 	err = bt_le_scan_start(BT_LE_SCAN_ACTIVE, NULL);
241 	if (err != 0 && err != -EALREADY) {
242 		LOG_DBG("Unable to start scan for broadcast sources: %d", err);
243 
244 		return BTP_STATUS_FAILED;
245 	}
246 
247 	return BTP_STATUS_SUCCESS;
248 }
249 
pbp_broadcast_scan_stop(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)250 static uint8_t pbp_broadcast_scan_stop(const void *cmd, uint16_t cmd_len, void *rsp,
251 				       uint16_t *rsp_len)
252 {
253 	int err;
254 
255 	err = bt_le_scan_stop();
256 	if (err != 0) {
257 		LOG_DBG("Failed to stop scan, %d", err);
258 
259 		return BTP_STATUS_FAILED;
260 	}
261 
262 	return BTP_STATUS_SUCCESS;
263 }
264 
265 static const struct btp_handler pbp_handlers[] = {
266 	{
267 		.opcode = BTP_PBP_READ_SUPPORTED_COMMANDS,
268 		.index = BTP_INDEX_NONE,
269 		.expect_len = 0,
270 		.func = pbp_read_supported_commands
271 	},
272 	{
273 		.opcode = BTP_PBP_SET_PUBLIC_BROADCAST_ANNOUNCEMENT,
274 		.expect_len = BTP_HANDLER_LENGTH_VARIABLE,
275 		.func = pbp_set_public_broadcast_announcement
276 	},
277 	{
278 		.opcode = BTP_PBP_SET_BROADCAST_NAME,
279 		.expect_len = BTP_HANDLER_LENGTH_VARIABLE,
280 		.func = pbp_set_broadcast_name
281 	},
282 	{
283 		.opcode = BTP_PBP_BROADCAST_SCAN_START,
284 		.expect_len = sizeof(struct btp_pbp_broadcast_scan_start_cmd),
285 		.func = pbp_broadcast_scan_start
286 	},
287 	{
288 		.opcode = BTP_PBP_BROADCAST_SCAN_STOP,
289 		.expect_len = sizeof(struct btp_pbp_broadcast_scan_stop_cmd),
290 		.func = pbp_broadcast_scan_stop
291 	}
292 };
293 
tester_init_pbp(void)294 uint8_t tester_init_pbp(void)
295 {
296 	tester_register_command_handlers(BTP_SERVICE_ID_PBP, pbp_handlers,
297 					 ARRAY_SIZE(pbp_handlers));
298 
299 	bt_le_scan_cb_register(&pbp_scan_cb);
300 
301 	return BTP_STATUS_SUCCESS;
302 }
303 
tester_unregister_pbp(void)304 uint8_t tester_unregister_pbp(void)
305 {
306 	return BTP_STATUS_SUCCESS;
307 }
308