Skip to content

Commit b771bea

Browse files
committed
usb: add USB mass storage class support
1 parent dcf609d commit b771bea

File tree

12 files changed

+1166
-6
lines changed

12 files changed

+1166
-6
lines changed

src/machine/machine_rp2350_usb.go

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func handleUSBIRQ(intr interrupt.Interrupt) {
8080

8181
if !ok {
8282
// Stall endpoint?
83-
sendStallViaEPIn(0)
83+
SetStallEPIn(0)
8484
}
8585

8686
}
@@ -248,7 +248,12 @@ func handleEndpointRx(ep uint32) []byte {
248248
}
249249

250250
func handleEndpointRxComplete(ep uint32) {
251-
epXdata0[ep] = !epXdata0[ep]
251+
SetEPDataPID(ep, !epXdata0[ep])
252+
}
253+
254+
// Set the USB endpoint Packet ID to DATA0 or DATA1
255+
func SetEPDataPID(ep uint32, dataOne bool) {
256+
epXdata0[ep&0x7F] = dataOne
252257
if epXdata0[ep] || ep == 0 {
253258
_usbDPSRAM.EPxBufferControl[ep].Out.SetBits(usbBuf0CtrlData1Pid)
254259
}
@@ -277,7 +282,8 @@ func sendViaEPIn(ep uint32, data []byte, count int) {
277282
_usbDPSRAM.EPxBufferControl[ep&0x7F].In.Set(val)
278283
}
279284

280-
func sendStallViaEPIn(ep uint32) {
285+
// Set the endpoint stall bit on a USB IN endpoint
286+
func SetStallEPIn(ep uint32) {
281287
// Prepare buffer control register value
282288
if ep == 0 {
283289
rp.USB.EP_STALL_ARM.Set(rp.USB_EP_STALL_ARM_EP0_IN)
@@ -288,6 +294,27 @@ func sendStallViaEPIn(ep uint32) {
288294
_usbDPSRAM.EPxBufferControl[ep&0x7F].In.Set(val)
289295
}
290296

297+
// Set the endpoint stall bit on a USB OUT endpoint
298+
func SetStallEPOut(ep uint32) {
299+
if ep == 0 {
300+
panic("SetStallEPOut: EP0 OUT not valid")
301+
}
302+
val := uint32(usbBuf0CtrlStall)
303+
_usbDPSRAM.EPxBufferControl[ep&0x7F].Out.Set(val)
304+
}
305+
306+
// Clear the endpoint stall bit on a USB IN endpoint
307+
func ClearStallEPIn(ep uint32) {
308+
val := uint32(usbBuf0CtrlStall)
309+
_usbDPSRAM.EPxBufferControl[ep&0x7F].In.ClearBits(val)
310+
}
311+
312+
// Clear the endpoint stall bit on a USB OUT endpoint
313+
func ClearStallEPOut(ep uint32) {
314+
val := uint32(usbBuf0CtrlStall)
315+
_usbDPSRAM.EPxBufferControl[ep&0x7F].Out.ClearBits(val)
316+
}
317+
291318
type usbDPSRAM struct {
292319
// Note that EPxControl[0] is not EP0Control but 8-byte setup data.
293320
EPxControl [16]usbEndpointControlRegister

src/machine/usb.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ var (
133133
usb.HID_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_DISABLE), // Interrupt Out
134134
usb.MIDI_ENDPOINT_IN: (usb.ENDPOINT_TYPE_DISABLE), // Bulk In
135135
usb.MIDI_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_DISABLE), // Bulk Out
136+
usb.MSC_ENDPOINT_IN: (usb.ENDPOINT_TYPE_DISABLE), // Bulk In
137+
usb.MSC_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_DISABLE), // Bulk Out
136138
}
137139
)
138140

src/machine/usb/descriptor/endpoint.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@ import (
44
"internal/binary"
55
)
66

7+
/* Endpoint Descriptor
8+
USB 2.0 Specification: 9.6.6 Endpoint
9+
*/
10+
11+
const (
12+
TransferTypeControl uint8 = iota
13+
TransferTypeIsochronous
14+
TransferTypeBulk
15+
TransferTypeInterrupt
16+
)
17+
718
var endpointEP1IN = [endpointTypeLen]byte{
819
endpointTypeLen,
920
TypeEndpoint,
@@ -74,6 +85,36 @@ var EndpointEP5OUT = EndpointType{
7485
data: endpointEP5OUT[:],
7586
}
7687

88+
// Mass Storage Class bulk in endpoint
89+
var endpointEP8IN = [endpointTypeLen]byte{
90+
endpointTypeLen,
91+
TypeEndpoint,
92+
0x88, // EndpointAddress
93+
TransferTypeBulk, // Attributes
94+
0x40, // MaxPacketSizeL (64 bytes)
95+
0x00, // MaxPacketSizeH
96+
0x00, // Interval
97+
}
98+
99+
var EndpointEP8IN = EndpointType{
100+
data: endpointEP8IN[:],
101+
}
102+
103+
// Mass Storage Class bulk out endpoint
104+
var endpointEP9OUT = [endpointTypeLen]byte{
105+
endpointTypeLen,
106+
TypeEndpoint,
107+
0x09, // EndpointAddress
108+
TransferTypeBulk, // Attributes
109+
0x40, // MaxPacketSizeL (64 bytes)
110+
0x00, // MaxPacketSizeH
111+
0x00, // Interval
112+
}
113+
114+
var EndpointEP9OUT = EndpointType{
115+
data: endpointEP9OUT[:],
116+
}
117+
77118
const (
78119
endpointTypeLen = 7
79120
)

src/machine/usb/descriptor/msc.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package descriptor
2+
3+
const (
4+
interfaceClassMSC = 0x08
5+
mscSubclassSCSI = 0x06
6+
mscProtocolBOT = 0x50
7+
)
8+
9+
var interfaceAssociationMSC = [interfaceAssociationTypeLen]byte{
10+
interfaceAssociationTypeLen,
11+
TypeInterfaceAssociation,
12+
0x02, // FirstInterface
13+
0x01, // InterfaceCount
14+
interfaceClassMSC, // FunctionClass
15+
mscSubclassSCSI, // FunctionSubClass
16+
mscProtocolBOT, // FunctionProtocol
17+
0x00, // Function
18+
}
19+
20+
var InterfaceAssociationMSC = InterfaceAssociationType{
21+
data: interfaceAssociationMSC[:],
22+
}
23+
24+
var interfaceMSC = [interfaceTypeLen]byte{
25+
interfaceTypeLen, // Length
26+
TypeInterface, // DescriptorType
27+
0x02, // InterfaceNumber
28+
0x00, // AlternateSetting
29+
0x02, // NumEndpoints
30+
interfaceClassMSC, // InterfaceClass (Mass Storage)
31+
mscSubclassSCSI, // InterfaceSubClass (SCSI Transparent)
32+
mscProtocolBOT, // InterfaceProtocol (Bulk-Only Transport)
33+
0x00, // Interface
34+
}
35+
36+
var InterfaceMSC = InterfaceType{
37+
data: interfaceMSC[:],
38+
}
39+
40+
var configurationMSC = [configurationTypeLen]byte{
41+
configurationTypeLen,
42+
TypeConfiguration,
43+
0x6a, 0x00, // wTotalLength
44+
0x03, // number of interfaces (bNumInterfaces)
45+
0x01, // configuration value (bConfigurationValue)
46+
0x00, // index to string description (iConfiguration)
47+
0xa0, // attributes (bmAttributes)
48+
0x32, // maxpower (100 mA) (bMaxPower)
49+
}
50+
51+
var ConfigurationMSC = ConfigurationType{
52+
data: configurationMSC[:],
53+
}
54+
55+
// Mass Storage Class
56+
var MSC = Descriptor{
57+
Device: DeviceCDC.Bytes(),
58+
Configuration: Append([][]byte{
59+
ConfigurationMSC.Bytes(),
60+
InterfaceAssociationCDC.Bytes(),
61+
InterfaceCDCControl.Bytes(),
62+
ClassSpecificCDCHeader.Bytes(),
63+
ClassSpecificCDCACM.Bytes(),
64+
ClassSpecificCDCUnion.Bytes(),
65+
ClassSpecificCDCCallManagement.Bytes(),
66+
EndpointEP1IN.Bytes(),
67+
InterfaceCDCData.Bytes(),
68+
EndpointEP2OUT.Bytes(),
69+
EndpointEP3IN.Bytes(),
70+
InterfaceAssociationMSC.Bytes(),
71+
InterfaceMSC.Bytes(),
72+
EndpointEP8IN.Bytes(),
73+
EndpointEP9OUT.Bytes(),
74+
}),
75+
}

src/machine/usb/msc/cbw.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package msc
2+
3+
import (
4+
"machine/usb/msc/csw"
5+
"machine/usb/msc/scsi"
6+
)
7+
8+
const (
9+
cbwMsgLen = 31 // Command Block Wrapper (CBW) message length
10+
)
11+
12+
type CBW struct {
13+
Data []byte
14+
}
15+
16+
func (c *CBW) length() int {
17+
return len(c.Data)
18+
}
19+
20+
func (c *CBW) validLength() bool {
21+
return len(c.Data) == cbwMsgLen
22+
}
23+
24+
func (c *CBW) validSignature() bool {
25+
return c.Data[0] == 0x55 && c.Data[1] == 0x53 && c.Data[2] == 0x42 && c.Data[3] == 0x43
26+
}
27+
28+
func (c *CBW) scsiCmd() scsi.Cmd {
29+
return scsi.Cmd{Data: c.Data[15:]}
30+
}
31+
32+
func (c *CBW) transferLength() uint32 {
33+
return uint32(c.Data[8]) | uint32(c.Data[9])<<8 | uint32(c.Data[10])<<16 | uint32(c.Data[11])<<24
34+
}
35+
36+
// isIn returns true if the command direction is from the device to the host.
37+
func (c *CBW) isIn() bool {
38+
return c.Data[12]>>7 != 0
39+
}
40+
41+
// isOut returns true if the command direction is from the host to the device.
42+
func (c *CBW) isOut() bool {
43+
return !c.isIn()
44+
}
45+
46+
func (c *CBW) CSW(status csw.Status, residue uint32, b []byte) {
47+
// Signature: 53425355h (little endian)
48+
b[3] = 0x53
49+
b[2] = 0x42
50+
b[1] = 0x53
51+
b[0] = 0x55
52+
// Tag:
53+
b[4] = c.Data[4]
54+
b[5] = c.Data[5]
55+
b[6] = c.Data[6]
56+
b[7] = c.Data[7]
57+
// Data Residue: (untransferred bytes)
58+
b[8] = byte(residue)
59+
b[9] = byte(residue >> 8)
60+
b[10] = byte(residue >> 16)
61+
b[11] = byte(residue >> 24)
62+
// Status:
63+
b[12] = byte(status)
64+
}

src/machine/usb/msc/csw/csw.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package csw
2+
3+
type Status uint8
4+
5+
const (
6+
StatusPassed Status = iota
7+
StatusFailed
8+
StatusPhaseError
9+
)
10+
11+
const (
12+
MsgLen = 13
13+
)

src/machine/usb/msc/disk.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package msc
2+
3+
type Disk interface {
4+
Ready() bool
5+
ReadOnly() bool
6+
BlockCount() uint32
7+
BlockSize() uint32
8+
Read(offset uint32, buffer []byte) (uint32, error)
9+
Write(offset uint32, buffer []byte) (uint32, error)
10+
}
11+
12+
// DefaultDisk is a placeholder disk implementation
13+
type DefaultDisk struct {
14+
}
15+
16+
// NewDefaultDisk creates a new DefaultDisk instance
17+
func NewDefaultDisk() *DefaultDisk {
18+
return &DefaultDisk{}
19+
}
20+
21+
func (d *DefaultDisk) Ready() bool {
22+
return true
23+
}
24+
25+
func (d *DefaultDisk) ReadOnly() bool {
26+
return false
27+
}
28+
29+
func (d *DefaultDisk) BlockCount() uint32 {
30+
return 4096 // 2MB
31+
}
32+
33+
func (d *DefaultDisk) BlockSize() uint32 {
34+
return 512 // 512 bytes
35+
}
36+
37+
func (d *DefaultDisk) Read(offset uint32, buffer []byte) (uint32, error) {
38+
n := uint8(offset)
39+
for i := range buffer {
40+
n++
41+
buffer[i] = n
42+
}
43+
return uint32(len(buffer)), nil
44+
}
45+
46+
func (d *DefaultDisk) Write(offset uint32, buffer []byte) (uint32, error) {
47+
return uint32(len(buffer)), nil
48+
}
49+
50+
// RegisterDisk registers a disk provider with the MSC driver
51+
func (m *msc) RegisterDisk(disk Disk) {
52+
m.disk = disk
53+
}

0 commit comments

Comments
 (0)