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>
This commit is contained in:
Emil Gydesen 2023-03-27 14:19:11 +02:00 committed by Anas Nashif
parent 486ea06fcb
commit e8ade2356a
10 changed files with 133 additions and 20 deletions

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -1,5 +1,7 @@
/* att_internal.h - Attribute protocol handling */
#include <zephyr/bluetooth/l2cap.h>
/*
* Copyright (c) 2015-2016 Intel Corporation
*

View File

@ -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

View File

@ -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

View File

@ -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