3      Copyright (C) Dean Camera, 2011. 
   5   dean [at] fourwalledcubicle [dot] com 
  10   Copyright 2011  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 "../../Core/USBMode.h" 
  34 #if defined(USB_CAN_BE_HOST) 
  36 #define  __INCLUDE_FROM_MS_DRIVER 
  37 #define  __INCLUDE_FROM_MASSSTORAGE_HOST_C 
  38 #include "MassStorage.h" 
  40 uint8_t MS_Host_ConfigurePipes(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
  41                                uint16_t ConfigDescriptorSize
, 
  42                                                            void* ConfigDescriptorData
) 
  44         USB_Descriptor_Endpoint_t
*  DataINEndpoint       
= NULL
; 
  45         USB_Descriptor_Endpoint_t
*  DataOUTEndpoint      
= NULL
; 
  46         USB_Descriptor_Interface_t
* MassStorageInterface 
= NULL
; 
  48         memset(&MSInterfaceInfo
->State
, 0x00, sizeof(MSInterfaceInfo
->State
)); 
  50         if (DESCRIPTOR_TYPE(ConfigDescriptorData
) != DTYPE_Configuration
) 
  51           return MS_ENUMERROR_InvalidConfigDescriptor
; 
  53         while (!(DataINEndpoint
) || !(DataOUTEndpoint
)) 
  55                 if (!(MassStorageInterface
) || 
  56                     USB_GetNextDescriptorComp(&ConfigDescriptorSize
, &ConfigDescriptorData
, 
  57                                               DCOMP_MS_Host_NextMSInterfaceEndpoint
) != DESCRIPTOR_SEARCH_COMP_Found
) 
  59                         if (USB_GetNextDescriptorComp(&ConfigDescriptorSize
, &ConfigDescriptorData
, 
  60                                                       DCOMP_MS_Host_NextMSInterface
) != DESCRIPTOR_SEARCH_COMP_Found
) 
  62                                 return MS_ENUMERROR_NoCompatibleInterfaceFound
; 
  65                         MassStorageInterface 
= DESCRIPTOR_PCAST(ConfigDescriptorData
, USB_Descriptor_Interface_t
); 
  67                         DataINEndpoint  
= NULL
; 
  68                         DataOUTEndpoint 
= NULL
; 
  73                 USB_Descriptor_Endpoint_t
* EndpointData 
= DESCRIPTOR_PCAST(ConfigDescriptorData
, USB_Descriptor_Endpoint_t
); 
  75                 if (EndpointData
->EndpointAddress 
& ENDPOINT_DESCRIPTOR_DIR_IN
) 
  76                   DataINEndpoint  
= EndpointData
; 
  78                   DataOUTEndpoint 
= EndpointData
; 
  81         for (uint8_t PipeNum 
= 1; PipeNum 
< PIPE_TOTAL_PIPES
; PipeNum
++) 
  86                 uint8_t  EndpointAddress
; 
  89                 if (PipeNum 
== MSInterfaceInfo
->Config
.DataINPipeNumber
) 
  91                         Size            
= DataINEndpoint
->EndpointSize
; 
  92                         EndpointAddress 
= DataINEndpoint
->EndpointAddress
; 
  93                         Token           
= PIPE_TOKEN_IN
; 
  95                         DoubleBanked    
= MSInterfaceInfo
->Config
.DataINPipeDoubleBank
; 
  97                         MSInterfaceInfo
->State
.DataINPipeSize 
= DataINEndpoint
->EndpointSize
; 
  99                 else if (PipeNum 
== MSInterfaceInfo
->Config
.DataOUTPipeNumber
) 
 101                         Size            
= DataOUTEndpoint
->EndpointSize
; 
 102                         EndpointAddress 
= DataOUTEndpoint
->EndpointAddress
; 
 103                         Token           
= PIPE_TOKEN_OUT
; 
 105                         DoubleBanked    
= MSInterfaceInfo
->Config
.DataOUTPipeDoubleBank
; 
 107                         MSInterfaceInfo
->State
.DataOUTPipeSize 
= DataOUTEndpoint
->EndpointSize
; 
 114                 if (!(Pipe_ConfigurePipe(PipeNum
, Type
, Token
, EndpointAddress
, Size
, 
 115                                          DoubleBanked ? PIPE_BANK_DOUBLE 
: PIPE_BANK_SINGLE
))) 
 117                         return MS_ENUMERROR_PipeConfigurationFailed
; 
 121         MSInterfaceInfo
->State
.InterfaceNumber 
= MassStorageInterface
->InterfaceNumber
; 
 122         MSInterfaceInfo
->State
.IsActive 
= true; 
 124         return MS_ENUMERROR_NoError
; 
 127 static uint8_t DCOMP_MS_Host_NextMSInterface(void* const CurrentDescriptor
) 
 129         USB_Descriptor_Header_t
* Header 
= DESCRIPTOR_PCAST(CurrentDescriptor
, USB_Descriptor_Header_t
); 
 131         if (Header
->Type 
== DTYPE_Interface
) 
 133                 USB_Descriptor_Interface_t
* Interface 
= DESCRIPTOR_PCAST(CurrentDescriptor
, USB_Descriptor_Interface_t
); 
 135                 if ((Interface
->Class    
== MS_CSCP_MassStorageClass
)        && 
 136                     (Interface
->SubClass 
== MS_CSCP_SCSITransparentSubclass
) && 
 137                     (Interface
->Protocol 
== MS_CSCP_BulkOnlyTransportProtocol
)) 
 139                         return DESCRIPTOR_SEARCH_Found
; 
 143         return DESCRIPTOR_SEARCH_NotFound
; 
 146 static uint8_t DCOMP_MS_Host_NextMSInterfaceEndpoint(void* const CurrentDescriptor
) 
 148         USB_Descriptor_Header_t
* Header 
= DESCRIPTOR_PCAST(CurrentDescriptor
, USB_Descriptor_Header_t
); 
 150         if (Header
->Type 
== DTYPE_Endpoint
) 
 152                 USB_Descriptor_Endpoint_t
* Endpoint 
= DESCRIPTOR_PCAST(CurrentDescriptor
, USB_Descriptor_Endpoint_t
); 
 154                 uint8_t EndpointType 
= (Endpoint
->Attributes 
& EP_TYPE_MASK
); 
 156                 if ((EndpointType 
== EP_TYPE_BULK
) && (!(Pipe_IsEndpointBound(Endpoint
->EndpointAddress
)))) 
 158                         return DESCRIPTOR_SEARCH_Found
; 
 161         else if (Header
->Type 
== DTYPE_Interface
) 
 163                 return DESCRIPTOR_SEARCH_Fail
; 
 166         return DESCRIPTOR_SEARCH_NotFound
; 
 169 static uint8_t MS_Host_SendCommand(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 170                                    MS_CommandBlockWrapper_t
* const SCSICommandBlock
, 
 171                                    const void* const BufferPtr
) 
 173         uint8_t ErrorCode 
= PIPE_RWSTREAM_NoError
; 
 175         SCSICommandBlock
->Signature 
= MS_CBW_SIGNATURE
; 
 176         SCSICommandBlock
->Tag       
= ++MSInterfaceInfo
->State
.TransactionTag
; 
 178         if (MSInterfaceInfo
->State
.TransactionTag 
== 0xFFFFFFFF) 
 179           MSInterfaceInfo
->State
.TransactionTag 
= 1; 
 181         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 184         if ((ErrorCode 
= Pipe_Write_Stream_LE(SCSICommandBlock
, sizeof(MS_CommandBlockWrapper_t
), 
 185                                               NULL
)) != PIPE_RWSTREAM_NoError
) 
 189         Pipe_WaitUntilReady(); 
 193         if ((BufferPtr 
!= NULL
) && 
 194             ((ErrorCode 
= MS_Host_SendReceiveData(MSInterfaceInfo
, SCSICommandBlock
, (void*)BufferPtr
)) != PIPE_RWSTREAM_NoError
)) 
 203 static uint8_t MS_Host_WaitForDataReceived(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
) 
 205         uint16_t TimeoutMSRem        
= MS_COMMAND_DATA_TIMEOUT_MS
; 
 206         uint16_t PreviousFrameNumber 
= USB_Host_GetFrameNumber(); 
 208         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 211         while (!(Pipe_IsINReceived())) 
 213                 uint16_t CurrentFrameNumber 
= USB_Host_GetFrameNumber(); 
 215                 if (CurrentFrameNumber 
!= PreviousFrameNumber
) 
 217                         PreviousFrameNumber 
= CurrentFrameNumber
; 
 219                         if (!(TimeoutMSRem
--)) 
 220                           return PIPE_RWSTREAM_Timeout
; 
 224                 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 227                 if (Pipe_IsStalled()) 
 229                         USB_Host_ClearPipeStall(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 231                         return PIPE_RWSTREAM_PipeStalled
; 
 235                 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 238                 if (Pipe_IsStalled()) 
 240                         USB_Host_ClearPipeStall(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 242                         return PIPE_RWSTREAM_PipeStalled
; 
 245                 if (USB_HostState 
== HOST_STATE_Unattached
) 
 246                   return PIPE_RWSTREAM_DeviceDisconnected
; 
 249         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 252         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 255         return PIPE_RWSTREAM_NoError
; 
 258 static uint8_t MS_Host_SendReceiveData(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 259                                        MS_CommandBlockWrapper_t
* const SCSICommandBlock
, 
 262         uint8_t  ErrorCode 
= PIPE_RWSTREAM_NoError
; 
 263         uint16_t BytesRem  
= SCSICommandBlock
->DataTransferLength
; 
 265         if (SCSICommandBlock
->Flags 
& MS_COMMAND_DIR_DATA_IN
) 
 267                 if ((ErrorCode 
= MS_Host_WaitForDataReceived(MSInterfaceInfo
)) != PIPE_RWSTREAM_NoError
) 
 273                 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 276                 if ((ErrorCode 
= Pipe_Read_Stream_LE(BufferPtr
, BytesRem
, NULL
)) != PIPE_RWSTREAM_NoError
) 
 283                 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 286                 if ((ErrorCode 
= Pipe_Write_Stream_LE(BufferPtr
, BytesRem
, NULL
)) != PIPE_RWSTREAM_NoError
) 
 291                 while (!(Pipe_IsOUTReady())) 
 293                         if (USB_HostState 
== HOST_STATE_Unattached
) 
 294                           return PIPE_RWSTREAM_DeviceDisconnected
; 
 303 static uint8_t MS_Host_GetReturnedStatus(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 304                                          MS_CommandStatusWrapper_t
* const SCSICommandStatus
) 
 306         uint8_t ErrorCode 
= PIPE_RWSTREAM_NoError
; 
 308         if ((ErrorCode 
= MS_Host_WaitForDataReceived(MSInterfaceInfo
)) != PIPE_RWSTREAM_NoError
) 
 311         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 314         if ((ErrorCode 
= Pipe_Read_Stream_LE(SCSICommandStatus
, sizeof(MS_CommandStatusWrapper_t
), 
 315                                              NULL
)) != PIPE_RWSTREAM_NoError
) 
 323         if (SCSICommandStatus
->Status 
!= MS_SCSI_COMMAND_Pass
) 
 324           ErrorCode 
= MS_ERROR_LOGICAL_CMD_FAILED
; 
 329 uint8_t MS_Host_ResetMSInterface(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
) 
 331         USB_ControlRequest 
= (USB_Request_Header_t
) 
 333                         .bmRequestType 
= (REQDIR_HOSTTODEVICE 
| REQTYPE_CLASS 
| REQREC_INTERFACE
), 
 334                         .bRequest      
= MS_REQ_MassStorageReset
, 
 336                         .wIndex        
= MSInterfaceInfo
->State
.InterfaceNumber
, 
 340         Pipe_SelectPipe(PIPE_CONTROLPIPE
); 
 342         return USB_Host_SendControlRequest(NULL
); 
 345 uint8_t MS_Host_GetMaxLUN(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 346                           uint8_t* const MaxLUNIndex
) 
 348         uint8_t ErrorCode 
= HOST_SENDCONTROL_Successful
; 
 350         USB_ControlRequest 
= (USB_Request_Header_t
) 
 352                         .bmRequestType 
= (REQDIR_DEVICETOHOST 
| REQTYPE_CLASS 
| REQREC_INTERFACE
), 
 353                         .bRequest      
= MS_REQ_GetMaxLUN
, 
 355                         .wIndex        
= MSInterfaceInfo
->State
.InterfaceNumber
, 
 359         Pipe_SelectPipe(PIPE_CONTROLPIPE
); 
 361         if ((ErrorCode 
= USB_Host_SendControlRequest(MaxLUNIndex
)) != HOST_SENDCONTROL_Successful
) 
 364                 ErrorCode    
= HOST_SENDCONTROL_Successful
; 
 370 uint8_t MS_Host_GetInquiryData(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 371                                const uint8_t LUNIndex
, 
 372                                SCSI_Inquiry_Response_t
* const InquiryData
) 
 374         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 375           return HOST_SENDCONTROL_DeviceDisconnected
; 
 379         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 381                         .DataTransferLength 
= sizeof(SCSI_Inquiry_Response_t
), 
 382                         .Flags              
= MS_COMMAND_DIR_DATA_IN
, 
 384                         .SCSICommandLength  
= 6, 
 391                                         sizeof(SCSI_Inquiry_Response_t
), // Allocation Length 
 392                                         0x00                             // Unused (control) 
 396         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 398         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, InquiryData
)) != PIPE_RWSTREAM_NoError
) 
 401         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 404         return PIPE_RWSTREAM_NoError
; 
 407 uint8_t MS_Host_TestUnitReady(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 408                               const uint8_t LUNIndex
) 
 410         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 411           return HOST_SENDCONTROL_DeviceDisconnected
; 
 415         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 417                         .DataTransferLength 
= 0, 
 418                         .Flags              
= MS_COMMAND_DIR_DATA_IN
, 
 420                         .SCSICommandLength  
= 6, 
 423                                         SCSI_CMD_TEST_UNIT_READY
, 
 428                                         0x00                    // Unused (control) 
 432         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 434         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, NULL
)) != PIPE_RWSTREAM_NoError
) 
 437         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 440         return PIPE_RWSTREAM_NoError
; 
 443 uint8_t MS_Host_ReadDeviceCapacity(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 444                                    const uint8_t LUNIndex
, 
 445                                    SCSI_Capacity_t
* const DeviceCapacity
) 
 447         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 448           return HOST_SENDCONTROL_DeviceDisconnected
; 
 452         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 454                         .DataTransferLength 
= sizeof(SCSI_Capacity_t
), 
 455                         .Flags              
= MS_COMMAND_DIR_DATA_IN
, 
 457                         .SCSICommandLength  
= 10, 
 460                                         SCSI_CMD_READ_CAPACITY_10
, 
 462                                         0x00,                   // MSB of Logical block address 
 465                                         0x00,                   // LSB of Logical block address 
 468                                         0x00,                   // Partial Medium Indicator 
 469                                         0x00                    // Unused (control) 
 473         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 475         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, DeviceCapacity
)) != PIPE_RWSTREAM_NoError
) 
 478         SwapEndian_n(&DeviceCapacity
->Blocks
,    sizeof(DeviceCapacity
->Blocks
)); 
 479         SwapEndian_n(&DeviceCapacity
->BlockSize
, sizeof(DeviceCapacity
->BlockSize
)); 
 481         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 484         return PIPE_RWSTREAM_NoError
; 
 487 uint8_t MS_Host_RequestSense(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 488                              const uint8_t LUNIndex
, 
 489                              SCSI_Request_Sense_Response_t
* const SenseData
) 
 491         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 492           return HOST_SENDCONTROL_DeviceDisconnected
; 
 496         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 498                         .DataTransferLength 
= sizeof(SCSI_Request_Sense_Response_t
), 
 499                         .Flags              
= MS_COMMAND_DIR_DATA_IN
, 
 501                         .SCSICommandLength  
= 6, 
 504                                         SCSI_CMD_REQUEST_SENSE
, 
 508                                         sizeof(SCSI_Request_Sense_Response_t
), // Allocation Length 
 509                                         0x00                                   // Unused (control) 
 513         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 515         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, SenseData
)) != PIPE_RWSTREAM_NoError
) 
 518         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 521         return PIPE_RWSTREAM_NoError
; 
 524 uint8_t MS_Host_PreventAllowMediumRemoval(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 525                                           const uint8_t LUNIndex
, 
 526                                           const bool PreventRemoval
) 
 528         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 529           return HOST_SENDCONTROL_DeviceDisconnected
; 
 533         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 535                         .DataTransferLength 
= 0, 
 536                         .Flags              
= MS_COMMAND_DIR_DATA_OUT
, 
 538                         .SCSICommandLength  
= 6, 
 541                                         SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL
, 
 544                                         PreventRemoval
,         // Prevent flag 
 546                                         0x00                    // Unused (control) 
 550         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 552         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, NULL
)) != PIPE_RWSTREAM_NoError
) 
 555         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 558         return PIPE_RWSTREAM_NoError
; 
 561 uint8_t MS_Host_ReadDeviceBlocks(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 562                                  const uint8_t LUNIndex
, 
 563                                  const uint32_t BlockAddress
, 
 564                                  const uint8_t Blocks
, 
 565                                  const uint16_t BlockSize
, 
 568         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 569           return HOST_SENDCONTROL_DeviceDisconnected
; 
 573         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 575                         .DataTransferLength 
= ((uint32_t)Blocks 
* BlockSize
), 
 576                         .Flags              
= MS_COMMAND_DIR_DATA_IN
, 
 578                         .SCSICommandLength  
= 10, 
 582                                         0x00,                   // Unused (control bits, all off) 
 583                                         (BlockAddress 
>> 24),   // MSB of Block Address 
 584                                         (BlockAddress 
>> 16), 
 586                                         (BlockAddress 
& 0xFF),  // LSB of Block Address 
 588                                         0x00,                   // MSB of Total Blocks to Read 
 589                                         Blocks
,                 // LSB of Total Blocks to Read 
 590                                         0x00                    // Unused (control) 
 594         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 596         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, BlockBuffer
)) != PIPE_RWSTREAM_NoError
) 
 599         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 602         return PIPE_RWSTREAM_NoError
; 
 605 uint8_t MS_Host_WriteDeviceBlocks(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 606                                   const uint8_t LUNIndex
, 
 607                                   const uint32_t BlockAddress
, 
 608                                   const uint8_t Blocks
, 
 609                                   const uint16_t BlockSize
, 
 610                                   const void* BlockBuffer
) 
 612         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 613           return HOST_SENDCONTROL_DeviceDisconnected
; 
 617         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 619                         .DataTransferLength 
= ((uint32_t)Blocks 
* BlockSize
), 
 620                         .Flags              
= MS_COMMAND_DIR_DATA_OUT
, 
 622                         .SCSICommandLength  
= 10, 
 626                                         0x00,                   // Unused (control bits, all off) 
 627                                         (BlockAddress 
>> 24),   // MSB of Block Address 
 628                                         (BlockAddress 
>> 16), 
 630                                         (BlockAddress 
& 0xFF),  // LSB of Block Address 
 632                                         0x00,                   // MSB of Total Blocks to Write 
 633                                         Blocks
,                 // LSB of Total Blocks to Write 
 634                                         0x00                    // Unused (control) 
 638         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 640         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, BlockBuffer
)) != PIPE_RWSTREAM_NoError
) 
 643         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 646         return PIPE_RWSTREAM_NoError
;