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:
parent
c002b1dc9e
commit
5a8daffc32
7 changed files with 276 additions and 35 deletions
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue