25 Mar

The host uses a READ CAPACITY command to learn how many bytes a device can store. In the data-transport phase, the device returns a structure that contains the LBA of the last block in the media and the number of bytes per block. Note that the command requests the LBA of the last block, not the number of blocks in the media. The first LBA is zero, so the LBA of the last block equals the number of blocks – 1.

There are two READ CAPACITY commands: READ CAPACITY(10) and READ CAPACITY(16). The commands differ in the length of the fields in the command block and response structure. SBC devices must support READ CAPACITY(10).

A MultiMediaCard host uses information in the card’s CSD register to calculate the LBA of the final block. The MultiMediaCard specification shows how to do the calculations. The fields in the CSD register aren’t all byte-aligned, so obtaining a value sometimes requires reading multiple bytes and selecting bits from each to obtain the value of interest.

DWORD gblNumBLKS=0×00;
DWORD gblBLKLen=0×00;
void MSDReadCapacityHandler()
dword one = 0×1;
dword C_size;
dword C_mult;
dword Mult;
dword C_size_U;
dword C_size_H;
dword C_size_L;
dword C_mult_H;
dword C_mult_L;
dword C_Read_Bl_Len;
// The block length is in byte 5, bits 3..0 in the MultiMediaCard’s CSD register.
// Block length = 2^(C_Read_Bl_Len)
// If block length = 512, C_Read_Bl_Len = 9 because 2^9 = 512.
C_Read_Bl_Len = gblCSDReg._byte[5] & 0×0f;
// Shift left C_Read_Bl_Len positions to get the block-length value.
gblBLKLen._dword = one << C_Read_Bl_Len;
// The C_size value is 12 bits.
// The two MSbs are in byte 6, bits 1..0.
// The next 8 bits are in byte 7.
// The two LSbs are in byte 8, bits 7..6.
C_size_U = gblCSDReg._byte[6] & 0×03;
C_size_H = gblCSDReg._byte[7];
C_size_L = (gblCSDReg._byte[8]&0xC0) >> 6;
C_size = (C_size_U<<10) | (C_size_H<<2) | (C_size_L);
// C_mult is a 3-bit value stored in two bytes.
// The two MSbs are in byte 9, bits 1..0.
// The LSb is in byte 10, bit 7.
C_mult_H = gblCSDReg._byte[9] & 0×03;
C_mult_L = (gblCSDReg._byte[10] & 0×80) >> 7;
C_mult = (C_mult_H << 1 ) | C_mult_L;

// See the MultiMediaCard spec, section 5.3, for the calculations below.
Mult = one << (C_mult + 2);
// Return a value equal to the last LBA – 1.
gblNumBLKS._dword = Mult * (C_size + 1) – 1;
// Place gblNumBLKS and gblBLKLen in msd_buffer for sending to the host.
msd_buffer[0] = gblNumBLKS.v[3];
msd_buffer[1] = gblNumBLKS.v[2];
msd_buffer[2] = gblNumBLKS.v[1];
msd_buffer[3] = gblNumBLKS.v[0];
msd_buffer[4] = gblBLKLen.v[3];
msd_buffer[5] = gblBLKLen.v[2];
msd_buffer[6] = gblBLKLen.v[1];
msd_buffer[7] = gblBLKLen.v[0];
// Set fields in the CSW.
msd_csw.dCSWDataResidue = 0×08; // Number of bytes in the response.
msd_csw.bCSWStatus = 0×00; // Success.

A host issues the START STOP UNIT command to request to change the device’s power condition to active, idle, or standby and to request the device to load or eject its storage media. The command has no data-transport phase. SBC devices aren’t required to support this command.

A host sends a SYNCHRONIZE CACHE command to request the device to ensure that the specified sectors on the media and in any non-volatile cache contain the most recent data. The SYNCHRONIZE CACHE(10) and SYNCHRONIZE CACHE(16) commands vary in the size of the fields in the command block. This command has no data-transport phase. SBC devices aren’t required to support this command.

The VERIFY command requests the device to test one or more sectors. If the BYTCHK bit in the command block equals zero, the device should attempt to read from the specified locations. If BYTCHK = 1, the host sends data in the data-transport phase, and the device should verify that the received data matches what is stored in the device. If the media uses error-checking protection information as specified in the response to an INQUIRY command, the verify operation checks this information as well.

There are four VERIFY commands: VERIFY(10), VERIFY(12), VERIFY( 16), and VERIFY(32). The commands vary in the size of the fields in the command block. The VERIFY(32) command block also contains additional fields. Support for this command is optional for SBC devices.

The WRITE commands are the complements of the READ commands. The host issues a WRITE command to request to write a block of data to the device. In the command block, the LOGICAL BLOCK ADDRESS field specifies the LBA of the first block to write to, and the TRANSFER LENGTH field contains the number of blocks to write.

There are five WRITE commands: WRITE(6), WRITE(10), WRITE(12), WRITE(16), and WRITE(32). The commands vary in the sizes of the logical- block-address and transfer-length fields and in the quantity and type of status and control information included in the command block. WRITE(6) and WRITE(10) are mandatory for SBC devices (except of course for read-only media). The specification recommends migrating all code from WRITE(6) to WRITE(10), but a host might still attempt to use WRITE(6). The Windows USB mass-storage driver uses WRITE(10).

After receiving a WRITE(10) command block, a device should receive the data to write in the data-transport phase and should write the received data to the specified locations in the storage media. The device doesn’t have to know or care what is in the received blocks. All the device needs is a block number and data to write to the block.

the MSDDataOut function from Chapter 3 to write the received data to the storage media.

void MSDWriteHandler()
byte * adr;
byte Flags;
word i;
dword sectorNumber;
SDC_Error status = sdcValid;
WORD TransferLength;
// The command block stores MSB first. Device firmware stores LSB first.
// The starting LBA to write to is in bytes 2-5 in the command block.
LBA.v[3] = gblCBW.CBWCB[2];
LBA.v[2] = gblCBW.CBWCB[3];
LBA.v[1] = gblCBW.CBWCB[4];
LBA.v[0] = gblCBW.CBWCB[5];
// The number of blocks being written is in bytes 7-8 of the CBW.
TransferLength.v[1] = gblCBW.CBWCB[7];
TransferLength.v[0] = gblCBW.CBWCB[8];
// Set bCSWStatus to a default value.
msd_csw.bCSWStatus = 0×0; // Success.

while (TransferLength._word > 0) {
// Read data received from the USB host and write the contents to the media
// until TransferLength = 0.
// Set dCSWDataResidue to the media’s block size.
msd_csw.dCSWDataResidue = 512;
while (msd_csw.dCSWDataResidue > 0)
// The MSDDataOut function reads data from the USB host.
// dCSWDataResidue is decremented as the data is read.
// Continue until dCSWDataResidue = 0.
if (IsWriteProtected()) {
// If the media is write protected, set the sense data
// and bCSWStatus in the CSW.
gblSenseData.SenseKey = S_NOT_READY;
msd_csw.bCSWStatus = 0×01; // Command failed.
} else {
// The received data is in msd_buffer.
// Write the data to the media beginning at the LBA specified in the CBW.
status = SectorWrite((LBA._dword), (byte*)&msd_buffer[0]);

if (status) {
// The sector write failed.
msd_csw.bCSWStatus = 0×01;
// Store sense data to describe the error.
gblSenseData.SenseKey = S_MEDIUM_ERROR;
// Increment the LBA. Decrement the number of blocks remaining to write.
if (TransferLength._word > 0){
// There is more data to receive.
// Configure the endpoint’s buffer descriptor to prepare for the data.
MSD_BD_OUT.ADR = (byte*)&msd_buffer[0];
} else {
// All of the data has been received.
// Configure the endpoint’s buffer descriptor to prepare for the next command.
MSD_BD_OUT.Cnt = sizeof(msd_cbw);
MSD_BD_OUT.ADR = (byte*)&msd_cbw;
} // End: while (TransferLength._word > 0)

Random Posts

No comments yet

Leave a Reply

You must be logged in to post a comment.