diff --git a/src/portable/microchip/samd/dcd_samd.c b/src/portable/microchip/samd/dcd_samd.c index a01bef38a..a859b72b9 100644 --- a/src/portable/microchip/samd/dcd_samd.c +++ b/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) 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