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_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_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_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_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
, 
 135         uint8_t ErrorCode 
= PIPE_RWSTREAM_NoError
; 
 137         SCSICommandBlock
->Tag 
= ++MSInterfaceInfo
->State
.TransactionTag
; 
 139         if (MSInterfaceInfo
->State
.TransactionTag 
== 0xFFFFFFFF) 
 140           MSInterfaceInfo
->State
.TransactionTag 
= 1; 
 142         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 145         if ((ErrorCode 
= Pipe_Write_Stream_LE(SCSICommandBlock
, sizeof(MS_CommandBlockWrapper_t
), 
 146                                               NO_STREAM_CALLBACK
)) != PIPE_RWSTREAM_NoError
) 
 150         Pipe_WaitUntilReady(); 
 154         if ((BufferPtr 
!= NULL
) && 
 155             ((ErrorCode 
= MS_Host_SendReceiveData(MSInterfaceInfo
, SCSICommandBlock
, BufferPtr
)) != PIPE_RWSTREAM_NoError
)) 
 164 static uint8_t MS_Host_WaitForDataReceived(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
) 
 166         uint16_t TimeoutMSRem 
= COMMAND_DATA_TIMEOUT_MS
; 
 168         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 171         while (!(Pipe_IsINReceived())) 
 173                 if (USB_INT_HasOccurred(USB_INT_HSOFI
)) 
 175                         USB_INT_Clear(USB_INT_HSOFI
); 
 179                           return PIPE_RWSTREAM_Timeout
; 
 183                 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 186                 if (Pipe_IsStalled()) 
 188                         USB_Host_ClearPipeStall(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 190                         return PIPE_RWSTREAM_PipeStalled
; 
 194                 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 197                 if (Pipe_IsStalled()) 
 199                         USB_Host_ClearPipeStall(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 201                         return PIPE_RWSTREAM_PipeStalled
; 
 204                 if (USB_HostState 
== HOST_STATE_Unattached
) 
 205                   return PIPE_RWSTREAM_DeviceDisconnected
; 
 208         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 211         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 214         return PIPE_RWSTREAM_NoError
; 
 217 static uint8_t MS_Host_SendReceiveData(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 218                                        MS_CommandBlockWrapper_t
* const SCSICommandBlock
, void* BufferPtr
) 
 220         uint8_t  ErrorCode 
= PIPE_RWSTREAM_NoError
; 
 221         uint16_t BytesRem  
= SCSICommandBlock
->DataTransferLength
; 
 223         if (SCSICommandBlock
->Flags 
& COMMAND_DIRECTION_DATA_IN
) 
 225                 if ((ErrorCode 
= MS_Host_WaitForDataReceived(MSInterfaceInfo
)) != PIPE_RWSTREAM_NoError
) 
 231                 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 234                 if ((ErrorCode 
= Pipe_Read_Stream_LE(BufferPtr
, BytesRem
, NO_STREAM_CALLBACK
)) != PIPE_RWSTREAM_NoError
) 
 241                 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 244                 if ((ErrorCode 
= Pipe_Write_Stream_LE(BufferPtr
, BytesRem
, NO_STREAM_CALLBACK
)) != PIPE_RWSTREAM_NoError
) 
 249                 while (!(Pipe_IsOUTReady())) 
 251                         if (USB_HostState 
== HOST_STATE_Unattached
) 
 252                           return PIPE_RWSTREAM_DeviceDisconnected
; 
 261 static uint8_t MS_Host_GetReturnedStatus(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 262                                          MS_CommandStatusWrapper_t
* const SCSICommandStatus
) 
 264         uint8_t ErrorCode 
= PIPE_RWSTREAM_NoError
; 
 266         if ((ErrorCode 
= MS_Host_WaitForDataReceived(MSInterfaceInfo
)) != PIPE_RWSTREAM_NoError
) 
 269         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 272         if ((ErrorCode 
= Pipe_Read_Stream_LE(SCSICommandStatus
, sizeof(MS_CommandStatusWrapper_t
), 
 273                                              NO_STREAM_CALLBACK
)) != PIPE_RWSTREAM_NoError
) 
 281         if (SCSICommandStatus
->Status 
!= SCSI_Command_Pass
) 
 282           ErrorCode 
= MS_ERROR_LOGICAL_CMD_FAILED
; 
 287 uint8_t MS_Host_ResetMSInterface(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
) 
 289         USB_ControlRequest 
= (USB_Request_Header_t
) 
 291                         .bmRequestType 
= (REQDIR_HOSTTODEVICE 
| REQTYPE_CLASS 
| REQREC_INTERFACE
), 
 292                         .bRequest      
= REQ_MassStorageReset
, 
 294                         .wIndex        
= MSInterfaceInfo
->State
.InterfaceNumber
, 
 298         Pipe_SelectPipe(PIPE_CONTROLPIPE
); 
 300         return USB_Host_SendControlRequest(NULL
); 
 303 uint8_t MS_Host_GetMaxLUN(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, uint8_t* const MaxLUNIndex
) 
 305         uint8_t ErrorCode 
= HOST_SENDCONTROL_Successful
; 
 307         USB_ControlRequest 
= (USB_Request_Header_t
) 
 309                         .bmRequestType 
= (REQDIR_DEVICETOHOST 
| REQTYPE_CLASS 
| REQREC_INTERFACE
), 
 310                         .bRequest      
= REQ_GetMaxLUN
, 
 312                         .wIndex        
= MSInterfaceInfo
->State
.InterfaceNumber
, 
 316         Pipe_SelectPipe(PIPE_CONTROLPIPE
); 
 318         if ((ErrorCode 
= USB_Host_SendControlRequest(MaxLUNIndex
)) != HOST_SENDCONTROL_Successful
) 
 321                 ErrorCode 
= HOST_SENDCONTROL_Successful
; 
 327 uint8_t MS_Host_GetInquiryData(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
, 
 328                                SCSI_Inquiry_Response_t
* const InquiryData
) 
 330         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 331           return HOST_SENDCONTROL_DeviceDisconnected
; 
 335         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 337                         .Signature          
= CBW_SIGNATURE
, 
 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                         .Signature          
= CBW_SIGNATURE
, 
 374                         .DataTransferLength 
= 0, 
 375                         .Flags              
= COMMAND_DIRECTION_DATA_IN
, 
 377                         .SCSICommandLength  
= 6, 
 380                                         SCSI_CMD_TEST_UNIT_READY
, 
 385                                         0x00                    // Unused (control) 
 389         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 391         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, NULL
)) != PIPE_RWSTREAM_NoError
) 
 394         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 397         return PIPE_RWSTREAM_NoError
; 
 400 uint8_t MS_Host_ReadDeviceCapacity(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
, 
 401                                    SCSI_Capacity_t
* const DeviceCapacity
) 
 403         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 404           return HOST_SENDCONTROL_DeviceDisconnected
; 
 408         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 410                         .Signature          
= CBW_SIGNATURE
, 
 411                         .DataTransferLength 
= sizeof(SCSI_Capacity_t
), 
 412                         .Flags              
= COMMAND_DIRECTION_DATA_IN
, 
 414                         .SCSICommandLength  
= 10, 
 417                                         SCSI_CMD_READ_CAPACITY_10
, 
 419                                         0x00,                   // MSB of Logical block address 
 422                                         0x00,                   // LSB of Logical block address 
 425                                         0x00,                   // Partial Medium Indicator 
 426                                         0x00                    // Unused (control) 
 430         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 432         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, DeviceCapacity
)) != PIPE_RWSTREAM_NoError
) 
 435         DeviceCapacity
->Blocks    
= SwapEndian_32(DeviceCapacity
->Blocks
); 
 436         DeviceCapacity
->BlockSize 
= SwapEndian_32(DeviceCapacity
->BlockSize
); 
 438         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 441         return PIPE_RWSTREAM_NoError
; 
 444 uint8_t MS_Host_RequestSense(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
, 
 445                              SCSI_Request_Sense_Response_t
* const SenseData
) 
 447         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 448           return HOST_SENDCONTROL_DeviceDisconnected
; 
 452         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 454                         .Signature          
= CBW_SIGNATURE
, 
 455                         .DataTransferLength 
= sizeof(SCSI_Request_Sense_Response_t
), 
 456                         .Flags              
= COMMAND_DIRECTION_DATA_IN
, 
 458                         .SCSICommandLength  
= 6, 
 461                                         SCSI_CMD_REQUEST_SENSE
, 
 465                                         sizeof(SCSI_Request_Sense_Response_t
), // Allocation Length 
 466                                         0x00                                   // Unused (control) 
 470         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 472         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, SenseData
)) != PIPE_RWSTREAM_NoError
) 
 475         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 478         return PIPE_RWSTREAM_NoError
; 
 481 uint8_t MS_Host_PreventAllowMediumRemoval(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
, 
 482                                           const bool PreventRemoval
) 
 484         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 485           return HOST_SENDCONTROL_DeviceDisconnected
; 
 489         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 491                         .Signature          
= CBW_SIGNATURE
, 
 492                         .DataTransferLength 
= 0, 
 493                         .Flags              
= COMMAND_DIRECTION_DATA_OUT
, 
 495                         .SCSICommandLength  
= 6, 
 498                                         SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL
, 
 501                                         PreventRemoval
,         // Prevent flag 
 503                                         0x00                    // Unused (control) 
 507         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 509         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, NULL
)) != PIPE_RWSTREAM_NoError
) 
 512         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 515         return PIPE_RWSTREAM_NoError
; 
 518 uint8_t MS_Host_ReadDeviceBlocks(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
, const uint32_t BlockAddress
, 
 519                                  const uint8_t Blocks
, const uint16_t BlockSize
, void* BlockBuffer
) 
 521         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 522           return HOST_SENDCONTROL_DeviceDisconnected
; 
 526         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 528                         .Signature          
= CBW_SIGNATURE
, 
 529                         .DataTransferLength 
= ((uint32_t)Blocks 
* BlockSize
), 
 530                         .Flags              
= COMMAND_DIRECTION_DATA_IN
, 
 532                         .SCSICommandLength  
= 10, 
 536                                         0x00,                   // Unused (control bits, all off) 
 537                                         (BlockAddress 
>> 24),   // MSB of Block Address 
 538                                         (BlockAddress 
>> 16), 
 540                                         (BlockAddress 
& 0xFF),  // LSB of Block Address 
 541                                         0x00,                   // Unused (reserved) 
 542                                         0x00,                   // MSB of Total Blocks to Read 
 543                                         Blocks
,                 // LSB of Total Blocks to Read 
 544                                         0x00                    // Unused (control) 
 548         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 550         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, BlockBuffer
)) != PIPE_RWSTREAM_NoError
) 
 553         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 556         return PIPE_RWSTREAM_NoError
; 
 559 uint8_t MS_Host_WriteDeviceBlocks(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
, const uint32_t BlockAddress
, 
 560                                   const uint8_t Blocks
, const uint16_t BlockSize
, void* BlockBuffer
) 
 562         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 563           return HOST_SENDCONTROL_DeviceDisconnected
; 
 567         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 569                         .Signature          
= CBW_SIGNATURE
, 
 570                         .DataTransferLength 
= ((uint32_t)Blocks 
* BlockSize
), 
 571                         .Flags              
= COMMAND_DIRECTION_DATA_OUT
, 
 573                         .SCSICommandLength  
= 10, 
 577                                         0x00,                   // Unused (control bits, all off) 
 578                                         (BlockAddress 
>> 24),   // MSB of Block Address 
 579                                         (BlockAddress 
>> 16), 
 581                                         (BlockAddress 
& 0xFF),  // LSB of Block Address 
 582                                         0x00,                   // Unused (reserved) 
 583                                         0x00,                   // MSB of Total Blocks to Write 
 584                                         Blocks
,                 // LSB of Total Blocks to Write 
 585                                         0x00                    // Unused (control) 
 589         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 591         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, BlockBuffer
)) != PIPE_RWSTREAM_NoError
) 
 594         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 597         return PIPE_RWSTREAM_NoError
;