diff --git a/subsys/bluetooth/audio/Kconfig.ascs b/subsys/bluetooth/audio/Kconfig.ascs index 147799f7545..86034477b66 100644 --- a/subsys/bluetooth/audio/Kconfig.ascs +++ b/subsys/bluetooth/audio/Kconfig.ascs @@ -35,4 +35,13 @@ config BT_ASCS_ASE_SNK config BT_ASCS_ASE_SRC def_bool BT_ASCS_ASE_SRC_COUNT > 0 select BT_PAC_SRC + +config BT_ASCS_MAX_ACTIVE_ASES + int "Number of simultaneously supported ASE sessions" + default BT_ISO_MAX_CHAN + range 1 65535 + help + The number of simultanesouly supported active ASEs, in particular + meaning the number of ASEs that are allowed to be in a non-idle state at + a single time. endif # BT_ASCS diff --git a/subsys/bluetooth/audio/ascs.c b/subsys/bluetooth/audio/ascs.c index 66bd077711d..4007dccdea1 100644 --- a/subsys/bluetooth/audio/ascs.c +++ b/subsys/bluetooth/audio/ascs.c @@ -39,6 +39,14 @@ LOG_MODULE_REGISTER(bt_ascs, CONFIG_BT_ASCS_LOG_LEVEL); #include "pacs_internal.h" #include "cap_internal.h" +#define MAX_ASES_SESSIONS CONFIG_BT_MAX_CONN * \ + (CONFIG_BT_ASCS_ASE_SNK_COUNT + \ + CONFIG_BT_ASCS_ASE_SRC_COUNT) + +BUILD_ASSERT(CONFIG_BT_ASCS_MAX_ACTIVE_ASES <= MAX(MAX_ASES_SESSIONS, + CONFIG_BT_ISO_MAX_CHAN), + "Max active ASEs are set to more than actual number of ASEs or ISOs"); + #if defined(CONFIG_BT_AUDIO_UNICAST_SERVER) #define ASE_ID(_ase) ase->ep.status.id @@ -52,17 +60,43 @@ struct bt_ascs_ase { struct bt_ascs *ascs; struct bt_audio_ep ep; const struct bt_gatt_attr *attr; + sys_snode_t node; }; struct bt_ascs { struct bt_conn *conn; - struct bt_ascs_ase ases[ASE_COUNT]; + sys_slist_t ases; }; +K_MEM_SLAB_DEFINE(ase_slab, sizeof(struct bt_ascs_ase), + CONFIG_BT_ASCS_MAX_ACTIVE_ASES, + __alignof__(struct bt_ascs_ase)); + static struct bt_ascs sessions[CONFIG_BT_MAX_CONN]; static int control_point_notify(struct bt_conn *conn, const void *data, uint16_t len); +static void bt_ascs_ase_return_to_slab(struct bt_ascs_ase *ase) +{ + __ASSERT(ase && ase->ascs, "Non-existing ASE or ASCS"); + + LOG_DBG("Returning ase %p to slab", ase); + + sys_slist_find_and_remove(&ase->ascs->ases, &ase->node); + k_mem_slab_free(&ase_slab, (void **)&ase); +} + +static struct bt_ascs_ase *bt_ascs_ase_get_from_slab(void) +{ + struct bt_ascs_ase *ase = NULL; + + if (k_mem_slab_alloc(&ase_slab, (void **)&ase, K_NO_WAIT) < 0) { + LOG_DBG("Could not get ASE from slab, out of memory"); + } + + return ase; +} + static void ase_status_changed(struct bt_audio_ep *ep, uint8_t old_state, uint8_t state) { @@ -105,6 +139,10 @@ void ascs_ep_set_state(struct bt_audio_ep *ep, uint8_t state) if (ops->released != NULL) { ops->released(stream); } + struct bt_ascs_ase *ase = CONTAINER_OF(ep, struct bt_ascs_ase, ep); + + /* Return the ase to slab */ + bt_ascs_ase_return_to_slab(ase); break; case BT_AUDIO_EP_STATE_CODEC_CONFIGURED: @@ -353,6 +391,26 @@ static void ascs_ep_get_status_enable(struct bt_audio_ep *ep, LOG_DBG("dir 0x%02x cig 0x%02x cis 0x%02x", ep->dir, ep->cig_id, ep->cis_id); } +static int ascs_ep_get_status_idle(uint8_t ase_id, struct net_buf_simple *buf) +{ + struct bt_ascs_ase_status *status; + + if (!buf || ase_id > ASE_COUNT) { + return -EINVAL; + } + + net_buf_simple_reset(buf); + + status = net_buf_simple_add(buf, sizeof(*status)); + status->id = ase_id; + status->state = BT_AUDIO_EP_STATE_IDLE; + + LOG_DBG("id 0x%02x state %s", ase_id, + bt_audio_ep_state_str(status->state)); + + return 0; +} + static int ascs_ep_get_status(struct bt_audio_ep *ep, struct net_buf_simple *buf) { @@ -823,12 +881,19 @@ static void disconnected(struct bt_conn *conn, uint8_t reason) return; } - for (size_t i = 0; i < ARRAY_SIZE(session->ases); i++) { - struct bt_ascs_ase *ase = &session->ases[i]; - struct bt_audio_stream *stream = ase->ep.stream; + sys_snode_t *ase_node, *s; + + SYS_SLIST_FOR_EACH_NODE_SAFE(&session->ases, ase_node, s) { + struct bt_audio_stream *stream; + struct bt_ascs_ase *ase; + + ase = CONTAINER_OF(ase_node, struct bt_ascs_ase, node); + stream = ase->ep.stream; if (ase->ep.status.state != BT_AUDIO_EP_STATE_IDLE) { - /* ase_process will handle the final state transition into idle state */ + /* ase_process will handle the final state transition into idle + * state, where the ase finally will be deallocated + */ ase_release(ase); } @@ -997,7 +1062,6 @@ void ascs_ep_init(struct bt_audio_ep *ep, uint8_t id) static void ase_init(struct bt_ascs_ase *ase, uint8_t id) { - memset(ase, 0, sizeof(*ase)); ascs_ep_init(&ase->ep, id); /* Lookup ASE characteristic */ @@ -1009,30 +1073,19 @@ static void ase_init(struct bt_ascs_ase *ase, uint8_t id) static struct bt_ascs_ase *ase_new(struct bt_ascs *ascs, uint8_t id) { struct bt_ascs_ase *ase; - int i; - if (id) { - if (id > ASE_COUNT) { - return NULL; - } - i = id; - ase = &ascs->ases[i - 1]; - goto done; + if (!id || id > ASE_COUNT) { + return NULL; } - for (i = 0; i < ASE_COUNT; i++) { - ase = &ascs->ases[i]; - - if (!ase->ep.status.id) { - i++; - goto done; - } + ase = bt_ascs_ase_get_from_slab(); + if (!ase) { + return NULL; } - return NULL; + sys_slist_append(&ascs->ases, &ase->node); -done: - ase_init(ase, i); + ase_init(ase, id); ase->ascs = ascs; return ase; @@ -1040,15 +1093,14 @@ done: static struct bt_ascs_ase *ase_find(struct bt_ascs *ascs, uint8_t id) { - struct bt_ascs_ase *ase; + sys_snode_t *ase_node; - if (!id || id > ASE_COUNT) { - return NULL; - } + SYS_SLIST_FOR_EACH_NODE(&ascs->ases, ase_node) { + struct bt_ascs_ase *ase = CONTAINER_OF(ase_node, struct bt_ascs_ase, node); - ase = &ascs->ases[id - 1]; - if (ase->ep.status.id == id) { - return ase; + if (ase->ep.status.id == id) { + return ase; + } } return NULL; @@ -1072,16 +1124,25 @@ static ssize_t ascs_ase_read(struct bt_conn *conn, { struct bt_ascs *ascs = ascs_get(conn); struct bt_ascs_ase *ase; + uint8_t ase_id; LOG_DBG("conn %p attr %p buf %p len %u offset %u", conn, attr, buf, len, offset); - ase = ase_get(ascs, POINTER_TO_UINT(BT_AUDIO_CHRC_USER_DATA(attr))); - if (!ase) { - LOG_ERR("Unable to get ASE"); + ase_id = POINTER_TO_UINT(BT_AUDIO_CHRC_USER_DATA(attr)); + + if (ase_id > ASE_COUNT) { + LOG_ERR("Unable to get ASE, id out of range"); return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY); } - ascs_ep_get_status(&ase->ep, &ase_buf); + ase = ase_find(ascs, ase_id); + + /* If NULL, we haven't assigned an ASE, this also means that we are currently in IDLE */ + if (!ase) { + ascs_ep_get_status_idle(ase_id, &ase_buf); + } else { + ascs_ep_get_status(&ase->ep, &ase_buf); + } return bt_gatt_attr_read(conn, attr, buf, len, offset, ase_buf.data, ase_buf.len); @@ -1367,16 +1428,19 @@ static ssize_t ascs_config(struct bt_ascs *ascs, struct net_buf_simple *buf) LOG_DBG("ase 0x%02x cc_len %u", cfg->ase, cfg->cc_len); - if (cfg->ase) { - ase = ase_get(ascs, cfg->ase); + if (!cfg->ase || cfg->ase > ASE_COUNT) { + LOG_WRN("Invalid ASE ID: %u", cfg->ase); + ascs_cp_rsp_add(cfg->ase, BT_ASCS_CONFIG_OP, + BT_ASCS_RSP_INVALID_ASE, 0x00); + continue; } else { - ase = ase_new(ascs, 0); + ase = ase_get(ascs, cfg->ase); } if (!ase) { ascs_cp_rsp_add(cfg->ase, BT_ASCS_CONFIG_OP, - BT_ASCS_RSP_INVALID_ASE, 0x00); - LOG_WRN("Unknown ase 0x%02x", cfg->ase); + BT_ASCS_RSP_NO_MEM, 0x00); + LOG_WRN("No free ASE found for config ASE ID 0x%02x", cfg->ase); continue; } diff --git a/subsys/bluetooth/audio/unicast_client.c b/subsys/bluetooth/audio/unicast_client.c index ac30d7eca29..1fa90f10bbc 100644 --- a/subsys/bluetooth/audio/unicast_client.c +++ b/subsys/bluetooth/audio/unicast_client.c @@ -57,8 +57,11 @@ static const struct bt_uuid *ase_snk_uuid = BT_UUID_ASCS_ASE_SNK; static const struct bt_uuid *ase_src_uuid = BT_UUID_ASCS_ASE_SRC; static const struct bt_uuid *cp_uuid = BT_UUID_ASCS_ASE_CP; -static struct bt_unicast_client_ep snks[CONFIG_BT_MAX_CONN][CONFIG_BT_AUDIO_UNICAST_CLIENT_ASE_SNK_COUNT]; -static struct bt_unicast_client_ep srcs[CONFIG_BT_MAX_CONN][CONFIG_BT_AUDIO_UNICAST_CLIENT_ASE_SRC_COUNT]; + +static struct bt_unicast_client_ep snks[CONFIG_BT_MAX_CONN] + [CONFIG_BT_AUDIO_UNICAST_CLIENT_ASE_SNK_COUNT]; +static struct bt_unicast_client_ep srcs[CONFIG_BT_MAX_CONN] + [CONFIG_BT_AUDIO_UNICAST_CLIENT_ASE_SRC_COUNT]; static struct bt_gatt_subscribe_params cp_subscribe[CONFIG_BT_MAX_CONN]; static struct bt_gatt_subscribe_params snk_loc_subscribe[CONFIG_BT_MAX_CONN]; @@ -235,14 +238,14 @@ static struct bt_iso_chan_ops unicast_client_iso_ops = { static void unicast_client_ep_init(struct bt_audio_ep *ep, uint16_t handle, uint8_t dir) { - struct bt_unicast_client_ep *client; + struct bt_unicast_client_ep *client_ep; LOG_DBG("ep %p dir 0x%02x handle 0x%04x", ep, dir, handle); - client = CONTAINER_OF(ep, struct bt_unicast_client_ep, ep); + client_ep = CONTAINER_OF(ep, struct bt_unicast_client_ep, ep); (void)memset(ep, 0, sizeof(*ep)); - client->handle = handle; + client_ep->handle = handle; ep->status.id = 0U; ep->dir = dir; } @@ -256,20 +259,20 @@ static struct bt_audio_ep *unicast_client_ep_find(struct bt_conn *conn, index = bt_conn_index(conn); for (i = 0; i < ARRAY_SIZE(snks[index]); i++) { - struct bt_unicast_client_ep *client = &snks[index][i]; + struct bt_unicast_client_ep *client_ep = &snks[index][i]; - if ((handle && client->handle == handle) || - (!handle && client->handle)) { - return &client->ep; + if ((handle && client_ep->handle == handle) || + (!handle && client_ep->handle)) { + return &client_ep->ep; } } for (i = 0; i < ARRAY_SIZE(srcs[index]); i++) { - struct bt_unicast_client_ep *client = &srcs[index][i]; + struct bt_unicast_client_ep *client_ep = &srcs[index][i]; - if ((handle && client->handle == handle) || - (!handle && client->handle)) { - return &client->ep; + if ((handle && client_ep->handle == handle) || + (!handle && client_ep->handle)) { + return &client_ep->ep; } } @@ -316,11 +319,11 @@ static struct bt_audio_ep *unicast_client_ep_new(struct bt_conn *conn, } for (i = 0; i < size; i++) { - struct bt_unicast_client_ep *client = &cache[i]; + struct bt_unicast_client_ep *client_ep = &cache[i]; - if (!client->handle) { - unicast_client_ep_init(&client->ep, handle, dir); - return &client->ep; + if (!client_ep->handle) { + unicast_client_ep_init(&client_ep->ep, handle, dir); + return &client_ep->ep; } } @@ -678,7 +681,7 @@ static void unicast_client_ep_set_status(struct bt_audio_ep *ep, struct net_buf_simple *buf) { struct bt_ascs_ase_status *status; - struct bt_unicast_client_ep *client; + struct bt_unicast_client_ep *client_ep; bool state_changed; uint8_t old_state; @@ -686,7 +689,7 @@ static void unicast_client_ep_set_status(struct bt_audio_ep *ep, return; } - client = CONTAINER_OF(ep, struct bt_unicast_client_ep, ep); + client_ep = CONTAINER_OF(ep, struct bt_unicast_client_ep, ep); status = net_buf_simple_pull_mem(buf, sizeof(*status)); @@ -694,7 +697,7 @@ static void unicast_client_ep_set_status(struct bt_audio_ep *ep, ep->status = *status; state_changed = old_state != ep->status.state; - LOG_DBG("ep %p handle 0x%04x id 0x%02x dir %u state %s -> %s", ep, client->handle, + LOG_DBG("ep %p handle 0x%04x id 0x%02x dir %u state %s -> %s", ep, client_ep->handle, status->id, ep->dir, bt_audio_ep_state_str(old_state), bt_audio_ep_state_str(status->state)); @@ -1083,11 +1086,11 @@ static uint8_t unicast_client_ep_notify(struct bt_conn *conn, const void *data, uint16_t length) { struct net_buf_simple buf; - struct bt_unicast_client_ep *client; + struct bt_unicast_client_ep *client_ep; - client = CONTAINER_OF(params, struct bt_unicast_client_ep, subscribe); + client_ep = CONTAINER_OF(params, struct bt_unicast_client_ep, subscribe); - LOG_DBG("conn %p ep %p len %u", conn, &client->ep, length); + LOG_DBG("conn %p ep %p len %u", conn, &client_ep->ep, length); if (!data) { LOG_DBG("Unsubscribed"); @@ -1102,7 +1105,7 @@ static uint8_t unicast_client_ep_notify(struct bt_conn *conn, return BT_GATT_ITER_STOP; } - unicast_client_ep_set_status(&client->ep, &buf); + unicast_client_ep_set_status(&client_ep->ep, &buf); return BT_GATT_ITER_CONTINUE; } @@ -1110,25 +1113,25 @@ static uint8_t unicast_client_ep_notify(struct bt_conn *conn, static int unicast_client_ep_subscribe(struct bt_conn *conn, struct bt_audio_ep *ep) { - struct bt_unicast_client_ep *client; + struct bt_unicast_client_ep *client_ep; - client = CONTAINER_OF(ep, struct bt_unicast_client_ep, ep); + client_ep = CONTAINER_OF(ep, struct bt_unicast_client_ep, ep); - LOG_DBG("ep %p handle 0x%02x", ep, client->handle); + LOG_DBG("ep %p handle 0x%02x", ep, client_ep->handle); - if (client->subscribe.value_handle) { + if (client_ep->subscribe.value_handle) { return 0; } - client->subscribe.value_handle = client->handle; - client->subscribe.ccc_handle = 0x0000; - client->subscribe.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE; - client->subscribe.disc_params = &client->discover; - client->subscribe.notify = unicast_client_ep_notify; - client->subscribe.value = BT_GATT_CCC_NOTIFY; - atomic_set_bit(client->subscribe.flags, BT_GATT_SUBSCRIBE_FLAG_VOLATILE); + client_ep->subscribe.value_handle = client_ep->handle; + client_ep->subscribe.ccc_handle = 0x0000; + client_ep->subscribe.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE; + client_ep->subscribe.disc_params = &client_ep->discover; + client_ep->subscribe.notify = unicast_client_ep_notify; + client_ep->subscribe.value = BT_GATT_CCC_NOTIFY; + atomic_set_bit(client_ep->subscribe.flags, BT_GATT_SUBSCRIBE_FLAG_VOLATILE); - return bt_gatt_subscribe(conn, &client->subscribe); + return bt_gatt_subscribe(conn, &client_ep->subscribe); } static void unicast_client_ep_set_cp(struct bt_conn *conn, uint16_t handle) @@ -1154,18 +1157,18 @@ static void unicast_client_ep_set_cp(struct bt_conn *conn, uint16_t handle) } for (i = 0; i < ARRAY_SIZE(snks[index]); i++) { - struct bt_unicast_client_ep *client = &snks[index][i]; + struct bt_unicast_client_ep *client_ep = &snks[index][i]; - if (client->handle) { - client->cp_handle = handle; + if (client_ep->handle) { + client_ep->cp_handle = handle; } } for (i = 0; i < ARRAY_SIZE(srcs[index]); i++) { - struct bt_unicast_client_ep *client = &srcs[index][i]; + struct bt_unicast_client_ep *client_ep = &srcs[index][i]; - if (client->handle) { - client->cp_handle = handle; + if (client_ep->handle) { + client_ep->cp_handle = handle; } } } @@ -1449,11 +1452,11 @@ static int unicast_client_ep_release(struct bt_audio_ep *ep, int bt_unicast_client_ep_send(struct bt_conn *conn, struct bt_audio_ep *ep, struct net_buf_simple *buf) { - struct bt_unicast_client_ep *client = CONTAINER_OF(ep, struct bt_unicast_client_ep, ep); + struct bt_unicast_client_ep *client_ep = CONTAINER_OF(ep, struct bt_unicast_client_ep, ep); LOG_DBG("conn %p ep %p buf %p len %u", conn, ep, buf, buf->len); - return bt_gatt_write_without_response(conn, client->cp_handle, + return bt_gatt_write_without_response(conn, client_ep->cp_handle, buf->data, buf->len, false); }