diff --git a/apps/btshell/src/btshell.h b/apps/btshell/src/btshell.h index 021f54c88..17b07458a 100644 --- a/apps/btshell/src/btshell.h +++ b/apps/btshell/src/btshell.h @@ -28,6 +28,9 @@ #include "host/ble_gatt.h" #include "host/ble_gap.h" +#if (MYNEWT_VAL(BLE_ISO_BROADCASTER)) +#include "host/ble_audio_broadcast.h" +#endif #ifdef __cplusplus extern "C" { @@ -182,6 +185,36 @@ int btshell_l2cap_send(uint16_t conn, uint16_t idx, uint16_t bytes); int btshell_l2cap_reconfig(uint16_t conn_handle, uint16_t mtu, uint8_t num, uint8_t idxs[]); +#if (MYNEWT_VAL(BLE_ISO_BROADCASTER)) +int btshell_broadcast_base_add(uint8_t adv_instance, uint32_t presentation_delay); +int btshell_broadcast_big_sub_add(uint8_t adv_instance, + uint8_t codec_fmt, + uint16_t company_id, + uint16_t vendor_spec, + uint8_t *metadata, + unsigned int metadata_len, + uint8_t *codec_spec_cfg, + unsigned int codec_spec_cfg_len); +int btshell_broadcast_bis_add(uint8_t adv_instance, + uint8_t *codec_spec_cfg, + unsigned int codec_spec_cfg_len); +int btshell_broadcast_create(uint8_t adv_instance, + struct ble_gap_ext_adv_params *ext_params, + struct ble_gap_periodic_adv_params + *per_params, + const char *name, + struct ble_iso_big_params big_params, + uint8_t *extra_data, + unsigned int extra_data_len); +int btshell_broadcast_destroy(uint8_t adv_instance); +int btshell_broadcast_update(uint8_t adv_instance, + const char *name, + uint8_t *extra_data, + unsigned int extra_data_len); +int btshell_broadcast_start(uint8_t adv_instance); +int btshell_broadcast_stop(uint8_t adv_instance); +#endif + 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 1bdd48e89..bba5022df 100644 --- a/apps/btshell/src/cmd.c +++ b/apps/btshell/src/cmd.c @@ -42,6 +42,7 @@ #include "btshell.h" #include "cmd_gatt.h" #include "cmd_l2cap.h" +#include "cmd_leaudio.h" #define BTSHELL_MODULE "btshell" @@ -4168,6 +4169,140 @@ static const struct shell_cmd_help sync_stats_help = { #endif #endif +#if MYNEWT_VAL(BLE_ISO_BROADCASTER) +#if MYNEWT_VAL(SHELL_CMD_HELP) +static const struct shell_param leaudio_base_add_params[] = { + {"adv_instance", "Advertising instance, usage: ="}, + { "presentation_delay", "usage: =" }, + + { NULL, NULL} +}; + +static const struct shell_cmd_help leaudio_base_add_help = { + .summary = "Add BASE configuration for broadcast", + .usage = NULL, + .params = leaudio_base_add_params, +}; + +static const struct shell_param leaudio_big_sub_add_params[] = { + {"adv_instance", "Advertising instance, usage: ="}, + { "codec_fmt", "usage: " }, + { "company_id", "usage: =" }, + { "vendor_spec", "usage: =" }, + { "codec_spec_config", "usage: =[XX:XX...]" }, + { "metadata", "usage: =[XX:XX...]" }, + + { NULL, NULL} +}; + +static const struct shell_cmd_help leaudio_big_sub_add_help = { + .summary = "Add BIG subgroup configuration for broadcast", + .usage = NULL, + .params = leaudio_big_sub_add_params, +}; + +static const struct shell_param leaudio_bis_add_params[] = { + {"adv_instance", "Advertising instance, usage: ="}, + { "codec_spec_config", "usage: =[XX:XX...]" }, + + { NULL, NULL} +}; + +static const struct shell_cmd_help leaudio_bis_add_help = { + .summary = "Add BIS configuration for broadcast announcements to last " + "added BIG subgroup", + .usage = NULL, + .params = leaudio_bis_add_params, +}; + +static const struct shell_param leaudio_broadcast_create_params[] = { + {"adv_instance", "Advertising instance, usage: ="}, + {"ext_interval_min", "usage: =[0-UINT16_MAX], default: 0"}, + {"ext_interval_max", "usage: =[0-UINT16_MAX], default: 0"}, + {"per_interval_min", "usage: =[0-UINT16_MAX], default: 0"}, + {"per_interval_max", "usage: =[0-UINT16_MAX], default: 0"}, + + {"name", "usage: =[string]"}, + + {"sdu_interval", "SDU interval, in us, usage: ="}, + {"max_sdu", "max SDU size, in octets, usage: ="}, + {"max_latency", "max transport latency, in ms, usage: ="}, + {"rtn", "RTN, usage: ="}, + {"phy", "PHY, usage: ="}, + {"packing", "packing, optional, true if not given, usage: ="}, + {"framing", "framing, optional, false if not given, usage: ="}, + {"encryption", "optional, encryption, usage: ="}, + {"broadcast_code", "optional, obligatory if encryption is enabled, " + "usage: =, len=16 octets"}, + {"extra_data", "usage: =[XX:XX...]"}, + + { NULL, NULL} +}; + +static const struct shell_cmd_help leaudio_broadcast_create_help = { + .summary = "Add BIS configuration for broadcast to last added BIG " + "subgroup", + .usage = NULL, + .params = leaudio_broadcast_create_params, +}; + +static const struct shell_param leaudio_broadcast_destroy_params[] = { + {"adv_instance", "Advertising instance, usage: ="}, + + { NULL, NULL} +}; + +static const struct shell_cmd_help leaudio_broadcast_destroy_help = { + .summary = "Destroy BASE", + .usage = NULL, + + .params = leaudio_broadcast_destroy_params, +}; + +static const struct shell_param leaudio_broadcast_update_params[] = { + {"adv_instance", "Advertising instance, usage: ="}, + {"name", "usage: =[string]"}, + {"extra_data_len", "usage: ="}, + {"extra_data", "usage: =[XX:XX...]"}, + + { NULL, NULL} +}; + +static const struct shell_cmd_help leaudio_broadcast_update_help = { + .summary = "Update broadcast", + .usage = NULL, + + .params = leaudio_broadcast_update_params, +}; + +static const struct shell_param leaudio_broadcast_start_params[] = { + {"adv_instance", "Advertising instance, usage: ="}, + + { NULL, NULL} +}; + +static const struct shell_cmd_help leaudio_broadcast_start_help = { + .summary = "Start broadcast", + .usage = NULL, + + .params = leaudio_broadcast_start_params, +}; + +static const struct shell_param leaudio_broadcast_stop_params[] = { + {"adv_instance", "Advertising instance, usage: ="}, + + { NULL, NULL} +}; + +static const struct shell_cmd_help leaudio_broadcast_stop_help = { + .summary = "Stop broadcast", + .usage = NULL, + + .params = leaudio_broadcast_stop_params, +}; +#endif +#endif + static const struct shell_cmd btshell_commands[] = { #if MYNEWT_VAL(BLE_EXT_ADV) { @@ -4664,6 +4799,64 @@ static const struct shell_cmd btshell_commands[] = { #endif }, #endif +#endif +#if MYNEWT_VAL(BLE_ISO) + { + .sc_cmd = "base_add", + .sc_cmd_func = cmd_leaudio_base_add, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &leaudio_base_add_help, +#endif + }, + { + .sc_cmd = "big_sub_add", + .sc_cmd_func = cmd_leaudio_big_sub_add, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &leaudio_big_sub_add_help, +#endif + }, + { + .sc_cmd = "bis_add", + .sc_cmd_func = cmd_leaudio_bis_add, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &leaudio_bis_add_help, +#endif + }, + { + .sc_cmd = "broadcast_create", + .sc_cmd_func = cmd_leaudio_broadcast_create, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &leaudio_broadcast_create_help, +#endif + }, + { + .sc_cmd = "broadcast_destroy", + .sc_cmd_func = cmd_leaudio_broadcast_destroy, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &leaudio_broadcast_destroy_help, +#endif + }, + { + .sc_cmd = "broadcast_update", + .sc_cmd_func = cmd_leaudio_broadcast_update, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &leaudio_broadcast_update_help, +#endif + }, + { + .sc_cmd = "broadcast_start", + .sc_cmd_func = cmd_leaudio_broadcast_start, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &leaudio_broadcast_start_help, +#endif + }, + { + .sc_cmd = "broadcast_stop", + .sc_cmd_func = cmd_leaudio_broadcast_stop, +#if MYNEWT_VAL(SHELL_CMD_HELP) + .help = &leaudio_broadcast_stop_help, +#endif + }, #endif { 0 }, }; diff --git a/apps/btshell/src/cmd_leaudio.c b/apps/btshell/src/cmd_leaudio.c new file mode 100644 index 000000000..bdbc82584 --- /dev/null +++ b/apps/btshell/src/cmd_leaudio.c @@ -0,0 +1,385 @@ +/* + * 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 "host/ble_audio_broadcast.h" +#include "cmd_leaudio.h" +#include "btshell.h" +#include "console/console.h" +#include "errno.h" + +#if (MYNEWT_VAL(BLE_ISO_BROADCASTER)) +int +cmd_leaudio_base_add(int argc, char **argv) +{ + uint32_t presentation_delay; + uint8_t adv_instance; + int rc; + + rc = parse_arg_init(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + adv_instance = parse_arg_uint8("adv_instance", &rc); + if (rc != 0 || adv_instance >= BLE_ADV_INSTANCES) { + console_printf("invalid advertising instance\n"); + return rc; + } + + presentation_delay = parse_arg_uint32("presentation_delay", &rc); + if (rc != 0) { + return rc; + } + + return btshell_broadcast_base_add(adv_instance, presentation_delay); +} + +int +cmd_leaudio_big_sub_add(int argc, char **argv) +{ + uint8_t adv_instance; + uint8_t codec_fmt; + uint16_t company_id; + uint16_t vendor_spec; + static uint8_t metadata[CMD_ADV_DATA_METADATA_MAX_SZ]; + unsigned int metadata_len; + static uint8_t codec_spec_cfg[CMD_ADV_DATA_CODEC_SPEC_CFG_MAX_SZ]; + unsigned int codec_spec_cfg_len; + int rc; + + rc = parse_arg_init(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + adv_instance = parse_arg_uint8("adv_instance", &rc); + if (rc != 0 || adv_instance >= BLE_ADV_INSTANCES) { + console_printf("invalid advertising instance\n"); + return rc; + } + + codec_fmt = parse_arg_uint8("codec_fmt", &rc); + if (rc != 0) { + return rc; + } + + company_id = parse_arg_uint16("company_id", &rc); + if (rc != 0) { + return rc; + } + + vendor_spec = parse_arg_uint16("vendor_spec", &rc); + if (rc != 0) { + return rc; + } + + rc = parse_arg_byte_stream("codec_spec_config", + CMD_ADV_DATA_CODEC_SPEC_CFG_MAX_SZ, + codec_spec_cfg, &codec_spec_cfg_len); + if (rc != 0 && rc != ENOENT) { + return rc; + } + + rc = parse_arg_byte_stream("metadata", CMD_ADV_DATA_METADATA_MAX_SZ, + metadata, &metadata_len); + if (rc != 0 && rc != ENOENT) { + return rc; + } + + return btshell_broadcast_big_sub_add(adv_instance, + codec_fmt, company_id, + vendor_spec, + metadata, metadata_len, + codec_spec_cfg, codec_spec_cfg_len); +} + +int +cmd_leaudio_bis_add(int argc, char **argv) +{ + uint8_t adv_instance; + static uint8_t codec_spec_cfg[CMD_ADV_DATA_CODEC_SPEC_CFG_MAX_SZ]; + unsigned int codec_spec_cfg_len; + int rc; + + rc = parse_arg_init(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + adv_instance = parse_arg_uint8("adv_instance", &rc); + if (rc != 0 || adv_instance >= BLE_ADV_INSTANCES) { + console_printf("invalid advertising instance\n"); + return rc; + } + + rc = parse_arg_byte_stream("codec_spec_config", + CMD_ADV_DATA_CODEC_SPEC_CFG_MAX_SZ, + codec_spec_cfg, &codec_spec_cfg_len); + if (rc != 0) { + return rc; + } + + return btshell_broadcast_bis_add(adv_instance, codec_spec_cfg, + codec_spec_cfg_len); +} + +int +cmd_leaudio_broadcast_create(int argc, char **argv) +{ + struct ble_iso_big_params big_params; + uint8_t adv_instance; + const char *name; + uint8_t extra_data[CMD_ADV_DATA_EXTRA_MAX_SZ]; + static uint8_t own_addr_type; + unsigned int extra_data_len; + struct ble_gap_periodic_adv_params periodic_params; + struct ble_gap_ext_adv_params extended_params = { + .scannable = 0, + .connectable = 0, + .primary_phy = BLE_HCI_LE_PHY_1M, + }; + int rc; + + rc = ble_hs_id_infer_auto(0, &own_addr_type); + if (rc != 0) { + return rc; + } + + extended_params.own_addr_type = own_addr_type; + + rc = parse_arg_init(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + adv_instance = parse_arg_uint8("adv_instance", &rc); + if (rc != 0 || adv_instance >= BLE_ADV_INSTANCES) { + console_printf("invalid advertising instance\n"); + return rc; + } + + extended_params.sid = adv_instance; + extended_params.itvl_min = parse_arg_uint8_dflt("ext_interval_min", + 0, &rc); + if (rc != 0 && rc != ENOENT) { + console_printf("invalid extended advertising interval (min)\n"); + return rc; + } + + extended_params.itvl_max = parse_arg_uint8_dflt("ext_interval_max", 0, + &rc); + if (rc != 0 && rc != ENOENT) { + console_printf("invalid extended advertising interval (max)\n"); + return rc; + } + + periodic_params.itvl_min = parse_arg_uint8_dflt("per_interval_min", 0, + &rc); + if (rc != 0 && rc != ENOENT) { + console_printf("invalid periodic advertising interval (min)\n"); + return rc; + } + + periodic_params.itvl_max = parse_arg_uint8_dflt("per_interval_max", 0, + &rc); + if (rc != 0 && rc != ENOENT) { + console_printf("invalid periodic advertising interval (max)\n"); + return rc; + } + + name = parse_arg_extract("name"); + + big_params.sdu_interval = parse_arg_uint32_bounds("sdu_interval", + 0x0000FF, 0x0FFFFF, + &rc); + if (rc != 0) { + console_printf("invalid SDU interval\n"); + return rc; + } + + big_params.max_sdu = parse_arg_uint16_bounds("max_sdu", 0x0001, 0x0FFF, + &rc); + if (rc != 0) { + console_printf("invalid max SDU size\n"); + return rc; + } + + big_params.max_transport_latency = parse_arg_uint16_bounds("max_latency", + 0x0005, 0x0FA0, + &rc); + if (rc != 0) { + console_printf("invalid max transport latency\n"); + return rc; + } + + big_params.rtn = parse_arg_uint8_bounds("rtn", 0x00, 0x1E, &rc); + if (rc != 0) { + console_printf("invalid RTN\n"); + return rc; + } + + big_params.phy = parse_arg_uint8_bounds("phy", 0, 2, &rc); + if (rc != 0) { + console_printf("invalid PHY\n"); + return rc; + } + + extended_params.secondary_phy = big_params.phy; + + big_params.packing = parse_arg_uint8_bounds_dflt("packing", 0, 1, 1, &rc); + if (rc != 0) { + console_printf("invalid packing\n"); + return rc; + } + + big_params.framing = parse_arg_uint8_bounds_dflt("framing", 0, 1, 0, &rc); + if (rc != 0) { + console_printf("invalid framing\n"); + return rc; + } + + big_params.encryption = parse_arg_uint8_bounds_dflt("encryption", 0, 1, 0, &rc); + if (rc != 0) { + console_printf("invalid encryption\n"); + return rc; + } + + if (big_params.encryption) { + big_params.broadcast_code = parse_arg_extract("broadcast_code"); + if (big_params.broadcast_code == NULL) { + console_printf("broadcast code missing\n"); + return ENOENT; + } + + if (strlen(big_params.broadcast_code) > 16) { + console_printf("broadcast code too long\n"); + return ENOENT; + } + } + + rc = parse_arg_byte_stream("extra_data", + CMD_ADV_DATA_EXTRA_MAX_SZ, + extra_data, &extra_data_len); + if (rc == ENOENT) { + extra_data_len = 0; + } else if (rc != 0) { + return rc; + } + + return btshell_broadcast_create(adv_instance, + &extended_params, + &periodic_params, + name, + big_params, + extra_data, + extra_data_len); +} + +int +cmd_leaudio_broadcast_destroy(int argc, char **argv) +{ + uint8_t adv_instance; + int rc; + + rc = parse_arg_init(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + adv_instance = parse_arg_uint8("adv_instance", &rc); + if (rc != 0 || adv_instance >= BLE_ADV_INSTANCES) { + console_printf("invalid advertising instance\n"); + return rc; + } + + return btshell_broadcast_destroy(adv_instance); +} + +int +cmd_leaudio_broadcast_update(int argc, char **argv) +{ + uint8_t adv_instance; + uint8_t extra_data[CMD_ADV_DATA_EXTRA_MAX_SZ]; + unsigned int extra_data_len; + const char *name; + int rc; + + rc = parse_arg_init(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + adv_instance = parse_arg_uint8("adv_instance", &rc); + if (rc != 0 || adv_instance >= BLE_ADV_INSTANCES) { + console_printf("invalid advertising instance\n"); + return rc; + } + + rc = parse_arg_byte_stream("extra_data", + CMD_ADV_DATA_EXTRA_MAX_SZ, + extra_data, &extra_data_len); + if (rc != 0 && rc != ENOENT) { + return rc; + } + + name = parse_arg_extract("name"); + + return btshell_broadcast_update(adv_instance, name, extra_data, extra_data_len); +} + +int +cmd_leaudio_broadcast_start(int argc, char **argv) +{ + uint8_t adv_instance; + int rc; + + rc = parse_arg_init(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + adv_instance = parse_arg_uint8("adv_instance", &rc); + if (rc != 0 || adv_instance >= BLE_ADV_INSTANCES) { + console_printf("invalid advertising instance\n"); + return rc; + } + + return btshell_broadcast_start(adv_instance); +} + +int +cmd_leaudio_broadcast_stop(int argc, char **argv) +{ + uint8_t adv_instance; + int rc; + + rc = parse_arg_init(argc - 1, argv + 1); + if (rc != 0) { + return rc; + } + + adv_instance = parse_arg_uint8("adv_instance", &rc); + if (rc != 0 || adv_instance >= BLE_ADV_INSTANCES) { + console_printf("invalid advertising instance\n"); + return rc; + } + + return btshell_broadcast_stop(adv_instance); +} +#endif diff --git a/apps/btshell/src/cmd_leaudio.h b/apps/btshell/src/cmd_leaudio.h new file mode 100644 index 000000000..84fc45862 --- /dev/null +++ b/apps/btshell/src/cmd_leaudio.h @@ -0,0 +1,46 @@ +/* + * 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_CMD_LEAUDIO_ +#define H_CMD_LEAUDIO_ + +#include "cmd.h" + +#define CMD_ADV_DATA_CODEC_SPEC_CFG_MAX_SZ (9) +/** + * Maximum Metadata size is maximum adv size minus minimum size of other + * fields in BASE advertising + */ +#define CMD_ADV_DATA_METADATA_MAX_SZ (MYNEWT_VAL(BLE_EXT_ADV_MAX_SIZE) - 27) +/** + * Maximum size of extra data included in BASE advertising. Assumes minimum + * size of other fields. + */ +#define CMD_ADV_DATA_EXTRA_MAX_SZ (MYNEWT_VAL(BLE_EXT_ADV_MAX_SIZE) - 27) + +int cmd_leaudio_base_add(int argc, char **argv); +int cmd_leaudio_big_sub_add(int argc, char **argv); +int cmd_leaudio_bis_add(int argc, char **argv); +int cmd_leaudio_broadcast_create(int argc, char **argv); +int cmd_leaudio_broadcast_destroy(int argc, char **argv); +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); + +#endif /* H_CMD_LEAUDIO_ */ diff --git a/apps/btshell/src/main.c b/apps/btshell/src/main.c index aaa423ea8..8224f42a5 100644 --- a/apps/btshell/src/main.c +++ b/apps/btshell/src/main.c @@ -41,6 +41,8 @@ #include "host/ble_gatt.h" #include "host/ble_store.h" #include "host/ble_sm.h" +#include "host/ble_audio_common.h" +#include "host/ble_audio_broadcast.h" #include "host/util/util.h" /* Mandatory services. */ @@ -123,6 +125,57 @@ int btshell_full_disc_prev_chr_val; struct ble_sm_sc_oob_data oob_data_local; struct ble_sm_sc_oob_data oob_data_remote; +#if MYNEWT_VAL(BLE_ISO_BROADCASTER) +static struct {struct ble_audio_base *base; uint8_t adv_instance;} +btshell_base_list[MYNEWT_VAL(BLE_MAX_BIG)]; + +static os_membuf_t btshell_base_mem[ + OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_MAX_BIG), + sizeof(struct ble_audio_base)) +]; +static struct os_mempool btshell_base_pool; + +static os_membuf_t btshell_big_params_mem[ + OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_MAX_BIG), + sizeof(struct ble_iso_big_params)) +]; +static struct os_mempool btshell_big_params_pool; + +/** Mempool size: in worst case every BIS is in separate subgroup */ +static os_membuf_t btshell_big_sub_mem[ + OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_MAX_BIS), + sizeof(struct ble_audio_big_subgroup)) +]; +static struct os_mempool btshell_big_sub_pool; + +static os_membuf_t btshell_bis_mem[ + OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_MAX_BIS), + sizeof(struct ble_audio_bis)) +]; +static struct os_mempool btshell_bis_pool; + +static os_membuf_t btshell_metadata_mem[ + OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_MAX_BIS), + MYNEWT_VAL(BLE_EXT_ADV_MAX_SIZE) - 27) +]; +static struct os_mempool btshell_metadata_pool; + +/** + * Mempool size: theoretically it is possible that every BIG Subgroup has + * codec specific configuration, every BIS is in separate subgroup and also + * has one. This is inefficient but possible and should not cause error if + * used that way */ +static os_membuf_t btshell_codec_spec_mem[ + OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_MAX_BIS) * 2, 9) +]; +static struct os_mempool btshell_codec_spec_pool; + +static os_membuf_t btshell_big_params_mem[ + OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_MAX_BIG), sizeof(struct ble_iso_big_params)) +]; +static struct os_mempool btshell_big_params_pool; +#endif + #define XSTR(s) STR(s) #ifndef STR #define STR(s) #s @@ -2618,6 +2671,276 @@ btshell_get_default_own_addr_type(void) return default_own_addr_type; } +#if (MYNEWT_VAL(BLE_ISO_BROADCASTER)) +static int +btshell_base_find_free(void) +{ + int i; + for (i = 0; i < MYNEWT_VAL(BLE_MAX_BIG); i++) { + if (btshell_base_list[i].base == NULL) { + return i; + } + } + + return -ENOMEM; +} + +static struct ble_audio_base * +btshell_base_find(uint8_t adv_instance) +{ + int i; + for (i = 0; i < MYNEWT_VAL(BLE_MAX_BIG); i++) { + if (btshell_base_list[i].adv_instance == adv_instance) { + return btshell_base_list[i].base; + } + } + + return NULL; +} + +static int +btshell_broadcast_destroy_fn(struct ble_audio_base *base, void *args) +{ + struct ble_iso_big_params *big_params = + (struct ble_iso_big_params *)args; + struct ble_audio_big_subgroup *big_sub; + struct ble_audio_bis *bis; + + STAILQ_FOREACH(big_sub, &base->subs, next) { + STAILQ_FOREACH(bis, &big_sub->bises, next) { + os_memblock_put(&btshell_bis_pool, bis); + os_memblock_put(&btshell_codec_spec_pool, bis->codec_spec_config); + STAILQ_REMOVE(&big_sub->bises, bis, ble_audio_bis, next); + } + os_memblock_put(&btshell_big_sub_pool, big_sub); + os_memblock_put(&btshell_codec_spec_pool, big_sub->codec_spec_config); + os_memblock_put(&btshell_metadata_pool, big_sub->metadata); + STAILQ_REMOVE(&base->subs, big_sub, ble_audio_big_subgroup, next); + } + + os_memblock_put(&btshell_big_params_pool, big_params); + + return 0; +} + +int +btshell_broadcast_base_add(uint8_t adv_instance, uint32_t presentation_delay) +{ + struct ble_audio_base *base; + int free_base_idx; + + base = os_memblock_get(&btshell_base_pool); + if (!base) { + return ENOMEM; + } + + free_base_idx = btshell_base_find_free(); + if (free_base_idx < 0) { + return ENOMEM; + } + + base->presentation_delay = presentation_delay; + + btshell_base_list[free_base_idx].base = base; + btshell_base_list[free_base_idx].adv_instance = adv_instance; + + return 0; +} + +int +btshell_broadcast_big_sub_add(uint8_t adv_instance, + uint8_t codec_fmt, uint16_t company_id, + uint16_t vendor_spec, + uint8_t *metadata, + unsigned int metadata_len, + uint8_t *codec_spec_cfg, + unsigned int codec_spec_cfg_len) +{ + struct ble_audio_big_subgroup *big_sub; + struct ble_audio_base *base; + uint8_t *new_metadata = NULL; + uint8_t *new_codec_spec_cfg = NULL; + + big_sub = os_memblock_get(&btshell_big_sub_pool); + if (!big_sub) { + return ENOMEM; + } + + base = btshell_base_find(adv_instance); + if (!base) { + os_memblock_put(&btshell_big_sub_pool, big_sub); + return ENOENT; + } + + if (metadata_len > 0) { + new_metadata = os_memblock_get(&btshell_metadata_pool); + if (!new_metadata) { + os_memblock_put(&btshell_big_sub_pool, big_sub); + return ENOMEM; + } + memcpy(new_metadata, metadata, metadata_len); + } + + if (codec_spec_cfg_len > 0) { + new_codec_spec_cfg = os_memblock_get(&btshell_codec_spec_pool); + if (!new_codec_spec_cfg) { + os_memblock_put(&btshell_big_sub_pool, big_sub); + os_memblock_put(&btshell_metadata_pool, new_metadata); + return ENOMEM; + } + memcpy(new_codec_spec_cfg, codec_spec_cfg, codec_spec_cfg_len); + } + + big_sub->codec_id.format = codec_fmt; + big_sub->codec_id.company_id = company_id; + big_sub->codec_id.vendor_specific = vendor_spec; + big_sub->codec_id.vendor_specific = vendor_spec; + big_sub->codec_spec_config = codec_spec_cfg_len > 0 ? + new_codec_spec_cfg : NULL; + big_sub->codec_spec_config_len = codec_spec_cfg_len; + big_sub->metadata = metadata_len > 0 ? new_metadata : NULL; + big_sub->metadata_len = metadata_len; + + if (STAILQ_EMPTY(&base->subs)) { + STAILQ_INSERT_HEAD(&base->subs, big_sub, next); + } else { + STAILQ_INSERT_TAIL(&base->subs, big_sub, next); + } + + base->num_subgroups++; + + return 0; +} + +int +btshell_broadcast_bis_add(uint8_t adv_instance, + uint8_t *codec_spec_cfg, + unsigned int codec_spec_cfg_len) +{ + struct ble_audio_bis *bis; + struct ble_audio_base *base; + struct ble_audio_big_subgroup *big_sub; + uint8_t *new_codec_spec_cfg; + + base = btshell_base_find(adv_instance); + if (!base) { + return ENOENT; + } + + big_sub = STAILQ_LAST(&base->subs, ble_audio_big_subgroup, next); + if (!big_sub) { + return ENOENT; + } + + bis = os_memblock_get(&btshell_bis_pool); + if (!bis) { + return ENOMEM; + } + + if (codec_spec_cfg_len > 0) { + new_codec_spec_cfg = os_memblock_get(&btshell_codec_spec_pool); + if (!new_codec_spec_cfg) { + os_memblock_put(&btshell_bis_pool, bis); + return ENOMEM; + } + memcpy(new_codec_spec_cfg, codec_spec_cfg, codec_spec_cfg_len); + } + + bis->codec_spec_config = codec_spec_cfg_len > 0 ? + new_codec_spec_cfg : NULL; + bis->codec_spec_config_len = codec_spec_cfg_len; + + if (STAILQ_EMPTY(&big_sub->bises)) { + STAILQ_INSERT_HEAD(&big_sub->bises, bis, next); + } else { + STAILQ_INSERT_TAIL(&big_sub->bises, bis, next); + } + + big_sub->bis_cnt++; + return 0; +} + +int +btshell_broadcast_create(uint8_t adv_instance, + struct ble_gap_ext_adv_params *ext_params, + struct ble_gap_periodic_adv_params *per_params, + const char *name, + struct ble_iso_big_params big_params, + uint8_t *extra_data, + unsigned int extra_data_len) +{ + struct ble_audio_base *base; + struct ble_broadcast_create_params create_params; + struct ble_iso_big_params *big_params_ptr; + int rc; + + base = btshell_base_find(adv_instance); + if (!base) { + return ENOENT; + } + + big_params_ptr = os_memblock_get(&btshell_big_params_pool); + if (!big_params_ptr) { + return ENOMEM; + } + + *big_params_ptr = big_params; + + create_params.base = base; + create_params.extended_params = ext_params; + create_params.periodic_params = per_params; + create_params.name = name; + create_params.adv_instance = adv_instance; + create_params.big_params = big_params_ptr; + create_params.svc_data = extra_data; + create_params.svc_data_len = extra_data_len; + + rc = ble_audio_broadcast_create(&create_params, + btshell_broadcast_destroy_fn, + (void *)big_params_ptr, + btshell_gap_event); + return rc; +} + +int +btshell_broadcast_destroy(uint8_t adv_instance) +{ + return ble_audio_broadcast_destroy(adv_instance); +} + +int +btshell_broadcast_update(uint8_t adv_instance, + const char *name, + uint8_t *extra_data, + unsigned int extra_data_len) +{ + struct ble_audio_base *base; + struct ble_broadcast_update_params params; + + base = btshell_base_find(adv_instance); + + params.name = name; + params.adv_instance = adv_instance; + params.svc_data = extra_data; + params.svc_data_len = extra_data_len; + params.broadcast_id = base->broadcast_id; + + return ble_audio_broadcast_update(¶ms); +} + +int +btshell_broadcast_start(uint8_t adv_instance) +{ + return ble_audio_broadcast_start(adv_instance, NULL, NULL); +} + +int +btshell_broadcast_stop(uint8_t adv_instance) +{ + return ble_audio_broadcast_stop(adv_instance); +} +#endif + /** * main * @@ -2667,7 +2990,43 @@ mynewt_main(int argc, char **argv) "btshell_coc_conn_pool"); assert(rc == 0); #endif +#if (MYNEWT_VAL(BLE_ISO_BROADCASTER)) + rc = os_mempool_init(&btshell_base_pool, MYNEWT_VAL(BLE_MAX_BIG), + sizeof(struct ble_audio_base), + btshell_base_mem, + "btshell_base_pool"); + assert(rc == 0); + rc = os_mempool_init(&btshell_big_params_pool, MYNEWT_VAL(BLE_MAX_BIG), + sizeof(struct ble_iso_big_params), + btshell_big_params_mem, + "btshell_big_params_pool"); + assert(rc == 0); + rc = os_mempool_init(&btshell_big_sub_pool, MYNEWT_VAL(BLE_MAX_BIS), + sizeof(struct ble_audio_big_subgroup), + btshell_big_sub_mem, + "btshell_big_sub_pool"); + assert(rc == 0); + rc = os_mempool_init(&btshell_bis_pool, MYNEWT_VAL(BLE_MAX_BIS), + sizeof(struct ble_audio_bis), btshell_bis_mem, + "btshell_bis_pool"); + assert(rc == 0); + rc = os_mempool_init(&btshell_metadata_pool, MYNEWT_VAL(BLE_MAX_BIS), + MYNEWT_VAL(BLE_EXT_ADV_MAX_SIZE) - 27, + btshell_metadata_mem, "btshell_metadata_pool"); + assert(rc == 0); + + rc = os_mempool_init(&btshell_codec_spec_pool, + MYNEWT_VAL(BLE_MAX_BIS) * 2, 19, + btshell_codec_spec_mem, "btshell_codec_spec_pool"); + assert(rc == 0); + + rc = os_mempool_init(&btshell_big_params_pool, + MYNEWT_VAL(BLE_MAX_BIG), + sizeof(struct ble_iso_big_params), + btshell_big_params_mem, "btshell_big_params_pool"); + assert(rc == 0); +#endif /* Initialize the NimBLE host configuration. */ ble_hs_cfg.reset_cb = btshell_on_reset; ble_hs_cfg.sync_cb = btshell_on_sync; diff --git a/nimble/host/src/ble_iso.c b/nimble/host/src/ble_iso.c index ba0fd3056..e1115a1d5 100644 --- a/nimble/host/src/ble_iso.c +++ b/nimble/host/src/ble_iso.c @@ -282,28 +282,24 @@ ble_iso_tx_complete(uint16_t conn_handle, const uint8_t *data, } static int -ble_iso_tx_segmented(uint16_t conn_handle, uint16_t max_pdu, - const uint8_t *data, uint16_t data_len) +ble_iso_tx_segmented(uint16_t conn_handle, const uint8_t *data, + uint16_t data_len) { struct os_mbuf *om; uint16_t data_left = data_len; uint16_t packet_len; uint16_t offset = 0; uint8_t pb; - uint8_t ts; int rc; while (data_left) { - packet_len = min(max_pdu - (BLE_HCI_DATA_HDR_SZ + 4), data_left); + packet_len = min(MYNEWT_VAL(BLE_TRANSPORT_ISO_SIZE), data_left); if (data_left == data_len) { pb = BLE_HCI_ISO_PB_FIRST; - ts = 1; } else if (packet_len == data_left) { pb = BLE_HCI_ISO_PB_LAST; - ts = 0; } else { pb = BLE_HCI_ISO_PB_CONTINUATION; - ts = 0; } om = ble_hs_mbuf_bare_pkt(); @@ -311,32 +307,26 @@ ble_iso_tx_segmented(uint16_t conn_handle, uint16_t max_pdu, return BLE_HS_ENOMEM; } - os_mbuf_extend(om, ts == 1 ? 10: 8); + os_mbuf_extend(om, pb == BLE_HCI_ISO_PB_FIRST ? 8: 4); /* Connection_Handle, PB_Flag, TS_Flag */ put_le16(&om->om_data[0], - BLE_HCI_ISO_HANDLE(conn_handle, pb, ts)); - /* Data_Total_Length = Data length + - * Packet_Sequence_Number placeholder + - * optional timestamp*/ - put_le16(&om->om_data[2], packet_len + 4 + 2 * ts); + BLE_HCI_ISO_HANDLE(conn_handle, pb, 0)); - if (ts) { - /* Timestamp placeholder */ - put_le16(&om->om_data[4], 0); + if (pb == BLE_HCI_ISO_PB_FIRST) { + /* Data_Total_Length = Data length + + * Packet_Sequence_Number placeholder*/ + put_le16(&om->om_data[2], packet_len + 4); /* Packet_Sequence_Number placeholder */ - put_le16(&om->om_data[6], 0); + put_le16(&om->om_data[8], 0); /* ISO_SDU_Length */ - put_le16(&om->om_data[8], packet_len); + put_le16(&om->om_data[10], packet_len); + } else { + put_le16(&om->om_data[2], packet_len); } - /* Packet_Sequence_Number placeholder */ - put_le16(&om->om_data[4], 0); - /* ISO_SDU_Length */ - put_le16(&om->om_data[6], packet_len); - rc = os_mbuf_append(om, data + offset, packet_len); if (rc) { return rc; @@ -345,40 +335,21 @@ ble_iso_tx_segmented(uint16_t conn_handle, uint16_t max_pdu, ble_transport_to_ll_iso(om); offset += packet_len; - data_len -= packet_len; + data_left -= packet_len; } return 0; } int -ble_iso_tx(uint16_t conn_handle, const uint8_t *data, uint16_t data_len) +ble_iso_tx(uint16_t conn_handle, void *data, uint16_t data_len) { - struct ble_iso_big *big; - uint16_t max_pdu = 0; int rc; - int i; - SLIST_FOREACH(big, &ble_iso_bigs, next) { - for (i = 0; i < big->num_bis; i++) { - if (big->conn_handles[i] == conn_handle) { - max_pdu = big->max_pdu; - break; - } - } - if (max_pdu > 0) { - break; - } - } - - if (!max_pdu) { - return BLE_HS_ENOENT; - } - - if (data_len <= max_pdu) { + if (data_len <= MYNEWT_VAL(BLE_TRANSPORT_ISO_SIZE)) { rc = ble_iso_tx_complete(conn_handle, data, data_len); } else { - rc = ble_iso_tx_segmented(conn_handle, max_pdu, data, data_len); + rc = ble_iso_tx_segmented(conn_handle, data, data_len); } return rc;