diff --git a/apps/bttester/src/btp/btp.h b/apps/bttester/src/btp/btp.h index 5e1a172e9f..4403a16038 100644 --- a/apps/bttester/src/btp/btp.h +++ b/apps/bttester/src/btp/btp.h @@ -33,6 +33,7 @@ #include "btp_gattc.h" #include "btp_l2cap.h" #include "btp_mesh.h" +#include "btp_bap.h" #define BTP_MTU MYNEWT_VAL(BTTESTER_BTP_DATA_SIZE_MAX) #define BTP_DATA_MAX_SIZE (BTP_MTU - sizeof(struct btp_hdr)) @@ -46,8 +47,9 @@ #define BTP_SERVICE_ID_L2CAP 3 #define BTP_SERVICE_ID_MESH 4 #define BTP_SERVICE_ID_GATTC 6 +#define BTP_SERVICE_ID_BAP 14 -#define BTP_SERVICE_ID_MAX BTP_SERVICE_ID_GATTC +#define BTP_SERVICE_ID_MAX BTP_SERVICE_ID_BAP #define BTP_STATUS_SUCCESS 0x00 #define BTP_STATUS_FAILED 0x01 diff --git a/apps/bttester/src/btp/btp_bap.h b/apps/bttester/src/btp/btp_bap.h new file mode 100644 index 0000000000..f25ab14748 --- /dev/null +++ b/apps/bttester/src/btp/btp_bap.h @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_BTP_BAP_ +#define H_BTP_BAP_ + +#include "nimble/ble.h" +#include + +#ifndef __packed +#define __packed __attribute__((__packed__)) +#endif + +/* BAP Service */ +/* commands */ +#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_SEND 0x03 +#define BTP_BAP_BROADCAST_SOURCE_SETUP 0x04 +struct 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 rtn; + uint16_t max_transport_lat; + uint8_t presentation_delay[3]; + uint8_t coding_fmt; + uint16_t vid; + uint16_t cid; + uint8_t cc_ltvs_len; + uint8_t cc_ltvs[]; +} __packed; + +struct bap_broadcast_source_setup_rp { + uint32_t gap_settings; + uint8_t broadcast_id[3]; +} __packed; + +#define BTP_BAP_BROADCAST_SOURCE_RELEASE 0x05 +struct bap_bap_broadcast_source_release_cmd { + uint8_t broadcast_id[3]; +} __packed; + +#define BTP_BAP_BROADCAST_ADV_START 0x06 +struct bap_bap_broadcast_adv_start_cmd { + uint8_t broadcast_id[3]; +} __packed; + +#define BTP_BAP_BROADCAST_ADV_STOP 0x07 +struct bap_bap_broadcast_adv_stop_cmd { + uint8_t broadcast_id[3]; +} __packed; + +#define BTP_BAP_BROADCAST_SOURCE_START 0x08 +struct bap_bap_broadcast_source_start_cmd { + uint8_t broadcast_id[3]; +} __packed; + +#define BTP_BAP_BROADCAST_SOURCE_STOP 0x09 +struct bap_bap_broadcast_source_stop_cmd { + uint8_t broadcast_id[3]; +} __packed; + +#define BTP_BAP_BROADCAST_SINK_SETUP 0x0a +#define BTP_BAP_BROADCAST_SINK_RELEASE 0x0b +#define BTP_BAP_BROADCAST_SCAN_START 0x0c +#define BTP_BAP_BROADCAST_SCAN_STOP 0x0d +#define BTP_BAP_BROADCAST_SINK_SYNC 0x0e +#define BTP_BAP_BROADCAST_SINK_STOP 0x0f +#define BTP_BAP_BROADCAST_SINK_BIS_SYNC 0x10 +#define BTP_BAP_DISCOVER_SCAN_DELEGATOR 0x11 +#define BTP_BAP_BROADCAST_ASSISTANT_SCAN_START 0x12 +#define BTP_BAP_BROADCAST_ASSISTANT_SCAN_STOP 0x13 +#define BTP_BAP_ADD_BROADCAST_SRC 0x14 +#define BTP_BAP_REMOVE_BROADCAST_SRC 0x15 +#define BTP_BAP_MODIFY_BROADCAST_SRC 0x16 +#define BTP_BAP_SET_BROADCAST_CODE 0x17 +#define BTP_BAP_SEND_PAST 0x18 + +#define BTP_BAP_EV_DISCOVERY_COMPLETED 0x80 +#define BTP_BAP_EV_CODEC_CAP_FOUND 0x81 +#define BTP_BAP_EV_ASE_FOUND 0x82 +#define BTP_BAP_EV_STREAM_RECEIVED 0x83 +#define BTP_BAP_EV_BAA_FOUND 0x84 +#define BTP_BAP_EV_BIS_FOUND 0x85 +#define BTP_BAP_EV_BIS_SYNCED 0x86 +#define BTP_BAP_EV_BIS_STREAM_RECEIVED 0x87 +#define BTP_BAP_EV_SCAN_DELEGATOR_FOUND 0x88 +#define BTP_BAP_EV_BROADCAST_RECEIVE_STATE 0x89 +#define BTP_BAP_EV_PA_SYNC_REQ 0x8a + +#endif /* H_BTP_BAP_ */ diff --git a/apps/bttester/src/btp/bttester.h b/apps/bttester/src/btp/bttester.h index 8e8b98b5ad..0b90b792ba 100644 --- a/apps/bttester/src/btp/bttester.h +++ b/apps/bttester/src/btp/bttester.h @@ -141,4 +141,12 @@ gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg); int gatt_svr_init(void); -#endif /* __BTTESTER_H__ */ \ No newline at end of file +#endif /* __BTTESTER_H__ */ + +#if MYNEWT_VAL(BLE_ISO_BROADCASTER) +uint8_t +tester_init_bap(void); +uint8_t +tester_unregister_bap(void); +#endif /* MYNEWT_VAL(BLE_ISO_BROADCASTER) +#endif /* MYNEWT_VAL() diff --git a/apps/bttester/src/btp_bap.c b/apps/bttester/src/btp_bap.c new file mode 100644 index 0000000000..39800fa82a --- /dev/null +++ b/apps/bttester/src/btp_bap.c @@ -0,0 +1,420 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* btp_bap.c - Bluetooth Basic Audio Profile Tester */ + + +#if MYNEWT_VAL(BLE_ISO_BROADCASTER) + +#include "btp/btp_bap.h" +#include "syscfg/syscfg.h" + + +#include "btp/btp.h" +#include "console/console.h" + +#include "nimble/ble.h" +#include "host/ble_hs.h" +#include "host/util/util.h" +#include "math.h" + +#include "audio/ble_audio_broadcast_source.h" +#include "audio/ble_audio.h" +#include "host/ble_iso.h" + +#include "bsp/bsp.h" + + +#define BROADCAST_ADV_INSTANCE 1 + +static struct ble_audio_big_subgroup big_subgroup; + +static uint8_t id_addr_type; +static uint8_t audio_data[155]; +static uint16_t max_sdu; +static uint32_t sdu_interval; + +static struct ble_audio_base tester_base; + +static os_membuf_t bis_mem[ + OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_ISO_MAX_BISES), + sizeof(struct ble_audio_bis)) +]; +static struct os_mempool bis_pool; + +static os_membuf_t codec_spec_mem[ + OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_ISO_MAX_BISES) * 2, 19) +]; +static struct os_mempool codec_spec_pool; + +static uint16_t bis_handles[MYNEWT_VAL(BLE_ISO_MAX_BISES)]; +/* The timer callout */ +static struct os_callout audio_broadcast_callout; + +struct ble_iso_big_params big_params; + +static int audio_data_offset; + +static void +audio_broadcast_event_cb(struct os_event *ev) +{ + assert(ev != NULL); + uint32_t ev_start_time = os_cputime_ticks_to_usecs(os_cputime_get32()); + + if (audio_data_offset + 2 * max_sdu >= sizeof(audio_data)) { + audio_data_offset = 0; + } + + uint8_t lr_payload[max_sdu * 2]; + memcpy(lr_payload, audio_data + audio_data_offset, max_sdu); + memcpy(lr_payload + max_sdu, audio_data + audio_data_offset, + max_sdu); + ble_iso_tx(bis_handles[0], (void *)(lr_payload), + max_sdu * 2); + + audio_data_offset += max_sdu; + + /** Use cputime to time BROADCAST_SDU_INTVL, as these ticks are more + * accurate than os_time ones. This assures that we do not push + * LC3 data to ISO before interval, which could lead to + * controller running out of buffers. This is only needed because + * we already have coded data in an array - in real world application + * we usually wait for new audio to arrive, and lose time to code it too. + */ + while (os_cputime_ticks_to_usecs(os_cputime_get32()) - ev_start_time < + (sdu_interval)); + + os_callout_reset(&audio_broadcast_callout, 0); +} + +static int +iso_event(struct ble_iso_event *event, void *arg) +{ + int i; + + switch (event->type) { + case BLE_ISO_EVENT_BIG_CREATE_COMPLETE: + console_printf("BIG created\n"); + if (event->big_created.desc.num_bis > + MYNEWT_VAL(BROADCASTER_CHAN_NUM)) { + return BLE_HS_EINVAL; + } + for (i = 0; i < MYNEWT_VAL(BROADCASTER_CHAN_NUM); i++) { + bis_handles[i] = event->big_created.desc.conn_handle[i]; + } + return 0; + case BLE_ISO_EVENT_BIG_TERMINATE_COMPLETE: + console_printf("BIG terminated\n"); + return 0; + default: + return BLE_HS_ENOTSUP; + } +} + +static uint8_t +supported_commands(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + struct btp_bap_read_supported_commands_rp *rp = rsp; + + /* octet 0 */ + tester_set_bit(rp->data, BTP_BAP_READ_SUPPORTED_COMMANDS); + tester_set_bit(rp->data, BTP_BAP_BROADCAST_SOURCE_SETUP); + tester_set_bit(rp->data, BTP_BAP_BROADCAST_SOURCE_RELEASE); + tester_set_bit(rp->data, BTP_BAP_BROADCAST_ADV_START); + tester_set_bit(rp->data, BTP_BAP_BROADCAST_ADV_STOP); + + /* octet 1 */ + tester_set_bit(rp->data, BTP_BAP_BROADCAST_SOURCE_START); + tester_set_bit(rp->data, BTP_BAP_BROADCAST_SOURCE_STOP); + + *rsp_len = sizeof(*rp) + 2; + + return BTP_STATUS_SUCCESS; +} + +static int +base_create(const struct bap_broadcast_source_setup_cmd *cmd) +{ + struct ble_audio_bis *bis; + uint8_t sampling_freq = cmd->cc_ltvs[2]; + uint8_t frame_duration = cmd->cc_ltvs[5]; + uint16_t chan_loc = BLE_AUDIO_LOCATION_FRONT_LEFT | + BLE_AUDIO_LOCATION_FRONT_RIGHT; + + + uint8_t codec_spec_config[] = + BLE_AUDIO_BUILD_CODEC_CONFIG(sampling_freq, frame_duration, chan_loc, + max_sdu, ); + + tester_base.broadcast_id = sampling_freq; + tester_base.presentation_delay = sampling_freq * 10000; + + big_subgroup.bis_cnt = MYNEWT_VAL(BROADCASTER_CHAN_NUM); + + /** LC3 */ + big_subgroup.codec_id.format = 0x06; + + big_subgroup.codec_spec_config_len = 0; + + bis = os_memblock_get(&bis_pool); + if (!bis) { + return BLE_HS_ENOMEM; + } + + bis->codec_spec_config = os_memblock_get(&codec_spec_pool); + memcpy(bis->codec_spec_config, + codec_spec_config, + sizeof(codec_spec_config)); + bis->codec_spec_config_len = sizeof(codec_spec_config); + STAILQ_INSERT_HEAD(&big_subgroup.bises, bis, next); + + STAILQ_INSERT_HEAD(&tester_base.subs, &big_subgroup, next); + tester_base.num_subgroups++; + return 0; +} + +static int +broadcast_destroy_fn(struct ble_audio_base *base, void *args) +{ + struct ble_audio_bis *bis; + + STAILQ_FOREACH(bis, &big_subgroup.bises, next) { + os_memblock_put(&codec_spec_pool, bis->codec_spec_config); + os_memblock_put(&bis_pool, bis); + } + + memset(&big_subgroup, 0, sizeof(big_subgroup)); + + return 0; +} + +static uint8_t +broadcast_source_setup(const void *cmd, uint16_t cmd_len, void *rsp, + uint16_t *rsp_len) +{ + int rc; + + const struct bap_broadcast_source_setup_cmd *source_config = cmd; + max_sdu = source_config->max_sdu; + sdu_interval = get_le24(source_config->sdu_interval); + + base_create(source_config); + + struct ble_gap_periodic_adv_params periodic_params = { + .itvl_min = 30, + .itvl_max = 30, + }; + + struct ble_gap_ext_adv_params extended_params = { + .itvl_min = 50, + .itvl_max = 50, + .scannable = 0, + .connectable = 0, + .primary_phy = BLE_HCI_LE_PHY_1M, + .secondary_phy = BLE_HCI_LE_PHY_2M, + .own_addr_type = id_addr_type, + .sid = BROADCAST_ADV_INSTANCE, + }; + + big_params.sdu_interval = sdu_interval; + big_params.max_sdu = max_sdu; + big_params.max_transport_latency = 8; + big_params.rtn = source_config->rtn; + big_params.phy = BLE_HCI_LE_PHY_2M; + big_params.packing = 0; + big_params.framing = source_config->framing; + big_params.encryption = 0; + + struct ble_broadcast_create_params create_params = { + .base = &tester_base, + .extended_params = &extended_params, + .periodic_params = &periodic_params, + .name = MYNEWT_VAL(BROADCASTER_BROADCAST_NAME), + .adv_instance = BROADCAST_ADV_INSTANCE, + .big_params = &big_params, + .svc_data = NULL, + .svc_data_len = 0, + }; + + rc = ble_audio_broadcast_create(&create_params, + broadcast_destroy_fn, + NULL, + NULL); + if (rc) { + return BTP_STATUS_FAILED; + } + + return BTP_STATUS_SUCCESS; +} + +static uint8_t +broadcast_source_release(const void *cmd, uint16_t cmd_len, void *rsp, + uint16_t *rsp_len) +{ + int rc; + + rc = ble_audio_broadcast_destroy(BROADCAST_ADV_INSTANCE); + if (rc) { + return BTP_STATUS_FAILED; + } + + return BTP_STATUS_SUCCESS; +} + +static uint8_t +broadcast_adv_start(const void *cmd, uint16_t cmd_len, void *rsp, + uint16_t *rsp_len) +{ + int rc; + + rc = ble_audio_broadcast_start(BROADCAST_ADV_INSTANCE, iso_event, NULL); + if (rc) { + 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 rc; + + rc = ble_audio_broadcast_stop(BROADCAST_ADV_INSTANCE); + if (rc) { + 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 rc; + + rc = os_callout_reset(&audio_broadcast_callout, 0); + if (rc) { + 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) +{ + os_callout_stop(&audio_broadcast_callout); + + return BTP_STATUS_SUCCESS; +} + +static const struct btp_handler handlers[] = { + { + .opcode = BTP_BAP_READ_SUPPORTED_COMMANDS, + .index = BTP_INDEX_NONE, + .expect_len = 0, + .func = supported_commands, + }, + { + .opcode = BTP_BAP_BROADCAST_SOURCE_SETUP, + .index = BTP_INDEX, + .expect_len = BTP_HANDLER_LENGTH_VARIABLE, + .func = broadcast_source_setup, + }, + { + .opcode = BTP_BAP_BROADCAST_SOURCE_RELEASE, + .index = BTP_INDEX, + .expect_len = BTP_HANDLER_LENGTH_VARIABLE, + .func = broadcast_source_release, + }, + { + .opcode = BTP_BAP_BROADCAST_ADV_START, + .index = BTP_INDEX, + .expect_len = sizeof(struct bap_bap_broadcast_adv_start_cmd), + .func = broadcast_adv_start, + }, + { + .opcode = BTP_BAP_BROADCAST_ADV_STOP, + .index = BTP_INDEX, + .expect_len = sizeof(struct bap_bap_broadcast_adv_stop_cmd), + .func = broadcast_adv_stop, + }, + { + .opcode = BTP_BAP_BROADCAST_SOURCE_START, + .index = BTP_INDEX, + .expect_len = sizeof(struct bap_bap_broadcast_source_start_cmd), + .func = broadcast_source_start, + }, + { + .opcode = BTP_BAP_BROADCAST_SOURCE_STOP, + .index = BTP_INDEX, + .expect_len = sizeof(struct bap_bap_broadcast_source_stop_cmd), + .func = broadcast_source_stop, + }, +}; + +uint8_t +tester_init_bap(void) +{ + int rc; + + /* Make sure we have proper identity address set (public preferred) */ + rc = ble_hs_util_ensure_addr(0); + assert(rc == 0); + + /* configure global address */ + rc = ble_hs_id_infer_auto(0, &id_addr_type); + assert(rc == 0); + + memset(audio_data, 36, sizeof(char)*155); + + os_callout_init(&audio_broadcast_callout, os_eventq_dflt_get(), + audio_broadcast_event_cb, NULL); + + rc = os_mempool_init(&bis_pool, MYNEWT_VAL(BLE_ISO_MAX_BISES), + sizeof(struct ble_audio_bis), bis_mem, + "bis_pool"); + if (rc) { + return BTP_STATUS_FAILED; + } + + rc = os_mempool_init(&codec_spec_pool, + MYNEWT_VAL(BLE_ISO_MAX_BISES) * 2, 19, + codec_spec_mem, "codec_spec_pool"); + if (rc) { + return BTP_STATUS_FAILED; + } + + tester_register_command_handlers(BTP_SERVICE_ID_BAP, handlers, + ARRAY_SIZE(handlers)); + + return BTP_STATUS_SUCCESS; +} + +uint8_t +tester_unregister_bap(void) +{ + return BTP_STATUS_SUCCESS; +} + diff --git a/apps/bttester/src/btp_core.c b/apps/bttester/src/btp_core.c index 5ab290d937..84165531f0 100644 --- a/apps/bttester/src/btp_core.c +++ b/apps/bttester/src/btp_core.c @@ -102,6 +102,9 @@ register_service(const void *cmd, uint16_t cmd_len, status = tester_init_mesh(); break; #endif /* MYNEWT_VAL(BLE_MESH) */ + case BTP_SERVICE_ID_BAP: + status = tester_init_bap(); + break; case BTP_SERVICE_ID_GATTC: status = tester_init_gatt_cl(); break; @@ -154,6 +157,9 @@ unregister_service(const void *cmd, uint16_t cmd_len, case BTP_SERVICE_ID_GATTC: status = tester_unregister_gatt_cl(); break; + case BTP_SERVICE_ID_BAP: + status = tester_unregister_bap(); + break; default: status = BTP_STATUS_FAILED; break; diff --git a/apps/bttester/syscfg.yml b/apps/bttester/syscfg.yml index 68a60f6a24..5876e4cf6d 100644 --- a/apps/bttester/syscfg.yml +++ b/apps/bttester/syscfg.yml @@ -79,11 +79,27 @@ syscfg.defs: description: NRPA rotation timeout in seconds value: 5 + BROADCASTER_CHAN_NUM: + description: Number of channels used for broadcast + value: 1 + BROADCASTER_BROADCAST_NAME: + description: Broadcast name + value: '"test_broadcast"' + syscfg.vals: CONSOLE_IMPLEMENTATION: full LOG_IMPLEMENTATION: full STATS_IMPLEMENTATION: full + BLE_ISO: 1 + BLE_AUDIO: 1 + BLE_ROLE_BROADCASTER: 1 + BLE_ISO_MAX_BISES: 1 + BLE_ISO_MAX_BIGS: 1 + BLE_EXT_ADV: 1 + BLE_PERIODIC_ADV: 1 + BLE_ISO_BROADCAST_SOURCE: 1 + OS_MAIN_STACK_SIZE: 512 SHELL_TASK: 0 SHELL_NEWTMGR: 0 @@ -101,7 +117,7 @@ syscfg.vals: BLE_L2CAP_SIG_MAX_PROCS: 2 BLE_L2CAP_ENHANCED_COC: 1 BLE_EATT_CHAN_NUM: 5 - BLE_VERSION: 53 + BLE_VERSION: 54 # Some testcases require MPS < MTU BLE_L2CAP_COC_MPS: 100 BLE_RPA_TIMEOUT: 30 @@ -143,7 +159,5 @@ syscfg.vals: BLE_MESH_TX_SEG_MSG_COUNT: 2 BLE_MAX_CONNECTIONS: 8 - BLE_MESH_ADV_BUF_COUNT: 20 - BLE_MESH_TX_SEG_MAX: 6 - BLE_EATT_CHAN_NUM: 2 +