You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by an...@apache.org on 2025/09/03 15:13:48 UTC
(mynewt-nimble) branch master updated (c9dbee471 -> a119bdaf5)
This is an automated email from the ASF dual-hosted git repository.
andk pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-nimble.git
from c9dbee471 nimble/host: fix ble_svc_gatt_cl_sup_feat_access
new ac1f8373f nimble/host: Rework L2CAP RX
new a119bdaf5 nimble/host: Fix L2CAP unit tests
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
nimble/host/src/ble_att.c | 4 +-
nimble/host/src/ble_hs_conn.c | 11 +-
nimble/host/src/ble_hs_conn_priv.h | 14 +-
nimble/host/src/ble_hs_hci_evt.c | 51 +-----
nimble/host/src/ble_l2cap.c | 285 +++++++++++++-------------------
nimble/host/src/ble_l2cap_coc.c | 7 +-
nimble/host/src/ble_l2cap_priv.h | 21 +--
nimble/host/src/ble_l2cap_sig.c | 4 +-
nimble/host/src/ble_sm.c | 8 +-
nimble/host/test/src/ble_hs_test_util.c | 38 +----
nimble/host/test/src/ble_l2cap_test.c | 18 +-
11 files changed, 165 insertions(+), 296 deletions(-)
(mynewt-nimble) 02/02: nimble/host: Fix L2CAP unit tests
Posted by an...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
andk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-nimble.git
commit a119bdaf5bb992681819c944a67d253d1a40b5f2
Author: Andrzej Kaczmarek <an...@codecoup.pl>
AuthorDate: Tue Apr 8 11:55:44 2025 +0200
nimble/host: Fix L2CAP unit tests
This fixes L2CAP unit tests after latest rework.
---
nimble/host/test/src/ble_hs_test_util.c | 38 ++++++---------------------------
nimble/host/test/src/ble_l2cap_test.c | 18 ++++++----------
2 files changed, 13 insertions(+), 43 deletions(-)
diff --git a/nimble/host/test/src/ble_hs_test_util.c b/nimble/host/test/src/ble_hs_test_util.c
index f5463b3ea..f0b26128b 100644
--- a/nimble/host/test/src/ble_hs_test_util.c
+++ b/nimble/host/test/src/ble_hs_test_util.c
@@ -104,7 +104,7 @@ ble_hs_test_util_prev_tx_dequeue(void)
pb = BLE_HCI_DATA_PB(hci_hdr.hdh_handle_pb_bc);
TEST_ASSERT_FATAL(pb == BLE_HCI_PB_FIRST_NON_FLUSH);
- rc = ble_l2cap_parse_hdr(om, 0, &l2cap_hdr);
+ rc = ble_l2cap_parse_hdr(om, &l2cap_hdr);
TEST_ASSERT_FATAL(rc == 0);
os_mbuf_adj(om, BLE_L2CAP_HDR_SZ);
@@ -670,36 +670,13 @@ ble_hs_test_util_l2cap_rx(uint16_t conn_handle,
struct hci_data_hdr *hci_hdr,
struct os_mbuf *om)
{
- struct ble_hs_conn *conn;
- ble_l2cap_rx_fn *rx_cb;
- int reject_cid;
+ uint8_t pb;
int rc;
- ble_hs_lock();
-
- conn = ble_hs_conn_find(conn_handle);
- if (conn != NULL) {
- rc = ble_l2cap_rx(conn, hci_hdr, om, &rx_cb, &reject_cid);
- } else {
- rc = os_mbuf_free_chain(om);
- TEST_ASSERT_FATAL(rc == 0);
- }
-
- ble_hs_unlock();
-
- if (conn == NULL) {
- rc = BLE_HS_ENOTCONN;
- } else if (rc == 0) {
- TEST_ASSERT_FATAL(rx_cb != NULL);
- rc = rx_cb(conn->bhc_rx_chan);
- ble_l2cap_remove_rx(conn, conn->bhc_rx_chan);
- } else if (rc == BLE_HS_EAGAIN) {
- /* More fragments on the way. */
+ pb = BLE_HCI_DATA_PB(hci_hdr->hdh_handle_pb_bc);
+ rc = ble_l2cap_rx(conn_handle, pb, om);
+ if (rc == BLE_HS_EAGAIN) {
rc = 0;
- } else {
- if (reject_cid != -1) {
- ble_l2cap_sig_reject_invalid_cid_tx(conn_handle, 0, 0, reject_cid);
- }
}
return rc;
@@ -1787,7 +1764,6 @@ ble_hs_test_util_mbuf_count(const struct ble_hs_test_util_mbuf_params *params)
{
const struct ble_att_prep_entry *prep;
const struct os_mbuf_pkthdr *omp;
- const struct ble_l2cap_chan *chan;
const struct ble_hs_conn *conn;
const struct os_mbuf *om;
int count;
@@ -1813,9 +1789,7 @@ ble_hs_test_util_mbuf_count(const struct ble_hs_test_util_mbuf_params *params)
}
if (params->rx_queue) {
- SLIST_FOREACH(chan, &conn->bhc_channels, next) {
- count += ble_hs_test_util_mbuf_chain_len(chan->rx_buf);
- }
+ count += ble_hs_test_util_mbuf_chain_len(conn->rx_frags);
}
if (params->prep_list) {
diff --git a/nimble/host/test/src/ble_l2cap_test.c b/nimble/host/test/src/ble_l2cap_test.c
index d1db844db..6fd294b82 100644
--- a/nimble/host/test/src/ble_l2cap_test.c
+++ b/nimble/host/test/src/ble_l2cap_test.c
@@ -124,7 +124,7 @@ ble_l2cap_test_util_verify_tx_update_conn(
}
static int
-ble_l2cap_test_util_dummy_rx(struct ble_l2cap_chan *chan)
+ble_l2cap_test_util_dummy_rx(struct ble_l2cap_chan *chan, struct os_mbuf **om)
{
return 0;
}
@@ -220,8 +220,7 @@ ble_l2cap_test_util_verify_first_frag(uint16_t conn_handle,
conn = ble_hs_conn_find(conn_handle);
TEST_ASSERT_FATAL(conn != NULL);
- TEST_ASSERT(conn->bhc_rx_chan != NULL &&
- conn->bhc_rx_chan->scid == BLE_L2CAP_TEST_CID);
+ TEST_ASSERT(conn->rx_len && conn->rx_cid == BLE_L2CAP_TEST_CID);
ble_hs_unlock();
}
@@ -240,8 +239,7 @@ ble_l2cap_test_util_verify_middle_frag(uint16_t conn_handle,
conn = ble_hs_conn_find(conn_handle);
TEST_ASSERT_FATAL(conn != NULL);
- TEST_ASSERT(conn->bhc_rx_chan != NULL &&
- conn->bhc_rx_chan->scid == BLE_L2CAP_TEST_CID);
+ TEST_ASSERT(conn->rx_len && conn->rx_cid == BLE_L2CAP_TEST_CID);
ble_hs_unlock();
}
@@ -260,7 +258,7 @@ ble_l2cap_test_util_verify_last_frag(uint16_t conn_handle,
conn = ble_hs_conn_find(conn_handle);
TEST_ASSERT_FATAL(conn != NULL);
- TEST_ASSERT(conn->bhc_rx_chan == NULL);
+ TEST_ASSERT(conn->rx_len == 0);
ble_hs_unlock();
}
@@ -279,7 +277,7 @@ TEST_CASE_SELF(ble_l2cap_test_case_bad_header)
NULL, NULL);
rc = ble_l2cap_test_util_rx_first_frag(2, 14, 1234, 10);
- TEST_ASSERT(rc == BLE_HS_ENOENT);
+ TEST_ASSERT(rc == BLE_HS_EBADDATA);
ble_hs_test_util_assert_mbufs_freed(NULL);
}
@@ -386,8 +384,7 @@ TEST_CASE_SELF(ble_l2cap_test_case_frag_channels)
ble_hs_lock();
conn = ble_hs_conn_find(2);
TEST_ASSERT_FATAL(conn != NULL);
- TEST_ASSERT(conn->bhc_rx_chan != NULL &&
- conn->bhc_rx_chan->scid == BLE_L2CAP_TEST_CID);
+ TEST_ASSERT(conn->rx_len && conn->rx_cid == BLE_L2CAP_TEST_CID);
ble_hs_unlock();
/* Receive a starting fragment on a different channel. The first fragment
@@ -400,8 +397,7 @@ TEST_CASE_SELF(ble_l2cap_test_case_frag_channels)
ble_hs_lock();
conn = ble_hs_conn_find(2);
TEST_ASSERT_FATAL(conn != NULL);
- TEST_ASSERT(conn->bhc_rx_chan != NULL &&
- conn->bhc_rx_chan->scid == BLE_L2CAP_CID_ATT);
+ TEST_ASSERT(conn->rx_len && conn->rx_cid == BLE_L2CAP_CID_ATT);
ble_hs_unlock();
/* Terminate the connection. The received fragments should get freed.
(mynewt-nimble) 01/02: nimble/host: Rework L2CAP RX
Posted by an...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
andk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-nimble.git
commit ac1f8373f9b79db122f9cb1a1ba06064fff31783
Author: Andrzej Kaczmarek <an...@codecoup.pl>
AuthorDate: Tue Apr 8 11:55:49 2025 +0200
nimble/host: Rework L2CAP RX
This reworks L2CAP RX path to simplify flow and fix reassembly issues.
RX is now done in conn context instead of chan context. This allows to
save few bytes of memory per chan since RX data are now held per-conn
instead of per-chan.
The new code also fixes reassembly issue where we couldn't properly
reassemble L2CAP SDU if the first fragment was shorter than the L2CAP
header.
---
nimble/host/src/ble_att.c | 4 +-
nimble/host/src/ble_hs_conn.c | 11 +-
nimble/host/src/ble_hs_conn_priv.h | 14 +-
nimble/host/src/ble_hs_hci_evt.c | 51 +------
nimble/host/src/ble_l2cap.c | 285 ++++++++++++++++---------------------
nimble/host/src/ble_l2cap_coc.c | 7 +-
nimble/host/src/ble_l2cap_priv.h | 21 +--
nimble/host/src/ble_l2cap_sig.c | 4 +-
nimble/host/src/ble_sm.c | 8 +-
9 files changed, 152 insertions(+), 253 deletions(-)
diff --git a/nimble/host/src/ble_att.c b/nimble/host/src/ble_att.c
index 6d4e05af1..8bde29b2a 100644
--- a/nimble/host/src/ble_att.c
+++ b/nimble/host/src/ble_att.c
@@ -564,7 +564,7 @@ ble_att_rx_extended(uint16_t conn_handle, uint16_t cid, struct os_mbuf **om)
}
static int
-ble_att_rx(struct ble_l2cap_chan *chan)
+ble_att_rx(struct ble_l2cap_chan *chan, struct os_mbuf **om)
{
uint16_t conn_handle;
@@ -573,7 +573,7 @@ ble_att_rx(struct ble_l2cap_chan *chan)
return BLE_HS_ENOTCONN;
}
- return ble_att_rx_extended(conn_handle, chan->scid, &chan->rx_buf);
+ return ble_att_rx_extended(conn_handle, chan->scid, om);
}
uint16_t
diff --git a/nimble/host/src/ble_hs_conn.c b/nimble/host/src/ble_hs_conn.c
index 1e83d33de..ba034a270 100644
--- a/nimble/host/src/ble_hs_conn.c
+++ b/nimble/host/src/ble_hs_conn.c
@@ -207,10 +207,6 @@ err:
void
ble_hs_conn_delete_chan(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan)
{
- if (conn->bhc_rx_chan == chan) {
- conn->bhc_rx_chan = NULL;
- }
-
SLIST_REMOVE(&conn->bhc_channels, chan, ble_l2cap_chan, next);
ble_l2cap_chan_free(conn, chan);
}
@@ -244,6 +240,9 @@ ble_hs_conn_free(struct ble_hs_conn *conn)
return;
}
+ os_mbuf_free_chain(conn->rx_frags);
+ conn->rx_frags = NULL;
+
ble_att_svr_prep_clear(&conn->bhc_att_svr.basc_prep_list);
while ((chan = SLIST_FIRST(&conn->bhc_channels)) != NULL) {
@@ -504,8 +503,8 @@ ble_hs_conn_timer(void)
* passes after a partial packet is received, the connection is
* terminated.
*/
- if (conn->bhc_rx_chan != NULL) {
- time_diff = conn->bhc_rx_timeout - now;
+ if (conn->rx_len) {
+ time_diff = conn->rx_frag_tmo - now;
if (time_diff <= 0) {
/* ACL reassembly has timed out.*/
diff --git a/nimble/host/src/ble_hs_conn_priv.h b/nimble/host/src/ble_hs_conn_priv.h
index 2d23422f5..8a0412fa5 100644
--- a/nimble/host/src/ble_hs_conn_priv.h
+++ b/nimble/host/src/ble_hs_conn_priv.h
@@ -69,8 +69,18 @@ struct ble_hs_conn {
ble_hs_conn_flags_t bhc_flags;
struct ble_l2cap_chan_list bhc_channels;
- struct ble_l2cap_chan *bhc_rx_chan; /* Channel rxing current packet. */
- ble_npl_time_t bhc_rx_timeout;
+
+ /* ACL RX fragments */
+ struct os_mbuf *rx_frags;
+ /* Expected data length for ACL RX */
+ uint16_t rx_len;
+ /* L2CAP Source CID for ACL RX */
+ uint16_t rx_cid;
+#if MYNEWT_VAL(BLE_L2CAP_RX_FRAG_TIMEOUT) != 0
+ /* Timeout for next fragment for ACL RX */
+ ble_npl_time_t rx_frag_tmo;
+#endif
+
#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
uint32_t l2cap_coc_cid_mask[BLE_HS_CONN_L2CAP_COC_CID_MASK_LEN];
#endif
diff --git a/nimble/host/src/ble_hs_hci_evt.c b/nimble/host/src/ble_hs_hci_evt.c
index 55031fcd7..8526d8caf 100644
--- a/nimble/host/src/ble_hs_hci_evt.c
+++ b/nimble/host/src/ble_hs_hci_evt.c
@@ -1099,15 +1099,14 @@ ble_hs_hci_evt_acl_process(struct os_mbuf *om)
{
#if NIMBLE_BLE_CONNECT
struct hci_data_hdr hci_hdr;
- struct ble_hs_conn *conn;
- ble_l2cap_rx_fn *rx_cb;
uint16_t conn_handle;
- int reject_cid;
+ uint8_t pb;
int rc;
rc = ble_hs_hci_util_data_hdr_strip(om, &hci_hdr);
if (rc != 0) {
- goto err;
+ os_mbuf_free_chain(om);
+ return rc;
}
#if (BLETEST_THROUGHPUT_TEST == 0)
@@ -1123,50 +1122,14 @@ ble_hs_hci_evt_acl_process(struct os_mbuf *om)
#endif
if (hci_hdr.hdh_len != OS_MBUF_PKTHDR(om)->omp_len) {
- rc = BLE_HS_EBADDATA;
- goto err;
+ os_mbuf_free_chain(om);
+ return BLE_HS_EBADDATA;
}
conn_handle = BLE_HCI_DATA_HANDLE(hci_hdr.hdh_handle_pb_bc);
+ pb = BLE_HCI_DATA_PB(hci_hdr.hdh_handle_pb_bc);
+ rc = ble_l2cap_rx(conn_handle, pb, om);
- ble_hs_lock();
-
- conn = ble_hs_conn_find(conn_handle);
- if (conn == NULL) {
- /* Peer not connected; quietly discard packet. */
- rc = BLE_HS_ENOTCONN;
- reject_cid = -1;
- } else {
- /* Forward ACL data to L2CAP. */
- rc = ble_l2cap_rx(conn, &hci_hdr, om, &rx_cb, &reject_cid);
- om = NULL;
- }
-
- ble_hs_unlock();
-
- switch (rc) {
- case 0:
- /* Final fragment received. */
- BLE_HS_DBG_ASSERT(rx_cb != NULL);
- rc = rx_cb(conn->bhc_rx_chan);
- ble_l2cap_remove_rx(conn, conn->bhc_rx_chan);
- break;
-
- case BLE_HS_EAGAIN:
- /* More fragments on the way. */
- break;
-
- default:
- if (reject_cid != -1) {
- ble_l2cap_sig_reject_invalid_cid_tx(conn_handle, 0, 0, reject_cid);
- }
- goto err;
- }
-
- return 0;
-
-err:
- os_mbuf_free_chain(om);
return rc;
#else
return BLE_HS_ENOTSUP;
diff --git a/nimble/host/src/ble_l2cap.c b/nimble/host/src/ble_l2cap.c
index d88f00616..5a4ff9ad4 100644
--- a/nimble/host/src/ble_l2cap.c
+++ b/nimble/host/src/ble_l2cap.c
@@ -80,7 +80,6 @@ ble_l2cap_chan_free(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan)
return;
}
- os_mbuf_free_chain(chan->rx_buf);
ble_l2cap_coc_cleanup_chan(conn, chan);
#if MYNEWT_VAL(BLE_HS_DEBUG)
@@ -99,18 +98,17 @@ ble_l2cap_is_mtu_req_sent(const struct ble_l2cap_chan *chan)
}
int
-ble_l2cap_parse_hdr(struct os_mbuf *om, int off,
- struct ble_l2cap_hdr *l2cap_hdr)
+ble_l2cap_parse_hdr(struct os_mbuf *om, struct ble_l2cap_hdr *hdr)
{
int rc;
- rc = os_mbuf_copydata(om, off, sizeof *l2cap_hdr, l2cap_hdr);
+ rc = os_mbuf_copydata(om, 0, sizeof(*hdr), hdr);
if (rc != 0) {
return BLE_HS_EMSGSIZE;
}
- l2cap_hdr->len = get_le16(&l2cap_hdr->len);
- l2cap_hdr->cid = get_le16(&l2cap_hdr->cid);
+ hdr->len = get_le16(&hdr->len);
+ hdr->cid = get_le16(&hdr->cid);
return 0;
}
@@ -233,213 +231,168 @@ ble_l2cap_recv_ready(struct ble_l2cap_chan *chan, struct os_mbuf *sdu_rx)
return ble_l2cap_coc_recv_ready(chan, sdu_rx);
}
-void
-ble_l2cap_remove_rx(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan)
+static uint16_t
+ble_l2cap_get_mtu(struct ble_l2cap_chan *chan)
{
- conn->bhc_rx_chan = NULL;
- os_mbuf_free_chain(chan->rx_buf);
- chan->rx_buf = NULL;
- chan->rx_len = 0;
+ if (chan->scid == BLE_L2CAP_CID_ATT) {
+ /* In case of ATT chan->my_mtu keeps preferred MTU which is later
+ * used during exchange MTU procedure. Helper below gives us actual
+ * MTU on the channel, which is 23 or higher if exchange MTU has been
+ * done
+ */
+ return ble_att_chan_mtu(chan);
+ }
+
+ return chan->my_mtu;
}
static void
-ble_l2cap_append_rx(struct ble_l2cap_chan *chan, struct os_mbuf *frag)
+ble_l2cap_rx_free(struct ble_hs_conn *conn)
{
-#if MYNEWT_VAL(BLE_L2CAP_JOIN_RX_FRAGS)
- struct os_mbuf *m;
-
- /* Copy the data from the incoming fragment into the packet in progress. */
- m = os_mbuf_pack_chains(chan->rx_buf, frag);
- assert(m);
-#else
- /* Join disabled or append failed due to mbuf shortage. Just attach the
- * mbuf to the end of the packet.
- */
- os_mbuf_concat(chan->rx_buf, frag);
-#endif
+ os_mbuf_free_chain(conn->rx_frags);
+ conn->rx_frags = NULL;
+ conn->rx_len = 0;
+ conn->rx_cid = 0;
}
static int
-ble_l2cap_rx_payload(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
- struct os_mbuf *om,
- ble_l2cap_rx_fn **out_rx_cb)
+ble_l2cap_rx_frags_process(struct ble_hs_conn *conn)
{
- int len_diff;
+ int rem_rx_len;
int rc;
- if (chan->rx_buf == NULL) {
- /* First fragment in packet. */
- chan->rx_buf = om;
- } else {
- /* Continuation of packet in progress. */
- ble_l2cap_append_rx(chan, om);
- }
-
- /* Determine if packet is fully reassembled. */
- len_diff = OS_MBUF_PKTLEN(chan->rx_buf) - chan->rx_len;
- if (len_diff > 0) {
- /* More data than expected; data corruption. */
- ble_l2cap_remove_rx(conn, chan);
- rc = BLE_HS_EBADDATA;
- } else if (len_diff == 0) {
- /* All fragments received. */
- *out_rx_cb = chan->rx_fn;
+ rem_rx_len = conn->rx_len - OS_MBUF_PKTLEN(conn->rx_frags);
+ if (rem_rx_len == 0) {
rc = 0;
- } else {
- /* More fragments remain. */
+ } else if (rem_rx_len > 0) {
#if MYNEWT_VAL(BLE_L2CAP_RX_FRAG_TIMEOUT) != 0
- conn->bhc_rx_timeout =
+ conn->rx_frag_tmo =
ble_npl_time_get() +
ble_npl_time_ms_to_ticks32(MYNEWT_VAL(BLE_L2CAP_RX_FRAG_TIMEOUT));
ble_hs_timer_resched();
#endif
rc = BLE_HS_EAGAIN;
+ } else {
+ ble_l2cap_rx_free(conn);
+ rc = BLE_HS_EBADDATA;
}
return rc;
}
-static uint16_t
-ble_l2cap_get_mtu(struct ble_l2cap_chan *chan)
-{
- if (chan->scid == BLE_L2CAP_CID_ATT) {
- /* In case of ATT chan->my_mtu keeps preferred MTU which is later
- * used during exchange MTU procedure. Helper below will gives us actual
- * MTU on the channel, which is 23 or higher if exchange MTU has been
- * done
- */
- return ble_att_chan_mtu(chan);
- }
-
- return chan->my_mtu;
-}
-
-/**
- * Processes an incoming L2CAP fragment.
- *
- * @param conn The connection the L2CAP fragment was sent
- * over.
- * @param hci_hdr The ACL data header that was at the start of
- * the L2CAP fragment. This header has been
- * stripped from the mbuf parameter.
- * @param om An mbuf containing the L2CAP data. If this is
- * the first fragment, the L2CAP header is at
- * the start of the mbuf. For subsequent
- * fragments, the mbuf starts with L2CAP
- * payload data.
- * @param out_rx_cb If a full L2CAP packet has been received, a
- * pointer to the appropriate handler gets
- * written here. The caller should pass the
- * receive buffer to this callback.
- * @param out_reject_cid Indicates whether an L2CAP Command Reject
- * command should be sent. If this equals -1,
- * no reject should get sent. Otherwise, the
- * value indicates the CID that the outgoing
- * reject should specify.
- *
- * @return 0 if a complete L2CAP packet has been received.
- * BLE_HS_EAGAIN if a partial L2CAP packet has
- * been received; more fragments are expected.
- * Other value on error.
- */
int
-ble_l2cap_rx(struct ble_hs_conn *conn,
- struct hci_data_hdr *hci_hdr,
- struct os_mbuf *om,
- ble_l2cap_rx_fn **out_rx_cb,
- int *out_reject_cid)
+ble_l2cap_rx(uint16_t conn_handle, uint8_t pb, struct os_mbuf *om)
{
+ struct ble_hs_conn *conn;
struct ble_l2cap_chan *chan;
- struct ble_l2cap_hdr l2cap_hdr;
- uint8_t pb;
+ struct ble_l2cap_hdr hdr;
+ struct os_mbuf *rx_frags;
+ uint16_t rx_len;
+ uint16_t rx_cid;
int rc;
- *out_reject_cid = -1;
+ ble_hs_lock();
+
+ conn = ble_hs_conn_find(conn_handle);
+ if (!conn) {
+ /* Invalid connection handle, discard packet */
+ os_mbuf_free_chain(om);
+ rc = BLE_HS_ENOTCONN;
+ goto done;
+ }
- pb = BLE_HCI_DATA_PB(hci_hdr->hdh_handle_pb_bc);
switch (pb) {
case BLE_HCI_PB_FIRST_FLUSH:
- /* First fragment. */
- rc = ble_l2cap_parse_hdr(om, 0, &l2cap_hdr);
- if (rc != 0) {
- goto err;
+ if (conn->rx_frags) {
+ /* Previously received data is incomplete, discard it */
+ ble_l2cap_rx_free(conn);
}
-
- /* Strip L2CAP header from the front of the mbuf. */
- os_mbuf_adj(om, BLE_L2CAP_HDR_SZ);
-
- chan = ble_hs_conn_chan_find_by_scid(conn, l2cap_hdr.cid);
- if (chan == NULL) {
- rc = BLE_HS_ENOENT;
-
- /* Unsupported channel. If the target CID is the black hole
- * channel, quietly drop the packet. Otherwise, send an invalid
- * CID response.
+ conn->rx_frags = om;
+ break;
+ case BLE_HCI_PB_MIDDLE:
+ if (!conn->rx_frags) {
+ /* Received continuation without 1st packet, discard it.
+ * This can also happen if we received invalid data earlier which
+ * was discarded, so we'll just keep discarding until valid 1st
+ * packet is received.
*/
- if (l2cap_hdr.cid != BLE_L2CAP_CID_BLACK_HOLE) {
- BLE_HS_LOG(DEBUG, "rx on unknown L2CAP channel: %d\n",
- l2cap_hdr.cid);
- *out_reject_cid = l2cap_hdr.cid;
- }
- goto err;
- }
-
- /* For CIDs from dynamic range we check if SDU size isn't larger than MPS */
- if (chan->dcid >= BLE_L2CAP_COC_CID_START &&
- chan->dcid <= BLE_L2CAP_COC_CID_END &&
- l2cap_hdr.len > chan->my_coc_mps) {
- /* Data exceeds MPS */
- BLE_HS_LOG(ERROR, "error: sdu_len > chan->my_coc_mps (%d>%d)\n",
- l2cap_hdr.len, chan->my_coc_mps);
- ble_l2cap_disconnect(chan);
+ os_mbuf_free_chain(om);
rc = BLE_HS_EBADDATA;
- goto err;
+ goto done;
}
- if (chan->rx_buf != NULL) {
- /* Previous data packet never completed. Discard old packet. */
- ble_l2cap_remove_rx(conn, chan);
- }
+ /* Append fragment to rx buffer */
+#if MYNEWT_VAL(BLE_L2CAP_JOIN_RX_FRAGS)
+ os_mbuf_pack_chains(conn->rx_frags, om);
+#else
+ os_mbuf_concat(conn->rx_frag, om);
+#endif
+ break;
+ default:
+ /* Invalid PB, discard packet */
+ os_mbuf_free_chain(om);
+ ble_l2cap_rx_free(conn);
+ rc = BLE_HS_EBADDATA;
+ goto done;
+ }
- if (l2cap_hdr.len > ble_l2cap_get_mtu(chan)) {
- /* More data than we expected on the channel.
- * Disconnect peer with invalid behaviour
- */
- rc = BLE_HS_EBADDATA;
- ble_l2cap_disconnect(chan);
- goto err;
+ /* Parse L2CAP header if not yet done */
+ if (!conn->rx_len) {
+ rc = ble_l2cap_parse_hdr(conn->rx_frags, &hdr);
+ if (rc) {
+ /* Incomplete header, wait for continuation */
+ rc = BLE_HS_EAGAIN;
+ goto done;
}
- /* Remember channel and length of L2CAP data for reassembly. */
- conn->bhc_rx_chan = chan;
- chan->rx_len = l2cap_hdr.len;
- break;
+ os_mbuf_adj(conn->rx_frags, BLE_L2CAP_HDR_SZ);
- case BLE_HCI_PB_MIDDLE:
- chan = conn->bhc_rx_chan;
- if (chan == NULL || chan->rx_buf == NULL) {
- /* Middle fragment without the start. Discard new packet. */
- rc = BLE_HS_EBADDATA;
- goto err;
- }
- break;
+ conn->rx_len = hdr.len;
+ conn->rx_cid = hdr.cid;
+ }
- default:
- rc = BLE_HS_EBADDATA;
- goto err;
+ /* Process fragments */
+ rc = ble_l2cap_rx_frags_process(conn);
+ if (rc) {
+ goto done;
}
- rc = ble_l2cap_rx_payload(conn, chan, om, out_rx_cb);
- om = NULL;
- if (rc != 0) {
- goto err;
+ rx_frags = conn->rx_frags;
+ rx_len = conn->rx_len;
+ rx_cid = conn->rx_cid;
+
+ conn->rx_frags = NULL;
+ ble_l2cap_rx_free(conn);
+
+ chan = ble_hs_conn_chan_find_by_scid(conn, rx_cid);
+
+ ble_hs_unlock();
+
+ if (!chan) {
+ ble_l2cap_sig_reject_invalid_cid_tx(conn_handle, 0, 0, rx_cid);
+ return BLE_HS_ENOENT;
}
- return 0;
+ if (chan->dcid >= BLE_L2CAP_COC_CID_START &&
+ chan->dcid <= BLE_L2CAP_COC_CID_END && rx_len > chan->my_coc_mps) {
+ ble_l2cap_disconnect(chan);
+ return BLE_HS_EBADDATA;
+ }
+
+ if (rx_len > ble_l2cap_get_mtu(chan)) {
+ ble_l2cap_disconnect(chan);
+ return BLE_HS_EBADDATA;
+ }
+
+ rc = chan->rx_fn(chan, &rx_frags);
+ os_mbuf_free_chain(rx_frags);
+
+ return rc;
+
+done:
+ ble_hs_unlock();
-err:
- os_mbuf_free_chain(om);
return rc;
}
diff --git a/nimble/host/src/ble_l2cap_coc.c b/nimble/host/src/ble_l2cap_coc.c
index 3a483c751..cb74e7b70 100644
--- a/nimble/host/src/ble_l2cap_coc.c
+++ b/nimble/host/src/ble_l2cap_coc.c
@@ -183,18 +183,13 @@ ble_l2cap_event_coc_received_data(struct ble_l2cap_chan *chan,
}
static int
-ble_l2cap_coc_rx_fn(struct ble_l2cap_chan *chan)
+ble_l2cap_coc_rx_fn(struct ble_l2cap_chan *chan, struct os_mbuf **om)
{
int rc;
- struct os_mbuf **om;
struct os_mbuf *rx_sdu;
struct ble_l2cap_coc_endpoint *rx;
uint16_t om_total;
- /* Create a shortcut to rx_buf */
- om = &chan->rx_buf;
- BLE_HS_DBG_ASSERT(*om != NULL);
-
/* Create a shortcut to rx endpoint */
rx = &chan->coc_rx;
BLE_HS_DBG_ASSERT(rx != NULL);
diff --git a/nimble/host/src/ble_l2cap_priv.h b/nimble/host/src/ble_l2cap_priv.h
index 21e6a8bf6..926d1b9ac 100644
--- a/nimble/host/src/ble_l2cap_priv.h
+++ b/nimble/host/src/ble_l2cap_priv.h
@@ -49,16 +49,11 @@ extern STATS_SECT_DECL(ble_l2cap_stats) ble_l2cap_stats;
extern struct os_mempool ble_l2cap_chan_pool;
-/* This is nimble specific; packets sent to the black hole CID do not elicit
- * an "invalid CID" response.
- */
-#define BLE_L2CAP_CID_BLACK_HOLE 0xffff
-
#define BLE_L2CAP_HDR_SZ 4
typedef uint8_t ble_l2cap_chan_flags;
-typedef int ble_l2cap_rx_fn(struct ble_l2cap_chan *chan);
+typedef int ble_l2cap_rx_fn(struct ble_l2cap_chan *chan, struct os_mbuf **om);
struct ble_l2cap_chan {
SLIST_ENTRY(ble_l2cap_chan) next;
@@ -81,9 +76,6 @@ struct ble_l2cap_chan {
ble_l2cap_chan_flags flags;
- struct os_mbuf *rx_buf;
- uint16_t rx_len; /* Length of current reassembled rx packet. */
-
ble_l2cap_rx_fn *rx_fn;
#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) != 0
@@ -109,8 +101,7 @@ typedef int ble_l2cap_tx_fn(struct ble_hs_conn *conn,
SLIST_HEAD(ble_l2cap_chan_list, ble_l2cap_chan);
-int ble_l2cap_parse_hdr(struct os_mbuf *om, int off,
- struct ble_l2cap_hdr *l2cap_hdr);
+int ble_l2cap_parse_hdr(struct os_mbuf *om, struct ble_l2cap_hdr *hdr);
struct os_mbuf *ble_l2cap_prepend_hdr(struct os_mbuf *om, uint16_t cid,
uint16_t len);
@@ -119,16 +110,10 @@ void ble_l2cap_chan_free(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan);
bool ble_l2cap_is_mtu_req_sent(const struct ble_l2cap_chan *chan);
-int ble_l2cap_rx(struct ble_hs_conn *conn,
- struct hci_data_hdr *hci_hdr,
- struct os_mbuf *om,
- ble_l2cap_rx_fn **out_rx_cb,
- int *out_reject_cid);
+int ble_l2cap_rx(uint16_t conn_handle, uint8_t pb, struct os_mbuf *om);
int ble_l2cap_tx(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan,
struct os_mbuf *txom);
-void ble_l2cap_remove_rx(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan);
-
int ble_l2cap_init(void);
/* Below experimental API is available when BLE_VERSION >= 52 */
diff --git a/nimble/host/src/ble_l2cap_sig.c b/nimble/host/src/ble_l2cap_sig.c
index 2e356d2e4..3b9e452e5 100644
--- a/nimble/host/src/ble_l2cap_sig.c
+++ b/nimble/host/src/ble_l2cap_sig.c
@@ -1850,16 +1850,14 @@ ble_l2cap_sig_rx_reject(uint16_t conn_handle,
*****************************************************************************/
static int
-ble_l2cap_sig_rx(struct ble_l2cap_chan *chan)
+ble_l2cap_sig_rx(struct ble_l2cap_chan *chan, struct os_mbuf **om)
{
struct ble_l2cap_sig_hdr hdr;
ble_l2cap_sig_rx_fn *rx_cb;
uint16_t conn_handle;
- struct os_mbuf **om;
int rc;
conn_handle = chan->conn_handle;
- om = &chan->rx_buf;
STATS_INC(ble_l2cap_stats, sig_rx);
diff --git a/nimble/host/src/ble_sm.c b/nimble/host/src/ble_sm.c
index a83b9ff28..a6ed2986c 100644
--- a/nimble/host/src/ble_sm.c
+++ b/nimble/host/src/ble_sm.c
@@ -2684,13 +2684,12 @@ ble_sm_enc_initiate(uint16_t conn_handle, uint8_t key_size,
}
static int
-ble_sm_rx(struct ble_l2cap_chan *chan)
+ble_sm_rx(struct ble_l2cap_chan *chan, struct os_mbuf **om)
{
struct ble_sm_result res;
ble_sm_rx_fn *rx_cb;
uint8_t op;
uint16_t conn_handle;
- struct os_mbuf **om;
int rc;
STATS_INC(ble_l2cap_stats, sm_rx);
@@ -2700,9 +2699,6 @@ ble_sm_rx(struct ble_l2cap_chan *chan)
return BLE_HS_ENOTCONN;
}
- om = &chan->rx_buf;
- BLE_HS_DBG_ASSERT(*om != NULL);
-
rc = os_mbuf_copydata(*om, 0, 1, &op);
if (rc != 0) {
return BLE_HS_EBADDATA;
@@ -2873,7 +2869,7 @@ ble_sm_init(void)
* simple
*/
static int
-ble_sm_rx(struct ble_l2cap_chan *chan)
+ble_sm_rx(struct ble_l2cap_chan *chan, struct os_mbuf **om)
{
struct ble_sm_pair_fail *cmd;
struct os_mbuf *txom;