Ver Fonte

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.
hathach há 6 anos atrás
pai
commit
a74a823b0a
1 ficheiros alterados com 15 adições e 15 exclusões
  1. 15 15
      src/portable/microchip/samd/dcd_samd.c

+ 15 - 15
src/portable/microchip/samd/dcd_samd.c

@@ -37,14 +37,11 @@
 static TU_ATTR_ALIGNED(4) UsbDeviceDescBank sram_registers[8][2];
 static TU_ATTR_ALIGNED(4) UsbDeviceDescBank sram_registers[8][2];
 static TU_ATTR_ALIGNED(4) uint8_t _setup_packet[8];
 static TU_ATTR_ALIGNED(4) uint8_t _setup_packet[8];
 
 
-
 // ready for receiving SETUP packet
 // ready for receiving SETUP packet
 static inline void prepare_setup(void)
 static inline void prepare_setup(void)
 {
 {
   // Only make sure the EP0 OUT buffer is ready
   // Only make sure the EP0 OUT buffer is ready
   sram_registers[0][0].ADDR.reg = (uint32_t) _setup_packet;
   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.
 // Setup the control endpoint 0.
@@ -64,7 +61,6 @@ static void bus_reset(void)
   prepare_setup();
   prepare_setup();
 }
 }
 
 
-
 /*------------------------------------------------------------------*/
 /*------------------------------------------------------------------*/
 /* Controller API
 /* 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
   // 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
   // but it has no harm to do it again here
   prepare_setup();
   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];
   UsbDeviceEndpoint* ep = &USB->DEVICE.DeviceEndpoint[epnum];
 
 
   bank->ADDR.reg = (uint32_t) buffer;
   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 )
   if ( dir == TUSB_DIR_OUT )
   {
   {
     bank->PCKSIZE.bit.MULTI_PACKET_SIZE = total_bytes;
     bank->PCKSIZE.bit.MULTI_PACKET_SIZE = total_bytes;
@@ -297,7 +301,7 @@ void maybe_transfer_complete(void) {
     // Handle IN completions
     // Handle IN completions
     if ((epintflag & USB_DEVICE_EPINTFLAG_TRCPT1) != 0) {
     if ((epintflag & USB_DEVICE_EPINTFLAG_TRCPT1) != 0) {
       UsbDeviceDescBank* bank = &sram_registers[epnum][TUSB_DIR_IN];
       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);
       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
     // Handle OUT completions
     if ((epintflag & USB_DEVICE_EPINTFLAG_TRCPT0) != 0) {
     if ((epintflag & USB_DEVICE_EPINTFLAG_TRCPT0) != 0) {
-
       UsbDeviceDescBank* bank = &sram_registers[epnum][TUSB_DIR_OUT];
       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);
       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.
     // This copies the data elsewhere so we can reuse the buffer.
     dcd_event_setup_received(0, _setup_packet, true);
     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
   // Handle complete transfer