diff --git a/include/zephyr/bluetooth/l2cap.h b/include/zephyr/bluetooth/l2cap.h index cdd162f9889..8358d62c309 100644 --- a/include/zephyr/bluetooth/l2cap.h +++ b/include/zephyr/bluetooth/l2cap.h @@ -18,6 +18,7 @@ * @{ */ +#include #include #include @@ -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 diff --git a/subsys/bluetooth/host/Kconfig.l2cap b/subsys/bluetooth/host/Kconfig.l2cap index 6499fbb19a8..2841e7d8a12 100644 --- a/subsys/bluetooth/host/Kconfig.l2cap +++ b/subsys/bluetooth/host/Kconfig.l2cap @@ -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 diff --git a/subsys/bluetooth/host/att.c b/subsys/bluetooth/host/att.c index 26b3c3f542b..2cca2dfbbd0 100644 --- a/subsys/bluetooth/host/att.c +++ b/subsys/bluetooth/host/att.c @@ -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; diff --git a/subsys/bluetooth/host/l2cap.c b/subsys/bluetooth/host/l2cap.c index 66674b28715..363e3867c09 100644 --- a/subsys/bluetooth/host/l2cap.c +++ b/subsys/bluetooth/host/l2cap.c @@ -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, diff --git a/subsys/bluetooth/host/l2cap_internal.h b/subsys/bluetooth/host/l2cap_internal.h index ced0493515e..60f614b7caa 100644 --- a/subsys/bluetooth/host/l2cap_internal.h +++ b/subsys/bluetooth/host/l2cap_internal.h @@ -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; diff --git a/tests/bluetooth/tester/prj.conf b/tests/bluetooth/tester/prj.conf index 5b1a271653c..2d07c4c6ca1 100644 --- a/tests/bluetooth/tester/prj.conf +++ b/tests/bluetooth/tester/prj.conf @@ -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 diff --git a/tests/bluetooth/tester/src/btp_l2cap.c b/tests/bluetooth/tester/src/btp_l2cap.c index 69c438f7f6f..288215aee5d 100644 --- a/tests/bluetooth/tester/src/btp_l2cap.c +++ b/tests/bluetooth/tester/src/btp_l2cap.c @@ -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; }