bluetooth: tester: Add support for BAP broadcast

Support for BAP/BSRC and BAP/BSNK test cases.

Add partial support for GAP/PADV as periodic advertising is needed
for BAP/BSRC tests and they can share common code.

Signed-off-by: Magdalena Kasenberg <magdalena.kasenberg@codecoup.pl>
This commit is contained in:
Magdalena Kasenberg 2023-08-16 11:21:55 +02:00 committed by Carles Cufí
commit efb5c0902b
8 changed files with 1443 additions and 80 deletions

View file

@ -25,6 +25,18 @@ CONFIG_BT_MICP_MIC_DEV_AICS_INSTANCE_COUNT=1
CONFIG_BT_MICP_MIC_CTLR=y
CONFIG_BT_MICP_MIC_CTLR_MAX_AICS_INST=1
# Broadcast Source
CONFIG_BT_BAP_BROADCAST_SOURCE=y
CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT=2
CONFIG_BT_BAP_BROADCAST_SRC_SUBGROUP_COUNT=2
CONFIG_BT_ISO_TX_BUF_COUNT=4
# Broadcast Sink
CONFIG_BT_BAP_SCAN_DELEGATOR=y
CONFIG_BT_BAP_BROADCAST_SINK=y
CONFIG_BT_BAP_BROADCAST_SNK_STREAM_COUNT=2
CONFIG_BT_BAP_BROADCAST_SNK_SUBGROUP_COUNT=2
# ASCS
CONFIG_BT_ASCS_ASE_SNK_COUNT=2
CONFIG_BT_ASCS_ASE_SRC_COUNT=2

View file

@ -50,6 +50,9 @@ static struct {
size_t num;
} service_handler[BTP_SERVICE_ID_MAX + 1];
static struct net_buf_simple *rsp_buf = NET_BUF_SIMPLE(BTP_MTU);
static K_MUTEX_DEFINE(rsp_buf_mutex);
static void tester_send_with_index(uint8_t service, uint8_t opcode, uint8_t index,
const uint8_t *data, size_t len);
static void tester_rsp_with_index(uint8_t service, uint8_t opcode, uint8_t index,
@ -241,6 +244,34 @@ void tester_init(void)
BTP_INDEX_NONE, NULL, 0);
}
int tester_rsp_buffer_lock(void)
{
if (k_mutex_lock(&rsp_buf_mutex, Z_FOREVER) != 0) {
LOG_ERR("Cannot lock rsp_ring_buf");
return -EACCES;
}
return 0;
}
void tester_rsp_buffer_unlock(void)
{
k_mutex_unlock(&rsp_buf_mutex);
}
void tester_rsp_buffer_free(void)
{
net_buf_simple_init(rsp_buf, 0);
}
void tester_rsp_buffer_allocate(size_t len, uint8_t **data)
{
tester_rsp_buffer_free();
*data = net_buf_simple_add(rsp_buf, len);
}
static void tester_send_with_index(uint8_t service, uint8_t opcode, uint8_t index,
const uint8_t *data, size_t len)
{

View file

@ -19,8 +19,8 @@ struct btp_ascs_configure_codec_cmd {
uint8_t coding_format;
uint16_t vid;
uint16_t cid;
uint8_t ltvs_len;
uint8_t ltvs[0];
uint8_t cc_ltvs_len;
uint8_t cc_ltvs[0];
} __packed;
#define BTP_ASCS_CONFIGURE_QOS 0x03

View file

@ -7,12 +7,12 @@
*/
/* BAP commands */
#define BTP_BAP_READ_SUPPORTED_COMMANDS 0x01
#define BTP_BAP_READ_SUPPORTED_COMMANDS 0x01
struct btp_bap_read_supported_commands_rp {
uint8_t data[0];
} __packed;
#define BTP_BAP_DISCOVER 0x02
#define BTP_BAP_DISCOVER 0x02
struct btp_bap_discover_cmd {
bt_addr_le_t address;
} __packed;
@ -20,7 +20,7 @@ struct btp_bap_discover_cmd {
#define BTP_BAP_DISCOVERY_STATUS_SUCCESS 0x00
#define BTP_BAP_DISCOVERY_STATUS_FAILED 0x01
#define BTP_BAP_SEND 0x03
#define BTP_BAP_SEND 0x03
struct btp_bap_send_cmd {
bt_addr_le_t address;
uint8_t ase_id;
@ -32,14 +32,91 @@ struct btp_bap_send_rp {
uint8_t data_len;
} __packed;
#define BTP_BAP_BROADCAST_SOURCE_SETUP 0x04
struct btp_bap_broadcast_source_setup_cmd {
uint8_t streams_per_subgroup;
uint8_t subgroups;
uint8_t sdu_interval[3];
uint8_t framing;
uint16_t max_sdu;
uint8_t retransmission_num;
uint16_t max_transport_latency;
uint8_t presentation_delay[3];
uint8_t coding_format;
uint16_t vid;
uint16_t cid;
uint8_t cc_ltvs_len;
uint8_t cc_ltvs[];
} __packed;
struct btp_bap_broadcast_source_setup_rp {
uint32_t gap_settings;
uint8_t broadcast_id[3];
} __packed;
#define BTP_BAP_BROADCAST_SOURCE_RELEASE 0x05
struct btp_bap_broadcast_source_release_cmd {
uint8_t broadcast_id[3];
} __packed;
#define BTP_BAP_BROADCAST_ADV_START 0x06
struct btp_bap_broadcast_adv_start_cmd {
uint8_t broadcast_id[3];
} __packed;
#define BTP_BAP_BROADCAST_ADV_STOP 0x07
struct btp_bap_broadcast_adv_stop_cmd {
uint8_t broadcast_id[3];
} __packed;
#define BTP_BAP_BROADCAST_SOURCE_START 0x08
struct btp_bap_broadcast_source_start_cmd {
uint8_t broadcast_id[3];
} __packed;
#define BTP_BAP_BROADCAST_SOURCE_STOP 0x09
struct btp_bap_broadcast_source_stop_cmd {
uint8_t broadcast_id[3];
} __packed;
#define BTP_BAP_BROADCAST_SINK_SETUP 0x0a
struct btp_bap_broadcast_sink_setup_cmd {
} __packed;
#define BTP_BAP_BROADCAST_SINK_RELEASE 0x0b
struct btp_bap_broadcast_sink_release_cmd {
} __packed;
#define BTP_BAP_BROADCAST_SCAN_START 0x0c
struct btp_bap_broadcast_scan_start_cmd {
} __packed;
#define BTP_BAP_BROADCAST_SCAN_STOP 0x0d
struct btp_bap_broadcast_scan_stop_cmd {
} __packed;
#define BTP_BAP_BROADCAST_SINK_SYNC 0x0e
struct btp_bap_broadcast_sink_sync_cmd {
bt_addr_le_t address;
uint8_t broadcast_id[3];
uint8_t advertiser_sid;
uint16_t skip;
uint16_t sync_timeout;
} __packed;
#define BTP_BAP_BROADCAST_SINK_STOP 0x0f
struct btp_bap_broadcast_sink_stop_cmd {
bt_addr_le_t address;
uint8_t broadcast_id[3];
} __packed;
/* BAP events */
#define BTP_BAP_EV_DISCOVERY_COMPLETED 0x80
#define BTP_BAP_EV_DISCOVERY_COMPLETED 0x80
struct btp_bap_discovery_completed_ev {
bt_addr_le_t address;
uint8_t status;
} __packed;
#define BTP_BAP_EV_CODEC_CAP_FOUND 0x81
#define BTP_BAP_EV_CODEC_CAP_FOUND 0x81
struct btp_bap_codec_cap_found_ev {
bt_addr_le_t address;
uint8_t dir;
@ -50,17 +127,55 @@ struct btp_bap_codec_cap_found_ev {
uint8_t channel_counts;
} __packed;
#define BTP_BAP_EV_ASE_FOUND 0x82
#define BTP_BAP_EV_ASE_FOUND 0x82
struct btp_ascs_ase_found_ev {
bt_addr_le_t address;
uint8_t dir;
uint8_t ase_id;
} __packed;
#define BTP_BAP_EV_STREAM_RECEIVED 0x83
#define BTP_BAP_EV_STREAM_RECEIVED 0x83
struct btp_bap_stream_received_ev {
bt_addr_le_t address;
uint8_t ase_id;
uint8_t data_len;
uint8_t data[0];
uint8_t data[];
} __packed;
#define BTP_BAP_EV_BAA_FOUND 0x84
struct btp_bap_baa_found_ev {
bt_addr_le_t address;
uint8_t broadcast_id[3];
uint8_t advertiser_sid;
uint16_t padv_interval;
} __packed;
#define BTP_BAP_EV_BIS_FOUND 0x85
struct btp_bap_bis_found_ev {
bt_addr_le_t address;
uint8_t broadcast_id[3];
uint8_t presentation_delay[3];
uint8_t subgroup_id;
uint8_t bis_id;
uint8_t coding_format;
uint16_t vid;
uint16_t cid;
uint8_t cc_ltvs_len;
uint8_t cc_ltvs[];
} __packed;
#define BTP_BAP_EV_BIS_SYNCED 0x86
struct btp_bap_bis_syned_ev {
bt_addr_le_t address;
uint8_t broadcast_id[3];
uint8_t bis_id;
} __packed;
#define BTP_BAP_EV_BIS_STREAM_RECEIVED 0x87
struct btp_bap_bis_stream_received_ev {
bt_addr_le_t address;
uint8_t broadcast_id[3];
uint8_t bis_id;
uint8_t data_len;
uint8_t data[];
} __packed;

View file

@ -101,6 +101,10 @@ struct btp_gap_set_bondable_rp {
uint32_t current_settings;
} __packed;
#define BTP_GAP_ADDR_TYPE_IDENTITY 0
#define BTP_GAP_ADDR_TYPE_RESOLVABLE_PRIVATE 1
#define BTP_GAP_ADDR_TYPE_NON_RESOLVABLE_PRIVATE 2
#define BTP_GAP_START_ADVERTISING 0x0a
struct btp_gap_start_advertising_cmd {
uint8_t adv_data_len;
@ -233,7 +237,7 @@ struct btp_gap_set_mitm {
#define BTP_GAP_SET_FILTER_LIST 0x1c
struct btp_gap_set_filter_list {
uint8_t cnt;
bt_addr_le_t addr[0];
bt_addr_le_t addr[];
} __packed;
#define BTP_GAP_SET_PRIVACY 0x1d
@ -249,6 +253,69 @@ struct btp_gap_set_extended_advertising_rp {
uint32_t current_settings;
} __packed;
#define BTP_GAP_PADV_CONFIGURE 0x22
/* bitmap of flags*/
#define BTP_GAP_PADV_INCLUDE_TX_POWER BIT(0)
struct btp_gap_padv_configure_cmd {
uint8_t flags;
uint16_t interval_min;
uint16_t interval_max;
} __packed;
struct btp_gap_padv_configure_rp {
uint32_t current_settings;
} __packed;
#define BTP_GAP_PADV_START 0x23
struct btp_gap_padv_start_cmd {
uint8_t flags;
} __packed;
struct btp_gap_padv_start_rp {
uint32_t current_settings;
} __packed;
#define BTP_GAP_PADV_STOP 0x24
struct btp_gap_padv_stop_cmd {
} __packed;
struct btp_gap_padv_stop_rp {
uint32_t current_settings;
} __packed;
#define BTP_GAP_PADV_SET_DATA 0x25
struct btp_gap_padv_set_data_cmd {
uint16_t data_len;
uint8_t data[];
} __packed;
#define BTP_GAP_PADV_CREATE_SYNC 0x26
struct btp_gap_padv_create_sync_cmd {
bt_addr_le_t address;
uint8_t advertiser_sid;
uint16_t skip;
uint16_t sync_timeout;
uint8_t flags;
} __packed;
#define BTP_GAP_PADV_SYNC_TRANSFER_SET_INFO 0x27
struct btp_gap_padv_sync_transfer_set_info_cmd {
bt_addr_le_t address;
uint16_t service_data;
} __packed;
#define BTP_GAP_PADV_SYNC_TRANSFER_START 0x28
struct btp_gap_padv_sync_transfer_start_cmd {
uint16_t sync_handle;
bt_addr_le_t address;
uint16_t service_data;
} __packed;
#define BTP_GAP_PADV_SYNC_TRANSFER_RECV 0x29
struct btp_gap_padv_sync_transfer_recv_cmd {
bt_addr_le_t address;
uint16_t skip;
uint16_t sync_timeout;
uint8_t flags;
} __packed;
/* events */
#define BTP_GAP_EV_NEW_SETTINGS 0x80
struct btp_gap_new_settings_ev {
@ -337,3 +404,57 @@ struct btp_gap_bond_pairing_failed_ev {
bt_addr_le_t address;
uint8_t reason;
} __packed;
#define BTP_GAP_EV_PERIODIC_SYNC_ESTABLISHED 0x8d
struct btp_gap_ev_periodic_sync_established_ev {
bt_addr_le_t address;
uint16_t sync_handle;
uint8_t status;
} __packed;
#define BTP_GAP_EV_PERIODIC_SYNC_LOST 0x8e
struct btp_gap_ev_periodic_sync_lost_ev {
uint16_t sync_handle;
uint8_t reason;
} __packed;
#define BTP_GAP_EV_PERIODIC_REPORT 0x8f
struct btp_gap_ev_periodic_report_ev {
uint16_t sync_handle;
uint8_t tx_power;
uint8_t rssi;
uint8_t cte_type;
uint8_t data_status;
uint8_t data_len;
uint8_t data[];
} __packed;
#define BTP_GAP_EV_PERIODIC_TRANSFER_RECEIVED 0x90
struct btp_gap_ev_periodic_transfer_received_ev {
uint16_t sync_handle;
uint8_t tx_power;
uint8_t rssi;
uint8_t cte_type;
uint8_t data_status;
uint8_t data_len;
uint8_t data[];
} __packed;
#if defined(CONFIG_BT_EXT_ADV)
struct bt_le_per_adv_param;
struct bt_le_per_adv_sync_param;
struct bt_le_adv_param;
struct bt_data;
struct bt_le_ext_adv *tester_gap_ext_adv_get(void);
struct bt_le_per_adv_sync *tester_gap_padv_get(void);
int tester_gap_create_adv_instance(struct bt_le_adv_param *param, uint8_t own_addr_type,
const struct bt_data *ad, size_t ad_len,
const struct bt_data *sd, size_t sd_len, uint32_t *settings);
int tester_gap_stop_ext_adv(void);
int tester_gap_start_ext_adv(void);
int tester_gap_padv_configure(const struct bt_le_per_adv_param *param);
int tester_gap_padv_set_data(struct bt_data *per_ad, uint8_t ad_len);
int tester_gap_padv_start(void);
int tester_gap_padv_stop(void);
int tester_padv_create_sync(struct bt_le_per_adv_sync_param *create_params);
#endif /* defined(CONFIG_BT_EXT_ADV) */

View file

@ -31,6 +31,10 @@ void tester_init(void);
void tester_rsp(uint8_t service, uint8_t opcode, uint8_t status);
void tester_rsp_full(uint8_t service, uint8_t opcode, const void *rsp, size_t len);
void tester_event(uint8_t service, uint8_t opcode, const void *data, size_t len);
int tester_rsp_buffer_lock(void);
void tester_rsp_buffer_unlock(void);
void tester_rsp_buffer_free(void);
void tester_rsp_buffer_allocate(size_t len, uint8_t **data);
/* Used to indicate that command length is variable and that validation will
* be done in handler.

View file

@ -17,6 +17,7 @@
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/audio/audio.h>
#include <zephyr/bluetooth/audio/pacs.h>
#include <zephyr/bluetooth/audio/bap_lc3_preset.h>
#include "bap_endpoint.h"
#include <zephyr/logging/log.h>
@ -44,8 +45,6 @@ static const struct bt_audio_codec_cap vendor_codec_cap = BT_AUDIO_CODEC_CAP(
struct audio_stream {
struct bt_bap_stream stream;
uint8_t ase_id;
uint8_t conn_id;
atomic_t seq_num;
uint16_t last_req_seq_num;
uint16_t last_sent_seq_num;
@ -53,10 +52,25 @@ struct audio_stream {
size_t len_to_send;
struct k_work_delayable audio_clock_work;
struct k_work_delayable audio_send_work;
uint8_t cig_id;
uint8_t cis_id;
struct bt_bap_unicast_group **cig;
bool already_sent;
bool broadcast;
union {
/* Unicast */
struct {
uint8_t ase_id;
uint8_t conn_id;
uint8_t cig_id;
uint8_t cis_id;
struct bt_bap_unicast_group **cig;
};
/* Broadcast */
struct {
uint8_t bis_id;
bool bis_synced;
};
};
};
#define MAX_STREAMS_COUNT MAX(CONFIG_BT_BAP_UNICAST_CLIENT_ASE_SNK_COUNT, \
@ -72,7 +86,7 @@ struct audio_connection {
struct bt_audio_codec_qos qos;
struct bt_bap_ep *end_points[MAX_END_POINTS_COUNT];
size_t end_points_count;
} connections[CONFIG_BT_MAX_CONN];
} connections[CONFIG_BT_MAX_CONN], broadcast_connection;
static struct bt_bap_unicast_group *cigs[CONFIG_BT_ISO_MAX_CIG];
@ -95,6 +109,17 @@ static void audio_send_timeout(struct k_work *work);
K_THREAD_STACK_DEFINE(iso_data_thread_stack_area, ISO_DATA_THREAD_STACK_SIZE);
static struct k_work_q iso_data_work_q;
static struct bt_bap_broadcast_source *broadcast_source;
static struct audio_connection *broadcaster;
static struct bt_bap_broadcast_sink *broadcast_sink;
static bt_addr_le_t broadcaster_addr;
static uint32_t broadcaster_broadcast_id;
static struct bt_bap_stream *sink_streams[MAX_STREAMS_COUNT];
/* A mask for the maximum BIS we can sync to. +1 since the BIS indexes start from 1. */
static const uint32_t bis_index_mask = BIT_MASK(MAX_STREAMS_COUNT + 1);
static uint32_t bis_index_bitfield;
#define INVALID_BROADCAST_ID (BT_AUDIO_BROADCAST_ID_MAX + 1)
static bool print_cb(struct bt_data *data, void *user_data)
{
const char *str = (const char *)user_data;
@ -474,6 +499,39 @@ static void btp_send_stream_received_ev(struct bt_conn *conn, struct bt_bap_ep *
tester_event(BTP_SERVICE_ID_BAP, BTP_BAP_EV_STREAM_RECEIVED, ev, sizeof(*ev) + data_len);
}
static void btp_send_bis_syced_ev(const bt_addr_le_t *address, uint32_t broadcast_id,
uint8_t bis_id)
{
struct btp_bap_bis_syned_ev ev;
bt_addr_le_copy(&ev.address, address);
sys_put_le24(broadcast_id, ev.broadcast_id);
ev.bis_id = bis_id;
tester_event(BTP_SERVICE_ID_BAP, BTP_BAP_EV_BIS_SYNCED, &ev, sizeof(ev));
}
static void btp_send_bis_stream_received_ev(const bt_addr_le_t *address, uint32_t broadcast_id,
uint8_t bis_id, uint8_t data_len, uint8_t *data)
{
struct btp_bap_bis_stream_received_ev *ev;
LOG_DBG("Stream received, len %d", data_len);
net_buf_simple_init(rx_ev_buf, 0);
ev = net_buf_simple_add(rx_ev_buf, sizeof(*ev));
bt_addr_le_copy(&ev->address, address);
sys_put_le24(broadcast_id, ev->broadcast_id);
ev->bis_id = bis_id;
ev->data_len = data_len;
memcpy(ev->data, data, data_len);
tester_event(BTP_SERVICE_ID_BAP, BTP_BAP_EV_BIS_STREAM_RECEIVED, ev,
sizeof(*ev) + data_len);
}
static void stream_configured(struct bt_bap_stream *stream,
const struct bt_audio_codec_qos_pref *pref)
{
@ -613,8 +671,13 @@ static void stream_started(struct bt_bap_stream *stream)
K_USEC(a_stream->stream.qos->interval));
}
btp_send_ascs_ase_state_changed_ev(stream->conn, a_stream->ase_id,
stream->ep->status.state);
if (a_stream->broadcast) {
btp_send_bis_syced_ev(&broadcaster_addr, broadcaster_broadcast_id,
a_stream->bis_id);
} else {
btp_send_ascs_ase_state_changed_ev(stream->conn, a_stream->ase_id,
stream->ep->status.state);
}
}
static void stream_stopped(struct bt_bap_stream *stream, uint8_t reason)
@ -627,8 +690,10 @@ static void stream_stopped(struct bt_bap_stream *stream, uint8_t reason)
k_work_cancel_delayable(&a_stream->audio_clock_work);
k_work_cancel_delayable(&a_stream->audio_send_work);
btp_send_ascs_operation_completed_ev(stream->conn, a_stream->ase_id,
BT_ASCS_STOP_OP, BTP_STATUS_SUCCESS);
if (!a_stream->broadcast) {
btp_send_ascs_operation_completed_ev(stream->conn, a_stream->ase_id,
BT_ASCS_STOP_OP, BTP_STATUS_SUCCESS);
}
}
static void stream_recv(struct bt_bap_stream *stream,
@ -643,7 +708,13 @@ static void stream_recv(struct bt_bap_stream *stream,
*/
LOG_DBG("Incoming audio on stream %p len %u", stream, buf->len);
a_stream->already_sent = true;
btp_send_stream_received_ev(stream->conn, stream->ep, buf->len, buf->data);
if (a_stream->broadcast) {
btp_send_bis_stream_received_ev(&broadcaster_addr, broadcaster_broadcast_id,
a_stream->bis_id, buf->len, buf->data);
} else {
btp_send_stream_received_ev(stream->conn, stream->ep, buf->len, buf->data);
}
}
}
@ -1020,6 +1091,10 @@ static void audio_send_timeout(struct k_work *work)
size = ring_buf_get_claim(&audio_ring_buf, &data, stream->stream.qos->sdu);
if (size != 0) {
net_buf_add_mem(buf, data, size);
} else {
k_work_schedule_for_queue(&iso_data_work_q, dwork,
K_USEC(stream->stream.qos->interval));
return;
}
/* Because the seq_num field of the audio_stream struct is atomic_val_t (4 bytes),
@ -1027,13 +1102,12 @@ static void audio_send_timeout(struct k_work *work)
*/
stream->last_req_seq_num = (uint16_t)atomic_get(&stream->seq_num);
LOG_DBG("Sending data to ASE: ase_id %d len %d seq %d", stream->stream.ep->status.id,
size, stream->last_req_seq_num);
LOG_DBG("Sending data to stream %p len %d seq %d", &stream->stream, size,
stream->last_req_seq_num);
err = bt_bap_stream_send(&stream->stream, buf, 0, BT_ISO_TIMESTAMP_NONE);
if (err != 0) {
LOG_ERR("Failed to send audio data to stream: ase_id %d dir %d seq %d err %d",
stream->ase_id, stream->stream.ep->dir, stream->last_req_seq_num, err);
LOG_ERR("Failed to send audio data to stream %p, err %d", &stream->stream, err);
net_buf_unref(buf);
}
@ -1086,6 +1160,566 @@ static uint8_t bap_send(const void *cmd, uint16_t cmd_len,
return BTP_STATUS_SUCCESS;
}
static int setup_broadcast_source(uint8_t streams_per_subgroup, uint8_t subgroups,
struct bt_bap_broadcast_source **source)
{
int err;
struct bt_bap_broadcast_source_stream_param
stream_params[CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT];
struct bt_bap_broadcast_source_subgroup_param
subgroup_param[CONFIG_BT_BAP_BROADCAST_SRC_SUBGROUP_COUNT];
struct bt_bap_broadcast_source_create_param create_param;
if (streams_per_subgroup * subgroups > CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT ||
subgroups > CONFIG_BT_BAP_BROADCAST_SRC_SUBGROUP_COUNT) {
return -EINVAL;
}
for (size_t i = 0U; i < subgroups; i++) {
subgroup_param[i].params_count = streams_per_subgroup;
subgroup_param[i].params = stream_params + i * streams_per_subgroup;
subgroup_param[i].codec_cfg = &broadcaster->codec_cfg;
}
for (size_t j = 0U; j < streams_per_subgroup; j++) {
broadcaster->streams[j].broadcast = true;
stream_params[j].stream = &broadcaster->streams[j].stream;
stream_params[j].data = NULL;
stream_params[j].data_len = 0U;
bt_bap_stream_cb_register(stream_params[j].stream, &stream_ops);
}
create_param.params_count = subgroups;
create_param.params = subgroup_param;
create_param.qos = &broadcaster->qos;
create_param.encryption = false;
create_param.packing = BT_ISO_PACKING_SEQUENTIAL;
LOG_DBG("Creating broadcast source with %zu subgroups with %zu streams",
subgroups, subgroups * streams_per_subgroup);
err = bt_bap_broadcast_source_create(&create_param, source);
if (err != 0) {
LOG_DBG("Unable to create broadcast source: %d", err);
return err;
}
return 0;
}
static uint8_t broadcast_source_setup(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
int err;
const struct btp_bap_broadcast_source_setup_cmd *cp = cmd;
struct btp_bap_broadcast_source_setup_rp *rp = rsp;
struct bt_le_adv_param *param = BT_LE_EXT_ADV_NCONN_NAME;
uint32_t broadcast_id;
uint32_t gap_settings = BIT(BTP_GAP_SETTINGS_DISCOVERABLE) |
BIT(BTP_GAP_SETTINGS_EXTENDED_ADVERTISING);
NET_BUF_SIMPLE_DEFINE(ad_buf, BT_UUID_SIZE_16 + BT_AUDIO_BROADCAST_ID_SIZE);
NET_BUF_SIMPLE_DEFINE(base_buf, 128);
/* Broadcast Audio Streaming Endpoint advertising data */
struct bt_data base_ad;
struct bt_data per_ad;
LOG_DBG("");
broadcaster->codec_cfg.id = cp->coding_format;
broadcaster->codec_cfg.vid = cp->vid;
broadcaster->codec_cfg.cid = cp->cid;
broadcaster->codec_cfg.data_len = cp->cc_ltvs_len;
memcpy(broadcaster->codec_cfg.data, cp->cc_ltvs, cp->cc_ltvs_len);
broadcaster->qos.phy = BT_AUDIO_CODEC_QOS_2M;
broadcaster->qos.framing = cp->framing;
broadcaster->qos.rtn = cp->retransmission_num;
broadcaster->qos.latency = sys_le16_to_cpu(cp->max_transport_latency);
broadcaster->qos.interval = sys_get_le24(cp->sdu_interval);
broadcaster->qos.pd = sys_get_le24(cp->presentation_delay);
broadcaster->qos.sdu = sys_le16_to_cpu(cp->max_sdu);
if (broadcast_source == NULL) {
err = setup_broadcast_source(cp->streams_per_subgroup, cp->subgroups,
&broadcast_source);
} else {
err = bt_bap_broadcast_source_reconfig(broadcast_source, &broadcaster->codec_cfg,
&broadcaster->qos);
}
if (err != 0) {
LOG_DBG("Unable to setup broadcast source: %d", err);
return BTP_STATUS_FAILED;
}
err = bt_bap_broadcast_source_get_id(broadcast_source, &broadcast_id);
if (err != 0) {
LOG_DBG("Unable to get broadcast ID: %d", err);
return BTP_STATUS_FAILED;
}
/* Setup extended advertising data */
net_buf_simple_add_le16(&ad_buf, BT_UUID_BROADCAST_AUDIO_VAL);
net_buf_simple_add_le24(&ad_buf, broadcast_id);
base_ad.type = BT_DATA_SVC_DATA16;
base_ad.data_len = ad_buf.len;
base_ad.data = ad_buf.data;
err = tester_gap_create_adv_instance(param, BTP_GAP_ADDR_TYPE_IDENTITY, &base_ad, 1, NULL,
0, &gap_settings);
if (err != 0) {
LOG_DBG("Failed to create extended advertising instance: %d", err);
return BTP_STATUS_FAILED;
}
err = tester_gap_padv_configure(BT_LE_PER_ADV_PARAM(BT_GAP_PER_ADV_FAST_INT_MIN_2,
BT_GAP_PER_ADV_FAST_INT_MAX_2,
BT_LE_PER_ADV_OPT_USE_TX_POWER));
if (err != 0) {
LOG_DBG("Failed to configure periodic advertising: %d", err);
return BTP_STATUS_FAILED;
}
err = bt_bap_broadcast_source_get_base(broadcast_source, &base_buf);
if (err != 0) {
LOG_DBG("Failed to get encoded BASE: %d\n", err);
return BTP_STATUS_FAILED;
}
per_ad.type = BT_DATA_SVC_DATA16;
per_ad.data_len = base_buf.len;
per_ad.data = base_buf.data;
err = tester_gap_padv_set_data(&per_ad, 1);
if (err != 0) {
return BTP_STATUS_FAILED;
}
rp->gap_settings = gap_settings;
sys_put_le24(broadcast_id, rp->broadcast_id);
*rsp_len = sizeof(*rp) + 1;
return BTP_STATUS_SUCCESS;
}
static uint8_t broadcast_source_release(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
int err;
LOG_DBG("");
err = bt_bap_broadcast_source_delete(broadcast_source);
if (err != 0) {
LOG_DBG("Unable to delete broadcast source: %d", err);
return BTP_STATUS_FAILED;
}
memset(broadcaster, 0, sizeof(*broadcaster));
broadcast_source = NULL;
return BTP_STATUS_SUCCESS;
}
static uint8_t broadcast_adv_start(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
int err;
struct bt_le_ext_adv *ext_adv = tester_gap_ext_adv_get();
LOG_DBG("");
if (ext_adv == NULL) {
return BTP_STATUS_FAILED;
}
err = tester_gap_start_ext_adv();
if (err != 0) {
return BTP_STATUS_FAILED;
}
err = tester_gap_padv_start();
if (err != 0) {
LOG_DBG("Unable to start periodic advertising: %d", err);
return BTP_STATUS_FAILED;
}
return BTP_STATUS_SUCCESS;
}
static uint8_t broadcast_adv_stop(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
int err;
LOG_DBG("");
err = tester_gap_padv_stop();
if (err != 0) {
return BTP_STATUS_FAILED;
}
err = tester_gap_stop_ext_adv();
if (err != 0) {
return BTP_STATUS_FAILED;
}
return BTP_STATUS_SUCCESS;
}
static uint8_t broadcast_source_start(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
int err;
struct bt_le_ext_adv *ext_adv = tester_gap_ext_adv_get();
LOG_DBG("");
if (ext_adv == NULL) {
return BTP_STATUS_FAILED;
}
err = bt_bap_broadcast_source_start(broadcast_source, ext_adv);
if (err != 0) {
LOG_DBG("Unable to start broadcast source: %d", err);
return BTP_STATUS_FAILED;
}
return BTP_STATUS_SUCCESS;
}
static uint8_t broadcast_source_stop(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
int err;
LOG_DBG("");
err = bt_bap_broadcast_source_stop(broadcast_source);
if (err != 0) {
LOG_DBG("Unable to stop broadcast source: %d", err);
return BTP_STATUS_FAILED;
}
return BTP_STATUS_SUCCESS;
}
static int broadcast_sink_reset(void)
{
bis_index_bitfield = 0U;
(void)memset(&broadcaster_addr, 0, sizeof(broadcaster_addr));
(void)memset(broadcaster, 0, sizeof(*broadcaster));
broadcaster_broadcast_id = INVALID_BROADCAST_ID;
return 0;
}
static void btp_send_baa_found_ev(const bt_addr_le_t *address, uint32_t broadcast_id,
uint8_t sid, uint16_t interval)
{
struct btp_bap_baa_found_ev ev;
bt_addr_le_copy(&ev.address, address);
sys_put_le24(broadcast_id, ev.broadcast_id);
ev.advertiser_sid = sid;
ev.padv_interval = interval;
tester_event(BTP_SERVICE_ID_BAP, BTP_BAP_EV_BAA_FOUND, &ev, sizeof(ev));
}
static bool scan_check_and_sync_broadcast(struct bt_data *data, void *user_data)
{
const struct bt_le_scan_recv_info *info = user_data;
char le_addr[BT_ADDR_LE_STR_LEN];
struct bt_uuid_16 adv_uuid;
uint32_t broadcast_id;
if (data->type != BT_DATA_SVC_DATA16) {
return true;
}
if (data->data_len < BT_UUID_SIZE_16 + BT_AUDIO_BROADCAST_ID_SIZE) {
return true;
}
if (!bt_uuid_create(&adv_uuid.uuid, data->data, BT_UUID_SIZE_16)) {
return true;
}
if (bt_uuid_cmp(&adv_uuid.uuid, BT_UUID_BROADCAST_AUDIO)) {
return true;
}
broadcast_id = sys_get_le24(data->data + BT_UUID_SIZE_16);
bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
LOG_DBG("Found broadcaster with ID 0x%06X and addr %s and sid 0x%02X", broadcast_id,
le_addr, info->sid);
btp_send_baa_found_ev(info->addr, broadcast_id, info->sid, info->interval);
/* Stop parsing */
return false;
}
static void broadcast_scan_recv(const struct bt_le_scan_recv_info *info, struct net_buf_simple *ad)
{
/* If 0 there is no periodic advertising. */
if (info->interval != 0U) {
bt_data_parse(ad, scan_check_and_sync_broadcast, (void *)info);
}
}
static struct bt_le_scan_cb bap_scan_cb = {
.recv = broadcast_scan_recv,
};
static void btp_send_bis_found_ev(const bt_addr_le_t *address, uint32_t broadcast_id, uint32_t pd,
uint8_t subgroup_index, uint8_t bis_index,
const struct bt_audio_codec_cfg *codec_cfg)
{
struct btp_bap_bis_found_ev *ev;
tester_rsp_buffer_lock();
tester_rsp_buffer_allocate(sizeof(*ev) + codec_cfg->data_len, (uint8_t **)&ev);
bt_addr_le_copy(&ev->address, address);
sys_put_le24(broadcast_id, ev->broadcast_id);
sys_put_le24(pd, ev->presentation_delay);
ev->subgroup_id = subgroup_index;
ev->bis_id = bis_index;
ev->coding_format = codec_cfg->id;
ev->vid = sys_cpu_to_le16(codec_cfg->vid);
ev->cid = sys_cpu_to_le16(codec_cfg->cid);
ev->cc_ltvs_len = codec_cfg->data_len;
memcpy(ev->cc_ltvs, codec_cfg->data, ev->cc_ltvs_len);
tester_event(BTP_SERVICE_ID_BAP, BTP_BAP_EV_BIS_FOUND, ev,
sizeof(*ev) + ev->cc_ltvs_len);
tester_rsp_buffer_free();
tester_rsp_buffer_unlock();
}
static void base_recv_cb(struct bt_bap_broadcast_sink *sink, const struct bt_bap_base *base)
{
size_t stream_count = 0U;
uint32_t base_bis_index_bitfield = 0U;
const struct bt_audio_codec_cfg *codec_cfg;
LOG_DBG("");
if (broadcaster_broadcast_id != sink->broadcast_id) {
return;
}
LOG_DBG("Received BASE with %u subgroups from broadcast sink %p", base->subgroup_count,
sink);
for (size_t i = 0U; i < base->subgroup_count; i++) {
for (size_t j = 0U; j < base->subgroups[i].bis_count; j++) {
const uint8_t index = base->subgroups[i].bis_data[j].index;
codec_cfg = &base->subgroups[i].codec_cfg;
base_bis_index_bitfield |= BIT(index);
if (stream_count < MAX_STREAMS_COUNT) {
broadcaster->streams[stream_count++].bis_id = index;
}
btp_send_bis_found_ev(&broadcaster_addr, sink->broadcast_id,
sink->base.pd, i, index, codec_cfg);
}
}
bis_index_bitfield = base_bis_index_bitfield & bis_index_mask;
}
static void syncable_cb(struct bt_bap_broadcast_sink *sink, bool encrypted)
{
int err;
LOG_DBG("");
err = bt_bap_broadcast_sink_sync(broadcast_sink, bis_index_bitfield, sink_streams, NULL);
if (err != 0) {
LOG_DBG("Unable to sync to broadcast source: %d", err);
}
}
static struct bt_bap_broadcast_sink_cb broadcast_sink_cbs = {
.base_recv = base_recv_cb,
.syncable = syncable_cb,
};
static void bap_pa_sync_synced_cb(struct bt_le_per_adv_sync *sync,
struct bt_le_per_adv_sync_synced_info *info)
{
int err;
struct bt_le_per_adv_sync *pa_sync;
LOG_DBG("");
pa_sync = tester_gap_padv_get();
if (sync != pa_sync) {
return;
}
err = bt_bap_broadcast_sink_create(pa_sync, broadcaster_broadcast_id, &broadcast_sink);
if (err != 0) {
LOG_DBG("Failed to create broadcast sink: ID 0x%06X, err %d",
broadcaster_broadcast_id, err);
}
}
static struct bt_le_per_adv_sync_cb bap_pa_sync_cb = {
.synced = bap_pa_sync_synced_cb,
};
static uint8_t broadcast_sink_setup(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
int err;
LOG_DBG("");
err = broadcast_sink_reset();
if (err != 0) {
return BTP_STATUS_FAILED;
}
for (size_t i = 0U; i < MAX_STREAMS_COUNT; i++) {
broadcaster->streams[i].broadcast = true;
sink_streams[i] = &broadcaster->streams[i].stream;
sink_streams[i]->ops = &stream_ops;
}
bt_bap_broadcast_sink_register_cb(&broadcast_sink_cbs);
bt_le_per_adv_sync_cb_register(&bap_pa_sync_cb);
bt_le_scan_cb_register(&bap_scan_cb);
return BTP_STATUS_SUCCESS;
}
static uint8_t broadcast_sink_release(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
int err;
LOG_DBG("");
err = broadcast_sink_reset();
if (err != 0) {
return BTP_STATUS_FAILED;
}
return BTP_STATUS_SUCCESS;
}
static uint8_t broadcast_scan_start(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
int err;
LOG_DBG("");
err = bt_le_scan_start(BT_LE_SCAN_ACTIVE, NULL);
if (err != 0 && err != -EALREADY) {
LOG_DBG("Unable to start scan for broadcast sources: %d", err);
return BTP_STATUS_FAILED;
}
return BTP_STATUS_SUCCESS;
}
static uint8_t broadcast_scan_stop(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
int err;
LOG_DBG("");
err = bt_le_scan_stop();
if (err != 0) {
LOG_DBG("Failed to stop scan, %d", err);
return BTP_STATUS_FAILED;
}
return BTP_STATUS_SUCCESS;
}
static uint8_t broadcast_sink_sync(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
int err;
const struct btp_bap_broadcast_sink_sync_cmd *cp = cmd;
struct bt_le_per_adv_sync_param create_params = {0};
LOG_DBG("");
/* Sink Sync steps:
* 1. bt_le_per_adv_sync_create()
* 2. bap_pa_sync_synced_cb()
* 3. bt_bap_broadcast_sink_create()
* 4. - base_recv_cb()
* - syncable_cb()
* - broadcast_code_cb() <- only with scan delegator
* - bis_sync_req_cb() <- only for scan delegator
* 5. bt_bap_broadcast_sink_sync()
* 6. stream_started()
* 7. stream_recv_cb()
* 8. bap_pa_sync_terminated_cb()
* 9. stream_stopped_cb()
*/
broadcaster_broadcast_id = sys_get_le24(cp->broadcast_id);
bt_addr_le_copy(&broadcaster_addr, &cp->address);
bt_addr_le_copy(&create_params.addr, &cp->address);
create_params.options = BT_LE_PER_ADV_SYNC_OPT_FILTER_DUPLICATE;
create_params.sid = cp->advertiser_sid;
create_params.skip = cp->skip;
create_params.timeout = cp->sync_timeout;
err = tester_padv_create_sync(&create_params);
if (err != 0) {
return BTP_STATUS_FAILED;
}
return BTP_STATUS_SUCCESS;
}
static uint8_t broadcast_sink_stop(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
int err;
LOG_DBG("");
err = bt_bap_broadcast_sink_stop(broadcast_sink);
if (err != 0) {
LOG_DBG("Unable to sync to broadcast source: %d", err);
return BTP_STATUS_FAILED;
}
return BTP_STATUS_SUCCESS;
}
static void connected(struct bt_conn *conn, uint8_t err)
{
struct audio_connection *audio_conn;
@ -1196,7 +1830,7 @@ static uint8_t client_add_ase_to_cis(struct audio_connection *audio_conn, uint8_
}
static int client_create_unicast_group(struct audio_connection *audio_conn, uint8_t ase_id,
uint8_t cig_id)
uint8_t cig_id)
{
int err;
struct bt_bap_unicast_group_stream_pair_param pair_params[MAX_STREAMS_COUNT];
@ -1383,9 +2017,9 @@ static uint8_t ascs_configure_codec(const void *cmd, uint16_t cmd_len,
codec_cfg->vid = cp->vid;
codec_cfg->cid = cp->cid;
if (cp->ltvs_len != 0) {
codec_cfg->data_len = cp->ltvs_len;
memcpy(codec_cfg->data, cp->ltvs, cp->ltvs_len);
if (cp->cc_ltvs_len != 0) {
codec_cfg->data_len = cp->cc_ltvs_len;
memcpy(codec_cfg->data, cp->cc_ltvs, cp->cc_ltvs_len);
}
if (conn_info.role == BT_HCI_ROLE_CENTRAL) {
@ -1938,6 +2572,66 @@ static const struct btp_handler bap_handlers[] = {
.expect_len = BTP_HANDLER_LENGTH_VARIABLE,
.func = bap_send,
},
{
.opcode = BTP_BAP_BROADCAST_SOURCE_SETUP,
.expect_len = BTP_HANDLER_LENGTH_VARIABLE,
.func = broadcast_source_setup,
},
{
.opcode = BTP_BAP_BROADCAST_SOURCE_RELEASE,
.expect_len = sizeof(struct btp_bap_broadcast_source_release_cmd),
.func = broadcast_source_release,
},
{
.opcode = BTP_BAP_BROADCAST_ADV_START,
.expect_len = sizeof(struct btp_bap_broadcast_adv_start_cmd),
.func = broadcast_adv_start,
},
{
.opcode = BTP_BAP_BROADCAST_ADV_STOP,
.expect_len = sizeof(struct btp_bap_broadcast_adv_stop_cmd),
.func = broadcast_adv_stop,
},
{
.opcode = BTP_BAP_BROADCAST_SOURCE_START,
.expect_len = sizeof(struct btp_bap_broadcast_source_start_cmd),
.func = broadcast_source_start,
},
{
.opcode = BTP_BAP_BROADCAST_SOURCE_STOP,
.expect_len = sizeof(struct btp_bap_broadcast_source_stop_cmd),
.func = broadcast_source_stop,
},
{
.opcode = BTP_BAP_BROADCAST_SINK_SETUP,
.expect_len = BTP_HANDLER_LENGTH_VARIABLE,
.func = broadcast_sink_setup,
},
{
.opcode = BTP_BAP_BROADCAST_SINK_RELEASE,
.expect_len = sizeof(struct btp_bap_broadcast_sink_release_cmd),
.func = broadcast_sink_release,
},
{
.opcode = BTP_BAP_BROADCAST_SCAN_START,
.expect_len = sizeof(struct btp_bap_broadcast_scan_start_cmd),
.func = broadcast_scan_start,
},
{
.opcode = BTP_BAP_BROADCAST_SCAN_STOP,
.expect_len = sizeof(struct btp_bap_broadcast_scan_stop_cmd),
.func = broadcast_scan_stop,
},
{
.opcode = BTP_BAP_BROADCAST_SINK_SYNC,
.expect_len = sizeof(struct btp_bap_broadcast_sink_sync_cmd),
.func = broadcast_sink_sync,
},
{
.opcode = BTP_BAP_BROADCAST_SINK_STOP,
.expect_len = sizeof(struct btp_bap_broadcast_sink_stop_cmd),
.func = broadcast_sink_stop,
},
};
uint8_t tester_init_pacs(void)
@ -1999,6 +2693,8 @@ uint8_t tester_init_bap(void)
/* reset data */
(void)memset(connections, 0, sizeof(connections));
broadcaster = &broadcast_connection;
err = bt_bap_unicast_client_register_cb(&unicast_client_cbs);
if (err != 0) {
LOG_DBG("Failed to register client callbacks: %d", err);

View file

@ -32,10 +32,6 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_BTTESTER_LOG_LEVEL);
#define BT_LE_AD_DISCOV_MASK (BT_LE_AD_LIMITED | BT_LE_AD_GENERAL)
#define ADV_BUF_LEN (sizeof(struct btp_gap_device_found_ev) + 2 * 31)
#if defined(CONFIG_BT_EXT_ADV)
static struct bt_le_ext_adv *ext_adv;
#endif
static atomic_t current_settings;
struct bt_conn_auth_cb cb;
static uint8_t oob_legacy_tk[16] = { 0 };
@ -476,6 +472,47 @@ static struct bt_data ad[10] = {
};
static struct bt_data sd[10];
#if defined(CONFIG_BT_EXT_ADV)
static struct bt_le_ext_adv *ext_adv;
struct bt_le_ext_adv *tester_gap_ext_adv_get(void)
{
return ext_adv;
}
int tester_gap_start_ext_adv(void)
{
int err;
err = bt_le_ext_adv_start(ext_adv, BT_LE_EXT_ADV_START_DEFAULT);
if (err != 0) {
LOG_ERR("Failed to start advertising");
return -EINVAL;
}
atomic_set_bit(&current_settings, BTP_GAP_SETTINGS_ADVERTISING);
return 0;
}
int tester_gap_stop_ext_adv(void)
{
int err;
err = bt_le_ext_adv_stop(ext_adv);
if (err != 0) {
LOG_ERR("Failed to stop advertising");
return -EINVAL;
}
atomic_clear_bit(&current_settings, BTP_GAP_SETTINGS_ADVERTISING);
return 0;
}
#endif /* defined(CONFIG_BT_EXT_ADV) */
static uint8_t set_discoverable(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
@ -528,6 +565,66 @@ static uint8_t set_bondable(const void *cmd, uint16_t cmd_len,
return BTP_STATUS_SUCCESS;
}
int tester_gap_create_adv_instance(struct bt_le_adv_param *param, uint8_t own_addr_type,
const struct bt_data *ad, size_t ad_len,
const struct bt_data *sd, size_t sd_len,
uint32_t *settings)
{
int err = 0;
if (settings != NULL) {
atomic_set(&current_settings, *settings);
}
if (atomic_test_bit(&current_settings, BTP_GAP_SETTINGS_CONNECTABLE)) {
param->options |= BT_LE_ADV_OPT_CONNECTABLE;
}
switch (own_addr_type) {
case BTP_GAP_ADDR_TYPE_IDENTITY:
param->options |= BT_LE_ADV_OPT_USE_IDENTITY;
break;
#if defined(CONFIG_BT_PRIVACY)
case BTP_GAP_ADDR_TYPE_RESOLVABLE_PRIVATE:
/* RPA usage is is controlled via privacy settings */
if (!atomic_test_bit(&current_settings, BTP_GAP_SETTINGS_PRIVACY)) {
return -EINVAL;
}
break;
case BTP_GAP_ADDR_TYPE_NON_RESOLVABLE_PRIVATE:
/* NRPA is used only for non-connectable advertising */
if (atomic_test_bit(&current_settings, BTP_GAP_SETTINGS_CONNECTABLE)) {
return -EINVAL;
}
break;
#endif
default:
return -EINVAL;
}
if (IS_ENABLED(CONFIG_BT_EXT_ADV) && atomic_test_bit(&current_settings,
BTP_GAP_SETTINGS_EXTENDED_ADVERTISING)) {
param->options |= BT_LE_ADV_OPT_EXT_ADV;
if (ext_adv != NULL) {
err = bt_le_ext_adv_delete(ext_adv);
if (err != 0) {
return err;
}
ext_adv = NULL;
}
err = bt_le_ext_adv_create(param, NULL, &ext_adv);
if (err != 0) {
return BTP_STATUS_FAILED;
}
err = bt_le_ext_adv_set_data(ext_adv, ad, ad_len, sd_len ? sd : NULL, sd_len);
}
return err;
}
static uint8_t start_advertising(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
@ -583,57 +680,16 @@ static uint8_t start_advertising(const void *cmd, uint16_t cmd_len,
i += sd[sd_len].data_len;
}
if (atomic_test_bit(&current_settings, BTP_GAP_SETTINGS_CONNECTABLE)) {
param.options |= BT_LE_ADV_OPT_CONNECTABLE;
}
switch (own_addr_type) {
case 0x00:
param.options |= BT_LE_ADV_OPT_USE_IDENTITY;
break;
#if defined(CONFIG_BT_PRIVACY)
case 0x01:
/* RPA usage is is controlled via privacy settings */
if (!atomic_test_bit(&current_settings, BTP_GAP_SETTINGS_PRIVACY)) {
return BTP_STATUS_FAILED;
}
break;
case 0x02:
/* NRPA is used only for non-connectable advertising */
if (atomic_test_bit(&current_settings, BTP_GAP_SETTINGS_CONNECTABLE)) {
return BTP_STATUS_FAILED;
}
break;
#endif
default:
err = tester_gap_create_adv_instance(&param, own_addr_type, ad, adv_len, sd, sd_len, NULL);
if (err != 0) {
return BTP_STATUS_FAILED;
}
if (atomic_test_bit(&current_settings, BTP_GAP_SETTINGS_EXTENDED_ADVERTISING)) {
#if defined(CONFIG_BT_EXT_ADV)
param.options |= BT_LE_ADV_OPT_EXT_ADV;
if (ext_adv != NULL) {
err = bt_le_ext_adv_delete(ext_adv);
if (err) {
return BTP_STATUS_FAILED;
}
ext_adv = NULL;
}
err = bt_le_ext_adv_create(&param, NULL, &ext_adv);
if (err) {
return BTP_STATUS_FAILED;
}
err = bt_le_ext_adv_set_data(ext_adv, ad, adv_len, sd_len ? sd : NULL, sd_len);
if (err) {
return BTP_STATUS_FAILED;
}
if (atomic_test_bit(&current_settings, BTP_GAP_SETTINGS_EXTENDED_ADVERTISING)) {
err = bt_le_ext_adv_start(ext_adv, BT_LE_EXT_ADV_START_DEFAULT);
#else
return BTP_STATUS_FAILED;
if (0) {
#endif
} else {
err = bt_le_adv_start(&param, ad, adv_len, sd_len ? sd : NULL, sd_len);
@ -642,6 +698,7 @@ static uint8_t start_advertising(const void *cmd, uint16_t cmd_len,
/* BTP API don't allow to set empty scan response data. */
if (err < 0) {
LOG_ERR("Failed to start advertising");
return BTP_STATUS_FAILED;
}
@ -1273,6 +1330,287 @@ static uint8_t set_extended_advertising(const void *cmd, uint16_t cmd_len,
return BTP_STATUS_SUCCESS;
}
#if defined(CONFIG_BT_PER_ADV)
static struct bt_data padv[10];
static struct bt_le_per_adv_sync *pa_sync;
struct bt_le_per_adv_sync *tester_gap_padv_get(void)
{
return pa_sync;
}
static void pa_sync_synced_cb(struct bt_le_per_adv_sync *sync,
struct bt_le_per_adv_sync_synced_info *info)
{
LOG_DBG("");
}
static void pa_sync_terminated_cb(struct bt_le_per_adv_sync *sync,
const struct bt_le_per_adv_sync_term_info *info)
{
LOG_DBG("");
if (sync == pa_sync) {
LOG_DBG("PA sync lost with reason %u", info->reason);
pa_sync = NULL;
}
}
static struct bt_le_per_adv_sync_cb pa_sync_cb = {
.synced = pa_sync_synced_cb,
.term = pa_sync_terminated_cb,
};
int tester_gap_padv_configure(const struct bt_le_per_adv_param *param)
{
int err;
struct bt_le_adv_param ext_adv_param = BT_LE_ADV_PARAM_INIT(BT_LE_ADV_OPT_ONE_TIME,
param->interval_min,
param->interval_max,
NULL);
if (ext_adv == NULL) {
current_settings = BIT(BTP_GAP_SETTINGS_DISCOVERABLE) |
BIT(BTP_GAP_SETTINGS_EXTENDED_ADVERTISING);
err = tester_gap_create_adv_instance(&ext_adv_param, BTP_GAP_ADDR_TYPE_IDENTITY, ad,
1, NULL, 0, NULL);
if (err != 0) {
return -EINVAL;
}
}
/* Set periodic advertising parameters and the required
* bit in AD Flags of extended advertising.
*/
err = bt_le_per_adv_set_param(ext_adv, param);
if (err != 0) {
LOG_DBG("Failed to set periodic advertising parameters (err %d)\n", err);
}
return err;
}
static uint8_t padv_configure(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
int err;
uint32_t options = BT_LE_PER_ADV_OPT_NONE;
const struct btp_gap_padv_configure_cmd *cp = cmd;
struct btp_gap_padv_configure_rp *rp = rsp;
if (cp->flags & BTP_GAP_PADV_INCLUDE_TX_POWER) {
options |= BT_LE_PER_ADV_OPT_USE_TX_POWER;
}
err = tester_gap_padv_configure(BT_LE_PER_ADV_PARAM(sys_le16_to_cpu(cp->interval_min),
sys_le16_to_cpu(cp->interval_max),
options));
if (err) {
return BTP_STATUS_FAILED;
}
rp->current_settings = sys_cpu_to_le32(current_settings);
*rsp_len = sizeof(*rp);
return BTP_STATUS_SUCCESS;
}
int tester_gap_padv_start(void)
{
int err;
if (ext_adv == NULL) {
return -EINVAL;
}
if (!atomic_test_bit(&current_settings, BTP_GAP_SETTINGS_ADVERTISING)) {
err = tester_gap_start_ext_adv();
if (err != 0) {
return -EINVAL;
}
}
/* Enable Periodic Advertising */
err = bt_le_per_adv_start(ext_adv);
if (err != 0) {
LOG_DBG("Failed to start periodic advertising data: %d", err);
}
return err;
}
static uint8_t padv_start(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
int err;
struct btp_gap_padv_start_rp *rp = rsp;
err = tester_gap_padv_start();
if (err) {
return BTP_STATUS_FAILED;
}
rp->current_settings = sys_cpu_to_le32(current_settings);
*rsp_len = sizeof(*rp);
return BTP_STATUS_SUCCESS;
}
int tester_gap_padv_stop(void)
{
int err;
if (ext_adv == NULL) {
return -EINVAL;
}
/* Enable Periodic Advertising */
err = bt_le_per_adv_stop(ext_adv);
if (err != 0) {
LOG_DBG("Failed to stop periodic advertising data: %d", err);
}
return err;
}
static uint8_t padv_stop(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
int err;
struct btp_gap_padv_stop_rp *rp = rsp;
err = tester_gap_padv_stop();
if (err) {
return BTP_STATUS_FAILED;
}
rp->current_settings = sys_cpu_to_le32(current_settings);
*rsp_len = sizeof(*rp);
return BTP_STATUS_SUCCESS;
}
int tester_gap_padv_set_data(struct bt_data *per_ad, uint8_t ad_len)
{
int err;
if (ext_adv == NULL) {
return -EINVAL;
}
/* Set Periodic Advertising data */
err = bt_le_per_adv_set_data(ext_adv, per_ad, ad_len);
if (err != 0) {
LOG_DBG("Failed to set periodic advertising data: %d", err);
}
return err;
}
static uint8_t padv_set_data(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
int err;
uint8_t padv_len = 0U;
const struct btp_gap_padv_set_data_cmd *cp = cmd;
for (uint8_t i = 0; i < cp->data_len; padv_len++) {
if (padv_len >= ARRAY_SIZE(padv)) {
LOG_ERR("padv[] Out of memory");
return BTP_STATUS_FAILED;
}
padv[padv_len].data_len = cp->data[i++] - 1;
padv[padv_len].type = cp->data[i++];
padv[padv_len].data = &cp->data[i];
i += padv[padv_len].data_len;
}
err = tester_gap_padv_set_data(padv, padv_len);
if (err != 0) {
return BTP_STATUS_FAILED;
}
return BTP_STATUS_SUCCESS;
}
int tester_padv_create_sync(struct bt_le_per_adv_sync_param *create_params)
{
int err;
if (pa_sync != NULL) {
return -EBUSY;
}
err = bt_le_per_adv_sync_create(create_params, &pa_sync);
if (err != 0) {
LOG_DBG("Unable to sync to PA: %d", err);
}
return err;
}
static uint8_t padv_create_sync(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
int err;
const struct btp_gap_padv_create_sync_cmd *cp = cmd;
struct bt_le_per_adv_sync_param create_params = {0};
bt_addr_le_copy(&create_params.addr, &cp->address);
create_params.options = BT_LE_PER_ADV_SYNC_OPT_FILTER_DUPLICATE;
create_params.sid = cp->advertiser_sid;
create_params.skip = cp->skip;
create_params.timeout = cp->sync_timeout;
err = tester_padv_create_sync(&create_params);
if (err != 0) {
return BTP_STATUS_FAILED;
}
return BTP_STATUS_SUCCESS;
}
static uint8_t padv_sync_transfer_set_info(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
const struct btp_gap_padv_sync_transfer_set_info_cmd *cp = cmd;
(void)cp;
/* TODO */
return BTP_STATUS_FAILED;
}
static uint8_t padv_sync_transfer_start(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
const struct btp_gap_padv_sync_transfer_start_cmd *cp = cmd;
(void)cp;
/* TODO */
return BTP_STATUS_FAILED;
}
static uint8_t padv_sync_transfer_recv(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
const struct btp_gap_padv_sync_transfer_recv_cmd *cp = cmd;
(void)cp;
/* TODO */
return BTP_STATUS_FAILED;
}
#endif /* defined(CONFIG_BT_PER_ADV) */
static const struct btp_handler handlers[] = {
{
.opcode = BTP_GAP_READ_SUPPORTED_COMMANDS,
@ -1404,7 +1742,49 @@ static const struct btp_handler handlers[] = {
.expect_len = sizeof(struct btp_gap_set_extended_advertising_cmd),
.func = set_extended_advertising,
},
#endif
#if defined(CONFIG_BT_PER_ADV)
{
.opcode = BTP_GAP_PADV_CONFIGURE,
.expect_len = sizeof(struct btp_gap_padv_configure_cmd),
.func = padv_configure,
},
{
.opcode = BTP_GAP_PADV_START,
.expect_len = sizeof(struct btp_gap_padv_start_cmd),
.func = padv_start,
},
{
.opcode = BTP_GAP_PADV_STOP,
.expect_len = sizeof(struct btp_gap_padv_stop_cmd),
.func = padv_stop,
},
{
.opcode = BTP_GAP_PADV_SET_DATA,
.expect_len = BTP_HANDLER_LENGTH_VARIABLE,
.func = padv_set_data,
},
{
.opcode = BTP_GAP_PADV_CREATE_SYNC,
.expect_len = sizeof(struct btp_gap_padv_create_sync_cmd),
.func = padv_create_sync,
},
{
.opcode = BTP_GAP_PADV_SYNC_TRANSFER_SET_INFO,
.expect_len = sizeof(struct btp_gap_padv_sync_transfer_set_info_cmd),
.func = padv_sync_transfer_set_info,
},
{
.opcode = BTP_GAP_PADV_SYNC_TRANSFER_START,
.expect_len = sizeof(struct btp_gap_padv_sync_transfer_start_cmd),
.func = padv_sync_transfer_start,
},
{
.opcode = BTP_GAP_PADV_SYNC_TRANSFER_RECV,
.expect_len = sizeof(struct btp_gap_padv_sync_transfer_recv_cmd),
.func = padv_sync_transfer_recv,
},
#endif /* defined(CONFIG_BT_PER_ADV) */
#endif /* defined(CONFIG_BT_EXT_ADV) */
};
uint8_t tester_init_gap(void)
@ -1436,6 +1816,10 @@ uint8_t tester_init_gap(void)
bt_conn_cb_register(&conn_callbacks);
bt_conn_auth_info_cb_register(&auth_info_cb);
#if defined(CONFIG_BT_PER_ADV)
bt_le_per_adv_sync_cb_register(&pa_sync_cb);
#endif /* defined(CONFIG_BT_PER_ADV) */
tester_register_command_handlers(BTP_SERVICE_ID_GAP, handlers,
ARRAY_SIZE(handlers));