Bluetooth: BAP: Add unicast client and server write long support
Add support for long writes for the unicast client and server.
This reuses the ATT buffer for long reads.
Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
diff --git a/samples/bluetooth/hap_ha/prj.conf b/samples/bluetooth/hap_ha/prj.conf
index 93384c4..eda9951 100644
--- a/samples/bluetooth/hap_ha/prj.conf
+++ b/samples/bluetooth/hap_ha/prj.conf
@@ -11,6 +11,9 @@
# 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 9750e0c..b821ba0 100644
--- a/samples/bluetooth/tmap_peripheral/prj.conf
+++ b/samples/bluetooth/tmap_peripheral/prj.conf
@@ -17,6 +17,8 @@
# 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 e87b46b..7963da2 100644
--- a/samples/bluetooth/unicast_audio_server/prj.conf
+++ b/samples/bluetooth/unicast_audio_server/prj.conf
@@ -8,5 +8,8 @@
# 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 a41d118..39902a1 100644
--- a/subsys/bluetooth/audio/ascs.c
+++ b/subsys/bluetooth/audio/ascs.c
@@ -27,6 +27,8 @@
#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 @@
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 07790ef..d66afe0 100644
--- a/subsys/bluetooth/audio/bap_unicast_client.c
+++ b/subsys/bluetooth/audio/bap_unicast_client.c
@@ -92,6 +92,7 @@
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 @@
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 @@
}
}
-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 @@
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 @@
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 @@
}
/* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 db6b482..cb8f350 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_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 b3d052e..7f81b18 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 <zephyr/bluetooth/l2cap.h>
+
/*
* Copyright (c) 2015-2016 Intel Corporation
*
diff --git a/tests/bluetooth/audio/ascs/prj.conf b/tests/bluetooth/audio/ascs/prj.conf
index ed2d370..d7a8a6e 100644
--- a/tests/bluetooth/audio/ascs/prj.conf
+++ b/tests/bluetooth/audio/ascs/prj.conf
@@ -12,6 +12,9 @@
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 2a76107..db9643b 100644
--- a/tests/bluetooth/shell/audio.conf
+++ b/tests/bluetooth/shell/audio.conf
@@ -16,7 +16,7 @@
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 664819d..15afe27 100644
--- a/tests/bsim/bluetooth/audio/prj.conf
+++ b/tests/bsim/bluetooth/audio/prj.conf
@@ -8,6 +8,7 @@
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