Skip to content

Commit 5a8daff

Browse files
alwa-nordicsjanc
authored andcommitted
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. Co-authored-by: Szymon Janc <[email protected]> Signed-off-by: Aleksander Wasaznik <[email protected]>
1 parent c002b1d commit 5a8daff

File tree

7 files changed

+276
-35
lines changed

7 files changed

+276
-35
lines changed

include/zephyr/bluetooth/l2cap.h

Lines changed: 83 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>
@@ -83,6 +84,42 @@ extern "C" {
8384
*/
8485
#define BT_L2CAP_SDU_BUF_SIZE(mtu) BT_L2CAP_BUF_SIZE(BT_L2CAP_SDU_HDR_SIZE + (mtu))
8586

87+
/** @brief L2CAP ECRED minimum MTU
88+
*
89+
* The minimum MTU for an L2CAP Enhanced Credit Based Connection.
90+
*
91+
* This requirement is inferred from text in Core 3.A.4.25 v6.0:
92+
*
93+
* L2CAP implementations shall support a minimum MTU size of 64
94+
* octets for these channels.
95+
*/
96+
#define BT_L2CAP_ECRED_MIN_MTU 64
97+
98+
/** @brief L2CAP ECRED minimum MPS
99+
*
100+
* The minimum MPS for an L2CAP Enhanced Credit Based Connection.
101+
*
102+
* This requirement is inferred from text in Core 3.A.4.25 v6.0:
103+
*
104+
* L2CAP implementations shall support a minimum MPS of 64 and may
105+
* support an MPS up to 65533 octets for these channels.
106+
*/
107+
#define BT_L2CAP_ECRED_MIN_MPS 64
108+
109+
/** @brief The maximum number of channels in ECRED L2CAP signaling PDUs
110+
*
111+
* Currently, this is the maximum number of channels referred to in the
112+
* following PDUs:
113+
* - L2CAP_CREDIT_BASED_CONNECTION_REQ
114+
* - L2CAP_CREDIT_BASED_RECONFIGURE_REQ
115+
*
116+
* @warning The commonality is inferred between the PDUs. The Bluetooth
117+
* specification treats these as separate numbers and does now
118+
* guarantee the same limit for potential future ECRED L2CAP signaling
119+
* PDUs.
120+
*/
121+
#define BT_L2CAP_ECRED_CHAN_MAX_PER_REQ 5
122+
86123
struct bt_l2cap_chan;
87124

88125
/** @typedef bt_l2cap_chan_destroy_t
@@ -557,6 +594,52 @@ int bt_l2cap_ecred_chan_connect(struct bt_conn *conn,
557594
*/
558595
int bt_l2cap_ecred_chan_reconfigure(struct bt_l2cap_chan **chans, uint16_t mtu);
559596

597+
/** @brief Reconfigure Enhanced Credit Based L2CAP channels
598+
*
599+
* Experimental API to reconfigure L2CAP ECRED channels with explicit MPS and
600+
* MTU values.
601+
*
602+
* Pend a L2CAP ECRED reconfiguration for up to 5 channels. All provided
603+
* channels must share the same @ref bt_conn.
604+
*
605+
* This API cannot decrease the MTU of any channel, and it cannot decrease the
606+
* MPS of any channel when more than one channel is provided.
607+
*
608+
* There is no dedicated callback for this operation, but whenever a peer
609+
* responds to a reconfiguration request, each affected channel's
610+
* reconfigured() callback is invoked.
611+
*
612+
* This function may block.
613+
*
614+
* @warning Known issue: The implementation returns -EBUSY if there already is
615+
* an ongoing reconfigure operation on the same connection. The caller may try
616+
* again later. There is no event signaling when the existing operation
617+
* finishes.
618+
*
619+
* @warning Known issue: The implementation returns -ENOMEM when unable to
620+
* allocate. The caller may try again later. There is no event signaling the
621+
* availability of buffers.
622+
*
623+
* @kconfig_dep{CONFIG_BT_L2CAP_RECONFIGURE_EXPLICIT}
624+
*
625+
* @param chans Array of channels to reconfigure. Must be non-empty and
626+
* contain at most 5 (@ref BT_L2CAP_ECRED_CHAN_MAX_PER_REQ)
627+
* elements.
628+
* @param chan_count Number of channels in the array.
629+
* @param mtu Desired MTU. Must be at least @ref BT_L2CAP_ECRED_MIN_MTU.
630+
* @param mps Desired MPS. Must be in range @ref BT_L2CAP_ECRED_MIN_MPS
631+
* to @ref BT_L2CAP_RX_MTU.
632+
*
633+
* @retval 0 Successfully pended operation.
634+
* @retval -EINVAL Bad arguments. See above requirements.
635+
* @retval -ENOTCONN Connection object is not in connected state.
636+
* @retval -EBUSY Another outgoing reconfiguration is pending on the same
637+
* connection.
638+
* @retval -ENOMEM Host is out of buffers.
639+
*/
640+
int bt_l2cap_ecred_chan_reconfigure_explicit(struct bt_l2cap_chan **chans, size_t chan_count,
641+
uint16_t mtu, uint16_t mps);
642+
560643
/** @brief Connect L2CAP channel
561644
*
562645
* Connect L2CAP channel by PSM, once the connection is completed channel

subsys/bluetooth/host/Kconfig.l2cap

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,18 @@ config BT_L2CAP_SEG_RECV
6262
bool "L2CAP Receive segment direct API [EXPERIMENTAL]"
6363
select EXPERIMENTAL
6464
help
65-
6665
Enable API for direct receiving of L2CAP SDU segments, bypassing the
6766
Host's fixed-function SDU re-assembler, RX SDU buffer management and
6867
credit issuer.
6968

7069
This API enforces conformance with L2CAP TS, but is otherwise as
7170
flexible and semantically simple as possible.
7271

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

subsys/bluetooth/host/att.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3668,15 +3668,15 @@ int bt_eatt_connect(struct bt_conn *conn, size_t num_channels)
36683668
}
36693669

36703670
while (offset < i) {
3671-
/* bt_l2cap_ecred_chan_connect() uses the first L2CAP_ECRED_CHAN_MAX_PER_REQ
3671+
/* bt_l2cap_ecred_chan_connect() uses the first BT_L2CAP_ECRED_CHAN_MAX_PER_REQ
36723672
* elements of the array or until a null-terminator is reached.
36733673
*/
36743674
err = bt_l2cap_ecred_chan_connect(conn, &chan[offset], BT_EATT_PSM);
36753675
if (err < 0) {
36763676
return err;
36773677
}
36783678

3679-
offset += L2CAP_ECRED_CHAN_MAX_PER_REQ;
3679+
offset += BT_L2CAP_ECRED_CHAN_MAX_PER_REQ;
36803680
}
36813681

36823682
return 0;
@@ -3767,15 +3767,15 @@ int bt_eatt_reconfigure(struct bt_conn *conn, uint16_t mtu)
37673767
}
37683768

37693769
while (offset < i) {
3770-
/* bt_l2cap_ecred_chan_reconfigure() uses the first L2CAP_ECRED_CHAN_MAX_PER_REQ
3770+
/* bt_l2cap_ecred_chan_reconfigure() uses the first BT_L2CAP_ECRED_CHAN_MAX_PER_REQ
37713771
* elements of the array or until a null-terminator is reached.
37723772
*/
37733773
err = bt_l2cap_ecred_chan_reconfigure(&chans[offset], mtu);
37743774
if (err < 0) {
37753775
return err;
37763776
}
37773777

3778-
offset += L2CAP_ECRED_CHAN_MAX_PER_REQ;
3778+
offset += BT_L2CAP_ECRED_CHAN_MAX_PER_REQ;
37793779
}
37803780

37813781
return 0;

subsys/bluetooth/host/l2cap.c

Lines changed: 106 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ LOG_MODULE_REGISTER(bt_l2cap, CONFIG_BT_L2CAP_LOG_LEVEL);
4040
#define CHAN_RX(_w) CONTAINER_OF(_w, struct bt_l2cap_le_chan, rx_work)
4141

4242
#define L2CAP_LE_MIN_MTU 23
43-
#define L2CAP_ECRED_MIN_MTU 64
4443

4544
#define L2CAP_LE_MAX_CREDITS (CONFIG_BT_BUF_ACL_RX_COUNT - 1)
4645

@@ -58,6 +57,10 @@ LOG_MODULE_REGISTER(bt_l2cap, CONFIG_BT_L2CAP_LOG_LEVEL);
5857

5958
#define L2CAP_CONN_TIMEOUT K_SECONDS(40)
6059
#define L2CAP_DISC_TIMEOUT K_SECONDS(2)
60+
/** @brief Local L2CAP RTX (Response Timeout eXpired)
61+
*
62+
* Specification-allowed range for the value of RTX is 1 to 60 seconds.
63+
*/
6164
#define L2CAP_RTX_TIMEOUT K_SECONDS(2)
6265

6366
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
@@ -613,15 +616,15 @@ static void l2cap_le_encrypt_change(struct bt_l2cap_chan *chan, uint8_t status)
613616

614617
#if defined(CONFIG_BT_L2CAP_ECRED)
615618
if (le->ident) {
616-
struct bt_l2cap_chan *echan[L2CAP_ECRED_CHAN_MAX_PER_REQ];
619+
struct bt_l2cap_chan *echan[BT_L2CAP_ECRED_CHAN_MAX_PER_REQ];
617620
struct bt_l2cap_chan *ch;
618621
int i = 0;
619622

620623
SYS_SLIST_FOR_EACH_CONTAINER(&chan->conn->channels, ch, node) {
621624
if (le->ident == BT_L2CAP_LE_CHAN(ch)->ident) {
622-
__ASSERT(i < L2CAP_ECRED_CHAN_MAX_PER_REQ,
623-
"There can only be L2CAP_ECRED_CHAN_MAX_PER_REQ channels "
624-
"from the same request.");
625+
__ASSERT(i < BT_L2CAP_ECRED_CHAN_MAX_PER_REQ,
626+
"There can only be BT_L2CAP_ECRED_CHAN_MAX_PER_REQ "
627+
"channels from the same request.");
625628
atomic_clear_bit(ch->status, BT_L2CAP_STATUS_ENCRYPT_PENDING);
626629
echan[i++] = ch;
627630
}
@@ -1518,14 +1521,14 @@ static void le_ecred_conn_req(struct bt_l2cap *l2cap, uint8_t ident,
15181521
struct net_buf *buf)
15191522
{
15201523
struct bt_conn *conn = l2cap->chan.chan.conn;
1521-
struct bt_l2cap_chan *chan[L2CAP_ECRED_CHAN_MAX_PER_REQ];
1524+
struct bt_l2cap_chan *chan[BT_L2CAP_ECRED_CHAN_MAX_PER_REQ];
15221525
struct bt_l2cap_le_chan *ch = NULL;
15231526
struct bt_l2cap_server *server;
15241527
struct bt_l2cap_ecred_conn_req *req;
15251528
struct bt_l2cap_ecred_conn_rsp *rsp;
15261529
uint16_t mtu, mps, credits, result = BT_L2CAP_LE_SUCCESS;
15271530
uint16_t psm = 0x0000;
1528-
uint16_t scid, dcid[L2CAP_ECRED_CHAN_MAX_PER_REQ];
1531+
uint16_t scid, dcid[BT_L2CAP_ECRED_CHAN_MAX_PER_REQ];
15291532
int i = 0;
15301533
uint8_t req_cid_count;
15311534
bool rsp_queued = false;
@@ -1544,7 +1547,7 @@ static void le_ecred_conn_req(struct bt_l2cap *l2cap, uint8_t ident,
15441547

15451548
if (buf->len > sizeof(dcid)) {
15461549
LOG_ERR("Too large LE conn req packet size");
1547-
req_cid_count = L2CAP_ECRED_CHAN_MAX_PER_REQ;
1550+
req_cid_count = BT_L2CAP_ECRED_CHAN_MAX_PER_REQ;
15481551
result = BT_L2CAP_LE_ERR_INVALID_PARAMS;
15491552
goto response;
15501553
}
@@ -1556,7 +1559,7 @@ static void le_ecred_conn_req(struct bt_l2cap *l2cap, uint8_t ident,
15561559

15571560
LOG_DBG("psm 0x%02x mtu %u mps %u credits %u", psm, mtu, mps, credits);
15581561

1559-
if (mtu < L2CAP_ECRED_MIN_MTU || mps < L2CAP_ECRED_MIN_MTU) {
1562+
if (mtu < BT_L2CAP_ECRED_MIN_MTU || mps < BT_L2CAP_ECRED_MIN_MTU) {
15601563
LOG_ERR("Invalid ecred conn req params. mtu %u mps %u", mtu, mps);
15611564
result = BT_L2CAP_LE_ERR_INVALID_PARAMS;
15621565
goto response;
@@ -1646,7 +1649,7 @@ static void le_ecred_reconf_req(struct bt_l2cap *l2cap, uint8_t ident,
16461649
struct net_buf *buf)
16471650
{
16481651
struct bt_conn *conn = l2cap->chan.chan.conn;
1649-
struct bt_l2cap_chan *chans[L2CAP_ECRED_CHAN_MAX_PER_REQ];
1652+
struct bt_l2cap_chan *chans[BT_L2CAP_ECRED_CHAN_MAX_PER_REQ];
16501653
struct bt_l2cap_ecred_reconf_req *req;
16511654
struct bt_l2cap_ecred_reconf_rsp *rsp;
16521655
uint16_t mtu, mps;
@@ -1664,18 +1667,18 @@ static void le_ecred_reconf_req(struct bt_l2cap *l2cap, uint8_t ident,
16641667
mtu = sys_le16_to_cpu(req->mtu);
16651668
mps = sys_le16_to_cpu(req->mps);
16661669

1667-
if (mps < L2CAP_ECRED_MIN_MTU) {
1670+
if (mps < BT_L2CAP_ECRED_MIN_MTU) {
16681671
result = BT_L2CAP_RECONF_OTHER_UNACCEPT;
16691672
goto response;
16701673
}
16711674

1672-
if (mtu < L2CAP_ECRED_MIN_MTU) {
1675+
if (mtu < BT_L2CAP_ECRED_MIN_MTU) {
16731676
result = BT_L2CAP_RECONF_INVALID_MTU;
16741677
goto response;
16751678
}
16761679

16771680
/* The specification only allows up to 5 CIDs in this packet */
1678-
if (buf->len > (L2CAP_ECRED_CHAN_MAX_PER_REQ * sizeof(scid))) {
1681+
if (buf->len > (BT_L2CAP_ECRED_CHAN_MAX_PER_REQ * sizeof(scid))) {
16791682
result = BT_L2CAP_RECONF_OTHER_UNACCEPT;
16801683
goto response;
16811684
}
@@ -2908,7 +2911,7 @@ int bt_l2cap_ecred_chan_connect(struct bt_conn *conn,
29082911
}
29092912

29102913
/* Init non-null channels */
2911-
for (i = 0; i < L2CAP_ECRED_CHAN_MAX_PER_REQ; i++) {
2914+
for (i = 0; i < BT_L2CAP_ECRED_CHAN_MAX_PER_REQ; i++) {
29122915
if (!chan[i]) {
29132916
break;
29142917
}
@@ -2962,7 +2965,7 @@ int bt_l2cap_ecred_chan_reconfigure(struct bt_l2cap_chan **chans, uint16_t mtu)
29622965
return -EINVAL;
29632966
}
29642967

2965-
for (i = 0; i < L2CAP_ECRED_CHAN_MAX_PER_REQ; i++) {
2968+
for (i = 0; i < BT_L2CAP_ECRED_CHAN_MAX_PER_REQ; i++) {
29662969
if (!chans[i]) {
29672970
break;
29682971
}
@@ -3035,6 +3038,94 @@ int bt_l2cap_ecred_chan_reconfigure(struct bt_l2cap_chan **chans, uint16_t mtu)
30353038
return 0;
30363039
}
30373040

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

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

subsys/bluetooth/host/l2cap_internal.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,6 @@ struct bt_l2cap_ecred_conn_rsp {
134134
uint16_t dcid[0];
135135
} __packed;
136136

137-
#define L2CAP_ECRED_CHAN_MAX_PER_REQ 5
138-
139137
#define BT_L2CAP_ECRED_RECONF_REQ 0x19
140138
struct bt_l2cap_ecred_reconf_req {
141139
uint16_t mtu;

0 commit comments

Comments
 (0)