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 4 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
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) {
AckEndpointRxMessage(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) {
AckEndpointRxMessage(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) {
// AckEndpointRxMessage is called to acknowledge the completion of a delayed USB OUT transfer.
func AckEndpointRxMessage(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(),
}),
}
62 changes: 62 additions & 0 deletions src/machine/usb/msc/cbw.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package msc

import (
"encoding/binary"
"machine/usb/msc/csw"
"machine/usb/msc/scsi"
)

const (
cbwMsgLen = 31 // Command Block Wrapper (CBW) message length
Signature = 0x43425355 // "USBC" in little endian
)

type CBW struct {
HasCmd bool
Data []byte
}

func (c *CBW) Tag() uint32 {
return binary.LittleEndian.Uint32(c.Data[4:8])
}

func (c *CBW) length() int {
return len(c.Data)
}

func (c *CBW) validLength() bool {
return len(c.Data) == cbwMsgLen
}

func (c *CBW) validSignature() bool {
return binary.LittleEndian.Uint32(c.Data[:4]) == Signature
}

func (c *CBW) SCSICmd() scsi.Cmd {
return scsi.Cmd{Data: c.Data[15:]}
}

func (c *CBW) transferLength() uint32 {
return binary.LittleEndian.Uint32(c.Data[8:12])
}

// isIn returns true if the command direction is from the device to the host.
func (c *CBW) isIn() bool {
return c.Data[12]>>7 != 0
}

// isOut returns true if the command direction is from the host to the device.
func (c *CBW) isOut() bool {
return !c.isIn()
}

func (c *CBW) CSW(status csw.Status, residue uint32, b []byte) {
// Signature: "USBS" 53425355h (little endian)
binary.LittleEndian.PutUint32(b[:4], csw.Signature)
// Tag: (same as CBW)
copy(b[4:8], c.Data[4:8])
// Data Residue: (untransferred bytes)
binary.LittleEndian.PutUint32(b[8:12], residue)
// Status:
b[12] = byte(status)
}
14 changes: 14 additions & 0 deletions src/machine/usb/msc/csw/csw.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package csw

type Status uint8

const (
StatusPassed Status = iota
StatusFailed
StatusPhaseError
)

const (
MsgLen = 13
Signature = 0x53425355 // "USBS" in little endian
)
Loading
Loading