Skip to content

Commit d5bc91c

Browse files
committed
chore: add task queueing for flash writes
1 parent 8db610f commit d5bc91c

13 files changed

+536
-201
lines changed

src/machine/machine_rp2040_usb.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,9 @@ func handleUSBIRQ(intr interrupt.Interrupt) {
9595
for i := 0; i < 16; i++ {
9696
if s2&(1<<(i*2+1)) > 0 {
9797
buf := handleEndpointRx(uint32(i))
98-
if usbRxHandler[i] != nil {
99-
usbRxHandler[i](buf)
98+
if usbRxHandler[i] == nil || usbRxHandler[i](buf) {
99+
AckEndpointRxMessage(uint32(i))
100100
}
101-
handleEndpointRxComplete(uint32(i))
102101
}
103102
}
104103

src/machine/machine_rp2350_usb.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,9 @@ func handleUSBIRQ(intr interrupt.Interrupt) {
9898
for i := 0; i < 16; i++ {
9999
if s2&(1<<(i*2+1)) > 0 {
100100
buf := handleEndpointRx(uint32(i))
101-
if usbRxHandler[i] != nil {
102-
usbRxHandler[i](buf)
101+
if usbRxHandler[i] == nil || usbRxHandler[i](buf) {
102+
AckEndpointRxMessage(uint32(i))
103103
}
104-
handleEndpointRxComplete(uint32(i))
105104
}
106105
}
107106

src/machine/machine_rp2_usb.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,9 @@ func handleEndpointRx(ep uint32) []byte {
115115
return _usbDPSRAM.EPxBuffer[ep].Buffer0[:sz]
116116
}
117117

118-
func handleEndpointRxComplete(ep uint32) {
118+
// AckEndpointRxMessage is called to acknowledge the completion of a delayed USB OUT transfer.
119+
func AckEndpointRxMessage(ep uint32) {
120+
ep = ep & 0x7F
119121
setEPDataPID(ep, !epXdata0[ep])
120122
}
121123

src/machine/usb.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ var usb_trans_buffer [255]uint8
121121

122122
var (
123123
usbTxHandler [usb.NumberOfEndpoints]func()
124-
usbRxHandler [usb.NumberOfEndpoints]func([]byte)
124+
usbRxHandler [usb.NumberOfEndpoints]func([]byte) bool
125125
usbSetupHandler [usb.NumberOfInterfaces]func(usb.Setup) bool
126126
usbStallHandler [usb.NumberOfEndpoints]func(usb.Setup) bool
127127

@@ -332,7 +332,12 @@ func ConfigureUSBEndpoint(desc descriptor.Descriptor, epSettings []usb.EndpointC
332332
} else {
333333
endPoints[ep.Index] = uint32(ep.Type | usb.EndpointOut)
334334
if ep.RxHandler != nil {
335-
usbRxHandler[ep.Index] = ep.RxHandler
335+
usbRxHandler[ep.Index] = func(b []byte) bool {
336+
ep.RxHandler(b)
337+
return true
338+
}
339+
} else if ep.DelayRxHandler != nil {
340+
usbRxHandler[ep.Index] = ep.DelayRxHandler
336341
}
337342
}
338343
if ep.StallHandler != nil {

src/machine/usb/config.go

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

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

1213
type SetupConfig struct {

src/machine/usb/msc/cbw.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@ const (
1212
)
1313

1414
type CBW struct {
15-
Data []byte
15+
HasCmd bool
16+
Data []byte
17+
}
18+
19+
func (c *CBW) Tag() uint32 {
20+
return binary.LittleEndian.Uint32(c.Data[4:8])
1621
}
1722

1823
func (c *CBW) length() int {
@@ -27,7 +32,7 @@ func (c *CBW) validSignature() bool {
2732
return binary.LittleEndian.Uint32(c.Data[:4]) == Signature
2833
}
2934

30-
func (c *CBW) scsiCmd() scsi.Cmd {
35+
func (c *CBW) SCSICmd() scsi.Cmd {
3136
return scsi.Cmd{Data: c.Data[15:]}
3237
}
3338

src/machine/usb/msc/disk.go

Lines changed: 173 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,72 +2,206 @@ package msc
22

33
import (
44
"encoding/binary"
5+
"errors"
6+
"fmt"
57
"machine"
8+
"runtime/interrupt"
9+
"time"
610
)
711

8-
var _ machine.BlockDevice = (*DefaultDisk)(nil)
12+
var (
13+
errWriteOutOfBounds = errors.New("WriteAt offset out of bounds")
14+
)
15+
16+
// RegisterBlockDevice registers a BlockDevice provider with the MSC driver
17+
func (m *msc) RegisterBlockDevice(dev machine.BlockDevice) {
18+
m.dev = dev
19+
20+
m.blockRatio = 1
21+
m.blockSize = uint32(m.dev.WriteBlockSize())
22+
if m.blockSize < 512 {
23+
// If the block size is less than 512 bytes, we'll scale it up to 512 for compatibility
24+
m.blockRatio = 512 / m.blockSize
25+
m.blockSize = 512
26+
}
27+
m.blockCount = uint32(m.dev.Size()) / m.blockSize
28+
// FIXME: Figure out what to do if the emulated write block size is larger than the erase block size
29+
30+
// Set VPD UNMAP fields
31+
for i := range vpdPages {
32+
if vpdPages[i].PageCode == 0xb0 {
33+
// 0xb0 - 5.4.5 Block Limits VPD page (B0h)
34+
if len(vpdPages[i].Data) >= 28 {
35+
// Set the OPTIMAL UNMAP GRANULARITY (write blocks per erase block)
36+
granularity := uint32(dev.EraseBlockSize()) / m.blockSize
37+
binary.BigEndian.PutUint32(vpdPages[i].Data[24:28], granularity)
38+
}
39+
if len(vpdPages[i].Data) >= 32 {
40+
// Set the UNMAP GRANULARITY ALIGNMENT (first sector of first full erase block)
41+
// The unmap granularity alignment is used to calculate an optimal unmap request starting LBA as follows:
42+
// optimal unmap request starting LBA = (n * OPTIMAL UNMAP GRANULARITY) + UNMAP GRANULARITY ALIGNMENT
43+
// where n is zero or any positive integer value
44+
// https://www.seagate.com/files/staticfiles/support/docs/manual/Interface%20manuals/100293068j.pdf
45+
46+
// We assume the block device is aligned to the end of the underlying block device
47+
blockOffset := uint32(dev.EraseBlockSize()) % m.blockSize
48+
binary.BigEndian.PutUint32(vpdPages[i].Data[28:32], blockOffset)
49+
}
50+
break
51+
}
52+
}
53+
}
54+
55+
var _ machine.BlockDevice = (*RecorderDisk)(nil)
956

10-
// DefaultDisk is a placeholder disk implementation
11-
type DefaultDisk struct {
57+
// RecorderDisk is a block device that records actions taken on it
58+
type RecorderDisk struct {
59+
data map[int64][]byte
60+
log []RecorderRecord
61+
last time.Time
62+
time time.Time
1263
}
1364

14-
// NewDefaultDisk creates a new DefaultDisk instance
15-
func NewDefaultDisk() *DefaultDisk {
16-
return &DefaultDisk{}
65+
type RecorderRecord struct {
66+
OpCode RecorderOpCode
67+
Offset int64
68+
Length int
69+
Data []byte
70+
Time int64
71+
valid bool
1772
}
1873

19-
func (d *DefaultDisk) Size() int64 {
74+
type RecorderOpCode uint8
75+
76+
const (
77+
RecorderOpCodeRead RecorderOpCode = iota
78+
RecorderOpCodeWrite
79+
RecorderOpCodeEraseBlocks
80+
)
81+
82+
// NewRecorderDisk creates a new RecorderDisk instance
83+
func NewRecorderDisk(count int) *RecorderDisk {
84+
d := &RecorderDisk{
85+
data: make(map[int64][]byte),
86+
log: make([]RecorderRecord, 0, count),
87+
last: time.Now(),
88+
}
89+
for i := 0; i < count; i++ {
90+
d.log = append(d.log, RecorderRecord{
91+
OpCode: RecorderOpCodeRead,
92+
Offset: 0,
93+
Length: 0,
94+
Data: make([]byte, 0, 64),
95+
Time: 0,
96+
})
97+
}
98+
return d
99+
}
100+
101+
func (d *RecorderDisk) Size() int64 {
20102
return 4096 * int64(d.WriteBlockSize()) // 2MB
21103
}
22104

23-
func (d *DefaultDisk) WriteBlockSize() int64 {
105+
func (d *RecorderDisk) WriteBlockSize() int64 {
24106
return 512 // 512 bytes
25107
}
26108

27-
func (d *DefaultDisk) EraseBlockSize() int64 {
109+
func (d *RecorderDisk) EraseBlockSize() int64 {
28110
return 2048 // 4 blocks of 512 bytes
29111
}
30112

31-
func (d *DefaultDisk) EraseBlocks(startBlock, numBlocks int64) error {
113+
func (d *RecorderDisk) EraseBlocks(startBlock, numBlocks int64) error {
114+
d.Record(RecorderOpCodeEraseBlocks, startBlock, int(numBlocks), []byte{})
115+
if interrupt.In() {
116+
// Flash erase commands are not allowed in interrupt context
117+
panic("EraseBlocks attempted in interrupt context")
118+
}
32119
return nil
33120
}
34121

35-
func (d *DefaultDisk) ReadAt(buffer []byte, offset int64) (int, error) {
36-
n := uint8(offset)
37-
for i := range buffer {
38-
n++
39-
buffer[i] = n
122+
func (d *RecorderDisk) ReadAt(buffer []byte, offset int64) (int, error) {
123+
d.Record(RecorderOpCodeRead, offset, len(buffer), []byte{})
124+
sector := offset / d.WriteBlockSize()
125+
if sector < 0 || offset+int64(len(buffer)) > d.Size() {
126+
return 0, fmt.Errorf("read out of bounds: %d", offset)
127+
}
128+
129+
sectorCount := int64(len(buffer)) / d.WriteBlockSize()
130+
for i := int64(0); i < sectorCount; i++ {
131+
if _, ok := d.data[sector+i]; ok {
132+
n := int(offset % d.WriteBlockSize())
133+
copy(buffer, d.data[sector+i][n:])
134+
}
40135
}
136+
41137
return len(buffer), nil
42138
}
43139

44-
func (d *DefaultDisk) WriteAt(buffer []byte, offset int64) (int, error) {
140+
func (d *RecorderDisk) WriteAt(buffer []byte, offset int64) (int, error) {
141+
if interrupt.In() {
142+
// Flash writes aren't possible in interrupt context
143+
panic("WriteAt attempted in interrupt context")
144+
}
145+
if offset < 0 || offset+int64(len(buffer)) > d.Size() {
146+
return 0, errWriteOutOfBounds
147+
}
148+
149+
d.Record(RecorderOpCodeWrite, offset, len(buffer), buffer)
150+
151+
sector := offset / d.WriteBlockSize()
152+
sectorCount := int64(len(buffer)) / d.WriteBlockSize()
153+
for i := int64(0); i < sectorCount; i++ {
154+
_, ok := d.data[sector+i]
155+
if !ok {
156+
d.data[sector+i] = make([]byte, d.WriteBlockSize())
157+
}
158+
n := int(offset % d.WriteBlockSize())
159+
copy(d.data[sector+i][n:], buffer)
160+
}
45161
return len(buffer), nil
46162
}
47163

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
164+
func (d *RecorderDisk) Record(opCode RecorderOpCode, offset int64, length int, data []byte) {
165+
n := len(d.log)
166+
if n == 0 {
167+
return
168+
} else if n == cap(d.log) {
169+
for i := 0; i < n-1; i++ {
170+
d.log[i] = d.log[i+1]
71171
}
72172
}
173+
174+
// Append the new record
175+
d.log[n-1].OpCode = opCode
176+
d.log[n-1].Offset = offset
177+
d.log[n-1].Length = length
178+
d.log[n-1].Data = d.log[n-1].Data[:len(data)]
179+
copy(d.log[n-1].Data, data)
180+
d.log[n-1].Time = time.Since(d.time).Microseconds()
181+
d.time = d.time.Add(time.Since(d.time))
182+
d.log[n-1].valid = true
183+
}
184+
185+
func (d *RecorderDisk) ClearLog() {
186+
for i := range d.log {
187+
d.log[i].valid = false
188+
}
189+
d.time = time.Now()
190+
}
191+
192+
func (d *RecorderDisk) GetLog() []RecorderRecord {
193+
return d.log
194+
}
195+
196+
func (r RecorderRecord) String() (string, bool) {
197+
opCode := "Unknown"
198+
switch r.OpCode {
199+
case RecorderOpCodeRead:
200+
opCode = "Read"
201+
case RecorderOpCodeWrite:
202+
opCode = "Write"
203+
case RecorderOpCodeEraseBlocks:
204+
opCode = "EraseBlocks"
205+
}
206+
return fmt.Sprintf("%s: %05d+%02d t:%d| % 0x", opCode, r.Offset, r.Length, r.Time, r.Data), r.valid
73207
}

0 commit comments

Comments
 (0)