3      Copyright (C) Dean Camera, 2010. 
   5   dean [at] fourwalledcubicle [dot] com 
   6       www.fourwalledcubicle.com 
  10   Copyright 2010  Dean Camera (dean [at] fourwalledcubicle [dot] com) 
  12   Permission to use, copy, modify, distribute, and sell this  
  13   software and its documentation for any purpose is hereby granted 
  14   without fee, provided that the above copyright notice appear in  
  15   all 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 
  31 #define  __INCLUDE_FROM_USB_DRIVER 
  32 #include "../../HighLevel/USBMode.h" 
  33 #if defined(USB_CAN_BE_HOST) 
  35 #define  __INCLUDE_FROM_MS_CLASS_HOST_C 
  36 #define  __INCLUDE_FROM_MS_DRIVER 
  37 #include "MassStorage.h" 
  39 uint8_t MS_Host_ConfigurePipes(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, uint16_t ConfigDescriptorSize
, 
  40                                                            void* DeviceConfigDescriptor
) 
  42         uint8_t FoundEndpoints 
= 0; 
  44         memset(&MSInterfaceInfo
->State
, 0x00, sizeof(MSInterfaceInfo
->State
)); 
  46         if (DESCRIPTOR_TYPE(DeviceConfigDescriptor
) != DTYPE_Configuration
) 
  47           return MS_ENUMERROR_InvalidConfigDescriptor
; 
  49         if (USB_GetNextDescriptorComp(&ConfigDescriptorSize
, &DeviceConfigDescriptor
, 
  50                                       DCOMP_MS_NextMSInterface
) != DESCRIPTOR_SEARCH_COMP_Found
) 
  52                 return MS_ENUMERROR_NoMSInterfaceFound
; 
  55         MSInterfaceInfo
->State
.InterfaceNumber 
= DESCRIPTOR_PCAST(DeviceConfigDescriptor
, USB_Descriptor_Interface_t
)->InterfaceNumber
; 
  57         while (FoundEndpoints 
!= (MS_FOUND_DATAPIPE_IN 
| MS_FOUND_DATAPIPE_OUT
)) 
  59                 if (USB_GetNextDescriptorComp(&ConfigDescriptorSize
, &DeviceConfigDescriptor
, 
  60                                               DCOMP_MS_NextMSInterfaceEndpoint
) != DESCRIPTOR_SEARCH_COMP_Found
) 
  62                         return MS_ENUMERROR_EndpointsNotFound
; 
  65                 USB_Descriptor_Endpoint_t
* EndpointData 
= DESCRIPTOR_PCAST(DeviceConfigDescriptor
, USB_Descriptor_Endpoint_t
); 
  67                 if (EndpointData
->EndpointAddress 
& ENDPOINT_DESCRIPTOR_DIR_IN
) 
  69                         Pipe_ConfigurePipe(MSInterfaceInfo
->Config
.DataINPipeNumber
, EP_TYPE_BULK
, PIPE_TOKEN_IN
, 
  70                                            EndpointData
->EndpointAddress
, EndpointData
->EndpointSize
, 
  71                                            MSInterfaceInfo
->Config
.DataINPipeDoubleBank ? PIPE_BANK_DOUBLE 
: PIPE_BANK_SINGLE
); 
  72                         MSInterfaceInfo
->State
.DataINPipeSize 
= EndpointData
->EndpointSize
; 
  74                         FoundEndpoints 
|= MS_FOUND_DATAPIPE_IN
; 
  78                         Pipe_ConfigurePipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
, EP_TYPE_BULK
, PIPE_TOKEN_OUT
, 
  79                                            EndpointData
->EndpointAddress
, EndpointData
->EndpointSize
, 
  80                                            MSInterfaceInfo
->Config
.DataOUTPipeDoubleBank ? PIPE_BANK_DOUBLE 
: PIPE_BANK_SINGLE
); 
  81                         MSInterfaceInfo
->State
.DataOUTPipeSize 
= EndpointData
->EndpointSize
; 
  83                         FoundEndpoints 
|= MS_FOUND_DATAPIPE_OUT
; 
  87         MSInterfaceInfo
->State
.IsActive 
= true; 
  88         return MS_ENUMERROR_NoError
; 
  91 static uint8_t DCOMP_MS_NextMSInterface(void* const CurrentDescriptor
) 
  93         if (DESCRIPTOR_TYPE(CurrentDescriptor
) == DTYPE_Interface
) 
  95                 USB_Descriptor_Interface_t
* CurrentInterface 
= DESCRIPTOR_PCAST(CurrentDescriptor
, 
  96                                                                                 USB_Descriptor_Interface_t
); 
  98                 if ((CurrentInterface
->Class    
== MASS_STORE_CLASS
)    && 
  99                     (CurrentInterface
->SubClass 
== MASS_STORE_SUBCLASS
) && 
 100                     (CurrentInterface
->Protocol 
== MASS_STORE_PROTOCOL
)) 
 102                         return DESCRIPTOR_SEARCH_Found
; 
 106         return DESCRIPTOR_SEARCH_NotFound
; 
 109 static uint8_t DCOMP_MS_NextMSInterfaceEndpoint(void* const CurrentDescriptor
) 
 111         if (DESCRIPTOR_TYPE(CurrentDescriptor
) == DTYPE_Endpoint
) 
 113                 USB_Descriptor_Endpoint_t
* CurrentEndpoint 
= DESCRIPTOR_PCAST(CurrentDescriptor
, 
 114                                                                               USB_Descriptor_Endpoint_t
); 
 116                 uint8_t EndpointType 
= (CurrentEndpoint
->Attributes 
& EP_TYPE_MASK
); 
 118                 if ((EndpointType 
== EP_TYPE_BULK
) && 
 119                     (!(Pipe_IsEndpointBound(CurrentEndpoint
->EndpointAddress
)))) 
 121                         return DESCRIPTOR_SEARCH_Found
; 
 124         else if (DESCRIPTOR_TYPE(CurrentDescriptor
) == DTYPE_Interface
) 
 126                 return DESCRIPTOR_SEARCH_Fail
; 
 129         return DESCRIPTOR_SEARCH_NotFound
; 
 132 static uint8_t MS_Host_SendCommand(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, MS_CommandBlockWrapper_t
* const SCSICommandBlock
, 
 133                                    const void* const BufferPtr
) 
 135         uint8_t ErrorCode 
= PIPE_RWSTREAM_NoError
; 
 137         SCSICommandBlock
->Signature 
= CBW_SIGNATURE
; 
 138         SCSICommandBlock
->Tag       
= ++MSInterfaceInfo
->State
.TransactionTag
; 
 140         if (MSInterfaceInfo
->State
.TransactionTag 
== 0xFFFFFFFF) 
 141           MSInterfaceInfo
->State
.TransactionTag 
= 1; 
 143         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 146         if ((ErrorCode 
= Pipe_Write_Stream_LE(SCSICommandBlock
, sizeof(MS_CommandBlockWrapper_t
), 
 147                                               NO_STREAM_CALLBACK
)) != PIPE_RWSTREAM_NoError
) 
 151         Pipe_WaitUntilReady(); 
 155         if ((BufferPtr 
!= NULL
) && 
 156             ((ErrorCode 
= MS_Host_SendReceiveData(MSInterfaceInfo
, SCSICommandBlock
, (void*)BufferPtr
)) != PIPE_RWSTREAM_NoError
)) 
 165 static uint8_t MS_Host_WaitForDataReceived(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
) 
 167         uint16_t TimeoutMSRem 
= COMMAND_DATA_TIMEOUT_MS
; 
 169         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 172         while (!(Pipe_IsINReceived())) 
 174                 if (USB_INT_HasOccurred(USB_INT_HSOFI
)) 
 176                         USB_INT_Clear(USB_INT_HSOFI
); 
 180                           return PIPE_RWSTREAM_Timeout
; 
 184                 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 187                 if (Pipe_IsStalled()) 
 189                         USB_Host_ClearPipeStall(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 191                         return PIPE_RWSTREAM_PipeStalled
; 
 195                 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 198                 if (Pipe_IsStalled()) 
 200                         USB_Host_ClearPipeStall(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 202                         return PIPE_RWSTREAM_PipeStalled
; 
 205                 if (USB_HostState 
== HOST_STATE_Unattached
) 
 206                   return PIPE_RWSTREAM_DeviceDisconnected
; 
 209         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 212         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 215         return PIPE_RWSTREAM_NoError
; 
 218 static uint8_t MS_Host_SendReceiveData(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 219                                        MS_CommandBlockWrapper_t
* const SCSICommandBlock
, void* BufferPtr
) 
 221         uint8_t  ErrorCode 
= PIPE_RWSTREAM_NoError
; 
 222         uint16_t BytesRem  
= SCSICommandBlock
->DataTransferLength
; 
 224         if (SCSICommandBlock
->Flags 
& COMMAND_DIRECTION_DATA_IN
) 
 226                 if ((ErrorCode 
= MS_Host_WaitForDataReceived(MSInterfaceInfo
)) != PIPE_RWSTREAM_NoError
) 
 232                 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 235                 if ((ErrorCode 
= Pipe_Read_Stream_LE(BufferPtr
, BytesRem
, NO_STREAM_CALLBACK
)) != PIPE_RWSTREAM_NoError
) 
 242                 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 245                 if ((ErrorCode 
= Pipe_Write_Stream_LE(BufferPtr
, BytesRem
, NO_STREAM_CALLBACK
)) != PIPE_RWSTREAM_NoError
) 
 250                 while (!(Pipe_IsOUTReady())) 
 252                         if (USB_HostState 
== HOST_STATE_Unattached
) 
 253                           return PIPE_RWSTREAM_DeviceDisconnected
; 
 262 static uint8_t MS_Host_GetReturnedStatus(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 263                                          MS_CommandStatusWrapper_t
* const SCSICommandStatus
) 
 265         uint8_t ErrorCode 
= PIPE_RWSTREAM_NoError
; 
 267         if ((ErrorCode 
= MS_Host_WaitForDataReceived(MSInterfaceInfo
)) != PIPE_RWSTREAM_NoError
) 
 270         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 273         if ((ErrorCode 
= Pipe_Read_Stream_LE(SCSICommandStatus
, sizeof(MS_CommandStatusWrapper_t
), 
 274                                              NO_STREAM_CALLBACK
)) != PIPE_RWSTREAM_NoError
) 
 282         if (SCSICommandStatus
->Status 
!= SCSI_Command_Pass
) 
 283           ErrorCode 
= MS_ERROR_LOGICAL_CMD_FAILED
; 
 288 uint8_t MS_Host_ResetMSInterface(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
) 
 290         USB_ControlRequest 
= (USB_Request_Header_t
) 
 292                         .bmRequestType 
= (REQDIR_HOSTTODEVICE 
| REQTYPE_CLASS 
| REQREC_INTERFACE
), 
 293                         .bRequest      
= REQ_MassStorageReset
, 
 295                         .wIndex        
= MSInterfaceInfo
->State
.InterfaceNumber
, 
 299         Pipe_SelectPipe(PIPE_CONTROLPIPE
); 
 301         return USB_Host_SendControlRequest(NULL
); 
 304 uint8_t MS_Host_GetMaxLUN(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, uint8_t* const MaxLUNIndex
) 
 306         uint8_t ErrorCode 
= HOST_SENDCONTROL_Successful
; 
 308         USB_ControlRequest 
= (USB_Request_Header_t
) 
 310                         .bmRequestType 
= (REQDIR_DEVICETOHOST 
| REQTYPE_CLASS 
| REQREC_INTERFACE
), 
 311                         .bRequest      
= REQ_GetMaxLUN
, 
 313                         .wIndex        
= MSInterfaceInfo
->State
.InterfaceNumber
, 
 317         Pipe_SelectPipe(PIPE_CONTROLPIPE
); 
 319         if ((ErrorCode 
= USB_Host_SendControlRequest(MaxLUNIndex
)) != HOST_SENDCONTROL_Successful
) 
 322                 ErrorCode 
= HOST_SENDCONTROL_Successful
; 
 328 uint8_t MS_Host_GetInquiryData(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
, 
 329                                SCSI_Inquiry_Response_t
* const InquiryData
) 
 331         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 332           return HOST_SENDCONTROL_DeviceDisconnected
; 
 336         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 338                         .DataTransferLength 
= sizeof(SCSI_Inquiry_Response_t
), 
 339                         .Flags              
= COMMAND_DIRECTION_DATA_IN
, 
 341                         .SCSICommandLength  
= 6, 
 348                                         sizeof(SCSI_Inquiry_Response_t
), // Allocation Length 
 349                                         0x00                             // Unused (control) 
 353         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 355         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, InquiryData
)) != PIPE_RWSTREAM_NoError
) 
 358         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 361         return PIPE_RWSTREAM_NoError
; 
 364 uint8_t MS_Host_TestUnitReady(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
) 
 366         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 367           return HOST_SENDCONTROL_DeviceDisconnected
; 
 371         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 373                         .DataTransferLength 
= 0, 
 374                         .Flags              
= COMMAND_DIRECTION_DATA_IN
, 
 376                         .SCSICommandLength  
= 6, 
 379                                         SCSI_CMD_TEST_UNIT_READY
, 
 384                                         0x00                    // Unused (control) 
 388         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 390         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, NULL
)) != PIPE_RWSTREAM_NoError
) 
 393         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 396         return PIPE_RWSTREAM_NoError
; 
 399 uint8_t MS_Host_ReadDeviceCapacity(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
, 
 400                                    SCSI_Capacity_t
* const DeviceCapacity
) 
 402         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 403           return HOST_SENDCONTROL_DeviceDisconnected
; 
 407         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 409                         .DataTransferLength 
= sizeof(SCSI_Capacity_t
), 
 410                         .Flags              
= COMMAND_DIRECTION_DATA_IN
, 
 412                         .SCSICommandLength  
= 10, 
 415                                         SCSI_CMD_READ_CAPACITY_10
, 
 417                                         0x00,                   // MSB of Logical block address 
 420                                         0x00,                   // LSB of Logical block address 
 423                                         0x00,                   // Partial Medium Indicator 
 424                                         0x00                    // Unused (control) 
 428         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 430         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, DeviceCapacity
)) != PIPE_RWSTREAM_NoError
) 
 433         SwapEndian_n(&DeviceCapacity
->Blocks
,    sizeof(DeviceCapacity
->Blocks
)); 
 434         SwapEndian_n(&DeviceCapacity
->BlockSize
, sizeof(DeviceCapacity
->BlockSize
)); 
 436         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 439         return PIPE_RWSTREAM_NoError
; 
 442 uint8_t MS_Host_RequestSense(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
, 
 443                              SCSI_Request_Sense_Response_t
* const SenseData
) 
 445         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 446           return HOST_SENDCONTROL_DeviceDisconnected
; 
 450         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 452                         .DataTransferLength 
= sizeof(SCSI_Request_Sense_Response_t
), 
 453                         .Flags              
= COMMAND_DIRECTION_DATA_IN
, 
 455                         .SCSICommandLength  
= 6, 
 458                                         SCSI_CMD_REQUEST_SENSE
, 
 462                                         sizeof(SCSI_Request_Sense_Response_t
), // Allocation Length 
 463                                         0x00                                   // Unused (control) 
 467         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 469         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, SenseData
)) != PIPE_RWSTREAM_NoError
) 
 472         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 475         return PIPE_RWSTREAM_NoError
; 
 478 uint8_t MS_Host_PreventAllowMediumRemoval(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
, 
 479                                           const bool PreventRemoval
) 
 481         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 482           return HOST_SENDCONTROL_DeviceDisconnected
; 
 486         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 488                         .DataTransferLength 
= 0, 
 489                         .Flags              
= COMMAND_DIRECTION_DATA_OUT
, 
 491                         .SCSICommandLength  
= 6, 
 494                                         SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL
, 
 497                                         PreventRemoval
,         // Prevent flag 
 499                                         0x00                    // Unused (control) 
 503         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 505         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, NULL
)) != PIPE_RWSTREAM_NoError
) 
 508         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 511         return PIPE_RWSTREAM_NoError
; 
 514 uint8_t MS_Host_ReadDeviceBlocks(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
, const uint32_t BlockAddress
, 
 515                                  const uint8_t Blocks
, const uint16_t BlockSize
, void* BlockBuffer
) 
 517         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 518           return HOST_SENDCONTROL_DeviceDisconnected
; 
 522         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 524                         .DataTransferLength 
= ((uint32_t)Blocks 
* BlockSize
), 
 525                         .Flags              
= COMMAND_DIRECTION_DATA_IN
, 
 527                         .SCSICommandLength  
= 10, 
 531                                         0x00,                   // Unused (control bits, all off) 
 532                                         (BlockAddress 
>> 24),   // MSB of Block Address 
 533                                         (BlockAddress 
>> 16), 
 535                                         (BlockAddress 
& 0xFF),  // LSB of Block Address 
 536                                         0x00,                   // Unused (reserved) 
 537                                         0x00,                   // MSB of Total Blocks to Read 
 538                                         Blocks
,                 // LSB of Total Blocks to Read 
 539                                         0x00                    // Unused (control) 
 543         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 545         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, BlockBuffer
)) != PIPE_RWSTREAM_NoError
) 
 548         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 551         return PIPE_RWSTREAM_NoError
; 
 554 uint8_t MS_Host_WriteDeviceBlocks(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
, const uint32_t BlockAddress
, 
 555                                   const uint8_t Blocks
, const uint16_t BlockSize
, const void* BlockBuffer
) 
 557         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 558           return HOST_SENDCONTROL_DeviceDisconnected
; 
 562         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 564                         .DataTransferLength 
= ((uint32_t)Blocks 
* BlockSize
), 
 565                         .Flags              
= COMMAND_DIRECTION_DATA_OUT
, 
 567                         .SCSICommandLength  
= 10, 
 571                                         0x00,                   // Unused (control bits, all off) 
 572                                         (BlockAddress 
>> 24),   // MSB of Block Address 
 573                                         (BlockAddress 
>> 16), 
 575                                         (BlockAddress 
& 0xFF),  // LSB of Block Address 
 576                                         0x00,                   // Unused (reserved) 
 577                                         0x00,                   // MSB of Total Blocks to Write 
 578                                         Blocks
,                 // LSB of Total Blocks to Write 
 579                                         0x00                    // Unused (control) 
 583         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 585         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, BlockBuffer
)) != PIPE_RWSTREAM_NoError
) 
 588         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 591         return PIPE_RWSTREAM_NoError
;