Skip to content

Commit 44e8369

Browse files
committed
fix: enable usb flow control on nrf52840
1 parent e129a81 commit 44e8369

File tree

3 files changed

+45
-5
lines changed

3 files changed

+45
-5
lines changed

src/machine/machine_nrf52840_usb.go

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ var (
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.
311334
func 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

src/machine/usb/msc/msc.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,8 +302,7 @@ func (m *msc) run(b []byte, isEpOut bool) bool {
302302
func (m *msc) RegisterBlockDevice(dev machine.BlockDevice) {
303303
m.dev = dev
304304

305-
maxPacketSize := descriptor.EndpointMSCIN.GetMaxPacketSize()
306-
bufSize := max(dev.WriteBlockSize(), int64(maxPacketSize))
305+
bufSize := max(dev.WriteBlockSize(), int64(m.maxPacketSize))
307306

308307
if cap(m.blockCache) != int(bufSize) {
309308
m.blockCache = make([]byte, bufSize)

src/machine/usb/msc/scsi.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,8 @@ func (m *msc) scsiQueueTask(cmdType scsi.CmdType, b []byte) bool {
267267
switch cmdType {
268268
case scsi.CmdWrite:
269269
// If we're writing data wait until we have a full write block of data that can be processed.
270-
if m.queuedBytes == uint32(cap(m.blockCache)) {
270+
// Or if we've received all the data we're expecting.
271+
if m.queuedBytes == uint32(cap(m.blockCache)) || m.sentBytes+m.queuedBytes >= m.totalBytes {
271272
m.taskQueued = true
272273
}
273274
case scsi.CmdUnmap:

0 commit comments

Comments
 (0)