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 "MassStorageClassHost.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_DIR_MASK
) == ENDPOINT_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
= le16_to_cpu(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
= le16_to_cpu(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 if (++MSInterfaceInfo
->State
.TransactionTag
== 0xFFFFFFFF)
176 MSInterfaceInfo
->State
.TransactionTag
= 1;
178 SCSICommandBlock
->Signature
= CPU_TO_LE32(MS_CBW_SIGNATURE
);
179 SCSICommandBlock
->Tag
= cpu_to_le32(MSInterfaceInfo
->State
.TransactionTag
);
181 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
);
184 if ((ErrorCode
= Pipe_Write_Stream_LE(SCSICommandBlock
, sizeof(MS_CommandBlockWrapper_t
),
185 NULL
)) != PIPE_RWSTREAM_NoError
)
191 Pipe_WaitUntilReady();
195 if (BufferPtr
!= NULL
)
197 ErrorCode
= MS_Host_SendReceiveData(MSInterfaceInfo
, SCSICommandBlock
, (void*)BufferPtr
);
199 if ((ErrorCode
!= PIPE_RWSTREAM_NoError
) && (ErrorCode
!= PIPE_RWSTREAM_PipeStalled
))
206 MS_CommandStatusWrapper_t SCSIStatusBlock
;
207 return MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSIStatusBlock
);
210 static uint8_t MS_Host_WaitForDataReceived(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
)
212 uint16_t TimeoutMSRem
= MS_COMMAND_DATA_TIMEOUT_MS
;
213 uint16_t PreviousFrameNumber
= USB_Host_GetFrameNumber();
215 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
);
218 while (!(Pipe_IsINReceived()))
220 uint16_t CurrentFrameNumber
= USB_Host_GetFrameNumber();
222 if (CurrentFrameNumber
!= PreviousFrameNumber
)
224 PreviousFrameNumber
= CurrentFrameNumber
;
226 if (!(TimeoutMSRem
--))
227 return PIPE_RWSTREAM_Timeout
;
231 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
);
234 if (Pipe_IsStalled())
236 USB_Host_ClearEndpointStall(Pipe_GetBoundEndpointAddress());
237 return PIPE_RWSTREAM_PipeStalled
;
241 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
);
244 if (Pipe_IsStalled())
246 USB_Host_ClearEndpointStall(Pipe_GetBoundEndpointAddress());
247 return PIPE_RWSTREAM_PipeStalled
;
250 if (USB_HostState
== HOST_STATE_Unattached
)
251 return PIPE_RWSTREAM_DeviceDisconnected
;
254 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
);
257 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
);
260 return PIPE_RWSTREAM_NoError
;
263 static uint8_t MS_Host_SendReceiveData(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
,
264 MS_CommandBlockWrapper_t
* const SCSICommandBlock
,
267 uint8_t ErrorCode
= PIPE_RWSTREAM_NoError
;
268 uint16_t BytesRem
= le32_to_cpu(SCSICommandBlock
->DataTransferLength
);
270 if (SCSICommandBlock
->Flags
& MS_COMMAND_DIR_DATA_IN
)
272 if ((ErrorCode
= MS_Host_WaitForDataReceived(MSInterfaceInfo
)) != PIPE_RWSTREAM_NoError
)
278 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
);
281 if ((ErrorCode
= Pipe_Read_Stream_LE(BufferPtr
, BytesRem
, NULL
)) != PIPE_RWSTREAM_NoError
)
288 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
);
291 if ((ErrorCode
= Pipe_Write_Stream_LE(BufferPtr
, BytesRem
, NULL
)) != PIPE_RWSTREAM_NoError
)
296 while (!(Pipe_IsOUTReady()))
298 if (USB_HostState
== HOST_STATE_Unattached
)
299 return PIPE_RWSTREAM_DeviceDisconnected
;
308 static uint8_t MS_Host_GetReturnedStatus(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
,
309 MS_CommandStatusWrapper_t
* const SCSICommandStatus
)
311 uint8_t ErrorCode
= PIPE_RWSTREAM_NoError
;
313 if ((ErrorCode
= MS_Host_WaitForDataReceived(MSInterfaceInfo
)) != PIPE_RWSTREAM_NoError
)
316 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
);
319 if ((ErrorCode
= Pipe_Read_Stream_LE(SCSICommandStatus
, sizeof(MS_CommandStatusWrapper_t
),
320 NULL
)) != PIPE_RWSTREAM_NoError
)
328 if (SCSICommandStatus
->Status
!= MS_SCSI_COMMAND_Pass
)
329 ErrorCode
= MS_ERROR_LOGICAL_CMD_FAILED
;
334 uint8_t MS_Host_ResetMSInterface(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
)
338 USB_ControlRequest
= (USB_Request_Header_t
)
340 .bmRequestType
= (REQDIR_HOSTTODEVICE
| REQTYPE_CLASS
| REQREC_INTERFACE
),
341 .bRequest
= MS_REQ_MassStorageReset
,
343 .wIndex
= MSInterfaceInfo
->State
.InterfaceNumber
,
347 Pipe_SelectPipe(PIPE_CONTROLPIPE
);
349 if ((ErrorCode
= USB_Host_SendControlRequest(NULL
)) != HOST_SENDCONTROL_Successful
)
352 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
);
354 if ((ErrorCode
= USB_Host_ClearEndpointStall(Pipe_GetBoundEndpointAddress())) != HOST_SENDCONTROL_Successful
)
357 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
);
359 if ((ErrorCode
= USB_Host_ClearEndpointStall(Pipe_GetBoundEndpointAddress())) != HOST_SENDCONTROL_Successful
)
362 return HOST_SENDCONTROL_Successful
;
365 uint8_t MS_Host_GetMaxLUN(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
,
366 uint8_t* const MaxLUNIndex
)
370 USB_ControlRequest
= (USB_Request_Header_t
)
372 .bmRequestType
= (REQDIR_DEVICETOHOST
| REQTYPE_CLASS
| REQREC_INTERFACE
),
373 .bRequest
= MS_REQ_GetMaxLUN
,
375 .wIndex
= MSInterfaceInfo
->State
.InterfaceNumber
,
379 Pipe_SelectPipe(PIPE_CONTROLPIPE
);
381 if ((ErrorCode
= USB_Host_SendControlRequest(MaxLUNIndex
)) == HOST_SENDCONTROL_SetupStalled
)
384 ErrorCode
= HOST_SENDCONTROL_Successful
;
390 uint8_t MS_Host_GetInquiryData(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
,
391 const uint8_t LUNIndex
,
392 SCSI_Inquiry_Response_t
* const InquiryData
)
394 if ((USB_HostState
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
))
395 return HOST_SENDCONTROL_DeviceDisconnected
;
397 MS_CommandBlockWrapper_t SCSICommandBlock
= (MS_CommandBlockWrapper_t
)
399 .DataTransferLength
= CPU_TO_LE32(sizeof(SCSI_Inquiry_Response_t
)),
400 .Flags
= MS_COMMAND_DIR_DATA_IN
,
402 .SCSICommandLength
= 6,
409 sizeof(SCSI_Inquiry_Response_t
), // Allocation Length
410 0x00 // Unused (control)
414 return MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, InquiryData
);
417 uint8_t MS_Host_TestUnitReady(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
,
418 const uint8_t LUNIndex
)
420 if ((USB_HostState
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
))
421 return HOST_SENDCONTROL_DeviceDisconnected
;
423 MS_CommandBlockWrapper_t SCSICommandBlock
= (MS_CommandBlockWrapper_t
)
425 .DataTransferLength
= CPU_TO_LE32(0),
426 .Flags
= MS_COMMAND_DIR_DATA_IN
,
428 .SCSICommandLength
= 6,
431 SCSI_CMD_TEST_UNIT_READY
,
436 0x00 // Unused (control)
440 return MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, NULL
);
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
= CPU_TO_LE32(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 if ((ErrorCode
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, DeviceCapacity
)) != PIPE_RWSTREAM_NoError
)
476 DeviceCapacity
->Blocks
= BE32_TO_CPU(DeviceCapacity
->Blocks
);
477 DeviceCapacity
->BlockSize
= BE32_TO_CPU(DeviceCapacity
->BlockSize
);
479 return PIPE_RWSTREAM_NoError
;
482 uint8_t MS_Host_RequestSense(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
,
483 const uint8_t LUNIndex
,
484 SCSI_Request_Sense_Response_t
* const SenseData
)
486 if ((USB_HostState
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
))
487 return HOST_SENDCONTROL_DeviceDisconnected
;
489 MS_CommandBlockWrapper_t SCSICommandBlock
= (MS_CommandBlockWrapper_t
)
491 .DataTransferLength
= CPU_TO_LE32(sizeof(SCSI_Request_Sense_Response_t
)),
492 .Flags
= MS_COMMAND_DIR_DATA_IN
,
494 .SCSICommandLength
= 6,
497 SCSI_CMD_REQUEST_SENSE
,
501 sizeof(SCSI_Request_Sense_Response_t
), // Allocation Length
502 0x00 // Unused (control)
506 return MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, SenseData
);
509 uint8_t MS_Host_PreventAllowMediumRemoval(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
,
510 const uint8_t LUNIndex
,
511 const bool PreventRemoval
)
513 if ((USB_HostState
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
))
514 return HOST_SENDCONTROL_DeviceDisconnected
;
516 MS_CommandBlockWrapper_t SCSICommandBlock
= (MS_CommandBlockWrapper_t
)
518 .DataTransferLength
= CPU_TO_LE32(0),
519 .Flags
= MS_COMMAND_DIR_DATA_OUT
,
521 .SCSICommandLength
= 6,
524 SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL
,
527 PreventRemoval
, // Prevent flag
529 0x00 // Unused (control)
533 return MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, NULL
);
536 uint8_t MS_Host_ReadDeviceBlocks(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
,
537 const uint8_t LUNIndex
,
538 const uint32_t BlockAddress
,
539 const uint8_t Blocks
,
540 const uint16_t BlockSize
,
543 if ((USB_HostState
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
))
544 return HOST_SENDCONTROL_DeviceDisconnected
;
546 MS_CommandBlockWrapper_t SCSICommandBlock
= (MS_CommandBlockWrapper_t
)
548 .DataTransferLength
= cpu_to_le32((uint32_t)Blocks
* BlockSize
),
549 .Flags
= MS_COMMAND_DIR_DATA_IN
,
551 .SCSICommandLength
= 10,
555 0x00, // Unused (control bits, all off)
556 (BlockAddress
>> 24), // MSB of Block Address
557 (BlockAddress
>> 16),
559 (BlockAddress
& 0xFF), // LSB of Block Address
561 0x00, // MSB of Total Blocks to Read
562 Blocks
, // LSB of Total Blocks to Read
563 0x00 // Unused (control)
567 return MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, BlockBuffer
);
570 uint8_t MS_Host_WriteDeviceBlocks(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
,
571 const uint8_t LUNIndex
,
572 const uint32_t BlockAddress
,
573 const uint8_t Blocks
,
574 const uint16_t BlockSize
,
575 const void* BlockBuffer
)
577 if ((USB_HostState
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
))
578 return HOST_SENDCONTROL_DeviceDisconnected
;
580 MS_CommandBlockWrapper_t SCSICommandBlock
= (MS_CommandBlockWrapper_t
)
582 .DataTransferLength
= cpu_to_le32((uint32_t)Blocks
* BlockSize
),
583 .Flags
= MS_COMMAND_DIR_DATA_OUT
,
585 .SCSICommandLength
= 10,
589 0x00, // Unused (control bits, all off)
590 (BlockAddress
>> 24), // MSB of Block Address
591 (BlockAddress
>> 16),
593 (BlockAddress
& 0xFF), // LSB of Block Address
595 0x00, // MSB of Total Blocks to Write
596 Blocks
, // LSB of Total Blocks to Write
597 0x00 // Unused (control)
601 return MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, BlockBuffer
);