From e344999b63df65daf9137e8f4b13e302e586b16d Mon Sep 17 00:00:00 2001 From: Mariusz Skamra Date: Thu, 25 Jan 2024 13:28:54 +0100 Subject: [PATCH] apps/btshell: Add Audio Broadcast Sink to shell application this extends the btshell application with Audio Broadcast Sink functionality. --- apps/btshell/src/btshell.h | 2 + apps/btshell/src/cmd.c | 86 +- apps/btshell/src/cmd.h | 7 + apps/btshell/src/cmd_leaudio.c | 757 +++++++++++++++++- apps/btshell/src/cmd_leaudio.h | 20 + apps/btshell/src/main.c | 6 + apps/btshell/syscfg.yml | 3 + .../host/audio/targets/btshell_native/pkg.yml | 1 + .../audio/targets/btshell_native/syscfg.yml | 3 + 9 files changed, 881 insertions(+), 4 deletions(-) diff --git a/apps/btshell/src/btshell.h b/apps/btshell/src/btshell.h index c2a2eb3e29..3d94c21204 100644 --- a/apps/btshell/src/btshell.h +++ b/apps/btshell/src/btshell.h @@ -222,6 +222,8 @@ int btshell_broadcast_start(uint8_t adv_instance); int btshell_broadcast_stop(uint8_t adv_instance); #endif +void btshell_leaudio_init(void); + int btshell_gap_event(struct ble_gap_event *event, void *arg); void btshell_sync_stats(uint16_t handle); uint8_t btshell_get_default_own_addr_type(void); diff --git a/apps/btshell/src/cmd.c b/apps/btshell/src/cmd.c index 50aac79400..a9758e14b2 100644 --- a/apps/btshell/src/cmd.c +++ b/apps/btshell/src/cmd.c @@ -87,14 +87,28 @@ static const struct parse_arg_kv_pair cmd_peer_addr_types[] = { { NULL } }; -static const struct parse_arg_kv_pair cmd_addr_type[] = { +const struct parse_arg_kv_pair cmd_addr_type[] = { { "public", BLE_ADDR_PUBLIC }, { "random", BLE_ADDR_RANDOM }, { NULL } }; +const char * +cmd_addr_type_str(uint8_t type) +{ + const struct parse_arg_kv_pair *kvs = cmd_addr_type; + int i; -static int + for (i = 0; kvs[i].key != NULL; i++) { + if (type == kvs[i].val) { + return kvs[i].key; + } + } + + return "unknown"; +} + +int parse_dev_addr(const char *prefix, const struct parse_arg_kv_pair *addr_types, ble_addr_t *addr) { @@ -152,6 +166,12 @@ parse_dev_addr(const char *prefix, const struct parse_arg_kv_pair *addr_types, return 0; } +int +cmd_parse_addr(const char *prefix, ble_addr_t *addr) +{ + return parse_dev_addr(prefix, cmd_addr_type, addr); +} + /***************************************************************************** * $advertise * *****************************************************************************/ @@ -4924,7 +4944,67 @@ static const struct shell_cmd btshell_commands[] = { .help = &leaudio_broadcast_stop_help, #endif }, -#endif /* BLE_ISO_BROADCAST_SOURCE */ +#endif +#if MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK) + { + .sc_cmd = "broadcast-sink-start", + .sc_cmd_func = cmd_leaudio_broadcast_sink_start, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &cmd_leaudio_broadcast_sink_start_help, +#endif + }, + { + .sc_cmd = "broadcast-sink-stop", + .sc_cmd_func = cmd_leaudio_broadcast_sink_stop, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &cmd_leaudio_broadcast_sink_stop_help, +#endif + }, + { + .sc_cmd = "broadcast-sink-metadata", + .sc_cmd_func = cmd_leaudio_broadcast_sink_metadata_update, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &cmd_leaudio_broadcast_sink_metadata_update_help, +#endif + }, +#endif /* BLE_AUDIO_BROADCAST_SINK */ +#if MYNEWT_VAL(BLE_AUDIO_SCAN_DELEGATOR) + { + .sc_cmd = "scan-delegator-add", + .sc_cmd_func = cmd_leaudio_scan_delegator_receive_state_add, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &cmd_leaudio_scan_delegator_receive_state_add_help, +#endif + }, + { + .sc_cmd = "scan-delegator-remove", + .sc_cmd_func = cmd_leaudio_scan_delegator_receive_state_remove, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &cmd_leaudio_scan_delegator_receive_state_remove_help, +#endif + }, + { + .sc_cmd = "scan-delegator-set", + .sc_cmd_func = cmd_leaudio_scan_delegator_receive_state_set, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &cmd_leaudio_scan_delegator_receive_state_set_help, +#endif + }, + { + .sc_cmd = "scan-delegator-get", + .sc_cmd_func = cmd_leaudio_scan_delegator_receive_state_get, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &cmd_leaudio_scan_delegator_receive_state_get_help, +#endif + }, + { + .sc_cmd = "scan-delegator-show", + .sc_cmd_func = cmd_leaudio_scan_delegator_receive_state_show, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &cmd_leaudio_scan_delegator_receive_state_show_help, +#endif + }, +#endif /* BLE_AUDIO_SCAN_DELEGATOR */ #if MYNEWT_VAL(BLE_ISO) #if MYNEWT_VAL(BLE_ISO_BROADCAST_SOURCE) { diff --git a/apps/btshell/src/cmd.h b/apps/btshell/src/cmd.h index 1477ea85c7..c5532f34ff 100644 --- a/apps/btshell/src/cmd.h +++ b/apps/btshell/src/cmd.h @@ -30,6 +30,13 @@ int parse_eddystone_url(char *full_url, uint8_t *out_scheme, char *out_body, int cmd_parse_conn_start_end(uint16_t *out_conn, uint16_t *out_start, uint16_t *out_end); +int parse_dev_addr(const char *prefix, const struct parse_arg_kv_pair *addr_types, + ble_addr_t *addr); + +int cmd_parse_addr(const char *prefix, ble_addr_t *addr); + +const char *cmd_addr_type_str(uint8_t type); + void cmd_init(void); #endif diff --git a/apps/btshell/src/cmd_leaudio.c b/apps/btshell/src/cmd_leaudio.c index 2f31246cf8..104661fa7b 100644 --- a/apps/btshell/src/cmd_leaudio.c +++ b/apps/btshell/src/cmd_leaudio.c @@ -17,12 +17,17 @@ * under the License. */ +#include "audio/ble_audio.h" #include "cmd_leaudio.h" #include "btshell.h" #include "console/console.h" +#include "shell/shell.h" +#include "bsp/bsp.h" #include "errno.h" -#if (MYNEWT_VAL(BLE_AUDIO)) +#define STR_NULL "null" + +#if (MYNEWT_VAL(BLE_ISO_BROADCAST_SOURCE)) #include "audio/ble_audio_broadcast_source.h" int cmd_leaudio_base_add(int argc, char **argv) @@ -382,4 +387,754 @@ cmd_leaudio_broadcast_stop(int argc, char **argv) return btshell_broadcast_stop(adv_instance); } +#endif /* BLE_ISO_BROADCAST_SOURCE */ + +#if (MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK)) +#include "audio/ble_audio_broadcast_sink.h" + +#define BROADCAST_SINK_PA_SYNC_TIMEOUT_DEFAULT 0x07D0 +#define BROADCAST_SINK_SYNC_RETRY_MAX 5 + +static int +iso_event_handler(struct ble_iso_event *event, void *arg) +{ + switch (event->type) { + case BLE_ISO_EVENT_ISO_RX: + os_mbuf_free_chain(event->iso_rx.om); + break; + + default: + break; + } + + return 0; +} + +static int +broadcast_sink_pa_sync_params_get(struct ble_gap_periodic_sync_params *params) +{ + params->skip = 0; + params->sync_timeout = BROADCAST_SINK_PA_SYNC_TIMEOUT_DEFAULT; + params->reports_disabled = false; + + return 0; +} + +static int +broadcast_sink_big_sync_params_get( + uint16_t iso_interval, uint8_t *out_mse, uint16_t *out_sync_timeout) +{ + *out_mse = 0; + /* TODO: fix the sync timeout calculation */ + *out_sync_timeout = BROADCAST_SINK_SYNC_RETRY_MAX * iso_interval; + + return 0; +} + +static void +codec_specific_config_printf( + const struct ble_audio_codec_id *unused, const uint8_t *data, uint8_t len) +{ + console_printf("data=%p len=%u\n", data, len); +} + +static void +base_bis_printf(const struct ble_audio_codec_id *codec_id, const struct ble_audio_base_bis *bis) +{ + console_printf("BISCodecConfig:\n\t"); + codec_specific_config_printf(codec_id,bis->codec_spec_config, bis->codec_spec_config_len); +} + +static void +metadata_printf(const uint8_t *data, uint8_t len) +{ + console_printf("data=%p len=%u\n", data, len); +} + +static void +base_subgroup_printf(uint8_t subgroup_index, const struct ble_audio_base_subgroup *subgroup) +{ + console_printf("subgroup_index=%u\n", subgroup_index); + console_printf("Codec ID:\n\tformat=0x%02x company_id=0x%04x vendor_specific=0x%02x\n", + subgroup->codec_id.format, subgroup->codec_id.company_id, + subgroup->codec_id.vendor_specific); + console_printf("SubgroupCodecConfig:\n\t"); + codec_specific_config_printf(&subgroup->codec_id, + subgroup->codec_spec_config, + subgroup->codec_spec_config_len); + console_printf("Metadata:\n\t"); + metadata_printf(subgroup->metadata, subgroup->metadata_len); +} + +static int +broadcast_sink_disc_start(const struct ble_gap_ext_disc_params *params) +{ + uint8_t own_addr_type; + int rc; + + /* Figure out address to use while scanning. */ + rc = ble_hs_id_infer_auto(0, &own_addr_type); + if (rc != 0) { + console_printf("determining own address type failed (%d)", rc); + assert(0); + } + + rc = ble_gap_ext_disc(own_addr_type, 0, 0, 0, 0, 0, params, NULL, NULL, NULL); + if (rc != 0) { + console_printf("ext disc failed (%d)", rc); + } + + return rc; +} + +static int +broadcast_sink_disc_stop(void) +{ + int rc; + + rc = ble_gap_disc_cancel(); + if (rc != 0) { + console_printf("disc cancel failed (%d)", rc); + } + + return rc; +} + +static int +broadcast_sink_action_fn( + struct ble_audio_broadcast_sink_action *action, void *arg) +{ + switch (action->type) { + case BLE_AUDIO_BROADCAST_SINK_ACTION_PA_SYNC: + console_printf("PA Sync:\n"); + return broadcast_sink_pa_sync_params_get(action->pa_sync.out_params); + case BLE_AUDIO_BROADCAST_SINK_ACTION_BIG_SYNC: + console_printf("BIG Sync:\nsource_id=0x%02x iso_interval=0x%04x" + " presentation_delay=%u[us]\n", + action->big_sync.source_id, action->big_sync.iso_interval, + action->big_sync.presentation_delay); + return broadcast_sink_big_sync_params_get( + action->big_sync.iso_interval, action->big_sync.out_mse, + action->big_sync.out_sync_timeout); + case BLE_AUDIO_BROADCAST_SINK_ACTION_BIS_SYNC: + console_printf("BIS Sync:\n\tsource_id=0x%02x bis_index=0x%02x\n", + action->bis_sync.source_id, action->bis_sync.bis->index); + base_subgroup_printf(action->bis_sync.subgroup_index, action->bis_sync.subgroup); + base_bis_printf(&action->bis_sync.subgroup->codec_id, action->bis_sync.bis); + *action->bis_sync.out_cb = &iso_event_handler; + *action->bis_sync.out_cb_arg = UINT_TO_POINTER(action->bis_sync.source_id); + return 0; + case BLE_AUDIO_BROADCAST_SINK_ACTION_DISC_START: + return broadcast_sink_disc_start(action->disc_start.params_preferred); + case BLE_AUDIO_BROADCAST_SINK_ACTION_DISC_STOP: + return broadcast_sink_disc_stop(); + default: + assert(false); + return ENOTSUP; + } + + return 0; +} + + +static const struct shell_param cmd_leaudio_broadcast_sink_start_params[] = { + {"source_id", "usage: ="}, + {"broadcast_code", "usage: =[string], default: NULL"}, + {NULL, NULL} +}; + +#if MYNEWT_VAL(SHELL_CMD_HELP) +const struct shell_cmd_help cmd_leaudio_broadcast_sink_start_help = { + .summary = "Start audio Broadcast Sink", + .usage = NULL, + .params = cmd_leaudio_broadcast_sink_start_params +}; #endif + +int +cmd_leaudio_broadcast_sink_start(int argc, char **argv) +{ + struct ble_audio_broadcast_sink_add_params params = {0}; + char *broadcast_code; + uint8_t source_id; + int rc; + + rc = parse_arg_init(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + source_id = parse_arg_uint8("source_id", &rc); + if (rc != 0) { + console_printf("invalid 'source_id' parameter\n"); + return rc; + } + + broadcast_code = parse_arg_extract("broadcast_code"); + if (broadcast_code != NULL) { + strncpy((char *)params.broadcast_code, broadcast_code, BLE_AUDIO_BROADCAST_CODE_SIZE); + params.broadcast_code_is_valid = true; + } + + rc = ble_audio_broadcast_sink_start(source_id, ¶ms); + if (rc != 0) { + console_printf("start failed (%d)\n", rc); + } + + return rc; +} + +static const struct shell_param cmd_leaudio_broadcast_sink_stop_params[] = { + {"source_id", "usage: ="}, + {NULL, NULL} +}; + +#if MYNEWT_VAL(SHELL_CMD_HELP) +const struct shell_cmd_help cmd_leaudio_broadcast_sink_stop_help = { + .summary = "Stop audio Broadcast Sink", + .usage = NULL, + .params = cmd_leaudio_broadcast_sink_stop_params +}; +#endif + +int +cmd_leaudio_broadcast_sink_stop(int argc, char **argv) +{ + uint8_t source_id; + int rc; + + rc = parse_arg_init(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + source_id = parse_arg_uint8("source_id", &rc); + if (rc != 0) { + console_printf("invalid 'source_id' parameter\n"); + return rc; + } + + rc = ble_audio_broadcast_sink_stop(source_id); + if (rc != 0) { + console_printf("stop failed (%d)\n", rc); + } + + return rc; +} + +static const struct shell_param cmd_leaudio_broadcast_sink_metadata_update_params[] = { + {"source_id", "usage: ="}, + {"subgroup_index", "usage: ="}, + {"metadata", "usage: =[XX:XX...]"}, + {NULL, NULL} +}; + +#if MYNEWT_VAL(SHELL_CMD_HELP) +const struct shell_cmd_help cmd_leaudio_broadcast_sink_metadata_update_help = { + .summary = "Update Broadcast Sink metadata", + .usage = NULL, + .params = cmd_leaudio_broadcast_sink_metadata_update_params +}; +#endif + +int +cmd_leaudio_broadcast_sink_metadata_update(int argc, char **argv) +{ + struct ble_audio_broadcast_sink_metadata_update_params params = {0}; + static bssnz_t uint8_t metadata[UINT8_MAX]; + unsigned int metadata_len; + uint8_t source_id; + int rc; + + rc = parse_arg_init(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + source_id = parse_arg_uint8("source_id", &rc); + if (rc != 0) { + console_printf("invalid 'source_id' parameter\n"); + return rc; + } + + params.subgroup_index = parse_arg_uint8("subgroup_index", &rc); + if (rc != 0) { + console_printf("invalid 'subgroup_index' parameter\n"); + return rc; + } + + rc = parse_arg_byte_stream("metadata", UINT8_MAX, metadata, &metadata_len); + if (rc == 0) { + params.metadata = metadata; + params.metadata_length = metadata_len; + } else if (rc != ENOENT) { + console_printf("invalid 'metadata' parameter\n"); + return rc; + } + + rc = ble_audio_broadcast_sink_metadata_update(source_id, ¶ms); + if (rc != 0) { + console_printf("metadata update failed (%d)\n", rc); + } + + return rc; +} + +static void +broadcast_sink_audio_data_path_setup(uint16_t conn_handle) +{ + struct ble_iso_data_path_setup_params params = { + .conn_handle = conn_handle, + .data_path_dir = BLE_ISO_DATA_DIR_RX, + .codec_id.format = 0x06, + }; + int rc; + + rc = ble_iso_data_path_setup(¶ms); + if (rc != 0) { + console_printf("datapath setup failed (%d)\n", rc); + } +} + +static int +broadcast_sink_audio_event_handler(struct ble_audio_event *event, void *arg) +{ + switch (event->type) { + case BLE_AUDIO_EVENT_BROADCAST_SINK_PA_SYNC_STATE: + console_printf("source_id=0x%02x PA sync: %s\n", + event->broadcast_sink_pa_sync_state.source_id, + ble_audio_broadcast_sink_sync_state_str( + event->broadcast_sink_pa_sync_state.state)); + break; + case BLE_AUDIO_EVENT_BROADCAST_SINK_BIS_SYNC_STATE: + console_printf("source_id=0x%02x bis_index=0x%02x BIS sync: %s\n", + event->broadcast_sink_bis_sync_state.source_id, + event->broadcast_sink_bis_sync_state.bis_index, + ble_audio_broadcast_sink_sync_state_str( + event->broadcast_sink_bis_sync_state.state)); + if (event->broadcast_sink_bis_sync_state.state == + BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_ESTABLISHED) { + console_printf("conn_handle=0x%04x\n", + event->broadcast_sink_bis_sync_state.conn_handle); + broadcast_sink_audio_data_path_setup( + event->broadcast_sink_bis_sync_state.conn_handle); + } + break; + default: + break; + } + + return 0; +} +#endif /* BLE_AUDIO_BROADCAST_SINK */ + +#if MYNEWT_VAL(BLE_AUDIO_SCAN_DELEGATOR) +#include "audio/ble_audio_scan_delegator.h" + +static void +scan_delegator_source_desc_printf(const struct ble_audio_scan_delegator_source_desc *source_desc) +{ + console_printf("broadcast_id=0x%6x adv_sid=%d adv_addr_type=%s adv_addr=", + source_desc->broadcast_id, source_desc->adv_sid, + cmd_addr_type_str(source_desc->addr.type)); + print_addr(source_desc->addr.val); + console_printf("\n"); +} + +static void +scan_delegator_sync_opt_printf(const struct ble_audio_scan_delegator_sync_opt *sync_opt) +{ + console_printf("pa_sync=%d pa_interval=0x%04x num_subgroups=%d", + sync_opt->pa_sync, sync_opt->pa_interval, sync_opt->num_subgroups); + for (uint8_t i = 0; i < sync_opt->num_subgroups; i++) { + console_printf("\n\tbis_sync=0x%04x metadata_length=%d metadata=", + sync_opt->subgroups[i].bis_sync, sync_opt->subgroups[i].metadata_length); + print_bytes(sync_opt->subgroups[i].metadata, sync_opt->subgroups[i].metadata_length); + } + console_printf("\n"); +} + +static int +scan_delegator_pick_source_id_to_swap(uint8_t *out_source_id_to_swap) +{ + /* TODO: Add some logic here */ + *out_source_id_to_swap = 0; + + return 0; +} + +static int +scan_delegator_action_fn( + struct ble_audio_scan_delegator_action *action, void *arg) +{ + switch (action->type) { + case BLE_AUDIO_SCAN_DELEGATOR_ACTION_SOURCE_ADD: + console_printf("Source Add:\nsource_id=%u\n", action->source_add.source_id); + scan_delegator_source_desc_printf(&action->source_add.source_desc); + scan_delegator_sync_opt_printf(&action->source_add.sync_opt); + if (action->source_add.out_source_id_to_swap == NULL) { + return 0; + } else { + return scan_delegator_pick_source_id_to_swap(action->source_add.out_source_id_to_swap); + } + case BLE_AUDIO_SCAN_DELEGATOR_ACTION_SOURCE_MODIFY: + console_printf("Source Modify:\nsource_id=%u\n", action->source_modify.source_id); + scan_delegator_sync_opt_printf(&action->source_modify.sync_opt); + break; + case BLE_AUDIO_SCAN_DELEGATOR_ACTION_SOURCE_REMOVE: + console_printf("Source Remove:\nsource_id=%u\n", action->source_remove.source_id); + break; + default: + assert(false); + return ENOTSUP; + } + + return 0; +} + +static const struct shell_param cmd_leaudio_scan_delegator_receive_state_add_params[] = { + {"addr_type", "usage: =[public|random], default: public"}, + {"addr", "usage: =[XX:XX:XX:XX:XX:XX]"}, + {"broadcast_id", "usage: =[0-0xFFFFFF]"}, + {"adv_sid", "usage: =[UINT8], default: 0"}, + {NULL, NULL} +}; + +#if MYNEWT_VAL(SHELL_CMD_HELP) +const struct shell_cmd_help cmd_leaudio_scan_delegator_receive_state_add_help = { + .summary = "Add receive state", + .usage = NULL, + .params = cmd_leaudio_scan_delegator_receive_state_add_params +}; +#endif /* SHELL_CMD_HELP */ + +int +cmd_leaudio_scan_delegator_receive_state_add(int argc, char **argv) +{ + struct ble_audio_scan_delegator_receive_state_add_params params = {0}; + uint8_t source_id; + int rc; + + rc = parse_arg_init(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + rc = cmd_parse_addr(NULL, ¶ms.source_desc.addr); + if (rc != 0) { + console_printf("invalid 'adv_addr' parameter\n"); + return rc; + } + + params.source_desc.broadcast_id = parse_arg_uint32("broadcast_id", &rc); + if (rc != 0) { + console_printf("invalid 'broadcast_id' parameter\n"); + return rc; + } + + params.source_desc.adv_sid = parse_arg_uint8_dflt("adv_sid", 0, &rc); + if (rc != 0) { + console_printf("invalid 'adv_sid' parameter\n"); + return rc; + } + + rc = ble_audio_scan_delegator_receive_state_add(¶ms, &source_id); + if (rc != 0) { + console_printf("Failed to add receive state (%d)\n", rc); + } else { + console_printf("New source_id=%u created\n", source_id); + } + + return rc; +} + +static const struct shell_param cmd_leaudio_scan_delegator_receive_state_remove_params[] = { + {"source_id", "usage: ="}, + {NULL, NULL} +}; + +#if MYNEWT_VAL(SHELL_CMD_HELP) +const struct shell_cmd_help cmd_leaudio_scan_delegator_receive_state_remove_help = { + .summary = "Remove receive state", + .usage = NULL, + .params = cmd_leaudio_scan_delegator_receive_state_remove_params +}; +#endif /* SHELL_CMD_HELP */ + +int +cmd_leaudio_scan_delegator_receive_state_remove(int argc, char **argv) +{ + uint8_t source_id; + int rc; + + rc = parse_arg_init(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + source_id = parse_arg_uint8("source_id", &rc); + if (rc != 0) { + console_printf("invalid 'source_id' parameter\n"); + return rc; + } + + rc = ble_audio_scan_delegator_receive_state_remove(source_id); + if (rc != 0) { + console_printf("remove failed (%d)\n", rc); + } + + return rc; +} + +const struct parse_arg_kv_pair cmd_pa_sync_type[] = { + { "not_synced", BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_NOT_SYNCED }, + { "sync_info_req", BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_SYNC_INFO_REQ }, + { "synced", BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_SYNCED }, + { "failed", BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_ERROR }, + { "no_past", BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_NO_PAST }, + { NULL } +}; + +const struct parse_arg_kv_pair cmd_big_enc_type[] = { + { "not_encrypted", BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_NONE }, + { "code_req", BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_BROADCAST_CODE_MISSING }, + { "decrypting", BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_DECRYPTING }, + { "bad_code", BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_BROADCAST_CODE_INVALID }, + { NULL } +}; + +static const struct shell_param cmd_leaudio_scan_delegator_receive_state_set_params[] = { + {"source_id", "usage: ="}, + {"pa_sync_state", "usage: =[not_synced|sync_info_req|synced|failed|no_past]," + " default: not_synced"}, + {"big_enc", "usage: =[not_encrypted|code_req|decrypting|bad_code]," + " default: not_encrypted"}, + {"bad_code", "usage: =[string], default: NULL"}, + {NULL, NULL} +}; + +#if MYNEWT_VAL(SHELL_CMD_HELP) +const struct shell_cmd_help cmd_leaudio_scan_delegator_receive_state_set_help = { + .summary = "Set receive state", + .usage = NULL, + .params = cmd_leaudio_scan_delegator_receive_state_set_params +}; +#endif /* SHELL_CMD_HELP */ + +int +cmd_leaudio_scan_delegator_receive_state_set(int argc, char **argv) +{ + struct ble_audio_scan_delegator_receive_state state = {0}; + char *bad_code; + uint8_t source_id; + int rc; + + rc = parse_arg_init(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + source_id = parse_arg_uint8("source_id", &rc); + if (rc != 0) { + console_printf("invalid 'source_id' parameter\n"); + return rc; + } + + state.pa_sync_state = parse_arg_kv_dflt("pa_sync_state", cmd_pa_sync_type, + BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_NOT_SYNCED, &rc); + if (rc != 0) { + console_printf("invalid 'pa_sync_state' parameter\n"); + return rc; + } + + state.big_enc = parse_arg_kv_dflt("big_enc", cmd_big_enc_type, + BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_NONE, &rc); + if (rc != 0) { + console_printf("invalid 'big_enc' parameter\n"); + return rc; + } + + bad_code = parse_arg_extract("bad_code"); + if (bad_code != NULL) { + strncpy((char *)state.bad_code, bad_code, BLE_AUDIO_BROADCAST_CODE_SIZE); + } + + /* TODO: initialize state.subgroups */ + state.num_subgroups = 0; + + rc = ble_audio_scan_delegator_receive_state_set(source_id, &state); + if (rc != 0) { + console_printf("set failed (%d)\n", rc); + } + + return rc; +} + +static const char * +pa_sync_type_str(enum ble_audio_scan_delegator_pa_sync_state pa_sync_state) +{ + for (size_t i = 0; i < ARRAY_SIZE(cmd_pa_sync_type); i++) { + if (cmd_pa_sync_type[i].val == pa_sync_state) { + return cmd_pa_sync_type[i].key; + } + } + + return STR_NULL; +} + +static const char * +big_enc_type_str(enum ble_audio_scan_delegator_big_enc big_enc) +{ + for (size_t i = 0; i < ARRAY_SIZE(cmd_big_enc_type); i++) { + if (cmd_big_enc_type[i].val == big_enc) { + return cmd_big_enc_type[i].key; + } + } + + return STR_NULL; +} + +static void +scan_delegator_receive_state_printf(const struct ble_audio_scan_delegator_receive_state *state) +{ + console_printf("pa_sync_state=%s big_enc=%s num_subgroups=%d", + pa_sync_type_str(state->pa_sync_state), big_enc_type_str(state->big_enc), + state->num_subgroups); + + if (state->big_enc == BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_BROADCAST_CODE_INVALID) { + console_printf("bad_code="); + print_bytes(state->bad_code, sizeof(state->bad_code)); + console_printf("\n"); + } + + for (uint8_t i = 0; i < state->num_subgroups; i++) { + console_printf("\n\tbis_sync=0x%04x metadata_length=%d metadata=", + state->subgroups[i].bis_sync, state->subgroups[i].metadata_length); + print_bytes(state->subgroups[i].metadata, state->subgroups[i].metadata_length); + console_printf("\n"); + } +} + +static const struct shell_param cmd_leaudio_scan_delegator_receive_state_get_params[] = { + {"source_id", "usage: ="}, + {NULL, NULL} +}; + +#if MYNEWT_VAL(SHELL_CMD_HELP) +const struct shell_cmd_help cmd_leaudio_scan_delegator_receive_state_get_help = { + .summary = "Get receive state", + .usage = NULL, + .params = cmd_leaudio_scan_delegator_receive_state_get_params +}; +#endif /* SHELL_CMD_HELP */ + +int +cmd_leaudio_scan_delegator_receive_state_get(int argc, char **argv) +{ + struct ble_audio_scan_delegator_receive_state state = {0}; + uint8_t source_id; + int rc; + + rc = parse_arg_init(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + source_id = parse_arg_uint8("source_id", &rc); + if (rc != 0) { + console_printf("invalid 'source_id' parameter\n"); + return rc; + } + + rc = ble_audio_scan_delegator_receive_state_get(source_id, &state); + if (rc != 0) { + console_printf("get failed (%d)\n", rc); + } else { + console_printf("source_id=%u\n", source_id); + scan_delegator_receive_state_printf(&state); + } + + return rc; +} + +static int +scan_delegator_receive_state_foreach_fn( + struct ble_audio_scan_delegator_receive_state_entry *entry, void *arg) +{ + console_printf("source_id=%u\n", entry->source_id); + scan_delegator_source_desc_printf(&entry->source_desc); + scan_delegator_receive_state_printf(&entry->state); + + return 0; +} + +#if MYNEWT_VAL(SHELL_CMD_HELP) +const struct shell_cmd_help cmd_leaudio_scan_delegator_receive_state_show_help = { + .summary = "List receive states", + .usage = NULL, + .params = NULL +}; +#endif /* SHELL_CMD_HELP */ + +int +cmd_leaudio_scan_delegator_receive_state_show(int argc, char **argv) +{ + uint8_t num_entries = 0; + + ble_audio_scan_delegator_receive_state_foreach(scan_delegator_receive_state_foreach_fn, + &num_entries); + if (num_entries == 0) { + console_printf("No receive state\n"); + } + + return 0; +} + +static int +scan_delegator_audio_event_handler(struct ble_audio_event *event, void *arg) +{ + switch (event->type) { + case BLE_AUDIO_EVENT_BROADCAST_ANNOUNCEMENT: + console_printf("Broadcast Announcement\n"); + console_printf("broadcast_id=0x%6x adv_sid=%d addr_type=%s addr=", + event->broadcast_announcement.broadcast_id, + event->broadcast_announcement.ext_disc->sid, + cmd_addr_type_str(event->broadcast_announcement.ext_disc->addr.type)); + print_addr(event->broadcast_announcement.ext_disc->addr.val); + console_printf("\n"); + break; + default: + break; + } + + return 0; +} +#endif /* BLE_AUDIO_SCAN_DELEGATOR */ + +void +btshell_leaudio_init(void) +{ + int rc = 0; + +#if (MYNEWT_VAL(BLE_AUDIO_SCAN_DELEGATOR)) + static struct ble_audio_event_listener scan_delegator_listener; + + rc = ble_audio_scan_delegator_action_fn_set(scan_delegator_action_fn, NULL); + assert(rc == 0); + + rc = ble_audio_event_listener_register(&scan_delegator_listener, + scan_delegator_audio_event_handler, NULL); +#endif /* BLE_AUDIO_SCAN_DELEGATOR */ + +#if (MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK)) + static struct ble_audio_event_listener broadcast_sink_listener; + + rc = ble_audio_broadcast_sink_cb_set(broadcast_sink_action_fn, NULL); + assert(rc == 0); + + rc = ble_audio_event_listener_register(&broadcast_sink_listener, + broadcast_sink_audio_event_handler, NULL); +#endif /* BLE_AUDIO_BROADCAST_SINK */ + + assert(rc == 0); +} diff --git a/apps/btshell/src/cmd_leaudio.h b/apps/btshell/src/cmd_leaudio.h index 84fc458626..477b33a21f 100644 --- a/apps/btshell/src/cmd_leaudio.h +++ b/apps/btshell/src/cmd_leaudio.h @@ -43,4 +43,24 @@ int cmd_leaudio_broadcast_update(int argc, char **argv); int cmd_leaudio_broadcast_start(int argc, char **argv); int cmd_leaudio_broadcast_stop(int argc, char **argv); +extern const struct shell_cmd_help cmd_leaudio_broadcast_sink_start_help; +extern const struct shell_cmd_help cmd_leaudio_broadcast_sink_stop_help; +extern const struct shell_cmd_help cmd_leaudio_broadcast_sink_metadata_update_help; + +int cmd_leaudio_broadcast_sink_start(int argc, char **argv); +int cmd_leaudio_broadcast_sink_stop(int argc, char **argv); +int cmd_leaudio_broadcast_sink_metadata_update(int argc, char **argv); + +extern const struct shell_cmd_help cmd_leaudio_scan_delegator_receive_state_add_help; +extern const struct shell_cmd_help cmd_leaudio_scan_delegator_receive_state_remove_help; +extern const struct shell_cmd_help cmd_leaudio_scan_delegator_receive_state_set_help; +extern const struct shell_cmd_help cmd_leaudio_scan_delegator_receive_state_get_help; +extern const struct shell_cmd_help cmd_leaudio_scan_delegator_receive_state_show_help; + +int cmd_leaudio_scan_delegator_receive_state_add(int argc, char **argv); +int cmd_leaudio_scan_delegator_receive_state_remove(int argc, char **argv); +int cmd_leaudio_scan_delegator_receive_state_set(int argc, char **argv); +int cmd_leaudio_scan_delegator_receive_state_get(int argc, char **argv); +int cmd_leaudio_scan_delegator_receive_state_show(int argc, char **argv); + #endif /* H_CMD_LEAUDIO_ */ diff --git a/apps/btshell/src/main.c b/apps/btshell/src/main.c index 0ba90ff131..7292927654 100644 --- a/apps/btshell/src/main.c +++ b/apps/btshell/src/main.c @@ -2078,6 +2078,10 @@ btshell_ext_scan(uint8_t own_addr_type, uint16_t duration, uint16_t period, const struct ble_gap_ext_disc_params *coded_params, void *cb_args) { + struct btshell_scan_opts *scan_opts = cb_args; + + console_printf("silent %d.", scan_opts->silent); + #if !MYNEWT_VAL(BLE_EXT_ADV) console_printf("BLE extended advertising not supported."); console_printf(" Configure nimble host to enable it\n"); @@ -3143,6 +3147,8 @@ mynewt_main(int argc, char **argv) btshell_init_ext_adv_restart(); + btshell_leaudio_init(); + while (1) { os_eventq_run(os_eventq_dflt_get()); } diff --git a/apps/btshell/syscfg.yml b/apps/btshell/syscfg.yml index 091a2df0c1..3bdfb34ac6 100644 --- a/apps/btshell/syscfg.yml +++ b/apps/btshell/syscfg.yml @@ -50,3 +50,6 @@ syscfg.vals: syscfg.vals.BLE_MESH: MSYS_1_BLOCK_COUNT: 16 + +syscfg.vals.BLE_AUDIO_BROADCAST_SINK: + BLE_AUDIO_BROADCAST_SINK_MAX: 1 diff --git a/nimble/host/audio/targets/btshell_native/pkg.yml b/nimble/host/audio/targets/btshell_native/pkg.yml index 67eb2d6fe5..7c129c62ff 100644 --- a/nimble/host/audio/targets/btshell_native/pkg.yml +++ b/nimble/host/audio/targets/btshell_native/pkg.yml @@ -24,3 +24,4 @@ pkg.description: Target for native btshell application with LE Audio pkg.author: "Apache Mynewt " pkg.homepage: "http://mynewt.apache.org/" pkg.deps: + - nimble/host/audio diff --git a/nimble/host/audio/targets/btshell_native/syscfg.yml b/nimble/host/audio/targets/btshell_native/syscfg.yml index e9db007074..34c9fae7f2 100644 --- a/nimble/host/audio/targets/btshell_native/syscfg.yml +++ b/nimble/host/audio/targets/btshell_native/syscfg.yml @@ -64,6 +64,9 @@ syscfg.vals: BLE_ISO_MAX_BIGS: 1 BLE_ISO_MAX_BISES: 2 + BLE_AUDIO_BROADCAST_SINK: 1 + BLE_AUDIO_SCAN_DELEGATOR: 1 + CONSOLE_UART: 1 CONSOLE_UART_BAUD: 1000000 CONSOLE_STICKY_PROMPT: 1