Skip to content

Commit

Permalink
[nrf fromlist] Bluetooth: Tester: Use BT_L2CAP_SEG_RECV for L2CAP tests
Browse files Browse the repository at this point in the history
This API gives better control on L2CAP COC credits and suits better
for Upper Tester implementation.

Upstream PR #: 80911

Signed-off-by: Szymon Janc <szymon.janc@codecoup.pl>
  • Loading branch information
sjanc authored and rlubos committed Nov 8, 2024
1 parent 1c79ce0 commit 17c202d
Show file tree
Hide file tree
Showing 5 changed files with 205 additions and 13 deletions.
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
100 changes: 100 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 @@ -3032,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
3 changes: 2 additions & 1 deletion tests/bluetooth/tester/prj.conf
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ CONFIG_BT_BONDABLE=y
CONFIG_BT_ATT_PREPARE_COUNT=12
CONFIG_BT_GATT_CLIENT=y
CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y
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 @@ -33,7 +35,6 @@ 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 Down
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

0 comments on commit 17c202d

Please sign in to comment.