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 "../../HighLevel/USBMode.h" 
  33 #if defined(USB_CAN_BE_HOST) 
  35 #define  __INCLUDE_FROM_MS_DRIVER 
  36 #define  __INCLUDE_FROM_MASSSTORAGE_HOST_C 
  37 #include "MassStorage.h" 
  39 uint8_t MS_Host_ConfigurePipes(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
  40                                uint16_t ConfigDescriptorSize
, 
  41                                                            void* ConfigDescriptorData
) 
  43         USB_Descriptor_Endpoint_t
*  DataINEndpoint       
= NULL
; 
  44         USB_Descriptor_Endpoint_t
*  DataOUTEndpoint      
= NULL
; 
  45         USB_Descriptor_Interface_t
* MassStorageInterface 
= NULL
; 
  47         memset(&MSInterfaceInfo
->State
, 0x00, sizeof(MSInterfaceInfo
->State
)); 
  49         if (DESCRIPTOR_TYPE(ConfigDescriptorData
) != DTYPE_Configuration
) 
  50           return MS_ENUMERROR_InvalidConfigDescriptor
; 
  52         while (!(DataINEndpoint
) || !(DataOUTEndpoint
)) 
  54                 if (!(MassStorageInterface
) || 
  55                     USB_GetNextDescriptorComp(&ConfigDescriptorSize
, &ConfigDescriptorData
, 
  56                                               DCOMP_MS_Host_NextMSInterfaceEndpoint
) != DESCRIPTOR_SEARCH_COMP_Found
) 
  58                         if (USB_GetNextDescriptorComp(&ConfigDescriptorSize
, &ConfigDescriptorData
, 
  59                                                       DCOMP_MS_Host_NextMSInterface
) != DESCRIPTOR_SEARCH_COMP_Found
) 
  61                                 return MS_ENUMERROR_NoCompatibleInterfaceFound
; 
  64                         MassStorageInterface 
= DESCRIPTOR_PCAST(ConfigDescriptorData
, USB_Descriptor_Interface_t
); 
  66                         DataINEndpoint  
= NULL
; 
  67                         DataOUTEndpoint 
= NULL
; 
  72                 USB_Descriptor_Endpoint_t
* EndpointData 
= DESCRIPTOR_PCAST(ConfigDescriptorData
, USB_Descriptor_Endpoint_t
); 
  74                 if (EndpointData
->EndpointAddress 
& ENDPOINT_DESCRIPTOR_DIR_IN
) 
  75                   DataINEndpoint  
= EndpointData
; 
  77                   DataOUTEndpoint 
= EndpointData
; 
  80         for (uint8_t PipeNum 
= 1; PipeNum 
< PIPE_TOTAL_PIPES
; PipeNum
++) 
  85                 uint8_t  EndpointAddress
; 
  88                 if (PipeNum 
== MSInterfaceInfo
->Config
.DataINPipeNumber
) 
  90                         Size            
= DataINEndpoint
->EndpointSize
; 
  91                         EndpointAddress 
= DataINEndpoint
->EndpointAddress
; 
  92                         Token           
= PIPE_TOKEN_IN
; 
  94                         DoubleBanked    
= MSInterfaceInfo
->Config
.DataINPipeDoubleBank
; 
  96                         MSInterfaceInfo
->State
.DataINPipeSize 
= DataINEndpoint
->EndpointSize
; 
  98                 else if (PipeNum 
== MSInterfaceInfo
->Config
.DataOUTPipeNumber
) 
 100                         Size            
= DataOUTEndpoint
->EndpointSize
; 
 101                         EndpointAddress 
= DataOUTEndpoint
->EndpointAddress
; 
 102                         Token           
= PIPE_TOKEN_OUT
; 
 104                         DoubleBanked    
= MSInterfaceInfo
->Config
.DataOUTPipeDoubleBank
; 
 106                         MSInterfaceInfo
->State
.DataOUTPipeSize 
= DataOUTEndpoint
->EndpointSize
; 
 113                 if (!(Pipe_ConfigurePipe(PipeNum
, Type
, Token
, EndpointAddress
, Size
, 
 114                                          DoubleBanked ? PIPE_BANK_DOUBLE 
: PIPE_BANK_SINGLE
))) 
 116                         return MS_ENUMERROR_PipeConfigurationFailed
; 
 120         MSInterfaceInfo
->State
.InterfaceNumber 
= MassStorageInterface
->InterfaceNumber
; 
 121         MSInterfaceInfo
->State
.IsActive 
= true; 
 123         return MS_ENUMERROR_NoError
; 
 126 static uint8_t DCOMP_MS_Host_NextMSInterface(void* const CurrentDescriptor
) 
 128         USB_Descriptor_Header_t
* Header 
= DESCRIPTOR_PCAST(CurrentDescriptor
, USB_Descriptor_Header_t
); 
 130         if (Header
->Type 
== DTYPE_Interface
) 
 132                 USB_Descriptor_Interface_t
* Interface 
= DESCRIPTOR_PCAST(CurrentDescriptor
, USB_Descriptor_Interface_t
); 
 134                 if ((Interface
->Class    
== MS_CSCP_MassStorageClass
)        && 
 135                     (Interface
->SubClass 
== MS_CSCP_SCSITransparentSubclass
) && 
 136                     (Interface
->Protocol 
== MS_CSCP_BulkOnlyTransportProtocol
)) 
 138                         return DESCRIPTOR_SEARCH_Found
; 
 142         return DESCRIPTOR_SEARCH_NotFound
; 
 145 static uint8_t DCOMP_MS_Host_NextMSInterfaceEndpoint(void* const CurrentDescriptor
) 
 147         USB_Descriptor_Header_t
* Header 
= DESCRIPTOR_PCAST(CurrentDescriptor
, USB_Descriptor_Header_t
); 
 149         if (Header
->Type 
== DTYPE_Endpoint
) 
 151                 USB_Descriptor_Endpoint_t
* Endpoint 
= DESCRIPTOR_PCAST(CurrentDescriptor
, USB_Descriptor_Endpoint_t
); 
 153                 uint8_t EndpointType 
= (Endpoint
->Attributes 
& EP_TYPE_MASK
); 
 155                 if ((EndpointType 
== EP_TYPE_BULK
) && (!(Pipe_IsEndpointBound(Endpoint
->EndpointAddress
)))) 
 157                         return DESCRIPTOR_SEARCH_Found
; 
 160         else if (Header
->Type 
== DTYPE_Interface
) 
 162                 return DESCRIPTOR_SEARCH_Fail
; 
 165         return DESCRIPTOR_SEARCH_NotFound
; 
 168 static uint8_t MS_Host_SendCommand(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 169                                    MS_CommandBlockWrapper_t
* const SCSICommandBlock
, 
 170                                    const void* const BufferPtr
) 
 172         uint8_t ErrorCode 
= PIPE_RWSTREAM_NoError
; 
 174         SCSICommandBlock
->Signature 
= MS_CBW_SIGNATURE
; 
 175         SCSICommandBlock
->Tag       
= ++MSInterfaceInfo
->State
.TransactionTag
; 
 177         if (MSInterfaceInfo
->State
.TransactionTag 
== 0xFFFFFFFF) 
 178           MSInterfaceInfo
->State
.TransactionTag 
= 1; 
 180         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 183         if ((ErrorCode 
= Pipe_Write_Stream_LE(SCSICommandBlock
, sizeof(MS_CommandBlockWrapper_t
), 
 184                                               NO_STREAM_CALLBACK
)) != PIPE_RWSTREAM_NoError
) 
 188         Pipe_WaitUntilReady(); 
 192         if ((BufferPtr 
!= NULL
) && 
 193             ((ErrorCode 
= MS_Host_SendReceiveData(MSInterfaceInfo
, SCSICommandBlock
, (void*)BufferPtr
)) != PIPE_RWSTREAM_NoError
)) 
 202 static uint8_t MS_Host_WaitForDataReceived(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
) 
 204         uint16_t TimeoutMSRem        
= MS_COMMAND_DATA_TIMEOUT_MS
; 
 205         uint16_t PreviousFrameNumber 
= USB_Host_GetFrameNumber(); 
 207         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 210         while (!(Pipe_IsINReceived())) 
 212                 uint16_t CurrentFrameNumber 
= USB_Host_GetFrameNumber(); 
 214                 if (CurrentFrameNumber 
!= PreviousFrameNumber
) 
 216                         PreviousFrameNumber 
= CurrentFrameNumber
; 
 218                         if (!(TimeoutMSRem
--)) 
 219                           return PIPE_RWSTREAM_Timeout
; 
 223                 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 226                 if (Pipe_IsStalled()) 
 228                         USB_Host_ClearPipeStall(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 230                         return PIPE_RWSTREAM_PipeStalled
; 
 234                 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 237                 if (Pipe_IsStalled()) 
 239                         USB_Host_ClearPipeStall(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 241                         return PIPE_RWSTREAM_PipeStalled
; 
 244                 if (USB_HostState 
== HOST_STATE_Unattached
) 
 245                   return PIPE_RWSTREAM_DeviceDisconnected
; 
 248         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 251         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 254         return PIPE_RWSTREAM_NoError
; 
 257 static uint8_t MS_Host_SendReceiveData(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 258                                        MS_CommandBlockWrapper_t
* const SCSICommandBlock
, 
 261         uint8_t  ErrorCode 
= PIPE_RWSTREAM_NoError
; 
 262         uint16_t BytesRem  
= SCSICommandBlock
->DataTransferLength
; 
 264         if (SCSICommandBlock
->Flags 
& MS_COMMAND_DIR_DATA_IN
) 
 266                 if ((ErrorCode 
= MS_Host_WaitForDataReceived(MSInterfaceInfo
)) != PIPE_RWSTREAM_NoError
) 
 272                 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 275                 if ((ErrorCode 
= Pipe_Read_Stream_LE(BufferPtr
, BytesRem
, NO_STREAM_CALLBACK
)) != PIPE_RWSTREAM_NoError
) 
 282                 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 285                 if ((ErrorCode 
= Pipe_Write_Stream_LE(BufferPtr
, BytesRem
, NO_STREAM_CALLBACK
)) != PIPE_RWSTREAM_NoError
) 
 290                 while (!(Pipe_IsOUTReady())) 
 292                         if (USB_HostState 
== HOST_STATE_Unattached
) 
 293                           return PIPE_RWSTREAM_DeviceDisconnected
; 
 302 static uint8_t MS_Host_GetReturnedStatus(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 303                                          MS_CommandStatusWrapper_t
* const SCSICommandStatus
) 
 305         uint8_t ErrorCode 
= PIPE_RWSTREAM_NoError
; 
 307         if ((ErrorCode 
= MS_Host_WaitForDataReceived(MSInterfaceInfo
)) != PIPE_RWSTREAM_NoError
) 
 310         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 313         if ((ErrorCode 
= Pipe_Read_Stream_LE(SCSICommandStatus
, sizeof(MS_CommandStatusWrapper_t
), 
 314                                              NO_STREAM_CALLBACK
)) != PIPE_RWSTREAM_NoError
) 
 322         if (SCSICommandStatus
->Status 
!= MS_SCSI_COMMAND_Pass
) 
 323           ErrorCode 
= MS_ERROR_LOGICAL_CMD_FAILED
; 
 328 uint8_t MS_Host_ResetMSInterface(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
) 
 330         USB_ControlRequest 
= (USB_Request_Header_t
) 
 332                         .bmRequestType 
= (REQDIR_HOSTTODEVICE 
| REQTYPE_CLASS 
| REQREC_INTERFACE
), 
 333                         .bRequest      
= MS_REQ_MassStorageReset
, 
 335                         .wIndex        
= MSInterfaceInfo
->State
.InterfaceNumber
, 
 339         Pipe_SelectPipe(PIPE_CONTROLPIPE
); 
 341         return USB_Host_SendControlRequest(NULL
); 
 344 uint8_t MS_Host_GetMaxLUN(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 345                           uint8_t* const MaxLUNIndex
) 
 347         uint8_t ErrorCode 
= HOST_SENDCONTROL_Successful
; 
 349         USB_ControlRequest 
= (USB_Request_Header_t
) 
 351                         .bmRequestType 
= (REQDIR_DEVICETOHOST 
| REQTYPE_CLASS 
| REQREC_INTERFACE
), 
 352                         .bRequest      
= MS_REQ_GetMaxLUN
, 
 354                         .wIndex        
= MSInterfaceInfo
->State
.InterfaceNumber
, 
 358         Pipe_SelectPipe(PIPE_CONTROLPIPE
); 
 360         if ((ErrorCode 
= USB_Host_SendControlRequest(MaxLUNIndex
)) != HOST_SENDCONTROL_Successful
) 
 363                 ErrorCode 
= HOST_SENDCONTROL_Successful
; 
 369 uint8_t MS_Host_GetInquiryData(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 370                                const uint8_t LUNIndex
, 
 371                                SCSI_Inquiry_Response_t
* const InquiryData
) 
 373         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 374           return HOST_SENDCONTROL_DeviceDisconnected
; 
 378         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 380                         .DataTransferLength 
= sizeof(SCSI_Inquiry_Response_t
), 
 381                         .Flags              
= MS_COMMAND_DIR_DATA_IN
, 
 383                         .SCSICommandLength  
= 6, 
 390                                         sizeof(SCSI_Inquiry_Response_t
), // Allocation Length 
 391                                         0x00                             // Unused (control) 
 395         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 397         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, InquiryData
)) != PIPE_RWSTREAM_NoError
) 
 400         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 403         return PIPE_RWSTREAM_NoError
; 
 406 uint8_t MS_Host_TestUnitReady(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 407                               const uint8_t LUNIndex
) 
 409         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 410           return HOST_SENDCONTROL_DeviceDisconnected
; 
 414         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 416                         .DataTransferLength 
= 0, 
 417                         .Flags              
= MS_COMMAND_DIR_DATA_IN
, 
 419                         .SCSICommandLength  
= 6, 
 422                                         SCSI_CMD_TEST_UNIT_READY
, 
 427                                         0x00                    // Unused (control) 
 431         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 433         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, NULL
)) != PIPE_RWSTREAM_NoError
) 
 436         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 439         return PIPE_RWSTREAM_NoError
; 
 442 uint8_t MS_Host_ReadDeviceCapacity(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 443                                    const uint8_t LUNIndex
, 
 444                                    SCSI_Capacity_t
* const DeviceCapacity
) 
 446         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 447           return HOST_SENDCONTROL_DeviceDisconnected
; 
 451         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 453                         .DataTransferLength 
= sizeof(SCSI_Capacity_t
), 
 454                         .Flags              
= MS_COMMAND_DIR_DATA_IN
, 
 456                         .SCSICommandLength  
= 10, 
 459                                         SCSI_CMD_READ_CAPACITY_10
, 
 461                                         0x00,                   // MSB of Logical block address 
 464                                         0x00,                   // LSB of Logical block address 
 467                                         0x00,                   // Partial Medium Indicator 
 468                                         0x00                    // Unused (control) 
 472         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 474         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, DeviceCapacity
)) != PIPE_RWSTREAM_NoError
) 
 477         SwapEndian_n(&DeviceCapacity
->Blocks
,    sizeof(DeviceCapacity
->Blocks
)); 
 478         SwapEndian_n(&DeviceCapacity
->BlockSize
, sizeof(DeviceCapacity
->BlockSize
)); 
 480         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 483         return PIPE_RWSTREAM_NoError
; 
 486 uint8_t MS_Host_RequestSense(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 487                              const uint8_t LUNIndex
, 
 488                              SCSI_Request_Sense_Response_t
* const SenseData
) 
 490         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 491           return HOST_SENDCONTROL_DeviceDisconnected
; 
 495         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 497                         .DataTransferLength 
= sizeof(SCSI_Request_Sense_Response_t
), 
 498                         .Flags              
= MS_COMMAND_DIR_DATA_IN
, 
 500                         .SCSICommandLength  
= 6, 
 503                                         SCSI_CMD_REQUEST_SENSE
, 
 507                                         sizeof(SCSI_Request_Sense_Response_t
), // Allocation Length 
 508                                         0x00                                   // Unused (control) 
 512         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 514         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, SenseData
)) != PIPE_RWSTREAM_NoError
) 
 517         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 520         return PIPE_RWSTREAM_NoError
; 
 523 uint8_t MS_Host_PreventAllowMediumRemoval(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 524                                           const uint8_t LUNIndex
, 
 525                                           const bool PreventRemoval
) 
 527         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 528           return HOST_SENDCONTROL_DeviceDisconnected
; 
 532         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 534                         .DataTransferLength 
= 0, 
 535                         .Flags              
= MS_COMMAND_DIR_DATA_OUT
, 
 537                         .SCSICommandLength  
= 6, 
 540                                         SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL
, 
 543                                         PreventRemoval
,         // Prevent flag 
 545                                         0x00                    // Unused (control) 
 549         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 551         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, NULL
)) != PIPE_RWSTREAM_NoError
) 
 554         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 557         return PIPE_RWSTREAM_NoError
; 
 560 uint8_t MS_Host_ReadDeviceBlocks(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 561                                  const uint8_t LUNIndex
, 
 562                                  const uint32_t BlockAddress
, 
 563                                  const uint8_t Blocks
, 
 564                                  const uint16_t BlockSize
, 
 567         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 568           return HOST_SENDCONTROL_DeviceDisconnected
; 
 572         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 574                         .DataTransferLength 
= ((uint32_t)Blocks 
* BlockSize
), 
 575                         .Flags              
= MS_COMMAND_DIR_DATA_IN
, 
 577                         .SCSICommandLength  
= 10, 
 581                                         0x00,                   // Unused (control bits, all off) 
 582                                         (BlockAddress 
>> 24),   // MSB of Block Address 
 583                                         (BlockAddress 
>> 16), 
 585                                         (BlockAddress 
& 0xFF),  // LSB of Block Address 
 587                                         0x00,                   // MSB of Total Blocks to Read 
 588                                         Blocks
,                 // LSB of Total Blocks to Read 
 589                                         0x00                    // Unused (control) 
 593         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 595         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, BlockBuffer
)) != PIPE_RWSTREAM_NoError
) 
 598         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 601         return PIPE_RWSTREAM_NoError
; 
 604 uint8_t MS_Host_WriteDeviceBlocks(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 605                                   const uint8_t LUNIndex
, 
 606                                   const uint32_t BlockAddress
, 
 607                                   const uint8_t Blocks
, 
 608                                   const uint16_t BlockSize
, 
 609                                   const void* BlockBuffer
) 
 611         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 612           return HOST_SENDCONTROL_DeviceDisconnected
; 
 616         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 618                         .DataTransferLength 
= ((uint32_t)Blocks 
* BlockSize
), 
 619                         .Flags              
= MS_COMMAND_DIR_DATA_OUT
, 
 621                         .SCSICommandLength  
= 10, 
 625                                         0x00,                   // Unused (control bits, all off) 
 626                                         (BlockAddress 
>> 24),   // MSB of Block Address 
 627                                         (BlockAddress 
>> 16), 
 629                                         (BlockAddress 
& 0xFF),  // LSB of Block Address 
 631                                         0x00,                   // MSB of Total Blocks to Write 
 632                                         Blocks
,                 // LSB of Total Blocks to Write 
 633                                         0x00                    // Unused (control) 
 637         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 639         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, BlockBuffer
)) != PIPE_RWSTREAM_NoError
) 
 642         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 645         return PIPE_RWSTREAM_NoError
;