Skip to content

Commit cf7b771

Browse files
jaguilarlaurensvalk
authored andcommitted
pbdrv/bluetooth: Further EV3 RX interrupt safety.
In the case where the RX ring buffer is full, disable interrupts to prevent an interrupt storm. When consuming bytes from the RX ring buffer, check if the interrupts are disabled, and if so, reenable them when the RX ring buffer has space. Tested by manually setting the ring buffer size to an artificially short length and observing the interrupts being repeatedly disabled and enabled. When the ring buffer is an appropriate length, none of the traffic that the CC2560 module generates during the startup phase is sufficient to trigger this condition.
1 parent c9aa9ac commit cf7b771

1 file changed

Lines changed: 67 additions & 7 deletions

File tree

lib/pbio/drv/bluetooth/bluetooth_btstack_ev3.c

Lines changed: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,8 @@ static volatile bool dma_write_complete;
202202
#define EDMA_BASE (SOC_EDMA30CC_0_REGS)
203203
#define DMA_EVENT_QUEUE (0)
204204

205+
static volatile bool rx_interrupts_enabled;
206+
205207
// The highest speed not greater than 3 Mbaud that can be exactly represented
206208
// with the AM1808's UART clock divider and a relatively high oversampling rate.
207209
// The formula is (CLOCK / (OVERSAMPLING_RATE * DIVISOR)) = BAUDRATE. Here are
@@ -219,23 +221,81 @@ void pbdrv_bluetooth_btstack_ev3_handle_tx_complete(void) {
219221
pbdrv_bluetooth_btstack_run_loop_trigger();
220222
}
221223

222-
static void uart_rx_interrupt_handler(void) {
223-
IntSystemStatusClear(UART_RX_INTR);
224+
static inline void uart_rx_interrupt_set_enabled(bool enabled) {
225+
if (enabled) {
226+
UARTIntEnable(UART_PORT, UART_INT_RXDATA_CTI | UART_INT_LINE_STAT);
227+
} else {
228+
UARTIntDisable(UART_PORT, UART_INT_RXDATA_CTI | UART_INT_LINE_STAT);
229+
}
230+
rx_interrupts_enabled = enabled;
231+
}
232+
233+
static inline bool uart_rx_interrupt_is_enabled(void) {
234+
return rx_interrupts_enabled;
235+
}
236+
237+
static void uart_rx_drain_fifo_into_ring_buffer(void) {
224238
int c;
225-
while ((c = UARTCharGetNonBlocking(UART_PORT)) >= 0) {
239+
int avail = lwrb_get_free(&uart_rx_pending_ring_buffer);
240+
while (avail > 0 && (c = UARTCharGetNonBlocking(UART_PORT)) >= 0) {
226241
lwrb_write(&uart_rx_pending_ring_buffer, (uint8_t *)&c, 1);
242+
avail--;
243+
}
244+
}
245+
246+
static void uart_rx_interrupt_handler(void) {
247+
IntSystemStatusClear(UART_RX_INTR);
248+
uart_rx_drain_fifo_into_ring_buffer();
249+
if (lwrb_get_free(&uart_rx_pending_ring_buffer) == 0) {
250+
// RX buffer full, disable further RX interrupts until btstack consumes
251+
// some of the data.
252+
uart_rx_interrupt_set_enabled(false);
253+
pbdrv_bluetooth_btstack_run_loop_trigger();
254+
return;
227255
}
228256
if (read_buf && (size_t)read_buf_len <= lwrb_get_full(&uart_rx_pending_ring_buffer)) {
229257
pbdrv_bluetooth_btstack_run_loop_trigger();
230258
}
231259
}
232260

233261
static void pbdrv_bluetooth_btstack_classic_drive_read() {
234-
if (!read_buf || read_buf_len <= 0 || lwrb_get_full(&uart_rx_pending_ring_buffer) < (size_t)read_buf_len) {
262+
if (!read_buf || read_buf_len <= 0) {
263+
return;
264+
}
265+
266+
int nread = lwrb_read(&uart_rx_pending_ring_buffer, read_buf, read_buf_len);
267+
read_buf += nread;
268+
read_buf_len -= nread;
269+
270+
// If UART RX interrupts are disabled (because we previously filled up
271+
// our ring buffer), see if we can re-enable them. We must first drain the
272+
// FIFO because the interrupt will not trigger if the FIFO is already full
273+
// above the trigger level.
274+
if (!uart_rx_interrupt_is_enabled()) {
275+
// Drain as much of the FIFO as we can directly into the read buffer.
276+
int c;
277+
while (read_buf_len > 0 && (c = UARTCharGetNonBlocking(UART_PORT)) >= 0) {
278+
*read_buf++ = (uint8_t)c;
279+
read_buf_len--;
280+
}
281+
282+
// Drain the rest into the ring buffer.
283+
uart_rx_drain_fifo_into_ring_buffer();
284+
285+
// If there's any space available in the ring buffer, re-enable RX
286+
// interrupts. Note that if there's not space available, that
287+
// necessarily means that the current message has been completely read
288+
// and we will arrive back here when the next btstack message request
289+
// comes in.
290+
if (lwrb_get_free(&uart_rx_pending_ring_buffer) > 0) {
291+
uart_rx_interrupt_set_enabled(true);
292+
}
293+
}
294+
295+
if (read_buf_len > 0) {
235296
return;
236297
}
237298

238-
lwrb_read(&uart_rx_pending_ring_buffer, read_buf, read_buf_len);
239299
read_buf = NULL;
240300
read_buf_len = 0;
241301
if (block_received) {
@@ -320,7 +380,7 @@ static int pbdrv_bluetooth_btstack_ev3_open(void) {
320380
IntRegister(UART_RX_INTR, uart_rx_interrupt_handler);
321381
IntChannelSet(UART_RX_INTR, 2);
322382
IntSystemEnable(UART_RX_INTR);
323-
UARTIntEnable(UART_PORT, UART_INT_RXDATA_CTI | UART_INT_LINE_STAT);
383+
uart_rx_interrupt_set_enabled(true);
324384

325385
btstack_run_loop_set_data_source_handler(&pbdrv_bluetooth_btstack_classic_poll_data_source, pbdrv_bluetooth_btstack_classic_poll);
326386
btstack_run_loop_enable_data_source_callbacks(&pbdrv_bluetooth_btstack_classic_poll_data_source, DATA_SOURCE_CALLBACK_POLL);
@@ -334,7 +394,7 @@ static int pbdrv_bluetooth_btstack_ev3_close(void) {
334394

335395
UARTDMADisable(UART_PORT, (UART_RX_INTR_TRIG_LEVEL | UART_FIFO_MODE));
336396
EDMA3FreeChannel(EDMA_BASE, EDMA3_CHANNEL_TYPE_DMA, DMA_CHA_TX, EDMA3_TRIG_MODE_EVENT, DMA_CHA_TX, DMA_EVENT_QUEUE);
337-
UARTIntDisable(UART_PORT, UART_INT_RXDATA_CTI | UART_INT_LINE_STAT);
397+
uart_rx_interrupt_set_enabled(false);
338398
UARTDisable(UART_PORT);
339399

340400
lwrb_free(&uart_rx_pending_ring_buffer);

0 commit comments

Comments
 (0)