The Mass Storage Class (MSC) driver uses the USB Mass Storage Class Bulk Only Transport (BOT) specification. This, in turn, simulates a SCSI disc controller and so many SCSI terms are used here.
The driver relies on user-supplied sector read/write routines. In order to reduce the time spent in the USB interrupt handler, the sector read/write routines are accessed by a separate task. A consequence of this is that if the USB and the device's application share access to the drive, e.g. through the FAT file system, semaphores must be used for disk access.
Note that the file %FATfiler%\SDspi.fth of 19 November 2008 or later is required for use with SD/MMC cards that use the generic SPI driver.
The file system needs these words to access the mass storage:
SecRead |
( addr sector dev -- ) Reads a sector from the specified device. |
SecWrite |
( addr sector dev -- ) Writes a sector to the specified device. |
MassInit |
( -- ) Initializes mass storage access. |
MassTerm |
( -- ) Terminates mass storage access. |
CDin? |
( -- flag ) Return true if card/device ready. |
WPin? |
( -- flag ) Return true if card/device write protected. |
To simplify the code, the raw disc read/write interface treats all
read/write errors as fatal, and THROW
s on error. Retries
should be accommodated within the sector read/write code.
The code is sensitive to the condition of the equate
usbDMA?
. We strongly recommend that this is set
non-zero if your hardware supports DMA for USB transfers.
The Mass Storage Class (MSC) bulk endpoint actions are controlled by a state machine.
: [sm \ -- 0
Starts the definition of a state machine's states.
: smState \ n -- n+1
Defines the next state as an EQU
and increments the
state number.
: sm] \ n --
Finishes the state machine and defines an equate of the
number of states.
The MSC bulk endpoint state machine.
[sm smState smCBW \ wait for a CBW from host, idle state smState smDOUT \ receive data blocks (sectors) from host smState smDIN \ transmit data blocks (sectors) to host smState smCSW \ send CSW to host smstate smCIN \ send command response to host smState smStalled \ both bulk endpoints stalled sm] #MSCsm \ -- n
smCBW value MSCsm# \ -- state
MSC Bulk endpoint state.
: .MSCstate \ --
Display the current state of the MSC state machine.
struct /DataPtr \ -- len
describes the layout of length/addr data pointers.
2variable mscBulkData \ -- addr
The data pointer for the remaining data of the bulk sector transfer.
Note that this may be only part of the transfer, especially
for multi-sector disk reads and writes.
variable mscBulkLen \ -- addr
Holds the remaining length of the complete transfer. Before
the transfer starts, the data corresponds to the Dx value
in the "13 conditions" tests.
variable mscHostLen \ -- addr
Holds the transfer size read from the CBW data block. This
corresponds to the Hx value in the "13 conditions" tests.
: initBulk \ addr len --
Set the data pointers for a bulk transfer.
struct /CBW \ -- len
SCSI Command Block Wrapper (CBW). The CBW fields are little
endian for USB, but the CDB fields are big-endian SCSI data.
struct /CSW \ -- len
SCSI Command Status Wrapper (CSW). Little-endian data for USB.
According to the specs (see the MSC overview), there should only be one CBW outstanding until the corresponding CSW has been sent. Consequently, we do not need to queue commands, and only need one set of buffers.
MscBoutEP BulkDD:
Bulk OUT DMA descriptor.
MscBinEP BulkDD:
Bulk IN DMA descriptor.
UAlign /MaxPacket buffer: CBWbuff \ -- addr
Holds a packet expected to be the next CBW. Includes the CDB.
UAlign /MaxPacket buffer: MSCbuff \ -- addr
Holds miscellaneous data in or out that is not sector data.
UAlign /CSW buffer: CSWbuff \ -- addr
Holds the CSW being assembled.
UAlign #512 buffer: USBSecBuff \ -- addr
Single sector buffer.
CBWbuff cbw.CDB constant CDBbuff \ -- addr
The base address of the CDB in CBWbuff
. The first byte
contains the operation code.
0 value SenseCode \ -- 00kkccqq
The REQUEST SENSE command returns data in the KEY, ASC and
ASQ fields. These are merged into the low 24 bits of
SenseCode
.
0x030C00 equ SenseWriteError \ Card fail 0x031100 equ SenseReadError \ Card fail 0x052000 equ SenseInvalidOp \ bad command 0x052400 equ SenseInvalidCDB \ command format bad 0x023A00 equ SenseNotReady \ not ready, no card 0x0B0C00 equ SenseWriteFail \ USB fail timeout 0x0B1100 equ SenseReadFail \ USB fail timeout
: StallMSCin \ --
Stall the bulk IN endpoint.
: StallMSCout \ --
Stall the bulk OUT endpoint.
: BOTStall \ --
Stall the transaction. Which endpoint to stall is determined
by looking at the transfer direction intended by the host.
: readMSCOut \ caddr -- len
Read the bulk OUT endpoint into a buffer of at least
/MaxPacket
bytes and return the number read.
: writeMSCIn \ caddr len --
Write the buffer to the bulk IN endpoint.
Disk read and write is controlled by a task. This is done to prevent the USB interrupt taking too long.
SecRead |
( addr sector dev -- ) Reads a sector from the specified device. |
SecWrite |
( addr sector dev -- ) Writes a sector to the specified device. |
-1 value USBdisk? \ -- flag ; FVD009
Returns true if the USB drive should be available to the USB host.
0 value DriveBusy? \ -- x
Returns the current drive command and direction.
$100 DirIn or equ SecReadCmd \ -- x
Indicates that sectors are being read.
$100 DirOut or equ SecWriteCmd \ --
Indicates that sectors are being written.
0 value DriveComplete \ -- x
Set non-zero when the USB system has finished a command.
0 value FirstSector \ -- u
First sector to be read/written.
0 value NumSectors \ -- u
Number of sectors to be read/written.
0 value DriveStatus \ -- x
Holds the drive action completion status. This is read
by the USB interrupt handler to decide how to terminate
the action and is cleared when termination has been handled.
Negative values indicate errors, 0 is success
0 equ DrvOK \ -- x
Drive status good.
-128 equ DrvWriteFail \ --
Fatal write error.
-129 equ DrvReadFail
Fatal read error.
-130 equ DrvBadCmd
Invalid drive command.
-131 equ DrvAborted.
Command aborted by USB
-132 equ DrvUSBto
Command aborted by USB timeout.
: setDriveCmd \ x --
Set the drive command and clear the status.
: Sector<>USB \ -- ior ; 0=success, nz=timeout
Send or receive a 512 byte sector data to/from USBSecBuff
.
: -BulkData \ --
Acknowledge a sector transfer by zeroing the transfer address
and length.
: Disk>USB \ --
Read the given sectors from the disk.
: USB>Disk \ --
Write the given sectors to the disk.
: DriveIn? \ -- flag
Return true if the drive is in.
: ?MSCdrive \ --
Wait for drive insertion after removal.
: WaitSecCmd \ --
Wait for a sector command
: WaitDrvComplete \ --
Wait for the drive action on the USB side to complete.
Task USBdiskTask \ -- addr
The task control block for the MSC sector transfer task.
0 value USBdiskTask? \ -- x
Returns non-zero when the task is running.
: +USBdiskTask \ --
Start the USB disk task if not already running.
: -USBdiskTask \ --
Stop the USB disk task if not already stopped.
: NoResidue \ --
Zero the CSWbuff csw.Residue
field.
: initCSW \ --
Set up the CSW from the CBW. The residue field is set to the
CBW data length field. For actions that complete immediately,
this should be cleared by NoResidue
above.
: sendCSW \ --
Send the CSW as is.
: badRWphase \ --
Perform the bad R/W setup actions, returning a phase error
status (0x02).
: badRWfail \ --
Perform the bad R/W setup actions, returning a fail status
(0x01).
The USB specifications define the host and device transactions
in terms of 13 cases. The file usbmassbulk_10.pdf
contains the gory details and can be downloaded from
www.usb.org
. You will probably use that document
in conjunction with INF-8070.pdf to be found at
ftp://ftp.seagate.com/sff/INF-8070.PDF
.
: mscHostDir@ \ -- $00/$80
Return the Host direction, where $80=IN to host.
: mscHostLen@ \ -- len
Return the host length from the CBW.
: NoMscDevData? \ -- ior ; 0=success
Checks cases 1, 4, and 9. On an error (Hx>0), a pipe is
stalled and a failure CSW sent. On success, no action is
taken.
: MscDevDataIN-1? \ caddr len -- caddr len' 0 | ior ; 0=success
Checks cases 2, 5, 6, 7, and 10. On error, appropriate action
is performed. On success, no action is taken. This version is
used for short responses less than /MaxPacket
, when
data is transmitted for case 5.
: MscDevDataIN-2? \ len -- ior ; 0=success
Checks cases 2, 5, 6, 7, and 10. On error, appropriate action
is performed. On success, no action is taken. This version
stalls and sends no data for case 5.
: MscDevDataOUT? \ len -- ior ; 0=success
Checks cases 3, 8, 11, 12, and 13. On error, appropriate action
is performed. On success, no action is taken.
2variable mscRespCIN \ -- addr
Contains a len/addr pair for command responses,
: -RespCIN \ --
Mark no command response data, and switch to a CSW send.
: +RespCIN \ caddr len --
Trigger a command response phase.
: doTUR \ --
Perform the TEST UNIT READY actions.
#18 equ /SenseData \ -- len
Size of the response to the REQUEST SENSE command.
create SenseTemplate \ -- addr
Holds the 18 byte response to the REQUEST SENSE command.
The $FF bytes are filled in with data from SenseCode
.
: doRS \ --
Respond to the REQUEST SENSE command.
#36 equ /InqData \ -- len
Size of returned INQUIRY data.
create InqData \ -- addr
The returned INQUIRY data.
: doINQ \ --
Return the INQUIRY data.
: doRC10 \ --
Performs the READ CAPACITY (10) command operations.
: initMSCrw \ --
Initialise the read/write data from the CDBbuff
: +DiskRD \ --
Start the disk task read.
: +DiskWR \ --
Start the disk task write.
: doRD10 \ --
Performs the READ (10) command operations.
: doWR10 \ --
Performs the WRITE (10) command operations.
: doVRFY10 \ --
Performs the VERIFY (10) command operations.
For the moment this is a dummy that always succeeds unless
no drive is present.
create MS6resp \ -- addr
The MODE SENSE (6) response.
: doMS6 \ --
Performs the MODE SENSE (6) command operations.
Taken from other code - I have no idea!
This command is not needed by Windows, but is required by
Linux.
: doRFC \ --
Performs the READ FORMAT CAPACITY command operations.
This command is supposed to be obsolete or vendor specific,
but Windows may want it. See INF-8070.PDF for the details.
NO LONGER REQUIRED.
This section contains the handlers for the MSC bulk IN and OUT handlers, plus the actions required for set up operations performed on endpoint 0.
: +DiskData \ u --
Add u bytes to the bulk transfer control variables.
: ?AckSector \ --
If a full sector has been transferred, acknowledge it.
: ReadCBW \ --
Read a CBW from the bulk OUT endpoint and process it.
: TransmitCSW \ --
Transmit the CSW on the bulk IN endpoint, and switch
the state machine to wait for the next CBW.
: readMSCdata \ --
Read the bulk OUT endpoint for a transfer operation
: writeMSCdata \ --
Write the bulk IN endpoint for a transfer operation. Note
that this may receive NAK interrupts as it is a Bulk IN endpoint.
: DiscardMSCout \ --
Read and discard data received on the MSC Bulk OUT endpoint.
: MSCBulkOut \ --
Handle bulk OUT actions to the device. Note that this includes NAK
interrupts.
: MSCBulkIn \ --
Handle bulk IN actions to the host. Note that this includes NAK
interrupts.
: doEP2 \ event --
The action of endpoint 2.
: MscFrameCheck \ --
Executed in the frame interrupt.
No longer needed as it is done by default for non-DMA drivers.
: DevMSCreset \ --
Reset the MSC driver. Performed at start up and as an EP0 action.
: startReadMSCdata \ --
Prepare the bulk OUT endpoint for a transfer operation
: endReadMSCdata \ --
Handle the bulk OUT endpoint data just received.
: startWriteMSCdata \ --
Write the bulk IN endpoint for a transfer operation.
: endWriteMSCdata \ --
Update after a bulk IN endpoint data transfer operation.
0 value txCSWdone? \ -- flag
An interlock used to prevent multiple transmissions of
the CSW.
: startTransmitCSW \ --
Transmit the CSW on the bulk IN endpoint.
: endTransmitCSW \ --
CSW transmit done, switch the state machine to wait for the next CBW.
: startRespCIN \ --
Start the command response transfer.
: endRespCIN \ --
Mark the end of the command response stage.
: startReadCBW \ --
prepare for a CBW read.
: endReadCBW \ --
Read a CBW from the bulk OUT endpoint and process it.
: MSCBulkOutNdr \ --
Handle bulk OUT requests (data to the device).
: MSCBulkOutEot \ --
Handle bulk OUT completions (data to the device).
: MSCBulkInNdr \ --
Handle requests for bulk IN actions (data to the host).
: MSCBulkInEOT \ --
Handle completion of bulk IN actions (data to the host).
: doEP2 \ event --
The action of endpoint 2.
: MscFrameCheck \ --
Executed in the frame interrupt. Enables both Bulk endpoints,
and seems to be required. We do not yet know why!
: resetMSCdata \ --
Reset the MSC driver data. Performed at start up and as
an EP0 action.
: DevMSCreset \ --
Reset the MSC driver. Performed as an EP0 action.
: MscReset \ -- flag ; 0=failed
Reset the MSC driver. EP0 action.
: MSCGetMaxLUN \ -- flag ; 0=failed
This device does not distinguish between LUNs. EP0 action.
: MSCdoEP0ClassRTI \ --
Handle the MSC class REQUEST_TO_INTERFACE operations.
: initMSCdata \ --
Initialise the MSC data and task.