|
1 | 1 | package msc |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "encoding/binary" |
4 | 5 | "machine" |
5 | 6 | "machine/usb" |
6 | 7 | "machine/usb/descriptor" |
@@ -72,9 +73,7 @@ func newMSC(dev machine.BlockDevice) *msc { |
72 | 73 | maxPacketSize := descriptor.EndpointMSCIN.GetMaxPacketSize() |
73 | 74 | m := &msc{ |
74 | 75 | // Some platforms require reads/writes to be aligned to the full underlying hardware block |
75 | | - blockCache: make([]byte, dev.WriteBlockSize()), |
76 | 76 | blockSizeUSB: 512, |
77 | | - buf: make([]byte, dev.WriteBlockSize()), |
78 | 77 | cswBuf: make([]byte, csw.MsgLen), |
79 | 78 | cbw: &CBW{Data: make([]byte, 31)}, |
80 | 79 | maxPacketSize: uint32(maxPacketSize), |
@@ -297,3 +296,49 @@ func (m *msc) run(b []byte, isEpOut bool) bool { |
297 | 296 |
|
298 | 297 | return ack |
299 | 298 | } |
| 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 | +} |
0 commit comments