diff --git a/samples/bluetooth/hap_ha/prj.conf b/samples/bluetooth/hap_ha/prj.conf index 93384c4e5ee..eda99512ca8 100644 --- a/samples/bluetooth/hap_ha/prj.conf +++ b/samples/bluetooth/hap_ha/prj.conf @@ -11,6 +11,9 @@ CONFIG_BT_DEVICE_NAME="Hearing Aid sample" # Appearance: Generic Hearing aid (0x0A40) CONFIG_BT_DEVICE_APPEARANCE=2624 +# Mandatory to support at least 1 for ASCS +CONFIG_BT_ATT_PREPARE_COUNT=1 + CONFIG_BT_AUDIO=y CONFIG_BT_BAP_UNICAST_SERVER=y CONFIG_BT_ASCS_ASE_SNK_COUNT=1 diff --git a/samples/bluetooth/tmap_peripheral/prj.conf b/samples/bluetooth/tmap_peripheral/prj.conf index 9750e0c2542..b821ba072a3 100644 --- a/samples/bluetooth/tmap_peripheral/prj.conf +++ b/samples/bluetooth/tmap_peripheral/prj.conf @@ -17,6 +17,8 @@ CONFIG_BT_CAP_ACCEPTOR=y # BAP support CONFIG_BT_BAP_UNICAST_SERVER=y +# Mandatory to support at least 1 for ASCS +CONFIG_BT_ATT_PREPARE_COUNT=1 # VCP support CONFIG_BT_VCP_VOL_REND=y diff --git a/samples/bluetooth/unicast_audio_server/prj.conf b/samples/bluetooth/unicast_audio_server/prj.conf index e87b46b9905..7963da2b157 100644 --- a/samples/bluetooth/unicast_audio_server/prj.conf +++ b/samples/bluetooth/unicast_audio_server/prj.conf @@ -8,5 +8,8 @@ CONFIG_BT_ASCS_ASE_SRC_COUNT=1 # Support an ISO channel per ASE CONFIG_BT_ISO_MAX_CHAN=4 +# Mandatory to support at least 1 for ASCS +CONFIG_BT_ATT_PREPARE_COUNT=1 + CONFIG_BT_EXT_ADV=y CONFIG_BT_DEVICE_NAME="Unicast Audio Server" diff --git a/subsys/bluetooth/audio/ascs.c b/subsys/bluetooth/audio/ascs.c index a41d1189b5d..39902a139a2 100644 --- a/subsys/bluetooth/audio/ascs.c +++ b/subsys/bluetooth/audio/ascs.c @@ -27,6 +27,8 @@ LOG_MODULE_REGISTER(bt_ascs, CONFIG_BT_ASCS_LOG_LEVEL); #include "common/bt_str.h" #include "common/assert.h" +#include "../host/att_internal.h" + #include "audio_internal.h" #include "bap_iso.h" #include "bap_endpoint.h" @@ -80,6 +82,19 @@ static struct bt_ascs_ase { MAX(MIN_CONFIG_STATE_SIZE + MAX_CODEC_CONFIG, \ MIN_QOS_STATE_SIZE + MAX_METADATA)) +/* Verify that the prepare count is large enough to cover the maximum value we support a client + * writing + */ +BUILD_ASSERT( + BT_ATT_BUF_SIZE - 3 >= ASE_BUF_SIZE || + DIV_ROUND_UP(ASE_BUF_SIZE, (BT_ATT_BUF_SIZE - 3)) <= CONFIG_BT_ATT_PREPARE_COUNT, + "CONFIG_BT_ATT_PREPARE_COUNT not large enough to cover the maximum supported ASCS value"); + +/* It is mandatory to support long writes in ASCS unconditionally, and thus + * CONFIG_BT_ATT_PREPARE_COUNT must be at least 1 to support the feature + */ +BUILD_ASSERT(CONFIG_BT_ATT_PREPARE_COUNT > 0, "CONFIG_BT_ATT_PREPARE_COUNT shall be at least 1"); + static const struct bt_bap_unicast_server_cb *unicast_server_cb; static K_SEM_DEFINE(ase_buf_sem, 1, 1); diff --git a/subsys/bluetooth/audio/bap_unicast_client.c b/subsys/bluetooth/audio/bap_unicast_client.c index 07790efe81c..d66afe0ac50 100644 --- a/subsys/bluetooth/audio/bap_unicast_client.c +++ b/subsys/bluetooth/audio/bap_unicast_client.c @@ -92,6 +92,7 @@ static struct unicast_client { union { struct bt_gatt_read_params read_params; struct bt_gatt_discover_params disc_params; + struct bt_gatt_write_params write_params; }; /* The att_buf needs to use the maximum ATT attribute size as a single @@ -129,7 +130,11 @@ static int unicast_client_send_start(struct bt_bap_ep *ep) struct net_buf_simple *buf; int err; - buf = bt_bap_unicast_client_ep_create_pdu(BT_ASCS_START_OP); + buf = bt_bap_unicast_client_ep_create_pdu(ep->stream->conn, BT_ASCS_START_OP); + if (buf == NULL) { + LOG_DBG("Could not create PDU"); + return -EBUSY; + } req = net_buf_simple_add(buf, sizeof(*req)); req->num_ases = 1U; @@ -1556,19 +1561,22 @@ static void unicast_client_ep_set_cp(struct bt_conn *conn, uint16_t handle) } } -NET_BUF_SIMPLE_DEFINE_STATIC(ep_buf, CONFIG_BT_L2CAP_TX_MTU); - -struct net_buf_simple *bt_bap_unicast_client_ep_create_pdu(uint8_t op) +struct net_buf_simple *bt_bap_unicast_client_ep_create_pdu(struct bt_conn *conn, uint8_t op) { + struct unicast_client *client = &uni_cli_insts[bt_conn_index(conn)]; struct bt_ascs_ase_cp *hdr; - /* Reset buffer before using */ - net_buf_simple_reset(&ep_buf); + if (client->busy) { + return NULL; + } - hdr = net_buf_simple_add(&ep_buf, sizeof(*hdr)); + /* Reset buffer before using */ + reset_att_buf(client); + + hdr = net_buf_simple_add(&client->net_buf, sizeof(*hdr)); hdr->op = op; - return &ep_buf; + return &client->net_buf; } static int unicast_client_ep_config(struct bt_bap_ep *ep, struct net_buf_simple *buf, @@ -1822,16 +1830,60 @@ static int unicast_client_ep_release(struct bt_bap_ep *ep, struct net_buf_simple return 0; } +static void gatt_write_cb(struct bt_conn *conn, uint8_t err, struct bt_gatt_write_params *params) +{ + struct unicast_client *client = &uni_cli_insts[bt_conn_index(conn)]; + + LOG_DBG("conn %p err %u", conn, err); + + memset(params, 0, sizeof(*params)); + client->busy = false; + + /* TBD: Should we do anything in case of error here? */ +} + int bt_bap_unicast_client_ep_send(struct bt_conn *conn, struct bt_bap_ep *ep, struct net_buf_simple *buf) { + const uint8_t att_write_header_size = 3; /* opcode (1) + handle (2) */ + const uint16_t max_write_size = bt_gatt_get_mtu(conn) - att_write_header_size; + struct unicast_client *client = &uni_cli_insts[bt_conn_index(conn)]; struct bt_bap_unicast_client_ep *client_ep = CONTAINER_OF(ep, struct bt_bap_unicast_client_ep, ep); + int err; LOG_DBG("conn %p ep %p buf %p len %u", conn, ep, buf, buf->len); - return bt_gatt_write_without_response(conn, client_ep->cp_handle, buf->data, buf->len, - false); + if (buf->len > max_write_size) { + if (client->busy) { + LOG_DBG("Client connection is busy"); + return -EBUSY; + } + + client->write_params.func = gatt_write_cb; + client->write_params.handle = client_ep->cp_handle; + client->write_params.offset = 0U; + client->write_params.data = buf->data; + client->write_params.length = buf->len; +#if defined(CONFIG_BT_EATT) + client->write_params.chan_opt = BT_ATT_CHAN_OPT_NONE; +#endif /* CONFIG_BT_EATT */ + + err = bt_gatt_write(conn, &client->write_params); + if (err != 0) { + LOG_DBG("bt_gatt_write failed: %d", err); + } + + client->busy = true; + } else { + err = bt_gatt_write_without_response(conn, client_ep->cp_handle, buf->data, + buf->len, false); + if (err != 0) { + LOG_DBG("bt_gatt_write_without_response failed: %d", err); + } + } + + return err; } static void unicast_client_reset(struct bt_bap_ep *ep) @@ -2523,7 +2575,11 @@ int bt_bap_unicast_client_config(struct bt_bap_stream *stream, const struct bt_c struct net_buf_simple *buf; int err; - buf = bt_bap_unicast_client_ep_create_pdu(BT_ASCS_CONFIG_OP); + buf = bt_bap_unicast_client_ep_create_pdu(stream->conn, BT_ASCS_CONFIG_OP); + if (buf == NULL) { + LOG_DBG("Could not create PDU"); + return -EBUSY; + } op = net_buf_simple_add(buf, sizeof(*op)); op->num_ases = 0x01; @@ -2616,7 +2672,11 @@ int bt_bap_unicast_client_qos(struct bt_conn *conn, struct bt_bap_unicast_group } /* Generate the control point write */ - buf = bt_bap_unicast_client_ep_create_pdu(BT_ASCS_QOS_OP); + buf = bt_bap_unicast_client_ep_create_pdu(conn, BT_ASCS_QOS_OP); + if (buf == NULL) { + LOG_DBG("Could not create PDU"); + return -EBUSY; + } op = net_buf_simple_add(buf, sizeof(*op)); @@ -2668,7 +2728,11 @@ int bt_bap_unicast_client_enable(struct bt_bap_stream *stream, struct bt_codec_d LOG_DBG("stream %p", stream); - buf = bt_bap_unicast_client_ep_create_pdu(BT_ASCS_ENABLE_OP); + buf = bt_bap_unicast_client_ep_create_pdu(stream->conn, BT_ASCS_ENABLE_OP); + if (buf == NULL) { + LOG_DBG("Could not create PDU"); + return -EBUSY; + } req = net_buf_simple_add(buf, sizeof(*req)); req->num_ases = 0x01; @@ -2691,7 +2755,11 @@ int bt_bap_unicast_client_metadata(struct bt_bap_stream *stream, struct bt_codec LOG_DBG("stream %p", stream); - buf = bt_bap_unicast_client_ep_create_pdu(BT_ASCS_METADATA_OP); + buf = bt_bap_unicast_client_ep_create_pdu(stream->conn, BT_ASCS_METADATA_OP); + if (buf == NULL) { + LOG_DBG("Could not create PDU"); + return -EBUSY; + } req = net_buf_simple_add(buf, sizeof(*req)); req->num_ases = 0x01; @@ -2713,7 +2781,11 @@ int bt_bap_unicast_client_start(struct bt_bap_stream *stream) LOG_DBG("stream %p", stream); - buf = bt_bap_unicast_client_ep_create_pdu(BT_ASCS_START_OP); + buf = bt_bap_unicast_client_ep_create_pdu(stream->conn, BT_ASCS_START_OP); + if (buf == NULL) { + LOG_DBG("Could not create PDU"); + return -EBUSY; + } req = net_buf_simple_add(buf, sizeof(*req)); req->num_ases = 0x00; @@ -2765,7 +2837,11 @@ int bt_bap_unicast_client_disable(struct bt_bap_stream *stream) LOG_DBG("stream %p", stream); - buf = bt_bap_unicast_client_ep_create_pdu(BT_ASCS_DISABLE_OP); + buf = bt_bap_unicast_client_ep_create_pdu(stream->conn, BT_ASCS_DISABLE_OP); + if (buf == NULL) { + LOG_DBG("Could not create PDU"); + return -EBUSY; + } req = net_buf_simple_add(buf, sizeof(*req)); req->num_ases = 0x01; @@ -2787,7 +2863,11 @@ int bt_bap_unicast_client_stop(struct bt_bap_stream *stream) LOG_DBG("stream %p", stream); - buf = bt_bap_unicast_client_ep_create_pdu(BT_ASCS_STOP_OP); + buf = bt_bap_unicast_client_ep_create_pdu(stream->conn, BT_ASCS_STOP_OP); + if (buf == NULL) { + LOG_DBG("Could not create PDU"); + return -EBUSY; + } req = net_buf_simple_add(buf, sizeof(*req)); req->num_ases = 0x00; @@ -2822,7 +2902,11 @@ int bt_bap_unicast_client_release(struct bt_bap_stream *stream) return -ENOTCONN; } - buf = bt_bap_unicast_client_ep_create_pdu(BT_ASCS_RELEASE_OP); + buf = bt_bap_unicast_client_ep_create_pdu(stream->conn, BT_ASCS_RELEASE_OP); + if (buf == NULL) { + LOG_DBG("Could not create PDU"); + return -EBUSY; + } req = net_buf_simple_add(buf, sizeof(*req)); req->num_ases = 0x01; diff --git a/subsys/bluetooth/audio/bap_unicast_client_internal.h b/subsys/bluetooth/audio/bap_unicast_client_internal.h index db6b4828b79..cb8f350ce23 100644 --- a/subsys/bluetooth/audio/bap_unicast_client_internal.h +++ b/subsys/bluetooth/audio/bap_unicast_client_internal.h @@ -24,7 +24,7 @@ int bt_bap_unicast_client_stop(struct bt_bap_stream *stream); int bt_bap_unicast_client_release(struct bt_bap_stream *stream); -struct net_buf_simple *bt_bap_unicast_client_ep_create_pdu(uint8_t op); +struct net_buf_simple *bt_bap_unicast_client_ep_create_pdu(struct bt_conn *conn, uint8_t op); int bt_bap_unicast_client_ep_qos(struct bt_bap_ep *ep, struct net_buf_simple *buf, struct bt_codec_qos *qos); diff --git a/subsys/bluetooth/host/att_internal.h b/subsys/bluetooth/host/att_internal.h index b3d052eed94..7f81b181989 100644 --- a/subsys/bluetooth/host/att_internal.h +++ b/subsys/bluetooth/host/att_internal.h @@ -1,5 +1,7 @@ /* att_internal.h - Attribute protocol handling */ +#include + /* * Copyright (c) 2015-2016 Intel Corporation * diff --git a/tests/bluetooth/audio/ascs/prj.conf b/tests/bluetooth/audio/ascs/prj.conf index ed2d370bb6d..d7a8a6e18d6 100644 --- a/tests/bluetooth/audio/ascs/prj.conf +++ b/tests/bluetooth/audio/ascs/prj.conf @@ -12,6 +12,9 @@ CONFIG_BT_ASCS_MAX_ACTIVE_ASES=1 CONFIG_BT_BAP_UNICAST_SERVER=y CONFIG_BT_ISO_MAX_CHAN=2 +# Mandatory to support at least 1 for ASCS +CONFIG_BT_ATT_PREPARE_COUNT=1 + CONFIG_BT_DEBUG_LOG=y CONFIG_BT_ASCS_LOG_LEVEL_DBG=y CONFIG_BT_BAP_ISO_LOG_LEVEL_DBG=y diff --git a/tests/bluetooth/shell/audio.conf b/tests/bluetooth/shell/audio.conf index 2a76107b998..db9643ba370 100644 --- a/tests/bluetooth/shell/audio.conf +++ b/tests/bluetooth/shell/audio.conf @@ -16,7 +16,7 @@ CONFIG_BT_L2CAP_ECRED=y CONFIG_BT_EATT=y CONFIG_BT_SIGNING=y CONFIG_BT_FIXED_PASSKEY=y -CONFIG_BT_ATT_PREPARE_COUNT=2 +CONFIG_BT_ATT_PREPARE_COUNT=5 CONFIG_BT_SHELL=y CONFIG_BT_DEVICE_NAME="audio test shell" CONFIG_BT_DEVICE_NAME_DYNAMIC=y diff --git a/tests/bsim/bluetooth/audio/prj.conf b/tests/bsim/bluetooth/audio/prj.conf index 664819daf98..15afe2770fb 100644 --- a/tests/bsim/bluetooth/audio/prj.conf +++ b/tests/bsim/bluetooth/audio/prj.conf @@ -8,6 +8,7 @@ CONFIG_BT_PERIPHERAL=y CONFIG_BT_DEVICE_NAME="bsim_test_audio" # TBS Client may require up to 12 buffers CONFIG_BT_L2CAP_TX_BUF_COUNT=12 +CONFIG_BT_ATT_PREPARE_COUNT=5 CONFIG_BT_MAX_CONN=5 CONFIG_BT_MAX_PAIRED=5 CONFIG_BT_GATT_DYNAMIC_DB=y