Skip to content

Commit 086830f

Browse files
committed
[nrf fromlist] Bluetooth: Tester: Use BT_L2CAP_SEG_RECV for L2CAP tests
This API gives better control on L2CAP COC credits and suits better for Upper Tester implementation. Upstream PR #: 80911
1 parent 34e1692 commit 086830f

File tree

5 files changed

+201
-8
lines changed

5 files changed

+201
-8
lines changed

include/zephyr/bluetooth/l2cap.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
* @{
1919
*/
2020

21+
#include <stdint.h>
2122
#include <sys/types.h>
2223

2324
#include <zephyr/sys/atomic.h>
@@ -552,6 +553,27 @@ int bt_l2cap_ecred_chan_connect(struct bt_conn *conn,
552553
*/
553554
int bt_l2cap_ecred_chan_reconfigure(struct bt_l2cap_chan **chans, uint16_t mtu);
554555

556+
/** @brief Reconfigure Enhanced Credit Based L2CAP channels
557+
*
558+
* Experimental API to reconfigure with explicit MPS and MTU values.
559+
*
560+
* Reconfigure up to 5 L2CAP channels. Channels must be from the same bt_conn.
561+
* Once reconfiguration is completed each channel reconfigured() callback will
562+
* be called. MTU cannot be decreased on any of provided channels.
563+
*
564+
* Available only when @kconfig{CONFIG_BT_L2CAP_RECONFIGURE_EXPLICIT} is
565+
* enabled.
566+
*
567+
* @param chans Array of channel objects. Null-terminated. Elements after the
568+
* first 5 are silently ignored.
569+
* @param mtu Channel MTU to reconfigure to.
570+
* @param mtu Channel MPS to reconfigure to.
571+
*
572+
* @return 0 in case of success or negative value in case of error.
573+
*/
574+
int bt_l2cap_ecred_chan_reconfigure_explicit(struct bt_l2cap_chan **chans, uint16_t mtu,
575+
uint16_t mps);
576+
555577
/** @brief Connect L2CAP channel
556578
*
557579
* Connect L2CAP channel by PSM, once the connection is completed channel

subsys/bluetooth/host/Kconfig.l2cap

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,12 @@ config BT_L2CAP_SEG_RECV
7070
This API enforces conformance with L2CAP TS, but is otherwise as
7171
flexible and semantically simple as possible.
7272

73+
config BT_L2CAP_RECONFIGURE_EXPLICIT
74+
bool "L2CAP Explicit reconfigure API [EXPERIMENTAL]"
75+
select EXPERIMENTAL
76+
help
77+
78+
Enable API for explicit reconfiguration of an L2CAP channel's MTU and
79+
MPS.
80+
7381
endmenu

subsys/bluetooth/host/l2cap.c

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3032,6 +3032,105 @@ int bt_l2cap_ecred_chan_reconfigure(struct bt_l2cap_chan **chans, uint16_t mtu)
30323032
return 0;
30333033
}
30343034

3035+
#if defined(CONFIG_BT_L2CAP_RECONFIGURE_EXPLICIT)
3036+
int bt_l2cap_ecred_chan_reconfigure_explicit(struct bt_l2cap_chan **chans, uint16_t mtu,
3037+
uint16_t mps)
3038+
{
3039+
struct bt_l2cap_ecred_reconf_req *req;
3040+
struct bt_conn *conn = NULL;
3041+
struct bt_l2cap_le_chan *ch;
3042+
struct net_buf *buf;
3043+
bool multiple_chan;
3044+
uint8_t ident;
3045+
int i;
3046+
3047+
LOG_DBG("chans %p mtu 0x%04x", chans, mtu);
3048+
3049+
if (!chans) {
3050+
return -EINVAL;
3051+
}
3052+
3053+
if (chans[0] == NULL) {
3054+
return -EINVAL;
3055+
}
3056+
3057+
if (mps > BT_L2CAP_RX_MTU) {
3058+
return -EINVAL;
3059+
}
3060+
3061+
multiple_chan = chans[1] != NULL;
3062+
3063+
for (i = 0; i < L2CAP_ECRED_CHAN_MAX_PER_REQ; i++) {
3064+
if (!chans[i]) {
3065+
break;
3066+
}
3067+
3068+
/* validate that all channels are from same connection */
3069+
if (conn) {
3070+
if (conn != chans[i]->conn) {
3071+
return -EINVAL;
3072+
}
3073+
} else {
3074+
conn = chans[i]->conn;
3075+
}
3076+
3077+
/* validate MTU is not decreased */
3078+
if (mtu < BT_L2CAP_LE_CHAN(chans[i])->rx.mtu) {
3079+
return -EINVAL;
3080+
}
3081+
3082+
/* MPS is not allowed to decrease when reconfiguring multiple channels.
3083+
* Core Specification 3.A.4.27 v6.0
3084+
*/
3085+
if (multiple_chan && mps < BT_L2CAP_LE_CHAN(chans[i])->rx.mps) {
3086+
return -EINVAL;
3087+
}
3088+
}
3089+
3090+
if (!conn) {
3091+
return -ENOTCONN;
3092+
}
3093+
3094+
if (conn->type != BT_CONN_TYPE_LE) {
3095+
return -EINVAL;
3096+
}
3097+
3098+
/* allow only 1 request at time */
3099+
if (l2cap_find_pending_reconf(conn)) {
3100+
return -EBUSY;
3101+
}
3102+
3103+
ident = get_ident();
3104+
3105+
buf = l2cap_create_le_sig_pdu(BT_L2CAP_ECRED_RECONF_REQ, ident,
3106+
sizeof(*req) + (i * sizeof(uint16_t)));
3107+
if (!buf) {
3108+
return -ENOMEM;
3109+
}
3110+
3111+
req = net_buf_add(buf, sizeof(*req));
3112+
req->mtu = sys_cpu_to_le16(mtu);
3113+
req->mps = sys_cpu_to_le16(mps);
3114+
3115+
for (int j = 0; j < i; j++) {
3116+
ch = BT_L2CAP_LE_CHAN(chans[j]);
3117+
3118+
ch->ident = ident;
3119+
ch->pending_rx_mtu = mtu;
3120+
3121+
net_buf_add_le16(buf, ch->rx.cid);
3122+
};
3123+
3124+
/* We set the RTX timer on one of the supplied channels, but when the
3125+
* request resolves or times out we will act on all the channels in the
3126+
* supplied array, using the ident field to find them.
3127+
*/
3128+
l2cap_chan_send_req(chans[0], buf, L2CAP_CONN_TIMEOUT);
3129+
3130+
return 0;
3131+
}
3132+
#endif /* defined(CONFIG_BT_L2CAP_RECONFIGURE_EXPLICIT) */
3133+
30353134
#endif /* defined(CONFIG_BT_L2CAP_ECRED) */
30363135

30373136
int bt_l2cap_chan_connect(struct bt_conn *conn, struct bt_l2cap_chan *chan,

tests/bluetooth/tester/prj.conf

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ CONFIG_BT_BONDABLE=y
1616
CONFIG_BT_ATT_PREPARE_COUNT=12
1717
CONFIG_BT_GATT_CLIENT=y
1818
CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y
19+
CONFIG_BT_L2CAP_SEG_RECV=y
20+
CONFIG_BT_L2CAP_RECONFIGURE_EXPLICIT=y
1921
CONFIG_BT_DEVICE_NAME="Tester"
2022
CONFIG_BT_DEVICE_NAME_MAX=32
2123
CONFIG_BT_DEVICE_NAME_DYNAMIC=y
@@ -33,7 +35,6 @@ CONFIG_BT_GATT_DYNAMIC_DB=y
3335
CONFIG_BT_EXT_ADV=y
3436
CONFIG_BT_PER_ADV=y
3537
CONFIG_BT_PER_ADV_SYNC=y
36-
CONFIG_BT_BUF_ACL_RX_SIZE=100
3738
CONFIG_BT_RX_STACK_SIZE=4096
3839

3940
CONFIG_BT_TESTING=y

tests/bluetooth/tester/src/btp_l2cap.c

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,15 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_BTTESTER_LOG_LEVEL);
1919

2020
#include "btp/btp.h"
2121

22-
#define DATA_MTU_INITIAL 128
23-
#define DATA_MTU 256
24-
#define DATA_BUF_SIZE BT_L2CAP_SDU_BUF_SIZE(DATA_MTU)
22+
#define L2CAP_MPS 96
23+
#define DATA_MTU (3 * L2CAP_MPS)
24+
#define DATA_MTU_INITIAL (2 * L2CAP_MPS)
25+
2526
#define CHANNELS 2
2627
#define SERVERS 1
2728

28-
NET_BUF_POOL_FIXED_DEFINE(data_pool, CHANNELS, DATA_BUF_SIZE, CONFIG_BT_CONN_TX_USER_DATA_SIZE,
29-
NULL);
29+
NET_BUF_POOL_FIXED_DEFINE(data_pool, CHANNELS, BT_L2CAP_SDU_BUF_SIZE(DATA_MTU),
30+
CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL);
3031

3132
static bool authorize_flag;
3233
static uint8_t req_keysize;
@@ -36,18 +37,51 @@ static struct channel {
3637
struct bt_l2cap_le_chan le;
3738
bool in_use;
3839
bool hold_credit;
40+
#if defined(CONFIG_BT_L2CAP_SEG_RECV)
41+
unsigned int pending_credits;
42+
uint8_t recv_cb_buf[DATA_MTU + sizeof(struct btp_l2cap_data_received_ev)];
43+
#else
3944
struct net_buf *pending_credit;
45+
#endif
4046
} channels[CHANNELS];
4147

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

51+
#if defined(CONFIG_BT_L2CAP_SEG_RECV)
52+
static void seg_recv_cb(struct bt_l2cap_chan *l2cap_chan, size_t sdu_len,
53+
off_t seg_offset, struct net_buf_simple *seg)
54+
{
55+
struct btp_l2cap_data_received_ev *ev;
56+
struct bt_l2cap_le_chan *l2cap_le_chan = CONTAINER_OF(
57+
l2cap_chan, struct bt_l2cap_le_chan, chan);
58+
struct channel *chan = CONTAINER_OF(l2cap_le_chan, struct channel, le);
59+
60+
ev = (void *)chan->recv_cb_buf;
61+
memcpy(&ev->data[seg_offset], seg->data, seg->len);
62+
63+
/* complete SDU received */
64+
if (seg_offset + seg->len == sdu_len) {
65+
ev->chan_id = chan->chan_id;
66+
ev->data_length = sys_cpu_to_le16(sdu_len);
67+
68+
tester_event(BTP_SERVICE_ID_L2CAP, BTP_L2CAP_EV_DATA_RECEIVED,
69+
chan->recv_cb_buf, sizeof(*ev) + sdu_len);
70+
}
71+
72+
if (chan->hold_credit) {
73+
chan->pending_credits++;
74+
} else {
75+
bt_l2cap_chan_give_credits(l2cap_chan, 1);
76+
}
77+
}
78+
#else
4579
static struct net_buf *alloc_buf_cb(struct bt_l2cap_chan *chan)
4680
{
4781
return net_buf_alloc(&data_pool, K_FOREVER);
4882
}
4983

50-
static uint8_t recv_cb_buf[DATA_BUF_SIZE + sizeof(struct btp_l2cap_data_received_ev)];
84+
static uint8_t recv_cb_buf[DATA_MTU + sizeof(struct btp_l2cap_data_received_ev)];
5185

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

74108
return 0;
75109
}
110+
#endif
76111

77112
static void connected_cb(struct bt_l2cap_chan *l2cap_chan)
78113
{
@@ -111,11 +146,13 @@ static void disconnected_cb(struct bt_l2cap_chan *l2cap_chan)
111146
struct channel *chan = CONTAINER_OF(l2cap_le_chan, struct channel, le);
112147
struct bt_conn_info info;
113148

149+
#if !defined(CONFIG_BT_L2CAP_SEG_RECV)
114150
/* release netbuf on premature disconnection */
115151
if (chan->pending_credit) {
116152
net_buf_unref(chan->pending_credit);
117153
chan->pending_credit = NULL;
118154
}
155+
#endif
119156

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

@@ -160,8 +197,12 @@ static void reconfigured_cb(struct bt_l2cap_chan *l2cap_chan)
160197
#endif
161198

162199
static const struct bt_l2cap_chan_ops l2cap_ops = {
200+
#if defined(CONFIG_BT_L2CAP_SEG_RECV)
201+
.seg_recv = seg_recv_cb,
202+
#else
163203
.alloc_buf = alloc_buf_cb,
164204
.recv = recv_cb,
205+
#endif
165206
.connected = connected_cb,
166207
.disconnected = disconnected_cb,
167208
#if defined(CONFIG_BT_L2CAP_ECRED)
@@ -222,10 +263,15 @@ static uint8_t connect(const void *cmd, uint16_t cmd_len,
222263
}
223264
chan->le.chan.ops = &l2cap_ops;
224265
chan->le.rx.mtu = mtu;
266+
#if defined(CONFIG_BT_L2CAP_SEG_RECV)
267+
chan->le.rx.mps = L2CAP_MPS;
268+
#endif
225269
rp->chan_id[i] = chan->chan_id;
226270
allocated_channels[i] = &chan->le.chan;
227271

228272
chan->hold_credit = cp->options & BTP_L2CAP_CONNECT_OPT_HOLD_CREDIT;
273+
274+
bt_l2cap_chan_give_credits(&chan->le.chan, 1);
229275
}
230276

231277
if (cp->num == 1 && !ecfc) {
@@ -289,6 +335,7 @@ static uint8_t reconfigure(const void *cmd, uint16_t cmd_len,
289335
{
290336
const struct btp_l2cap_reconfigure_cmd *cp = cmd;
291337
uint16_t mtu;
338+
uint16_t mps;
292339
struct bt_conn *conn;
293340
int err;
294341
struct bt_l2cap_chan *reconf_channels[CHANNELS + 1] = {};
@@ -321,7 +368,8 @@ static uint8_t reconfigure(const void *cmd, uint16_t cmd_len,
321368
return BTP_STATUS_FAILED;
322369
}
323370

324-
err = bt_l2cap_ecred_chan_reconfigure(reconf_channels, mtu);
371+
mps = MIN(L2CAP_MPS, BT_L2CAP_RX_MTU);
372+
err = bt_l2cap_ecred_chan_reconfigure_explicit(reconf_channels, mtu, mps);
325373
if (err) {
326374
bt_conn_unref(conn);
327375
return BTP_STATUS_FAILED;
@@ -454,9 +502,14 @@ static int accept(struct bt_conn *conn, struct bt_l2cap_server *server,
454502

455503
chan->le.chan.ops = &l2cap_ops;
456504
chan->le.rx.mtu = DATA_MTU_INITIAL;
505+
#if defined(CONFIG_BT_L2CAP_SEG_RECV)
506+
chan->le.rx.mps = L2CAP_MPS;
507+
#endif
457508

458509
*l2cap_chan = &chan->le.chan;
459510

511+
bt_l2cap_chan_give_credits(&chan->le.chan, 1);
512+
460513
return 0;
461514
}
462515

@@ -524,7 +577,16 @@ static uint8_t credits(const void *cmd, uint16_t cmd_len,
524577
if (!chan->in_use) {
525578
return BTP_STATUS_FAILED;
526579
}
580+
#if defined(CONFIG_BT_L2CAP_SEG_RECV)
581+
if (chan->pending_credits) {
582+
if (bt_l2cap_chan_give_credits(&chan->le.chan,
583+
chan->pending_credits) < 0) {
584+
return BTP_STATUS_FAILED;
585+
}
527586

587+
chan->pending_credits = 0;
588+
}
589+
#else
528590
if (chan->pending_credit) {
529591
if (bt_l2cap_chan_recv_complete(&chan->le.chan,
530592
chan->pending_credit) < 0) {
@@ -533,6 +595,7 @@ static uint8_t credits(const void *cmd, uint16_t cmd_len,
533595

534596
chan->pending_credit = NULL;
535597
}
598+
#endif
536599

537600
return BTP_STATUS_SUCCESS;
538601
}

0 commit comments

Comments
 (0)