fix: separate host expected bytes from device intended bytes

This commit is contained in:
Michael Smith
2025-12-03 09:50:03 -05:00
committed by Ron Evans
parent 908fa84d49
commit bd19f3baa3
5 changed files with 34 additions and 29 deletions
+11 -10
View File
@@ -35,12 +35,12 @@ type msc struct {
respStatus csw.Status // Response status for the last command respStatus csw.Status // Response status for the last command
sendZLP bool // Flag to indicate if a zero-length packet should be sent before sending CSW sendZLP bool // Flag to indicate if a zero-length packet should be sent before sending CSW
cbw *CBW // Last received Command Block Wrapper cbw *CBW // Last received Command Block Wrapper
queuedBytes uint32 // Number of bytes queued for sending queuedBytes uint32 // Number of bytes queued for sending
sentBytes uint32 // Number of bytes sent sentBytes uint32 // Number of bytes sent
totalBytes uint32 // Total bytes to send transferBytes uint32 // Total bytes to send
cswBuf []byte // CSW response buffer cswBuf []byte // CSW response buffer
state mscState state mscState
maxLUN uint8 // Maximum Logical Unit Number (n-1 for n LUNs) maxLUN uint8 // Maximum Logical Unit Number (n-1 for n LUNs)
dev machine.BlockDevice dev machine.BlockDevice
@@ -160,8 +160,9 @@ func (m *msc) sendUSBPacket(b []byte) {
func (m *msc) sendCSW(status csw.Status) { func (m *msc) sendCSW(status csw.Status) {
// Generate CSW packet into m.cswBuf and send it // Generate CSW packet into m.cswBuf and send it
residue := uint32(0) residue := uint32(0)
if m.totalBytes >= m.sentBytes { expected := m.cbw.transferLength()
residue = m.totalBytes - m.sentBytes if expected >= m.sentBytes {
residue = expected - m.sentBytes
} }
m.cbw.CSW(status, residue, m.cswBuf) m.cbw.CSW(status, residue, m.cswBuf)
m.state = mscStateStatusSent m.state = mscStateStatusSent
@@ -245,7 +246,7 @@ func (m *msc) run(b []byte, isEpOut bool) bool {
// Move on to the data transfer phase next go around (after sending the first message) // Move on to the data transfer phase next go around (after sending the first message)
m.state = mscStateData m.state = mscStateData
m.totalBytes = cbw.transferLength() m.transferBytes = cbw.transferLength()
m.queuedBytes = 0 m.queuedBytes = 0
m.sentBytes = 0 m.sentBytes = 0
m.respStatus = csw.StatusPassed m.respStatus = csw.StatusPassed
@@ -281,7 +282,7 @@ func (m *msc) run(b []byte, isEpOut bool) bool {
// to cycle back through this block, e.g. with TEST UNIT READY which sends only a CSW after // to cycle back through this block, e.g. with TEST UNIT READY which sends only a CSW after
// setting the sense key/add'l code/qualifier internally // setting the sense key/add'l code/qualifier internally
if m.state == mscStateStatus && !m.txStalled { if m.state == mscStateStatus && !m.txStalled {
if m.totalBytes > m.sentBytes && m.cbw.isIn() { if m.cbw.transferLength() > m.sentBytes && m.cbw.isIn() {
// 6.7.2 The Thirteen Cases - Case 5 (Hi > Di): STALL before status // 6.7.2 The Thirteen Cases - Case 5 (Hi > Di): STALL before status
m.stallEndpoint(usb.MSC_ENDPOINT_IN) m.stallEndpoint(usb.MSC_ENDPOINT_IN)
} else if m.sendZLP { } else if m.sendZLP {
+15 -11
View File
@@ -21,9 +21,9 @@ func (m *msc) scsiCmdBegin() {
return return
} }
if m.totalBytes > 0 && m.cbw.isOut() { if m.transferBytes > 0 && m.cbw.isOut() {
// Reject any other multi-packet commands // Reject any other multi-packet commands
if m.totalBytes > m.maxPacketSize { if m.transferBytes > m.maxPacketSize {
m.sendScsiError(csw.StatusFailed, scsi.SenseIllegalRequest, scsi.SenseCodeInvalidCmdOpCode) m.sendScsiError(csw.StatusFailed, scsi.SenseIllegalRequest, scsi.SenseCodeInvalidCmdOpCode)
return return
} else { } else {
@@ -53,7 +53,7 @@ func (m *msc) scsiCmdBegin() {
} }
if len(m.buf) == 0 { if len(m.buf) == 0 {
if m.totalBytes > 0 { if m.transferBytes > 0 {
// 6.7.2 The Thirteen Cases - Case 4 (Hi > Dn) // 6.7.2 The Thirteen Cases - Case 4 (Hi > Dn)
// https://usb.org/sites/default/files/usbmassbulk_10.pdf // https://usb.org/sites/default/files/usbmassbulk_10.pdf
m.sendScsiError(csw.StatusFailed, scsi.SenseIllegalRequest, 0) m.sendScsiError(csw.StatusFailed, scsi.SenseIllegalRequest, 0)
@@ -63,7 +63,7 @@ func (m *msc) scsiCmdBegin() {
m.state = mscStateStatus m.state = mscStateStatus
} }
} else { } else {
if m.totalBytes == 0 { if m.transferBytes == 0 {
// 6.7.1 The Thirteen Cases - Case 2 (Hn < Di) // 6.7.1 The Thirteen Cases - Case 2 (Hn < Di)
// https://usb.org/sites/default/files/usbmassbulk_10.pdf // https://usb.org/sites/default/files/usbmassbulk_10.pdf
m.sendScsiError(csw.StatusFailed, scsi.SenseIllegalRequest, 0) m.sendScsiError(csw.StatusFailed, scsi.SenseIllegalRequest, 0)
@@ -93,7 +93,7 @@ func (m *msc) scsiDataTransfer(b []byte) bool {
// Update our sent bytes count to include the just-confirmed bytes // Update our sent bytes count to include the just-confirmed bytes
m.sentBytes += m.queuedBytes m.sentBytes += m.queuedBytes
if m.sentBytes >= m.totalBytes { if m.sentBytes >= m.transferBytes {
// Transfer complete, send CSW after transfer confirmed // Transfer complete, send CSW after transfer confirmed
m.state = mscStateStatus m.state = mscStateStatus
} else if cmdType == scsi.CmdRead { } else if cmdType == scsi.CmdRead {
@@ -158,8 +158,8 @@ func (m *msc) scsiCmdModeSense(cmd scsi.Cmd) {
// The host allows a good amount of leeway in response size // The host allows a good amount of leeway in response size
// Reset total bytes to what we'll actually send // Reset total bytes to what we'll actually send
if m.totalBytes > respLen { if m.transferBytes > respLen {
m.totalBytes = respLen m.transferBytes = respLen
m.sendZLP = true m.sendZLP = true
} }
@@ -210,7 +210,7 @@ func (m *msc) scsiCmdRequestSense() {
// Set the buffer size to the SCSI sense message size and clear // Set the buffer size to the SCSI sense message size and clear
m.resetBuffer(scsi.RequestSenseRespLen) m.resetBuffer(scsi.RequestSenseRespLen)
m.queuedBytes = scsi.RequestSenseRespLen m.queuedBytes = scsi.RequestSenseRespLen
m.totalBytes = scsi.RequestSenseRespLen m.transferBytes = scsi.RequestSenseRespLen
// 0x70 - current error, 0x71 - deferred error (not used) // 0x70 - current error, 0x71 - deferred error (not used)
m.buf[0] = 0xF0 // 0x70 for current error plus 0x80 for valid flag bit m.buf[0] = 0xF0 // 0x70 for current error plus 0x80 for valid flag bit
@@ -266,7 +266,7 @@ func (m *msc) scsiQueueTask(cmdType scsi.CmdType, b []byte) bool {
switch cmdType { switch cmdType {
case scsi.CmdWrite: case scsi.CmdWrite:
// If we're writing data wait until we have a full write block of data that can be processed. // If we're writing data wait until we have a full write block of data that can be processed.
if m.queuedBytes == uint32(cap(m.blockCache)) { if m.queuedBytes == uint32(cap(m.blockCache)) || (m.sentBytes+m.queuedBytes >= m.transferBytes) {
m.taskQueued = true m.taskQueued = true
} }
case scsi.CmdUnmap: case scsi.CmdUnmap:
@@ -279,7 +279,11 @@ func (m *msc) scsiQueueTask(cmdType scsi.CmdType, b []byte) bool {
func (m *msc) sendScsiError(status csw.Status, key scsi.Sense, code scsi.SenseCode) { func (m *msc) sendScsiError(status csw.Status, key scsi.Sense, code scsi.SenseCode) {
// Generate CSW into m.cswBuf // Generate CSW into m.cswBuf
residue := m.totalBytes - m.sentBytes expected := m.cbw.transferLength()
residue := uint32(0)
if expected > m.sentBytes {
residue = expected - m.sentBytes
}
// Prepare to send CSW // Prepare to send CSW
m.sendZLP = true // Ensure the transaction is signaled as ended before a CSW is sent m.sendZLP = true // Ensure the transaction is signaled as ended before a CSW is sent
@@ -291,7 +295,7 @@ func (m *msc) sendScsiError(status csw.Status, key scsi.Sense, code scsi.SenseCo
m.addlSenseCode = code m.addlSenseCode = code
m.addlSenseQualifier = 0x00 // Not used m.addlSenseQualifier = 0x00 // Not used
if m.totalBytes > 0 && residue > 0 { if expected > 0 && residue > 0 {
if m.cbw.isIn() { if m.cbw.isIn() {
m.stallEndpoint(usb.MSC_ENDPOINT_IN) m.stallEndpoint(usb.MSC_ENDPOINT_IN)
} else { } else {
+2 -2
View File
@@ -136,13 +136,13 @@ func (m *msc) scsiEvpdInquiry(cmd scsi.Cmd, pageCode uint8) {
// Set total bytes to the length of our response // Set total bytes to the length of our response
m.queuedBytes = uint32(len(m.buf)) m.queuedBytes = uint32(len(m.buf))
m.totalBytes = uint32(len(m.buf)) m.transferBytes = uint32(len(m.buf))
} }
func (m *msc) scsiStdInquiry(cmd scsi.Cmd) { func (m *msc) scsiStdInquiry(cmd scsi.Cmd) {
m.resetBuffer(scsi.InquiryRespLen) m.resetBuffer(scsi.InquiryRespLen)
m.queuedBytes = scsi.InquiryRespLen m.queuedBytes = scsi.InquiryRespLen
m.totalBytes = scsi.InquiryRespLen m.transferBytes = scsi.InquiryRespLen
// byte 0 - Device Type (0x00 for direct access block device) // byte 0 - Device Type (0x00 for direct access block device)
// byte 1 - Removable media bit // byte 1 - Removable media bit
+5 -5
View File
@@ -12,7 +12,7 @@ func (m *msc) scsiCmdReadWrite(cmd scsi.Cmd) {
status := m.validateScsiReadWrite(cmd) status := m.validateScsiReadWrite(cmd)
if status != csw.StatusPassed { if status != csw.StatusPassed {
m.sendScsiError(status, scsi.SenseIllegalRequest, scsi.SenseCodeInvalidCmdOpCode) m.sendScsiError(status, scsi.SenseIllegalRequest, scsi.SenseCodeInvalidCmdOpCode)
} else if m.totalBytes > 0 { } else if m.transferBytes > 0 {
if cmd.CmdType() == scsi.CmdRead { if cmd.CmdType() == scsi.CmdRead {
m.scsiRead(cmd) m.scsiRead(cmd)
} else { } else {
@@ -28,7 +28,7 @@ func (m *msc) scsiCmdReadWrite(cmd scsi.Cmd) {
func (m *msc) validateScsiReadWrite(cmd scsi.Cmd) csw.Status { func (m *msc) validateScsiReadWrite(cmd scsi.Cmd) csw.Status {
blockCount := cmd.BlockCount() blockCount := cmd.BlockCount()
// CBW wrapper transfer length // CBW wrapper transfer length
if m.totalBytes == 0 { if m.transferBytes == 0 {
// If the SCSI command's block count doesn't loosely match the wrapper's transfer length something's wrong // If the SCSI command's block count doesn't loosely match the wrapper's transfer length something's wrong
if blockCount > 0 { if blockCount > 0 {
return csw.StatusPhaseError return csw.StatusPhaseError
@@ -50,7 +50,7 @@ func (m *msc) validateScsiReadWrite(cmd scsi.Cmd) csw.Status {
// https://usb.org/sites/default/files/usbmassbulk_10.pdf // https://usb.org/sites/default/files/usbmassbulk_10.pdf
return csw.StatusFailed return csw.StatusFailed
} }
if m.totalBytes/blockCount == 0 { if m.transferBytes/blockCount == 0 {
// Block size shouldn't be small enough to round to zero // Block size shouldn't be small enough to round to zero
// 6.7.2 The Thirteen Cases - Case 7 (Hi < Di) READ(10) or // 6.7.2 The Thirteen Cases - Case 7 (Hi < Di) READ(10) or
// 6.7.3 The Thirteen Cases - Case 13 (Ho < Do) WRITE(10) // 6.7.3 The Thirteen Cases - Case 13 (Ho < Do) WRITE(10)
@@ -103,7 +103,7 @@ func (m *msc) writeBlock(b []byte, lba, offset uint32) (n int, err error) {
func (m *msc) scsiRead(cmd scsi.Cmd) { func (m *msc) scsiRead(cmd scsi.Cmd) {
// Make sure we don't exceed the buffer size // Make sure we don't exceed the buffer size
readEnd := m.totalBytes - m.sentBytes readEnd := m.transferBytes - m.sentBytes
if readEnd > m.maxPacketSize { if readEnd > m.maxPacketSize {
readEnd = m.maxPacketSize readEnd = m.maxPacketSize
} }
@@ -136,7 +136,7 @@ func (m *msc) scsiWrite(cmd scsi.Cmd, b []byte) {
m.sentBytes += uint32(len(b)) m.sentBytes += uint32(len(b))
} }
if m.sentBytes >= m.totalBytes { if m.sentBytes >= m.transferBytes {
// Data transfer is complete, send CSW // Data transfer is complete, send CSW
m.state = mscStateStatus m.state = mscStateStatus
m.run([]byte{}, true) m.run([]byte{}, true)
+1 -1
View File
@@ -60,7 +60,7 @@ func (m *msc) scsiUnmap(b []byte) {
// FIXME: We need to handle erase block alignment // FIXME: We need to handle erase block alignment
m.sentBytes += uint32(len(b)) m.sentBytes += uint32(len(b))
if m.sentBytes >= m.totalBytes { if m.sentBytes >= m.transferBytes {
// Order 66 complete, send CSW to establish galactic empire // Order 66 complete, send CSW to establish galactic empire
m.state = mscStateStatus m.state = mscStateStatus
m.run([]byte{}, true) m.run([]byte{}, true)