usb/msc: wait for interrupt instead of polling a flag

This should make usb/msc a whole lot more efficient by pausing the
worker goroutine and waiting for an interrupt to unpause it instead of
waiting in a loop and sleeping for 0.1ms each cycle.

In other words, this should make it both faster (no unnecessary delay
due to the time.Sleep) and more efficient (no polling).
This commit is contained in:
Ayke van Laethem
2025-08-27 09:39:28 +02:00
parent fb2755d18f
commit 88db2c3594
2 changed files with 26 additions and 26 deletions
+22 -22
View File
@@ -1,12 +1,12 @@
package msc
import (
"internal/task"
"machine"
"machine/usb"
"machine/usb/descriptor"
"machine/usb/msc/csw"
"machine/usb/msc/scsi"
"time"
)
type mscState uint8
@@ -26,14 +26,14 @@ const (
var MSC *msc
type msc struct {
buf []byte // Buffer for incoming/outgoing data
blockCache []byte // Buffer for block read/write data
taskQueued bool // Flag to indicate if the buffer has a task queued
rxStalled bool // Flag to indicate if the RX endpoint is stalled
txStalled bool // Flag to indicate if the TX endpoint is stalled
maxPacketSize uint32 // Maximum packet size for the IN endpoint
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
buf []byte // Buffer for incoming/outgoing data
blockCache []byte // Buffer for block read/write data
taskWaiter task.Waiter // Waiter for events outside interrupt context
rxStalled bool // Flag to indicate if the RX endpoint is stalled
txStalled bool // Flag to indicate if the TX endpoint is stalled
maxPacketSize uint32 // Maximum packet size for the IN endpoint
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
cbw *CBW // Last received Command Block Wrapper
queuedBytes uint32 // Number of bytes queued for sending
@@ -120,21 +120,21 @@ func newMSC(dev machine.BlockDevice) *msc {
func (m *msc) processTasks() {
// Process tasks that cannot be done in an interrupt context
for {
if m.taskQueued {
cmd := m.cbw.SCSICmd()
switch cmd.CmdType() {
case scsi.CmdWrite:
m.scsiWrite(cmd, m.buf)
case scsi.CmdUnmap:
m.scsiUnmap(m.buf)
}
// Wait for the next task to arrive.
m.taskWaiter.Wait()
// Acknowledge the received data from the host
m.queuedBytes = 0
m.taskQueued = false
machine.AckUsbOutTransfer(usb.MSC_ENDPOINT_OUT)
cmd := m.cbw.SCSICmd()
switch cmd.CmdType() {
case scsi.CmdWrite:
m.scsiWrite(cmd, m.buf)
case scsi.CmdUnmap:
m.scsiUnmap(m.buf)
}
time.Sleep(100 * time.Microsecond)
// Acknowledge the received data from the host
m.queuedBytes = 0
m.taskWaiter.Done()
machine.AckUsbOutTransfer(usb.MSC_ENDPOINT_OUT)
}
}
+4 -4
View File
@@ -251,7 +251,7 @@ func (m *msc) scsiQueueTask(cmdType scsi.CmdType, b []byte) bool {
}
// Save the incoming data in our buffer for processing outside of interrupt context.
if m.taskQueued {
if m.taskWaiter.Working() {
// If we already have a full task queue we can't accept this data
m.sendScsiError(csw.StatusFailed, scsi.SenseAbortedCommand, scsi.SenseCodeMsgReject)
return true
@@ -268,14 +268,14 @@ func (m *msc) scsiQueueTask(cmdType scsi.CmdType, b []byte) bool {
case scsi.CmdWrite:
// 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)) || (m.sentBytes+m.queuedBytes >= m.transferBytes) {
m.taskQueued = true
m.taskWaiter.Resume()
}
case scsi.CmdUnmap:
m.taskQueued = true
m.taskWaiter.Resume()
}
// Don't acknowledge the incoming data until we can process it.
return !m.taskQueued
return !m.taskWaiter.Working()
}
func (m *msc) sendScsiError(status csw.Status, key scsi.Sense, code scsi.SenseCode) {