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
;