Skip to content

Commit 465bde5

Browse files
committed
usb/msc: erase blocks before writing to them
We could perhaps check whether they've been erased already (via unmap or something) but the default of most OSes seems to be to _not_ unmap blocks so we will have to erase in most cases anyway.
1 parent 3869f76 commit 465bde5

File tree

2 files changed

+41
-6
lines changed

2 files changed

+41
-6
lines changed

src/machine/usb/msc/msc.go

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

33
import (
4+
"errors"
45
"machine"
56
"machine/usb"
67
"machine/usb/descriptor"
78
"machine/usb/msc/csw"
89
"machine/usb/msc/scsi"
10+
"math/bits"
911
"time"
1012
)
1113

@@ -23,6 +25,8 @@ const (
2325
mscInterface = 2
2426
)
2527

28+
var errInvalidBlockSize = errors.New("usb/msc: invalid block size")
29+
2630
var MSC *msc
2731

2832
type msc struct {
@@ -60,20 +64,41 @@ type msc struct {
6064
}
6165

6266
// Port returns the USB Mass Storage port
63-
func Port(dev machine.BlockDevice) *msc {
67+
func Port(dev machine.BlockDevice) (*msc, error) {
6468
if MSC == nil {
65-
MSC = newMSC(dev)
69+
msc, err := newMSC(dev)
70+
if err != nil {
71+
return nil, err
72+
}
73+
MSC = msc
6674
}
67-
return MSC
75+
return MSC, nil
6876
}
6977

70-
func newMSC(dev machine.BlockDevice) *msc {
78+
func newMSC(dev machine.BlockDevice) (*msc, error) {
7179
// Size our buffer to match the maximum packet size of the IN endpoint
7280
maxPacketSize := descriptor.EndpointMSCIN.GetMaxPacketSize()
81+
82+
// Windows only supports block sizes of 512 or 4096 bytes, other systems are
83+
// probably similar.
84+
blockSize := max(dev.EraseBlockSize(), dev.WriteBlockSize())
85+
if bits.OnesCount32(uint32(blockSize)) != 1 {
86+
return nil, errInvalidBlockSize // not a power of two
87+
}
88+
var blockSizeUSB uint32
89+
switch {
90+
case blockSize <= 512:
91+
blockSizeUSB = 512
92+
case blockSize <= 4096:
93+
blockSizeUSB = 4096
94+
default:
95+
return nil, errInvalidBlockSize
96+
}
97+
7398
m := &msc{
7499
// Some platforms require reads/writes to be aligned to the full underlying hardware block
75100
blockCache: make([]byte, dev.WriteBlockSize()),
76-
blockSizeUSB: 512,
101+
blockSizeUSB: blockSizeUSB,
77102
buf: make([]byte, dev.WriteBlockSize()),
78103
cswBuf: make([]byte, csw.MsgLen),
79104
cbw: &CBW{Data: make([]byte, 31)},
@@ -114,7 +139,7 @@ func newMSC(dev machine.BlockDevice) *msc {
114139

115140
go m.processTasks()
116141

117-
return m
142+
return m, nil
118143
}
119144

120145
func (m *msc) processTasks() {

src/machine/usb/msc/scsi_readwrite.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,16 @@ func (m *msc) writeBlock(b []byte, lba, offset uint32) (n int, err error) {
9191
return 0, invalidWriteError
9292
}
9393

94+
// Erase the block first if needed.
95+
// Data packets arrive in order, so if we want to write to the start of a
96+
// block, that means it's the first write to this erase block and it needs
97+
// to be erased first.
98+
if uint32(blockStart)%m.blockSizeUSB == 0 {
99+
firstBlock := uint32(blockStart) / uint32(m.dev.EraseBlockSize())
100+
numBlocks := (m.blockSizeUSB + uint32(m.dev.EraseBlockSize()) - 1) / uint32(m.dev.EraseBlockSize())
101+
m.dev.EraseBlocks(int64(firstBlock), int64(numBlocks))
102+
}
103+
94104
// Write the full block to the underlying device
95105
n, err = m.dev.WriteAt(b, blockStart)
96106
n -= int(blockOffset)

0 commit comments

Comments
 (0)