summaryrefslogtreecommitdiff
path: root/extmod/nimble/modbluetooth_nimble.c
diff options
context:
space:
mode:
Diffstat (limited to 'extmod/nimble/modbluetooth_nimble.c')
-rw-r--r--extmod/nimble/modbluetooth_nimble.c36
1 files changed, 29 insertions, 7 deletions
diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c
index 8bead890d..a2eb8a588 100644
--- a/extmod/nimble/modbluetooth_nimble.c
+++ b/extmod/nimble/modbluetooth_nimble.c
@@ -1355,6 +1355,7 @@ typedef struct _mp_bluetooth_nimble_l2cap_channel_t {
struct os_mbuf_pool sdu_mbuf_pool;
struct os_mempool sdu_mempool;
struct os_mbuf *rx_pending;
+ bool irq_in_progress;
uint16_t mtu;
os_membuf_t sdu_mem[];
} mp_bluetooth_nimble_l2cap_channel_t;
@@ -1441,7 +1442,23 @@ STATIC int l2cap_channel_event(struct ble_l2cap_event *event, void *arg) {
chan->chan->coc_rx.sdu = sdu_rx;
ble_l2cap_get_chan_info(event->receive.chan, &info);
+
+ // Don't allow granting more credits until after the IRQ is handled.
+ chan->irq_in_progress = true;
+
mp_bluetooth_gattc_on_l2cap_recv(event->receive.conn_handle, info.scid);
+ chan->irq_in_progress = false;
+
+ // If all data has been consumed by the IRQ handler, then now allow
+ // more credits. If the IRQ handler doesn't consume all available data
+ // then rx_pending will be still set.
+ if (!chan->rx_pending) {
+ struct os_mbuf *sdu_rx = chan->chan->coc_rx.sdu;
+ assert(sdu_rx);
+ if (sdu_rx) {
+ ble_l2cap_recv_ready(chan->chan, sdu_rx);
+ }
+ }
break;
}
case BLE_L2CAP_EVENT_COC_TX_UNSTALLED: {
@@ -1508,6 +1525,7 @@ STATIC int create_l2cap_channel(uint16_t mtu, mp_bluetooth_nimble_l2cap_channel_
chan->mtu = mtu;
chan->rx_pending = NULL;
+ chan->irq_in_progress = false;
int err = os_mempool_init(&chan->sdu_mempool, buf_blocks, L2CAP_BUF_BLOCK_SIZE, chan->sdu_mem, "l2cap_sdu_pool");
if (err != 0) {
@@ -1635,13 +1653,17 @@ int mp_bluetooth_l2cap_recvinto(uint16_t conn_handle, uint16_t cid, uint8_t *buf
os_mbuf_free_chain(chan->rx_pending);
chan->rx_pending = NULL;
- // We've already given the channel a new mbuf in l2cap_channel_event above, so
- // re-use that mbuf in the call to ble_l2cap_recv_ready. This will just
- // give the channel more credits.
- struct os_mbuf *sdu_rx = chan->chan->coc_rx.sdu;
- assert(sdu_rx);
- if (sdu_rx) {
- ble_l2cap_recv_ready(chan->chan, sdu_rx);
+ // If we're in the call stack of the l2cap_channel_event handler, then don't
+ // re-enable receiving yet (as we need to complete the rest of IRQ handler first).
+ if (!chan->irq_in_progress) {
+ // We've already given the channel a new mbuf in l2cap_channel_event above, so
+ // re-use that mbuf in the call to ble_l2cap_recv_ready. This will just
+ // give the channel more credits.
+ struct os_mbuf *sdu_rx = chan->chan->coc_rx.sdu;
+ assert(sdu_rx);
+ if (sdu_rx) {
+ ble_l2cap_recv_ready(chan->chan, sdu_rx);
+ }
}
} else {
// Trim the used bytes from the start of the mbuf.