Skip to content

Commit da44fe3

Browse files
committed
nrf5x: Fix EP OUT race conditions
When dcd_edpt_xfer() starts new transfer two separate problems were observed. For both problems stream of OUT packets was pouring from host. First problem was that total_len and actual_len were not atomic. In case where incoming OUT packets are less (63) than MPS (64), actual_len and total_len are set 63. Then transfer complete from USBD is called that will schedule next 64 bytes transfer. At that point incoming packet would start DMA if there is place in RAM, normally it does not happen since actual_len == total_len. If packets arrives and interrupt is raised after total_len is set (64) but actual_len is still 63 from previous transfer, interrupt code sees that there is place in ram (1 byte) and transfer this 1 byte to buffer that was already filled with previous packet. To remedy this USB interrupt is blocked during transfer setup. Second problem can happen when dcd_edpt_xfer setups xfer->total_len and actual_len correctly but then context switch happens before xfer->data_received is checked. If during this time two packets arrive one will be copied to RAM second will stay in endpoint with data_received set to 1. Then when xfer_edpt_xfer() checks data_receive flag it starts DMA again overwriting data. To remedy this, data_received is checked together with check if data was already transferred. If transfer was complete, there is no need to start DMA yet. In such case data_received will be handled in same place by next xfer_edpt_xfer() correctly.
1 parent 3b09b82 commit da44fe3

File tree

1 file changed

+3
-1
lines changed

1 file changed

+3
-1
lines changed

src/portable/nordic/nrf5x/dcd_nrf5x.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -453,9 +453,11 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
453453

454454
xfer_td_t* xfer = get_td(epnum, dir);
455455

456+
dcd_int_disable(rhport);
456457
xfer->buffer = buffer;
457458
xfer->total_len = total_bytes;
458459
xfer->actual_len = 0;
460+
dcd_int_enable(rhport);
459461

460462
// Control endpoint with zero-length packet and opposite direction to 1st request byte --> status stage
461463
bool const control_status = (epnum == 0 && total_bytes == 0 && dir != tu_edpt_dir(NRF_USBD->BMREQUESTTYPE));
@@ -476,7 +478,7 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
476478
edpt_dma_start(&NRF_USBD->TASKS_EP0RCVOUT);
477479
}else
478480
{
479-
if ( xfer->data_received )
481+
if ( xfer->data_received && xfer->total_len > xfer->actual_len)
480482
{
481483
// Data is already received previously
482484
// start DMA to copy to SRAM

0 commit comments

Comments
 (0)