diff --git a/tests/bluetooth/tester/overlay-le-audio.conf b/tests/bluetooth/tester/overlay-le-audio.conf index 4478c9e7c21..99cb4f8f8ef 100644 --- a/tests/bluetooth/tester/overlay-le-audio.conf +++ b/tests/bluetooth/tester/overlay-le-audio.conf @@ -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 diff --git a/tests/bluetooth/tester/src/btp.c b/tests/bluetooth/tester/src/btp.c index ced25b2210e..60f3bc1cbbd 100644 --- a/tests/bluetooth/tester/src/btp.c +++ b/tests/bluetooth/tester/src/btp.c @@ -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) { diff --git a/tests/bluetooth/tester/src/btp/btp_ascs.h b/tests/bluetooth/tester/src/btp/btp_ascs.h index a5968a919af..f14cb5de3d4 100644 --- a/tests/bluetooth/tester/src/btp/btp_ascs.h +++ b/tests/bluetooth/tester/src/btp/btp_ascs.h @@ -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 diff --git a/tests/bluetooth/tester/src/btp/btp_bap.h b/tests/bluetooth/tester/src/btp/btp_bap.h index 2157d6c79fc..ede3448f2cf 100644 --- a/tests/bluetooth/tester/src/btp/btp_bap.h +++ b/tests/bluetooth/tester/src/btp/btp_bap.h @@ -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; diff --git a/tests/bluetooth/tester/src/btp/btp_gap.h b/tests/bluetooth/tester/src/btp/btp_gap.h index dee7d5be517..960d810e025 100644 --- a/tests/bluetooth/tester/src/btp/btp_gap.h +++ b/tests/bluetooth/tester/src/btp/btp_gap.h @@ -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) */ diff --git a/tests/bluetooth/tester/src/btp/bttester.h b/tests/bluetooth/tester/src/btp/bttester.h index 7d9e65c2d37..1602658ad17 100644 --- a/tests/bluetooth/tester/src/btp/bttester.h +++ b/tests/bluetooth/tester/src/btp/bttester.h @@ -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. diff --git a/tests/bluetooth/tester/src/btp_bap.c b/tests/bluetooth/tester/src/btp_bap.c index fd084c2aa4b..ba34d9613fc 100644 --- a/tests/bluetooth/tester/src/btp_bap.c +++ b/tests/bluetooth/tester/src/btp_bap.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "bap_endpoint.h" #include @@ -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); diff --git a/tests/bluetooth/tester/src/btp_gap.c b/tests/bluetooth/tester/src/btp_gap.c index 65792e7da65..2191da79534 100644 --- a/tests/bluetooth/tester/src/btp_gap.c +++ b/tests/bluetooth/tester/src/btp_gap.c @@ -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(¤t_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(¤t_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(¤t_settings, *settings); + } + + if (atomic_test_bit(¤t_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(¤t_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(¤t_settings, BTP_GAP_SETTINGS_CONNECTABLE)) { + return -EINVAL; + } + break; +#endif + default: + return -EINVAL; + } + + if (IS_ENABLED(CONFIG_BT_EXT_ADV) && atomic_test_bit(¤t_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(¤t_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(¤t_settings, BTP_GAP_SETTINGS_PRIVACY)) { - return BTP_STATUS_FAILED; - } - break; - case 0x02: - /* NRPA is used only for non-connectable advertising */ - if (atomic_test_bit(¤t_settings, BTP_GAP_SETTINGS_CONNECTABLE)) { - return BTP_STATUS_FAILED; - } - break; -#endif - default: + err = tester_gap_create_adv_instance(¶m, own_addr_type, ad, adv_len, sd, sd_len, NULL); + if (err != 0) { return BTP_STATUS_FAILED; } - if (atomic_test_bit(¤t_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(¶m, 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(¤t_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(¶m, 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(¤t_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));