2222 epinen uint32
2323 epouten uint32
2424 easyDMABusy volatile.Register8
25+ // epOutFlowControl contains the flow control state of the USB OUT endpoints.
26+ epOutFlowControl [NumberOfUSBEndpoints ]struct {
27+ // nak indicates that we are NAKing any further OUT packets because the rxHandler isn't ready yet.
28+ // When this is true, we do not restart the DMA for the endpoint, effectively pausing it.
29+ nak bool
30+ // dataPending indicates that we have data in the hardware buffer that hasn't been handled yet.
31+ // Having one in the buffer is what generates the NAK responses, this is a signal to handle it.
32+ dataPending bool
33+ }
2534
2635 endPoints = []uint32 {
2736 usb .CONTROL_ENDPOINT : usb .ENDPOINT_TYPE_CONTROL ,
@@ -196,7 +205,16 @@ func handleUSBIRQ(interrupt.Interrupt) {
196205 nrf .USBD .EPOUT [i ].PTR .Set (uint32 (uintptr (unsafe .Pointer (& udd_ep_out_cache_buffer [i ]))))
197206 count := nrf .USBD .SIZE .EPOUT [i ].Get ()
198207 nrf .USBD .EPOUT [i ].MAXCNT .Set (count )
199- nrf .USBD .TASKS_STARTEPOUT [i ].Set (1 )
208+ if ! epOutFlowControl [i ].nak {
209+ // Normal case: We want data, so start DMA immediately
210+ nrf .USBD .TASKS_STARTEPOUT [i ].Set (1 )
211+ epOutFlowControl [i ].dataPending = false
212+ } else {
213+ // NAK case: We want to NAK, so DO NOT start DMA.
214+ // The data stays in HW buffer. Host receives NAKs.
215+ // Mark that we have data waiting so we can fetch it later.
216+ epOutFlowControl [i ].dataPending = true
217+ }
200218 }
201219 }
202220 }
@@ -208,6 +226,10 @@ func handleUSBIRQ(interrupt.Interrupt) {
208226 buf := handleEndpointRx (uint32 (i ))
209227 if usbRxHandler [i ] == nil || usbRxHandler [i ](buf ) {
210228 AckUsbOutTransfer (uint32 (i ))
229+ } else {
230+ // usbRxHandler returned false, so NAK further OUT packets until we're ready
231+ epOutFlowControl [i ].nak = true
232+ nrf .USBD .SIZE .EPOUT [i ].Set (0 )
211233 }
212234 exitCriticalSection ()
213235 }
@@ -308,8 +330,26 @@ func handleEndpointRx(ep uint32) []byte {
308330}
309331
310332// AckUsbOutTransfer is called to acknowledge the completion of a USB OUT transfer.
333+ // It also clears the NAK state and resumes data flow if it was paused.
311334func AckUsbOutTransfer (ep uint32 ) {
312- // set ready for next data
335+ epOutFlowControl [ep ].nak = false
336+
337+ // If we ignored a packet earlier (Buffer Full strategy), we must manually
338+ // trigger the DMA now to pull it from the HW buffer.
339+ if epOutFlowControl [ep ].dataPending {
340+ epOutFlowControl [ep ].dataPending = false
341+
342+ // Prepare DMA to move data from HW Buffer -> RAM
343+ nrf .USBD .EPOUT [ep ].PTR .Set (uint32 (uintptr (unsafe .Pointer (& udd_ep_out_cache_buffer [ep ]))))
344+ count := nrf .USBD .SIZE .EPOUT [ep ].Get ()
345+ nrf .USBD .EPOUT [ep ].MAXCNT .Set (count )
346+
347+ // Kick the DMA
348+ nrf .USBD .TASKS_STARTEPOUT [ep ].Set (1 )
349+ return
350+ }
351+
352+ // Otherwise, just re-arm the endpoint to accept the NEXT packet
313353 nrf .USBD .SIZE .EPOUT [ep ].Set (0 )
314354}
315355
0 commit comments