Skip to content

Commit b5e9284

Browse files
committed
Tests: Bluetooth: Tester: Add support for PBP testing
Adds support for the existing BTP commands for PBP testing. This should enable bttester to be used to test this profile with AutoPTS. Signed-off-by: Frode van der Meeren <[email protected]>
1 parent 1aa2f9d commit b5e9284

File tree

9 files changed

+376
-6
lines changed

9 files changed

+376
-6
lines changed

tests/bluetooth/tester/overlay-le-audio.conf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,6 @@ CONFIG_BT_TBS_SUPPORTED_FEATURES=3
147147

148148
# TMAP
149149
CONFIG_BT_TMAP=y
150+
151+
# PBP
152+
CONFIG_BT_PBP=y

tests/bluetooth/tester/src/audio/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,7 @@ endif()
6767
if(CONFIG_BT_TMAP)
6868
target_sources(app PRIVATE btp_tmap.c)
6969
endif()
70+
71+
if(CONFIG_BT_PBP)
72+
target_sources(app PRIVATE btp_pbp.c)
73+
endif()

tests/bluetooth/tester/src/audio/btp/btp_cap.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
* SPDX-License-Identifier: Apache-2.0
77
*/
88

9+
int cap_extern_ext_ad_setup(bool prohibit);
10+
911
/* CAP commands */
1012
#define BTP_CAP_READ_SUPPORTED_COMMANDS 0x01
1113
struct btp_cap_read_supported_commands_rp {
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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+
/* This is main.h */
10+
11+
#define BTP_PBP_READ_SUPPORTED_COMMANDS 0x01
12+
struct btp_pbp_read_supported_commands_rp {
13+
uint8_t data[0];
14+
} __packed;
15+
16+
#define BTP_PBP_SET_PUBLIC_BROADCAST_ANNOUNCEMENT 0x02
17+
struct btp_pbp_set_public_broadcast_announcement_cmd {
18+
uint8_t features;
19+
uint8_t metadata_len;
20+
uint8_t metadata[];
21+
} __packed;
22+
23+
#define BTP_PBP_SET_BROADCAST_NAME 0x03
24+
struct btp_pbp_set_broadcast_name_cmd {
25+
uint8_t name_len;
26+
uint8_t name[];
27+
} __packed;
28+
29+
#define BTP_PBP_BROADCAST_SCAN_START 0x04
30+
struct btp_pbp_broadcast_scan_start_cmd {
31+
} __packed;
32+
33+
#define BTP_PBP_BROADCAST_SCAN_STOP 0x05
34+
struct btp_pbp_broadcast_scan_stop_cmd {
35+
} __packed;
36+
37+
#define BTP_PBP_EV_PUBLIC_BROADCAST_ANOUNCEMENT_FOUND 0x80
38+
struct btp_pbp_ev_public_broadcast_anouncement_found_rp {
39+
bt_addr_le_t address;
40+
uint8_t broadcast_id[BT_AUDIO_BROADCAST_ID_SIZE];
41+
uint8_t advertiser_sid;
42+
uint16_t padv_interval;
43+
uint8_t pba_features;
44+
uint8_t broadcast_name_len;
45+
uint8_t broadcast_name[];
46+
} __packed;

tests/bluetooth/tester/src/audio/btp_cap.c

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ static struct btp_bap_unicast_group *u_group;
2424

2525
extern struct bt_csip_set_coordinator_set_member *btp_csip_set_members[CONFIG_BT_MAX_CONN];
2626

27+
static uint8_t cap_prohibit_setting_up_ext_ad;
28+
2729
static struct bt_bap_stream *stream_unicast_to_bap(struct btp_bap_unicast_stream *stream)
2830
{
2931
return &stream->audio_stream.cap_stream.bap_stream;
@@ -560,12 +562,14 @@ static int cap_broadcast_source_adv_setup(struct btp_bap_broadcast_local_source
560562
base_ad[1].type = BT_DATA_NAME_COMPLETE;
561563
base_ad[1].data_len = sizeof(CONFIG_BT_DEVICE_NAME) - 1;
562564
base_ad[1].data = CONFIG_BT_DEVICE_NAME;
563-
err = tester_gap_create_adv_instance(&param, BTP_GAP_ADDR_TYPE_IDENTITY, base_ad, 2, NULL,
564-
0, gap_settings);
565-
if (err != 0) {
566-
LOG_DBG("Failed to create extended advertising instance: %d", err);
565+
if (!cap_prohibit_setting_up_ext_ad) {
566+
err = tester_gap_create_adv_instance(&param, BTP_GAP_ADDR_TYPE_IDENTITY, base_ad,
567+
2, NULL, 0, gap_settings);
568+
if (err != 0) {
569+
LOG_DBG("Failed to create extended advertising instance: %d", err);
567570

568-
return -EINVAL;
571+
return -EINVAL;
572+
}
569573
}
570574

571575
err = tester_gap_padv_configure(BT_LE_PER_ADV_PARAM(BT_GAP_PER_ADV_FAST_INT_MIN_2,
@@ -936,3 +940,13 @@ uint8_t tester_unregister_cap(void)
936940
{
937941
return BTP_STATUS_SUCCESS;
938942
}
943+
944+
/* Inhibit creating a new extended advertisement instance in btp_cap_broadcast_source_setup and
945+
* cap_broadcast_source_adv_setup, for profiles such as PBP which sets up its own instance with
946+
* more specialized data in advance of calling btp_cap_broadcast_source_setup.
947+
*/
948+
int cap_extern_ext_ad_setup(bool prohibit)
949+
{
950+
cap_prohibit_setting_up_ext_ad = prohibit;
951+
return 0;
952+
}
Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
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/pbp.h>
12+
#include <zephyr/logging/log.h>
13+
#include <zephyr/sys/util.h>
14+
#define LOG_MODULE_NAME bttester_pbp
15+
LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_BTTESTER_LOG_LEVEL);
16+
17+
#define PBP_EXT_ADV_METADATA_LEN_MAX 128
18+
#define PBP_ADV_HEADER_SIZE (BT_UUID_SIZE_16 + sizeof(uint8_t) * 2)
19+
20+
static uint8_t pbp_features_cached;
21+
static uint8_t pbp_metadata_cached[PBP_EXT_ADV_METADATA_LEN_MAX];
22+
static uint8_t pbp_metadata_cached_len;
23+
static uint8_t pbp_broadcast_name_cached[BT_AUDIO_BROADCAST_NAME_LEN_MAX];
24+
static uint8_t pbp_name_cached_len;
25+
26+
static bool scan_get_broadcast_name_len(struct bt_data *data, void *user_data)
27+
{
28+
uint8_t *broadcast_name_len = user_data;
29+
30+
switch (data->type) {
31+
case BT_DATA_BROADCAST_NAME:
32+
*broadcast_name_len = data->data_len;
33+
return false;
34+
default:
35+
return true;
36+
}
37+
}
38+
39+
static bool scan_get_data(struct bt_data *data, void *user_data)
40+
{
41+
enum bt_pbp_announcement_feature source_features;
42+
uint32_t broadcast_id;
43+
uint8_t *metadata;
44+
struct btp_pbp_ev_public_broadcast_anouncement_found_rp *ev = user_data;
45+
46+
switch (data->type) {
47+
case BT_DATA_BROADCAST_NAME:
48+
ev->broadcast_name_len = data->data_len;
49+
memcpy(ev->broadcast_name, data->data, data->data_len);
50+
return true;
51+
case BT_DATA_SVC_DATA16:
52+
if (!bt_uuid_cmp(&adv_uuid.uuid, BT_UUID_BROADCAST_AUDIO)) {
53+
broadcast_id = sys_get_le24(data->data + BT_UUID_SIZE_16);
54+
sys_put_le24(broadcast_id, ev->broadcast_id);
55+
return true;
56+
}
57+
58+
ret = bt_pbp_parse_announcement(data, &source_features, &metadata);
59+
if (ret >= 0) {
60+
ev->pba_features = source_features;
61+
return true;
62+
}
63+
64+
return true;
65+
default:
66+
return true;
67+
}
68+
}
69+
70+
static void pbp_scan_recv(const struct bt_le_scan_recv_info *info, struct net_buf_simple *ad)
71+
{
72+
if ((info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) ||
73+
!(info->adv_props & BT_GAP_ADV_PROP_EXT_ADV) || info->interval == 0) {
74+
return;
75+
}
76+
77+
uint8_t broadcast_name_len = 0;
78+
struct net_buf_simple ad_copy;
79+
80+
net_buf_simple_clone(ad, &ad_copy);
81+
bt_data_parse(&ad_copy, scan_get_broadcast_name_len, &broadcast_name_len);
82+
83+
struct btp_pbp_ev_public_broadcast_anouncement_found_rp *ev_ptr;
84+
85+
sys_put_le24(0xffffffff, ev_ptr->broadcast_id);
86+
ev_ptr->pba_features = 0xff;
87+
ev_ptr->broadcast_name_len = 0xff;
88+
89+
tester_rsp_buffer_lock();
90+
tester_rsp_buffer_allocate(sizeof(*ev_ptr) + broadcast_name_len, (uint8_t **)&ev_ptr);
91+
92+
bt_addr_le_copy(&ev_ptr->address, info->addr);
93+
ev_ptr->advertiser_sid = info->sid;
94+
ev_ptr->padv_interval = info->interval;
95+
bt_data_parse(ad, scan_get_data, ev_ptr);
96+
97+
if (sys_get_le24(cp->broadcast_id) != -1 &&
98+
ev_ptr->pba_features != -1 && ev_ptr->broadcast_name_len > 0) {
99+
tester_event(BTP_SERVICE_ID_PBP, BTP_PBP_EV_PUBLIC_BROADCAST_ANOUNCEMENT_FOUND, ev_ptr,
100+
sizeof(*ev_ptr) + broadcast_name_len);
101+
}
102+
103+
tester_rsp_buffer_free();
104+
tester_rsp_buffer_unlock();
105+
}
106+
107+
static struct bt_le_scan_cb pbp_scan_cb = {
108+
.recv = pbp_scan_recv,
109+
};
110+
111+
static uint8_t pbp_read_supported_commands(const void *cmd, uint16_t cmd_len, void *rsp,
112+
uint16_t *rsp_len)
113+
{
114+
struct btp_pbp_read_supported_commands_rp *rp = rsp;
115+
116+
tester_set_bit(rp->data, BTP_PBP_READ_SUPPORTED_COMMANDS);
117+
tester_set_bit(rp->data, BTP_PBP_SET_PUBLIC_BROADCAST_ANNOUNCEMENT);
118+
tester_set_bit(rp->data, BTP_PBP_SET_BROADCAST_NAME);
119+
tester_set_bit(rp->data, BTP_PBP_BROADCAST_SCAN_START);
120+
tester_set_bit(rp->data, BTP_PBP_BROADCAST_SCAN_STOP);
121+
122+
*rsp_len = sizeof(*rp) + 1;
123+
124+
return BTP_STATUS_SUCCESS;
125+
}
126+
127+
static int pbp_broadcast_source_adv_setup(void)
128+
{
129+
struct bt_le_adv_param param =
130+
BT_LE_ADV_PARAM_INIT(0, BT_GAP_ADV_FAST_INT_MIN_2, BT_GAP_ADV_FAST_INT_MAX_2, NULL);
131+
uint32_t gap_settings = BIT(BTP_GAP_SETTINGS_DISCOVERABLE) |
132+
BIT(BTP_GAP_SETTINGS_EXTENDED_ADVERTISING);
133+
uint32_t broadcast_id;
134+
135+
NET_BUF_SIMPLE_DEFINE(ad_buf, BT_UUID_SIZE_16 + BT_AUDIO_BROADCAST_ID_SIZE);
136+
NET_BUF_SIMPLE_DEFINE(pba_buf, PBP_ADV_HEADER_SIZE + pbp_metadata_cached_len);
137+
138+
int err = bt_rand(&broadcast_id, BT_AUDIO_BROADCAST_ID_SIZE);
139+
struct bt_data ext_ad[3];
140+
141+
if (err) {
142+
LOG_ERR("Unable to generate broadcast ID: %d\n", err);
143+
return -EINVAL;
144+
}
145+
146+
ext_ad[0].type = BT_DATA_BROADCAST_NAME;
147+
ext_ad[0].data_len = pbp_name_cached_len;
148+
ext_ad[0].data = pbp_broadcast_name_cached;
149+
net_buf_simple_add_le16(&ad_buf, BT_UUID_BROADCAST_AUDIO_VAL);
150+
net_buf_simple_add_le24(&ad_buf, broadcast_id);
151+
ext_ad[1].type = BT_DATA_SVC_DATA16;
152+
ext_ad[1].data_len = ad_buf.len;
153+
ext_ad[1].data = ad_buf.data;
154+
net_buf_simple_add_le16(&pba_buf, BT_UUID_PBA_VAL);
155+
net_buf_simple_add_u8(&pba_buf, pbp_features_cached);
156+
net_buf_simple_add_u8(&pba_buf, pbp_metadata_cached_len);
157+
net_buf_simple_add_mem(&pba_buf, pbp_metadata_cached, pbp_metadata_cached_len);
158+
ext_ad[2].type = BT_DATA_SVC_DATA16;
159+
ext_ad[2].data_len = pba_buf.len;
160+
ext_ad[2].data = pba_buf.data;
161+
162+
err = tester_gap_create_adv_instance(&param, BTP_GAP_ADDR_TYPE_IDENTITY, ext_ad,
163+
ARRAY_SIZE(ext_ad), NULL, 0, &gap_settings);
164+
if (err) {
165+
LOG_ERR("Could not set up extended advertisement: %d", err);
166+
return -EINVAL;
167+
}
168+
169+
cap_extern_ext_ad_setup(true);
170+
171+
return 0;
172+
}
173+
174+
static uint8_t pbp_set_public_broadcast_announcement(const void *cmd, uint16_t cmd_len,
175+
void *rsp, uint16_t *rsp_len)
176+
{
177+
const struct btp_pbp_set_public_broadcast_announcement_cmd *cp = cmd;
178+
int err = -EINVAL;
179+
180+
if (cp->metadata_len <= PBP_EXT_ADV_METADATA_LEN_MAX) {
181+
pbp_features_cached = cp->features;
182+
pbp_metadata_cached_len = cp->metadata_len;
183+
memcpy(pbp_metadata_cached, cp->metadata, cp->metadata_len);
184+
err = pbp_broadcast_source_adv_setup();
185+
} else {
186+
LOG_ERR("Metadata too long: %d > %d", cp->metadata_len,
187+
PBP_EXT_ADV_METADATA_LEN_MAX);
188+
}
189+
190+
return BTP_STATUS_VAL(err);
191+
}
192+
193+
static uint8_t pbp_set_broadcast_name(const void *cmd, uint16_t cmd_len, void *rsp,
194+
uint16_t *rsp_len)
195+
{
196+
const struct btp_pbp_set_broadcast_name_cmd *cp = cmd;
197+
int err = -EINVAL;
198+
199+
if (cp->name_len <= BT_AUDIO_BROADCAST_NAME_LEN_MAX) {
200+
pbp_name_cached_len = cp->name_len;
201+
memcpy(pbp_broadcast_name_cached, cp->name, cp->name_len);
202+
err = pbp_broadcast_source_adv_setup();
203+
} else {
204+
LOG_ERR("Broadcast name too long: %d > %d", cp->name_len,
205+
BT_AUDIO_BROADCAST_NAME_LEN_MAX);
206+
}
207+
208+
return BTP_STATUS_VAL(err);
209+
}
210+
211+
static uint8_t pbp_broadcast_scan_start(const void *cmd, uint16_t cmd_len, void *rsp,
212+
uint16_t *rsp_len)
213+
{
214+
int err;
215+
216+
err = bt_le_scan_start(BT_LE_SCAN_ACTIVE, NULL);
217+
if (err != 0 && err != -EALREADY) {
218+
LOG_DBG("Unable to start scan for broadcast sources: %d", err);
219+
220+
return BTP_STATUS_FAILED;
221+
}
222+
223+
return BTP_STATUS_SUCCESS;
224+
}
225+
226+
static uint8_t pbp_broadcast_scan_stop(const void *cmd, uint16_t cmd_len, void *rsp,
227+
uint16_t *rsp_len)
228+
{
229+
int err;
230+
231+
err = bt_le_scan_stop();
232+
if (err != 0) {
233+
LOG_DBG("Failed to stop scan, %d", err);
234+
235+
return BTP_STATUS_FAILED;
236+
}
237+
238+
return BTP_STATUS_SUCCESS;
239+
}
240+
241+
static const struct btp_handler pbp_handlers[] = {
242+
{
243+
.opcode = BTP_PBP_READ_SUPPORTED_COMMANDS,
244+
.index = BTP_INDEX_NONE,
245+
.expect_len = 0,
246+
.func = pbp_read_supported_commands
247+
},
248+
{
249+
.opcode = BTP_PBP_SET_PUBLIC_BROADCAST_ANNOUNCEMENT,
250+
.expect_len = BTP_HANDLER_LENGTH_VARIABLE,
251+
.func = pbp_set_public_broadcast_announcement
252+
},
253+
{
254+
.opcode = BTP_PBP_SET_BROADCAST_NAME,
255+
.expect_len = BTP_HANDLER_LENGTH_VARIABLE,
256+
.func = pbp_set_broadcast_name
257+
},
258+
{
259+
.opcode = BTP_PBP_BROADCAST_SCAN_START,
260+
.expect_len = sizeof(struct btp_pbp_broadcast_scan_start_cmd),
261+
.func = pbp_broadcast_scan_start
262+
},
263+
{
264+
.opcode = BTP_PBP_BROADCAST_SCAN_STOP,
265+
.expect_len = sizeof(struct btp_pbp_broadcast_scan_stop_cmd),
266+
.func = pbp_broadcast_scan_stop
267+
}
268+
};
269+
270+
uint8_t tester_init_pbp(void)
271+
{
272+
tester_register_command_handlers(BTP_SERVICE_ID_PBP, pbp_handlers,
273+
ARRAY_SIZE(pbp_handlers));
274+
275+
bt_le_scan_cb_register(&pbp_scan_cb);
276+
277+
return BTP_STATUS_SUCCESS;
278+
}
279+
280+
uint8_t tester_unregister_pbp(void)
281+
{
282+
return BTP_STATUS_SUCCESS;
283+
}

0 commit comments

Comments
 (0)