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 <szymon.janc@codecoup.pl>
Signed-off-by: Aleksander Wasaznik <aleksander.wasaznik@nordicsemi.no>
This commit is contained in:
Aleksander Wasaznik 2024-11-12 16:04:41 +01:00 committed by Benjamin Cabé
commit 5a8daffc32
7 changed files with 276 additions and 35 deletions

View file

@ -18,6 +18,7 @@
* @{ * @{
*/ */
#include <stdint.h>
#include <sys/types.h> #include <sys/types.h>
#include <zephyr/sys/atomic.h> #include <zephyr/sys/atomic.h>
@ -83,6 +84,42 @@ extern "C" {
*/ */
#define BT_L2CAP_SDU_BUF_SIZE(mtu) BT_L2CAP_BUF_SIZE(BT_L2CAP_SDU_HDR_SIZE + (mtu)) #define BT_L2CAP_SDU_BUF_SIZE(mtu) BT_L2CAP_BUF_SIZE(BT_L2CAP_SDU_HDR_SIZE + (mtu))
/** @brief L2CAP ECRED minimum MTU
*
* The minimum MTU for an L2CAP Enhanced Credit Based Connection.
*
* This requirement is inferred from text in Core 3.A.4.25 v6.0:
*
* L2CAP implementations shall support a minimum MTU size of 64
* octets for these channels.
*/
#define BT_L2CAP_ECRED_MIN_MTU 64
/** @brief L2CAP ECRED minimum MPS
*
* The minimum MPS for an L2CAP Enhanced Credit Based Connection.
*
* This requirement is inferred from text in Core 3.A.4.25 v6.0:
*
* L2CAP implementations shall support a minimum MPS of 64 and may
* support an MPS up to 65533 octets for these channels.
*/
#define BT_L2CAP_ECRED_MIN_MPS 64
/** @brief The maximum number of channels in ECRED L2CAP signaling PDUs
*
* Currently, this is the maximum number of channels referred to in the
* following PDUs:
* - L2CAP_CREDIT_BASED_CONNECTION_REQ
* - L2CAP_CREDIT_BASED_RECONFIGURE_REQ
*
* @warning The commonality is inferred between the PDUs. The Bluetooth
* specification treats these as separate numbers and does now
* guarantee the same limit for potential future ECRED L2CAP signaling
* PDUs.
*/
#define BT_L2CAP_ECRED_CHAN_MAX_PER_REQ 5
struct bt_l2cap_chan; struct bt_l2cap_chan;
/** @typedef bt_l2cap_chan_destroy_t /** @typedef bt_l2cap_chan_destroy_t
@ -557,6 +594,52 @@ int bt_l2cap_ecred_chan_connect(struct bt_conn *conn,
*/ */
int bt_l2cap_ecred_chan_reconfigure(struct bt_l2cap_chan **chans, uint16_t mtu); 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 L2CAP ECRED channels with explicit MPS and
* MTU values.
*
* Pend a L2CAP ECRED reconfiguration for up to 5 channels. All provided
* channels must share the same @ref bt_conn.
*
* This API cannot decrease the MTU of any channel, and it cannot decrease the
* MPS of any channel when more than one channel is provided.
*
* There is no dedicated callback for this operation, but whenever a peer
* responds to a reconfiguration request, each affected channel's
* reconfigured() callback is invoked.
*
* This function may block.
*
* @warning Known issue: The implementation returns -EBUSY if there already is
* an ongoing reconfigure operation on the same connection. The caller may try
* again later. There is no event signaling when the existing operation
* finishes.
*
* @warning Known issue: The implementation returns -ENOMEM when unable to
* allocate. The caller may try again later. There is no event signaling the
* availability of buffers.
*
* @kconfig_dep{CONFIG_BT_L2CAP_RECONFIGURE_EXPLICIT}
*
* @param chans Array of channels to reconfigure. Must be non-empty and
* contain at most 5 (@ref BT_L2CAP_ECRED_CHAN_MAX_PER_REQ)
* elements.
* @param chan_count Number of channels in the array.
* @param mtu Desired MTU. Must be at least @ref BT_L2CAP_ECRED_MIN_MTU.
* @param mps Desired MPS. Must be in range @ref BT_L2CAP_ECRED_MIN_MPS
* to @ref BT_L2CAP_RX_MTU.
*
* @retval 0 Successfully pended operation.
* @retval -EINVAL Bad arguments. See above requirements.
* @retval -ENOTCONN Connection object is not in connected state.
* @retval -EBUSY Another outgoing reconfiguration is pending on the same
* connection.
* @retval -ENOMEM Host is out of buffers.
*/
int bt_l2cap_ecred_chan_reconfigure_explicit(struct bt_l2cap_chan **chans, size_t chan_count,
uint16_t mtu, uint16_t mps);
/** @brief Connect L2CAP channel /** @brief Connect L2CAP channel
* *
* Connect L2CAP channel by PSM, once the connection is completed channel * Connect L2CAP channel by PSM, once the connection is completed channel

View file

@ -62,7 +62,6 @@ config BT_L2CAP_SEG_RECV
bool "L2CAP Receive segment direct API [EXPERIMENTAL]" bool "L2CAP Receive segment direct API [EXPERIMENTAL]"
select EXPERIMENTAL select EXPERIMENTAL
help help
Enable API for direct receiving of L2CAP SDU segments, bypassing the Enable API for direct receiving of L2CAP SDU segments, bypassing the
Host's fixed-function SDU re-assembler, RX SDU buffer management and Host's fixed-function SDU re-assembler, RX SDU buffer management and
credit issuer. credit issuer.
@ -70,4 +69,11 @@ config BT_L2CAP_SEG_RECV
This API enforces conformance with L2CAP TS, but is otherwise as This API enforces conformance with L2CAP TS, but is otherwise as
flexible and semantically simple as possible. 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 endmenu

View file

@ -3668,7 +3668,7 @@ int bt_eatt_connect(struct bt_conn *conn, size_t num_channels)
} }
while (offset < i) { while (offset < i) {
/* bt_l2cap_ecred_chan_connect() uses the first L2CAP_ECRED_CHAN_MAX_PER_REQ /* bt_l2cap_ecred_chan_connect() uses the first BT_L2CAP_ECRED_CHAN_MAX_PER_REQ
* elements of the array or until a null-terminator is reached. * elements of the array or until a null-terminator is reached.
*/ */
err = bt_l2cap_ecred_chan_connect(conn, &chan[offset], BT_EATT_PSM); err = bt_l2cap_ecred_chan_connect(conn, &chan[offset], BT_EATT_PSM);
@ -3676,7 +3676,7 @@ int bt_eatt_connect(struct bt_conn *conn, size_t num_channels)
return err; return err;
} }
offset += L2CAP_ECRED_CHAN_MAX_PER_REQ; offset += BT_L2CAP_ECRED_CHAN_MAX_PER_REQ;
} }
return 0; return 0;
@ -3767,7 +3767,7 @@ int bt_eatt_reconfigure(struct bt_conn *conn, uint16_t mtu)
} }
while (offset < i) { while (offset < i) {
/* bt_l2cap_ecred_chan_reconfigure() uses the first L2CAP_ECRED_CHAN_MAX_PER_REQ /* bt_l2cap_ecred_chan_reconfigure() uses the first BT_L2CAP_ECRED_CHAN_MAX_PER_REQ
* elements of the array or until a null-terminator is reached. * elements of the array or until a null-terminator is reached.
*/ */
err = bt_l2cap_ecred_chan_reconfigure(&chans[offset], mtu); err = bt_l2cap_ecred_chan_reconfigure(&chans[offset], mtu);
@ -3775,7 +3775,7 @@ int bt_eatt_reconfigure(struct bt_conn *conn, uint16_t mtu)
return err; return err;
} }
offset += L2CAP_ECRED_CHAN_MAX_PER_REQ; offset += BT_L2CAP_ECRED_CHAN_MAX_PER_REQ;
} }
return 0; return 0;

View file

@ -40,7 +40,6 @@ LOG_MODULE_REGISTER(bt_l2cap, CONFIG_BT_L2CAP_LOG_LEVEL);
#define CHAN_RX(_w) CONTAINER_OF(_w, struct bt_l2cap_le_chan, rx_work) #define CHAN_RX(_w) CONTAINER_OF(_w, struct bt_l2cap_le_chan, rx_work)
#define L2CAP_LE_MIN_MTU 23 #define L2CAP_LE_MIN_MTU 23
#define L2CAP_ECRED_MIN_MTU 64
#define L2CAP_LE_MAX_CREDITS (CONFIG_BT_BUF_ACL_RX_COUNT - 1) #define L2CAP_LE_MAX_CREDITS (CONFIG_BT_BUF_ACL_RX_COUNT - 1)
@ -58,6 +57,10 @@ LOG_MODULE_REGISTER(bt_l2cap, CONFIG_BT_L2CAP_LOG_LEVEL);
#define L2CAP_CONN_TIMEOUT K_SECONDS(40) #define L2CAP_CONN_TIMEOUT K_SECONDS(40)
#define L2CAP_DISC_TIMEOUT K_SECONDS(2) #define L2CAP_DISC_TIMEOUT K_SECONDS(2)
/** @brief Local L2CAP RTX (Response Timeout eXpired)
*
* Specification-allowed range for the value of RTX is 1 to 60 seconds.
*/
#define L2CAP_RTX_TIMEOUT K_SECONDS(2) #define L2CAP_RTX_TIMEOUT K_SECONDS(2)
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL) #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)
#if defined(CONFIG_BT_L2CAP_ECRED) #if defined(CONFIG_BT_L2CAP_ECRED)
if (le->ident) { if (le->ident) {
struct bt_l2cap_chan *echan[L2CAP_ECRED_CHAN_MAX_PER_REQ]; struct bt_l2cap_chan *echan[BT_L2CAP_ECRED_CHAN_MAX_PER_REQ];
struct bt_l2cap_chan *ch; struct bt_l2cap_chan *ch;
int i = 0; int i = 0;
SYS_SLIST_FOR_EACH_CONTAINER(&chan->conn->channels, ch, node) { SYS_SLIST_FOR_EACH_CONTAINER(&chan->conn->channels, ch, node) {
if (le->ident == BT_L2CAP_LE_CHAN(ch)->ident) { if (le->ident == BT_L2CAP_LE_CHAN(ch)->ident) {
__ASSERT(i < L2CAP_ECRED_CHAN_MAX_PER_REQ, __ASSERT(i < BT_L2CAP_ECRED_CHAN_MAX_PER_REQ,
"There can only be L2CAP_ECRED_CHAN_MAX_PER_REQ channels " "There can only be BT_L2CAP_ECRED_CHAN_MAX_PER_REQ "
"from the same request."); "channels from the same request.");
atomic_clear_bit(ch->status, BT_L2CAP_STATUS_ENCRYPT_PENDING); atomic_clear_bit(ch->status, BT_L2CAP_STATUS_ENCRYPT_PENDING);
echan[i++] = ch; echan[i++] = ch;
} }
@ -1518,14 +1521,14 @@ static void le_ecred_conn_req(struct bt_l2cap *l2cap, uint8_t ident,
struct net_buf *buf) struct net_buf *buf)
{ {
struct bt_conn *conn = l2cap->chan.chan.conn; struct bt_conn *conn = l2cap->chan.chan.conn;
struct bt_l2cap_chan *chan[L2CAP_ECRED_CHAN_MAX_PER_REQ]; struct bt_l2cap_chan *chan[BT_L2CAP_ECRED_CHAN_MAX_PER_REQ];
struct bt_l2cap_le_chan *ch = NULL; struct bt_l2cap_le_chan *ch = NULL;
struct bt_l2cap_server *server; struct bt_l2cap_server *server;
struct bt_l2cap_ecred_conn_req *req; struct bt_l2cap_ecred_conn_req *req;
struct bt_l2cap_ecred_conn_rsp *rsp; struct bt_l2cap_ecred_conn_rsp *rsp;
uint16_t mtu, mps, credits, result = BT_L2CAP_LE_SUCCESS; uint16_t mtu, mps, credits, result = BT_L2CAP_LE_SUCCESS;
uint16_t psm = 0x0000; uint16_t psm = 0x0000;
uint16_t scid, dcid[L2CAP_ECRED_CHAN_MAX_PER_REQ]; uint16_t scid, dcid[BT_L2CAP_ECRED_CHAN_MAX_PER_REQ];
int i = 0; int i = 0;
uint8_t req_cid_count; uint8_t req_cid_count;
bool rsp_queued = false; bool rsp_queued = false;
@ -1544,7 +1547,7 @@ static void le_ecred_conn_req(struct bt_l2cap *l2cap, uint8_t ident,
if (buf->len > sizeof(dcid)) { if (buf->len > sizeof(dcid)) {
LOG_ERR("Too large LE conn req packet size"); LOG_ERR("Too large LE conn req packet size");
req_cid_count = L2CAP_ECRED_CHAN_MAX_PER_REQ; req_cid_count = BT_L2CAP_ECRED_CHAN_MAX_PER_REQ;
result = BT_L2CAP_LE_ERR_INVALID_PARAMS; result = BT_L2CAP_LE_ERR_INVALID_PARAMS;
goto response; goto response;
} }
@ -1556,7 +1559,7 @@ static void le_ecred_conn_req(struct bt_l2cap *l2cap, uint8_t ident,
LOG_DBG("psm 0x%02x mtu %u mps %u credits %u", psm, mtu, mps, credits); LOG_DBG("psm 0x%02x mtu %u mps %u credits %u", psm, mtu, mps, credits);
if (mtu < L2CAP_ECRED_MIN_MTU || mps < L2CAP_ECRED_MIN_MTU) { if (mtu < BT_L2CAP_ECRED_MIN_MTU || mps < BT_L2CAP_ECRED_MIN_MTU) {
LOG_ERR("Invalid ecred conn req params. mtu %u mps %u", mtu, mps); LOG_ERR("Invalid ecred conn req params. mtu %u mps %u", mtu, mps);
result = BT_L2CAP_LE_ERR_INVALID_PARAMS; result = BT_L2CAP_LE_ERR_INVALID_PARAMS;
goto response; goto response;
@ -1646,7 +1649,7 @@ static void le_ecred_reconf_req(struct bt_l2cap *l2cap, uint8_t ident,
struct net_buf *buf) struct net_buf *buf)
{ {
struct bt_conn *conn = l2cap->chan.chan.conn; struct bt_conn *conn = l2cap->chan.chan.conn;
struct bt_l2cap_chan *chans[L2CAP_ECRED_CHAN_MAX_PER_REQ]; struct bt_l2cap_chan *chans[BT_L2CAP_ECRED_CHAN_MAX_PER_REQ];
struct bt_l2cap_ecred_reconf_req *req; struct bt_l2cap_ecred_reconf_req *req;
struct bt_l2cap_ecred_reconf_rsp *rsp; struct bt_l2cap_ecred_reconf_rsp *rsp;
uint16_t mtu, mps; uint16_t mtu, mps;
@ -1664,18 +1667,18 @@ static void le_ecred_reconf_req(struct bt_l2cap *l2cap, uint8_t ident,
mtu = sys_le16_to_cpu(req->mtu); mtu = sys_le16_to_cpu(req->mtu);
mps = sys_le16_to_cpu(req->mps); mps = sys_le16_to_cpu(req->mps);
if (mps < L2CAP_ECRED_MIN_MTU) { if (mps < BT_L2CAP_ECRED_MIN_MTU) {
result = BT_L2CAP_RECONF_OTHER_UNACCEPT; result = BT_L2CAP_RECONF_OTHER_UNACCEPT;
goto response; goto response;
} }
if (mtu < L2CAP_ECRED_MIN_MTU) { if (mtu < BT_L2CAP_ECRED_MIN_MTU) {
result = BT_L2CAP_RECONF_INVALID_MTU; result = BT_L2CAP_RECONF_INVALID_MTU;
goto response; goto response;
} }
/* The specification only allows up to 5 CIDs in this packet */ /* The specification only allows up to 5 CIDs in this packet */
if (buf->len > (L2CAP_ECRED_CHAN_MAX_PER_REQ * sizeof(scid))) { if (buf->len > (BT_L2CAP_ECRED_CHAN_MAX_PER_REQ * sizeof(scid))) {
result = BT_L2CAP_RECONF_OTHER_UNACCEPT; result = BT_L2CAP_RECONF_OTHER_UNACCEPT;
goto response; goto response;
} }
@ -2908,7 +2911,7 @@ int bt_l2cap_ecred_chan_connect(struct bt_conn *conn,
} }
/* Init non-null channels */ /* Init non-null channels */
for (i = 0; i < L2CAP_ECRED_CHAN_MAX_PER_REQ; i++) { for (i = 0; i < BT_L2CAP_ECRED_CHAN_MAX_PER_REQ; i++) {
if (!chan[i]) { if (!chan[i]) {
break; break;
} }
@ -2962,7 +2965,7 @@ int bt_l2cap_ecred_chan_reconfigure(struct bt_l2cap_chan **chans, uint16_t mtu)
return -EINVAL; return -EINVAL;
} }
for (i = 0; i < L2CAP_ECRED_CHAN_MAX_PER_REQ; i++) { for (i = 0; i < BT_L2CAP_ECRED_CHAN_MAX_PER_REQ; i++) {
if (!chans[i]) { if (!chans[i]) {
break; break;
} }
@ -3035,6 +3038,94 @@ int bt_l2cap_ecred_chan_reconfigure(struct bt_l2cap_chan **chans, uint16_t mtu)
return 0; return 0;
} }
#if defined(CONFIG_BT_L2CAP_RECONFIGURE_EXPLICIT)
int bt_l2cap_ecred_chan_reconfigure_explicit(struct bt_l2cap_chan **chans, size_t chan_count,
uint16_t mtu, uint16_t mps)
{
struct bt_l2cap_ecred_reconf_req *req;
struct bt_conn *conn = NULL;
struct net_buf *buf;
uint8_t ident;
LOG_DBG("chans %p chan_count %u mtu 0x%04x mps 0x%04x", chans, chan_count, mtu, mps);
if (!chans || !IN_RANGE(chan_count, 1, BT_L2CAP_ECRED_CHAN_MAX_PER_REQ)) {
return -EINVAL;
}
if (!IN_RANGE(mps, BT_L2CAP_ECRED_MIN_MPS, BT_L2CAP_RX_MTU)) {
return -EINVAL;
}
for (size_t i = 0; i < chan_count; i++) {
/* 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 (chan_count > 1 && 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) + (chan_count * 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 (size_t i = 0; i < chan_count; i++) {
struct bt_l2cap_le_chan *ch;
ch = BT_L2CAP_LE_CHAN(chans[i]);
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) */ #endif /* defined(CONFIG_BT_L2CAP_ECRED) */
int bt_l2cap_chan_connect(struct bt_conn *conn, struct bt_l2cap_chan *chan, int bt_l2cap_chan_connect(struct bt_conn *conn, struct bt_l2cap_chan *chan,

View file

@ -134,8 +134,6 @@ struct bt_l2cap_ecred_conn_rsp {
uint16_t dcid[0]; uint16_t dcid[0];
} __packed; } __packed;
#define L2CAP_ECRED_CHAN_MAX_PER_REQ 5
#define BT_L2CAP_ECRED_RECONF_REQ 0x19 #define BT_L2CAP_ECRED_RECONF_REQ 0x19
struct bt_l2cap_ecred_reconf_req { struct bt_l2cap_ecred_reconf_req {
uint16_t mtu; uint16_t mtu;

View file

@ -16,6 +16,8 @@ CONFIG_BT_BONDABLE=y
CONFIG_BT_ATT_PREPARE_COUNT=12 CONFIG_BT_ATT_PREPARE_COUNT=12
CONFIG_BT_GATT_CLIENT=y CONFIG_BT_GATT_CLIENT=y
CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=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="Tester"
CONFIG_BT_DEVICE_NAME_MAX=32 CONFIG_BT_DEVICE_NAME_MAX=32
CONFIG_BT_DEVICE_NAME_DYNAMIC=y CONFIG_BT_DEVICE_NAME_DYNAMIC=y
@ -35,7 +37,6 @@ CONFIG_BT_GATT_DYNAMIC_DB=y
CONFIG_BT_EXT_ADV=y CONFIG_BT_EXT_ADV=y
CONFIG_BT_PER_ADV=y CONFIG_BT_PER_ADV=y
CONFIG_BT_PER_ADV_SYNC=y CONFIG_BT_PER_ADV_SYNC=y
CONFIG_BT_BUF_ACL_RX_SIZE=100
CONFIG_BT_RX_STACK_SIZE=4096 CONFIG_BT_RX_STACK_SIZE=4096
CONFIG_BT_TESTING=y CONFIG_BT_TESTING=y

View file

@ -19,14 +19,15 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_BTTESTER_LOG_LEVEL);
#include "btp/btp.h" #include "btp/btp.h"
#define DATA_MTU_INITIAL 128 #define L2CAP_MPS 96
#define DATA_MTU 256 #define DATA_MTU (3 * L2CAP_MPS)
#define DATA_BUF_SIZE BT_L2CAP_SDU_BUF_SIZE(DATA_MTU) #define DATA_MTU_INITIAL (2 * L2CAP_MPS)
#define CHANNELS 2 #define CHANNELS 2
#define SERVERS 1 #define SERVERS 1
NET_BUF_POOL_FIXED_DEFINE(data_pool, CHANNELS, DATA_BUF_SIZE, CONFIG_BT_CONN_TX_USER_DATA_SIZE, NET_BUF_POOL_FIXED_DEFINE(data_pool, CHANNELS, BT_L2CAP_SDU_BUF_SIZE(DATA_MTU),
NULL); CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL);
static bool authorize_flag; static bool authorize_flag;
static uint8_t req_keysize; static uint8_t req_keysize;
@ -36,18 +37,51 @@ static struct channel {
struct bt_l2cap_le_chan le; struct bt_l2cap_le_chan le;
bool in_use; bool in_use;
bool hold_credit; 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; struct net_buf *pending_credit;
#endif
} channels[CHANNELS]; } channels[CHANNELS];
/* TODO Extend to support multiple servers */ /* TODO Extend to support multiple servers */
static struct bt_l2cap_server servers[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) static struct net_buf *alloc_buf_cb(struct bt_l2cap_chan *chan)
{ {
return net_buf_alloc(&data_pool, K_FOREVER); 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) static int recv_cb(struct bt_l2cap_chan *l2cap_chan, struct net_buf *buf)
{ {
@ -73,6 +107,7 @@ static int recv_cb(struct bt_l2cap_chan *l2cap_chan, struct net_buf *buf)
return 0; return 0;
} }
#endif
static void connected_cb(struct bt_l2cap_chan *l2cap_chan) static void connected_cb(struct bt_l2cap_chan *l2cap_chan)
{ {
@ -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 channel *chan = CONTAINER_OF(l2cap_le_chan, struct channel, le);
struct bt_conn_info info; struct bt_conn_info info;
#if !defined(CONFIG_BT_L2CAP_SEG_RECV)
/* release netbuf on premature disconnection */ /* release netbuf on premature disconnection */
if (chan->pending_credit) { if (chan->pending_credit) {
net_buf_unref(chan->pending_credit); net_buf_unref(chan->pending_credit);
chan->pending_credit = NULL; chan->pending_credit = NULL;
} }
#endif
(void)memset(&ev, 0, sizeof(struct btp_l2cap_disconnected_ev)); (void)memset(&ev, 0, sizeof(struct btp_l2cap_disconnected_ev));
@ -160,12 +197,16 @@ static void reconfigured_cb(struct bt_l2cap_chan *l2cap_chan)
#endif #endif
static const struct bt_l2cap_chan_ops l2cap_ops = { static const struct bt_l2cap_chan_ops l2cap_ops = {
.alloc_buf = alloc_buf_cb, #if defined(CONFIG_BT_L2CAP_SEG_RECV)
.recv = recv_cb, .seg_recv = seg_recv_cb,
.connected = connected_cb, #else
.disconnected = disconnected_cb, .alloc_buf = alloc_buf_cb,
.recv = recv_cb,
#endif
.connected = connected_cb,
.disconnected = disconnected_cb,
#if defined(CONFIG_BT_L2CAP_ECRED) #if defined(CONFIG_BT_L2CAP_ECRED)
.reconfigured = reconfigured_cb, .reconfigured = reconfigured_cb,
#endif #endif
}; };
@ -222,10 +263,15 @@ static uint8_t connect(const void *cmd, uint16_t cmd_len,
} }
chan->le.chan.ops = &l2cap_ops; chan->le.chan.ops = &l2cap_ops;
chan->le.rx.mtu = mtu; 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; rp->chan_id[i] = chan->chan_id;
allocated_channels[i] = &chan->le.chan; allocated_channels[i] = &chan->le.chan;
chan->hold_credit = cp->options & BTP_L2CAP_CONNECT_OPT_HOLD_CREDIT; 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) { if (cp->num == 1 && !ecfc) {
@ -289,6 +335,7 @@ static uint8_t reconfigure(const void *cmd, uint16_t cmd_len,
{ {
const struct btp_l2cap_reconfigure_cmd *cp = cmd; const struct btp_l2cap_reconfigure_cmd *cp = cmd;
uint16_t mtu; uint16_t mtu;
uint16_t mps;
struct bt_conn *conn; struct bt_conn *conn;
int err; int err;
struct bt_l2cap_chan *reconf_channels[CHANNELS + 1] = {}; struct bt_l2cap_chan *reconf_channels[CHANNELS + 1] = {};
@ -321,7 +368,8 @@ static uint8_t reconfigure(const void *cmd, uint16_t cmd_len,
return BTP_STATUS_FAILED; 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, cp->num, mtu, mps);
if (err) { if (err) {
bt_conn_unref(conn); bt_conn_unref(conn);
return BTP_STATUS_FAILED; return BTP_STATUS_FAILED;
@ -454,9 +502,14 @@ static int accept(struct bt_conn *conn, struct bt_l2cap_server *server,
chan->le.chan.ops = &l2cap_ops; chan->le.chan.ops = &l2cap_ops;
chan->le.rx.mtu = DATA_MTU_INITIAL; 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; *l2cap_chan = &chan->le.chan;
bt_l2cap_chan_give_credits(&chan->le.chan, 1);
return 0; return 0;
} }
@ -524,7 +577,15 @@ static uint8_t credits(const void *cmd, uint16_t cmd_len,
if (!chan->in_use) { if (!chan->in_use) {
return BTP_STATUS_FAILED; 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 (chan->pending_credit) {
if (bt_l2cap_chan_recv_complete(&chan->le.chan, if (bt_l2cap_chan_recv_complete(&chan->le.chan,
chan->pending_credit) < 0) { chan->pending_credit) < 0) {
@ -533,6 +594,7 @@ static uint8_t credits(const void *cmd, uint16_t cmd_len,
chan->pending_credit = NULL; chan->pending_credit = NULL;
} }
#endif
return BTP_STATUS_SUCCESS; return BTP_STATUS_SUCCESS;
} }