Compare commits

...

1 Commits

Author SHA1 Message Date
Ayke van Laethem 465bde5df6 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.
2025-08-30 08:13:07 +02:00
2 changed files with 41 additions and 6 deletions
+31 -6
View File
@@ -1,11 +1,13 @@
package msc package msc
import ( import (
"errors"
"machine" "machine"
"machine/usb" "machine/usb"
"machine/usb/descriptor" "machine/usb/descriptor"
"machine/usb/msc/csw" "machine/usb/msc/csw"
"machine/usb/msc/scsi" "machine/usb/msc/scsi"
"math/bits"
"time" "time"
) )
@@ -23,6 +25,8 @@ const (
mscInterface = 2 mscInterface = 2
) )
var errInvalidBlockSize = errors.New("usb/msc: invalid block size")
var MSC *msc var MSC *msc
type msc struct { type msc struct {
@@ -60,20 +64,41 @@ type msc struct {
} }
// Port returns the USB Mass Storage port // Port returns the USB Mass Storage port
func Port(dev machine.BlockDevice) *msc { func Port(dev machine.BlockDevice) (*msc, error) {
if MSC == nil { if MSC == nil {
MSC = newMSC(dev) msc, err := newMSC(dev)
if err != nil {
return nil, err
}
MSC = msc
} }
return MSC return MSC, nil
} }
func newMSC(dev machine.BlockDevice) *msc { func newMSC(dev machine.BlockDevice) (*msc, error) {
// Size our buffer to match the maximum packet size of the IN endpoint // Size our buffer to match the maximum packet size of the IN endpoint
maxPacketSize := descriptor.EndpointMSCIN.GetMaxPacketSize() maxPacketSize := descriptor.EndpointMSCIN.GetMaxPacketSize()
// Windows only supports block sizes of 512 or 4096 bytes, other systems are
// probably similar.
blockSize := max(dev.EraseBlockSize(), dev.WriteBlockSize())
if bits.OnesCount32(uint32(blockSize)) != 1 {
return nil, errInvalidBlockSize // not a power of two
}
var blockSizeUSB uint32
switch {
case blockSize <= 512:
blockSizeUSB = 512
case blockSize <= 4096:
blockSizeUSB = 4096
default:
return nil, errInvalidBlockSize
}
m := &msc{ m := &msc{
// Some platforms require reads/writes to be aligned to the full underlying hardware block // Some platforms require reads/writes to be aligned to the full underlying hardware block
blockCache: make([]byte, dev.WriteBlockSize()), blockCache: make([]byte, dev.WriteBlockSize()),
blockSizeUSB: 512, blockSizeUSB: blockSizeUSB,
buf: make([]byte, dev.WriteBlockSize()), buf: make([]byte, dev.WriteBlockSize()),
cswBuf: make([]byte, csw.MsgLen), cswBuf: make([]byte, csw.MsgLen),
cbw: &CBW{Data: make([]byte, 31)}, cbw: &CBW{Data: make([]byte, 31)},
@@ -114,7 +139,7 @@ func newMSC(dev machine.BlockDevice) *msc {
go m.processTasks() go m.processTasks()
return m return m, nil
} }
func (m *msc) processTasks() { func (m *msc) processTasks() {
+10
View File
@@ -91,6 +91,16 @@ func (m *msc) writeBlock(b []byte, lba, offset uint32) (n int, err error) {
return 0, invalidWriteError return 0, invalidWriteError
} }
// Erase the block first if needed.
// Data packets arrive in order, so if we want to write to the start of a
// block, that means it's the first write to this erase block and it needs
// to be erased first.
if uint32(blockStart)%m.blockSizeUSB == 0 {
firstBlock := uint32(blockStart) / uint32(m.dev.EraseBlockSize())
numBlocks := (m.blockSizeUSB + uint32(m.dev.EraseBlockSize()) - 1) / uint32(m.dev.EraseBlockSize())
m.dev.EraseBlocks(int64(firstBlock), int64(numBlocks))
}
// Write the full block to the underlying device // Write the full block to the underlying device
n, err = m.dev.WriteAt(b, blockStart) n, err = m.dev.WriteAt(b, blockStart)
n -= int(blockOffset) n -= int(blockOffset)