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
* MSInterfaceInfo
, uint16_t ConfigDescriptorLength
, 
  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(&ConfigDescriptorLength
, &DeviceConfigDescriptor
, 
  48                                       DComp_NextMSInterface
) != DESCRIPTOR_SEARCH_COMP_Found
) 
  50                 return MS_ENUMERROR_NoMSInterfaceFound
; 
  53         MSInterfaceInfo
->State
.InterfaceNumber       
= 
  54         #if defined(USE_NONSTANDARD_DESCRIPTOR_NAMES) 
  55                              DESCRIPTOR_PCAST(DeviceConfigDescriptor
, USB_Descriptor_Interface_t
)->InterfaceNumber
; 
  57                              DESCRIPTOR_PCAST(DeviceConfigDescriptor
, USB_Descriptor_Interface_t
)->bInterfaceNumber
; 
  60         while (FoundEndpoints 
!= (MS_FOUND_DATAPIPE_IN 
| MS_FOUND_DATAPIPE_OUT
)) 
  62                 if (USB_GetNextDescriptorComp(&ConfigDescriptorLength
, &DeviceConfigDescriptor
, 
  63                                               DComp_NextMSInterfaceEndpoint
) != DESCRIPTOR_SEARCH_COMP_Found
) 
  65                         return MS_ENUMERROR_EndpointsNotFound
; 
  68                 USB_Descriptor_Endpoint_t
* EndpointData 
= DESCRIPTOR_PCAST(DeviceConfigDescriptor
, USB_Descriptor_Endpoint_t
); 
  70                 if (EndpointData
->EndpointAddress 
& ENDPOINT_DESCRIPTOR_DIR_IN
) 
  72                         Pipe_ConfigurePipe(MSInterfaceInfo
->Config
.DataINPipeNumber
, EP_TYPE_BULK
, PIPE_TOKEN_IN
, 
  73                                            EndpointData
->EndpointAddress
, EndpointData
->EndpointSize
, 
  75                         MSInterfaceInfo
->State
.DataINPipeSize 
= EndpointData
->EndpointSize
; 
  77                         FoundEndpoints 
|= MS_FOUND_DATAPIPE_IN
; 
  81                         Pipe_ConfigurePipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
, EP_TYPE_BULK
, PIPE_TOKEN_OUT
, 
  82                                            EndpointData
->EndpointAddress
, EndpointData
->EndpointSize
, 
  84                         MSInterfaceInfo
->State
.DataOUTPipeSize 
= EndpointData
->EndpointSize
; 
  86                         FoundEndpoints 
|= MS_FOUND_DATAPIPE_OUT
; 
  90         MSInterfaceInfo
->State
.Active 
= true; 
  91         return MS_ENUMERROR_NoError
; 
  94 static uint8_t DComp_NextMSInterface(void* CurrentDescriptor
) 
  96         if (DESCRIPTOR_TYPE(CurrentDescriptor
) == DTYPE_Interface
) 
  98                 USB_Descriptor_Interface_t
* CurrentInterface 
= DESCRIPTOR_PCAST(CurrentDescriptor
, 
  99                                                                                 USB_Descriptor_Interface_t
); 
 101                 if ((CurrentInterface
->Class    
== MASS_STORE_CLASS
)    && 
 102                     (CurrentInterface
->SubClass 
== MASS_STORE_SUBCLASS
) && 
 103                     (CurrentInterface
->Protocol 
== MASS_STORE_PROTOCOL
)) 
 105                         return DESCRIPTOR_SEARCH_Found
; 
 109         return DESCRIPTOR_SEARCH_NotFound
; 
 112 static uint8_t DComp_NextMSInterfaceEndpoint(void* CurrentDescriptor
) 
 114         if (DESCRIPTOR_TYPE(CurrentDescriptor
) == DTYPE_Endpoint
) 
 116                 USB_Descriptor_Endpoint_t
* CurrentEndpoint 
= DESCRIPTOR_PCAST(CurrentDescriptor
, 
 117                                                                               USB_Descriptor_Endpoint_t
); 
 119                 uint8_t EndpointType 
= (CurrentEndpoint
->Attributes 
& EP_TYPE_MASK
); 
 121                 if ((EndpointType 
== EP_TYPE_BULK
) && 
 122                     (!(Pipe_IsEndpointBound(CurrentEndpoint
->EndpointAddress
)))) 
 124                         return DESCRIPTOR_SEARCH_Found
; 
 127         else if (DESCRIPTOR_TYPE(CurrentDescriptor
) == DTYPE_Interface
) 
 129                 return DESCRIPTOR_SEARCH_Fail
; 
 132         return DESCRIPTOR_SEARCH_NotFound
; 
 135 void MS_Host_USBTask(USB_ClassInfo_MS_Host_t
* MSInterfaceInfo
) 
 140 static uint8_t MS_Host_SendCommand(USB_ClassInfo_MS_Host_t
* MSInterfaceInfo
, MS_CommandBlockWrapper_t
* SCSICommandBlock
, 
 143         uint8_t ErrorCode 
= PIPE_RWSTREAM_NoError
; 
 145         SCSICommandBlock
->Tag 
= ++MSInterfaceInfo
->State
.TransactionTag
; 
 147         if (MSInterfaceInfo
->State
.TransactionTag 
== 0xFFFFFFFF) 
 148           MSInterfaceInfo
->State
.TransactionTag 
= 1; 
 150         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 153         if ((ErrorCode 
= Pipe_Write_Stream_LE(SCSICommandBlock
, sizeof(MS_CommandBlockWrapper_t
))) != PIPE_RWSTREAM_NoError
) 
 157         Pipe_WaitUntilReady(); 
 161         if ((BufferPtr 
!= NULL
) && 
 162             ((ErrorCode 
= MS_Host_SendReceiveData(MSInterfaceInfo
, SCSICommandBlock
, BufferPtr
)) != PIPE_RWSTREAM_NoError
)) 
 171 static uint8_t MS_Host_WaitForDataReceived(USB_ClassInfo_MS_Host_t
* MSInterfaceInfo
) 
 173         uint16_t TimeoutMSRem 
= COMMAND_DATA_TIMEOUT_MS
; 
 175         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 178         while (!(Pipe_IsINReceived())) 
 180                 if (USB_INT_HasOccurred(USB_INT_HSOFI
)) 
 182                         USB_INT_Clear(USB_INT_HSOFI
); 
 186                           return PIPE_RWSTREAM_Timeout
; 
 190                 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 193                 if (Pipe_IsStalled()) 
 195                         USB_Host_ClearPipeStall(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 197                         return PIPE_RWSTREAM_PipeStalled
; 
 201                 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 204                 if (Pipe_IsStalled()) 
 206                         USB_Host_ClearPipeStall(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 208                         return PIPE_RWSTREAM_PipeStalled
; 
 211                 if (USB_HostState 
== HOST_STATE_Unattached
) 
 212                   return PIPE_RWSTREAM_DeviceDisconnected
; 
 215         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 218         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 221         return PIPE_RWSTREAM_NoError
; 
 224 static uint8_t MS_Host_SendReceiveData(USB_ClassInfo_MS_Host_t
* MSInterfaceInfo
, 
 225                                        MS_CommandBlockWrapper_t
* SCSICommandBlock
, void* BufferPtr
) 
 227         uint8_t  ErrorCode 
= PIPE_RWSTREAM_NoError
; 
 228         uint16_t BytesRem  
= SCSICommandBlock
->DataTransferLength
; 
 230         if (SCSICommandBlock
->Flags 
& COMMAND_DIRECTION_DATA_IN
) 
 232                 if ((ErrorCode 
= MS_Host_WaitForDataReceived(MSInterfaceInfo
)) != PIPE_RWSTREAM_NoError
) 
 238                 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 241                 if ((ErrorCode 
= Pipe_Read_Stream_LE(BufferPtr
, BytesRem
)) != PIPE_RWSTREAM_NoError
) 
 248                 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 251                 if ((ErrorCode 
= Pipe_Write_Stream_LE(BufferPtr
, BytesRem
)) != PIPE_RWSTREAM_NoError
) 
 256                 while (!(Pipe_IsOUTReady())) 
 258                         if (USB_HostState 
== HOST_STATE_Unattached
) 
 259                           return PIPE_RWSTREAM_DeviceDisconnected
; 
 268 static uint8_t MS_Host_GetReturnedStatus(USB_ClassInfo_MS_Host_t
* MSInterfaceInfo
, 
 269                                          MS_CommandStatusWrapper_t
* SCSICommandStatus
) 
 271         uint8_t ErrorCode 
= PIPE_RWSTREAM_NoError
; 
 273         if ((ErrorCode 
= MS_Host_WaitForDataReceived(MSInterfaceInfo
)) != PIPE_RWSTREAM_NoError
) 
 276         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 279         if ((ErrorCode 
= Pipe_Read_Stream_LE(SCSICommandStatus
, sizeof(MS_CommandStatusWrapper_t
))) != PIPE_RWSTREAM_NoError
) 
 285         if (SCSICommandStatus
->Status 
!= SCSI_Command_Pass
) 
 286           ErrorCode 
= MS_ERROR_LOGICAL_CMD_FAILED
; 
 291 uint8_t MS_Host_ResetMSInterface(USB_ClassInfo_MS_Host_t
* MSInterfaceInfo
) 
 293         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.Active
)) 
 294           return HOST_SENDCONTROL_DeviceDisconnect
; 
 296         USB_ControlRequest 
= (USB_Request_Header_t
) 
 298                         .bmRequestType 
= (REQDIR_HOSTTODEVICE 
| REQTYPE_CLASS 
| REQREC_INTERFACE
), 
 299                         .bRequest      
= REQ_MassStorageReset
, 
 301                         .wIndex        
= MSInterfaceInfo
->State
.InterfaceNumber
, 
 305         Pipe_SelectPipe(PIPE_CONTROLPIPE
); 
 307         return USB_Host_SendControlRequest(NULL
); 
 310 uint8_t MS_Host_GetMaxLUN(USB_ClassInfo_MS_Host_t
* MSInterfaceInfo
, uint8_t* MaxLUNIndex
) 
 312         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.Active
)) 
 313           return HOST_SENDCONTROL_DeviceDisconnect
; 
 317         USB_ControlRequest 
= (USB_Request_Header_t
) 
 319                         .bmRequestType 
= (REQDIR_DEVICETOHOST 
| REQTYPE_CLASS 
| REQREC_INTERFACE
), 
 320                         .bRequest      
= REQ_GetMaxLUN
, 
 322                         .wIndex        
= MSInterfaceInfo
->State
.InterfaceNumber
, 
 326         Pipe_SelectPipe(PIPE_CONTROLPIPE
); 
 328         if ((ErrorCode 
= USB_Host_SendControlRequest(MaxLUNIndex
)) == HOST_SENDCONTROL_SetupStalled
) 
 338 uint8_t MS_Host_GetInquiryData(USB_ClassInfo_MS_Host_t
* MSInterfaceInfo
, uint8_t LUNIndex
, SCSI_Inquiry_Response_t
* InquiryData
) 
 340         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.Active
)) 
 341           return HOST_SENDCONTROL_DeviceDisconnect
; 
 343         uint8_t ErrorCode 
= PIPE_RWSTREAM_NoError
; 
 345         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 347                         .Signature          
= CBW_SIGNATURE
, 
 348                         .DataTransferLength 
= sizeof(SCSI_Inquiry_Response_t
), 
 349                         .Flags              
= COMMAND_DIRECTION_DATA_IN
, 
 351                         .SCSICommandLength  
= 6, 
 358                                         sizeof(SCSI_Inquiry_Response_t
), // Allocation Length 
 359                                         0x00                             // Unused (control) 
 363         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 365         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, InquiryData
)) != PIPE_RWSTREAM_NoError
) 
 371         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 380 uint8_t MS_Host_TestUnitReady(USB_ClassInfo_MS_Host_t
* MSInterfaceInfo
, uint8_t LUNIndex
) 
 382         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.Active
)) 
 383           return HOST_SENDCONTROL_DeviceDisconnect
; 
 385         uint8_t ErrorCode 
= PIPE_RWSTREAM_NoError
;       
 387         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 389                         .Signature          
= CBW_SIGNATURE
, 
 390                         .DataTransferLength 
= 0, 
 391                         .Flags              
= COMMAND_DIRECTION_DATA_IN
, 
 393                         .SCSICommandLength  
= 6, 
 396                                         SCSI_CMD_TEST_UNIT_READY
, 
 401                                         0x00                    // Unused (control) 
 405         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 407         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, NULL
)) != PIPE_RWSTREAM_NoError
) 
 413         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 422 uint8_t MS_Host_ReadDeviceCapacity(USB_ClassInfo_MS_Host_t
* MSInterfaceInfo
, uint8_t LUNIndex
, 
 423                                    SCSI_Capacity_t
* DeviceCapacity
) 
 425         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.Active
)) 
 426           return HOST_SENDCONTROL_DeviceDisconnect
; 
 428         uint8_t ErrorCode 
= PIPE_RWSTREAM_NoError
; 
 430         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 432                         .Signature          
= CBW_SIGNATURE
, 
 433                         .DataTransferLength 
= sizeof(SCSI_Capacity_t
), 
 434                         .Flags              
= COMMAND_DIRECTION_DATA_IN
, 
 436                         .SCSICommandLength  
= 10, 
 439                                         SCSI_CMD_READ_CAPACITY_10
, 
 441                                         0x00,                   // MSB of Logical block address 
 444                                         0x00,                   // LSB of Logical block address 
 447                                         0x00,                   // Partial Medium Indicator 
 448                                         0x00                    // Unused (control) 
 452         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 454         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, DeviceCapacity
)) != PIPE_RWSTREAM_NoError
) 
 460         DeviceCapacity
->Blocks    
= SwapEndian_32(DeviceCapacity
->Blocks
); 
 461         DeviceCapacity
->BlockSize 
= SwapEndian_32(DeviceCapacity
->BlockSize
); 
 463         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 472 uint8_t MS_Host_RequestSense(USB_ClassInfo_MS_Host_t
* MSInterfaceInfo
, uint8_t LUNIndex
, 
 473                              SCSI_Request_Sense_Response_t
* SenseData
) 
 475         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.Active
)) 
 476           return HOST_SENDCONTROL_DeviceDisconnect
; 
 478         uint8_t ErrorCode 
= PIPE_RWSTREAM_NoError
; 
 480         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 482                         .Signature          
= CBW_SIGNATURE
, 
 483                         .DataTransferLength 
= sizeof(SCSI_Request_Sense_Response_t
), 
 484                         .Flags              
= COMMAND_DIRECTION_DATA_IN
, 
 486                         .SCSICommandLength  
= 6, 
 489                                         SCSI_CMD_REQUEST_SENSE
, 
 493                                         sizeof(SCSI_Request_Sense_Response_t
), // Allocation Length 
 494                                         0x00                                   // Unused (control) 
 498         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 500         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, SenseData
)) != PIPE_RWSTREAM_NoError
) 
 506         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 515 uint8_t MS_Host_PreventAllowMediumRemoval(USB_ClassInfo_MS_Host_t
* MSInterfaceInfo
, uint8_t LUNIndex
, bool PreventRemoval
) 
 517         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.Active
)) 
 518           return HOST_SENDCONTROL_DeviceDisconnect
; 
 520         uint8_t ErrorCode 
= PIPE_RWSTREAM_NoError
; 
 522         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 524                         .Signature          
= CBW_SIGNATURE
, 
 525                         .DataTransferLength 
= 0, 
 526                         .Flags              
= COMMAND_DIRECTION_DATA_OUT
, 
 528                         .SCSICommandLength  
= 6, 
 531                                         SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL
, 
 534                                         PreventRemoval
,         // Prevent flag 
 536                                         0x00                    // Unused (control) 
 540         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 542         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, NULL
)) != PIPE_RWSTREAM_NoError
) 
 548         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 557 uint8_t MS_Host_ReadDeviceBlocks(USB_ClassInfo_MS_Host_t
* MSInterfaceInfo
, uint8_t LUNIndex
, uint32_t BlockAddress
, 
 558                                  uint8_t Blocks
, uint16_t BlockSize
, void* BlockBuffer
) 
 560         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.Active
)) 
 561           return HOST_SENDCONTROL_DeviceDisconnect
; 
 563         uint8_t ErrorCode 
= PIPE_RWSTREAM_NoError
; 
 565         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 567                         .Signature          
= CBW_SIGNATURE
, 
 568                         .DataTransferLength 
= ((uint32_t)Blocks 
* BlockSize
), 
 569                         .Flags              
= COMMAND_DIRECTION_DATA_IN
, 
 571                         .SCSICommandLength  
= 10, 
 575                                         0x00,                   // Unused (control bits, all off) 
 576                                         (BlockAddress 
>> 24),   // MSB of Block Address 
 577                                         (BlockAddress 
>> 16), 
 579                                         (BlockAddress 
& 0xFF),  // LSB of Block Address 
 580                                         0x00,                   // Unused (reserved) 
 581                                         0x00,                   // MSB of Total Blocks to Read 
 582                                         Blocks
,                 // LSB of Total Blocks to Read 
 583                                         0x00                    // Unused (control) 
 587         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 589         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, BlockBuffer
)) != PIPE_RWSTREAM_NoError
) 
 595         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 604 uint8_t MS_Host_WriteDeviceBlocks(USB_ClassInfo_MS_Host_t
* MSInterfaceInfo
, uint8_t LUNIndex
, uint32_t BlockAddress
, 
 605                                   uint8_t Blocks
, uint16_t BlockSize
, void* BlockBuffer
) 
 607         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.Active
)) 
 608           return HOST_SENDCONTROL_DeviceDisconnect
; 
 610         uint8_t ErrorCode 
= PIPE_RWSTREAM_NoError
; 
 612         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 614                         .Signature          
= CBW_SIGNATURE
, 
 615                         .DataTransferLength 
= ((uint32_t)Blocks 
* BlockSize
), 
 616                         .Flags              
= COMMAND_DIRECTION_DATA_OUT
, 
 618                         .SCSICommandLength  
= 10, 
 622                                         0x00,                   // Unused (control bits, all off) 
 623                                         (BlockAddress 
>> 24),   // MSB of Block Address 
 624                                         (BlockAddress 
>> 16), 
 626                                         (BlockAddress 
& 0xFF),  // LSB of Block Address 
 627                                         0x00,                   // Unused (reserved) 
 628                                         0x00,                   // MSB of Total Blocks to Write 
 629                                         Blocks
,                 // LSB of Total Blocks to Write 
 630                                         0x00                    // Unused (control) 
 634         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 636         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, BlockBuffer
)) != PIPE_RWSTREAM_NoError
) 
 642         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
)