Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[nrf fromlist] Bluetooth: Tester: Use BT_L2CAP_SEG_RECV (experimental variant) #2223

Merged
merged 3 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions include/zephyr/bluetooth/l2cap.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
* @{
*/

#include <stdint.h>
#include <sys/types.h>

#include <zephyr/sys/atomic.h>
Expand Down Expand Up @@ -552,6 +553,26 @@ int bt_l2cap_ecred_chan_connect(struct bt_conn *conn,
*/
int bt_l2cap_ecred_chan_reconfigure(struct bt_l2cap_chan **chans, uint16_t mtu);

/** @brief Reconfigure Enhanced Credit Based L2CAP channels
*
* Experimental API to reconfigure with explicit MPS and MTU values.
*
* Reconfigure up to 5 L2CAP channels. Channels must be from the same bt_conn.
* Once reconfiguration is completed each channel reconfigured() callback will
* be called. MTU cannot be decreased on any of provided channels.
*
* @kconfig_dep{CONFIG_BT_L2CAP_RECONFIGURE_EXPLICIT}
*
* @param chans Array of channel objects. Null-terminated. Elements after the
* first 5 are silently ignored.
* @param mtu Channel MTU to reconfigure to.
* @param mps Channel MPS to reconfigure to.
*
* @return 0 in case of success or negative value in case of error.
*/
int bt_l2cap_ecred_chan_reconfigure_explicit(struct bt_l2cap_chan **chans, uint16_t mtu,
uint16_t mps);

/** @brief Connect L2CAP channel
*
* Connect L2CAP channel by PSM, once the connection is completed channel
Expand Down
8 changes: 8 additions & 0 deletions subsys/bluetooth/host/Kconfig.l2cap
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,12 @@ config BT_L2CAP_SEG_RECV
This API enforces conformance with L2CAP TS, but is otherwise as
flexible and semantically simple as possible.

config BT_L2CAP_RECONFIGURE_EXPLICIT
bool "L2CAP Explicit reconfigure API [EXPERIMENTAL]"
select EXPERIMENTAL
help

Enable API for explicit reconfiguration of an L2CAP channel's MTU and
MPS.

endmenu
101 changes: 101 additions & 0 deletions subsys/bluetooth/host/l2cap.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ LOG_MODULE_REGISTER(bt_l2cap, CONFIG_BT_L2CAP_LOG_LEVEL);

#define L2CAP_LE_MIN_MTU 23
#define L2CAP_ECRED_MIN_MTU 64
#define L2CAP_ECRED_MIN_MPS 64

#define L2CAP_LE_MAX_CREDITS (CONFIG_BT_BUF_ACL_RX_COUNT - 1)

Expand Down Expand Up @@ -2557,6 +2558,7 @@ static void l2cap_chan_le_recv_seg_direct(struct bt_l2cap_le_chan *chan, struct
if (seg->len > sdu_remaining) {
LOG_WRN("L2CAP RX PDU total exceeds SDU");
bt_l2cap_chan_disconnect(&chan->chan);
return;
}

/* Commit receive. */
Expand Down Expand Up @@ -3031,6 +3033,105 @@ int bt_l2cap_ecred_chan_reconfigure(struct bt_l2cap_chan **chans, uint16_t mtu)
return 0;
}

#if defined(CONFIG_BT_L2CAP_RECONFIGURE_EXPLICIT)
int bt_l2cap_ecred_chan_reconfigure_explicit(struct bt_l2cap_chan **chans, uint16_t mtu,
uint16_t mps)
{
struct bt_l2cap_ecred_reconf_req *req;
struct bt_conn *conn = NULL;
struct bt_l2cap_le_chan *ch;
struct net_buf *buf;
bool multiple_chan;
uint8_t ident;
int i;

LOG_DBG("chans %p mtu 0x%04x mps 0x%04x", chans, mtu, mps);

if (!chans) {
return -EINVAL;
}

if (chans[0] == NULL) {
return -EINVAL;
}

if (mps < L2CAP_ECRED_MIN_MPS || mps > BT_L2CAP_RX_MTU) {
return -EINVAL;
}

multiple_chan = chans[1] != NULL;

for (i = 0; i < L2CAP_ECRED_CHAN_MAX_PER_REQ; i++) {
if (!chans[i]) {
break;
}

/* validate that all channels are from same connection */
if (conn) {
if (conn != chans[i]->conn) {
return -EINVAL;
}
} else {
conn = chans[i]->conn;
}

/* validate MTU is not decreased */
if (mtu < BT_L2CAP_LE_CHAN(chans[i])->rx.mtu) {
return -EINVAL;
}

/* MPS is not allowed to decrease when reconfiguring multiple channels.
* Core Specification 3.A.4.27 v6.0
*/
if (multiple_chan && mps < BT_L2CAP_LE_CHAN(chans[i])->rx.mps) {
return -EINVAL;
}
}

if (!conn) {
return -ENOTCONN;
}

if (conn->type != BT_CONN_TYPE_LE) {
return -EINVAL;
}

/* allow only 1 request at time */
if (l2cap_find_pending_reconf(conn)) {
return -EBUSY;
}

ident = get_ident();

buf = l2cap_create_le_sig_pdu(BT_L2CAP_ECRED_RECONF_REQ, ident,
sizeof(*req) + (i * sizeof(uint16_t)));
if (!buf) {
return -ENOMEM;
}

req = net_buf_add(buf, sizeof(*req));
req->mtu = sys_cpu_to_le16(mtu);
req->mps = sys_cpu_to_le16(mps);

for (int j = 0; j < i; j++) {
ch = BT_L2CAP_LE_CHAN(chans[j]);

ch->ident = ident;
ch->pending_rx_mtu = mtu;

net_buf_add_le16(buf, ch->rx.cid);
};

/* We set the RTX timer on one of the supplied channels, but when the
* request resolves or times out we will act on all the channels in the
* supplied array, using the ident field to find them.
*/
l2cap_chan_send_req(chans[0], buf, L2CAP_CONN_TIMEOUT);

return 0;
}
#endif /* defined(CONFIG_BT_L2CAP_RECONFIGURE_EXPLICIT) */

#endif /* defined(CONFIG_BT_L2CAP_ECRED) */

int bt_l2cap_chan_connect(struct bt_conn *conn, struct bt_l2cap_chan *chan,
Expand Down
14 changes: 11 additions & 3 deletions tests/bluetooth/tester/prj.conf
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ CONFIG_BT_BONDABLE=y
CONFIG_BT_ATT_PREPARE_COUNT=12
CONFIG_BT_GATT_CLIENT=y
CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y
CONFIG_BT_L2CAP_TX_MTU=255
CONFIG_BT_L2CAP_SEG_RECV=y
CONFIG_BT_L2CAP_RECONFIGURE_EXPLICIT=y
CONFIG_BT_DEVICE_NAME="Tester"
CONFIG_BT_DEVICE_NAME_MAX=32
CONFIG_BT_DEVICE_NAME_DYNAMIC=y
Expand All @@ -25,15 +28,14 @@ CONFIG_BT_L2CAP_ECRED=y
CONFIG_BT_EATT_MAX=5
CONFIG_BT_FILTER_ACCEPT_LIST=y
CONFIG_BT_EATT_AUTO_CONNECT=n
CONFIG_BT_MAX_CONN=2
CONFIG_BT_MAX_PAIRED=2
CONFIG_BT_MAX_CONN=3
CONFIG_BT_MAX_PAIRED=3
CONFIG_BT_GATT_NOTIFY_MULTIPLE=y
CONFIG_BT_ATT_RETRY_ON_SEC_ERR=n
CONFIG_BT_GATT_DYNAMIC_DB=y
CONFIG_BT_EXT_ADV=y
CONFIG_BT_PER_ADV=y
CONFIG_BT_PER_ADV_SYNC=y
CONFIG_BT_BUF_ACL_RX_SIZE=100
CONFIG_BT_RX_STACK_SIZE=4096

CONFIG_BT_TESTING=y
Expand All @@ -54,3 +56,9 @@ CONFIG_BT_DIS_SERIAL_NUMBER=y
CONFIG_BT_DIS_FW_REV=y
CONFIG_BT_DIS_HW_REV=y
CONFIG_BT_DIS_SW_REV=y

CONFIG_BT_BUF_EVT_RX_COUNT=16
CONFIG_BT_BUF_EVT_RX_SIZE=255
CONFIG_BT_BUF_CMD_TX_SIZE=255
CONFIG_BT_BUF_ACL_TX_SIZE=251
CONFIG_BT_BUF_ACL_RX_SIZE=255
86 changes: 74 additions & 12 deletions tests/bluetooth/tester/src/btp_l2cap.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_BTTESTER_LOG_LEVEL);

#include "btp/btp.h"

#define DATA_MTU_INITIAL 128
#define DATA_MTU 256
#define DATA_BUF_SIZE BT_L2CAP_SDU_BUF_SIZE(DATA_MTU)
#define L2CAP_MPS 96
#define DATA_MTU (3 * L2CAP_MPS)
#define DATA_MTU_INITIAL (2 * L2CAP_MPS)

#define CHANNELS 2
#define SERVERS 1

NET_BUF_POOL_FIXED_DEFINE(data_pool, CHANNELS, DATA_BUF_SIZE, CONFIG_BT_CONN_TX_USER_DATA_SIZE,
NULL);
NET_BUF_POOL_FIXED_DEFINE(data_pool, CHANNELS, BT_L2CAP_SDU_BUF_SIZE(DATA_MTU),
CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL);

static bool authorize_flag;
static uint8_t req_keysize;
Expand All @@ -36,18 +37,51 @@ static struct channel {
struct bt_l2cap_le_chan le;
bool in_use;
bool hold_credit;
#if defined(CONFIG_BT_L2CAP_SEG_RECV)
unsigned int pending_credits;
uint8_t recv_cb_buf[DATA_MTU + sizeof(struct btp_l2cap_data_received_ev)];
#else
struct net_buf *pending_credit;
#endif
} channels[CHANNELS];

/* TODO Extend to support multiple servers */
static struct bt_l2cap_server servers[SERVERS];

#if defined(CONFIG_BT_L2CAP_SEG_RECV)
static void seg_recv_cb(struct bt_l2cap_chan *l2cap_chan, size_t sdu_len, off_t seg_offset,
struct net_buf_simple *seg)
{
struct btp_l2cap_data_received_ev *ev;
struct bt_l2cap_le_chan *l2cap_le_chan =
CONTAINER_OF(l2cap_chan, struct bt_l2cap_le_chan, chan);
struct channel *chan = CONTAINER_OF(l2cap_le_chan, struct channel, le);

ev = (void *)chan->recv_cb_buf;
memcpy(&ev->data[seg_offset], seg->data, seg->len);

/* complete SDU received */
if (seg_offset + seg->len == sdu_len) {
ev->chan_id = chan->chan_id;
ev->data_length = sys_cpu_to_le16(sdu_len);

tester_event(BTP_SERVICE_ID_L2CAP, BTP_L2CAP_EV_DATA_RECEIVED, chan->recv_cb_buf,
sizeof(*ev) + sdu_len);
}

if (chan->hold_credit) {
chan->pending_credits++;
} else {
bt_l2cap_chan_give_credits(l2cap_chan, 1);
}
}
#else
static struct net_buf *alloc_buf_cb(struct bt_l2cap_chan *chan)
{
return net_buf_alloc(&data_pool, K_FOREVER);
}

static uint8_t recv_cb_buf[DATA_BUF_SIZE + sizeof(struct btp_l2cap_data_received_ev)];
static uint8_t recv_cb_buf[DATA_MTU + sizeof(struct btp_l2cap_data_received_ev)];

static int recv_cb(struct bt_l2cap_chan *l2cap_chan, struct net_buf *buf)
{
Expand All @@ -73,6 +107,7 @@ static int recv_cb(struct bt_l2cap_chan *l2cap_chan, struct net_buf *buf)

return 0;
}
#endif

static void connected_cb(struct bt_l2cap_chan *l2cap_chan)
{
Expand Down Expand Up @@ -111,11 +146,13 @@ static void disconnected_cb(struct bt_l2cap_chan *l2cap_chan)
struct channel *chan = CONTAINER_OF(l2cap_le_chan, struct channel, le);
struct bt_conn_info info;

#if !defined(CONFIG_BT_L2CAP_SEG_RECV)
/* release netbuf on premature disconnection */
if (chan->pending_credit) {
net_buf_unref(chan->pending_credit);
chan->pending_credit = NULL;
}
#endif

(void)memset(&ev, 0, sizeof(struct btp_l2cap_disconnected_ev));

Expand Down Expand Up @@ -160,12 +197,16 @@ static void reconfigured_cb(struct bt_l2cap_chan *l2cap_chan)
#endif

static const struct bt_l2cap_chan_ops l2cap_ops = {
.alloc_buf = alloc_buf_cb,
.recv = recv_cb,
.connected = connected_cb,
.disconnected = disconnected_cb,
#if defined(CONFIG_BT_L2CAP_SEG_RECV)
.seg_recv = seg_recv_cb,
#else
.alloc_buf = alloc_buf_cb,
.recv = recv_cb,
#endif
.connected = connected_cb,
.disconnected = disconnected_cb,
#if defined(CONFIG_BT_L2CAP_ECRED)
.reconfigured = reconfigured_cb,
.reconfigured = reconfigured_cb,
#endif
};

Expand Down Expand Up @@ -222,10 +263,15 @@ static uint8_t connect(const void *cmd, uint16_t cmd_len,
}
chan->le.chan.ops = &l2cap_ops;
chan->le.rx.mtu = mtu;
#if defined(CONFIG_BT_L2CAP_SEG_RECV)
chan->le.rx.mps = L2CAP_MPS;
#endif
rp->chan_id[i] = chan->chan_id;
allocated_channels[i] = &chan->le.chan;

chan->hold_credit = cp->options & BTP_L2CAP_CONNECT_OPT_HOLD_CREDIT;

bt_l2cap_chan_give_credits(&chan->le.chan, 1);
}

if (cp->num == 1 && !ecfc) {
Expand Down Expand Up @@ -289,6 +335,7 @@ static uint8_t reconfigure(const void *cmd, uint16_t cmd_len,
{
const struct btp_l2cap_reconfigure_cmd *cp = cmd;
uint16_t mtu;
uint16_t mps;
struct bt_conn *conn;
int err;
struct bt_l2cap_chan *reconf_channels[CHANNELS + 1] = {};
Expand Down Expand Up @@ -321,7 +368,8 @@ static uint8_t reconfigure(const void *cmd, uint16_t cmd_len,
return BTP_STATUS_FAILED;
}

err = bt_l2cap_ecred_chan_reconfigure(reconf_channels, mtu);
mps = MIN(L2CAP_MPS, BT_L2CAP_RX_MTU);
err = bt_l2cap_ecred_chan_reconfigure_explicit(reconf_channels, mtu, mps);
if (err) {
bt_conn_unref(conn);
return BTP_STATUS_FAILED;
Expand Down Expand Up @@ -454,9 +502,14 @@ static int accept(struct bt_conn *conn, struct bt_l2cap_server *server,

chan->le.chan.ops = &l2cap_ops;
chan->le.rx.mtu = DATA_MTU_INITIAL;
#if defined(CONFIG_BT_L2CAP_SEG_RECV)
chan->le.rx.mps = L2CAP_MPS;
#endif

*l2cap_chan = &chan->le.chan;

bt_l2cap_chan_give_credits(&chan->le.chan, 1);

return 0;
}

Expand Down Expand Up @@ -524,7 +577,15 @@ static uint8_t credits(const void *cmd, uint16_t cmd_len,
if (!chan->in_use) {
return BTP_STATUS_FAILED;
}
#if defined(CONFIG_BT_L2CAP_SEG_RECV)
if (chan->pending_credits) {
if (bt_l2cap_chan_give_credits(&chan->le.chan, chan->pending_credits) < 0) {
return BTP_STATUS_FAILED;
}

chan->pending_credits = 0;
}
#else
if (chan->pending_credit) {
if (bt_l2cap_chan_recv_complete(&chan->le.chan,
chan->pending_credit) < 0) {
Expand All @@ -533,6 +594,7 @@ static uint8_t credits(const void *cmd, uint16_t cmd_len,

chan->pending_credit = NULL;
}
#endif

return BTP_STATUS_SUCCESS;
}
Expand Down
Loading