3      Copyright (C) Dean Camera, 2009. 
   5   dean [at] fourwalledcubicle [dot] com 
   6       www.fourwalledcubicle.com 
  10   Copyright 2009  Dean Camera (dean [at] fourwalledcubicle [dot] com) 
  12   Permission to use, copy, modify, and distribute this software 
  13   and its documentation for any purpose and without fee is hereby 
  14   granted, provided that the above copyright notice appear in all 
  15   copies and that both that the copyright notice and this 
  16   permission notice and warranty disclaimer appear in supporting 
  17   documentation, and that the name of the author not be used in 
  18   advertising or publicity pertaining to distribution of the 
  19   software without specific, written prior permission. 
  21   The author disclaim all warranties with regard to this 
  22   software, including all implied warranties of merchantability 
  23   and fitness.  In no event shall the author be liable for any 
  24   special, indirect or consequential damages or any damages 
  25   whatsoever resulting from loss of use, data or profits, whether 
  26   in an action of contract, negligence or other tortious action, 
  27   arising out of or in connection with the use or performance of 
  33  *  Mass Storage Device commands, to issue MSD commands to the device for 
  34  *  reading device status, capacity, and other characteristics. This file 
  35  *  also contains block read and write functions, so that device blocks 
  36  *  can be read and written. In general, these functions would be chained 
  37  *  to a FAT library to give file-level access to an attached device's contents. 
  39  *  \note Many Mass Storage devices on the market are non-compliant to the 
  40  *        specifications and thus can proove difficult to interface with. It 
  41  *        may be neccesary to retry the functions in the module several times 
  42  *        after they have returned and error to successfully send the command 
  43  *        to the device. Some devices may also need to have the stream function 
  44  *        timeout period extended beyond 100ms by defining USB_STREAM_TIMEOUT_MS 
  45  *        to a larger value in the project makefile and passing it to the compiler 
  49 #define  INCLUDE_FROM_MASSSTORE_COMMANDS_C 
  50 #include "MassStoreCommands.h" 
  53 /** Current CBW to send to the device. This is automatically filled by the routines 
  54  *  in this file and is not externally accessable. 
  56 static CommandBlockWrapper_t  SCSICommandBlock
; 
  58 /** Current CSW received from the device. This is automatically filled by the routines 
  59  *  in this file and is externally accessable so that the return codes may be checked. 
  61 CommandStatusWrapper_t        SCSICommandStatus
; 
  63 /** Current Tag value used in issued CBWs to the device. This is automatically incremented 
  64  *  by the routines in this file, and is not externally accessable. 
  66 static uint32_t               MassStore_Tag 
= 1; 
  69 /** Routine to send the current CBW to the device, and increment the Tag value as needed. 
  71  *  \return A value from the Pipe_Stream_RW_ErrorCodes_t enum 
  73 static uint8_t MassStore_SendCommand(void) 
  75         uint8_t ErrorCode 
= PIPE_RWSTREAM_ERROR_NoError
; 
  77         /* Each transmission should have a unique tag value, excluding valued 0 and 0xFFFFFFFF */ 
  78         if (++MassStore_Tag 
== 0xFFFFFFFF) 
  81         /* Select the OUT data pipe for CBW transmission */ 
  82         Pipe_SelectPipe(MASS_STORE_DATA_OUT_PIPE
); 
  85         /* Write the CBW command to the OUT pipe */ 
  86         if ((ErrorCode 
= Pipe_Write_Stream_LE(&SCSICommandBlock
, sizeof(CommandBlockWrapper_t
))) != PIPE_RWSTREAM_ERROR_NoError
) 
  89         /* Send the data in the OUT pipe to the attached device */ 
  90         Pipe_ClearCurrentBank(); 
  92         /* Some buggy devices require a delay here before the pipe freezing or they will lock up */ 
  95         /* Freeze pipe after use */ 
  98         return PIPE_RWSTREAM_ERROR_NoError
; 
 101 /** Waits until the attached device is ready to accept data following a CBW, checking 
 102  *  to ensure that the device has not stalled the transaction. 
 104  *  \return A value from the Pipe_Stream_RW_ErrorCodes_t enum 
 106 static uint8_t MassStore_WaitForDataReceived(void) 
 108         uint16_t TimeoutMSRem 
= COMMAND_DATA_TIMEOUT_MS
; 
 110         /* Unfreeze the OUT pipe so that it can be checked */ 
 111         Pipe_SelectPipe(MASS_STORE_DATA_OUT_PIPE
); 
 114         /* Select the IN data pipe for data reception */ 
 115         Pipe_SelectPipe(MASS_STORE_DATA_IN_PIPE
); 
 118         /* Wait until data received in the IN pipe */ 
 119         while (!(Pipe_ReadWriteAllowed())) 
 121                 /* Check to see if a new frame has been issued (1ms elapsed) */ 
 122                 if (USB_INT_HasOccurred(USB_INT_HSOFI
)) 
 124                         /* Clear the flag and decrement the timout period counter */ 
 125                         USB_INT_Clear(USB_INT_HSOFI
); 
 128                         /* Check to see if the timeout period for the command has elapsed */ 
 130                           return PIPE_RWSTREAM_ERROR_Timeout
; 
 133                 Pipe_SelectPipe(MASS_STORE_DATA_OUT_PIPE
); 
 135                 /* Check if pipe stalled (command failed by device) */ 
 136                 if (Pipe_IsStalled()) 
 138                         /* Clear the stall condition on the OUT pipe */ 
 139                         MassStore_ClearPipeStall(MASS_STORE_DATA_OUT_PIPE
); 
 141                         return PIPE_RWSTREAM_ERROR_PipeStalled
; 
 144                 Pipe_SelectPipe(MASS_STORE_DATA_IN_PIPE
); 
 146                 /* Check if pipe stalled (command failed by device) */ 
 147                 if (Pipe_IsStalled()) 
 149                         /* Clear the stall condition on the IN pipe */ 
 150                         MassStore_ClearPipeStall(MASS_STORE_DATA_IN_PIPE
); 
 152                         return PIPE_RWSTREAM_ERROR_PipeStalled
; 
 155                 /* Check to see if the device was disconnected, if so exit function */ 
 156                 if (!(USB_IsConnected
)) 
 157                   return PIPE_RWSTREAM_ERROR_DeviceDisconnected
; 
 160         return PIPE_RWSTREAM_ERROR_NoError
; 
 163 /** Sends or receives the transaction's data stage to or from the attached device, reading or 
 164  *  writing to the nominated buffer. 
 166  *  \param  BufferPtr  Pointer to the data buffer to read from or write to 
 168  *  \return A value from the Pipe_Stream_RW_ErrorCodes_t enum 
 170 static uint8_t MassStore_SendReceiveData(void* BufferPtr
) 
 172         uint8_t  ErrorCode 
= PIPE_RWSTREAM_ERROR_NoError
; 
 173         uint16_t BytesRem  
= SCSICommandBlock
.Header
.DataTransferLength
; 
 175         /* Check the direction of the SCSI command data stage */ 
 176         if (SCSICommandBlock
.Header
.Flags 
& COMMAND_DIRECTION_DATA_IN
) 
 178                 /* Select the IN data pipe for data reception */ 
 179                 Pipe_SelectPipe(MASS_STORE_DATA_IN_PIPE
); 
 182                 /* Read in the block data from the pipe */ 
 183                 if ((ErrorCode 
= Pipe_Read_Stream_LE(BufferPtr
, BytesRem
)) != PIPE_RWSTREAM_ERROR_NoError
) 
 188                 /* Select the OUT data pipe for data transmission */ 
 189                 Pipe_SelectPipe(MASS_STORE_DATA_OUT_PIPE
); 
 192                 /* Write the block data to the pipe */ 
 193                 if ((ErrorCode 
= Pipe_Write_Stream_LE(BufferPtr
, BytesRem
)) != PIPE_RWSTREAM_ERROR_NoError
) 
 197         /* Acknowledge the packet */ 
 198         Pipe_ClearCurrentBank(); 
 200         /* Some buggy devices require a delay here before the pipe freezing or they will lock up */ 
 203         /* Freeze used pipe after use */ 
 206         return PIPE_RWSTREAM_ERROR_NoError
; 
 209 /** Routine to receive the current CSW from the device. 
 211  *  \return A value from the Pipe_Stream_RW_ErrorCodes_t enum 
 213 static uint8_t MassStore_GetReturnedStatus(void) 
 215         uint8_t ErrorCode 
= PIPE_RWSTREAM_ERROR_NoError
; 
 217         /* Select the IN data pipe for data reception */ 
 218         Pipe_SelectPipe(MASS_STORE_DATA_IN_PIPE
); 
 221         /* Load in the CSW from the attached device */ 
 222         if ((ErrorCode 
= Pipe_Read_Stream_LE(&SCSICommandStatus
, sizeof(CommandStatusWrapper_t
))) != PIPE_RWSTREAM_ERROR_NoError
) 
 225         /* Clear the data ready for next reception */ 
 226         Pipe_ClearCurrentBank(); 
 228         /* Some buggy devices require a delay here before the pipe freezing or they will lock up */ 
 231         /* Freeze the IN pipe after use */ 
 234         return PIPE_RWSTREAM_ERROR_NoError
; 
 237 /** Clears the stall condition in the attached device on the nominated endpoint number. 
 239  *  \param EndpointNum  Endpoint number in the attached device whose stall condition is to be cleared 
 241  *  \return A value from the USB_Host_SendControlErrorCodes_t enum 
 243 uint8_t MassStore_ClearPipeStall(const uint8_t EndpointNum
) 
 245         USB_HostRequest 
= (USB_Host_Request_Header_t
) 
 247                         bmRequestType
: (REQDIR_HOSTTODEVICE 
| REQTYPE_STANDARD 
| REQREC_ENDPOINT
), 
 248                         bRequest
:      REQ_ClearFeature
, 
 249                         wValue
:        FEATURE_ENDPOINT_HALT
, 
 254         return USB_Host_SendControlRequest(NULL
); 
 257 /** Issues a Mass Storage class specific request to reset the attached device's Mass Storage interface, 
 258  *  readying the device for the next CBW. 
 260  *  \return A value from the USB_Host_SendControlErrorCodes_t enum 
 262 uint8_t MassStore_MassStorageReset(void) 
 264         USB_HostRequest 
= (USB_Host_Request_Header_t
) 
 266                         bmRequestType
: (REQDIR_HOSTTODEVICE 
| REQTYPE_CLASS 
| REQREC_INTERFACE
), 
 267                         bRequest
:      REQ_MassStorageReset
, 
 273         return USB_Host_SendControlRequest(NULL
); 
 276 /** Issues a Mass Storage class specific request to determine the index of the highest numbered Logical 
 277  *  Unit in the attached device. 
 279  *  \param MaxLUNIndex  Pointer to the location that the maximum LUN index value should be stored 
 281  *  \return A value from the USB_Host_SendControlErrorCodes_t enum 
 283 uint8_t MassStore_GetMaxLUN(uint8_t* const MaxLUNIndex
) 
 287         USB_HostRequest 
= (USB_Host_Request_Header_t
) 
 289                         bmRequestType
: (REQDIR_DEVICETOHOST 
| REQTYPE_CLASS 
| REQREC_INTERFACE
), 
 290                         bRequest
:      REQ_GetMaxLUN
, 
 296         if ((ErrorCode 
= USB_Host_SendControlRequest(MaxLUNIndex
)) == HOST_SENDCONTROL_SetupStalled
) 
 298                 /* Clear the pipe stall */ 
 301                 /* Some faulty Mass Storage devices don't implement the GET_MAX_LUN request, so assume a single LUN */ 
 308 /** Issues a SCSI Request Sense command to the attached device, to determine the current SCSI sense information. This 
 309  *  gives error codes for the last issued SCSI command to the device. 
 311  *  \param LUNIndex  Index of the LUN inside the device the command is being addressed to 
 312  *  \param SensePtr  Pointer to the sense data structure where the sense data from the device is to be stored 
 314  *  \return A value from the Pipe_Stream_RW_ErrorCodes_t enum 
 316 uint8_t MassStore_RequestSense(const uint8_t LUNIndex
, const SCSI_Request_Sense_Response_t
* const SensePtr
) 
 318         uint8_t ReturnCode 
= PIPE_RWSTREAM_ERROR_NoError
; 
 320         /* Create a CBW with a SCSI command to issue REQUEST SENSE command */ 
 321         SCSICommandBlock 
= (CommandBlockWrapper_t
) 
 325                                         Signature
:          CBW_SIGNATURE
, 
 327                                         DataTransferLength
: sizeof(SCSI_Request_Sense_Response_t
), 
 328                                         Flags
:              COMMAND_DIRECTION_DATA_IN
, 
 335                                         SCSI_CMD_REQUEST_SENSE
, 
 339                                         sizeof(SCSI_Request_Sense_Response_t
), // Allocation Length 
 340                                         0x00                    // Unused (control) 
 344         /* Send SCSI command to the attached device */ 
 345         MassStore_SendCommand(); 
 347         /* Wait until data received from the device */ 
 348         if ((ReturnCode 
= MassStore_WaitForDataReceived()) != PIPE_RWSTREAM_ERROR_NoError
) 
 354         /* Read the returned sense data into the buffer */ 
 355         if ((ReturnCode 
= MassStore_SendReceiveData((uint8_t*)SensePtr
)) != PIPE_RWSTREAM_ERROR_NoError
) 
 361         /* Read in the returned CSW from the device */ 
 362         if ((ReturnCode 
= MassStore_GetReturnedStatus()) != PIPE_RWSTREAM_ERROR_NoError
) 
 368         return PIPE_RWSTREAM_ERROR_NoError
; 
 371 /** Issues a SCSI Device Block Read command to the attached device, to read in one or more data blocks from the 
 372  *  storage medium into a buffer. 
 374  *  \param LUNIndex      Index of the LUN inside the device the command is being addressed to 
 375  *  \param BlockAddress  Start block address to read from 
 376  *  \param Blocks        Number of blocks to read from the device 
 377  *  \param BlockSize     Size in bytes of each block to read 
 378  *  \param BufferPtr     Pointer to the buffer where the read data is to be written to 
 380  *  \return A value from the Pipe_Stream_RW_ErrorCodes_t enum 
 382 uint8_t MassStore_ReadDeviceBlock(const uint8_t LUNIndex
, const uint32_t BlockAddress
, 
 383                                   const uint8_t Blocks
, const uint16_t BlockSize
, void* BufferPtr
) 
 385         uint8_t ReturnCode 
= PIPE_RWSTREAM_ERROR_NoError
; 
 387         /* Create a CBW with a SCSI command to read in the given blocks from the device */ 
 388         SCSICommandBlock 
= (CommandBlockWrapper_t
) 
 392                                         Signature
:          CBW_SIGNATURE
, 
 394                                         DataTransferLength
: ((uint32_t)Blocks 
* BlockSize
), 
 395                                         Flags
:              COMMAND_DIRECTION_DATA_IN
, 
 397                                         SCSICommandLength
:  10 
 403                                         0x00,                   // Unused (control bits, all off) 
 404                                         (BlockAddress 
>> 24),   // MSB of Block Address 
 405                                         (BlockAddress 
>> 16), 
 407                                         (BlockAddress 
& 0xFF),  // LSB of Block Address 
 408                                         0x00,                   // Unused (reserved) 
 409                                         0x00,                   // MSB of Total Blocks to Read 
 410                                         Blocks
,                 // LSB of Total Blocks to Read 
 411                                         0x00                    // Unused (control) 
 415         /* Send SCSI command to the attached device */ 
 416         MassStore_SendCommand(); 
 418         /* Wait until data received from the device */ 
 419         if ((ReturnCode 
= MassStore_WaitForDataReceived()) != PIPE_RWSTREAM_ERROR_NoError
) 
 425         /* Read the returned block data into the buffer */ 
 426         if ((ReturnCode 
= MassStore_SendReceiveData(BufferPtr
)) != PIPE_RWSTREAM_ERROR_NoError
) 
 432         /* Read in the returned CSW from the device */ 
 433         if ((ReturnCode 
= MassStore_GetReturnedStatus()) != PIPE_RWSTREAM_ERROR_NoError
) 
 439         return PIPE_RWSTREAM_ERROR_NoError
; 
 442 /** Issues a SCSI Device Block Write command to the attached device, to write one or more data blocks to the 
 443  *  storage medium from a buffer. 
 445  *  \param LUNIndex      Index of the LUN inside the device the command is being addressed to 
 446  *  \param BlockAddress  Start block address to write to 
 447  *  \param Blocks        Number of blocks to write to in the device 
 448  *  \param BlockSize     Size in bytes of each block to write 
 449  *  \param BufferPtr     Pointer to the buffer where the write data is to be sourced from 
 451  *  \return A value from the Pipe_Stream_RW_ErrorCodes_t enum 
 453 uint8_t MassStore_WriteDeviceBlock(const uint8_t LUNIndex
, const uint32_t BlockAddress
, 
 454                                    const uint8_t Blocks
, const uint16_t BlockSize
, void* BufferPtr
) 
 456         uint8_t ReturnCode 
= PIPE_RWSTREAM_ERROR_NoError
; 
 458         /* Create a CBW with a SCSI command to write the given blocks to the device */ 
 459         SCSICommandBlock 
= (CommandBlockWrapper_t
) 
 463                                         Signature
:          CBW_SIGNATURE
, 
 465                                         DataTransferLength
: ((uint32_t)Blocks 
* BlockSize
), 
 466                                         Flags
:              COMMAND_DIRECTION_DATA_OUT
, 
 468                                         SCSICommandLength
:  10 
 474                                         0x00,                   // Unused (control bits, all off) 
 475                                         (BlockAddress 
>> 24),   // MSB of Block Address 
 476                                         (BlockAddress 
>> 16), 
 478                                         (BlockAddress 
& 0xFF),  // LSB of Block Address 
 479                                         0x00,                   // Unused (reserved) 
 480                                         0x00,                   // MSB of Total Blocks to Write 
 481                                         Blocks
,                 // LSB of Total Blocks to Write 
 482                                         0x00                    // Unused (control) 
 486         /* Send SCSI command to the attached device */ 
 487         MassStore_SendCommand(); 
 489         /* Write the data to the device from the buffer */ 
 490         if ((ReturnCode 
= MassStore_SendReceiveData(BufferPtr
)) != PIPE_RWSTREAM_ERROR_NoError
) 
 496         /* Read in the returned CSW from the device */ 
 497         if ((ReturnCode 
= MassStore_GetReturnedStatus()) != PIPE_RWSTREAM_ERROR_NoError
) 
 503         return PIPE_RWSTREAM_ERROR_NoError
; 
 506 /** Issues a SCSI Device Test Unit Ready command to the attached device, to determine if the device is ready to accept 
 509  *  \param LUNIndex      Index of the LUN inside the device the command is being addressed to 
 511  *  \return A value from the Pipe_Stream_RW_ErrorCodes_t enum 
 513 uint8_t MassStore_TestUnitReady(const uint8_t LUNIndex
) 
 515         uint8_t ReturnCode 
= PIPE_RWSTREAM_ERROR_NoError
;        
 517         /* Create a CBW with a SCSI command to issue TEST UNIT READY command */ 
 518         SCSICommandBlock 
= (CommandBlockWrapper_t
) 
 522                                         Signature
:          CBW_SIGNATURE
, 
 524                                         DataTransferLength
: 0, 
 525                                         Flags
:              COMMAND_DIRECTION_DATA_IN
, 
 532                                         SCSI_CMD_TEST_UNIT_READY
, 
 537                                         0x00                    // Unused (control) 
 541         /* Send SCSI command to the attached device */ 
 542         MassStore_SendCommand(); 
 544         /* Read in the returned CSW from the device */ 
 545         if ((ReturnCode 
= MassStore_GetReturnedStatus()) != PIPE_RWSTREAM_ERROR_NoError
) 
 551         return PIPE_RWSTREAM_ERROR_NoError
; 
 554 /** Issues a SCSI Device Read Capacity command to the attached device, to determine the capacity of the 
 555  *  given Logical Unit within the device. 
 557  *  \param LUNIndex     Index of the LUN inside the device the command is being addressed to 
 558  *  \param CapacityPtr  Device capacity structure where the capacity data is to be stored 
 560  *  \return A value from the Pipe_Stream_RW_ErrorCodes_t enum 
 562 uint8_t MassStore_ReadCapacity(const uint8_t LUNIndex
, SCSI_Capacity_t
* const CapacityPtr
) 
 564         uint8_t ReturnCode 
= PIPE_RWSTREAM_ERROR_NoError
; 
 566         /* Create a CBW with a SCSI command to issue READ CAPACITY command */ 
 567         SCSICommandBlock 
= (CommandBlockWrapper_t
) 
 571                                         Signature
:          CBW_SIGNATURE
, 
 573                                         DataTransferLength
: 8, 
 574                                         Flags
:              COMMAND_DIRECTION_DATA_IN
, 
 576                                         SCSICommandLength
:  10 
 581                                         SCSI_CMD_READ_CAPACITY_10
, 
 583                                         0x00,                   // MSB of Logical block address 
 586                                         0x00,                   // LSB of Logical block address 
 589                                         0x00,                   // Partial Medium Indicator 
 590                                         0x00                    // Unused (control) 
 594         /* Send SCSI command to the attached device */ 
 595         MassStore_SendCommand(); 
 597         /* Wait until data received from the device */ 
 598         if ((ReturnCode 
= MassStore_WaitForDataReceived()) != PIPE_RWSTREAM_ERROR_NoError
) 
 604         /* Read the returned capacity data into the buffer */ 
 605         if ((ReturnCode 
= MassStore_SendReceiveData(CapacityPtr
)) != PIPE_RWSTREAM_ERROR_NoError
) 
 611         /* Endian-correct the read data */ 
 612         CapacityPtr
->Blocks    
= SwapEndian_32(CapacityPtr
->Blocks
); 
 613         CapacityPtr
->BlockSize 
= SwapEndian_32(CapacityPtr
->BlockSize
); 
 615         /* Read in the returned CSW from the device */ 
 616         if ((ReturnCode 
= MassStore_GetReturnedStatus()) != PIPE_RWSTREAM_ERROR_NoError
) 
 622         return PIPE_RWSTREAM_ERROR_NoError
; 
 625 /** Issues a SCSI Device Prevent/Allow Medium Removal command to the attached device, to lock the physical media from 
 626  *  being removed. This is a legacy command for SCSI disks with removable storage (such as ZIP disks), but should still 
 627  *  be issued before the first read or write command is sent. 
 629  *  \param LUNIndex        Index of the LUN inside the device the command is being addressed to 
 630  *  \param PreventRemoval  Whether or not the LUN media should be locked to prevent removal or not 
 632  *  \return A value from the Pipe_Stream_RW_ErrorCodes_t enum 
 634 uint8_t MassStore_PreventAllowMediumRemoval(const uint8_t LUNIndex
, const bool PreventRemoval
) 
 636         uint8_t ReturnCode 
= PIPE_RWSTREAM_ERROR_NoError
; 
 638         /* Create a CBW with a SCSI command to issue PREVENT ALLOW MEDIUM REMOVAL command */ 
 639         SCSICommandBlock 
= (CommandBlockWrapper_t
) 
 643                                         Signature
:          CBW_SIGNATURE
, 
 645                                         DataTransferLength
: 0, 
 646                                         Flags
:              COMMAND_DIRECTION_DATA_OUT
, 
 653                                         SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL
, 
 656                                         PreventRemoval
,         // Prevent flag 
 658                                         0x00                    // Unused (control) 
 662         /* Send SCSI command to the attached device */ 
 663         MassStore_SendCommand(); 
 665         /* Read in the returned CSW from the device */ 
 666         if ((ReturnCode 
= MassStore_GetReturnedStatus()) != PIPE_RWSTREAM_ERROR_NoError
) 
 672         return PIPE_RWSTREAM_ERROR_NoError
;