Skip to content

Commit d29a2f6

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 d29a2f6

File tree

9 files changed

+375
-6
lines changed

9 files changed

+375
-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: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
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 && ev_ptr->pba_features != -1 && ev_ptr->broadcast_name_len > 0) {
98+
tester_event(BTP_SERVICE_ID_PBP, BTP_PBP_EV_PUBLIC_BROADCAST_ANOUNCEMENT_FOUND, ev_ptr,
99+
sizeof(*ev_ptr) + broadcast_name_len);
100+
}
101+
102+
tester_rsp_buffer_free();
103+
tester_rsp_buffer_unlock();
104+
}
105+
106+
static struct bt_le_scan_cb pbp_scan_cb = {
107+
.recv = pbp_scan_recv,
108+
};
109+
110+
static uint8_t pbp_read_supported_commands(const void *cmd, uint16_t cmd_len, void *rsp,
111+
uint16_t *rsp_len)
112+
{
113+
struct btp_pbp_read_supported_commands_rp *rp = rsp;
114+
115+
tester_set_bit(rp->data, BTP_PBP_READ_SUPPORTED_COMMANDS);
116+
tester_set_bit(rp->data, BTP_PBP_SET_PUBLIC_BROADCAST_ANNOUNCEMENT);
117+
tester_set_bit(rp->data, BTP_PBP_SET_BROADCAST_NAME);
118+
tester_set_bit(rp->data, BTP_PBP_BROADCAST_SCAN_START);
119+
tester_set_bit(rp->data, BTP_PBP_BROADCAST_SCAN_STOP);
120+
121+
*rsp_len = sizeof(*rp) + 1;
122+
123+
return BTP_STATUS_SUCCESS;
124+
}
125+
126+
static int pbp_broadcast_source_adv_setup(void)
127+
{
128+
struct bt_le_adv_param param =
129+
BT_LE_ADV_PARAM_INIT(0, BT_GAP_ADV_FAST_INT_MIN_2, BT_GAP_ADV_FAST_INT_MAX_2, NULL);
130+
uint32_t gap_settings = BIT(BTP_GAP_SETTINGS_DISCOVERABLE) |
131+
BIT(BTP_GAP_SETTINGS_EXTENDED_ADVERTISING);
132+
uint32_t broadcast_id;
133+
134+
NET_BUF_SIMPLE_DEFINE(ad_buf, BT_UUID_SIZE_16 + BT_AUDIO_BROADCAST_ID_SIZE);
135+
NET_BUF_SIMPLE_DEFINE(pba_buf, PBP_ADV_HEADER_SIZE + pbp_metadata_cached_len);
136+
137+
int err = bt_rand(&broadcast_id, BT_AUDIO_BROADCAST_ID_SIZE);
138+
struct bt_data ext_ad[3];
139+
140+
if (err) {
141+
LOG_ERR("Unable to generate broadcast ID: %d\n", err);
142+
return -EINVAL;
143+
}
144+
145+
ext_ad[0].type = BT_DATA_BROADCAST_NAME;
146+
ext_ad[0].data_len = pbp_name_cached_len;
147+
ext_ad[0].data = pbp_broadcast_name_cached;
148+
net_buf_simple_add_le16(&ad_buf, BT_UUID_BROADCAST_AUDIO_VAL);
149+
net_buf_simple_add_le24(&ad_buf, broadcast_id);
150+
ext_ad[1].type = BT_DATA_SVC_DATA16;
151+
ext_ad[1].data_len = ad_buf.len;
152+
ext_ad[1].data = ad_buf.data;
153+
net_buf_simple_add_le16(&pba_buf, BT_UUID_PBA_VAL);
154+
net_buf_simple_add_u8(&pba_buf, pbp_features_cached);
155+
net_buf_simple_add_u8(&pba_buf, pbp_metadata_cached_len);
156+
net_buf_simple_add_mem(&pba_buf, pbp_metadata_cached, pbp_metadata_cached_len);
157+
ext_ad[2].type = BT_DATA_SVC_DATA16;
158+
ext_ad[2].data_len = pba_buf.len;
159+
ext_ad[2].data = pba_buf.data;
160+
161+
err = tester_gap_create_adv_instance(&param, BTP_GAP_ADDR_TYPE_IDENTITY, ext_ad,
162+
ARRAY_SIZE(ext_ad), NULL, 0, &gap_settings);
163+
if (err) {
164+
LOG_ERR("Could not set up extended advertisement: %d", err);
165+
return -EINVAL;
166+
}
167+
168+
cap_extern_ext_ad_setup(true);
169+
170+
return 0;
171+
}
172+
173+
static uint8_t pbp_set_public_broadcast_announcement(const void *cmd, uint16_t cmd_len,
174+
void *rsp, uint16_t *rsp_len)
175+
{
176+
const struct btp_pbp_set_public_broadcast_announcement_cmd *cp = cmd;
177+
int err = -EINVAL;
178+
179+
if (cp->metadata_len <= PBP_EXT_ADV_METADATA_LEN_MAX) {
180+
pbp_features_cached = cp->features;
181+
pbp_metadata_cached_len = cp->metadata_len;
182+
memcpy(pbp_metadata_cached, cp->metadata, cp->metadata_len);
183+
err = pbp_broadcast_source_adv_setup();
184+
} else {
185+
LOG_ERR("Metadata too long: %d > %d", cp->metadata_len,
186+
PBP_EXT_ADV_METADATA_LEN_MAX);
187+
}
188+
189+
return BTP_STATUS_VAL(err);
190+
}
191+
192+
static uint8_t pbp_set_broadcast_name(const void *cmd, uint16_t cmd_len, void *rsp,
193+
uint16_t *rsp_len)
194+
{
195+
const struct btp_pbp_set_broadcast_name_cmd *cp = cmd;
196+
int err = -EINVAL;
197+
198+
if (cp->name_len <= BT_AUDIO_BROADCAST_NAME_LEN_MAX) {
199+
pbp_name_cached_len = cp->name_len;
200+
memcpy(pbp_broadcast_name_cached, cp->name, cp->name_len);
201+
err = pbp_broadcast_source_adv_setup();
202+
} else {
203+
LOG_ERR("Broadcast name too long: %d > %d", cp->name_len,
204+
BT_AUDIO_BROADCAST_NAME_LEN_MAX);
205+
}
206+
207+
return BTP_STATUS_VAL(err);
208+
}
209+
210+
static uint8_t pbp_broadcast_scan_start(const void *cmd, uint16_t cmd_len, void *rsp,
211+
uint16_t *rsp_len)
212+
{
213+
int err;
214+
215+
err = bt_le_scan_start(BT_LE_SCAN_ACTIVE, NULL);
216+
if (err != 0 && err != -EALREADY) {
217+
LOG_DBG("Unable to start scan for broadcast sources: %d", err);
218+
219+
return BTP_STATUS_FAILED;
220+
}
221+
222+
return BTP_STATUS_SUCCESS;
223+
}
224+
225+
static uint8_t pbp_broadcast_scan_stop(const void *cmd, uint16_t cmd_len, void *rsp,
226+
uint16_t *rsp_len)
227+
{
228+
int err;
229+
230+
err = bt_le_scan_stop();
231+
if (err != 0) {
232+
LOG_DBG("Failed to stop scan, %d", err);
233+
234+
return BTP_STATUS_FAILED;
235+
}
236+
237+
return BTP_STATUS_SUCCESS;
238+
}
239+
240+
static const struct btp_handler pbp_handlers[] = {
241+
{
242+
.opcode = BTP_PBP_READ_SUPPORTED_COMMANDS,
243+
.index = BTP_INDEX_NONE,
244+
.expect_len = 0,
245+
.func = pbp_read_supported_commands
246+
},
247+
{
248+
.opcode = BTP_PBP_SET_PUBLIC_BROADCAST_ANNOUNCEMENT,
249+
.expect_len = BTP_HANDLER_LENGTH_VARIABLE,
250+
.func = pbp_set_public_broadcast_announcement
251+
},
252+
{
253+
.opcode = BTP_PBP_SET_BROADCAST_NAME,
254+
.expect_len = BTP_HANDLER_LENGTH_VARIABLE,
255+
.func = pbp_set_broadcast_name
256+
},
257+
{
258+
.opcode = BTP_PBP_BROADCAST_SCAN_START,
259+
.expect_len = sizeof(struct btp_pbp_broadcast_scan_start_cmd),
260+
.func = pbp_broadcast_scan_start
261+
},
262+
{
263+
.opcode = BTP_PBP_BROADCAST_SCAN_STOP,
264+
.expect_len = sizeof(struct btp_pbp_broadcast_scan_stop_cmd),
265+
.func = pbp_broadcast_scan_stop
266+
}
267+
};
268+
269+
uint8_t tester_init_pbp(void)
270+
{
271+
tester_register_command_handlers(BTP_SERVICE_ID_PBP, pbp_handlers,
272+
ARRAY_SIZE(pbp_handlers));
273+
274+
bt_le_scan_cb_register(&pbp_scan_cb);
275+
276+
return BTP_STATUS_SUCCESS;
277+
}
278+
279+
uint8_t tester_unregister_pbp(void)
280+
{
281+
return BTP_STATUS_SUCCESS;
282+
}

0 commit comments

Comments
 (0)