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 11 commits into
base: dev
Choose a base branch
from
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
2 changes: 1 addition & 1 deletion builder/sizes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func TestBinarySize(t *testing.T) {
// microcontrollers
{"hifive1b", "examples/echo", 4560, 280, 0, 2268},
{"microbit", "examples/serial", 2924, 388, 8, 2272},
{"wioterminal", "examples/pininterrupt", 7383, 1489, 116, 6912},
{"wioterminal", "examples/pininterrupt", 7550, 1494, 116, 7232},

// TODO: also check wasm. Right now this is difficult, because
// wasm binaries are run through wasm-opt and therefore the
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)
}
}
8 changes: 4 additions & 4 deletions src/machine/machine_atsamd21_usb.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,10 +179,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 +401,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
8 changes: 4 additions & 4 deletions src/machine/machine_atsamd51_usb.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,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 +404,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
8 changes: 4 additions & 4 deletions src/machine/machine_nrf52840_usb.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,10 +193,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 +290,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
14 changes: 9 additions & 5 deletions src/machine/machine_rp2_usb.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,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 +154,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 +182,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
11 changes: 9 additions & 2 deletions src/machine/usb.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ var usb_trans_buffer [255]uint8

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

Expand All @@ -134,6 +134,8 @@ var (
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
usb.MSC_ENDPOINT_IN: (usb.ENDPOINT_TYPE_DISABLE), // Bulk In
usb.MSC_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_DISABLE), // Bulk Out
}
)

Expand Down Expand Up @@ -330,7 +332,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
45 changes: 45 additions & 0 deletions src/machine/usb/descriptor/endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ import (
"internal/binary"
)

/* Endpoint Descriptor
USB 2.0 Specification: 9.6.6 Endpoint
*/

const (
TransferTypeControl uint8 = iota
TransferTypeIsochronous
TransferTypeBulk
TransferTypeInterrupt
)

var endpointEP1IN = [endpointTypeLen]byte{
endpointTypeLen,
TypeEndpoint,
Expand Down Expand Up @@ -74,6 +85,36 @@ var EndpointEP5OUT = EndpointType{
data: endpointEP5OUT[:],
}

// Mass Storage Class bulk in endpoint
var endpointEP8IN = [endpointTypeLen]byte{
endpointTypeLen,
TypeEndpoint,
0x88, // EndpointAddress
TransferTypeBulk, // Attributes
0x40, // MaxPacketSizeL (64 bytes)
0x00, // MaxPacketSizeH
0x00, // Interval
}

var EndpointEP8IN = EndpointType{
data: endpointEP8IN[:],
}

// Mass Storage Class bulk out endpoint
var endpointEP9OUT = [endpointTypeLen]byte{
endpointTypeLen,
TypeEndpoint,
0x09, // EndpointAddress
TransferTypeBulk, // Attributes
0x40, // MaxPacketSizeL (64 bytes)
0x00, // MaxPacketSizeH
0x00, // Interval
}

var EndpointEP9OUT = EndpointType{
data: endpointEP9OUT[:],
}

const (
endpointTypeLen = 7
)
Expand Down Expand Up @@ -109,3 +150,7 @@ func (d EndpointType) MaxPacketSize(v uint16) {
func (d EndpointType) Interval(v uint8) {
d.data[6] = byte(v)
}

func (d EndpointType) GetMaxPacketSize() uint16 {
return binary.LittleEndian.Uint16(d.data[4:6])
}
75 changes: 75 additions & 0 deletions src/machine/usb/descriptor/msc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package descriptor

const (
interfaceClassMSC = 0x08
mscSubclassSCSI = 0x06
mscProtocolBOT = 0x50
)

var interfaceAssociationMSC = [interfaceAssociationTypeLen]byte{
interfaceAssociationTypeLen,
TypeInterfaceAssociation,
0x02, // FirstInterface
0x01, // InterfaceCount
interfaceClassMSC, // FunctionClass
mscSubclassSCSI, // FunctionSubClass
mscProtocolBOT, // FunctionProtocol
0x00, // Function
}

var InterfaceAssociationMSC = InterfaceAssociationType{
data: interfaceAssociationMSC[:],
}

var interfaceMSC = [interfaceTypeLen]byte{
interfaceTypeLen, // Length
TypeInterface, // DescriptorType
0x02, // InterfaceNumber
0x00, // AlternateSetting
0x02, // NumEndpoints
interfaceClassMSC, // InterfaceClass (Mass Storage)
mscSubclassSCSI, // InterfaceSubClass (SCSI Transparent)
mscProtocolBOT, // InterfaceProtocol (Bulk-Only Transport)
0x00, // Interface
}

var InterfaceMSC = InterfaceType{
data: interfaceMSC[:],
}

var configurationMSC = [configurationTypeLen]byte{
configurationTypeLen,
TypeConfiguration,
0x6a, 0x00, // wTotalLength
0x03, // number of interfaces (bNumInterfaces)
0x01, // configuration value (bConfigurationValue)
0x00, // index to string description (iConfiguration)
0xa0, // attributes (bmAttributes)
0x32, // maxpower (100 mA) (bMaxPower)
}

var ConfigurationMSC = ConfigurationType{
data: configurationMSC[:],
}

// Mass Storage Class
var MSC = Descriptor{
Device: DeviceCDC.Bytes(),
Configuration: Append([][]byte{
ConfigurationMSC.Bytes(),
InterfaceAssociationCDC.Bytes(),
InterfaceCDCControl.Bytes(),
ClassSpecificCDCHeader.Bytes(),
ClassSpecificCDCACM.Bytes(),
ClassSpecificCDCUnion.Bytes(),
ClassSpecificCDCCallManagement.Bytes(),
EndpointEP1IN.Bytes(),
InterfaceCDCData.Bytes(),
EndpointEP2OUT.Bytes(),
EndpointEP3IN.Bytes(),
InterfaceAssociationMSC.Bytes(),
InterfaceMSC.Bytes(),
EndpointEP8IN.Bytes(),
EndpointEP9OUT.Bytes(),
}),
}
Loading