Skip to content

usb: add USB mass storage class support #4844

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 14 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,10 @@ endif
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/usb-midi
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pico examples/usb-storage
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pico2 examples/usb-storage
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=nrf52840-s140v6-uf2-generic examples/machinetest
@$(MD5SUM) test.hex
ifneq ($(STM32), 0)
Expand Down
15 changes: 15 additions & 0 deletions src/examples/usb-storage/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package main

import (
"machine"
"machine/usb/msc"
"time"
)

func main() {
msc.Port(machine.Flash)

for {
time.Sleep(2 * time.Second)
}
}
23 changes: 19 additions & 4 deletions src/machine/machine_atsamd21_usb.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,21 @@ const (

usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos = 14
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask = 0x3FFF

NumberOfUSBEndpoints = 8
)

var (
endPoints = []uint32{
usb.CONTROL_ENDPOINT: usb.ENDPOINT_TYPE_CONTROL,
usb.CDC_ENDPOINT_ACM: (usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointIn),
usb.CDC_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_BULK | usb.EndpointOut),
usb.CDC_ENDPOINT_IN: (usb.ENDPOINT_TYPE_BULK | usb.EndpointIn),
usb.HID_ENDPOINT_IN: (usb.ENDPOINT_TYPE_DISABLE), // Interrupt In
usb.HID_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_DISABLE), // Interrupt Out
usb.MIDI_ENDPOINT_IN: (usb.ENDPOINT_TYPE_DISABLE), // Bulk In
usb.MIDI_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_DISABLE), // Bulk Out
}
)

// Configure the USB peripheral. The config is here for compatibility with the UART interface.
Expand Down Expand Up @@ -179,10 +194,9 @@ func handleUSBIRQ(intr interrupt.Interrupt) {
setEPINTFLAG(i, epFlags)
if (epFlags & sam.USB_DEVICE_EPINTFLAG_TRCPT0) > 0 {
buf := handleEndpointRx(i)
if usbRxHandler[i] != nil {
usbRxHandler[i](buf)
if usbRxHandler[i] == nil || usbRxHandler[i](buf) {
AckUsbOutTransfer(i)
}
handleEndpointRxComplete(i)
} else if (epFlags & sam.USB_DEVICE_EPINTFLAG_TRCPT1) > 0 {
if usbTxHandler[i] != nil {
usbTxHandler[i]()
Expand Down Expand Up @@ -402,7 +416,8 @@ func handleEndpointRx(ep uint32) []byte {
return udd_ep_out_cache_buffer[ep][:count]
}

func handleEndpointRxComplete(ep uint32) {
// AckUsbOutTransfer is called to acknowledge the completion of a USB OUT transfer.
func AckUsbOutTransfer(ep uint32) {
// set byte count to zero
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)

Expand Down
23 changes: 19 additions & 4 deletions src/machine/machine_atsamd51_usb.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,21 @@ const (

usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos = 14
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask = 0x3FFF

NumberOfUSBEndpoints = 8
)

var (
endPoints = []uint32{
usb.CONTROL_ENDPOINT: usb.ENDPOINT_TYPE_CONTROL,
usb.CDC_ENDPOINT_ACM: (usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointIn),
usb.CDC_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_BULK | usb.EndpointOut),
usb.CDC_ENDPOINT_IN: (usb.ENDPOINT_TYPE_BULK | usb.EndpointIn),
usb.HID_ENDPOINT_IN: (usb.ENDPOINT_TYPE_DISABLE), // Interrupt In
usb.HID_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_DISABLE), // Interrupt Out
usb.MIDI_ENDPOINT_IN: (usb.ENDPOINT_TYPE_DISABLE), // Bulk In
usb.MIDI_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_DISABLE), // Bulk Out
}
)

// Configure the USB peripheral. The config is here for compatibility with the UART interface.
Expand Down Expand Up @@ -182,10 +197,9 @@ func handleUSBIRQ(intr interrupt.Interrupt) {
setEPINTFLAG(i, epFlags)
if (epFlags & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT0) > 0 {
buf := handleEndpointRx(i)
if usbRxHandler[i] != nil {
usbRxHandler[i](buf)
if usbRxHandler[i] == nil || usbRxHandler[i](buf) {
AckUsbOutTransfer(i)
}
handleEndpointRxComplete(i)
} else if (epFlags & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) > 0 {
if usbTxHandler[i] != nil {
usbTxHandler[i]()
Expand Down Expand Up @@ -405,7 +419,8 @@ func handleEndpointRx(ep uint32) []byte {
return udd_ep_out_cache_buffer[ep][:count]
}

func handleEndpointRxComplete(ep uint32) {
// AckUsbOutTransfer is called to acknowledge the completion of a USB OUT transfer.
func AckUsbOutTransfer(ep uint32) {
// set byte count to zero
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)

Expand Down
21 changes: 17 additions & 4 deletions src/machine/machine_nrf52840_usb.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"unsafe"
)

const NumberOfUSBEndpoints = 8

var (
sendOnEP0DATADONE struct {
ptr *byte
Expand All @@ -20,6 +22,17 @@ var (
epinen uint32
epouten uint32
easyDMABusy volatile.Register8

endPoints = []uint32{
usb.CONTROL_ENDPOINT: usb.ENDPOINT_TYPE_CONTROL,
usb.CDC_ENDPOINT_ACM: (usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointIn),
usb.CDC_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_BULK | usb.EndpointOut),
usb.CDC_ENDPOINT_IN: (usb.ENDPOINT_TYPE_BULK | usb.EndpointIn),
usb.HID_ENDPOINT_IN: (usb.ENDPOINT_TYPE_DISABLE), // Interrupt In
usb.HID_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_DISABLE), // Interrupt Out
usb.MIDI_ENDPOINT_IN: (usb.ENDPOINT_TYPE_DISABLE), // Bulk In
usb.MIDI_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_DISABLE), // Bulk Out
}
)

// enterCriticalSection is used to protect access to easyDMA - only one thing
Expand Down Expand Up @@ -193,10 +206,9 @@ func handleUSBIRQ(interrupt.Interrupt) {
if nrf.USBD.EVENTS_ENDEPOUT[i].Get() > 0 {
nrf.USBD.EVENTS_ENDEPOUT[i].Set(0)
buf := handleEndpointRx(uint32(i))
if usbRxHandler[i] != nil {
usbRxHandler[i](buf)
if usbRxHandler[i] == nil || usbRxHandler[i](buf) {
AckUsbOutTransfer(uint32(i))
}
handleEndpointRxComplete(uint32(i))
exitCriticalSection()
}
}
Expand Down Expand Up @@ -291,7 +303,8 @@ func handleEndpointRx(ep uint32) []byte {
return udd_ep_out_cache_buffer[ep][:count]
}

func handleEndpointRxComplete(ep uint32) {
// AckUsbOutTransfer is called to acknowledge the completion of a USB OUT transfer.
func AckUsbOutTransfer(ep uint32) {
// set ready for next data
nrf.USBD.SIZE.EPOUT[ep].Set(0)
}
Expand Down
5 changes: 2 additions & 3 deletions src/machine/machine_rp2040_usb.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,9 @@ func handleUSBIRQ(intr interrupt.Interrupt) {
for i := 0; i < 16; i++ {
if s2&(1<<(i*2+1)) > 0 {
buf := handleEndpointRx(uint32(i))
if usbRxHandler[i] != nil {
usbRxHandler[i](buf)
if usbRxHandler[i] == nil || usbRxHandler[i](buf) {
AckUsbOutTransfer(uint32(i))
}
handleEndpointRxComplete(uint32(i))
}
}

Expand Down
5 changes: 2 additions & 3 deletions src/machine/machine_rp2350_usb.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,9 @@ func handleUSBIRQ(intr interrupt.Interrupt) {
for i := 0; i < 16; i++ {
if s2&(1<<(i*2+1)) > 0 {
buf := handleEndpointRx(uint32(i))
if usbRxHandler[i] != nil {
usbRxHandler[i](buf)
if usbRxHandler[i] == nil || usbRxHandler[i](buf) {
AckUsbOutTransfer(uint32(i))
}
handleEndpointRxComplete(uint32(i))
}
}

Expand Down
27 changes: 22 additions & 5 deletions src/machine/machine_rp2_usb.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,25 @@ import (
"unsafe"
)

const NumberOfUSBEndpoints = 10

var (
sendOnEP0DATADONE struct {
offset int
data []byte
pid uint32
}

endPoints = []uint32{
usb.CONTROL_ENDPOINT: usb.ENDPOINT_TYPE_CONTROL,
usb.CDC_ENDPOINT_ACM: (usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointIn),
usb.CDC_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_BULK | usb.EndpointOut),
usb.CDC_ENDPOINT_IN: (usb.ENDPOINT_TYPE_BULK | usb.EndpointIn),
usb.HID_ENDPOINT_IN: (usb.ENDPOINT_TYPE_DISABLE), // Interrupt In
usb.HID_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_DISABLE), // Interrupt Out
usb.MIDI_ENDPOINT_IN: (usb.ENDPOINT_TYPE_DISABLE), // Bulk In
usb.MIDI_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_DISABLE), // Bulk Out
}
)

func initEndpoint(ep, config uint32) {
Expand Down Expand Up @@ -115,7 +128,9 @@ func handleEndpointRx(ep uint32) []byte {
return _usbDPSRAM.EPxBuffer[ep].Buffer0[:sz]
}

func handleEndpointRxComplete(ep uint32) {
// AckUsbOutTransfer is called to acknowledge the completion of a USB OUT transfer.
func AckUsbOutTransfer(ep uint32) {
ep = ep & 0x7F
setEPDataPID(ep, !epXdata0[ep])
}

Expand Down Expand Up @@ -152,23 +167,25 @@ func sendViaEPIn(ep uint32, data []byte, count int) {

// Set ENDPOINT_HALT/stall status on a USB IN endpoint.
func (dev *USBDevice) SetStallEPIn(ep uint32) {
ep = ep & 0x7F
// Prepare buffer control register value
if ep == 0 {
armEPZeroStall()
}
val := uint32(usbBuf0CtrlFull)
_usbDPSRAM.EPxBufferControl[ep&0x7F].In.Set(val)
_usbDPSRAM.EPxBufferControl[ep].In.Set(val)
val |= uint32(usbBuf0CtrlStall)
_usbDPSRAM.EPxBufferControl[ep&0x7F].In.Set(val)
_usbDPSRAM.EPxBufferControl[ep].In.Set(val)
}

// Set ENDPOINT_HALT/stall status on a USB OUT endpoint.
func (dev *USBDevice) SetStallEPOut(ep uint32) {
ep = ep & 0x7F
if ep == 0 {
panic("SetStallEPOut: EP0 OUT not valid")
}
val := uint32(usbBuf0CtrlStall)
_usbDPSRAM.EPxBufferControl[ep&0x7F].Out.Set(val)
_usbDPSRAM.EPxBufferControl[ep].Out.Set(val)
}

// Clear the ENDPOINT_HALT/stall on a USB IN endpoint.
Expand All @@ -178,7 +195,7 @@ func (dev *USBDevice) ClearStallEPIn(ep uint32) {
_usbDPSRAM.EPxBufferControl[ep].In.ClearBits(val)
if epXPIDReset[ep] {
// Reset the PID to DATA0
setEPDataPID(ep&0x7F, false)
setEPDataPID(ep, false)
}
}

Expand Down
34 changes: 14 additions & 20 deletions src/machine/usb.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ var (
)

var (
usbEndpointDescriptors [usb.NumberOfEndpoints]descriptor.Device
usbEndpointDescriptors [NumberOfUSBEndpoints]descriptor.Device

isEndpointHalt = false
isRemoteWakeUpEnabled = false
Expand All @@ -107,10 +107,10 @@ var (
var udd_ep_control_cache_buffer [256]uint8

//go:align 4
var udd_ep_in_cache_buffer [usb.NumberOfEndpoints][64]uint8
var udd_ep_in_cache_buffer [NumberOfUSBEndpoints][64]uint8

//go:align 4
var udd_ep_out_cache_buffer [usb.NumberOfEndpoints][64]uint8
var udd_ep_out_cache_buffer [NumberOfUSBEndpoints][64]uint8

// usb_trans_buffer max size is 255 since that is max size
// for a descriptor (bLength is 1 byte), and the biggest use
Expand All @@ -120,21 +120,10 @@ var udd_ep_out_cache_buffer [usb.NumberOfEndpoints][64]uint8
var usb_trans_buffer [255]uint8

var (
usbTxHandler [usb.NumberOfEndpoints]func()
usbRxHandler [usb.NumberOfEndpoints]func([]byte)
usbTxHandler [NumberOfUSBEndpoints]func()
usbRxHandler [NumberOfUSBEndpoints]func([]byte) bool
usbSetupHandler [usb.NumberOfInterfaces]func(usb.Setup) bool
usbStallHandler [usb.NumberOfEndpoints]func(usb.Setup) bool

endPoints = []uint32{
usb.CONTROL_ENDPOINT: usb.ENDPOINT_TYPE_CONTROL,
usb.CDC_ENDPOINT_ACM: (usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointIn),
usb.CDC_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_BULK | usb.EndpointOut),
usb.CDC_ENDPOINT_IN: (usb.ENDPOINT_TYPE_BULK | usb.EndpointIn),
usb.HID_ENDPOINT_IN: (usb.ENDPOINT_TYPE_DISABLE), // Interrupt In
usb.HID_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_DISABLE), // Interrupt Out
usb.MIDI_ENDPOINT_IN: (usb.ENDPOINT_TYPE_DISABLE), // Bulk In
usb.MIDI_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_DISABLE), // Bulk Out
}
usbStallHandler [NumberOfUSBEndpoints]func(usb.Setup) bool
)

// sendDescriptor creates and sends the various USB descriptor types that
Expand Down Expand Up @@ -213,7 +202,7 @@ func handleStandardSetup(setup usb.Setup) bool {
if setup.WValueL == 1 { // DEVICEREMOTEWAKEUP
isRemoteWakeUpEnabled = false
} else if setup.WValueL == 0 { // ENDPOINTHALT
if idx := setup.WIndex & 0x7F; idx < usb.NumberOfEndpoints && usbStallHandler[idx] != nil {
if idx := setup.WIndex & 0x7F; idx < NumberOfUSBEndpoints && usbStallHandler[idx] != nil {
// Host has requested to clear an endpoint stall. If the request is addressed to
// an endpoint with a configured StallHandler, forward the message on.
// The 0x7F mask is used to clear the direction bit from the endpoint number
Expand All @@ -228,7 +217,7 @@ func handleStandardSetup(setup usb.Setup) bool {
if setup.WValueL == 1 { // DEVICEREMOTEWAKEUP
isRemoteWakeUpEnabled = true
} else if setup.WValueL == 0 { // ENDPOINTHALT
if idx := setup.WIndex & 0x7F; idx < usb.NumberOfEndpoints && usbStallHandler[idx] != nil {
if idx := setup.WIndex & 0x7F; idx < NumberOfUSBEndpoints && usbStallHandler[idx] != nil {
// Host has requested to stall an endpoint. If the request is addressed to
// an endpoint with a configured StallHandler, forward the message on.
// The 0x7F mask is used to clear the direction bit from the endpoint number
Expand Down Expand Up @@ -330,7 +319,12 @@ func ConfigureUSBEndpoint(desc descriptor.Descriptor, epSettings []usb.EndpointC
} else {
endPoints[ep.Index] = uint32(ep.Type | usb.EndpointOut)
if ep.RxHandler != nil {
usbRxHandler[ep.Index] = ep.RxHandler
usbRxHandler[ep.Index] = func(b []byte) bool {
ep.RxHandler(b)
return true
}
} else if ep.DelayRxHandler != nil {
usbRxHandler[ep.Index] = ep.DelayRxHandler
}
}
if ep.StallHandler != nil {
Expand Down
13 changes: 7 additions & 6 deletions src/machine/usb/config.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package usb

type EndpointConfig struct {
Index uint8
IsIn bool
TxHandler func()
RxHandler func([]byte)
StallHandler func(Setup) bool
Type uint8
Index uint8
IsIn bool
TxHandler func()
RxHandler func([]byte)
DelayRxHandler func([]byte) bool
StallHandler func(Setup) bool
Type uint8
}

type SetupConfig struct {
Expand Down
Loading