Skip to content

Commit 156ea34

Browse files
committed
fix: handle write block sizes < usb packet size
refactor: clean up duplicated device init logic
1 parent ced1d5c commit 156ea34

File tree

3 files changed

+48
-48
lines changed

3 files changed

+48
-48
lines changed

src/machine/usb/msc/msc.go

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

33
import (
4+
"encoding/binary"
45
"machine"
56
"machine/usb"
67
"machine/usb/descriptor"
@@ -72,9 +73,7 @@ func newMSC(dev machine.BlockDevice) *msc {
7273
maxPacketSize := descriptor.EndpointMSCIN.GetMaxPacketSize()
7374
m := &msc{
7475
// Some platforms require reads/writes to be aligned to the full underlying hardware block
75-
blockCache: make([]byte, dev.WriteBlockSize()),
7676
blockSizeUSB: 512,
77-
buf: make([]byte, dev.WriteBlockSize()),
7877
cswBuf: make([]byte, csw.MsgLen),
7978
cbw: &CBW{Data: make([]byte, 31)},
8079
maxPacketSize: uint32(maxPacketSize),
@@ -297,3 +296,49 @@ func (m *msc) run(b []byte, isEpOut bool) bool {
297296

298297
return ack
299298
}
299+
300+
// RegisterBlockDevice registers a BlockDevice provider with the MSC driver
301+
func (m *msc) RegisterBlockDevice(dev machine.BlockDevice) {
302+
m.dev = dev
303+
304+
maxPacketSize := descriptor.EndpointMSCIN.GetMaxPacketSize()
305+
bufSize := max(dev.WriteBlockSize(), int64(maxPacketSize))
306+
307+
if cap(m.blockCache) != int(bufSize) {
308+
m.blockCache = make([]byte, bufSize)
309+
m.buf = make([]byte, bufSize)
310+
}
311+
312+
m.blockSizeRaw = uint32(m.dev.WriteBlockSize())
313+
m.blockCount = uint32(m.dev.Size()) / m.blockSizeUSB
314+
// Read/write/erase operations must be aligned to the underlying hardware blocks. In order to align
315+
// them we assume the provided block device is aligned to the end of the underlying hardware block
316+
// device and offset all reads/writes by the remaining bytes that don't make up a full block.
317+
m.blockOffset = uint32(m.dev.Size()) % m.blockSizeUSB
318+
319+
// Set VPD UNMAP fields
320+
for i := range vpdPages {
321+
if vpdPages[i].PageCode == 0xb0 {
322+
// 0xb0 - 5.4.5 Block Limits VPD page (B0h)
323+
if len(vpdPages[i].Data) >= 28 {
324+
// Set the OPTIMAL UNMAP GRANULARITY (write blocks per erase block)
325+
granularity := uint32(dev.EraseBlockSize()) / m.blockSizeUSB
326+
binary.BigEndian.PutUint32(vpdPages[i].Data[24:28], granularity)
327+
}
328+
if len(vpdPages[i].Data) >= 32 {
329+
// Set the UNMAP GRANULARITY ALIGNMENT (first sector of first full erase block)
330+
// The unmap granularity alignment is used to calculate an optimal unmap request starting LBA as follows:
331+
// optimal unmap request starting LBA = (n * OPTIMAL UNMAP GRANULARITY) + UNMAP GRANULARITY ALIGNMENT
332+
// where n is zero or any positive integer value
333+
// https://www.seagate.com/files/staticfiles/support/docs/manual/Interface%20manuals/100293068j.pdf
334+
335+
// We assume the block device is aligned to the end of the underlying block device
336+
blockOffset := uint32(dev.EraseBlockSize()) % m.blockSizeUSB
337+
// Set the UGAVALID bit to indicate that the UNMAP GRANULARITY ALIGNMENT is valid
338+
blockOffset |= 0x80000000
339+
binary.BigEndian.PutUint32(vpdPages[i].Data[28:32], blockOffset)
340+
}
341+
break
342+
}
343+
}
344+
}

src/machine/usb/msc/disk.go renamed to src/machine/usb/msc/recorder.go

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

33
import (
4-
"encoding/binary"
54
"errors"
65
"fmt"
76
"machine"
@@ -12,50 +11,6 @@ var (
1211
errWriteOutOfBounds = errors.New("WriteAt offset out of bounds")
1312
)
1413

15-
// RegisterBlockDevice registers a BlockDevice provider with the MSC driver
16-
func (m *msc) RegisterBlockDevice(dev machine.BlockDevice) {
17-
m.dev = dev
18-
19-
if cap(m.blockCache) != int(dev.WriteBlockSize()) {
20-
m.blockCache = make([]byte, dev.WriteBlockSize())
21-
m.buf = make([]byte, dev.WriteBlockSize())
22-
}
23-
24-
m.blockSizeRaw = uint32(m.dev.WriteBlockSize())
25-
m.blockCount = uint32(m.dev.Size()) / m.blockSizeUSB
26-
// Read/write/erase operations must be aligned to the underlying hardware blocks. In order to align
27-
// them we assume the provided block device is aligned to the end of the underlying hardware block
28-
// device and offset all reads/writes by the remaining bytes that don't make up a full block.
29-
m.blockOffset = uint32(m.dev.Size()) % m.blockSizeUSB
30-
// FIXME: Figure out what to do if the emulated write block size is larger than the erase block size
31-
32-
// Set VPD UNMAP fields
33-
for i := range vpdPages {
34-
if vpdPages[i].PageCode == 0xb0 {
35-
// 0xb0 - 5.4.5 Block Limits VPD page (B0h)
36-
if len(vpdPages[i].Data) >= 28 {
37-
// Set the OPTIMAL UNMAP GRANULARITY (write blocks per erase block)
38-
granularity := uint32(dev.EraseBlockSize()) / m.blockSizeUSB
39-
binary.BigEndian.PutUint32(vpdPages[i].Data[24:28], granularity)
40-
}
41-
if len(vpdPages[i].Data) >= 32 {
42-
// Set the UNMAP GRANULARITY ALIGNMENT (first sector of first full erase block)
43-
// The unmap granularity alignment is used to calculate an optimal unmap request starting LBA as follows:
44-
// optimal unmap request starting LBA = (n * OPTIMAL UNMAP GRANULARITY) + UNMAP GRANULARITY ALIGNMENT
45-
// where n is zero or any positive integer value
46-
// https://www.seagate.com/files/staticfiles/support/docs/manual/Interface%20manuals/100293068j.pdf
47-
48-
// We assume the block device is aligned to the end of the underlying block device
49-
blockOffset := uint32(dev.EraseBlockSize()) % m.blockSizeUSB
50-
// Set the UGAVALID bit to indicate that the UNMAP GRANULARITY ALIGNMENT is valid
51-
blockOffset |= 0x80000000
52-
binary.BigEndian.PutUint32(vpdPages[i].Data[28:32], blockOffset)
53-
}
54-
break
55-
}
56-
}
57-
}
58-
5914
var _ machine.BlockDevice = (*RecorderDisk)(nil)
6015

6116
// RecorderDisk is a block device that records actions taken on it

src/machine/usb/msc/scsi_readwrite.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ func (m *msc) writeBlock(b []byte, lba, offset uint32) (n int, err error) {
8787
// Convert the emulated block address to the underlying hardware block's start and offset
8888
blockStart, blockOffset := m.usbToRawOffset(lba, offset)
8989

90-
if blockOffset != 0 || len(b) != int(m.blockSizeRaw) {
90+
if blockOffset != 0 || len(b)%int(m.blockSizeRaw) != 0 {
9191
return 0, invalidWriteError
9292
}
9393

0 commit comments

Comments
 (0)