Skip to content

Commit 649a66d

Browse files
committed
feat: add usb msc support for nrf52840
1 parent 9118e91 commit 649a66d

File tree

3 files changed

+160
-50
lines changed

3 files changed

+160
-50
lines changed

src/machine/machine_nrf52840_usb.go

Lines changed: 113 additions & 3 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
}
@@ -229,19 +251,23 @@ func initEndpoint(ep, config uint32) {
229251
switch config {
230252
case usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointIn:
231253
enableEPIn(ep)
254+
setEPDataPID(ep|usb.EndpointIn, false)
232255

233256
case usb.ENDPOINT_TYPE_BULK | usb.EndpointOut:
234257
nrf.USBD.INTENSET.Set(nrf.USBD_INTENSET_ENDEPOUT0 << ep)
235258
nrf.USBD.SIZE.EPOUT[ep].Set(0)
236259
enableEPOut(ep)
260+
setEPDataPID(ep, false)
237261

238262
case usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointOut:
239263
nrf.USBD.INTENSET.Set(nrf.USBD_INTENSET_ENDEPOUT0 << ep)
240264
nrf.USBD.SIZE.EPOUT[ep].Set(0)
241265
enableEPOut(ep)
266+
setEPDataPID(ep, false)
242267

243268
case usb.ENDPOINT_TYPE_BULK | usb.EndpointIn:
244269
enableEPIn(ep)
270+
setEPDataPID(ep|usb.EndpointIn, false)
245271

246272
case usb.ENDPOINT_TYPE_CONTROL:
247273
enableEPIn(0)
@@ -259,7 +285,7 @@ func SendUSBInPacket(ep uint32, data []byte) bool {
259285
sendUSBPacket(ep, data, 0)
260286

261287
// clear transfer complete flag
262-
nrf.USBD.INTENCLR.Set(nrf.USBD_INTENCLR_ENDEPOUT0 << 4)
288+
nrf.USBD.INTENCLR.Set(nrf.USBD_INTENCLR_ENDEPOUT0 << ep)
263289

264290
return true
265291
}
@@ -304,8 +330,26 @@ func handleEndpointRx(ep uint32) []byte {
304330
}
305331

306332
// 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.
307334
func AckUsbOutTransfer(ep uint32) {
308-
// 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
309353
nrf.USBD.SIZE.EPOUT[ep].Set(0)
310354
}
311355

@@ -379,3 +423,69 @@ func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
379423

380424
return b, nil
381425
}
426+
427+
// Set the USB endpoint Packet ID to DATA0 or DATA1.
428+
// In endpoints must have bit 7 (0x80) set.
429+
func setEPDataPID(ep uint32, dataOne bool) {
430+
val := ep
431+
if dataOne {
432+
val |= nrf.USBD_DTOGGLE_VALUE_Data1 << nrf.USBD_DTOGGLE_VALUE_Pos
433+
} else {
434+
val |= nrf.USBD_DTOGGLE_VALUE_Data0 << nrf.USBD_DTOGGLE_VALUE_Pos
435+
}
436+
nrf.USBD.DTOGGLE.Set(val)
437+
}
438+
439+
// Set ENDPOINT_HALT/stall status on a USB IN endpoint.
440+
func (dev *USBDevice) SetStallEPIn(ep uint32) {
441+
if ep&0x7F == 0 {
442+
nrf.USBD.TASKS_EP0STALL.Set(1)
443+
} else if ep&0x7F < NumberOfUSBEndpoints {
444+
// Stall In Endpoint
445+
val := 0x100 | 0x80 | ep
446+
nrf.USBD.EPSTALL.Set(val)
447+
}
448+
}
449+
450+
// Set ENDPOINT_HALT/stall status on a USB OUT endpoint.
451+
func (dev *USBDevice) SetStallEPOut(ep uint32) {
452+
if ep == 0 {
453+
nrf.USBD.TASKS_EP0STALL.Set(1)
454+
} else if ep < NumberOfUSBEndpoints {
455+
// Stall Out Endpoint
456+
val := 0x100 | 0x00 | ep
457+
nrf.USBD.EPSTALL.Set(val)
458+
}
459+
}
460+
461+
// Clear the ENDPOINT_HALT/stall on a USB IN endpoint.
462+
func (dev *USBDevice) ClearStallEPIn(ep uint32) {
463+
if ep&0x7F == 0 {
464+
nrf.USBD.TASKS_EP0STALL.Set(0)
465+
} else if ep&0x7F < NumberOfUSBEndpoints {
466+
// Reset the endpoint data PID to DATA0
467+
ep |= 0x80 // Set endpoint direction bit
468+
setEPDataPID(ep, false)
469+
470+
// No-stall In Endpoint
471+
val := 0x000 | 0x80 | ep
472+
nrf.USBD.EPSTALL.Set(val)
473+
}
474+
}
475+
476+
// Clear the ENDPOINT_HALT/stall on a USB OUT endpoint.
477+
func (dev *USBDevice) ClearStallEPOut(ep uint32) {
478+
if ep == 0 {
479+
nrf.USBD.TASKS_EP0STALL.Set(0)
480+
} else if ep < NumberOfUSBEndpoints {
481+
// Reset the endpoint data PID to DATA0
482+
setEPDataPID(ep, false)
483+
484+
// No-stall Out Endpoint
485+
val := 0x000 | 0x00 | ep
486+
nrf.USBD.EPSTALL.Set(val)
487+
488+
// Write a value to the SIZE register to allow nRF to ACK/accept data
489+
nrf.USBD.SIZE.EPOUT[ep].Set(0)
490+
}
491+
}

src/machine/usb/msc/msc.go

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package msc
22

33
import (
4+
"encoding/binary"
45
"machine"
56
"machine/usb"
67
"machine/usb/descriptor"
@@ -72,9 +73,7 @@ func newMSC(dev machine.BlockDevice) *msc {
7273
maxPacketSize := descriptor.EndpointMSCIN.GetMaxPacketSize()
7374
m := &msc{
7475
// Some platforms require reads/writes to be aligned to the full underlying hardware block
75-
blockCache: make([]byte, dev.WriteBlockSize()),
7676
blockSizeUSB: 512,
77-
buf: make([]byte, dev.WriteBlockSize()),
7877
cswBuf: make([]byte, csw.MsgLen),
7978
cbw: &CBW{Data: make([]byte, 31)},
8079
maxPacketSize: uint32(maxPacketSize),
@@ -165,6 +164,7 @@ func (m *msc) sendCSW(status csw.Status) {
165164
}
166165
m.cbw.CSW(status, residue, m.cswBuf)
167166
m.state = mscStateStatusSent
167+
m.queuedBytes = csw.MsgLen
168168
m.sendUSBPacket(m.cswBuf)
169169
}
170170

@@ -297,3 +297,48 @@ func (m *msc) run(b []byte, isEpOut bool) bool {
297297

298298
return ack
299299
}
300+
301+
// RegisterBlockDevice registers a BlockDevice provider with the MSC driver
302+
func (m *msc) RegisterBlockDevice(dev machine.BlockDevice) {
303+
m.dev = dev
304+
305+
bufSize := max(dev.WriteBlockSize(), int64(m.maxPacketSize))
306+
307+
if cap(m.blockCache) != int(bufSize) {
308+
m.blockCache = make([]byte, bufSize)
309+
m.buf = make([]byte, bufSize)
310+
}
311+
312+
m.blockSizeRaw = uint32(m.dev.WriteBlockSize())
313+
m.blockCount = uint32(m.dev.Size()) / m.blockSizeUSB
314+
// Read/write/erase operations must be aligned to the underlying hardware blocks. In order to align
315+
// them we assume the provided block device is aligned to the end of the underlying hardware block
316+
// device and offset all reads/writes by the remaining bytes that don't make up a full block.
317+
m.blockOffset = uint32(m.dev.Size()) % m.blockSizeUSB
318+
319+
// Set VPD UNMAP fields
320+
for i := range vpdPages {
321+
if vpdPages[i].PageCode == 0xb0 {
322+
// 0xb0 - 5.4.5 Block Limits VPD page (B0h)
323+
if len(vpdPages[i].Data) >= 28 {
324+
// Set the OPTIMAL UNMAP GRANULARITY (write blocks per erase block)
325+
granularity := uint32(dev.EraseBlockSize()) / m.blockSizeUSB
326+
binary.BigEndian.PutUint32(vpdPages[i].Data[24:28], granularity)
327+
}
328+
if len(vpdPages[i].Data) >= 32 {
329+
// Set the UNMAP GRANULARITY ALIGNMENT (first sector of first full erase block)
330+
// The unmap granularity alignment is used to calculate an optimal unmap request starting LBA as follows:
331+
// optimal unmap request starting LBA = (n * OPTIMAL UNMAP GRANULARITY) + UNMAP GRANULARITY ALIGNMENT
332+
// where n is zero or any positive integer value
333+
// https://www.seagate.com/files/staticfiles/support/docs/manual/Interface%20manuals/100293068j.pdf
334+
335+
// We assume the block device is aligned to the end of the underlying block device
336+
blockOffset := uint32(dev.EraseBlockSize()) % m.blockSizeUSB
337+
// Set the UGAVALID bit to indicate that the UNMAP GRANULARITY ALIGNMENT is valid
338+
blockOffset |= 0x80000000
339+
binary.BigEndian.PutUint32(vpdPages[i].Data[28:32], blockOffset)
340+
}
341+
break
342+
}
343+
}
344+
}

src/machine/usb/msc/disk.go renamed to src/machine/usb/msc/recorder.go

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package msc
22

33
import (
4-
"encoding/binary"
54
"errors"
65
"fmt"
76
"machine"
@@ -12,50 +11,6 @@ var (
1211
errWriteOutOfBounds = errors.New("WriteAt offset out of bounds")
1312
)
1413

15-
// RegisterBlockDevice registers a BlockDevice provider with the MSC driver
16-
func (m *msc) RegisterBlockDevice(dev machine.BlockDevice) {
17-
m.dev = dev
18-
19-
if cap(m.blockCache) != int(dev.WriteBlockSize()) {
20-
m.blockCache = make([]byte, dev.WriteBlockSize())
21-
m.buf = make([]byte, dev.WriteBlockSize())
22-
}
23-
24-
m.blockSizeRaw = uint32(m.dev.WriteBlockSize())
25-
m.blockCount = uint32(m.dev.Size()) / m.blockSizeUSB
26-
// Read/write/erase operations must be aligned to the underlying hardware blocks. In order to align
27-
// them we assume the provided block device is aligned to the end of the underlying hardware block
28-
// device and offset all reads/writes by the remaining bytes that don't make up a full block.
29-
m.blockOffset = uint32(m.dev.Size()) % m.blockSizeUSB
30-
// FIXME: Figure out what to do if the emulated write block size is larger than the erase block size
31-
32-
// Set VPD UNMAP fields
33-
for i := range vpdPages {
34-
if vpdPages[i].PageCode == 0xb0 {
35-
// 0xb0 - 5.4.5 Block Limits VPD page (B0h)
36-
if len(vpdPages[i].Data) >= 28 {
37-
// Set the OPTIMAL UNMAP GRANULARITY (write blocks per erase block)
38-
granularity := uint32(dev.EraseBlockSize()) / m.blockSizeUSB
39-
binary.BigEndian.PutUint32(vpdPages[i].Data[24:28], granularity)
40-
}
41-
if len(vpdPages[i].Data) >= 32 {
42-
// Set the UNMAP GRANULARITY ALIGNMENT (first sector of first full erase block)
43-
// The unmap granularity alignment is used to calculate an optimal unmap request starting LBA as follows:
44-
// optimal unmap request starting LBA = (n * OPTIMAL UNMAP GRANULARITY) + UNMAP GRANULARITY ALIGNMENT
45-
// where n is zero or any positive integer value
46-
// https://www.seagate.com/files/staticfiles/support/docs/manual/Interface%20manuals/100293068j.pdf
47-
48-
// We assume the block device is aligned to the end of the underlying block device
49-
blockOffset := uint32(dev.EraseBlockSize()) % m.blockSizeUSB
50-
// Set the UGAVALID bit to indicate that the UNMAP GRANULARITY ALIGNMENT is valid
51-
blockOffset |= 0x80000000
52-
binary.BigEndian.PutUint32(vpdPages[i].Data[28:32], blockOffset)
53-
}
54-
break
55-
}
56-
}
57-
}
58-
5914
var _ machine.BlockDevice = (*RecorderDisk)(nil)
6015

6116
// RecorderDisk is a block device that records actions taken on it

0 commit comments

Comments
 (0)