summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--extmod/nimble/modbluetooth_nimble.c48
1 files changed, 42 insertions, 6 deletions
diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c
index f3679354f..d7ad7e17c 100644
--- a/extmod/nimble/modbluetooth_nimble.c
+++ b/extmod/nimble/modbluetooth_nimble.c
@@ -1392,7 +1392,16 @@ int mp_bluetooth_gattc_exchange_mtu(uint16_t conn_handle) {
#endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
+#if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS
+STATIC void unstall_l2cap_channel(void);
+#endif
+
void mp_bluetooth_nimble_sent_hci_packet(void) {
+ #if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS
+ if (os_msys_num_free() >= os_msys_count() * 3 / 4) {
+ unstall_l2cap_channel();
+ }
+ #endif
}
#if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS
@@ -1416,6 +1425,7 @@ typedef struct _mp_bluetooth_nimble_l2cap_channel_t {
struct os_mempool sdu_mempool;
struct os_mbuf *rx_pending;
bool irq_in_progress;
+ bool mem_stalled;
uint16_t mtu;
os_membuf_t sdu_mem[];
} mp_bluetooth_nimble_l2cap_channel_t;
@@ -1433,6 +1443,19 @@ STATIC void destroy_l2cap_channel() {
}
}
+STATIC void unstall_l2cap_channel(void) {
+ // Whenever we send an HCI packet and the sys mempool is now less than 1/4 full,
+ // we can unstall the L2CAP channel if it was marked as "mem_stalled" by
+ // mp_bluetooth_l2cap_send. (This happens if the pool is half-empty).
+ mp_bluetooth_nimble_l2cap_channel_t *chan = MP_STATE_PORT(bluetooth_nimble_root_pointers)->l2cap_chan;
+ if (!chan || !chan->mem_stalled) {
+ return;
+ }
+ DEBUG_printf("unstall_l2cap_channel: count %d, free: %d\n", os_msys_count(), os_msys_num_free());
+ chan->mem_stalled = false;
+ mp_bluetooth_on_l2cap_send_ready(chan->chan->conn_handle, chan->chan->scid, 0);
+}
+
STATIC int l2cap_channel_event(struct ble_l2cap_event *event, void *arg) {
DEBUG_printf("l2cap_channel_event: type=%d\n", event->type);
mp_bluetooth_nimble_l2cap_channel_t *chan = (mp_bluetooth_nimble_l2cap_channel_t *)arg;
@@ -1528,9 +1551,13 @@ STATIC int l2cap_channel_event(struct ble_l2cap_event *event, void *arg) {
}
case BLE_L2CAP_EVENT_COC_TX_UNSTALLED: {
DEBUG_printf("l2cap_channel_event: tx_unstalled: conn_handle=%d status=%d\n", event->tx_unstalled.conn_handle, event->tx_unstalled.status);
- ble_l2cap_get_chan_info(event->receive.chan, &info);
- // Map status to {0,1} (i.e. "sent everything", or "partial send").
- mp_bluetooth_on_l2cap_send_ready(event->tx_unstalled.conn_handle, info.scid, event->tx_unstalled.status == 0 ? 0 : 1);
+ assert(event->tx_unstalled.conn_handle == chan->chan->conn_handle);
+ // Don't unstall if we're still waiting for room in the sys pool.
+ if (!chan->mem_stalled) {
+ ble_l2cap_get_chan_info(event->receive.chan, &info);
+ // Map status to {0,1} (i.e. "sent everything", or "partial send").
+ mp_bluetooth_on_l2cap_send_ready(event->tx_unstalled.conn_handle, info.scid, event->tx_unstalled.status == 0 ? 0 : 1);
+ }
break;
}
case BLE_L2CAP_EVENT_COC_RECONFIG_COMPLETED: {
@@ -1678,10 +1705,13 @@ int mp_bluetooth_l2cap_send(uint16_t conn_handle, uint16_t cid, const uint8_t *b
return MP_ENOMEM;
}
+ *stalled = false;
+
err = ble_l2cap_send(chan->chan, sdu_tx);
if (err == BLE_HS_ESTALLED) {
// Stalled means that this one will still send but any future ones
// will fail until we receive an unstalled event.
+ DEBUG_printf("mp_bluetooth_l2cap_send: credit stall\n");
*stalled = true;
err = 0;
} else {
@@ -1689,15 +1719,21 @@ int mp_bluetooth_l2cap_send(uint16_t conn_handle, uint16_t cid, const uint8_t *b
// Anything except stalled means it won't attempt to send,
// so free the mbuf (we're failing the op entirely).
os_mbuf_free_chain(sdu_tx);
- } else {
- *stalled = false;
}
}
+ if (os_msys_num_free() <= os_msys_count() / 2) {
+ // If the sys mempool is less than half-full, then back off sending more
+ // on this channel.
+ DEBUG_printf("mp_bluetooth_l2cap_send: forcing mem stall: count %d, free: %d\n", os_msys_count(), os_msys_num_free());
+ chan->mem_stalled = true;
+ *stalled = true;
+ }
+
// Sometimes we see what looks like BLE_HS_EAGAIN (but it's actually
// OS_ENOMEM in disguise). Fixed in NimBLE v1.4.
if (err == OS_ENOMEM) {
- return MP_ENOMEM;
+ err = BLE_HS_ENOMEM;
}
// Other error codes such as BLE_HS_EBUSY (we're stalled) or BLE_HS_EBADDATA (bigger than MTU).