fix dcd samd race condition with setup packet

setup packet can complete together with previous status (in & out).
Always make sure to prepare valid buffer for holding setup packet when
queuing control status.
This commit is contained in:
hathach 2020-04-30 00:29:47 +07:00
parent e9c71055ac
commit a74a823b0a
1 changed files with 15 additions and 15 deletions

View File

@ -37,14 +37,11 @@
static TU_ATTR_ALIGNED(4) UsbDeviceDescBank sram_registers[8][2];
static TU_ATTR_ALIGNED(4) uint8_t _setup_packet[8];
// ready for receiving SETUP packet
static inline void prepare_setup(void)
{
// Only make sure the EP0 OUT buffer is ready
sram_registers[0][0].ADDR.reg = (uint32_t) _setup_packet;
sram_registers[0][0].PCKSIZE.bit.MULTI_PACKET_SIZE = sizeof(_setup_packet);
sram_registers[0][0].PCKSIZE.bit.BYTE_COUNT = 0;
}
// Setup the control endpoint 0.
@ -64,7 +61,6 @@ static void bus_reset(void)
prepare_setup();
}
/*------------------------------------------------------------------*/
/* Controller API
*------------------------------------------------------------------*/
@ -183,7 +179,7 @@ void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * re
}
// Just finished status stage, prepare for next setup packet
// Note: we may already prepare setup when the last EP0 OUT complete.
// Note: we may already prepare setup when queueing the control status.
// but it has no harm to do it again here
prepare_setup();
}
@ -235,6 +231,14 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
bank->ADDR.reg = (uint32_t) buffer;
// A SETUP token can occur immediately after an ZLP Status.
// So make sure we have a valid buffer for setup packet.
// Status = ZLP EP0 with direction opposite to one in the dir bit of current setup
if ( (epnum == 0) && (buffer == NULL) && (total_bytes == 0) && (dir != tu_edpt_dir(_setup_packet[0])) ) {
prepare_setup();
}
if ( dir == TUSB_DIR_OUT )
{
bank->PCKSIZE.bit.MULTI_PACKET_SIZE = total_bytes;
@ -297,7 +301,7 @@ void maybe_transfer_complete(void) {
// Handle IN completions
if ((epintflag & USB_DEVICE_EPINTFLAG_TRCPT1) != 0) {
UsbDeviceDescBank* bank = &sram_registers[epnum][TUSB_DIR_IN];
uint16_t total_transfer_size = bank->PCKSIZE.bit.BYTE_COUNT;
uint16_t const total_transfer_size = bank->PCKSIZE.bit.BYTE_COUNT;
dcd_event_xfer_complete(0, epnum | TUSB_DIR_IN_MASK, total_transfer_size, XFER_RESULT_SUCCESS, true);
@ -306,15 +310,8 @@ void maybe_transfer_complete(void) {
// Handle OUT completions
if ((epintflag & USB_DEVICE_EPINTFLAG_TRCPT0) != 0) {
UsbDeviceDescBank* bank = &sram_registers[epnum][TUSB_DIR_OUT];
uint16_t total_transfer_size = bank->PCKSIZE.bit.BYTE_COUNT;
// A SETUP token can occur immediately after an OUT packet
// so make sure we have a valid buffer for the control endpoint.
if (epnum == 0) {
prepare_setup();
}
uint16_t const total_transfer_size = bank->PCKSIZE.bit.BYTE_COUNT;
dcd_event_xfer_complete(0, epnum, total_transfer_size, XFER_RESULT_SUCCESS, true);
@ -381,7 +378,10 @@ void dcd_int_handler (uint8_t rhport)
// This copies the data elsewhere so we can reuse the buffer.
dcd_event_setup_received(0, _setup_packet, true);
USB->DEVICE.DeviceEndpoint[0].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_RXSTP;
// Although Setup packet only set RXSTP bit,
// TRCPT0 bit could already be set by previous ZLP OUT Status (not handled until now).
// Since control status complete event is optional, we can just clear TRCPT0 and skip the status event
USB->DEVICE.DeviceEndpoint[0].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_RXSTP | USB_DEVICE_EPINTFLAG_TRCPT0;
}
// Handle complete transfer