diff --git a/apps/btshell/src/btshell.h b/apps/btshell/src/btshell.h index c2a2eb3e2..3d94c2120 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 50aac7940..a9758e14b 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 1477ea85c..c5532f34f 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 2f31246cf..cea206b6e 100644 --- a/apps/btshell/src/cmd_leaudio.c +++ b/apps/btshell/src/cmd_leaudio.c @@ -20,9 +20,13 @@ #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 +386,703 @@ 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 + +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 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); + break; + 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); + 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 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); + } + 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); + assert(rc == 0); +#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 84fc45862..477b33a21 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 0ba90ff13..729292765 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 091a2df0c..3bdfb34ac 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/include/audio/ble_audio.h b/nimble/host/audio/include/audio/ble_audio.h index 836a1ab53..6fb5f295e 100644 --- a/nimble/host/audio/include/audio/ble_audio.h +++ b/nimble/host/audio/include/audio/ble_audio.h @@ -68,6 +68,9 @@ /** Broadcast Audio Broadcast Code Size. */ #define BLE_AUDIO_BROADCAST_CODE_SIZE 16 +/** Basic Audio Announcement Service UUID. */ +#define BLE_BASIC_AUDIO_ANNOUNCEMENT_SVC_UUID 0x1851 + /** Broadcast Audio Announcement Service UUID. */ #define BLE_BROADCAST_AUDIO_ANNOUNCEMENT_SVC_UUID 0x1852 @@ -576,6 +579,15 @@ struct ble_audio_broadcast_name { /** BLE Audio event: BASS - Set Broadcast Code */ #define BLE_AUDIO_EVENT_BASS_BROADCAST_CODE_SET 6 +/** BLE Audio event: Broadcast Sink - PA Sync state */ +#define BLE_AUDIO_EVENT_BROADCAST_SINK_PA_SYNC_STATE 7 + +/** BLE Audio event: Broadcast Sink - BIS Sync state */ +#define BLE_AUDIO_EVENT_BROADCAST_SINK_BIS_SYNC_STATE 8 + +/** BLE Audio event: Broadcast Sink - Metadata Updated */ +#define BLE_AUDIO_EVENT_BROADCAST_SINK_METADATA 9 + /** @} */ /** @brief Broadcast Announcement */ @@ -650,6 +662,56 @@ struct ble_audio_event_bass_set_broadcast_code { uint8_t broadcast_code[BLE_AUDIO_BROADCAST_CODE_SIZE]; }; +enum ble_audio_broadcast_sink_sync_state { + /** Broadcast Sink Sync State: Not Synced */ + BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_NOT_SYNCED, + + /** Broadcast Sink Sync State: Sync initiated */ + BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_INITIATED, + + /** Broadcast Sink Sync State: Sync established */ + BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_ESTABLISHED, +}; + +/** @brief PA Synchronization state */ +struct ble_audio_event_broadcast_sink_pa_sync_state { + /** Source ID */ + uint8_t source_id; + + /** Sync state */ + enum ble_audio_broadcast_sink_sync_state state; +}; + +/** @brief BIS Synchronization state */ +struct ble_audio_event_broadcast_sink_bis_sync_state { + /** Source ID */ + uint8_t source_id; + + /** BIS Index */ + uint8_t bis_index; + + /** Sync state */ + enum ble_audio_broadcast_sink_sync_state state; + + /** Connection Handle. Valid if `state` is @ref BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_ESTABLISHED */ + uint16_t conn_handle; +}; + +/** @brief Broadcast Sink Metadata changed */ +struct ble_audio_event_broadcast_sink_metadata { + /** Source ID */ + uint8_t source_id; + + /** BIS Index Mask */ + uint8_t bis_sync; + + /** Scan Delegator Subgroup: Metadata */ + const uint8_t *metadata; + + /** Scan Delegator Subgroup: Metadata length */ + uint8_t metadata_length; +}; + /** * Represents a BLE Audio related event. When such an event occurs, the host * notifies the application by passing an instance of this structure to an @@ -715,6 +777,27 @@ struct ble_audio_event { * Represents a Broadcast Code baing set in BASS. */ struct ble_audio_event_bass_set_broadcast_code bass_set_broadcast_code; + + /** + * @ref BLE_AUDIO_EVENT_BROADCAST_SINK_PA_SYNC_STATE + * + * Represents a update of Broadcast Sink PA sync state. + */ + struct ble_audio_event_broadcast_sink_pa_sync_state broadcast_sink_pa_sync_state; + + /** + * @ref BLE_AUDIO_EVENT_BROADCAST_SINK_BIS_SYNC_STATE + * + * Represents an update of Broadcast Sink BIS sync state. + */ + struct ble_audio_event_broadcast_sink_bis_sync_state broadcast_sink_bis_sync_state; + + /** + * @ref BLE_AUDIO_EVENT_BROADCAST_SINK_METADATA + * + * Represents an update in Broadcast Sink Metadata. + */ + struct ble_audio_event_broadcast_sink_metadata broadcast_sink_metadata; }; }; @@ -950,6 +1033,21 @@ struct ble_audio_base { STAILQ_HEAD(, ble_audio_big_subgroup) subs; }; +static inline const char * +ble_audio_broadcast_sink_sync_state_str(enum ble_audio_broadcast_sink_sync_state state) +{ + switch (state) { + case BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_NOT_SYNCED: + return "not synced"; + case BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_INITIATED: + return "initiated"; + case BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_ESTABLISHED: + return "established"; + default: + return "invalid"; + } +} + /** @} */ #endif /* H_BLE_AUDIO_ */ diff --git a/nimble/host/audio/include/audio/ble_audio_broadcast_sink.h b/nimble/host/audio/include/audio/ble_audio_broadcast_sink.h new file mode 100644 index 000000000..36da4e6c1 --- /dev/null +++ b/nimble/host/audio/include/audio/ble_audio_broadcast_sink.h @@ -0,0 +1,281 @@ +/* + * 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_BLE_AUDIO_BROADCAST_SINK_ +#define H_BLE_AUDIO_BROADCAST_SINK_ + +/** + * @file ble_audio_broadcast_sink.h + * + * @brief Bluetooth LE Audio BAP Broadcast Sink API + * + * @defgroup ble_audio_broadcast_sink Bluetooth LE Audio BAP Broadcast Sink + * @ingroup bt_host + * @{ + * + */ + +#include +#include "host/ble_gap.h" +#include "host/ble_iso.h" +#include "audio/ble_audio.h" +#include "audio/ble_audio_scan_delegator.h" +#include "nimble/ble.h" + +enum ble_audio_broadcast_sink_action_type { + /** Broadcast Sink Action Type: PA sync */ + BLE_AUDIO_BROADCAST_SINK_ACTION_PA_SYNC, + + /** Broadcast Sink Action Type: BIG sync */ + BLE_AUDIO_BROADCAST_SINK_ACTION_BIG_SYNC, + + /** Broadcast Sink Action Type: BIS sync */ + BLE_AUDIO_BROADCAST_SINK_ACTION_BIS_SYNC, + + /** Broadcast Sink Action Type: Start discovery (scan) */ + BLE_AUDIO_BROADCAST_SINK_ACTION_DISC_START, + + /** Broadcast Sink Action Type: Start discovery (scan) */ + BLE_AUDIO_BROADCAST_SINK_ACTION_DISC_STOP, +}; + +struct ble_audio_broadcast_sink_action { + /** + * Indicates the type of action that is requested. + */ + enum ble_audio_broadcast_sink_action_type type; + + /** + * A discriminated union containing additional details concerning the action. + * The 'type' field indicates which member of the union is valid. + */ + union { + /** + * Represents PA Sync parameters request. + * + * The action triggered on locally or remotely initiated PA synchronization request. + * The application should initialize the `out_parameters`, or abort the process. + * + * Valid for the following action types: + * o BLE_AUDIO_BROADCAST_SINK_ACTION_PA_SYNC + * + * Return: + * o 0 on success; + * o A non-zero value to abort. + */ + struct { + /** Pointer to Periodic Sync parameters to initialize. */ + struct ble_gap_periodic_sync_params *out_params; + } pa_sync; + + /** + * Represents BIG Sync request. + * + * The action triggered on locally or remotely initiated BIG synchronization request. + * The application should provide the `out_mse` and `out_sync_timeout`, + * or reject the request. + * + * Valid for the following action types: + * o BLE_AUDIO_BROADCAST_SINK_ACTION_BIG_SYNC + * + * Return: + * o 0 on success; + * o A non-zero value to abort. + */ + struct { + /** Source ID. */ + uint8_t source_id; + + /** ISO Interval. */ + uint16_t iso_interval; + + /** Presentation delay. */ + uint32_t presentation_delay; + + /** Number of SubEvents. The total number of subevents that are used to transmit BIS Data. */ + uint8_t nse; + + /** Burst Number. The number of new payloads for each BIS in a BIS event. */ + uint8_t bn; + + /** + * Pointer to Maximum subevents value to initialize. + * Range: 0x00 to 0x1F. + * Default: 0x00, meaning the Controller can schedule reception of any number of subevents up to NSE. + */ + uint8_t *out_mse; + + /** + * Pointer to Sync Timeout value to initialize. + * Range: 0x000A to 0x4000. + * Default: @ref iso_interval * 6. + */ + uint16_t *out_sync_timeout; + } big_sync; + + /** + * Represents BIS Sync request. + * + * The action triggered on locally or remotely initiated BIS synchronization request. + * The application should provide the `out_cb` and optionally `out_cb_arg`, + * or reject the request. + * + * @note The `subgroup` object as well as it's `base` object, + * therefore must be copied to in order to cache its information. + * + * Valid for the following action types: + * o BLE_AUDIO_BROADCAST_SINK_ACTION_BIS_SYNC + * + * Return: + * o 0 on success; + * o A non-zero value to abort. + */ + struct { + /** Source ID. */ + uint8_t source_id; + + /** Subgroup index. */ + uint8_t subgroup_index; + + /** Broadcast Audio Source Endpoint BIS. */ + const struct ble_audio_base_bis *bis; + + /** Broadcast Audio Source Endpoint Subgroup. */ + const struct ble_audio_base_subgroup *subgroup; + } bis_sync; + + /** + * Represents discovery start request. + * + * The action triggered on locally as part of PA synchronization process. + * + * Valid for the following action types: + * o BLE_AUDIO_BROADCAST_SINK_ACTION_SCAN_START + * + * Return: + * o 0 on success; + * o A non-zero value to abort. + */ + struct { + /** Preferred extended discovery parameters. */ + const struct ble_gap_ext_disc_params *params_preferred; + } disc_start; + }; +}; + +/** + * Prototype of Broadcast Sink action callback. + * This function shall return 0 if operation is accepted, and error code if rejected. + */ +typedef int ble_audio_broadcast_sink_action_fn(struct ble_audio_broadcast_sink_action *action, + void *arg); + +/** + * @brief Sets the application callback function. + * + * This function sets the callback function and its argument that will be called + * when a Broadcast Sink action is triggered. + * + * @param cb Pointer to the callback function of type ble_audio_scan_delegator_ev_cb. + * @param arg Pointer to the argument to be passed to the callback function. + * + * @return Returns 0 on success, or a non-zero error code otherwise. + */ +int ble_audio_broadcast_sink_cb_set(ble_audio_broadcast_sink_action_fn *cb, void *arg); + +/** Sink Add function parameters */ +struct ble_audio_broadcast_sink_add_params { + /** Broadcast Code */ + uint8_t broadcast_code[BLE_AUDIO_BROADCAST_CODE_SIZE]; + + /** Broadcast Code parameter is valid */ + uint8_t broadcast_code_is_valid : 1; +}; + +/** + * @brief Start audio broadcast sink synchronization with the source. + * + * This function synchronizes the audio broadcast sink with the source + * identified by the given source ID. + * The source can be added locally using @ref ble_svc_audio_bass_receive_state_add function + * or requested by remote device. + * + * @param source_id Source ID of Broadcast Source to synchronize to. + * @param params Parameters to be used. + * + * @return 0 on success; + * BLE_HS_ENOENT if the source ID is invalid; + * BLE_HS_EDONE if synced already; + * BLE_HS_EALREADY if the synchronization is in progress; + * BLE_HS_ENOMEM if memory allocation fails; + * Any other non-zero value on failure. + */ +int ble_audio_broadcast_sink_start(uint8_t source_id, + const struct ble_audio_broadcast_sink_add_params *params); + +/** + * @brief Stop audio broadcast sink synchronization. + * + * This function terminates or aborts the pending synchronization with the source + * identified by the given source ID. + * + * @param source_id Source ID of Broadcast Source to synchronize to. + * + * @return 0 on success; + * BLE_HS_ENOENT if the source ID is invalid; + * Any other non-zero value on failure. + */ +int ble_audio_broadcast_sink_stop(uint8_t source_id); + +/** Metadata Update function parameters */ +struct ble_audio_broadcast_sink_metadata_update_params { + /** Subgroup index */ + uint8_t subgroup_index; + + /** Scan Delegator Subgroup: Metadata */ + uint8_t *metadata; + + /** Scan Delegator Subgroup: Metadata length */ + uint8_t metadata_length; +}; + +/** + * @brief Sets audio broadcast sink metadata. + * + * This function updates the broadcast sink metadata identified by the given source ID. + * + * @param source_id Source ID of Broadcast Source. + * @param params Parameters to be used. + * + * @return 0 on success; + * BLE_HS_ENOENT if the source ID is invalid; + * Any other non-zero value on failure. + */ +int ble_audio_broadcast_sink_metadata_update(uint8_t source_id, + const struct ble_audio_broadcast_sink_metadata_update_params *params); + +/** + * @brief Initialize Broadcast Sink + * + * This function is restricted to be called by sysinit. + * + * @return Returns 0 on success, or a non-zero error code otherwise. + */ +int ble_audio_broadcast_sink_init(void); +#endif /* H_BLE_AUDIO_BROADCAST_SINK_ */ diff --git a/nimble/host/audio/include/audio/ble_audio_scan_delegator.h b/nimble/host/audio/include/audio/ble_audio_scan_delegator.h new file mode 100644 index 000000000..5c9aa6605 --- /dev/null +++ b/nimble/host/audio/include/audio/ble_audio_scan_delegator.h @@ -0,0 +1,393 @@ +/* + * 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_BLE_AUDIO_SCAN_DELEGATOR_ +#define H_BLE_AUDIO_SCAN_DELEGATOR_ + +/** + * @file ble_audio_scan_delegator.h + * + * @brief Bluetooth LE Audio BAP Scan Delegator API + * + * @defgroup ble_audio_scan_delegator Bluetooth LE Audio BAP Scan Delegator + * @ingroup bt_host + * @{ + * + */ + +#include +#include "audio/ble_audio.h" +#include "nimble/ble.h" + +#if MYNEWT_VAL(BLE_AUDIO_SCAN_DELEGATOR) +#define BLE_AUDIO_SCAN_DELEGATOR_SUBGROUP_MAX \ + MYNEWT_VAL(BLE_AUDIO_SCAN_DELEGATOR_SUBGROUP_MAX) +#else +#define BLE_AUDIO_SCAN_DELEGATOR_SUBGROUP_MAX 0 +#endif /* BLE_AUDIO_SCAN_DELEGATOR */ + +/** No preferred BIS Synchronization. Decision is left to application. */ +#define BLE_AUDIO_SCAN_DELEGATOR_BIS_SYNC_ANY 0xFFFFFFFF + +/** Unknown PA Interval */ +#define BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_UNKNOWN 0xFFFF + +/** Scan Delegator Source descriptor */ +struct ble_audio_scan_delegator_source_desc { + /** Scan Delegator Source: BLE Address */ + ble_addr_t addr; + + /** Scan Delegator Source: Advertising SID */ + uint8_t adv_sid; + + /** Scan Delegator Source: Broadcast ID */ + uint32_t broadcast_id; +}; + +/** Scan Delegator Broadcast Encryption States */ +enum ble_audio_scan_delegator_big_enc { + /** Scan Delegator BIG Encryption: Not Encrypted */ + BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_NONE, + + /** Scan Delegator BIG Encryption: Broadcast Code Required */ + BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_BROADCAST_CODE_MISSING, + + /** Scan Delegator BIG Encryption: Decrypting */ + BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_DECRYPTING, + + /** Scan Delegator BIG Encryption: Bad Code */ + BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_BROADCAST_CODE_INVALID +}; + +/** Scan Delegator PA Sync States */ +enum ble_audio_scan_delegator_pa_sync_state { + /** Scan Delegator PA Sync State: Not synchronized to PA */ + BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_NOT_SYNCED, + + /** Scan Delegator PA Sync State: SyncInfo Request */ + BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_SYNC_INFO_REQ, + + /** Scan Delegator PA Sync State: Synchronized to PA */ + BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_SYNCED, + + /** Scan Delegator PA Sync State: Failed to synchronize to PAA */ + BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_ERROR, + + /** Scan Delegator PA Sync State: No PAST */ + BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_NO_PAST +}; + +/** Scan Delegator Subgroup definition */ +struct ble_audio_scan_delegator_subgroup { + /** Scan Delegator Subgroup: BIS Synchronization */ + uint32_t bis_sync; + + /** Scan Delegator Subgroup: Metadata */ + uint8_t *metadata; + + /** Scan Delegator Subgroup: Metadata length */ + uint8_t metadata_length; +}; + +/** Scan Delegator PA Sync option */ +enum ble_audio_scan_delegator_pa_sync { + /** Scan Delegator PA Sync: Do not synchronize to PA */ + BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_DO_NOT_SYNC, + + /** Scan Delegator PA Sync: Synchronize to PA – PAST available */ + BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_PAST_AVAILABLE, + + /** Scan Delegator PA Sync: Synchronize to PA – PAST not available */ + BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_PAST_NOT_AVAILABLE, +}; + +/** Scan Delegator Broadcast Source Synchronization option */ +struct ble_audio_scan_delegator_sync_opt { + /** PA Sync option */ + enum ble_audio_scan_delegator_pa_sync pa_sync; + + /** PA Sync interval */ + uint16_t pa_interval; + + /** Number of Subgroups */ + uint8_t num_subgroups; + + /** Subgroup sync option */ + struct ble_audio_scan_delegator_subgroup subgroups[ + BLE_AUDIO_SCAN_DELEGATOR_SUBGROUP_MAX]; +}; + +enum ble_audio_scan_delegator_action_type { + /** Scan Delegator Action Type: Add Source */ + BLE_AUDIO_SCAN_DELEGATOR_ACTION_SOURCE_ADD, + + /** Scan Delegator Action Type: Modify Source */ + BLE_AUDIO_SCAN_DELEGATOR_ACTION_SOURCE_MODIFY, + + /** Scan Delegator Action Type: Remove Source */ + BLE_AUDIO_SCAN_DELEGATOR_ACTION_SOURCE_REMOVE, +}; + +struct ble_audio_scan_delegator_action { + /** + * Indicates the type of action that is requested. + */ + enum ble_audio_scan_delegator_action_type type; + + /** + * A union containing additional details concerning the action. + * The 'type' field indicates which member of the union is valid. + */ + union { + /** + * Represents remote Add Source operation request. + * + * Valid for the following action types: + * o BLE_AUDIO_SCAN_DELEGATOR_ACTION_SOURCE_ADD + * + * @note The @ref ble_audio_scan_delegator_subgroup.metadata object is temporary, therefore must be copied to in + * order to cache its information. + * + * Return: + * o 0 on success; + * o A non-zero value to reject. + */ + struct { + /** Source ID */ + uint8_t source_id; + + /** Broadcast Source descriptor */ + struct ble_audio_scan_delegator_source_desc source_desc; + + /** Broadcast synchronization option */ + struct ble_audio_scan_delegator_sync_opt sync_opt; + + /** + * Valid pointer to provide source ID to be swapped or NULL. + * + * If there are insufficient resources to handle the operation, the application is requested to provide + * source ID to be removed once accepted. + */ + uint8_t *out_source_id_to_swap; + } source_add; + + /** + * Represents remote Modify Source operation request. + * + * Valid for the following action types: + * o BLE_AUDIO_SCAN_DELEGATOR_ACTION_SOURCE_MODIFY + * + * @note The @ref ble_audio_scan_delegator_subgroup.metadata object is temporary, therefore must be copied to in + * order to cache its information. + * + * Return: + * o 0 on success; + * o A non-zero value to reject. + */ + struct { + /** Source ID */ + uint8_t source_id; + + /** Broadcast synchronization option */ + struct ble_audio_scan_delegator_sync_opt sync_opt; + } source_modify; + + /** + * Represents remote Remove Source operation request. + * + * Valid for the following action types: + * o BLE_AUDIO_SCAN_DELEGATOR_ACTION_SOURCE_REMOVE + * + * Return: + * o 0 on success; + * o A non-zero value to reject. + */ + struct { + /** Source ID */ + uint8_t source_id; + } source_remove; + + /** + * Represents remote Broadcast Code Set operation request. + * + * Valid for the following action types: + * o BLE_AUDIO_SCAN_DELEGATOR_ACTION_BROADCAST_CODE + * + * Return: + * o 0 on success; + * o A non-zero value on failure. + */ + struct { + /** Source ID */ + uint8_t source_id; + + /** Broadcast Code value to be stored. */ + const uint8_t value[BLE_AUDIO_BROADCAST_CODE_SIZE]; + } broadcast_code; + }; +}; + +/** + * Prototype of Scan Delegator action callback. + * This function shall return 0 if operation is accepted, and error code if rejected. + */ +typedef int ble_audio_scan_delegator_action_fn( + struct ble_audio_scan_delegator_action *action, void *arg); + +/** + * @brief Sets the application callback function. + * + * This function sets the callback function that will be called on remote device request. + * + * @param[in] cb Pointer to the callback function. + * @param[in] arg Pointer to any additional arguments that need to + * be passed to the callback function. + * + * @return 0 on success; + * A non-zero value on failure. + */ +int ble_audio_scan_delegator_action_fn_set(ble_audio_scan_delegator_action_fn *cb, void *arg); + +/** Scan Delegator Receive State definition */ +struct ble_audio_scan_delegator_receive_state { + /** Scan Delegator Receive State: PA Sync state */ + enum ble_audio_scan_delegator_pa_sync_state pa_sync_state; + + /** Scan Delegator Receive State: BIG Encryption */ + enum ble_audio_scan_delegator_big_enc big_enc; + + /** + * Incorrect Bad Broadcast Code. + * Valid for @ref BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_BROADCAST_CODE_INVALID. + */ + uint8_t bad_code[BLE_AUDIO_BROADCAST_CODE_SIZE]; + + /** Scan Delegator Receive State: Number of subgroups */ + uint8_t num_subgroups; + + /** Scan Delegator Receive State: subgroup entries */ + struct ble_audio_scan_delegator_subgroup subgroups[ + MYNEWT_VAL(BLE_AUDIO_SCAN_DELEGATOR_SUBGROUP_MAX)]; +}; + +/** Receive State Add function parameters */ +struct ble_audio_scan_delegator_receive_state_add_params { + /** Broadcast Source descriptor */ + struct ble_audio_scan_delegator_source_desc source_desc; + + /** Receive state */ + struct ble_audio_scan_delegator_receive_state state; +}; + +/** + * @brief Adds the receive state. + * + * This function allocates receive state and returns it's source ID. + * + * @param[in] params Parameters to be used. + * @param[in,out] source_id Unique source ID of receive state added. + * + * @return 0 on success; + * A non-zero value on failure. + */ +int ble_audio_scan_delegator_receive_state_add(const struct ble_audio_scan_delegator_receive_state_add_params *params, + uint8_t *source_id); + +/** + * @brief Removes the receive state. + * + * This function removes the specific receive state identified by source ID. + * + * @param[in] source_id Source ID of receive state to be removed. + * @param[in] params Parameters to be used. + * + * @return 0 on success; + * A non-zero value on failure. + */ +int ble_audio_scan_delegator_receive_state_remove(uint8_t source_id); + +/** + * @brief Set the receive state. + * + * This function updates the specific receive state identified by source ID. + * + * @param[in] source_id Source ID of receive state to be updated. + * @param[in] state Receive state to be set. + * + * @return 0 on success; + * A non-zero value on failure. + */ +int ble_audio_scan_delegator_receive_state_set(uint8_t source_id, + const struct ble_audio_scan_delegator_receive_state *state); + +/** + * @brief get the receive state. + * + * This function returns the specific receive state identified by source ID. + * + * @param[in] source_id Source ID of receive state to be updated. + * @param[in,out] state Pointer to receive state to be populate. + * + * @return 0 on success; + * A non-zero value on failure. + */ +int ble_audio_scan_delegator_receive_state_get(uint8_t source_id, + struct ble_audio_scan_delegator_receive_state *state); + +/** Scan Delegator Receive State entry definition */ +struct ble_audio_scan_delegator_receive_state_entry { + /** Source ID */ + uint8_t source_id; + + /** Broadcast Source descriptor */ + struct ble_audio_scan_delegator_source_desc source_desc; + + /** Receive state */ + struct ble_audio_scan_delegator_receive_state state; +}; + +/** + * Type definition Receive State iteration callback function. + * + * @note Return 0 to continue, or a non-zero to abort foreach loop. + */ +typedef int ble_audio_scan_delegator_receive_state_foreach_fn( + struct ble_audio_scan_delegator_receive_state_entry *entry, void *arg); + +/** + * @brief Iterate receive states. + * + * @param[in] cb Callback to be called on codec entries. + * @param[in] arg Optional callback argument. + * + * @return 0 on success; + * A non-zero value on failure. + */ +void ble_audio_scan_delegator_receive_state_foreach(ble_audio_scan_delegator_receive_state_foreach_fn *cb, void *arg); + +/** + * @brief Initialize Scan Delegator + * + * This function is restricted to be called by sysinit. + * + * @return 0 on success; + * A non-zero value on failure. + */ +int ble_audio_scan_delegator_init(void); +#endif /* H_BLE_AUDIO_SCAN_DELEGATOR_ */ diff --git a/nimble/host/audio/pkg.yml b/nimble/host/audio/pkg.yml index 66d418d31..43385d271 100644 --- a/nimble/host/audio/pkg.yml +++ b/nimble/host/audio/pkg.yml @@ -30,3 +30,15 @@ pkg.keywords: pkg.deps: - nimble - nimble/host + +pkg.deps.BLE_AUDIO_BROADCAST_SINK: + - nimble/host/audio/services/pacs/lc3 + +pkg.deps.BLE_AUDIO_SCAN_DELEGATOR: + - nimble/host/audio/services/bass + +pkg.init.BLE_AUDIO_BROADCAST_SINK: + ble_audio_broadcast_sink_init: 'MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK_SYSINIT_STAGE)' + +pkg.init.BLE_AUDIO_SCAN_DELEGATOR: + ble_audio_scan_delegator_init: 'MYNEWT_VAL(BLE_AUDIO_SCAN_DELEGATOR_SYSINIT_STAGE)' diff --git a/nimble/host/audio/services/pacs/lc3/syscfg.yml b/nimble/host/audio/services/pacs/lc3/syscfg.yml index f2a2ff068..2a12d89b5 100644 --- a/nimble/host/audio/services/pacs/lc3/syscfg.yml +++ b/nimble/host/audio/services/pacs/lc3/syscfg.yml @@ -129,3 +129,12 @@ syscfg.defs: Audio Locations supported by sink codec. Value is an any combination of values defined in Bluetooth Assigned Numbers 6.12.3. Default: Media value: 0x0004 + +syscfg.vals.BLE_AUDIO_BROADCAST_SINK: + BLE_SVC_AUDIO_PACS_LC3_SNK_SAMPLING_FREQUENCIES: > + BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_16000_HZ || BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_24000_HZ + BLE_SVC_AUDIO_PACS_LC3_SNK_FRAME_DURATIONS: BLE_AUDIO_CODEC_SUPPORTED_FRAME_DURATION_10_MS + BLE_SVC_AUDIO_PACS_LC3_SNK_AUDIO_CHANNEL_COUNTS: BLE_AUDIO_CODEC_SUPPORTED_CHANNEL_COUNT_1 + BLE_SVC_AUDIO_PACS_LC3_SNK_MIN_OCTETS_PER_CODEC_FRAME: 40 + BLE_SVC_AUDIO_PACS_LC3_SNK_MAX_OCTETS_PER_CODEC_FRAME: 60 + BLE_SVC_AUDIO_PACS_LC3_SNK_MAX_CODEC_FRAMES_PER_SDU: 1 diff --git a/nimble/host/audio/src/ble_audio.c b/nimble/host/audio/src/ble_audio.c index 425334781..f601a07fb 100644 --- a/nimble/host/audio/src/ble_audio.c +++ b/nimble/host/audio/src/ble_audio.c @@ -136,7 +136,9 @@ ble_audio_gap_event(struct ble_gap_event *gap_event, void *arg) { switch (gap_event->type) { case BLE_GAP_EVENT_EXT_DISC: { - struct ble_audio_adv_parse_broadcast_announcement_data data = { 0 }; + struct ble_audio_adv_parse_broadcast_announcement_data data = { + .success = false, + }; int rc; rc = ble_hs_adv_parse(gap_event->ext_disc.data, diff --git a/nimble/host/audio/src/ble_audio_broadcast_sink.c b/nimble/host/audio/src/ble_audio_broadcast_sink.c new file mode 100644 index 000000000..8c2f05451 --- /dev/null +++ b/nimble/host/audio/src/ble_audio_broadcast_sink.c @@ -0,0 +1,1647 @@ +/* + * 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. + */ + +#include "sysinit/sysinit.h" + +#if MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK) +#include "stdlib.h" + +#include "audio/ble_audio.h" +#include "audio/ble_audio_broadcast_sink.h" +#include "audio/ble_audio_scan_delegator.h" +#include "host/ble_gap.h" +#include "host/ble_hs.h" +#include "host/ble_iso.h" +#include "host/ble_uuid.h" + +#include "ble_audio_priv.h" +#include "ble_audio_scan_delegator_priv.h" + +#define CLAMP(_n, _min, _max) (MAX(_min, MIN(_n, _max))) +#define BROADCAST_ID_INVALID 0xFFFFFFFF +#define BIS_INDEX_TEST(_bis_sync, _index) (((_bis_sync) & (1 << ((_index) - 1))) > 0) +#define BIS_INDEX_SET(_bis_sync, _index) ((_bis_sync) |= (1 << ((_index) - 1))) +#define BIS_INDEX_CLEAR(_bis_sync, _index) ((_bis_sync) &= ~(1 << ((_index) - 1))) + +static struct { + ble_audio_broadcast_sink_action_fn *fn; + void *arg; +} action_cb; + +enum pa_sync_state_internal { + PA_SYNC_STATE_IDLE, + PA_SYNC_STATE_PENDING_DISC, + PA_SYNC_STATE_PENDING_PAST, + PA_SYNC_STATE_PENDING_SYNC, + PA_SYNC_STATE_ACTIVE, + PA_SYNC_STATE_ERROR, + PA_SYNC_STATE_TIMEOUT, +}; + +enum big_sync_state_internal { + BIG_SYNC_STATE_IDLE, + BIG_SYNC_STATE_PENDING_BIG_INFO, + BIG_SYNC_STATE_PENDING_CODE, + BIG_SYNC_STATE_PENDING_BASE, + BIG_SYNC_STATE_PENDING_SYNC, + BIG_SYNC_STATE_FAILED, + BIG_SYNC_STATE_ACTIVE, +}; + +struct ble_audio_broadcast_sink { + /** Instance ID, same as BASS Source ID */ + uint8_t source_id; + + /** Internal PA sync state */ + enum pa_sync_state_internal pa_sync_state; + + /** Internal BIG sync state */ + enum big_sync_state_internal big_sync_state; + + /** Periodic sync handle */ + uint16_t pa_sync_handle; + + /** Connection handle or @ref BLE_HS_CONN_HANDLE_NONE */ + uint16_t past_conn_handle; + + /** BIG Handle */ + uint8_t big_handle; + + /** ISO Interval */ + uint16_t iso_interval; + + /** Burst Number */ + uint8_t bn; + + /** Number of SubEvents */ + uint8_t nse; + + /** Callback function */ + ble_audio_event_fn *cb; + + /** Broadcast code */ + uint8_t broadcast_code[BLE_AUDIO_BROADCAST_CODE_SIZE]; + + /** The optional argument to pass to the callback function. */ + void *cb_arg; + + /** BIG Sync Parameters */ + struct ble_audio_broadcast_sink_big_sync_params *big_sync_params; + + /** If true, the broadcast is encrypted and requires broadcast_code */ + uint8_t is_encrypted : 1; + /** If true, the broadcast_code value is valid */ + uint8_t broadcast_code_is_valid : 1; + + /** Internal subgroups state */ + uint8_t num_subgroups; + struct { + uint32_t bis_sync; + } subgroups[MYNEWT_VAL(BLE_AUDIO_SCAN_DELEGATOR_SUBGROUP_MAX)]; + + /** Singly-linked list entry. */ + SLIST_ENTRY(ble_audio_broadcast_sink) next; +}; + +static SLIST_HEAD(, ble_audio_broadcast_sink) ble_audio_broadcast_sink_list; +static struct os_mempool ble_audio_broadcast_sink_pool; +static os_membuf_t ble_audio_broadcast_sink_mem[ + OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK_MAX), + sizeof(struct ble_audio_broadcast_sink))]; + +/** If true, the discovery was started by us, otherwise by someone else */ +static bool disc_self_initiated; + +/** If true, the Periodic Advertising Sync is pending */ +static bool periodic_adv_sync_in_progress; + +static int gap_event_handler(struct ble_gap_event *event, void *arg); +static int iso_event_handler(struct ble_iso_event *event, void *arg); +static void big_sync_state_set(struct ble_audio_broadcast_sink *sink, enum big_sync_state_internal state_internal); +static void pa_sync_state_set(struct ble_audio_broadcast_sink *sink, enum pa_sync_state_internal state_internal); + +static struct ble_audio_broadcast_sink * +broadcast_sink_get(uint8_t source_id) +{ + struct ble_audio_broadcast_sink *sink; + + SLIST_FOREACH(sink, &ble_audio_broadcast_sink_list, next) { + if (source_id == sink->source_id) { + return sink; + } + } + + return NULL; +} + +static struct ble_audio_broadcast_sink * +broadcast_lookup_pa_sync_handle(uint16_t pa_sync_handle) +{ + struct ble_audio_broadcast_sink *sink; + + SLIST_FOREACH(sink, &ble_audio_broadcast_sink_list, next) { + if (pa_sync_handle == sink->pa_sync_handle) { + return sink; + } + } + + return NULL; +} + +static struct ble_audio_broadcast_sink * +broadcast_sink_lookup_adv_sid_broadcast_id_pair(uint8_t adv_sid, uint32_t broadcast_id) +{ + struct ble_audio_scan_delegator_source_desc source_desc; + struct ble_audio_broadcast_sink *sink; + int rc; + + SLIST_FOREACH(sink, &ble_audio_broadcast_sink_list, next) { + rc = ble_audio_scan_delegator_source_desc_get(sink->source_id, &source_desc); + if (rc != 0) { + BLE_HS_LOG_ERROR("source desc get failed (%d)\n", rc); + continue; + } + + if (source_desc.adv_sid == adv_sid && source_desc.broadcast_id == broadcast_id) { + return sink; + } + } + + return NULL; +} + +static struct ble_audio_broadcast_sink * +broadcast_sink_lookup_addr_adv_sid_pair(const ble_addr_t *addr, uint8_t adv_sid) +{ + struct ble_audio_scan_delegator_source_desc source_desc; + struct ble_audio_broadcast_sink *sink; + int rc; + + SLIST_FOREACH(sink, &ble_audio_broadcast_sink_list, next) { + rc = ble_audio_scan_delegator_source_desc_get(sink->source_id, &source_desc); + if (rc != 0) { + BLE_HS_LOG_ERROR("source desc get failed (%d)\n", rc); + continue; + } + + if (source_desc.adv_sid == adv_sid && ble_addr_cmp(&source_desc.addr, addr) == 0) { + return sink; + } + } + + return NULL; +} + +static struct ble_audio_broadcast_sink * +broadcast_sink_lookup_pa_sync_state(enum pa_sync_state_internal state_internal) +{ + struct ble_audio_broadcast_sink *sink; + + SLIST_FOREACH(sink, &ble_audio_broadcast_sink_list, next) { + if (sink->pa_sync_state == state_internal) { + return sink; + } + } + + return NULL; +} + +static int +disc_start(void) +{ + struct ble_audio_broadcast_sink_action action; + struct ble_gap_ext_disc_params disc_params; + int rc; + + if (ble_gap_disc_active()) { + return 0; + } + + disc_params.itvl = BLE_GAP_SCAN_FAST_INTERVAL_MIN; + disc_params.window = BLE_GAP_SCAN_FAST_WINDOW; + disc_params.passive = true; + + action.type = BLE_AUDIO_BROADCAST_SINK_ACTION_DISC_START; + action.disc_start.params_preferred = &disc_params; + + rc = action_cb.fn(&action, action_cb.arg); + if (rc != 0) { + BLE_HS_LOG_WARN("disc start rejected by user (%d)\n", rc); + } else { + disc_self_initiated = true; + } + + return rc; +} + +static void +disc_stop(void) +{ + struct ble_audio_broadcast_sink_action action; + int rc; + + if (!disc_self_initiated || !ble_gap_disc_active()) { + return; + } + + action.type = BLE_AUDIO_BROADCAST_SINK_ACTION_DISC_STOP; + + rc = action_cb.fn(&action, action_cb.arg); + if (rc != 0) { + BLE_HS_LOG_WARN("disc stop rejected by user (%d)\n", rc); + } + + disc_self_initiated = false; +} + +struct basic_audio_announcement_svc_data { + /** BASE length */ + uint8_t length; + + /** BASE */ + const uint8_t *base; +}; + +struct service_data_uuid16 { + struct basic_audio_announcement_svc_data basic_audio_announcement; +}; + +static void +service_data_uuid16_parse(const uint16_t uuid16, const uint8_t * const value, const uint8_t value_len, + void *user_data) +{ + struct service_data_uuid16 *data = user_data; + + if (uuid16 == BLE_BASIC_AUDIO_ANNOUNCEMENT_SVC_UUID) { + data->basic_audio_announcement.base = value; + data->basic_audio_announcement.length = value_len; + } +} + +struct periodic_report { + struct service_data_uuid16 uuid16; +}; + +static int +periodic_report_parse(const struct ble_hs_adv_field *field, void *user_data) +{ + struct periodic_report *report = user_data; + const uint8_t value_len = field->length - sizeof(field->length); + ble_uuid16_t uuid16 = BLE_UUID16_INIT(0); + uint8_t offset = 0; + + switch (field->type) { + case BLE_HS_ADV_TYPE_SVC_DATA_UUID16: + if (value_len < 2) { + break; + } + + uuid16.value = get_le16(&field->value[offset]); + offset += 2; + + service_data_uuid16_parse(uuid16.value, &field->value[offset], value_len - offset, &report->uuid16); + break; + + default: + /* Continue */ + return BLE_HS_ENOENT; + } + + /* Stop */ + return 0; +} + +static uint32_t +subgroup_bis_sync_get(struct ble_audio_broadcast_sink *sink, uint8_t subgroup_index) +{ + if (subgroup_index > sink->num_subgroups) { + return 0; + } + + return sink->subgroups[subgroup_index].bis_sync; +} + +static void +bass_big_state_update(struct ble_audio_broadcast_sink *sink, enum big_sync_state_internal from, + enum big_sync_state_internal to) +{ + struct ble_audio_scan_delegator_receive_state receive_state = {0}; + int rc; + + rc = ble_audio_scan_delegator_receive_state_get(sink->source_id, &receive_state); + if (rc != 0) { + BLE_HS_LOG_ERROR("receive state get failed (%d)\n", rc); + return; + } + + switch (to) { + case BIG_SYNC_STATE_PENDING_CODE: + receive_state.big_enc = BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_BROADCAST_CODE_MISSING; + break; + + case BIG_SYNC_STATE_IDLE: + if (from == BIG_SYNC_STATE_PENDING_CODE) { + /* FIXME: this does not seem to be right */ + receive_state.big_enc = BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_BROADCAST_CODE_INVALID; + memcpy(receive_state.bad_code, sink->broadcast_code, sizeof(receive_state.bad_code)); + } else if (from == BIG_SYNC_STATE_ACTIVE || from == BIG_SYNC_STATE_FAILED) { + /* Iterate subgroup indexes to update the BIS Sync state */ + for (uint8_t index = 0; index < receive_state.num_subgroups; index++) { + receive_state.subgroups[index].bis_sync = 0; + } + } + /* fallthrough */ + + case BIG_SYNC_STATE_PENDING_BIG_INFO: + case BIG_SYNC_STATE_PENDING_BASE: + case BIG_SYNC_STATE_PENDING_SYNC: + receive_state.big_enc = BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_NONE; + break; + + case BIG_SYNC_STATE_FAILED: + receive_state.big_enc = BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_NONE; + + /* BASS v1.0 3.1.1.4 Add Source operation + * "(...) if the server fails to synchronize to the BIG, the server shall write a value of + * 0xFFFFFFFF (Failed to synchronize to BIG) to the BIS_Sync_State field + */ + for (uint8_t index = 0; index < receive_state.num_subgroups; index++) { + receive_state.subgroups[index].bis_sync = BLE_AUDIO_SCAN_DELEGATOR_BIS_SYNC_ANY; + } + break; + + case BIG_SYNC_STATE_ACTIVE: + if (sink->is_encrypted) { + receive_state.big_enc = BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_DECRYPTING; + } else { + receive_state.big_enc = BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_NONE; + } + + /* Iterate subgroup indexes to update the BIS Sync state */ + for (uint8_t index = 0; index < receive_state.num_subgroups; index++) { + receive_state.subgroups[index].bis_sync = subgroup_bis_sync_get(sink, index); + } + break; + } + + rc = ble_audio_scan_delegator_receive_state_set(sink->source_id, &receive_state); + if (rc != 0) { + BLE_HS_LOG_ERROR("receive state update failed (%d)\n", rc); + } +} + +static enum ble_audio_broadcast_sink_sync_state +big_state_internal_to_api(enum big_sync_state_internal state_internal) +{ + switch (state_internal) { + case BIG_SYNC_STATE_PENDING_CODE: + case BIG_SYNC_STATE_PENDING_BIG_INFO: + case BIG_SYNC_STATE_PENDING_BASE: + case BIG_SYNC_STATE_PENDING_SYNC: + return BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_INITIATED; + + case BIG_SYNC_STATE_ACTIVE: + return BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_ESTABLISHED; + + default: + return BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_NOT_SYNCED; + } +} + +static void +api_bis_state_update(struct ble_audio_broadcast_sink *sink, enum big_sync_state_internal from, + enum big_sync_state_internal to) +{ + struct ble_audio_event event; + + if (to == BIG_SYNC_STATE_ACTIVE) { + /* special case. event already sent from big_sync_established_handler() */ + return; + } + + event.type = BLE_AUDIO_EVENT_BROADCAST_SINK_BIS_SYNC_STATE; + event.broadcast_sink_bis_sync_state.source_id = sink->source_id; + event.broadcast_sink_bis_sync_state.state = big_state_internal_to_api(to); + event.broadcast_sink_bis_sync_state.conn_handle = BLE_HS_CONN_HANDLE_NONE; + + for (uint8_t subgroup_index = 0; subgroup_index < sink->num_subgroups; subgroup_index++) { + uint32_t bis_sync = sink->subgroups[subgroup_index].bis_sync; + + if (bis_sync != BLE_AUDIO_SCAN_DELEGATOR_BIS_SYNC_ANY) { + for (uint8_t bis_index = 0; bis_sync > 0; bis_index++) { + if (BIS_INDEX_TEST(bis_sync, bis_index)) { + event.broadcast_sink_bis_sync_state.bis_index = bis_index; + ble_audio_event_listener_call(&event); + BIS_INDEX_CLEAR(bis_sync, bis_index); + } + } + } + } +} + +static void +big_sync_state_set(struct ble_audio_broadcast_sink *sink, enum big_sync_state_internal state_internal) +{ + enum big_sync_state_internal state_internal_old = sink->big_sync_state; + + if (state_internal == state_internal_old) { + return; + } + + sink->big_sync_state = state_internal; + + api_bis_state_update(sink, state_internal_old, state_internal); + bass_big_state_update(sink, state_internal_old, state_internal); +} + +static void +bass_pa_state_update(struct ble_audio_broadcast_sink *sink, enum pa_sync_state_internal from, + enum pa_sync_state_internal to) +{ + struct ble_audio_scan_delegator_receive_state receive_state; + int rc; + + rc = ble_audio_scan_delegator_receive_state_get(sink->source_id, &receive_state); + if (rc != 0) { + BLE_HS_LOG_ERROR("receive state get failed (%d)\n", rc); + return; + } + + switch (to) { + case PA_SYNC_STATE_IDLE: + case PA_SYNC_STATE_PENDING_DISC: + case PA_SYNC_STATE_PENDING_SYNC: + receive_state.pa_sync_state = BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_NOT_SYNCED; + break; + + case PA_SYNC_STATE_PENDING_PAST: + receive_state.pa_sync_state = BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_SYNC_INFO_REQ; + break; + + case PA_SYNC_STATE_ACTIVE: + receive_state.pa_sync_state = BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_SYNCED; + break; + + case PA_SYNC_STATE_ERROR: + receive_state.pa_sync_state = BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_ERROR; + break; + + case PA_SYNC_STATE_TIMEOUT: + if (from == PA_SYNC_STATE_PENDING_PAST) { + receive_state.pa_sync_state = BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_NO_PAST; + } else { + receive_state.pa_sync_state = BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_ERROR; + } + break; + } + + rc = ble_audio_scan_delegator_receive_state_set(sink->source_id, &receive_state); + if (rc != 0) { + BLE_HS_LOG_ERROR("receive state set failed (%d)\n", rc); + } +} + +static enum ble_audio_broadcast_sink_sync_state +pa_state_internal_to_api(enum pa_sync_state_internal state_internal) +{ + switch (state_internal) { + case PA_SYNC_STATE_PENDING_DISC: + case PA_SYNC_STATE_PENDING_SYNC: + case PA_SYNC_STATE_PENDING_PAST: + return BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_INITIATED; + + case PA_SYNC_STATE_ACTIVE: + return BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_ESTABLISHED; + + default: + return BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_NOT_SYNCED; + } +} + +static void +api_pa_state_update(struct ble_audio_broadcast_sink *sink, enum pa_sync_state_internal from, + enum pa_sync_state_internal to) +{ + struct ble_audio_event event; + + event.type = BLE_AUDIO_EVENT_BROADCAST_SINK_PA_SYNC_STATE; + event.broadcast_sink_pa_sync_state.source_id = sink->source_id; + event.broadcast_sink_pa_sync_state.state = pa_state_internal_to_api(to); + + if (event.broadcast_sink_pa_sync_state.state != pa_state_internal_to_api(from)) { + ble_audio_event_listener_call(&event); + } +} + +static void +pa_sync_state_set(struct ble_audio_broadcast_sink *sink, enum pa_sync_state_internal state_internal) +{ + enum pa_sync_state_internal state_internal_old = sink->pa_sync_state; + + if (state_internal == state_internal_old) { + return; + } + + sink->pa_sync_state = state_internal; + api_pa_state_update(sink, state_internal_old, state_internal); + bass_pa_state_update(sink, state_internal_old, state_internal); +} + +static uint32_t +group_bis_sync_get(struct ble_audio_broadcast_sink *sink) +{ + uint32_t bis_sync_flat = 0; + + for (uint8_t subgroup_index = 0; subgroup_index < sink->num_subgroups; subgroup_index++) { + bis_sync_flat |= sink->subgroups[subgroup_index].bis_sync; + } + + return bis_sync_flat; +} + +static void +pa_sync_create(void) +{ + struct ble_gap_periodic_sync_params periodic_sync_params = {0}; + struct ble_audio_broadcast_sink_action action; + int rc; + + if (periodic_adv_sync_in_progress) { + return; + } + + action.type = BLE_AUDIO_BROADCAST_SINK_ACTION_PA_SYNC; + action.pa_sync.out_params = &periodic_sync_params; + + rc = action_cb.fn(&action, action_cb.arg); + if (rc != 0) { + BLE_HS_LOG_WARN("rejected by user (%d)\n", rc); + return; + } + + rc = ble_gap_periodic_adv_sync_create(NULL, 0, &periodic_sync_params, + gap_event_handler, NULL); + if (rc != 0) { + BLE_HS_LOG_ERROR("adv sync create failed (%d)\n", rc); + } else { + periodic_adv_sync_in_progress = true; + } +} + +static void +big_sync_established_handler(uint8_t source_id, uint8_t status, const struct ble_iso_big_desc *desc) +{ + struct ble_audio_broadcast_sink *sink; + uint32_t bis_sync_state = 0; + uint32_t group_bis_sync; + uint8_t bis_index; + + /* Restart scan if needed */ + if (broadcast_sink_lookup_pa_sync_state(PA_SYNC_STATE_PENDING_SYNC) != NULL) { + pa_sync_create(); + disc_start(); + } else if (broadcast_sink_lookup_pa_sync_state(PA_SYNC_STATE_PENDING_DISC) != NULL) { + disc_start(); + } + + sink = broadcast_sink_get(source_id); + if (sink == NULL) { + BLE_HS_LOG_DEBUG("Unknown source_id %u\n", source_id); + return; + } + + if (status != 0) { + big_sync_state_set(sink, BIG_SYNC_STATE_FAILED); + return; + } + + bis_index = 0; + group_bis_sync = group_bis_sync_get(sink); + + for (uint8_t i = 0; i < desc->num_bis; i++) { + uint16_t conn_handle = desc->conn_handle[i]; + + for (; group_bis_sync > 0; bis_index++) { + if (BIS_INDEX_TEST(group_bis_sync, bis_index)) { + struct ble_audio_event event; + + event.type = BLE_AUDIO_EVENT_BROADCAST_SINK_BIS_SYNC_STATE; + event.broadcast_sink_bis_sync_state.source_id = sink->source_id; + event.broadcast_sink_bis_sync_state.bis_index = bis_index; + event.broadcast_sink_bis_sync_state.state = BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_ESTABLISHED; + event.broadcast_sink_bis_sync_state.conn_handle = conn_handle; + + ble_audio_event_listener_call(&event); + + BIS_INDEX_SET(bis_sync_state, bis_index); + BIS_INDEX_CLEAR(group_bis_sync, bis_index); + break; + } + } + } + + /* Check whether all BISes got conn handle */ + group_bis_sync = group_bis_sync_get(sink); + if (bis_sync_state != group_bis_sync) { + BLE_HS_LOG_WARN("not all BISes synced"); + + for (uint8_t subgroup_index = 0; subgroup_index < sink->num_subgroups; subgroup_index++) { + uint32_t bis_sync_missing; + + bis_sync_missing = sink->subgroups[subgroup_index].bis_sync & ~bis_sync_state; + if (bis_sync_missing == 0) { + continue; + } + + for (bis_index = 0; bis_sync_missing > 0; bis_index++) { + if (BIS_INDEX_TEST(bis_sync_missing, bis_index)) { + struct ble_audio_event event; + + event.type = BLE_AUDIO_EVENT_BROADCAST_SINK_BIS_SYNC_STATE; + event.broadcast_sink_bis_sync_state.source_id = sink->source_id; + event.broadcast_sink_bis_sync_state.bis_index = bis_index; + event.broadcast_sink_bis_sync_state.state = BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_NOT_SYNCED; + event.broadcast_sink_bis_sync_state.conn_handle = BLE_HS_CONN_HANDLE_NONE; + + ble_audio_event_listener_call(&event); + + BIS_INDEX_CLEAR(bis_sync_missing, bis_index); + } + } + + sink->subgroups[subgroup_index].bis_sync &= bis_sync_state; + } + } + + big_sync_state_set(sink, BIG_SYNC_STATE_ACTIVE); +} + +static void +big_sync_terminated_handler(uint8_t source_id, uint8_t reason) +{ + struct ble_audio_broadcast_sink *sink; + + sink = broadcast_sink_get(source_id); + if (sink == NULL) { + BLE_HS_LOG_DEBUG("Unknown source_id %u\n", source_id); + return; + } + + big_sync_state_set(sink, BIG_SYNC_STATE_IDLE); +} + +static int +iso_event_handler(struct ble_iso_event *event, void *arg) +{ + switch (event->type) { + case BLE_ISO_EVENT_BIG_SYNC_ESTABLISHED: + big_sync_established_handler(POINTER_TO_UINT(arg), event->big_sync_established.status, + &event->big_sync_established.desc); + break; + + case BLE_ISO_EVENT_BIG_SYNC_TERMINATED: + big_sync_terminated_handler(POINTER_TO_UINT(arg), event->big_terminated.reason); + break; + + default: + break; + } + + return 0; +} + +static int +pa_sync_create_cancel(void) +{ + int rc; + + if (!periodic_adv_sync_in_progress) { + return 0; + } + + rc = ble_gap_periodic_adv_sync_create_cancel(); + if (rc != 0) { + BLE_HS_LOG_ERROR("adv sync create cancel failed (%d)\n", rc); + } else { + periodic_adv_sync_in_progress = false; + } + + return rc; +} + +static int +pa_sync_add(const ble_addr_t *addr, uint8_t adv_sid) +{ + int rc; + + if (periodic_adv_sync_in_progress) { + rc = pa_sync_create_cancel(); + if (rc != 0) { + return rc; + } + } + + rc = ble_gap_add_dev_to_periodic_adv_list(addr, adv_sid); + if (rc != 0) { + BLE_HS_LOG_ERROR("add dev to periodic adv list failed (%d)\n", rc); + /* TODO: destroy sink */ + return rc; + } + + (void)pa_sync_create(); + + return rc; +} + +static int +pa_sync_remove(const ble_addr_t *addr, uint8_t adv_sid) +{ + int rc; + + if (periodic_adv_sync_in_progress) { + rc = pa_sync_create_cancel(); + if (rc != 0) { + return rc; + } + } + + rc = ble_gap_rem_dev_from_periodic_adv_list(addr, adv_sid); + if (rc != 0) { + BLE_HS_LOG_ERROR("rem dev from periodic adv list failed (%d)\n", rc); + } + + return rc; +} + +static void +periodic_sync_handler(const ble_addr_t *addr, uint8_t adv_sid, uint8_t status, uint16_t pa_sync_handle) +{ + struct ble_audio_broadcast_sink *sink; + + periodic_adv_sync_in_progress = false; + + sink = broadcast_sink_lookup_addr_adv_sid_pair(addr, adv_sid); + if (sink == NULL) { + BLE_HS_LOG_DEBUG("sink not found\n"); + } else { + (void)pa_sync_remove(addr, adv_sid); + + if (status == BLE_ERR_SUCCESS) { + sink->pa_sync_handle = pa_sync_handle; + pa_sync_state_set(sink, PA_SYNC_STATE_ACTIVE); + } else if (status == BLE_ERR_OPERATION_CANCELLED) { + pa_sync_state_set(sink, PA_SYNC_STATE_IDLE); + } else if (status == BLE_ERR_CONN_ESTABLISHMENT) { + pa_sync_state_set(sink, PA_SYNC_STATE_TIMEOUT); + } else { + pa_sync_state_set(sink, PA_SYNC_STATE_ERROR); + } + } + + sink = broadcast_sink_lookup_pa_sync_state(PA_SYNC_STATE_PENDING_SYNC); + if (sink != NULL) { + pa_sync_create(); + disc_start(); + } else { + disc_stop(); + } +} + +static void +periodic_sync_lost_handler(uint16_t sync_handle, int reason) +{ + struct ble_audio_broadcast_sink *sink; + + sink = broadcast_lookup_pa_sync_handle(sync_handle); + if (sink == NULL) { + BLE_HS_LOG_DEBUG("Unknown sync_handle %u\n", sync_handle); + return; + } + + if (reason == BLE_HS_EDONE) { + pa_sync_state_set(sink, PA_SYNC_STATE_IDLE); + } else if (reason == BLE_HS_ETIMEOUT) { + pa_sync_state_set(sink, PA_SYNC_STATE_TIMEOUT); + } else { + pa_sync_state_set(sink, PA_SYNC_STATE_ERROR); + } +} + +static void +biginfo_report_handler(uint16_t pa_sync_handle, uint16_t iso_interval, uint8_t nse, uint8_t bn, uint8_t encryption) +{ + struct ble_audio_broadcast_sink *sink; + + sink = broadcast_lookup_pa_sync_handle(pa_sync_handle); + if (sink == NULL) { + BLE_HS_LOG_DEBUG("Unknown pa_sync_handle %u\n", pa_sync_handle); + return; + } + + if (sink->big_sync_state == BIG_SYNC_STATE_PENDING_BIG_INFO) { + sink->is_encrypted = encryption; + sink->iso_interval = iso_interval; + sink->nse = nse; + sink->bn = bn; + + if (sink->is_encrypted && !sink->broadcast_code_is_valid) { + big_sync_state_set(sink, BIG_SYNC_STATE_PENDING_CODE); + } else { + big_sync_state_set(sink, BIG_SYNC_STATE_PENDING_BASE); + } + } +} + +static int +bis_params_get(struct ble_audio_broadcast_sink *sink, struct ble_audio_base_group *group, + struct ble_audio_base_iter *subgroup_iter, + struct ble_iso_bis_params bis_params[MYNEWT_VAL(BLE_ISO_MAX_BISES)], uint8_t *out_bis_cnt) +{ + uint8_t subgroup_index; + uint32_t group_bis_sync = 0; + uint8_t bis_cnt = 0; + int rc = 0; + + if (action_cb.fn == NULL) { + BLE_HS_LOG_ERROR("action_cb.fn is NULL\n"); + return BLE_HS_EAPP; + } + + for (subgroup_index = 0; subgroup_index < group->num_subgroups; subgroup_index++) { + struct ble_audio_base_subgroup subgroup; + struct ble_audio_base_iter bis_iter; + uint32_t subgroup_bis_sync_req; + + rc = ble_audio_base_subgroup_iter(subgroup_iter, &subgroup, &bis_iter); + if (rc != 0) { + break; + } + + subgroup_bis_sync_req = subgroup_bis_sync_get(sink, subgroup_index); + if (subgroup_bis_sync_req == 0) { + /* No BISes requested for this subgroup */ + continue; + } + + sink->subgroups[subgroup_index].bis_sync = 0; + + for (uint8_t i = 0; i < subgroup.num_bis && bis_cnt < MYNEWT_VAL(BLE_ISO_MAX_BISES); i++) { + struct ble_audio_broadcast_sink_action action = {0}; + struct ble_audio_base_bis bis; + + rc = ble_audio_base_bis_iter(&bis_iter, &bis); + if (rc != 0) { + break; + } + + /* Core 5.4 | Vol 4, Part E; 7.8.106 LE BIG Create Sync command + * BIS[i]: 0x01 to 0x1F + */ + if (bis.index < 0x01 || bis.index > 0x7F) { + continue; + } + + if (BIS_INDEX_TEST(group_bis_sync, bis.index)) { + /* BAP_v1.0.1; 3.7.2.2 Basic Audio Announcements + * + * Rule 3: Every BIS in the BIG, denoted by its BIS_index value, + * shall only be present in one subgroup. + */ + BLE_HS_LOG_WARN("duplicated bis index 0x%02x", bis.index); + continue; + } + + if (!BIS_INDEX_TEST(subgroup_bis_sync_req, bis.index)) { + continue; + } + + action.type = BLE_AUDIO_BROADCAST_SINK_ACTION_BIS_SYNC; + action.bis_sync.source_id = sink->source_id; + action.bis_sync.subgroup_index = subgroup_index; + action.bis_sync.bis = &bis; + action.bis_sync.subgroup = &subgroup; + + rc = action_cb.fn(&action, action_cb.arg); + if (rc != 0) { + BLE_HS_LOG_WARN("bis sync rejected by user (%d)\n", rc); + } else { + BIS_INDEX_SET(sink->subgroups[subgroup_index].bis_sync, bis.index); + bis_params[bis_cnt].bis_index = bis.index; + bis_cnt++; + } + } + + group_bis_sync |= sink->subgroups[subgroup_index].bis_sync; + } + + sink->num_subgroups = subgroup_index; + + *out_bis_cnt = bis_cnt; + return rc; +} + +static int +bis_params_bis_index_cmp(const void *p1, const void *p2) +{ + const struct ble_iso_bis_params *bis_params_1 = p1; + const struct ble_iso_bis_params *bis_params_2 = p2; + + return bis_params_1->bis_index - bis_params_2->bis_index; +} + +static void +big_sync(struct ble_audio_broadcast_sink *sink, const uint8_t *base, uint8_t base_len) +{ + struct ble_audio_broadcast_sink_action action; + struct ble_audio_base_group group; + struct ble_audio_base_iter subgroup_iter; + struct ble_iso_big_sync_create_params big_sync_create_params; + struct ble_iso_bis_params bis_params[MYNEWT_VAL(BLE_ISO_MAX_BISES)]; + char broadcast_code[BLE_AUDIO_BROADCAST_CODE_SIZE + 1]; + uint8_t bis_cnt = 0; + int rc; + + rc = ble_audio_base_parse(base, base_len, &group, &subgroup_iter); + if (rc != 0) { + BLE_HS_LOG_ERROR("base parse failed (%d)\n", rc); + ble_audio_broadcast_sink_stop(sink->source_id); + return; + } + + /* By default, the controller can schedule reception of any number of subevents up to NSE */ + big_sync_create_params.mse = 0x00; + + /* By default, (6 * ISO_Interval * 1.25) / 10 = 3/4 * ISO_Interval */ + big_sync_create_params.sync_timeout = (uint16_t)((3.0 * sink->iso_interval) / 4.0); + big_sync_create_params.sync_timeout = CLAMP(big_sync_create_params.sync_timeout, 0x000A, 0x4000); + + action.type = BLE_AUDIO_BROADCAST_SINK_ACTION_BIG_SYNC; + action.big_sync.source_id = sink->source_id; + action.big_sync.iso_interval = sink->iso_interval; + action.big_sync.nse = sink->nse; + action.big_sync.bn = sink->bn; + action.big_sync.presentation_delay = group.presentation_delay; + action.big_sync.out_mse = &big_sync_create_params.mse; + action.big_sync.out_sync_timeout = &big_sync_create_params.sync_timeout; + + if (action_cb.fn == NULL) { + BLE_HS_LOG_ERROR("action_cb.fn is NULL\n"); + ble_audio_broadcast_sink_stop(sink->source_id); + return; + } + + rc = action_cb.fn(&action, action_cb.arg); + if (rc != 0) { + BLE_HS_LOG_WARN("big sync rejected by user (%d)\n", rc); + ble_audio_broadcast_sink_stop(sink->source_id); + return; + } + + if (sink->broadcast_code_is_valid) { + memcpy(broadcast_code, sink->broadcast_code, BLE_AUDIO_BROADCAST_CODE_SIZE); + broadcast_code[BLE_AUDIO_BROADCAST_CODE_SIZE] = '\0'; + big_sync_create_params.broadcast_code = broadcast_code; + } else { + big_sync_create_params.broadcast_code = NULL; + } + + big_sync_create_params.sync_handle = sink->pa_sync_handle; + big_sync_create_params.cb = iso_event_handler; + big_sync_create_params.cb_arg = UINT_TO_POINTER(sink->source_id); + big_sync_create_params.bis_params = bis_params; + + rc = bis_params_get(sink, &group, &subgroup_iter, bis_params, &bis_cnt); + if (rc != 0 || bis_cnt == 0) { + ble_audio_broadcast_sink_stop(sink->source_id); + return; + } + + /* Sort the parameters by BIS index in ascending order. It is required to properly match the + * BIS index with conn handle in BIG Sync Established event handler. + */ + qsort(bis_params, bis_cnt, sizeof(*bis_params), bis_params_bis_index_cmp); + + big_sync_create_params.bis_cnt = bis_cnt; + + /* Stop scan first */ + disc_stop(); + + rc = ble_iso_big_sync_create(&big_sync_create_params, &sink->big_handle); + if (rc != 0) { + BLE_HS_LOG_ERROR("big sync failed (%d)\n", rc); + return; + } + + big_sync_state_set(sink, BIG_SYNC_STATE_PENDING_SYNC); +} + +static void +metadata_update(struct ble_audio_broadcast_sink *sink, const uint8_t *base, uint8_t base_len) +{ + struct ble_audio_event event; + struct ble_audio_base_group group; + struct ble_audio_base_iter subgroup_iter; + int rc = 0; + + rc = ble_audio_base_parse(base, base_len, &group, &subgroup_iter); + if (rc != 0) { + BLE_HS_LOG_WARN("base parse failed (%d)\n", rc); + return; + } + + for (uint8_t subgroup_index = 0; subgroup_index < group.num_subgroups; subgroup_index++) { + struct ble_audio_base_subgroup subgroup; + uint32_t bis_sync_state; + + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, NULL); + if (rc != 0) { + break; + } + + bis_sync_state = subgroup_bis_sync_get(sink, subgroup_index); + if (bis_sync_state == 0) { + /* No BISes synced for this subgroup */ + continue; + } + + event.type = BLE_AUDIO_EVENT_BROADCAST_SINK_METADATA; + event.broadcast_sink_metadata.source_id = sink->source_id; + event.broadcast_sink_metadata.bis_sync = bis_sync_state; + event.broadcast_sink_metadata.metadata = subgroup.metadata; + event.broadcast_sink_metadata.metadata_length = subgroup.metadata_len; + + /* TODO: filter duplicates */ + + ble_audio_event_listener_call(&event); + } +} + +static void +periodic_report_handler(uint16_t pa_sync_handle, const uint8_t *data, uint8_t data_length, uint8_t data_status, + int8_t rssi, int8_t tx_power) +{ + struct ble_audio_broadcast_sink *sink; + struct periodic_report report = {0}; + int rc; + + if (data_status != BLE_HCI_PERIODIC_DATA_STATUS_COMPLETE) { + return; + } + + sink = broadcast_lookup_pa_sync_handle(pa_sync_handle); + if (sink == NULL) { + BLE_HS_LOG_DEBUG("Unknown pa_sync_handle %u\n", pa_sync_handle); + return; + } + + if (sink->big_sync_state == BIG_SYNC_STATE_PENDING_BASE) { + rc = ble_hs_adv_parse(data, data_length, periodic_report_parse, &report); + if (rc != 0 || report.uuid16.basic_audio_announcement.length == 0) { + BLE_HS_LOG_WARN("source_id %u incorrectly formatted BASE\n", sink->source_id); + ble_audio_broadcast_sink_stop(sink->source_id); + return; + } + + big_sync(sink, report.uuid16.basic_audio_announcement.base, report.uuid16.basic_audio_announcement.length); + } else if (sink->big_sync_state == BIG_SYNC_STATE_ACTIVE) { + rc = ble_hs_adv_parse(data, data_length, periodic_report_parse, &report); + if (rc != 0 || report.uuid16.basic_audio_announcement.length == 0) { + BLE_HS_LOG_WARN("source_id %u incorrectly formatted BASE\n", sink->source_id); + return; + } + + metadata_update(sink, report.uuid16.basic_audio_announcement.base, + report.uuid16.basic_audio_announcement.length); + } +} + +static int +broadcast_id_parse_from_adv(const struct ble_hs_adv_field *field, void *user_data) +{ + const uint8_t value_len = field->length - sizeof(field->length); + uint32_t *broadcast_id = user_data; + + if (field->type == BLE_HS_ADV_TYPE_SVC_DATA_UUID16) { + ble_uuid16_t uuid16 = BLE_UUID16_INIT(0); + uint8_t offset = 0; + + if (value_len < 2) { + /* Continue parsing */ + return BLE_HS_ENOENT; + } + + uuid16.value = get_le16(&field->value[offset]); + offset += 2; + + if (uuid16.value == BLE_BROADCAST_AUDIO_ANNOUNCEMENT_SVC_UUID) { + if ((value_len - offset) >= 3) { + *broadcast_id = get_le24(&field->value[offset]); + } + + /* stop parsing */ + return 0; + } + } + + /* continue parsing */ + return BLE_HS_ENOENT; +} + +static void +ext_disc_handler(const ble_addr_t *addr, uint8_t adv_sid, const uint8_t *data, uint8_t data_length) +{ + uint32_t broadcast_id = BROADCAST_ID_INVALID; + struct ble_audio_broadcast_sink *sink; + int rc; + + if (broadcast_sink_lookup_pa_sync_state(PA_SYNC_STATE_PENDING_DISC) == NULL) { + return; + } + + rc = ble_hs_adv_parse(data, data_length, broadcast_id_parse_from_adv, &broadcast_id); + if (rc != 0 || broadcast_id == BROADCAST_ID_INVALID) { + return; + } + + sink = broadcast_sink_lookup_adv_sid_broadcast_id_pair(adv_sid, broadcast_id); + if (sink == NULL) { + return; + } + + if (sink->pa_sync_state == PA_SYNC_STATE_PENDING_DISC) { + rc = pa_sync_add(addr, adv_sid); + if (rc != 0) { + /* TODO: */ + } else { + pa_sync_state_set(sink, PA_SYNC_STATE_PENDING_SYNC); + } + } +} + +static int +gap_event_handler(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_PERIODIC_SYNC: + periodic_sync_handler(&event->periodic_sync.adv_addr, event->periodic_sync.sid, + event->periodic_sync.status, event->periodic_sync.sync_handle); + break; + + case BLE_GAP_EVENT_PERIODIC_SYNC_LOST: + periodic_sync_lost_handler(event->periodic_sync_lost.sync_handle, event->periodic_sync_lost.reason); + break; + + case BLE_GAP_EVENT_PERIODIC_TRANSFER: + periodic_sync_handler(&event->periodic_transfer.adv_addr, event->periodic_transfer.sid, + event->periodic_transfer.status, event->periodic_transfer.sync_handle); + break; + + case BLE_GAP_EVENT_BIGINFO_REPORT: + biginfo_report_handler(event->biginfo_report.sync_handle, event->biginfo_report.iso_interval, + event->biginfo_report.nse, event->biginfo_report.bn, event->biginfo_report.encryption); + + break; + + case BLE_GAP_EVENT_PERIODIC_REPORT: + periodic_report_handler(event->periodic_report.sync_handle, event->periodic_report.data, + event->periodic_report.data_length, + event->periodic_report.data_status, event->periodic_report.rssi, + event->periodic_report.tx_power); + break; + + case BLE_GAP_EVENT_EXT_DISC: + ext_disc_handler(&event->ext_disc.addr, event->ext_disc.sid, event->ext_disc.data, + event->ext_disc.length_data); + break; + + default: + break; + } + + return 0; +} + +int +ble_audio_broadcast_sink_cb_set(ble_audio_broadcast_sink_action_fn *cb, void *arg) +{ + if (action_cb.fn != NULL) { + return BLE_HS_EALREADY; + } + + action_cb.fn = cb; + action_cb.arg = arg; + + return 0; +} + +void +ble_audio_broadcast_sink_code_set(uint8_t source_id, const uint8_t broadcast_code[BLE_AUDIO_BROADCAST_CODE_SIZE]) +{ + struct ble_audio_broadcast_sink *sink; + + BLE_AUDIO_DBG_ASSERT(broadcast_code != NULL); + + sink = broadcast_sink_get(source_id); + if (sink == NULL) { + BLE_HS_LOG_DEBUG("Unknown source_id %u\n", source_id); + return; + } + + if (sink->big_sync_state == BIG_SYNC_STATE_PENDING_CODE) { + memcpy(sink->broadcast_code, broadcast_code, BLE_AUDIO_BROADCAST_CODE_SIZE); + + big_sync_state_set(sink, BIG_SYNC_STATE_PENDING_BASE); + } +} + +static struct ble_audio_broadcast_sink * +broadcast_sink_new(uint8_t source_id) +{ + struct ble_audio_broadcast_sink *sink; + + sink = os_memblock_get(&ble_audio_broadcast_sink_pool); + if (sink == NULL) { + BLE_HS_LOG_WARN("Out of memory\n"); + return NULL; + } + + memset(sink, 0, sizeof(*sink)); + + sink->source_id = source_id; + + SLIST_INSERT_HEAD(&ble_audio_broadcast_sink_list, sink, next); + + return sink; +} + +static int +pa_sync_receive(struct ble_audio_broadcast_sink *sink, const uint16_t *conn_handle) +{ + struct ble_audio_broadcast_sink_action action; + struct ble_audio_scan_delegator_source_desc source_desc; + struct ble_gap_periodic_sync_params periodic_sync_params = {0}; + int rc; + + rc = ble_audio_scan_delegator_source_desc_get(sink->source_id, &source_desc); + if (rc != 0) { + BLE_HS_LOG_ERROR("source desc get failed (%d)\n", rc); + return rc; + } + + if (action_cb.fn == NULL) { + BLE_HS_LOG_ERROR("action_cb.fn is NULL\n"); + return BLE_HS_EAPP; + } + + action.type = BLE_AUDIO_BROADCAST_SINK_ACTION_PA_SYNC; + action.pa_sync.out_params = &periodic_sync_params; + + rc = action_cb.fn(&action, action_cb.arg); + if (rc != 0) { + BLE_HS_LOG_WARN("pa sync rejected by user (%d)\n", rc); + return rc; + } + + rc = ble_gap_periodic_adv_sync_receive( + *conn_handle, &periodic_sync_params, gap_event_handler, NULL); + if (rc != 0) { + BLE_HS_LOG_ERROR("sync receive failed (%d)\n", rc); + return rc; + } + + return 0; +} + +static int +broadcast_sink_start(uint8_t source_id, const uint8_t *broadcast_code, uint16_t *conn_handle) +{ + struct ble_audio_scan_delegator_source_desc source_desc; + struct ble_audio_broadcast_sink *sink; + int rc; + + rc = ble_audio_scan_delegator_source_desc_get(source_id, &source_desc); + if (rc != 0) { + BLE_HS_LOG_ERROR("source desc get failed (%d)\n", rc); + return rc; + } + + sink = broadcast_sink_get(source_id); + if (sink == NULL) { + sink = broadcast_sink_new(source_id); + if (sink == NULL) { + return BLE_HS_ENOMEM; + } + } + + if (broadcast_code != NULL) { + memcpy(sink->broadcast_code, broadcast_code, BLE_AUDIO_BROADCAST_CODE_SIZE); + sink->broadcast_code_is_valid = true; + } + + /* If not previously set, let the application decide which BISes to sync */ + if (sink->num_subgroups == 0) { + sink->num_subgroups = ARRAY_SIZE(sink->subgroups); + for (uint8_t i = 0; i < sink->num_subgroups; i++) { + sink->subgroups[i].bis_sync = BLE_AUDIO_SCAN_DELEGATOR_BIS_SYNC_ANY; + } + } + + switch (sink->pa_sync_state) { + case PA_SYNC_STATE_PENDING_PAST: + case PA_SYNC_STATE_PENDING_DISC: + case PA_SYNC_STATE_PENDING_SYNC: + case PA_SYNC_STATE_ACTIVE: + break; + + case PA_SYNC_STATE_IDLE: + case PA_SYNC_STATE_ERROR: + case PA_SYNC_STATE_TIMEOUT: + if (conn_handle != NULL) { + /* sync using PAST procedure */ + rc = pa_sync_receive(sink, conn_handle); + if (rc != 0) { + return rc; + } + pa_sync_state_set(sink, PA_SYNC_STATE_PENDING_PAST); + } else if (source_desc.addr.type == BLE_ADDR_PUBLIC) { + /* sync to public address (we don't need to scan, as the address won't change) */ + rc = pa_sync_add(&source_desc.addr, source_desc.adv_sid); + if (rc != 0) { + return rc; + } + pa_sync_state_set(sink, PA_SYNC_STATE_PENDING_SYNC); + } else { + /* scan to find broadcaster using Adv SID and Broadcast ID */ + rc = disc_start(); + if (rc != 0) { + return rc; + } + pa_sync_state_set(sink, PA_SYNC_STATE_PENDING_DISC); + } + break; + + default: + BLE_AUDIO_DBG_ASSERT(false); + ble_audio_broadcast_sink_stop(source_id); + return BLE_HS_EAGAIN; + } + + switch (sink->big_sync_state) { + case BIG_SYNC_STATE_ACTIVE: + case BIG_SYNC_STATE_PENDING_BIG_INFO: + case BIG_SYNC_STATE_PENDING_SYNC: + case BIG_SYNC_STATE_PENDING_BASE: + break; + + case BIG_SYNC_STATE_PENDING_CODE: + if (broadcast_code != NULL) { + big_sync_state_set(sink, BIG_SYNC_STATE_PENDING_BASE); + } + break; + + case BIG_SYNC_STATE_FAILED: + case BIG_SYNC_STATE_IDLE: + big_sync_state_set(sink, BIG_SYNC_STATE_PENDING_BIG_INFO); + break; + + default: + BLE_AUDIO_DBG_ASSERT(false); + ble_audio_broadcast_sink_stop(source_id); + return BLE_HS_EAGAIN; + } + + if (sink->pa_sync_state == PA_SYNC_STATE_ACTIVE && + sink->big_sync_state == BIG_SYNC_STATE_ACTIVE) { + return BLE_HS_EDONE; + } + + return 0; +} + +int +ble_audio_broadcast_sink_start(uint8_t source_id, const struct ble_audio_broadcast_sink_add_params *params) +{ + return broadcast_sink_start(source_id, + params->broadcast_code_is_valid ? params->broadcast_code : NULL, + NULL); +} + +static void +ble_audio_broadcast_sink_destroy(struct ble_audio_broadcast_sink *sink) +{ + os_error_t os_error; + + os_error = os_memblock_put(&ble_audio_broadcast_sink_pool, sink); + if (os_error != OS_OK) { + BLE_HS_LOG_ERROR("Failed to put memory block (os_error %d)\n", os_error); + return; + } + + SLIST_REMOVE(&ble_audio_broadcast_sink_list, sink, ble_audio_broadcast_sink, next); +} + +static int +pa_sync_term(struct ble_audio_broadcast_sink *sink) +{ + int rc; + + switch (sink->pa_sync_state) { + case PA_SYNC_STATE_ACTIVE: + rc = ble_gap_periodic_adv_sync_terminate(sink->pa_sync_handle); + if (rc != 0) { + BLE_HS_LOG_ERROR("adv sync terminate failed (%d)\n", rc); + return rc; + } + break; + + case PA_SYNC_STATE_PENDING_PAST: + rc = ble_gap_periodic_adv_sync_receive(sink->past_conn_handle, NULL, gap_event_handler, NULL); + if (rc != 0) { + BLE_HS_LOG_ERROR("adv sync receive cancel failed (%d)\n", rc); + return rc; + } + break; + + case PA_SYNC_STATE_PENDING_SYNC: { + struct ble_audio_scan_delegator_source_desc source_desc; + + rc = ble_audio_scan_delegator_source_desc_get(sink->source_id, &source_desc); + if (rc != 0) { + BLE_HS_LOG_ERROR("source desc get failed (%d)\n", rc); + return rc; + } + + rc = pa_sync_remove(&source_desc.addr, source_desc.adv_sid); + if (rc != 0) { + return rc; + } + break; + } + + default: + break; + } + + pa_sync_state_set(sink, PA_SYNC_STATE_IDLE); + + return 0; +} + +static int +big_sync_term(struct ble_audio_broadcast_sink *sink) +{ + int rc; + + switch (sink->big_sync_state) { + case BIG_SYNC_STATE_ACTIVE: + case BIG_SYNC_STATE_PENDING_SYNC: + rc = ble_iso_big_sync_terminate(sink->big_handle); + if (rc != 0) { + BLE_HS_LOG_ERROR("big sync terminate failed (%d)\n", rc); + return rc; + } + break; + + case BIG_SYNC_STATE_IDLE: + case BIG_SYNC_STATE_FAILED: + case BIG_SYNC_STATE_PENDING_CODE: + case BIG_SYNC_STATE_PENDING_BASE: + case BIG_SYNC_STATE_PENDING_BIG_INFO: + break; + } + + big_sync_state_set(sink, BIG_SYNC_STATE_IDLE); + + return 0; +} + +int +ble_audio_broadcast_sink_stop(uint8_t source_id) +{ + struct ble_audio_broadcast_sink *sink; + int rc; + + sink = broadcast_sink_get(source_id); + if (sink == NULL) { + BLE_HS_LOG_WARN("no sink with source_id=0x%02x\n", source_id); + return 0; + } + + rc = pa_sync_term(sink); + if (rc != 0) { + return rc; + } + + rc = big_sync_term(sink); + if (rc != 0) { + return rc; + } + + ble_audio_broadcast_sink_destroy(sink); + + return 0; +} + +int +ble_audio_broadcast_sink_metadata_update(uint8_t source_id, + const struct ble_audio_broadcast_sink_metadata_update_params *params) +{ + struct ble_audio_broadcast_sink *sink; + + sink = broadcast_sink_get(source_id); + if (sink == NULL) { + BLE_HS_LOG_WARN("no sink with source_id=0x%02x\n", source_id); + return 0; + } + + return ble_audio_scan_delegator_metadata_update(sink->source_id, params->subgroup_index, params->metadata, + params->metadata_length); +} + +int +ble_audio_broadcast_sink_config(uint8_t source_id, uint16_t conn_handle, + const struct ble_audio_scan_delegator_sync_opt *sync_opt) +{ + struct ble_audio_broadcast_sink *sink; + int rc; + + BLE_AUDIO_DBG_ASSERT(sync_opt != NULL); + + sink = broadcast_sink_get(source_id); + if (sink == NULL) { + if (sync_opt->pa_sync != BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_DO_NOT_SYNC) { + sink = broadcast_sink_new(source_id); + if (sink == NULL) { + return BLE_HS_ENOMEM; + } + } else { + /* nothing to do */ + return 0; + } + } + + if (sync_opt->pa_sync != BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_DO_NOT_SYNC) { + /* TODO: Skip if the BIS Sync is same */ + if (sink->num_subgroups != 0) { + rc = big_sync_term(sink); + if (rc != 0) { + return rc; + } + } + + sink->num_subgroups = sync_opt->num_subgroups; + + for (uint8_t subgroup_index = 0; subgroup_index < sink->num_subgroups; subgroup_index++) { + sink->subgroups[subgroup_index].bis_sync = sync_opt->subgroups[subgroup_index].bis_sync; + } + + if (sync_opt->pa_sync == BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_PAST_NOT_AVAILABLE) { + rc = broadcast_sink_start(source_id, NULL, NULL); + } else { + rc = broadcast_sink_start(source_id, NULL, &conn_handle); + } + } else { + rc = pa_sync_term(sink); + } + + return rc; +} + +int +ble_audio_broadcast_sink_init(void) +{ + static struct ble_gap_event_listener gap_event_listener; + int rc; + + /* Ensure this function only gets called by sysinit. */ + SYSINIT_ASSERT_ACTIVE(); + + rc = os_mempool_init(&ble_audio_broadcast_sink_pool, + MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK_MAX), + sizeof(struct ble_audio_broadcast_sink), + ble_audio_broadcast_sink_mem, + "ble_audio_broadcast_sink_pool"); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = ble_gap_event_listener_register(&gap_event_listener, gap_event_handler, NULL); + SYSINIT_PANIC_ASSERT(rc == 0); + + return 0; +} +#endif /* BLE_AUDIO_BROADCAST_SINK */ diff --git a/nimble/host/audio/src/ble_audio_broadcast_sink_priv.h b/nimble/host/audio/src/ble_audio_broadcast_sink_priv.h new file mode 100644 index 000000000..ad7990144 --- /dev/null +++ b/nimble/host/audio/src/ble_audio_broadcast_sink_priv.h @@ -0,0 +1,34 @@ +/* + * 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_BLE_AUDIO_BROADCAST_SINK_PRIV_ +#define H_BLE_AUDIO_BROADCAST_SINK_PRIV_ + +#include +#include "audio/ble_audio.h" +#include "audio/ble_audio_broadcast_sink.h" +#include "audio/ble_audio_scan_delegator.h" + +int ble_audio_broadcast_sink_config( + uint8_t source_id, uint16_t conn_handle, + const struct ble_audio_scan_delegator_sync_opt *sync_opt); +void ble_audio_broadcast_sink_code_set( + uint8_t source_id, const uint8_t broadcast_code[16]); + +#endif /* H_BLE_AUDIO_BROADCAST_SINK_PRIV_ */ diff --git a/nimble/host/audio/src/ble_audio_priv.h b/nimble/host/audio/src/ble_audio_priv.h index 10f0109dd..d575c5496 100644 --- a/nimble/host/audio/src/ble_audio_priv.h +++ b/nimble/host/audio/src/ble_audio_priv.h @@ -22,6 +22,17 @@ #include "audio/ble_audio.h" +#define MIN(_a, _b) ((_a) < (_b) ? (_a) : (_b)) +#define MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b)) + +#if MYNEWT_VAL(BLE_HS_DEBUG) +#define BLE_AUDIO_DBG_ASSERT(x) assert(x) +#define BLE_AUDIO_DBG_ASSERT_EVAL(x) assert(x) +#else +#define BLE_AUDIO_DBG_ASSERT(x) +#define BLE_AUDIO_DBG_ASSERT_EVAL(x) ((void)(x)) +#endif + int ble_audio_event_listener_call(struct ble_audio_event *event); #endif /* H_BLE_AUDIO_PRIV_ */ diff --git a/nimble/host/audio/src/ble_audio_scan_delegator.c b/nimble/host/audio/src/ble_audio_scan_delegator.c new file mode 100644 index 000000000..db580c639 --- /dev/null +++ b/nimble/host/audio/src/ble_audio_scan_delegator.c @@ -0,0 +1,460 @@ +/* + * 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. + */ + +#include "sysinit/sysinit.h" + +#if MYNEWT_VAL(BLE_AUDIO_SCAN_DELEGATOR) +#include "audio/ble_audio.h" +#include "audio/ble_audio_broadcast_sink.h" +#include "audio/ble_audio_scan_delegator.h" +#include "host/ble_hs.h" +#include "host/ble_uuid.h" + +#include "../services/bass/include/services/bass/ble_audio_svc_bass.h" + +#include "ble_audio_priv.h" +#include "ble_audio_broadcast_sink_priv.h" + +static ble_audio_scan_delegator_action_fn *action_cb; + +int +ble_audio_scan_delegator_source_desc_get(uint8_t source_id, struct ble_audio_scan_delegator_source_desc *source_desc) +{ + struct ble_svc_audio_bass_receiver_state *state; + int rc; + + rc = ble_svc_audio_bass_receiver_state_get(source_id, &state); + if (rc != 0) { + BLE_HS_LOG_ERROR("bass receiver state get failed (%d)\n", rc); + return rc; + } + + source_desc->addr = state->source_addr; + source_desc->adv_sid = state->source_adv_sid; + source_desc->broadcast_id = state->broadcast_id; + + return 0; +} + +int +ble_audio_scan_delegator_receive_state_get(uint8_t source_id, struct ble_audio_scan_delegator_receive_state *out_state) +{ + struct ble_svc_audio_bass_receiver_state *state; + int rc; + + rc = ble_svc_audio_bass_receiver_state_get(source_id, &state); + if (rc != 0) { + BLE_HS_LOG_ERROR("bass receiver state get failed (%d)\n", rc); + return rc; + } + + out_state->pa_sync_state = (uint8_t)state->pa_sync_state; + out_state->big_enc = (uint8_t)state->big_encryption; + if (out_state->big_enc == BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_BROADCAST_CODE_INVALID) { + memcpy(out_state->bad_code, state->bad_code, sizeof(out_state->bad_code)); + } + out_state->num_subgroups = state->num_subgroups; + for (uint8_t i = 0; i < out_state->num_subgroups; i++) { + out_state->subgroups[i].bis_sync = state->subgroups[i].bis_sync_state; + out_state->subgroups[i].metadata = state->subgroups[i].metadata; + out_state->subgroups[i].metadata_length = state->subgroups[i].metadata_length; + } + + return 0; +} + +int +ble_audio_scan_delegator_metadata_update(uint8_t source_id, uint8_t subgroup_index, const uint8_t *metadata, + uint8_t metadata_length) +{ + struct ble_svc_audio_bass_metadata_params params; + + params.subgroup_idx = subgroup_index; + params.metadata = metadata; + params.metadata_length = metadata_length; + + return ble_svc_audio_bass_update_metadata(¶ms, source_id); +} + +static int +action_call(struct ble_audio_scan_delegator_action *action, void *arg) +{ + int rc; + + if (action_cb == NULL) { + BLE_HS_LOG_ERROR("callback is NULL\n"); + return BLE_HS_EAPP; + } + + rc = action_cb(action, arg); + if (rc != 0) { + return rc; + } + + return 0; +} + +static void +source_desc_init(struct ble_audio_scan_delegator_source_desc *source_desc, const ble_addr_t *addr, uint8_t adv_sid, + uint32_t broadcast_id) +{ + source_desc->addr = *addr; + source_desc->adv_sid = adv_sid; + source_desc->broadcast_id = broadcast_id; +} + +static void +subgroups_init(struct ble_audio_scan_delegator_subgroup *subgroups, + const struct ble_svc_audio_bass_subgroup *bass_subgroups, uint8_t num_subgroups) +{ + BLE_AUDIO_DBG_ASSERT(num_subgroups < MYNEWT_VAL(BLE_AUDIO_SCAN_DELEGATOR_SUBGROUP_MAX)); + + for (uint8_t i = 0; i < num_subgroups; i++) { + subgroups[i].bis_sync = bass_subgroups[i].bis_sync_state; + subgroups[i].metadata_length = bass_subgroups[i].metadata_length; + subgroups[i].metadata = bass_subgroups[i].metadata; + } +} + +static void +sync_opt_init(struct ble_audio_scan_delegator_sync_opt *sync_opt, enum ble_svc_audio_bass_pa_sync pa_sync, + uint16_t pa_interval, const struct ble_svc_audio_bass_subgroup *bass_subgroups, uint8_t num_subgroups) +{ + sync_opt->pa_sync = (uint8_t)pa_sync; + sync_opt->pa_interval = pa_interval; + sync_opt->num_subgroups = num_subgroups; + if (sync_opt->num_subgroups > 0) { + subgroups_init(sync_opt->subgroups, bass_subgroups, num_subgroups); + } +} + +static int +bass_add_source_op_handler(struct ble_svc_audio_bass_operation *op, void *arg) +{ + struct ble_audio_scan_delegator_action action = {0}; + const uint8_t source_id = op->add_source.source_id; + int rc; + + source_desc_init(&action.source_add.source_desc, &op->add_source.adv_addr, + op->add_source.adv_sid, op->add_source.broadcast_id); + sync_opt_init(&action.source_add.sync_opt, op->add_source.pa_sync, op->add_source.pa_interval, + op->add_source.subgroups, op->add_source.num_subgroups); + + action.type = BLE_AUDIO_SCAN_DELEGATOR_ACTION_SOURCE_ADD; + action.source_add.source_id = source_id; + action.source_add.out_source_id_to_swap = op->add_source.out_source_id_to_swap; + + rc = action_call(&action, arg); + if (rc != 0) { + BLE_HS_LOG_DEBUG("API callback (%d)\n", rc); + return rc; + } + +#if MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK) + if (op->add_source.out_source_id_to_swap != NULL) { + rc = ble_audio_broadcast_sink_stop(*op->add_source.out_source_id_to_swap); + if (rc != 0) { + BLE_HS_LOG_WARN("sink stop failed (%d)\n", rc); + } + } + + rc = ble_audio_broadcast_sink_config(source_id, op->conn_handle, &action.source_add.sync_opt); + if (rc != 0) { + BLE_HS_LOG_WARN("sink config failed (%d)\n", rc); + } +#endif /* BLE_AUDIO_BROADCAST_SINK */ + + return 0; +} + +static int +bass_modify_source_op_handler(struct ble_svc_audio_bass_operation *op, void *arg) +{ + struct ble_audio_scan_delegator_action action = {0}; + struct ble_audio_scan_delegator_sync_opt *sync_opt; + const uint8_t source_id = op->modify_source.source_id; + int rc; + + sync_opt = &action.source_modify.sync_opt; + sync_opt_init(sync_opt, op->modify_source.pa_sync, op->modify_source.pa_interval, NULL, 0); + + BLE_AUDIO_DBG_ASSERT(sync_opt->num_subgroups < ARRAY_SIZE(subgroups)); + + for (uint8_t i = 0; i < sync_opt->num_subgroups; i++) { + sync_opt->subgroups[i].bis_sync = op->modify_source.bis_sync[i]; + /* FIXME: Missing metadata in Modify Source */ + } + + action.type = BLE_AUDIO_SCAN_DELEGATOR_ACTION_SOURCE_MODIFY; + action.source_modify.source_id = source_id; + + rc = action_call(&action, arg); + if (rc != 0) { + return rc; + } + +#if MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK) + rc = ble_audio_broadcast_sink_config(source_id, op->conn_handle, sync_opt); + if (rc != 0) { + BLE_HS_LOG_WARN("sink config failed (%d)\n", rc); + } +#endif /* BLE_AUDIO_BROADCAST_SINK */ + + return 0; +} + +static int +bass_remove_source_op_handler(struct ble_svc_audio_bass_operation *op, void *arg) +{ + struct ble_audio_scan_delegator_action action; + const uint8_t source_id = op->remove_source.source_id; + int rc; + + action.type = BLE_AUDIO_SCAN_DELEGATOR_ACTION_SOURCE_REMOVE; + action.source_remove.source_id = source_id; + + rc = action_call(&action, arg); + if (rc != 0) { + return rc; + } + +#if MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK) + rc = ble_audio_broadcast_sink_stop(source_id); + if (rc != 0) { + BLE_HS_LOG_WARN("sink stop failed (%d)\n", rc); + } +#endif /* BLE_AUDIO_BROADCAST_SINK */ + + return 0; +} + +static int +bass_accept_fn(struct ble_svc_audio_bass_operation *op, void *arg) +{ + switch (op->op) { + case BLE_SVC_AUDIO_BASS_OPERATION_ADD_SOURCE: + return bass_add_source_op_handler(op, arg); + + case BLE_SVC_AUDIO_BASS_OPERATION_MODIFY_SOURCE: + return bass_modify_source_op_handler(op, arg); + + case BLE_SVC_AUDIO_BASS_OPERATION_REMOVE_SOURCE: + return bass_remove_source_op_handler(op, arg); + + default: + return BLE_HS_ENOTSUP; + } +} + +int +ble_audio_scan_delegator_action_fn_set(ble_audio_scan_delegator_action_fn *fn, void *arg) +{ + int rc; + + if (fn == NULL) { + BLE_HS_LOG_ERROR("callback is NULL\n"); + return BLE_HS_EINVAL; + } + + if (action_cb != NULL) { + return BLE_HS_EALREADY; + } + + action_cb = fn; + + rc = ble_svc_audio_bass_accept_fn_set(bass_accept_fn, arg); + if (rc != 0) { + action_cb = NULL; + } + + return 0; +} + +int +ble_audio_scan_delegator_receive_state_add(const struct ble_audio_scan_delegator_receive_state_add_params *params, + uint8_t *source_id) +{ + struct ble_svc_audio_bass_receiver_state_add_params bass_params = {0}; + + if (params == NULL) { + BLE_HS_LOG_ERROR("NULL params\n"); + return BLE_HS_EINVAL; + } + + if (source_id == NULL) { + BLE_HS_LOG_ERROR("NULL source_id\n"); + return BLE_HS_EINVAL; + } + + bass_params.source_addr = params->source_desc.addr; + bass_params.source_adv_sid = params->source_desc.adv_sid; + bass_params.broadcast_id = params->source_desc.broadcast_id; + bass_params.pa_sync_state = (uint8_t)params->state.pa_sync_state; + bass_params.big_encryption = (uint8_t)params->state.big_enc; + + if (params->state.big_enc == BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_BROADCAST_CODE_INVALID) { + bass_params.bad_code = params->state.bad_code; + } else { + bass_params.bad_code = NULL; + } + + bass_params.num_subgroups = params->state.num_subgroups; + if (bass_params.num_subgroups > BLE_SVC_AUDIO_BASS_SUB_NUM_MAX) { + BLE_HS_LOG_ERROR("num_subgroups above the limit\n"); + return BLE_HS_ENOMEM; + } + + for (uint8_t i = 0; i < bass_params.num_subgroups; i++) { + bass_params.subgroups[i].bis_sync_state = params->state.subgroups->bis_sync; + bass_params.subgroups[i].metadata_length = params->state.subgroups->metadata_length; + bass_params.subgroups[i].metadata = params->state.subgroups->metadata; + } + + return ble_svc_audio_bass_receive_state_add(&bass_params, source_id); +} + +int +ble_audio_scan_delegator_receive_state_remove(uint8_t source_id) +{ + int rc; + +#if MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK) + rc = ble_audio_broadcast_sink_stop(source_id); + if (rc != 0) { + BLE_HS_LOG_WARN("sink stop failed (%d)\n", rc); + } +#endif /* BLE_AUDIO_BROADCAST_SINK */ + + return ble_svc_audio_bass_receive_state_remove(source_id); +} + +int +ble_audio_scan_delegator_receive_state_set(uint8_t source_id, + const struct ble_audio_scan_delegator_receive_state *state) +{ + struct ble_svc_audio_bass_update_params bass_params; + int rc; + + if (state == NULL) { + BLE_HS_LOG_ERROR("NULL state\n"); + return BLE_HS_EINVAL; + } + + bass_params.pa_sync_state = (uint8_t)state->pa_sync_state; + bass_params.big_encryption = (uint8_t)state->big_enc; + + if (state->big_enc == BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_BROADCAST_CODE_INVALID) { + bass_params.bad_code = state->bad_code; + } else { + bass_params.bad_code = NULL; + } + + bass_params.num_subgroups = state->num_subgroups; + if (bass_params.num_subgroups > BLE_SVC_AUDIO_BASS_SUB_NUM_MAX) { + BLE_HS_LOG_ERROR("num_subgroups above the limit\n"); + return BLE_HS_ENOMEM; + } + + for (uint8_t i = 0; i < bass_params.num_subgroups; i++) { + bass_params.subgroups[i].bis_sync_state = state->subgroups[i].bis_sync; + bass_params.subgroups[i].metadata_length = state->subgroups[i].metadata_length; + bass_params.subgroups[i].metadata = state->subgroups[i].metadata; + } + + rc = ble_svc_audio_bass_receive_state_update(&bass_params, source_id); + if (rc != 0) { + BLE_HS_LOG_ERROR("Failed to update receive state (rc %d)\n", rc); + } + + return 0; +} + +void +ble_audio_scan_delegator_receive_state_foreach(ble_audio_scan_delegator_receive_state_foreach_fn *fn, void *arg) +{ + struct ble_audio_scan_delegator_receive_state_entry entry; + int rc; + + if (fn == NULL) { + BLE_HS_LOG_ERROR("callback is NULL\n"); + return; + } + + for (int i = 0; i < MYNEWT_VAL(BLE_AUDIO_SCAN_DELEGATOR_RECEIVE_STATE_MAX); i++) { + struct ble_svc_audio_bass_receiver_state *state; + uint8_t source_id; + + rc = ble_svc_audio_bass_source_id_get(i, &source_id); + if (rc != 0) { + continue; + } + + rc = ble_svc_audio_bass_receiver_state_get(source_id, &state); + if (rc != 0) { + BLE_HS_LOG_ERROR("Failed to get receiver state (rc %d)\n", rc); + continue; + } + + entry.source_id = source_id; + source_desc_init(&entry.source_desc, &state->source_addr, state->source_adv_sid, state->source_adv_sid); + entry.state.pa_sync_state = (uint8_t)state->pa_sync_state; + entry.state.big_enc = (uint8_t)state->big_encryption; + if (entry.state.big_enc == BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_BROADCAST_CODE_INVALID) { + memcpy(entry.state.bad_code, state->bad_code, sizeof(entry.state.bad_code)); + } + entry.state.num_subgroups = state->num_subgroups; + subgroups_init(entry.state.subgroups, state->subgroups, state->num_subgroups); + + if (fn(&entry, arg) != 0) { + break; + } + } +} + +#if MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK) +static int +audio_event_handler(struct ble_audio_event *event, void *arg) +{ + if (event->type == BLE_AUDIO_EVENT_BASS_BROADCAST_CODE_SET) { + ble_audio_broadcast_sink_code_set(event->bass_set_broadcast_code.source_id, + event->bass_set_broadcast_code.broadcast_code); + } + + return 0; +} +#endif /* BLE_AUDIO_BROADCAST_SINK */ + +int +ble_audio_scan_delegator_init(void) +{ + int rc; + + /* Ensure this function only gets called by sysinit. */ + SYSINIT_ASSERT_ACTIVE(); + +#if MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK) + static struct ble_audio_event_listener listener; + + rc = ble_audio_event_listener_register(&listener, audio_event_handler, NULL); + SYSINIT_PANIC_ASSERT(rc == 0); +#endif /* BLE_AUDIO_BROADCAST_SINK */ + + return rc; +} +#endif /* BLE_AUDIO_SCAN_DELEGATOR */ diff --git a/nimble/host/audio/src/ble_audio_scan_delegator_priv.h b/nimble/host/audio/src/ble_audio_scan_delegator_priv.h new file mode 100644 index 000000000..ae5fc6510 --- /dev/null +++ b/nimble/host/audio/src/ble_audio_scan_delegator_priv.h @@ -0,0 +1,33 @@ +/* + * 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_BLE_AUDIO_SCAN_DELEGATOR_PRIV_ +#define H_BLE_AUDIO_SCAN_DELEGATOR_PRIV_ + +#include +#include "audio/ble_audio.h" +#include "audio/ble_audio_scan_delegator.h" + +int ble_audio_scan_delegator_source_desc_get( + uint8_t source_id, struct ble_audio_scan_delegator_source_desc *source_desc); +int ble_audio_scan_delegator_metadata_update( + uint8_t source_id, uint8_t subgroup_index, const uint8_t *metadata, + uint8_t metadata_length); + +#endif /* H_BLE_AUDIO_SCAN_DELEGATOR_PRIV_ */ diff --git a/nimble/host/audio/syscfg.yml b/nimble/host/audio/syscfg.yml index 5f0269e0a..a5c8907f6 100644 --- a/nimble/host/audio/syscfg.yml +++ b/nimble/host/audio/syscfg.yml @@ -16,10 +16,88 @@ # under the License. # +# syscfg.defs section syscfg.defs: BLE_AUDIO_MAX_CODEC_RECORDS: description: > Maximum number of registered audio codecs. value: 0 -syscfg.logs: + BLE_AUDIO_BROADCAST_SINK: + description: > + This option enables BLE Audio Sink support. + value: 0 + restrictions: + - '(BLE_ISO_BROADCAST_SINK > 0) if 1' + + BLE_AUDIO_SCAN_DELEGATOR: + description: > + This option enables BLE Audio Scan Delegator support. + value: 0 + +syscfg.defs.BLE_AUDIO_BROADCAST_SINK: + BLE_AUDIO_BROADCAST_SINK_SYSINIT_STAGE: + description: > + Primary sysinit stage for BLE Audio Broadcast Sink. + value: 500 + + BLE_AUDIO_BROADCAST_SINK_LOG_MOD: + description: 'Numeric module ID to use for BLE Audio Broadcast Sink log messages.' + value: 28 + + BLE_AUDIO_BROADCAST_SINK_LOG_LVL: + description: 'Minimum level for the BLE Audio Broadcast Sink log log.' + value: 1 + + BLE_AUDIO_BROADCAST_SINK_MAX: + description: > + Maximum umber of Audio Broadcast Sink instances. + value: 'MYNEWT_VAL_BLE_ISO_MAX_BIGS' + +syscfg.defs.BLE_AUDIO_SCAN_DELEGATOR: + BLE_AUDIO_SCAN_DELEGATOR_SYSINIT_STAGE: + description: > + Primary sysinit stage for BLE Audio Scan Delegator. + value: 499 + + BLE_AUDIO_SCAN_DELEGATOR_LOG_MOD: + description: 'Numeric module ID to use for BLE Audio Scan Delegator log messages.' + value: 29 + + BLE_AUDIO_SCAN_DELEGATOR_LOG_LVL: + description: 'Minimum level for the BLE Audio Scan Delegator log log.' + value: 1 + + BLE_AUDIO_SCAN_DELEGATOR_RECEIVE_STATE_MAX: + description: > + Maximum number of Receive State instances. + value: MYNEWT_VAL(BLE_SVC_AUDIO_BASS_RECEIVE_STATE_MAX) + + BLE_AUDIO_SCAN_DELEGATOR_SUBGROUP_MAX: + description: > + Maximum number of Subgroups per Receive State. + value: MYNEWT_VAL(BLE_SVC_AUDIO_BASS_SUB_NUM_MAX) + + BLE_AUDIO_SCAN_DELEGATOR_STANDALONE: + description: > + This option enables the BLE Audio Scan Delegator as standalone device. + value: 1 + restrictions: + - '(BLE_AUDIO_BROADCAST_SINK == 0) if 1' + +# syscfg.logs section +syscfg.logs.BLE_AUDIO_BROADCAST_SINK: + BLE_AUDIO_BROADCAST_SINK_LOG: + module: MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK_LOG_MOD) + level: MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK_LOG_LVL) + +syscfg.logs.BLE_AUDIO_SCAN_DELEGATOR: + BLE_AUDIO_SCAN_DELEGATOR_LOG: + module: MYNEWT_VAL(BLE_AUDIO_SCAN_DELEGATOR_LOG_MOD) + level: MYNEWT_VAL(BLE_AUDIO_SCAN_DELEGATOR_LOG_LVL) + +# syscfg.vals section +syscfg.vals.BLE_AUDIO_BROADCAST_SINK: + BLE_AUDIO_SCAN_DELEGATOR: 1 + BLE_AUDIO_SCAN_DELEGATOR_STANDALONE: 0 + BLE_AUDIO_MAX_CODEC_RECORDS: 2 diff --git a/nimble/host/audio/targets/btshell_native/pkg.yml b/nimble/host/audio/targets/btshell_native/pkg.yml index 67eb2d6fe..7c129c62f 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 e9db00707..3c04c0c12 100644 --- a/nimble/host/audio/targets/btshell_native/syscfg.yml +++ b/nimble/host/audio/targets/btshell_native/syscfg.yml @@ -30,7 +30,7 @@ syscfg.vals: # Disable security manager (pairing and bonding). BLE_SM_LEGACY: 0 - BLE_SM_SC: 0 + BLE_SM_SC: 1 # Default task settings OS_MAIN_STACK_SIZE: 512 @@ -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