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 
  31 #include "../../HighLevel/USBMode.h" 
  32 #if defined(USB_CAN_BE_HOST) 
  34 #define INCLUDE_FROM_MS_CLASS_HOST_C 
  35 #include "MassStorage.h" 
  37 uint8_t MS_Host_ConfigurePipes(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, uint16_t ConfigDescriptorSize
, 
  38                                                            uint8_t* DeviceConfigDescriptor
) 
  40         uint8_t FoundEndpoints 
= 0; 
  42         memset(&MSInterfaceInfo
->State
, 0x00, sizeof(MSInterfaceInfo
->State
)); 
  44         if (DESCRIPTOR_TYPE(DeviceConfigDescriptor
) != DTYPE_Configuration
) 
  45           return MS_ENUMERROR_InvalidConfigDescriptor
; 
  47         if (USB_GetNextDescriptorComp(&ConfigDescriptorSize
, &DeviceConfigDescriptor
, 
  48                                       DComp_NextMSInterface
) != DESCRIPTOR_SEARCH_COMP_Found
) 
  50                 return MS_ENUMERROR_NoMSInterfaceFound
; 
  53         MSInterfaceInfo
->State
.InterfaceNumber 
= DESCRIPTOR_PCAST(DeviceConfigDescriptor
, USB_Descriptor_Interface_t
)->InterfaceNumber
; 
  55         while (FoundEndpoints 
!= (MS_FOUND_DATAPIPE_IN 
| MS_FOUND_DATAPIPE_OUT
)) 
  57                 if (USB_GetNextDescriptorComp(&ConfigDescriptorSize
, &DeviceConfigDescriptor
, 
  58                                               DComp_NextMSInterfaceEndpoint
) != DESCRIPTOR_SEARCH_COMP_Found
) 
  60                         return MS_ENUMERROR_EndpointsNotFound
; 
  63                 USB_Descriptor_Endpoint_t
* EndpointData 
= DESCRIPTOR_PCAST(DeviceConfigDescriptor
, USB_Descriptor_Endpoint_t
); 
  65                 if (EndpointData
->EndpointAddress 
& ENDPOINT_DESCRIPTOR_DIR_IN
) 
  67                         Pipe_ConfigurePipe(MSInterfaceInfo
->Config
.DataINPipeNumber
, EP_TYPE_BULK
, PIPE_TOKEN_IN
, 
  68                                            EndpointData
->EndpointAddress
, EndpointData
->EndpointSize
, 
  70                         MSInterfaceInfo
->State
.DataINPipeSize 
= EndpointData
->EndpointSize
; 
  72                         FoundEndpoints 
|= MS_FOUND_DATAPIPE_IN
; 
  76                         Pipe_ConfigurePipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
, EP_TYPE_BULK
, PIPE_TOKEN_OUT
, 
  77                                            EndpointData
->EndpointAddress
, EndpointData
->EndpointSize
, 
  79                         MSInterfaceInfo
->State
.DataOUTPipeSize 
= EndpointData
->EndpointSize
; 
  81                         FoundEndpoints 
|= MS_FOUND_DATAPIPE_OUT
; 
  85         MSInterfaceInfo
->State
.IsActive 
= true; 
  86         return MS_ENUMERROR_NoError
; 
  89 static uint8_t DComp_NextMSInterface(void* CurrentDescriptor
) 
  91         if (DESCRIPTOR_TYPE(CurrentDescriptor
) == DTYPE_Interface
) 
  93                 USB_Descriptor_Interface_t
* CurrentInterface 
= DESCRIPTOR_PCAST(CurrentDescriptor
, 
  94                                                                                 USB_Descriptor_Interface_t
); 
  96                 if ((CurrentInterface
->Class    
== MASS_STORE_CLASS
)    && 
  97                     (CurrentInterface
->SubClass 
== MASS_STORE_SUBCLASS
) && 
  98                     (CurrentInterface
->Protocol 
== MASS_STORE_PROTOCOL
)) 
 100                         return DESCRIPTOR_SEARCH_Found
; 
 104         return DESCRIPTOR_SEARCH_NotFound
; 
 107 static uint8_t DComp_NextMSInterfaceEndpoint(void* CurrentDescriptor
) 
 109         if (DESCRIPTOR_TYPE(CurrentDescriptor
) == DTYPE_Endpoint
) 
 111                 USB_Descriptor_Endpoint_t
* CurrentEndpoint 
= DESCRIPTOR_PCAST(CurrentDescriptor
, 
 112                                                                               USB_Descriptor_Endpoint_t
); 
 114                 uint8_t EndpointType 
= (CurrentEndpoint
->Attributes 
& EP_TYPE_MASK
); 
 116                 if ((EndpointType 
== EP_TYPE_BULK
) && 
 117                     (!(Pipe_IsEndpointBound(CurrentEndpoint
->EndpointAddress
)))) 
 119                         return DESCRIPTOR_SEARCH_Found
; 
 122         else if (DESCRIPTOR_TYPE(CurrentDescriptor
) == DTYPE_Interface
) 
 124                 return DESCRIPTOR_SEARCH_Fail
; 
 127         return DESCRIPTOR_SEARCH_NotFound
; 
 130 void MS_Host_USBTask(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
) 
 135 static uint8_t MS_Host_SendCommand(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, MS_CommandBlockWrapper_t
* const SCSICommandBlock
, 
 138         uint8_t ErrorCode 
= PIPE_RWSTREAM_NoError
; 
 140         SCSICommandBlock
->Tag 
= ++MSInterfaceInfo
->State
.TransactionTag
; 
 142         if (MSInterfaceInfo
->State
.TransactionTag 
== 0xFFFFFFFF) 
 143           MSInterfaceInfo
->State
.TransactionTag 
= 1; 
 145         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 148         if ((ErrorCode 
= Pipe_Write_Stream_LE(SCSICommandBlock
, sizeof(MS_CommandBlockWrapper_t
), 
 149                                               NO_STREAM_CALLBACK
)) != PIPE_RWSTREAM_NoError
) 
 153         Pipe_WaitUntilReady(); 
 157         if ((BufferPtr 
!= NULL
) && 
 158             ((ErrorCode 
= MS_Host_SendReceiveData(MSInterfaceInfo
, SCSICommandBlock
, BufferPtr
)) != PIPE_RWSTREAM_NoError
)) 
 167 static uint8_t MS_Host_WaitForDataReceived(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
) 
 169         uint16_t TimeoutMSRem 
= COMMAND_DATA_TIMEOUT_MS
; 
 171         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 174         while (!(Pipe_IsINReceived())) 
 176                 if (USB_INT_HasOccurred(USB_INT_HSOFI
)) 
 178                         USB_INT_Clear(USB_INT_HSOFI
); 
 182                           return PIPE_RWSTREAM_Timeout
; 
 186                 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 189                 if (Pipe_IsStalled()) 
 191                         USB_Host_ClearPipeStall(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 193                         return PIPE_RWSTREAM_PipeStalled
; 
 197                 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 200                 if (Pipe_IsStalled()) 
 202                         USB_Host_ClearPipeStall(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 204                         return PIPE_RWSTREAM_PipeStalled
; 
 207                 if (USB_HostState 
== HOST_STATE_Unattached
) 
 208                   return PIPE_RWSTREAM_DeviceDisconnected
; 
 211         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 214         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 217         return PIPE_RWSTREAM_NoError
; 
 220 static uint8_t MS_Host_SendReceiveData(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 221                                        MS_CommandBlockWrapper_t
* const SCSICommandBlock
, void* BufferPtr
) 
 223         uint8_t  ErrorCode 
= PIPE_RWSTREAM_NoError
; 
 224         uint16_t BytesRem  
= SCSICommandBlock
->DataTransferLength
; 
 226         if (SCSICommandBlock
->Flags 
& COMMAND_DIRECTION_DATA_IN
) 
 228                 if ((ErrorCode 
= MS_Host_WaitForDataReceived(MSInterfaceInfo
)) != PIPE_RWSTREAM_NoError
) 
 234                 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 237                 if ((ErrorCode 
= Pipe_Read_Stream_LE(BufferPtr
, BytesRem
, NO_STREAM_CALLBACK
)) != PIPE_RWSTREAM_NoError
) 
 244                 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 247                 if ((ErrorCode 
= Pipe_Write_Stream_LE(BufferPtr
, BytesRem
, NO_STREAM_CALLBACK
)) != PIPE_RWSTREAM_NoError
) 
 252                 while (!(Pipe_IsOUTReady())) 
 254                         if (USB_HostState 
== HOST_STATE_Unattached
) 
 255                           return PIPE_RWSTREAM_DeviceDisconnected
; 
 264 static uint8_t MS_Host_GetReturnedStatus(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 265                                          MS_CommandStatusWrapper_t
* const SCSICommandStatus
) 
 267         uint8_t ErrorCode 
= PIPE_RWSTREAM_NoError
; 
 269         if ((ErrorCode 
= MS_Host_WaitForDataReceived(MSInterfaceInfo
)) != PIPE_RWSTREAM_NoError
) 
 272         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 275         if ((ErrorCode 
= Pipe_Read_Stream_LE(SCSICommandStatus
, sizeof(MS_CommandStatusWrapper_t
), 
 276                                              NO_STREAM_CALLBACK
)) != PIPE_RWSTREAM_NoError
) 
 284         if (SCSICommandStatus
->Status 
!= SCSI_Command_Pass
) 
 285           ErrorCode 
= MS_ERROR_LOGICAL_CMD_FAILED
; 
 290 uint8_t MS_Host_ResetMSInterface(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
) 
 292         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 293           return HOST_SENDCONTROL_DeviceDisconnect
; 
 295         USB_ControlRequest 
= (USB_Request_Header_t
) 
 297                         .bmRequestType 
= (REQDIR_HOSTTODEVICE 
| REQTYPE_CLASS 
| REQREC_INTERFACE
), 
 298                         .bRequest      
= REQ_MassStorageReset
, 
 300                         .wIndex        
= MSInterfaceInfo
->State
.InterfaceNumber
, 
 304         Pipe_SelectPipe(PIPE_CONTROLPIPE
); 
 306         return USB_Host_SendControlRequest(NULL
); 
 309 uint8_t MS_Host_GetMaxLUN(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, uint8_t* const MaxLUNIndex
) 
 311         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 312           return HOST_SENDCONTROL_DeviceDisconnect
; 
 316         USB_ControlRequest 
= (USB_Request_Header_t
) 
 318                         .bmRequestType 
= (REQDIR_DEVICETOHOST 
| REQTYPE_CLASS 
| REQREC_INTERFACE
), 
 319                         .bRequest      
= REQ_GetMaxLUN
, 
 321                         .wIndex        
= MSInterfaceInfo
->State
.InterfaceNumber
, 
 325         Pipe_SelectPipe(PIPE_CONTROLPIPE
); 
 327         if ((ErrorCode 
= USB_Host_SendControlRequest(MaxLUNIndex
)) != HOST_SENDCONTROL_Successful
) 
 333 uint8_t MS_Host_GetInquiryData(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
, 
 334                                SCSI_Inquiry_Response_t
* const InquiryData
) 
 336         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 337           return HOST_SENDCONTROL_DeviceDisconnect
; 
 341         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 343                         .Signature          
= CBW_SIGNATURE
, 
 344                         .DataTransferLength 
= sizeof(SCSI_Inquiry_Response_t
), 
 345                         .Flags              
= COMMAND_DIRECTION_DATA_IN
, 
 347                         .SCSICommandLength  
= 6, 
 354                                         sizeof(SCSI_Inquiry_Response_t
), // Allocation Length 
 355                                         0x00                             // Unused (control) 
 359         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 361         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, InquiryData
)) != PIPE_RWSTREAM_NoError
) 
 364         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 367         return PIPE_RWSTREAM_NoError
; 
 370 uint8_t MS_Host_TestUnitReady(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
) 
 372         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 373           return HOST_SENDCONTROL_DeviceDisconnect
; 
 377         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 379                         .Signature          
= CBW_SIGNATURE
, 
 380                         .DataTransferLength 
= 0, 
 381                         .Flags              
= COMMAND_DIRECTION_DATA_IN
, 
 383                         .SCSICommandLength  
= 6, 
 386                                         SCSI_CMD_TEST_UNIT_READY
, 
 391                                         0x00                    // Unused (control) 
 395         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 397         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, NULL
)) != PIPE_RWSTREAM_NoError
) 
 400         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 403         return PIPE_RWSTREAM_NoError
; 
 406 uint8_t MS_Host_ReadDeviceCapacity(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
, 
 407                                    SCSI_Capacity_t
* const DeviceCapacity
) 
 409         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 410           return HOST_SENDCONTROL_DeviceDisconnect
; 
 414         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 416                         .Signature          
= CBW_SIGNATURE
, 
 417                         .DataTransferLength 
= sizeof(SCSI_Capacity_t
), 
 418                         .Flags              
= COMMAND_DIRECTION_DATA_IN
, 
 420                         .SCSICommandLength  
= 10, 
 423                                         SCSI_CMD_READ_CAPACITY_10
, 
 425                                         0x00,                   // MSB of Logical block address 
 428                                         0x00,                   // LSB of Logical block address 
 431                                         0x00,                   // Partial Medium Indicator 
 432                                         0x00                    // Unused (control) 
 436         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 438         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, DeviceCapacity
)) != PIPE_RWSTREAM_NoError
) 
 441         DeviceCapacity
->Blocks    
= SwapEndian_32(DeviceCapacity
->Blocks
); 
 442         DeviceCapacity
->BlockSize 
= SwapEndian_32(DeviceCapacity
->BlockSize
); 
 444         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 447         return PIPE_RWSTREAM_NoError
; 
 450 uint8_t MS_Host_RequestSense(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
, 
 451                              SCSI_Request_Sense_Response_t
* const SenseData
) 
 453         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 454           return HOST_SENDCONTROL_DeviceDisconnect
; 
 458         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 460                         .Signature          
= CBW_SIGNATURE
, 
 461                         .DataTransferLength 
= sizeof(SCSI_Request_Sense_Response_t
), 
 462                         .Flags              
= COMMAND_DIRECTION_DATA_IN
, 
 464                         .SCSICommandLength  
= 6, 
 467                                         SCSI_CMD_REQUEST_SENSE
, 
 471                                         sizeof(SCSI_Request_Sense_Response_t
), // Allocation Length 
 472                                         0x00                                   // Unused (control) 
 476         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 478         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, SenseData
)) != PIPE_RWSTREAM_NoError
) 
 481         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 484         return PIPE_RWSTREAM_NoError
; 
 487 uint8_t MS_Host_PreventAllowMediumRemoval(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
, 
 488                                           const bool PreventRemoval
) 
 490         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 491           return HOST_SENDCONTROL_DeviceDisconnect
; 
 495         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 497                         .Signature          
= CBW_SIGNATURE
, 
 498                         .DataTransferLength 
= 0, 
 499                         .Flags              
= COMMAND_DIRECTION_DATA_OUT
, 
 501                         .SCSICommandLength  
= 6, 
 504                                         SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL
, 
 507                                         PreventRemoval
,         // Prevent flag 
 509                                         0x00                    // Unused (control) 
 513         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 515         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, NULL
)) != PIPE_RWSTREAM_NoError
) 
 518         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 521         return PIPE_RWSTREAM_NoError
; 
 524 uint8_t MS_Host_ReadDeviceBlocks(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
, const uint32_t BlockAddress
, 
 525                                  const uint8_t Blocks
, const uint16_t BlockSize
, void* BlockBuffer
) 
 527         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 528           return HOST_SENDCONTROL_DeviceDisconnect
; 
 532         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 534                         .Signature          
= CBW_SIGNATURE
, 
 535                         .DataTransferLength 
= ((uint32_t)Blocks 
* BlockSize
), 
 536                         .Flags              
= COMMAND_DIRECTION_DATA_IN
, 
 538                         .SCSICommandLength  
= 10, 
 542                                         0x00,                   // Unused (control bits, all off) 
 543                                         (BlockAddress 
>> 24),   // MSB of Block Address 
 544                                         (BlockAddress 
>> 16), 
 546                                         (BlockAddress 
& 0xFF),  // LSB of Block Address 
 547                                         0x00,                   // Unused (reserved) 
 548                                         0x00,                   // MSB of Total Blocks to Read 
 549                                         Blocks
,                 // LSB of Total Blocks to Read 
 550                                         0x00                    // Unused (control) 
 554         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 556         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, BlockBuffer
)) != PIPE_RWSTREAM_NoError
) 
 559         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 562         return PIPE_RWSTREAM_NoError
; 
 565 uint8_t MS_Host_WriteDeviceBlocks(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
, const uint32_t BlockAddress
, 
 566                                   const uint8_t Blocks
, const uint16_t BlockSize
, void* BlockBuffer
) 
 568         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 569           return HOST_SENDCONTROL_DeviceDisconnect
; 
 573         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 575                         .Signature          
= CBW_SIGNATURE
, 
 576                         .DataTransferLength 
= ((uint32_t)Blocks 
* BlockSize
), 
 577                         .Flags              
= COMMAND_DIRECTION_DATA_OUT
, 
 579                         .SCSICommandLength  
= 10, 
 583                                         0x00,                   // Unused (control bits, all off) 
 584                                         (BlockAddress 
>> 24),   // MSB of Block Address 
 585                                         (BlockAddress 
>> 16), 
 587                                         (BlockAddress 
& 0xFF),  // LSB of Block Address 
 588                                         0x00,                   // Unused (reserved) 
 589                                         0x00,                   // MSB of Total Blocks to Write 
 590                                         Blocks
,                 // LSB of Total Blocks to Write 
 591                                         0x00                    // Unused (control) 
 595         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 597         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, BlockBuffer
)) != PIPE_RWSTREAM_NoError
) 
 600         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 603         return PIPE_RWSTREAM_NoError
;