Skip to content

Commit 7d4d405

Browse files
committed
feat: add discard/erase block support
1 parent aad443e commit 7d4d405

File tree

16 files changed

+1114
-290
lines changed

16 files changed

+1114
-290
lines changed

src/machine/usb.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ var (
123123
usbTxHandler [usb.NumberOfEndpoints]func()
124124
usbRxHandler [usb.NumberOfEndpoints]func([]byte)
125125
usbSetupHandler [usb.NumberOfInterfaces]func(usb.Setup) bool
126+
usbStallHandler [usb.NumberOfEndpoints]func(usb.Setup) bool
126127

127128
endPoints = []uint32{
128129
usb.CONTROL_ENDPOINT: usb.ENDPOINT_TYPE_CONTROL,
@@ -214,7 +215,11 @@ func handleStandardSetup(setup usb.Setup) bool {
214215
if setup.WValueL == 1 { // DEVICEREMOTEWAKEUP
215216
isRemoteWakeUpEnabled = false
216217
} else if setup.WValueL == 0 { // ENDPOINTHALT
217-
isEndpointHalt = false
218+
if usbStallHandler[setup.WIndex&0x7F] != nil {
219+
return usbStallHandler[setup.WIndex&0x7F](setup)
220+
} else {
221+
isEndpointHalt = false
222+
}
218223
}
219224
SendZlp()
220225
return true
@@ -223,7 +228,11 @@ func handleStandardSetup(setup usb.Setup) bool {
223228
if setup.WValueL == 1 { // DEVICEREMOTEWAKEUP
224229
isRemoteWakeUpEnabled = true
225230
} else if setup.WValueL == 0 { // ENDPOINTHALT
226-
isEndpointHalt = true
231+
if usbStallHandler[setup.WIndex&0x7F] != nil {
232+
return usbStallHandler[setup.WIndex&0x7F](setup)
233+
} else {
234+
isEndpointHalt = true
235+
}
227236
}
228237
SendZlp()
229238
return true
@@ -322,6 +331,9 @@ func ConfigureUSBEndpoint(desc descriptor.Descriptor, epSettings []usb.EndpointC
322331
usbRxHandler[ep.Index] = ep.RxHandler
323332
}
324333
}
334+
if ep.StallHandler != nil {
335+
usbStallHandler[ep.Index] = ep.StallHandler
336+
}
325337
}
326338

327339
for _, s := range setup {

src/machine/usb/config.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
package usb
22

33
type EndpointConfig struct {
4-
Index uint8
5-
IsIn bool
6-
TxHandler func()
7-
RxHandler func([]byte)
8-
Type uint8
4+
Index uint8
5+
IsIn bool
6+
TxHandler func()
7+
RxHandler func([]byte)
8+
StallHandler func(Setup) bool
9+
Type uint8
910
}
1011

1112
type SetupConfig struct {

src/machine/usb/descriptor/endpoint.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,3 +150,7 @@ func (d EndpointType) MaxPacketSize(v uint16) {
150150
func (d EndpointType) Interval(v uint8) {
151151
d.data[6] = byte(v)
152152
}
153+
154+
func (d EndpointType) GetMaxPacketSize() uint16 {
155+
return binary.LittleEndian.Uint16(d.data[4:6])
156+
}

src/machine/usb/msc/cbw.go

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package msc
22

33
import (
4+
"encoding/binary"
45
"machine/usb/msc/csw"
56
"machine/usb/msc/scsi"
67
)
78

89
const (
9-
cbwMsgLen = 31 // Command Block Wrapper (CBW) message length
10+
cbwMsgLen = 31 // Command Block Wrapper (CBW) message length
11+
Signature = 0x43425355 // "USBC" in little endian
1012
)
1113

1214
type CBW struct {
@@ -22,15 +24,15 @@ func (c *CBW) validLength() bool {
2224
}
2325

2426
func (c *CBW) validSignature() bool {
25-
return c.Data[0] == 0x55 && c.Data[1] == 0x53 && c.Data[2] == 0x42 && c.Data[3] == 0x43
27+
return binary.LittleEndian.Uint32(c.Data[:4]) == Signature
2628
}
2729

2830
func (c *CBW) scsiCmd() scsi.Cmd {
2931
return scsi.Cmd{Data: c.Data[15:]}
3032
}
3133

3234
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
35+
return binary.LittleEndian.Uint32(c.Data[8:12])
3436
}
3537

3638
// isIn returns true if the command direction is from the device to the host.
@@ -44,21 +46,12 @@ func (c *CBW) isOut() bool {
4446
}
4547

4648
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]
49+
// Signature: "USBS" 53425355h (little endian)
50+
binary.LittleEndian.PutUint32(b[:4], csw.Signature)
51+
// Tag: (same as CBW)
52+
copy(b[4:8], c.Data[4:8])
5753
// 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)
54+
binary.LittleEndian.PutUint32(b[8:12], residue)
6255
// Status:
6356
b[12] = byte(status)
6457
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ const (
99
)
1010

1111
const (
12-
MsgLen = 13
12+
MsgLen = 13
13+
Signature = 0x53425355 // "USBS" in little endian
1314
)

src/machine/usb/msc/disk.go

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
package msc
22

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-
}
3+
import (
4+
"encoding/binary"
5+
"machine"
6+
)
7+
8+
var _ machine.BlockDevice = (*DefaultDisk)(nil)
119

1210
// DefaultDisk is a placeholder disk implementation
1311
type DefaultDisk struct {
@@ -18,36 +16,58 @@ func NewDefaultDisk() *DefaultDisk {
1816
return &DefaultDisk{}
1917
}
2018

21-
func (d *DefaultDisk) Ready() bool {
22-
return true
19+
func (d *DefaultDisk) Size() int64 {
20+
return 4096 * int64(d.WriteBlockSize()) // 2MB
2321
}
2422

25-
func (d *DefaultDisk) ReadOnly() bool {
26-
return false
23+
func (d *DefaultDisk) WriteBlockSize() int64 {
24+
return 512 // 512 bytes
2725
}
2826

29-
func (d *DefaultDisk) BlockCount() uint32 {
30-
return 4096 // 2MB
27+
func (d *DefaultDisk) EraseBlockSize() int64 {
28+
return 2048 // 4 blocks of 512 bytes
3129
}
3230

33-
func (d *DefaultDisk) BlockSize() uint32 {
34-
return 512 // 512 bytes
31+
func (d *DefaultDisk) EraseBlocks(startBlock, numBlocks int64) error {
32+
return nil
3533
}
3634

37-
func (d *DefaultDisk) Read(offset uint32, buffer []byte) (uint32, error) {
35+
func (d *DefaultDisk) ReadAt(buffer []byte, offset int64) (int, error) {
3836
n := uint8(offset)
3937
for i := range buffer {
4038
n++
4139
buffer[i] = n
4240
}
43-
return uint32(len(buffer)), nil
41+
return len(buffer), nil
4442
}
4543

46-
func (d *DefaultDisk) Write(offset uint32, buffer []byte) (uint32, error) {
47-
return uint32(len(buffer)), nil
44+
func (d *DefaultDisk) WriteAt(buffer []byte, offset int64) (int, error) {
45+
return len(buffer), nil
4846
}
4947

50-
// RegisterDisk registers a disk provider with the MSC driver
51-
func (m *msc) RegisterDisk(disk Disk) {
52-
m.disk = disk
48+
// RegisterBlockDevice registers a BlockDevice provider with the MSC driver
49+
func (m *msc) RegisterBlockDevice(dev machine.BlockDevice) {
50+
m.dev = dev
51+
52+
// Set VPD UNMAP fields
53+
for i := range vpdPages {
54+
if vpdPages[i].PageCode == 0xb0 {
55+
// 0xb0 - 5.4.5 Block Limits VPD page (B0h)
56+
if len(vpdPages[i].Data) >= 28 {
57+
// Set the OPTIMAL UNMAP GRANULARITY (write blocks per erase block)
58+
granularity := uint32(dev.EraseBlockSize()) / uint32(dev.WriteBlockSize())
59+
binary.BigEndian.PutUint32(vpdPages[i].Data[24:28], granularity)
60+
}
61+
/* TODO: Add method for working out the optimal unmap granularity alignment
62+
if len(vpdPages[i].Data) >= 32 {
63+
// Set the UNMAP GRANULARITY ALIGNMENT (first sector of first full erase block)
64+
// The unmap granularity alignment is used to calculate an optimal unmap request starting LBA as follows:
65+
// optimal unmap request starting LBA = (n * OPTIMAL UNMAP GRANULARITY) + UNMAP GRANULARITY ALIGNMENT
66+
// where n is zero or any positive integer value
67+
// https://www.seagate.com/files/staticfiles/support/docs/manual/Interface%20manuals/100293068j.pdf
68+
}
69+
*/
70+
break
71+
}
72+
}
5373
}

src/machine/usb/msc/fakefs/fakefs.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package fakefs
2+
3+
type MockFS interface {
4+
ReadAt(p []byte, off int64) (n int, err error)
5+
WriteAt(p []byte, off int64) (n int, err error)
6+
ReadOnly() bool
7+
/* FIXME: Cleanup?
8+
SectorCount() uint32
9+
SectorSize() uint16
10+
*/
11+
}
12+
13+
type FakeFS struct {
14+
fs MockFS
15+
ready bool
16+
}
17+
18+
func NewFakeFS() *FakeFS {
19+
// FIXME: Initialize the fake FAT12 file system
20+
return &FakeFS{fs: NewFAT12(), ready: true}
21+
}
22+
23+
func (fs *FakeFS) TestUnitReady() bool {
24+
// Check if the disk is ready
25+
return fs.ready
26+
}
27+
28+
func (fs *FakeFS) SetReady(ready bool) {
29+
// Set the disk ready state
30+
fs.ready = ready
31+
}
32+
33+
func (fs *FakeFS) Read(offset uint32, buffer []byte) (uint32, error) {
34+
return uint32(len(buffer)), nil // FIXME: Cleanup
35+
// Read data from the file system
36+
n, err := fs.fs.ReadAt(buffer, int64(offset))
37+
if err != nil {
38+
return 0, err
39+
}
40+
return uint32(n), nil
41+
}
42+
43+
func (fs *FakeFS) Write(offset uint32, buffer []byte) (uint32, error) {
44+
return uint32(len(buffer)), nil // FIXME: Cleanup
45+
// Write data to the file system
46+
n, err := fs.fs.WriteAt(buffer, int64(offset))
47+
if err != nil {
48+
return 0, err
49+
}
50+
return uint32(n), nil
51+
}
52+
53+
/* FIXME: Cleanup
54+
func (fs *FakeFS) ReadCapacity() (uint32, uint32) {
55+
// Return the total number of blocks and block size
56+
return 0, 0 // FIXME: Implement finding actual values
57+
}
58+
59+
func (fs *FakeFS) ReadFormatCapacity() (uint32, uint32) {
60+
// Return the total number of blocks and block size
61+
return 0, 0 // FIXME: Implement finding actual values
62+
}
63+
64+
func (fs *FakeFS) Inquiry() ([]byte, error) {
65+
// Return the inquiry data
66+
return nil, nil // FIXME: Implement finding actual values
67+
}
68+
*/
69+
70+
func (fs *FakeFS) ReadOnly() bool {
71+
// If the file system is read-only, return true to indicate the hardware is read-only also
72+
return fs.fs.ReadOnly()
73+
}
74+
75+
func (fs *FakeFS) BlockCount() uint32 {
76+
// Return the total number of blocks
77+
return 4096 // FIXME: Implement finding actual values
78+
}
79+
80+
func (fs *FakeFS) BlockSize() uint32 {
81+
// Return the block size
82+
return 512 // FIXME: Implement finding actual values
83+
}

src/machine/usb/msc/fakefs/fat12.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package fakefs
2+
3+
/* READ10 Callback - pico-littlefs-usb
4+
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) {
5+
(void)lun;
6+
(void)offset;
7+
8+
if (!is_initialized) {
9+
mimic_fat_init(&lfs_pico_flash_config);
10+
mimic_fat_update_usb_device_is_enabled(true);
11+
mimic_fat_create_cache();
12+
is_initialized = true;
13+
}
14+
mimic_fat_read(lun, lba, buffer, bufsize);
15+
16+
return (int32_t)bufsize;
17+
*/
18+
19+
type FAT12 struct {
20+
readonly bool
21+
}
22+
23+
func NewFAT12() *FAT12 {
24+
return &FAT12{}
25+
}
26+
27+
func (f *FAT12) ReadOnly() bool {
28+
return f.readonly
29+
}
30+
31+
func (f *FAT12) ReadAt(p []byte, off int64) (n int, err error) {
32+
// FIXME: Implement this function
33+
/* ReadAt reads len(p) bytes into p starting at offset off in the underlying input source. It returns the number of bytes read (0 <= n <= len(p)) and any error encountered.
34+
When ReadAt returns n < len(p), it returns a non-nil error explaining why more bytes were not returned. In this respect, ReadAt is stricter than Read.
35+
Even if ReadAt returns n < len(p), it may use all of p as scratch space during the call. If some data is available but not len(p) bytes, ReadAt blocks until either all the data is available or an error occurs. In this respect ReadAt is different from Read.
36+
If the n = len(p) bytes returned by ReadAt are at the end of the input source, ReadAt may return either err == EOF or err == nil.
37+
If ReadAt is reading from an input source with a seek offset, ReadAt should not affect nor be affected by the underlying seek offset.
38+
Clients of ReadAt can execute parallel ReadAt calls on the same input source.
39+
Implementations must not retain p.
40+
*/
41+
42+
return 0, nil
43+
}
44+
45+
func (f *FAT12) WriteAt(p []byte, off int64) (n int, err error) {
46+
// FIXME: Implement this function
47+
/* WriteAt writes len(p) bytes from p to the underlying data stream at offset off. It returns the number of bytes written from p (0 <= n <= len(p)) and any error encountered that caused the write to stop early. WriteAt must return a non-nil error if it returns n < len(p).
48+
If WriteAt is writing to a destination with a seek offset, WriteAt should not affect nor be affected by the underlying seek offset.
49+
Clients of WriteAt can execute parallel WriteAt calls on the same destination if the ranges do not overlap.
50+
Implementations must not retain p.
51+
*/
52+
53+
return 0, nil
54+
}
55+
56+
/* FIXME: Cleanup?
57+
func (f *FAT12) SectorCount() uint32 {
58+
// FIXME: Implement this:
59+
// uint64_t storage_size = littlefs_lfs_config->block_count * littlefs_lfs_config->block_size;
60+
// return (double)storage_size / DISK_SECTOR_SIZE;
61+
return f.sectors
62+
}
63+
64+
func (f *FAT12) SectorSize() uint16 {
65+
return 512
66+
}
67+
*/

0 commit comments

Comments
 (0)