Skip to content

Commit 3974ae2

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 3974ae2

File tree

9 files changed

+351
-6
lines changed

9 files changed

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

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,9 @@
7575
#define BTP_SERVICE_ID_TBS 27
7676
#define BTP_SERVICE_ID_TMAP 28
7777
#define BTP_SERVICE_ID_OTS 29
78+
#define BTP_SERVICE_ID_PBP 0x1e
7879

79-
#define BTP_SERVICE_ID_MAX BTP_SERVICE_ID_OTS
80+
#define BTP_SERVICE_ID_MAX BTP_SERVICE_ID_PBP
8081

8182
#define BTP_STATUS_SUCCESS 0x00
8283
#define BTP_STATUS_FAILED 0x01

tests/bluetooth/tester/src/btp/bttester.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,6 @@ uint8_t tester_unregister_tmap(void);
138138

139139
uint8_t tester_init_ots(void);
140140
uint8_t tester_unregister_ots(void);
141+
142+
uint8_t tester_init_pbp(void);
143+
uint8_t tester_unregister_pbp(void);

0 commit comments

Comments
 (0)