mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-08 21:13:39 +00:00
92 lines
2.7 KiB
Go
92 lines
2.7 KiB
Go
package msc
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"machine/usb/msc/csw"
|
|
"machine/usb/msc/scsi"
|
|
)
|
|
|
|
type Error int
|
|
|
|
const (
|
|
errorLBAOutOfRange Error = iota
|
|
)
|
|
|
|
func (e Error) Error() string {
|
|
switch e {
|
|
case errorLBAOutOfRange:
|
|
return "LBA out of range"
|
|
default:
|
|
return "unknown error"
|
|
}
|
|
}
|
|
|
|
func (m *msc) scsiUnmap(b []byte) {
|
|
// Execute Order 66 (0x42) to wipe out the blocks
|
|
// 3.54 Unmap Command (SBC-4)
|
|
// https://www.seagate.com/files/staticfiles/support/docs/manual/Interface%20manuals/100293068j.pdf
|
|
if m.readOnly {
|
|
m.sendScsiError(csw.StatusFailed, scsi.SenseDataProtect, scsi.SenseCodeWriteProtected)
|
|
return
|
|
}
|
|
|
|
// blockDescLen is the remaining length of block descriptors in the message, offset 8 bytes from
|
|
// the start of this packet
|
|
var blockDescLen uint16
|
|
|
|
// Decode the parameter list
|
|
msgLen := binary.BigEndian.Uint16(b[:2])
|
|
// Length of the block descriptor portion of the message
|
|
blockDescLen = binary.BigEndian.Uint16(b[2:4])
|
|
// Do some sanity checks on the message lengths (max 3 block descriptors to fit in one 64 byte packet)
|
|
if msgLen < 8 || blockDescLen < 16 || msgLen-blockDescLen != 6 || blockDescLen > (3*16) {
|
|
m.sendScsiError(csw.StatusFailed, scsi.SenseIllegalRequest, scsi.SenseCodeInvalidFieldInCDB)
|
|
return
|
|
}
|
|
|
|
// descEnd marks the end of the last full block descriptor in this packet
|
|
descEnd := int(blockDescLen + 8)
|
|
|
|
// Unmap the blocks we can from this packet
|
|
for i := 8; i < descEnd; i += 16 {
|
|
err := m.unmapBlocksFromDescriptor(b[i:], uint64(m.blockCount))
|
|
if err != nil {
|
|
// TODO: Might need a better error code here for device errors?
|
|
m.sendScsiError(csw.StatusFailed, scsi.SenseVolumeOverflow, scsi.SenseCodeLBAOutOfRange)
|
|
return
|
|
}
|
|
}
|
|
|
|
// FIXME: We need to handle erase block alignment
|
|
|
|
m.sentBytes += uint32(len(b))
|
|
if m.sentBytes >= m.totalBytes {
|
|
// Order 66 complete, send CSW to establish galactic empire
|
|
m.state = mscStateStatus
|
|
m.run([]byte{}, true)
|
|
}
|
|
}
|
|
|
|
func (m *msc) unmapBlocksFromDescriptor(b []byte, numBlocks uint64) error {
|
|
blockCount := binary.BigEndian.Uint32(b[8:12])
|
|
if blockCount == 0 {
|
|
// No blocks to unmap. Explicitly not an error per the spec
|
|
return nil
|
|
}
|
|
// This is technically a 64-bit LBA, but we can't address that many bytes
|
|
// let alone blocks, so we just use the lower 32 bits
|
|
lba := binary.BigEndian.Uint32(b[4:8])
|
|
|
|
// Make sure the unmap command doesn't extend past the end of the volume
|
|
if lba+blockCount > m.blockCount {
|
|
return errorLBAOutOfRange
|
|
}
|
|
|
|
// Convert the emulated block size to the underlying hardware erase block size
|
|
blockStart := int64(lba*m.blockSizeUSB) / m.dev.EraseBlockSize()
|
|
rawBlockCount := int64(blockCount*m.blockSizeUSB) / m.dev.EraseBlockSize()
|
|
|
|
// Unmap the blocks
|
|
return m.dev.EraseBlocks(blockStart, rawBlockCount)
|
|
}
|