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
, 
  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
++) 
  82                 if (PipeNum 
== MSInterfaceInfo
->Config
.DataINPipeNumber
) 
  84                         Pipe_ConfigurePipe(PipeNum
, EP_TYPE_BULK
, PIPE_TOKEN_IN
, 
  85                                                            DataINEndpoint
->EndpointAddress
, DataINEndpoint
->EndpointSize
, 
  86                                                            MSInterfaceInfo
->Config
.DataINPipeDoubleBank ? PIPE_BANK_DOUBLE 
: PIPE_BANK_SINGLE
); 
  88                         MSInterfaceInfo
->State
.DataINPipeSize 
= DataINEndpoint
->EndpointSize
;                    
  90                 else if (PipeNum 
== MSInterfaceInfo
->Config
.DataOUTPipeNumber
) 
  92                         Pipe_ConfigurePipe(PipeNum
, EP_TYPE_BULK
, PIPE_TOKEN_OUT
, 
  93                                                            DataOUTEndpoint
->EndpointAddress
, DataOUTEndpoint
->EndpointSize
, 
  94                                                            MSInterfaceInfo
->Config
.DataOUTPipeDoubleBank ? PIPE_BANK_DOUBLE 
: PIPE_BANK_SINGLE
); 
  96                         MSInterfaceInfo
->State
.DataOUTPipeSize 
= DataOUTEndpoint
->EndpointSize
;                  
 100         MSInterfaceInfo
->State
.InterfaceNumber 
= MassStorageInterface
->InterfaceNumber
; 
 101         MSInterfaceInfo
->State
.IsActive 
= true; 
 103         return MS_ENUMERROR_NoError
; 
 106 static uint8_t DCOMP_MS_Host_NextMSInterface(void* const CurrentDescriptor
) 
 108         if (DESCRIPTOR_TYPE(CurrentDescriptor
) == DTYPE_Interface
) 
 110                 USB_Descriptor_Interface_t
* CurrentInterface 
= DESCRIPTOR_PCAST(CurrentDescriptor
, 
 111                                                                                 USB_Descriptor_Interface_t
); 
 113                 if ((CurrentInterface
->Class    
== MASS_STORE_CLASS
)    && 
 114                     (CurrentInterface
->SubClass 
== MASS_STORE_SUBCLASS
) && 
 115                     (CurrentInterface
->Protocol 
== MASS_STORE_PROTOCOL
)) 
 117                         return DESCRIPTOR_SEARCH_Found
; 
 121         return DESCRIPTOR_SEARCH_NotFound
; 
 124 static uint8_t DCOMP_MS_Host_NextMSInterfaceEndpoint(void* const CurrentDescriptor
) 
 126         if (DESCRIPTOR_TYPE(CurrentDescriptor
) == DTYPE_Endpoint
) 
 128                 USB_Descriptor_Endpoint_t
* CurrentEndpoint 
= DESCRIPTOR_PCAST(CurrentDescriptor
, 
 129                                                                               USB_Descriptor_Endpoint_t
); 
 131                 uint8_t EndpointType 
= (CurrentEndpoint
->Attributes 
& EP_TYPE_MASK
); 
 133                 if ((EndpointType 
== EP_TYPE_BULK
) && 
 134                     (!(Pipe_IsEndpointBound(CurrentEndpoint
->EndpointAddress
)))) 
 136                         return DESCRIPTOR_SEARCH_Found
; 
 139         else if (DESCRIPTOR_TYPE(CurrentDescriptor
) == DTYPE_Interface
) 
 141                 return DESCRIPTOR_SEARCH_Fail
; 
 144         return DESCRIPTOR_SEARCH_NotFound
; 
 147 static uint8_t MS_Host_SendCommand(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 148                                    MS_CommandBlockWrapper_t
* const SCSICommandBlock
, 
 149                                    const void* const BufferPtr
) 
 151         uint8_t ErrorCode 
= PIPE_RWSTREAM_NoError
; 
 153         SCSICommandBlock
->Signature 
= CBW_SIGNATURE
; 
 154         SCSICommandBlock
->Tag       
= ++MSInterfaceInfo
->State
.TransactionTag
; 
 156         if (MSInterfaceInfo
->State
.TransactionTag 
== 0xFFFFFFFF) 
 157           MSInterfaceInfo
->State
.TransactionTag 
= 1; 
 159         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 162         if ((ErrorCode 
= Pipe_Write_Stream_LE(SCSICommandBlock
, sizeof(MS_CommandBlockWrapper_t
), 
 163                                               NO_STREAM_CALLBACK
)) != PIPE_RWSTREAM_NoError
) 
 167         Pipe_WaitUntilReady(); 
 171         if ((BufferPtr 
!= NULL
) && 
 172             ((ErrorCode 
= MS_Host_SendReceiveData(MSInterfaceInfo
, SCSICommandBlock
, (void*)BufferPtr
)) != PIPE_RWSTREAM_NoError
)) 
 181 static uint8_t MS_Host_WaitForDataReceived(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
) 
 183         uint16_t TimeoutMSRem        
= COMMAND_DATA_TIMEOUT_MS
; 
 184         uint16_t PreviousFrameNumber 
= USB_Host_GetFrameNumber(); 
 186         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 189         while (!(Pipe_IsINReceived())) 
 191                 uint16_t CurrentFrameNumber 
= USB_Host_GetFrameNumber(); 
 193                 if (CurrentFrameNumber 
!= PreviousFrameNumber
) 
 195                         PreviousFrameNumber 
= CurrentFrameNumber
; 
 197                         if (!(TimeoutMSRem
--)) 
 198                           return PIPE_RWSTREAM_Timeout
; 
 202                 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 205                 if (Pipe_IsStalled()) 
 207                         USB_Host_ClearPipeStall(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 209                         return PIPE_RWSTREAM_PipeStalled
; 
 213                 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 216                 if (Pipe_IsStalled()) 
 218                         USB_Host_ClearPipeStall(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 220                         return PIPE_RWSTREAM_PipeStalled
; 
 223                 if (USB_HostState 
== HOST_STATE_Unattached
) 
 224                   return PIPE_RWSTREAM_DeviceDisconnected
; 
 227         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 230         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 233         return PIPE_RWSTREAM_NoError
; 
 236 static uint8_t MS_Host_SendReceiveData(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 237                                        MS_CommandBlockWrapper_t
* const SCSICommandBlock
, 
 240         uint8_t  ErrorCode 
= PIPE_RWSTREAM_NoError
; 
 241         uint16_t BytesRem  
= SCSICommandBlock
->DataTransferLength
; 
 243         if (SCSICommandBlock
->Flags 
& COMMAND_DIRECTION_DATA_IN
) 
 245                 if ((ErrorCode 
= MS_Host_WaitForDataReceived(MSInterfaceInfo
)) != PIPE_RWSTREAM_NoError
) 
 251                 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 254                 if ((ErrorCode 
= Pipe_Read_Stream_LE(BufferPtr
, BytesRem
, NO_STREAM_CALLBACK
)) != PIPE_RWSTREAM_NoError
) 
 261                 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
); 
 264                 if ((ErrorCode 
= Pipe_Write_Stream_LE(BufferPtr
, BytesRem
, NO_STREAM_CALLBACK
)) != PIPE_RWSTREAM_NoError
) 
 269                 while (!(Pipe_IsOUTReady())) 
 271                         if (USB_HostState 
== HOST_STATE_Unattached
) 
 272                           return PIPE_RWSTREAM_DeviceDisconnected
; 
 281 static uint8_t MS_Host_GetReturnedStatus(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 282                                          MS_CommandStatusWrapper_t
* const SCSICommandStatus
) 
 284         uint8_t ErrorCode 
= PIPE_RWSTREAM_NoError
; 
 286         if ((ErrorCode 
= MS_Host_WaitForDataReceived(MSInterfaceInfo
)) != PIPE_RWSTREAM_NoError
) 
 289         Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
); 
 292         if ((ErrorCode 
= Pipe_Read_Stream_LE(SCSICommandStatus
, sizeof(MS_CommandStatusWrapper_t
), 
 293                                              NO_STREAM_CALLBACK
)) != PIPE_RWSTREAM_NoError
) 
 301         if (SCSICommandStatus
->Status 
!= MS_SCSI_COMMAND_Pass
) 
 302           ErrorCode 
= MS_ERROR_LOGICAL_CMD_FAILED
; 
 307 uint8_t MS_Host_ResetMSInterface(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
) 
 309         USB_ControlRequest 
= (USB_Request_Header_t
) 
 311                         .bmRequestType 
= (REQDIR_HOSTTODEVICE 
| REQTYPE_CLASS 
| REQREC_INTERFACE
), 
 312                         .bRequest      
= REQ_MassStorageReset
, 
 314                         .wIndex        
= MSInterfaceInfo
->State
.InterfaceNumber
, 
 318         Pipe_SelectPipe(PIPE_CONTROLPIPE
); 
 320         return USB_Host_SendControlRequest(NULL
); 
 323 uint8_t MS_Host_GetMaxLUN(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 324                           uint8_t* const MaxLUNIndex
) 
 326         uint8_t ErrorCode 
= HOST_SENDCONTROL_Successful
; 
 328         USB_ControlRequest 
= (USB_Request_Header_t
) 
 330                         .bmRequestType 
= (REQDIR_DEVICETOHOST 
| REQTYPE_CLASS 
| REQREC_INTERFACE
), 
 331                         .bRequest      
= REQ_GetMaxLUN
, 
 333                         .wIndex        
= MSInterfaceInfo
->State
.InterfaceNumber
, 
 337         Pipe_SelectPipe(PIPE_CONTROLPIPE
); 
 339         if ((ErrorCode 
= USB_Host_SendControlRequest(MaxLUNIndex
)) != HOST_SENDCONTROL_Successful
) 
 342                 ErrorCode 
= HOST_SENDCONTROL_Successful
; 
 348 uint8_t MS_Host_GetInquiryData(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 349                                const uint8_t LUNIndex
, 
 350                                SCSI_Inquiry_Response_t
* const InquiryData
) 
 352         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 353           return HOST_SENDCONTROL_DeviceDisconnected
; 
 357         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 359                         .DataTransferLength 
= sizeof(SCSI_Inquiry_Response_t
), 
 360                         .Flags              
= COMMAND_DIRECTION_DATA_IN
, 
 362                         .SCSICommandLength  
= 6, 
 369                                         sizeof(SCSI_Inquiry_Response_t
), // Allocation Length 
 370                                         0x00                             // Unused (control) 
 374         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 376         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, InquiryData
)) != PIPE_RWSTREAM_NoError
) 
 379         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 382         return PIPE_RWSTREAM_NoError
; 
 385 uint8_t MS_Host_TestUnitReady(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 386                               const uint8_t LUNIndex
) 
 388         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 389           return HOST_SENDCONTROL_DeviceDisconnected
; 
 393         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 395                         .DataTransferLength 
= 0, 
 396                         .Flags              
= COMMAND_DIRECTION_DATA_IN
, 
 398                         .SCSICommandLength  
= 6, 
 401                                         SCSI_CMD_TEST_UNIT_READY
, 
 406                                         0x00                    // Unused (control) 
 410         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 412         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, NULL
)) != PIPE_RWSTREAM_NoError
) 
 415         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 418         return PIPE_RWSTREAM_NoError
; 
 421 uint8_t MS_Host_ReadDeviceCapacity(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 422                                    const uint8_t LUNIndex
, 
 423                                    SCSI_Capacity_t
* const DeviceCapacity
) 
 425         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 426           return HOST_SENDCONTROL_DeviceDisconnected
; 
 430         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 432                         .DataTransferLength 
= sizeof(SCSI_Capacity_t
), 
 433                         .Flags              
= COMMAND_DIRECTION_DATA_IN
, 
 435                         .SCSICommandLength  
= 10, 
 438                                         SCSI_CMD_READ_CAPACITY_10
, 
 440                                         0x00,                   // MSB of Logical block address 
 443                                         0x00,                   // LSB of Logical block address 
 446                                         0x00,                   // Partial Medium Indicator 
 447                                         0x00                    // Unused (control) 
 451         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 453         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, DeviceCapacity
)) != PIPE_RWSTREAM_NoError
) 
 456         SwapEndian_n(&DeviceCapacity
->Blocks
,    sizeof(DeviceCapacity
->Blocks
)); 
 457         SwapEndian_n(&DeviceCapacity
->BlockSize
, sizeof(DeviceCapacity
->BlockSize
)); 
 459         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 462         return PIPE_RWSTREAM_NoError
; 
 465 uint8_t MS_Host_RequestSense(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 466                              const uint8_t LUNIndex
, 
 467                              SCSI_Request_Sense_Response_t
* const SenseData
) 
 469         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 470           return HOST_SENDCONTROL_DeviceDisconnected
; 
 474         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 476                         .DataTransferLength 
= sizeof(SCSI_Request_Sense_Response_t
), 
 477                         .Flags              
= COMMAND_DIRECTION_DATA_IN
, 
 479                         .SCSICommandLength  
= 6, 
 482                                         SCSI_CMD_REQUEST_SENSE
, 
 486                                         sizeof(SCSI_Request_Sense_Response_t
), // Allocation Length 
 487                                         0x00                                   // Unused (control) 
 491         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 493         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, SenseData
)) != PIPE_RWSTREAM_NoError
) 
 496         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 499         return PIPE_RWSTREAM_NoError
; 
 502 uint8_t MS_Host_PreventAllowMediumRemoval(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 503                                           const uint8_t LUNIndex
, 
 504                                           const bool PreventRemoval
) 
 506         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 507           return HOST_SENDCONTROL_DeviceDisconnected
; 
 511         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 513                         .DataTransferLength 
= 0, 
 514                         .Flags              
= COMMAND_DIRECTION_DATA_OUT
, 
 516                         .SCSICommandLength  
= 6, 
 519                                         SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL
, 
 522                                         PreventRemoval
,         // Prevent flag 
 524                                         0x00                    // Unused (control) 
 528         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 530         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, NULL
)) != PIPE_RWSTREAM_NoError
) 
 533         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 536         return PIPE_RWSTREAM_NoError
; 
 539 uint8_t MS_Host_ReadDeviceBlocks(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 540                                  const uint8_t LUNIndex
, 
 541                                  const uint32_t BlockAddress
, 
 542                                  const uint8_t Blocks
, 
 543                                  const uint16_t BlockSize
, 
 546         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 547           return HOST_SENDCONTROL_DeviceDisconnected
; 
 551         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 553                         .DataTransferLength 
= ((uint32_t)Blocks 
* BlockSize
), 
 554                         .Flags              
= COMMAND_DIRECTION_DATA_IN
, 
 556                         .SCSICommandLength  
= 10, 
 560                                         0x00,                   // Unused (control bits, all off) 
 561                                         (BlockAddress 
>> 24),   // MSB of Block Address 
 562                                         (BlockAddress 
>> 16), 
 564                                         (BlockAddress 
& 0xFF),  // LSB of Block Address 
 566                                         0x00,                   // MSB of Total Blocks to Read 
 567                                         Blocks
,                 // LSB of Total Blocks to Read 
 568                                         0x00                    // Unused (control) 
 572         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 574         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, BlockBuffer
)) != PIPE_RWSTREAM_NoError
) 
 577         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 580         return PIPE_RWSTREAM_NoError
; 
 583 uint8_t MS_Host_WriteDeviceBlocks(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, 
 584                                   const uint8_t LUNIndex
, 
 585                                   const uint32_t BlockAddress
, 
 586                                   const uint8_t Blocks
, 
 587                                   const uint16_t BlockSize
, 
 588                                   const void* BlockBuffer
) 
 590         if ((USB_HostState 
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
)) 
 591           return HOST_SENDCONTROL_DeviceDisconnected
; 
 595         MS_CommandBlockWrapper_t SCSICommandBlock 
= (MS_CommandBlockWrapper_t
) 
 597                         .DataTransferLength 
= ((uint32_t)Blocks 
* BlockSize
), 
 598                         .Flags              
= COMMAND_DIRECTION_DATA_OUT
, 
 600                         .SCSICommandLength  
= 10, 
 604                                         0x00,                   // Unused (control bits, all off) 
 605                                         (BlockAddress 
>> 24),   // MSB of Block Address 
 606                                         (BlockAddress 
>> 16), 
 608                                         (BlockAddress 
& 0xFF),  // LSB of Block Address 
 610                                         0x00,                   // MSB of Total Blocks to Write 
 611                                         Blocks
,                 // LSB of Total Blocks to Write 
 612                                         0x00                    // Unused (control) 
 616         MS_CommandStatusWrapper_t SCSICommandStatus
; 
 618         if ((ErrorCode 
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, BlockBuffer
)) != PIPE_RWSTREAM_NoError
) 
 621         if ((ErrorCode 
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
) 
 624         return PIPE_RWSTREAM_NoError
;