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                                                            void* 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
, 
  69                                            MSInterfaceInfo
->Config
.DataINPipeDoubleBank ? PIPE_BANK_DOUBLE 
: PIPE_BANK_SINGLE
); 
  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
, 
  78                                            MSInterfaceInfo
->Config
.DataOUTPipeDoubleBank ? PIPE_BANK_DOUBLE 
: PIPE_BANK_SINGLE
); 
  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* const 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* const 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
) 
 132         (void)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         USB_ControlRequest 
= (USB_Request_Header_t
) 
 294                         .bmRequestType 
= (REQDIR_HOSTTODEVICE 
| REQTYPE_CLASS 
| REQREC_INTERFACE
), 
 295                         .bRequest      
= REQ_MassStorageReset
, 
 297                         .wIndex        
= MSInterfaceInfo
->State
.InterfaceNumber
, 
 301         Pipe_SelectPipe(PIPE_CONTROLPIPE
); 
 303         return USB_Host_SendControlRequest(NULL
); 
 306 uint8_t MS_Host_GetMaxLUN(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, uint8_t* const MaxLUNIndex
) 
 308         uint8_t ErrorCode 
= HOST_SENDCONTROL_Successful
; 
 310         USB_ControlRequest 
= (USB_Request_Header_t
) 
 312                         .bmRequestType 
= (REQDIR_DEVICETOHOST 
| REQTYPE_CLASS 
| REQREC_INTERFACE
), 
 313                         .bRequest      
= REQ_GetMaxLUN
, 
 315                         .wIndex        
= MSInterfaceInfo
->State
.InterfaceNumber
, 
 319         Pipe_SelectPipe(PIPE_CONTROLPIPE
); 
 321         if ((ErrorCode 
= USB_Host_SendControlRequest(MaxLUNIndex
)) != HOST_SENDCONTROL_Successful
) 
 324                 ErrorCode 
= HOST_SENDCONTROL_Successful
; 
 330 uint8_t MS_Host_GetInquiryData(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
, 
 331                                SCSI_Inquiry_Response_t
* const InquiryData
) 
 333         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 334           return HOST_SENDCONTROL_DeviceDisconnected
; 
 338         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 340                         .Signature          
= CBW_SIGNATURE
, 
 341                         .DataTransferLength 
= sizeof(SCSI_Inquiry_Response_t
), 
 342                         .Flags              
= COMMAND_DIRECTION_DATA_IN
, 
 344                         .SCSICommandLength  
= 6, 
 351                                         sizeof(SCSI_Inquiry_Response_t
), // Allocation Length 
 352                                         0x00                             // Unused (control) 
 356         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 358         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, InquiryData
)) != PIPE_RWSTREAM_NoError
) 
 361         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 364         return PIPE_RWSTREAM_NoError
; 
 367 uint8_t MS_Host_TestUnitReady(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
) 
 369         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 370           return HOST_SENDCONTROL_DeviceDisconnected
; 
 374         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 376                         .Signature          
= CBW_SIGNATURE
, 
 377                         .DataTransferLength 
= 0, 
 378                         .Flags              
= COMMAND_DIRECTION_DATA_IN
, 
 380                         .SCSICommandLength  
= 6, 
 383                                         SCSI_CMD_TEST_UNIT_READY
, 
 388                                         0x00                    // Unused (control) 
 392         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 394         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, NULL
)) != PIPE_RWSTREAM_NoError
) 
 397         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 400         return PIPE_RWSTREAM_NoError
; 
 403 uint8_t MS_Host_ReadDeviceCapacity(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
, 
 404                                    SCSI_Capacity_t
* const DeviceCapacity
) 
 406         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 407           return HOST_SENDCONTROL_DeviceDisconnected
; 
 411         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 413                         .Signature          
= CBW_SIGNATURE
, 
 414                         .DataTransferLength 
= sizeof(SCSI_Capacity_t
), 
 415                         .Flags              
= COMMAND_DIRECTION_DATA_IN
, 
 417                         .SCSICommandLength  
= 10, 
 420                                         SCSI_CMD_READ_CAPACITY_10
, 
 422                                         0x00,                   // MSB of Logical block address 
 425                                         0x00,                   // LSB of Logical block address 
 428                                         0x00,                   // Partial Medium Indicator 
 429                                         0x00                    // Unused (control) 
 433         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 435         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, DeviceCapacity
)) != PIPE_RWSTREAM_NoError
) 
 438         DeviceCapacity
->Blocks    
= SwapEndian_32(DeviceCapacity
->Blocks
); 
 439         DeviceCapacity
->BlockSize 
= SwapEndian_32(DeviceCapacity
->BlockSize
); 
 441         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 444         return PIPE_RWSTREAM_NoError
; 
 447 uint8_t MS_Host_RequestSense(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
, 
 448                              SCSI_Request_Sense_Response_t
* const SenseData
) 
 450         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 451           return HOST_SENDCONTROL_DeviceDisconnected
; 
 455         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 457                         .Signature          
= CBW_SIGNATURE
, 
 458                         .DataTransferLength 
= sizeof(SCSI_Request_Sense_Response_t
), 
 459                         .Flags              
= COMMAND_DIRECTION_DATA_IN
, 
 461                         .SCSICommandLength  
= 6, 
 464                                         SCSI_CMD_REQUEST_SENSE
, 
 468                                         sizeof(SCSI_Request_Sense_Response_t
), // Allocation Length 
 469                                         0x00                                   // Unused (control) 
 473         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 475         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, SenseData
)) != PIPE_RWSTREAM_NoError
) 
 478         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 481         return PIPE_RWSTREAM_NoError
; 
 484 uint8_t MS_Host_PreventAllowMediumRemoval(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
, 
 485                                           const bool PreventRemoval
) 
 487         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 488           return HOST_SENDCONTROL_DeviceDisconnected
; 
 492         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 494                         .Signature          
= CBW_SIGNATURE
, 
 495                         .DataTransferLength 
= 0, 
 496                         .Flags              
= COMMAND_DIRECTION_DATA_OUT
, 
 498                         .SCSICommandLength  
= 6, 
 501                                         SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL
, 
 504                                         PreventRemoval
,         // Prevent flag 
 506                                         0x00                    // Unused (control) 
 510         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 512         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, NULL
)) != PIPE_RWSTREAM_NoError
) 
 515         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 518         return PIPE_RWSTREAM_NoError
; 
 521 uint8_t MS_Host_ReadDeviceBlocks(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
, const uint32_t BlockAddress
, 
 522                                  const uint8_t Blocks
, const uint16_t BlockSize
, void* BlockBuffer
) 
 524         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 525           return HOST_SENDCONTROL_DeviceDisconnected
; 
 529         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 531                         .Signature          
= CBW_SIGNATURE
, 
 532                         .DataTransferLength 
= ((uint32_t)Blocks 
* BlockSize
), 
 533                         .Flags              
= COMMAND_DIRECTION_DATA_IN
, 
 535                         .SCSICommandLength  
= 10, 
 539                                         0x00,                   // Unused (control bits, all off) 
 540                                         (BlockAddress 
>> 24),   // MSB of Block Address 
 541                                         (BlockAddress 
>> 16), 
 543                                         (BlockAddress 
& 0xFF),  // LSB of Block Address 
 544                                         0x00,                   // Unused (reserved) 
 545                                         0x00,                   // MSB of Total Blocks to Read 
 546                                         Blocks
,                 // LSB of Total Blocks to Read 
 547                                         0x00                    // Unused (control) 
 551         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 553         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, BlockBuffer
)) != PIPE_RWSTREAM_NoError
) 
 556         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 559         return PIPE_RWSTREAM_NoError
; 
 562 uint8_t MS_Host_WriteDeviceBlocks(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
, const uint32_t BlockAddress
, 
 563                                   const uint8_t Blocks
, const uint16_t BlockSize
, void* BlockBuffer
) 
 565         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 566           return HOST_SENDCONTROL_DeviceDisconnected
; 
 570         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 572                         .Signature          
= CBW_SIGNATURE
, 
 573                         .DataTransferLength 
= ((uint32_t)Blocks 
* BlockSize
), 
 574                         .Flags              
= COMMAND_DIRECTION_DATA_OUT
, 
 576                         .SCSICommandLength  
= 10, 
 580                                         0x00,                   // Unused (control bits, all off) 
 581                                         (BlockAddress 
>> 24),   // MSB of Block Address 
 582                                         (BlockAddress 
>> 16), 
 584                                         (BlockAddress 
& 0xFF),  // LSB of Block Address 
 585                                         0x00,                   // Unused (reserved) 
 586                                         0x00,                   // MSB of Total Blocks to Write 
 587                                         Blocks
,                 // LSB of Total Blocks to Write 
 588                                         0x00                    // Unused (control) 
 592         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 594         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, BlockBuffer
)) != PIPE_RWSTREAM_NoError
) 
 597         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 600         return PIPE_RWSTREAM_NoError
;