3 Copyright (C) Dean Camera, 2010.
5 dean [at] fourwalledcubicle [dot] 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_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
++)
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
== MS_CSCP_MassStorageClass
) &&
114 (CurrentInterface
->SubClass
== MS_CSCP_SCSITransparentSubclass
) &&
115 (CurrentInterface
->Protocol
== MS_CSCP_BulkOnlyTransportProtocol
))
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
= MS_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
= MS_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
& MS_COMMAND_DIR_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
= MS_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
= MS_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
= MS_COMMAND_DIR_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
= MS_COMMAND_DIR_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
= MS_COMMAND_DIR_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
= MS_COMMAND_DIR_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
= MS_COMMAND_DIR_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
= MS_COMMAND_DIR_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
= MS_COMMAND_DIR_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
;