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 <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))
/** @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;
/** @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);
/** @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
*
* 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]"
select EXPERIMENTAL
help
Enable API for direct receiving of L2CAP SDU segments, bypassing the
Host's fixed-function SDU re-assembler, RX SDU buffer management and
credit issuer.
@ -70,4 +69,11 @@ 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

View file

@ -3668,7 +3668,7 @@ int bt_eatt_connect(struct bt_conn *conn, size_t num_channels)
}
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.
*/
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;
}
offset += L2CAP_ECRED_CHAN_MAX_PER_REQ;
offset += BT_L2CAP_ECRED_CHAN_MAX_PER_REQ;
}
return 0;
@ -3767,7 +3767,7 @@ int bt_eatt_reconfigure(struct bt_conn *conn, uint16_t mtu)
}
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.
*/
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;
}
offset += L2CAP_ECRED_CHAN_MAX_PER_REQ;
offset += BT_L2CAP_ECRED_CHAN_MAX_PER_REQ;
}
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 L2CAP_LE_MIN_MTU 23
#define L2CAP_ECRED_MIN_MTU 64
#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_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)
#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 (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;
int i = 0;
SYS_SLIST_FOR_EACH_CONTAINER(&chan->conn->channels, ch, node) {
if (le->ident == BT_L2CAP_LE_CHAN(ch)->ident) {
__ASSERT(i < L2CAP_ECRED_CHAN_MAX_PER_REQ,
"There can only be L2CAP_ECRED_CHAN_MAX_PER_REQ channels "
"from the same request.");
__ASSERT(i < BT_L2CAP_ECRED_CHAN_MAX_PER_REQ,
"There can only be BT_L2CAP_ECRED_CHAN_MAX_PER_REQ "
"channels from the same request.");
atomic_clear_bit(ch->status, BT_L2CAP_STATUS_ENCRYPT_PENDING);
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 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_server *server;
struct bt_l2cap_ecred_conn_req *req;
struct bt_l2cap_ecred_conn_rsp *rsp;
uint16_t mtu, mps, credits, result = BT_L2CAP_LE_SUCCESS;
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;
uint8_t req_cid_count;
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)) {
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;
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);
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);
result = BT_L2CAP_LE_ERR_INVALID_PARAMS;
goto response;
@ -1646,7 +1649,7 @@ static void le_ecred_reconf_req(struct bt_l2cap *l2cap, uint8_t ident,
struct net_buf *buf)
{
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_rsp *rsp;
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);
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;
goto response;
}
if (mtu < L2CAP_ECRED_MIN_MTU) {
if (mtu < BT_L2CAP_ECRED_MIN_MTU) {
result = BT_L2CAP_RECONF_INVALID_MTU;
goto response;
}
/* 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;
goto response;
}
@ -2908,7 +2911,7 @@ int bt_l2cap_ecred_chan_connect(struct bt_conn *conn,
}
/* 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]) {
break;
}
@ -2962,7 +2965,7 @@ int bt_l2cap_ecred_chan_reconfigure(struct bt_l2cap_chan **chans, uint16_t mtu)
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]) {
break;
}
@ -3035,6 +3038,94 @@ 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, 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) */
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];
} __packed;
#define L2CAP_ECRED_CHAN_MAX_PER_REQ 5
#define BT_L2CAP_ECRED_RECONF_REQ 0x19
struct bt_l2cap_ecred_reconf_req {
uint16_t mtu;

View file

@ -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
@ -35,7 +37,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

View file

@ -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;
@ -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)
{
@ -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)
{
@ -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));
@ -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
};
@ -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) {
@ -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] = {};
@ -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, cp->num, mtu, mps);
if (err) {
bt_conn_unref(conn);
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.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;
}
@ -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) {
@ -533,6 +594,7 @@ static uint8_t credits(const void *cmd, uint16_t cmd_len,
chan->pending_credit = NULL;
}
#endif
return BTP_STATUS_SUCCESS;
}