mirror of
https://github.com/espressif/esp-nimble.git
synced 2026-08-20 08:50:02 +00:00
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.
This commit is contained in:
committed by
Rahul Tank
parent
eed4636870
commit
4f5675f517
@@ -584,7 +584,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;
|
||||
|
||||
@@ -593,7 +593,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
|
||||
|
||||
@@ -252,15 +252,6 @@ err:
|
||||
void
|
||||
ble_hs_conn_delete_chan(struct ble_hs_conn *conn, struct ble_l2cap_chan *chan)
|
||||
{
|
||||
#if MYNEWT_VAL(BT_NIMBLE_MEM_OPTIMIZATION)
|
||||
if (conn->bhc_rx_scid == chan->scid) {
|
||||
conn->bhc_rx_scid = 0x0000;
|
||||
#else
|
||||
if (conn->bhc_rx_chan == chan) {
|
||||
conn->bhc_rx_chan = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
SLIST_REMOVE(&conn->bhc_channels, chan, ble_l2cap_chan, next);
|
||||
ble_l2cap_chan_free(conn, chan);
|
||||
}
|
||||
@@ -294,6 +285,10 @@ ble_hs_conn_free(struct ble_hs_conn *conn)
|
||||
return;
|
||||
}
|
||||
|
||||
os_mbuf_free_chain(conn->rx_frags);
|
||||
conn->rx_frags = NULL;
|
||||
|
||||
|
||||
#if MYNEWT_VAL(BLE_GATTS)
|
||||
ble_att_svr_prep_clear(&conn->bhc_att_svr.basc_prep_list);
|
||||
#endif
|
||||
@@ -583,12 +578,8 @@ ble_hs_conn_timer(void)
|
||||
* passes after a partial packet is received, the connection is
|
||||
* terminated.
|
||||
*/
|
||||
#if MYNEWT_VAL(BT_NIMBLE_MEM_OPTIMIZATION)
|
||||
if (conn->bhc_rx_scid != 0x0000) {
|
||||
#else
|
||||
if (conn->bhc_rx_chan != NULL) {
|
||||
#endif
|
||||
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.*/
|
||||
|
||||
@@ -76,10 +76,18 @@ struct ble_hs_conn {
|
||||
ble_hs_conn_flags_t bhc_flags;
|
||||
|
||||
struct ble_l2cap_chan_list bhc_channels;
|
||||
#if !MYNEWT_VAL(BT_NIMBLE_MEM_OPTIMIZATION)
|
||||
struct ble_l2cap_chan *bhc_rx_chan; /* Channel rxing current packet. */
|
||||
|
||||
/* 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
|
||||
ble_npl_time_t bhc_rx_timeout;
|
||||
|
||||
#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
|
||||
uint32_t l2cap_coc_cid_mask[BLE_HS_CONN_L2CAP_COC_CID_MASK_LEN];
|
||||
#endif
|
||||
|
||||
@@ -1858,18 +1858,14 @@ int
|
||||
ble_hs_hci_evt_acl_process(struct os_mbuf *om)
|
||||
{
|
||||
struct hci_data_hdr hci_hdr;
|
||||
struct ble_hs_conn *conn;
|
||||
ble_l2cap_rx_fn *rx_cb;
|
||||
#if MYNEWT_VAL(BT_NIMBLE_MEM_OPTIMIZATION)
|
||||
struct ble_l2cap_chan *chan;
|
||||
#endif
|
||||
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)
|
||||
@@ -1885,56 +1881,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);
|
||||
#if MYNEWT_VAL(BT_NIMBLE_MEM_OPTIMIZATION)
|
||||
chan = ble_hs_conn_chan_find_by_scid(conn,conn->bhc_rx_scid);
|
||||
rc = rx_cb(chan);
|
||||
ble_l2cap_remove_rx(conn, chan);
|
||||
#else
|
||||
rc = rx_cb(conn->bhc_rx_chan);
|
||||
ble_l2cap_remove_rx(conn, conn->bhc_rx_chan);
|
||||
#endif
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
+136
-201
@@ -113,7 +113,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)
|
||||
@@ -139,18 +138,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;
|
||||
}
|
||||
@@ -296,91 +294,12 @@ 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)
|
||||
{
|
||||
#if MYNEWT_VAL(BT_NIMBLE_MEM_OPTIMIZATION)
|
||||
conn->bhc_rx_scid = 0x0000;
|
||||
#else
|
||||
conn->bhc_rx_chan = NULL;
|
||||
#endif
|
||||
if (chan->rx_buf == NULL) {
|
||||
BLE_HS_LOG(INFO, "L2CAP remove_rx: rx_buf already NULL; "
|
||||
"conn_handle=0x%04x scid=0x%04x\n",
|
||||
conn->bhc_handle, chan->scid);
|
||||
} else {
|
||||
BLE_HS_LOG(INFO, "L2CAP remove_rx: freeing partial rx_buf; "
|
||||
"conn_handle=0x%04x scid=0x%04x rx_len=%d\n",
|
||||
conn->bhc_handle, chan->scid, chan->rx_len);
|
||||
}
|
||||
os_mbuf_free_chain(chan->rx_buf);
|
||||
chan->rx_buf = NULL;
|
||||
chan->rx_len = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
ble_l2cap_append_rx(struct ble_l2cap_chan *chan, struct os_mbuf *frag)
|
||||
{
|
||||
#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
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
int len_diff;
|
||||
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;
|
||||
rc = 0;
|
||||
} else {
|
||||
/* More fragments remain. */
|
||||
#if MYNEWT_VAL(BLE_L2CAP_RX_FRAG_TIMEOUT) != 0
|
||||
conn->bhc_rx_timeout =
|
||||
ble_npl_time_get() + MYNEWT_VAL(BLE_L2CAP_RX_FRAG_TIMEOUT);
|
||||
|
||||
ble_hs_timer_resched();
|
||||
#endif
|
||||
rc = BLE_HS_EAGAIN;
|
||||
}
|
||||
|
||||
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
|
||||
* 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
|
||||
*/
|
||||
@@ -390,137 +309,153 @@ ble_l2cap_get_mtu(struct ble_l2cap_chan *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)
|
||||
static void
|
||||
ble_l2cap_rx_free(struct ble_hs_conn *conn)
|
||||
{
|
||||
struct ble_l2cap_chan *chan;
|
||||
struct ble_l2cap_hdr l2cap_hdr;
|
||||
uint8_t pb;
|
||||
os_mbuf_free_chain(conn->rx_frags);
|
||||
conn->rx_frags = NULL;
|
||||
conn->rx_len = 0;
|
||||
conn->rx_cid = 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ble_l2cap_rx_frags_process(struct ble_hs_conn *conn)
|
||||
{
|
||||
int rem_rx_len;
|
||||
int rc;
|
||||
|
||||
*out_reject_cid = -1;
|
||||
rem_rx_len = conn->rx_len - OS_MBUF_PKTLEN(conn->rx_frags);
|
||||
if (rem_rx_len == 0) {
|
||||
rc = 0;
|
||||
} else if (rem_rx_len > 0) {
|
||||
#if MYNEWT_VAL(BLE_L2CAP_RX_FRAG_TIMEOUT) != 0
|
||||
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;
|
||||
}
|
||||
|
||||
int
|
||||
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 hdr;
|
||||
struct os_mbuf *rx_frags;
|
||||
uint16_t rx_len;
|
||||
uint16_t rx_cid;
|
||||
int rc;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
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 >= 0x0040 && chan->dcid <= 0x007F && l2cap_hdr.len > (chan->my_coc_mps + BLE_L2CAP_SDU_SZ)) {
|
||||
/* 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);
|
||||
rc = BLE_HS_EBADDATA;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (chan->rx_buf != NULL) {
|
||||
/* Previous data packet never completed. Discard old packet. */
|
||||
ble_l2cap_remove_rx(conn, chan);
|
||||
}
|
||||
|
||||
if (l2cap_hdr.len - BLE_L2CAP_SDU_SZ > 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;
|
||||
}
|
||||
|
||||
/* Remember channel and length of L2CAP data for reassembly. */
|
||||
#if MYNEWT_VAL(BT_NIMBLE_MEM_OPTIMIZATION)
|
||||
conn->bhc_rx_scid = chan->scid;
|
||||
#else
|
||||
conn->bhc_rx_chan = chan;
|
||||
#endif
|
||||
chan->rx_len = l2cap_hdr.len;
|
||||
break;
|
||||
|
||||
case BLE_HCI_PB_MIDDLE:
|
||||
#if MYNEWT_VAL(BT_NIMBLE_MEM_OPTIMIZATION)
|
||||
chan = ble_hs_conn_chan_find_by_scid(conn,conn->bhc_rx_scid);
|
||||
#else
|
||||
chan = conn->bhc_rx_chan;
|
||||
#endif
|
||||
if (chan == NULL || chan->rx_buf == NULL) {
|
||||
/* Middle fragment without the start. Discard new packet. */
|
||||
rc = BLE_HS_EBADDATA;
|
||||
goto err;
|
||||
if (conn->rx_frags) {
|
||||
/* Previously received data is incomplete, discard it */
|
||||
ble_l2cap_rx_free(conn);
|
||||
}
|
||||
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.
|
||||
*/
|
||||
os_mbuf_free_chain(om);
|
||||
rc = BLE_HS_EBADDATA;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* 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 err;
|
||||
goto done;
|
||||
}
|
||||
|
||||
rc = ble_l2cap_rx_payload(conn, chan, om, out_rx_cb);
|
||||
om = NULL;
|
||||
if (rc != 0) {
|
||||
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;
|
||||
}
|
||||
|
||||
os_mbuf_adj(conn->rx_frags, BLE_L2CAP_HDR_SZ);
|
||||
|
||||
conn->rx_len = hdr.len;
|
||||
conn->rx_cid = hdr.cid;
|
||||
}
|
||||
|
||||
return 0;
|
||||
/* Process fragments */
|
||||
rc = ble_l2cap_rx_frags_process(conn);
|
||||
if (rc) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -202,18 +202,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);
|
||||
|
||||
@@ -59,17 +59,12 @@ extern ble_l2cap_ctx_t *ble_l2cap_ctx;
|
||||
extern struct os_mempool ble_l2cap_chan_pool;
|
||||
#endif
|
||||
|
||||
/* 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
|
||||
#define BLE_L2CAP_SDU_SZ 2
|
||||
|
||||
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;
|
||||
@@ -92,9 +87,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
|
||||
@@ -133,8 +125,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);
|
||||
|
||||
@@ -143,16 +134,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);
|
||||
#if MYNEWT_VAL(BLE_STATIC_TO_DYNAMIC)
|
||||
void ble_l2cap_deinit(void);
|
||||
|
||||
@@ -1880,16 +1880,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);
|
||||
|
||||
|
||||
@@ -3029,13 +3029,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);
|
||||
@@ -3045,9 +3044,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;
|
||||
@@ -3335,7 +3331,7 @@ ble_sm_deinit(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;
|
||||
|
||||
Reference in New Issue
Block a user