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
;