From 73a293fa5f8c5cfe0fe4b976c45bc17e3a2a4c2d Mon Sep 17 00:00:00 2001 From: Szymon Czapracki Date: Fri, 5 Apr 2024 14:48:45 +0200 Subject: [PATCH] apps/bttester: Add initial support for PACS autopts tests This commit adds support for Published Audio Capabilities Service in bttester application. --- apps/bttester/pkg.yml | 3 + apps/bttester/src/btp/btp.h | 2 + apps/bttester/src/btp/btp_pacs.h | 59 ++++ apps/bttester/src/btp/bttester.h | 7 + apps/bttester/src/btp_core.c | 10 + apps/bttester/src/btp_pacs.c | 331 ++++++++++++++++++++ apps/bttester/syscfg.yml | 1 + nimble/host/audio/include/audio/ble_audio.h | 2 +- 8 files changed, 414 insertions(+), 1 deletion(-) create mode 100644 apps/bttester/src/btp/btp_pacs.h create mode 100644 apps/bttester/src/btp_pacs.c diff --git a/apps/bttester/pkg.yml b/apps/bttester/pkg.yml index e763185fe5..0b82d4d435 100644 --- a/apps/bttester/pkg.yml +++ b/apps/bttester/pkg.yml @@ -32,10 +32,13 @@ pkg.deps: - "@apache-mynewt-core/sys/stats" - "@apache-mynewt-core/sys/shell" - "@apache-mynewt-nimble/nimble/host" + - "@apache-mynewt-nimble/nimble/host/audio" - "@apache-mynewt-nimble/nimble/host/util" - "@apache-mynewt-nimble/nimble/host/services/gap" - "@apache-mynewt-nimble/nimble/host/services/gatt" - "@apache-mynewt-nimble/nimble/host/services/dis" + - "@apache-mynewt-nimble/nimble/host/audio/services/pacs" + - "@apache-mynewt-nimble/nimble/host/audio/services/pacs/lc3" - "@apache-mynewt-nimble/nimble/host/store/config" - "@apache-mynewt-core/hw/drivers/uart" - "@apache-mynewt-core/hw/drivers/rtt" diff --git a/apps/bttester/src/btp/btp.h b/apps/bttester/src/btp/btp.h index 4403a16038..17fe388767 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_pacs.h" #include "btp_bap.h" #define BTP_MTU MYNEWT_VAL(BTTESTER_BTP_DATA_SIZE_MAX) @@ -47,6 +48,7 @@ #define BTP_SERVICE_ID_L2CAP 3 #define BTP_SERVICE_ID_MESH 4 #define BTP_SERVICE_ID_GATTC 6 +#define BTP_SERVICE_ID_PACS 12 #define BTP_SERVICE_ID_BAP 14 #define BTP_SERVICE_ID_MAX BTP_SERVICE_ID_BAP diff --git a/apps/bttester/src/btp/btp_pacs.h b/apps/bttester/src/btp/btp_pacs.h new file mode 100644 index 0000000000..afb553c2e5 --- /dev/null +++ b/apps/bttester/src/btp/btp_pacs.h @@ -0,0 +1,59 @@ +/* + * 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 BTP_PACS_H +#define BTP_PACS_H + +#include + +#ifndef __packed +#define __packed __attribute__((__packed__)) +#endif + +#define BTP_PACS_READ_SUPPORTED_COMMANDS 0x01 +struct btp_pacs_read_supported_commands_rp { + uint8_t data[0]; +} __packed; + +#define BTP_PACS_UPDATE_CHARACTERISTIC 0x02 +struct btp_pacs_update_characteristic_cmd { + uint8_t char_id; +} __packed; + +#define BTP_PACS_SET_LOCATION 0x03 + +#define BTP_PACS_SET_AVAILABLE_CONTEXTS 0x04 +struct btp_pacs_set_available_contexts_cmd { + uint16_t sink_contexts; + uint16_t source_contexts; +} __packed; + +#define BTP_PACS_SET_SUPPORTED_CONTEXTS 0x05 +struct btp_pacs_set_supported_contexts_cmd { + uint16_t sink_contexts; + uint16_t source_contexts; +} __packed; + +#define BTP_PACS_CHARACTERISTIC_SINK_PAC 0x01 +#define BTP_PACS_CHARACTERISTIC_SOURCE_PAC 0x02 +#define BTP_PACS_CHARACTERISTIC_SINK_AUDIO_LOCATIONS 0x03 +#define BTP_PACS_CHARACTERISTIC_SOURCE_AUDIO_LOCATIONS 0x04 +#define BTP_PACS_CHARACTERISTIC_AVAILABLE_AUDIO_CONTEXTS 0x05 + +#endif /* BTP_PACS_H*/ diff --git a/apps/bttester/src/btp/bttester.h b/apps/bttester/src/btp/bttester.h index 7cb82b5c92..413953d234 100644 --- a/apps/bttester/src/btp/bttester.h +++ b/apps/bttester/src/btp/bttester.h @@ -149,5 +149,12 @@ uint8_t tester_unregister_bap(void); #endif /* MYNEWT_VAL(BLE_ISO_BROADCAST_SOURCE) */ +#if MYNEWT_VAL(BLE_AUDIO) +uint8_t +tester_init_pacs(void); +uint8_t +tester_unregister_pacs(void); +#endif /* MYNEWT_VAL(BLE_AUDIO) */ + #endif /* __BTTESTER_H__ */ diff --git a/apps/bttester/src/btp_core.c b/apps/bttester/src/btp_core.c index a7b1878322..6d209df709 100644 --- a/apps/bttester/src/btp_core.c +++ b/apps/bttester/src/btp_core.c @@ -107,6 +107,11 @@ register_service(const void *cmd, uint16_t cmd_len, status = tester_init_bap(); break; #endif /* MYNEWT_VAL(BLE_ISO_BROADCAST_SOURCE) */ +#if MYNEWT_VAL(BLE_AUDIO) + case BTP_SERVICE_ID_PACS: + status = tester_init_pacs(); + break; +#endif /* MYNEWT(BLE_AUDIO) */ case BTP_SERVICE_ID_GATTC: status = tester_init_gatt_cl(); break; @@ -164,6 +169,11 @@ unregister_service(const void *cmd, uint16_t cmd_len, status = tester_unregister_bap(); break; #endif /* MYNEWT_VAL(BLE_ISO_BROADCAST_SOURCE) */ +#if MYNEWT_VAL(BLE_AUDIO) + case BTP_SERVICE_ID_PACS: + status = tester_unregister_pacs(); + break; +#endif /* MYNEWT_VAL (BLE_AUDIO) */ default: status = BTP_STATUS_FAILED; break; diff --git a/apps/bttester/src/btp_pacs.c b/apps/bttester/src/btp_pacs.c new file mode 100644 index 0000000000..f2de409146 --- /dev/null +++ b/apps/bttester/src/btp_pacs.c @@ -0,0 +1,331 @@ +/* + * 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_pacs.c - Bluetooth Published Audio Capacity Service Tester */ + +#include "audio/ble_audio.h" +#include "audio/ble_audio_codec.h" +#include "btp/bttester.h" +#include "host/ble_gap.h" +#include "os/util.h" +#include "syscfg/syscfg.h" +#include + +#if MYNEWT_VAL(BLE_AUDIO) + +#include "btp/btp.h" +#include "btp/btp_pacs.h" +#include "services/pacs/ble_audio_svc_pacs.h" +#include "services/pacs/ble_audio_svc_pacs_lc3.h" + +#define BLE_SVC_AUDIO_PACS_LC3_CODEC_ID 0x06 +#define BTTESTER_SUPPORTED_CTXTS 0x07 + +struct set_avail_cb_data { + uint16_t src_ctxts; + uint16_t snk_ctxts; +}; + +#ifdef MYNEWT_VAL_BLE_SVC_AUDIO_PACS_LC3_SNK_METADATA +static uint8_t ble_svc_audio_pacs_lc3_snk_metadata[] = +{ UNMANGLE_MYNEWT_VAL(MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SNK_METADATA)) }; +#endif + +static uint8_t ble_svc_audio_pacs_lc3_snk_codec_spec_caps[] = BLE_AUDIO_BUILD_CODEC_CAPS( + MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SNK_SAMPLING_FREQUENCIES), + MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SNK_FRAME_DURATIONS), + #ifdef MYNEWT_VAL_BLE_SVC_AUDIO_PACS_LC3_SRC_AUDIO_CHANNEL_COUNTS + MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SNK_AUDIO_CHANNEL_COUNTS), + #else + , + #endif + MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SNK_MIN_OCTETS_PER_CODEC_FRAME), + MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SRC_MAX_OCTETS_PER_CODEC_FRAME), + #ifdef MYNEWT_VAL_BLE_SVC_AUDIO_PACS_LC3_SRC_MAX_CODEC_FRAMES_PER_SDU + MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SNK_MAX_CODEC_FRAMES_PER_SDU), + #endif +); + +static uint8_t ble_svc_audio_pacs_lc3_src_codec_spec_caps[] = BLE_AUDIO_BUILD_CODEC_CAPS( + MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SRC_SAMPLING_FREQUENCIES), + MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SRC_FRAME_DURATIONS), +#ifdef MYNEWT_VAL_BLE_SVC_AUDIO_PACS_LC3_SRC_AUDIO_CHANNEL_COUNTS + MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SRC_AUDIO_CHANNEL_COUNTS), +#else + , +#endif + MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SRC_MIN_OCTETS_PER_CODEC_FRAME), + MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SRC_MAX_OCTETS_PER_CODEC_FRAME), +#ifdef MYNEWT_VAL_BLE_SVC_AUDIO_PACS_LC3_SRC_MAX_CODEC_FRAMES_PER_SDU + MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SRC_MAX_CODEC_FRAMES_PER_SDU), +#endif +); + +static struct ble_audio_codec_register_params snk_codec_params = { + .codec_id = { + .format = BLE_SVC_AUDIO_PACS_LC3_CODEC_ID, + .company_id = 0x00, + .vendor_specific = 0x00 + }, + .codec_spec_caps_len = sizeof(ble_svc_audio_pacs_lc3_snk_codec_spec_caps), + .codec_spec_caps = ble_svc_audio_pacs_lc3_snk_codec_spec_caps, +#ifdef MYNEWT_VAL_BLE_SVC_AUDIO_PACS_LC3_SNK_METADATA + .metadata_len = sizeof(ble_svc_audio_pacs_lc3_snk_metadata), + .metadata = ble_svc_audio_pacs_lc3_snk_metadata, +#else + .metadata_len = 0, +#endif + + .direction = BLE_AUDIO_CODEC_DIR_SINK_BIT +}; + +static struct ble_audio_codec_register_params src_codec_params = { + .codec_id = { + .format = BLE_SVC_AUDIO_PACS_LC3_CODEC_ID, + .company_id = 0x00, + .vendor_specific = 0x00 + }, + .codec_spec_caps_len = sizeof(ble_svc_audio_pacs_lc3_src_codec_spec_caps), + .codec_spec_caps = ble_svc_audio_pacs_lc3_src_codec_spec_caps, +#ifdef MYNEWT_VAL_BLE_SVC_AUDIO_PACS_LC3_SRC_METADATA + .metadata_len = sizeof(ble_svc_audio_pacs_lc3_src_metadata), + .metadata = ble_svc_audio_pacs_lc3_src_metadata, +#else + .metadata_len = 0, +#endif + .direction = BLE_AUDIO_CODEC_DIR_SOURCE_BIT +}; + +int +set_available(uint16_t conn_handle, void *arg) +{ + int rc; + struct set_avail_cb_data *avail_data = arg; + + rc = ble_svc_audio_pacs_avail_contexts_set(conn_handle, + avail_data->snk_ctxts, + avail_data->src_ctxts); + if (rc) { + return BTP_STATUS_FAILED; + } + + return BTP_STATUS_SUCCESS; +} + +static uint8_t +pacs_set_available_contexts(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + struct set_avail_cb_data cb_data; + const struct btp_pacs_set_available_contexts_cmd *avail = cmd; + + /* If this originated from pacs_update_characteristic - we update with unspecified */ + if (le16toh(avail->sink_contexts) == BTP_PACS_CHARACTERISTIC_AVAILABLE_AUDIO_CONTEXTS) { + cb_data.snk_ctxts = BLE_AUDIO_CONTEXT_TYPE_UNSPECIFIED; + cb_data.src_ctxts = BLE_AUDIO_CONTEXT_TYPE_UNSPECIFIED; + } else { + cb_data.snk_ctxts = avail->sink_contexts; + cb_data.src_ctxts = avail->source_contexts; + } + + ble_gap_conn_foreach_handle(set_available, &cb_data); + + return BTP_STATUS_SUCCESS; +} + +static uint8_t +pacs_set_snk_location(void) +{ + int rc; + struct ble_svc_audio_pacs_set_param snk_params = { + .audio_locations = 0, + .supported_contexts = MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SNK_SUP_CONTEXTS) + }; + + rc = ble_svc_audio_pacs_set(BLE_AUDIO_CODEC_DIR_SINK_BIT, &snk_params); + if (rc) { + return BTP_STATUS_FAILED; + } + + return BTP_STATUS_SUCCESS; +} + +static uint8_t +pacs_set_src_location(void) +{ + int rc; + struct ble_svc_audio_pacs_set_param src_params = { + .audio_locations = 0, + .supported_contexts = MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SRC_SUP_CONTEXTS) + }; + + rc = ble_svc_audio_pacs_set(BLE_AUDIO_CODEC_DIR_SOURCE_BIT, &src_params); + if (rc) { + return BTP_STATUS_FAILED; + } + + return BTP_STATUS_SUCCESS; +} + +static uint8_t +pacs_update_characteristic(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + int rc; + const struct btp_pacs_update_characteristic_cmd *char_cmd = cmd; + + switch (char_cmd->char_id) { + case BTP_PACS_CHARACTERISTIC_SINK_PAC: + rc = ble_audio_codec_register(&snk_codec_params, NULL); + if (rc) { + return BTP_STATUS_FAILED; + } + break; + case BTP_PACS_CHARACTERISTIC_SOURCE_PAC: + rc = ble_audio_codec_register(&src_codec_params, NULL); + if (rc) { + return BTP_STATUS_FAILED; + } + break; + case BTP_PACS_CHARACTERISTIC_SINK_AUDIO_LOCATIONS: + rc = pacs_set_snk_location(); + if (rc) { + return BTP_STATUS_FAILED; + } + break; + case BTP_PACS_CHARACTERISTIC_SOURCE_AUDIO_LOCATIONS: + rc = pacs_set_src_location(); + if (rc) { + return BTP_STATUS_FAILED; + } + break; + case BTP_PACS_CHARACTERISTIC_AVAILABLE_AUDIO_CONTEXTS: + rc = pacs_set_available_contexts(cmd, cmd_len, rsp, rsp_len); + if (rc) { + return BTP_STATUS_FAILED; + } + break; + default: + return BTP_STATUS_FAILED; + } + + return BTP_STATUS_SUCCESS; +} + +static uint8_t +pacs_set_supported_contexts(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + int rc; + const struct btp_pacs_set_supported_contexts_cmd *sup_ctxts = cmd; + + struct ble_svc_audio_pacs_set_param src_params = { + .audio_locations = 0, + .supported_contexts = sup_ctxts->source_contexts, + }; + struct ble_svc_audio_pacs_set_param snk_params = { + .audio_locations = 0, + .supported_contexts = sup_ctxts->sink_contexts, + }; + + rc = ble_svc_audio_pacs_set(BLE_AUDIO_CODEC_DIR_SOURCE_BIT, &src_params); + if (rc) { + return BTP_STATUS_FAILED; + } + + rc = ble_svc_audio_pacs_set(BLE_AUDIO_CODEC_DIR_SINK_BIT, &snk_params); + if (rc) { + return BTP_STATUS_FAILED; + } + + return BTP_STATUS_SUCCESS; +} + +static uint8_t +supported_commands(const void *cmd, uint16_t cmd_len, + void *rsp, uint16_t *rsp_len) +{ + struct btp_pacs_read_supported_commands_rp *rp = rsp; + + /* octet 0 */ + tester_set_bit(rp->data, BTP_PACS_READ_SUPPORTED_COMMANDS); + tester_set_bit(rp->data, BTP_PACS_UPDATE_CHARACTERISTIC); + tester_set_bit(rp->data, BTP_PACS_SET_AVAILABLE_CONTEXTS); + tester_set_bit(rp->data, BTP_PACS_SET_SUPPORTED_CONTEXTS); + + *rsp_len = sizeof(*rp) + 1; + + return BTP_STATUS_SUCCESS; +} + +static const struct btp_handler handlers[] = { + { + .opcode = BTP_PACS_READ_SUPPORTED_COMMANDS, + .expect_len = 0, + .func = supported_commands, + }, + { + .opcode = BTP_PACS_UPDATE_CHARACTERISTIC, + .expect_len = sizeof(struct btp_pacs_update_characteristic_cmd), + .func = pacs_update_characteristic, + }, + { + .opcode = BTP_PACS_SET_AVAILABLE_CONTEXTS, + .expect_len = sizeof(struct btp_pacs_set_available_contexts_cmd), + .func = pacs_set_available_contexts, + }, + { + .opcode = BTP_PACS_SET_SUPPORTED_CONTEXTS, + .expect_len = sizeof(struct btp_pacs_set_supported_contexts_cmd), + .func = pacs_set_supported_contexts, + }, +}; + +uint8_t +tester_init_pacs(void) +{ + int rc; + struct ble_svc_audio_pacs_set_param src_params = { + .audio_locations = MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SRC_AUDIO_LOCATIONS), + .supported_contexts = BTTESTER_SUPPORTED_CTXTS + }; + struct ble_svc_audio_pacs_set_param snk_params = { + .audio_locations = MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SRC_AUDIO_LOCATIONS), + .supported_contexts = BTTESTER_SUPPORTED_CTXTS + }; + + rc = ble_svc_audio_pacs_set(BLE_AUDIO_CODEC_DIR_SOURCE_BIT, &src_params); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = ble_svc_audio_pacs_set(BLE_AUDIO_CODEC_DIR_SINK_BIT, &snk_params); + SYSINIT_PANIC_ASSERT(rc == 0); + + tester_register_command_handlers(BTP_SERVICE_ID_PACS, handlers, + ARRAY_SIZE(handlers)); + + return BTP_STATUS_SUCCESS; +} + +uint8_t +tester_unregister_pacs(void) +{ + return BTP_STATUS_SUCCESS; +} + +#endif /* MYNEWT_VAL(BLE_AUDIO)*/ diff --git a/apps/bttester/syscfg.yml b/apps/bttester/syscfg.yml index 80c377a7c1..d059849322 100644 --- a/apps/bttester/syscfg.yml +++ b/apps/bttester/syscfg.yml @@ -87,6 +87,7 @@ syscfg.defs: value: '"test_broadcast"' syscfg.vals: + BLE_AUDIO_MAX_CODEC_RECORDS: 3 CONSOLE_IMPLEMENTATION: full LOG_IMPLEMENTATION: full STATS_IMPLEMENTATION: full diff --git a/nimble/host/audio/include/audio/ble_audio.h b/nimble/host/audio/include/audio/ble_audio.h index 6fb5f295ed..3853655a19 100644 --- a/nimble/host/audio/include/audio/ble_audio.h +++ b/nimble/host/audio/include/audio/ble_audio.h @@ -308,7 +308,7 @@ /** LE Audio Codec Context Type: Unspecified. */ #define BLE_AUDIO_CONTEXT_TYPE_UNSPECIFIED 0x0001 -/** LE Audio Codec Context Type: Conversational. */ +/** LE Audio Codec Context Type: Conversettional. */ #define BLE_AUDIO_CONTEXT_TYPE_CONVERSATIONAL 0x0002 /** LE Audio Codec Context Type: Media. */