Skip to content

Commit ae83f51

Browse files
committed
usb: add USB mass storage class support
1 parent 1c53ff1 commit ae83f51

File tree

15 files changed

+1714
-3
lines changed

15 files changed

+1714
-3
lines changed

GNUmakefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,10 @@ endif
792792
@$(MD5SUM) test.hex
793793
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/usb-midi
794794
@$(MD5SUM) test.hex
795+
$(TINYGO) build -size short -o test.hex -target=pico examples/usb-storage
796+
@$(MD5SUM) test.hex
797+
$(TINYGO) build -size short -o test.hex -target=pico2 examples/usb-storage
798+
@$(MD5SUM) test.hex
795799
$(TINYGO) build -size short -o test.hex -target=nrf52840-s140v6-uf2-generic examples/machinetest
796800
@$(MD5SUM) test.hex
797801
ifneq ($(STM32), 0)

src/examples/usb-storage/main.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package main
2+
3+
import (
4+
"machine"
5+
"machine/usb/msc"
6+
"time"
7+
)
8+
9+
func main() {
10+
msc.Port(machine.Flash)
11+
12+
for {
13+
time.Sleep(2 * time.Second)
14+
}
15+
}

src/machine/usb/descriptor/endpoint.go

Lines changed: 45 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 endpointMSCIN = [endpointTypeLen]byte{
90+
endpointTypeLen,
91+
TypeEndpoint,
92+
0x86, // EndpointAddress
93+
TransferTypeBulk, // Attributes
94+
0x40, // MaxPacketSizeL (64 bytes)
95+
0x00, // MaxPacketSizeH
96+
0x00, // Interval
97+
}
98+
99+
var EndpointMSCIN = EndpointType{
100+
data: endpointMSCIN[:],
101+
}
102+
103+
// Mass Storage Class bulk out endpoint
104+
var endpointMSCOUT = [endpointTypeLen]byte{
105+
endpointTypeLen,
106+
TypeEndpoint,
107+
0x07, // EndpointAddress
108+
TransferTypeBulk, // Attributes
109+
0x40, // MaxPacketSizeL (64 bytes)
110+
0x00, // MaxPacketSizeH
111+
0x00, // Interval
112+
}
113+
114+
var EndpointMSCOUT = EndpointType{
115+
data: endpointMSCOUT[:],
116+
}
117+
77118
const (
78119
endpointTypeLen = 7
79120
)
@@ -109,3 +150,7 @@ func (d EndpointType) MaxPacketSize(v uint16) {
109150
func (d EndpointType) Interval(v uint8) {
110151
d.data[6] = byte(v)
111152
}
153+
154+
func (d EndpointType) GetMaxPacketSize() uint16 {
155+
return binary.LittleEndian.Uint16(d.data[4:6])
156+
}

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+
EndpointMSCIN.Bytes(),
73+
EndpointMSCOUT.Bytes(),
74+
}),
75+
}

src/machine/usb/msc/cbw.go

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

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
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+
Signature = 0x53425355 // "USBS" in little endian
14+
)

0 commit comments

Comments
 (0)