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 #include "../../HighLevel/USBMode.h"
32 #if defined(USB_CAN_BE_HOST)
34 #define INCLUDE_FROM_MS_CLASS_HOST_C
35 #include "MassStorage.h"
37 uint8_t MS_Host_ConfigurePipes(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, uint16_t ConfigDescriptorSize
,
38 void* DeviceConfigDescriptor
)
40 uint8_t FoundEndpoints
= 0;
42 memset(&MSInterfaceInfo
->State
, 0x00, sizeof(MSInterfaceInfo
->State
));
44 if (DESCRIPTOR_TYPE(DeviceConfigDescriptor
) != DTYPE_Configuration
)
45 return MS_ENUMERROR_InvalidConfigDescriptor
;
47 if (USB_GetNextDescriptorComp(&ConfigDescriptorSize
, &DeviceConfigDescriptor
,
48 DComp_NextMSInterface
) != DESCRIPTOR_SEARCH_COMP_Found
)
50 return MS_ENUMERROR_NoMSInterfaceFound
;
53 MSInterfaceInfo
->State
.InterfaceNumber
= DESCRIPTOR_PCAST(DeviceConfigDescriptor
, USB_Descriptor_Interface_t
)->InterfaceNumber
;
55 while (FoundEndpoints
!= (MS_FOUND_DATAPIPE_IN
| MS_FOUND_DATAPIPE_OUT
))
57 if (USB_GetNextDescriptorComp(&ConfigDescriptorSize
, &DeviceConfigDescriptor
,
58 DComp_NextMSInterfaceEndpoint
) != DESCRIPTOR_SEARCH_COMP_Found
)
60 return MS_ENUMERROR_EndpointsNotFound
;
63 USB_Descriptor_Endpoint_t
* EndpointData
= DESCRIPTOR_PCAST(DeviceConfigDescriptor
, USB_Descriptor_Endpoint_t
);
65 if (EndpointData
->EndpointAddress
& ENDPOINT_DESCRIPTOR_DIR_IN
)
67 Pipe_ConfigurePipe(MSInterfaceInfo
->Config
.DataINPipeNumber
, EP_TYPE_BULK
, PIPE_TOKEN_IN
,
68 EndpointData
->EndpointAddress
, EndpointData
->EndpointSize
,
69 MSInterfaceInfo
->Config
.DataINPipeDoubleBank ? PIPE_BANK_DOUBLE
: PIPE_BANK_SINGLE
);
70 MSInterfaceInfo
->State
.DataINPipeSize
= EndpointData
->EndpointSize
;
72 FoundEndpoints
|= MS_FOUND_DATAPIPE_IN
;
76 Pipe_ConfigurePipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
, EP_TYPE_BULK
, PIPE_TOKEN_OUT
,
77 EndpointData
->EndpointAddress
, EndpointData
->EndpointSize
,
78 MSInterfaceInfo
->Config
.DataOUTPipeDoubleBank ? PIPE_BANK_DOUBLE
: PIPE_BANK_SINGLE
);
79 MSInterfaceInfo
->State
.DataOUTPipeSize
= EndpointData
->EndpointSize
;
81 FoundEndpoints
|= MS_FOUND_DATAPIPE_OUT
;
85 MSInterfaceInfo
->State
.IsActive
= true;
86 return MS_ENUMERROR_NoError
;
89 static uint8_t DComp_NextMSInterface(void* const CurrentDescriptor
)
91 if (DESCRIPTOR_TYPE(CurrentDescriptor
) == DTYPE_Interface
)
93 USB_Descriptor_Interface_t
* CurrentInterface
= DESCRIPTOR_PCAST(CurrentDescriptor
,
94 USB_Descriptor_Interface_t
);
96 if ((CurrentInterface
->Class
== MASS_STORE_CLASS
) &&
97 (CurrentInterface
->SubClass
== MASS_STORE_SUBCLASS
) &&
98 (CurrentInterface
->Protocol
== MASS_STORE_PROTOCOL
))
100 return DESCRIPTOR_SEARCH_Found
;
104 return DESCRIPTOR_SEARCH_NotFound
;
107 static uint8_t DComp_NextMSInterfaceEndpoint(void* const CurrentDescriptor
)
109 if (DESCRIPTOR_TYPE(CurrentDescriptor
) == DTYPE_Endpoint
)
111 USB_Descriptor_Endpoint_t
* CurrentEndpoint
= DESCRIPTOR_PCAST(CurrentDescriptor
,
112 USB_Descriptor_Endpoint_t
);
114 uint8_t EndpointType
= (CurrentEndpoint
->Attributes
& EP_TYPE_MASK
);
116 if ((EndpointType
== EP_TYPE_BULK
) &&
117 (!(Pipe_IsEndpointBound(CurrentEndpoint
->EndpointAddress
))))
119 return DESCRIPTOR_SEARCH_Found
;
122 else if (DESCRIPTOR_TYPE(CurrentDescriptor
) == DTYPE_Interface
)
124 return DESCRIPTOR_SEARCH_Fail
;
127 return DESCRIPTOR_SEARCH_NotFound
;
130 static uint8_t MS_Host_SendCommand(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, MS_CommandBlockWrapper_t
* const SCSICommandBlock
,
133 uint8_t ErrorCode
= PIPE_RWSTREAM_NoError
;
135 SCSICommandBlock
->Tag
= ++MSInterfaceInfo
->State
.TransactionTag
;
137 if (MSInterfaceInfo
->State
.TransactionTag
== 0xFFFFFFFF)
138 MSInterfaceInfo
->State
.TransactionTag
= 1;
140 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
);
143 if ((ErrorCode
= Pipe_Write_Stream_LE(SCSICommandBlock
, sizeof(MS_CommandBlockWrapper_t
),
144 NO_STREAM_CALLBACK
)) != PIPE_RWSTREAM_NoError
)
148 Pipe_WaitUntilReady();
152 if ((BufferPtr
!= NULL
) &&
153 ((ErrorCode
= MS_Host_SendReceiveData(MSInterfaceInfo
, SCSICommandBlock
, BufferPtr
)) != PIPE_RWSTREAM_NoError
))
162 static uint8_t MS_Host_WaitForDataReceived(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
)
164 uint16_t TimeoutMSRem
= COMMAND_DATA_TIMEOUT_MS
;
166 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
);
169 while (!(Pipe_IsINReceived()))
171 if (USB_INT_HasOccurred(USB_INT_HSOFI
))
173 USB_INT_Clear(USB_INT_HSOFI
);
177 return PIPE_RWSTREAM_Timeout
;
181 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
);
184 if (Pipe_IsStalled())
186 USB_Host_ClearPipeStall(MSInterfaceInfo
->Config
.DataOUTPipeNumber
);
188 return PIPE_RWSTREAM_PipeStalled
;
192 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
);
195 if (Pipe_IsStalled())
197 USB_Host_ClearPipeStall(MSInterfaceInfo
->Config
.DataINPipeNumber
);
199 return PIPE_RWSTREAM_PipeStalled
;
202 if (USB_HostState
== HOST_STATE_Unattached
)
203 return PIPE_RWSTREAM_DeviceDisconnected
;
206 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
);
209 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
);
212 return PIPE_RWSTREAM_NoError
;
215 static uint8_t MS_Host_SendReceiveData(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
,
216 MS_CommandBlockWrapper_t
* const SCSICommandBlock
, void* BufferPtr
)
218 uint8_t ErrorCode
= PIPE_RWSTREAM_NoError
;
219 uint16_t BytesRem
= SCSICommandBlock
->DataTransferLength
;
221 if (SCSICommandBlock
->Flags
& COMMAND_DIRECTION_DATA_IN
)
223 if ((ErrorCode
= MS_Host_WaitForDataReceived(MSInterfaceInfo
)) != PIPE_RWSTREAM_NoError
)
229 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
);
232 if ((ErrorCode
= Pipe_Read_Stream_LE(BufferPtr
, BytesRem
, NO_STREAM_CALLBACK
)) != PIPE_RWSTREAM_NoError
)
239 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
);
242 if ((ErrorCode
= Pipe_Write_Stream_LE(BufferPtr
, BytesRem
, NO_STREAM_CALLBACK
)) != PIPE_RWSTREAM_NoError
)
247 while (!(Pipe_IsOUTReady()))
249 if (USB_HostState
== HOST_STATE_Unattached
)
250 return PIPE_RWSTREAM_DeviceDisconnected
;
259 static uint8_t MS_Host_GetReturnedStatus(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
,
260 MS_CommandStatusWrapper_t
* const SCSICommandStatus
)
262 uint8_t ErrorCode
= PIPE_RWSTREAM_NoError
;
264 if ((ErrorCode
= MS_Host_WaitForDataReceived(MSInterfaceInfo
)) != PIPE_RWSTREAM_NoError
)
267 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
);
270 if ((ErrorCode
= Pipe_Read_Stream_LE(SCSICommandStatus
, sizeof(MS_CommandStatusWrapper_t
),
271 NO_STREAM_CALLBACK
)) != PIPE_RWSTREAM_NoError
)
279 if (SCSICommandStatus
->Status
!= SCSI_Command_Pass
)
280 ErrorCode
= MS_ERROR_LOGICAL_CMD_FAILED
;
285 uint8_t MS_Host_ResetMSInterface(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
)
287 USB_ControlRequest
= (USB_Request_Header_t
)
289 .bmRequestType
= (REQDIR_HOSTTODEVICE
| REQTYPE_CLASS
| REQREC_INTERFACE
),
290 .bRequest
= REQ_MassStorageReset
,
292 .wIndex
= MSInterfaceInfo
->State
.InterfaceNumber
,
296 Pipe_SelectPipe(PIPE_CONTROLPIPE
);
298 return USB_Host_SendControlRequest(NULL
);
301 uint8_t MS_Host_GetMaxLUN(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, uint8_t* const MaxLUNIndex
)
303 uint8_t ErrorCode
= HOST_SENDCONTROL_Successful
;
305 USB_ControlRequest
= (USB_Request_Header_t
)
307 .bmRequestType
= (REQDIR_DEVICETOHOST
| REQTYPE_CLASS
| REQREC_INTERFACE
),
308 .bRequest
= REQ_GetMaxLUN
,
310 .wIndex
= MSInterfaceInfo
->State
.InterfaceNumber
,
314 Pipe_SelectPipe(PIPE_CONTROLPIPE
);
316 if ((ErrorCode
= USB_Host_SendControlRequest(MaxLUNIndex
)) != HOST_SENDCONTROL_Successful
)
319 ErrorCode
= HOST_SENDCONTROL_Successful
;
325 uint8_t MS_Host_GetInquiryData(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
,
326 SCSI_Inquiry_Response_t
* const InquiryData
)
328 if ((USB_HostState
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
))
329 return HOST_SENDCONTROL_DeviceDisconnected
;
333 MS_CommandBlockWrapper_t SCSICommandBlock
= (MS_CommandBlockWrapper_t
)
335 .Signature
= CBW_SIGNATURE
,
336 .DataTransferLength
= sizeof(SCSI_Inquiry_Response_t
),
337 .Flags
= COMMAND_DIRECTION_DATA_IN
,
339 .SCSICommandLength
= 6,
346 sizeof(SCSI_Inquiry_Response_t
), // Allocation Length
347 0x00 // Unused (control)
351 MS_CommandStatusWrapper_t SCSICommandStatus
;
353 if ((ErrorCode
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, InquiryData
)) != PIPE_RWSTREAM_NoError
)
356 if ((ErrorCode
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
)
359 return PIPE_RWSTREAM_NoError
;
362 uint8_t MS_Host_TestUnitReady(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
)
364 if ((USB_HostState
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
))
365 return HOST_SENDCONTROL_DeviceDisconnected
;
369 MS_CommandBlockWrapper_t SCSICommandBlock
= (MS_CommandBlockWrapper_t
)
371 .Signature
= CBW_SIGNATURE
,
372 .DataTransferLength
= 0,
373 .Flags
= COMMAND_DIRECTION_DATA_IN
,
375 .SCSICommandLength
= 6,
378 SCSI_CMD_TEST_UNIT_READY
,
383 0x00 // Unused (control)
387 MS_CommandStatusWrapper_t SCSICommandStatus
;
389 if ((ErrorCode
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, NULL
)) != PIPE_RWSTREAM_NoError
)
392 if ((ErrorCode
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
)
395 return PIPE_RWSTREAM_NoError
;
398 uint8_t MS_Host_ReadDeviceCapacity(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
,
399 SCSI_Capacity_t
* const DeviceCapacity
)
401 if ((USB_HostState
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
))
402 return HOST_SENDCONTROL_DeviceDisconnected
;
406 MS_CommandBlockWrapper_t SCSICommandBlock
= (MS_CommandBlockWrapper_t
)
408 .Signature
= CBW_SIGNATURE
,
409 .DataTransferLength
= sizeof(SCSI_Capacity_t
),
410 .Flags
= COMMAND_DIRECTION_DATA_IN
,
412 .SCSICommandLength
= 10,
415 SCSI_CMD_READ_CAPACITY_10
,
417 0x00, // MSB of Logical block address
420 0x00, // LSB of Logical block address
423 0x00, // Partial Medium Indicator
424 0x00 // Unused (control)
428 MS_CommandStatusWrapper_t SCSICommandStatus
;
430 if ((ErrorCode
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, DeviceCapacity
)) != PIPE_RWSTREAM_NoError
)
433 DeviceCapacity
->Blocks
= SwapEndian_32(DeviceCapacity
->Blocks
);
434 DeviceCapacity
->BlockSize
= SwapEndian_32(DeviceCapacity
->BlockSize
);
436 if ((ErrorCode
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
)
439 return PIPE_RWSTREAM_NoError
;
442 uint8_t MS_Host_RequestSense(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
,
443 SCSI_Request_Sense_Response_t
* const SenseData
)
445 if ((USB_HostState
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
))
446 return HOST_SENDCONTROL_DeviceDisconnected
;
450 MS_CommandBlockWrapper_t SCSICommandBlock
= (MS_CommandBlockWrapper_t
)
452 .Signature
= CBW_SIGNATURE
,
453 .DataTransferLength
= sizeof(SCSI_Request_Sense_Response_t
),
454 .Flags
= COMMAND_DIRECTION_DATA_IN
,
456 .SCSICommandLength
= 6,
459 SCSI_CMD_REQUEST_SENSE
,
463 sizeof(SCSI_Request_Sense_Response_t
), // Allocation Length
464 0x00 // Unused (control)
468 MS_CommandStatusWrapper_t SCSICommandStatus
;
470 if ((ErrorCode
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, SenseData
)) != PIPE_RWSTREAM_NoError
)
473 if ((ErrorCode
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
)
476 return PIPE_RWSTREAM_NoError
;
479 uint8_t MS_Host_PreventAllowMediumRemoval(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
,
480 const bool PreventRemoval
)
482 if ((USB_HostState
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
))
483 return HOST_SENDCONTROL_DeviceDisconnected
;
487 MS_CommandBlockWrapper_t SCSICommandBlock
= (MS_CommandBlockWrapper_t
)
489 .Signature
= CBW_SIGNATURE
,
490 .DataTransferLength
= 0,
491 .Flags
= COMMAND_DIRECTION_DATA_OUT
,
493 .SCSICommandLength
= 6,
496 SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL
,
499 PreventRemoval
, // Prevent flag
501 0x00 // Unused (control)
505 MS_CommandStatusWrapper_t SCSICommandStatus
;
507 if ((ErrorCode
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, NULL
)) != PIPE_RWSTREAM_NoError
)
510 if ((ErrorCode
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
)
513 return PIPE_RWSTREAM_NoError
;
516 uint8_t MS_Host_ReadDeviceBlocks(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
, const uint32_t BlockAddress
,
517 const uint8_t Blocks
, const uint16_t BlockSize
, void* BlockBuffer
)
519 if ((USB_HostState
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
))
520 return HOST_SENDCONTROL_DeviceDisconnected
;
524 MS_CommandBlockWrapper_t SCSICommandBlock
= (MS_CommandBlockWrapper_t
)
526 .Signature
= CBW_SIGNATURE
,
527 .DataTransferLength
= ((uint32_t)Blocks
* BlockSize
),
528 .Flags
= COMMAND_DIRECTION_DATA_IN
,
530 .SCSICommandLength
= 10,
534 0x00, // Unused (control bits, all off)
535 (BlockAddress
>> 24), // MSB of Block Address
536 (BlockAddress
>> 16),
538 (BlockAddress
& 0xFF), // LSB of Block Address
539 0x00, // Unused (reserved)
540 0x00, // MSB of Total Blocks to Read
541 Blocks
, // LSB of Total Blocks to Read
542 0x00 // Unused (control)
546 MS_CommandStatusWrapper_t SCSICommandStatus
;
548 if ((ErrorCode
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, BlockBuffer
)) != PIPE_RWSTREAM_NoError
)
551 if ((ErrorCode
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
)
554 return PIPE_RWSTREAM_NoError
;
557 uint8_t MS_Host_WriteDeviceBlocks(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
, const uint8_t LUNIndex
, const uint32_t BlockAddress
,
558 const uint8_t Blocks
, const uint16_t BlockSize
, void* BlockBuffer
)
560 if ((USB_HostState
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
))
561 return HOST_SENDCONTROL_DeviceDisconnected
;
565 MS_CommandBlockWrapper_t SCSICommandBlock
= (MS_CommandBlockWrapper_t
)
567 .Signature
= CBW_SIGNATURE
,
568 .DataTransferLength
= ((uint32_t)Blocks
* BlockSize
),
569 .Flags
= COMMAND_DIRECTION_DATA_OUT
,
571 .SCSICommandLength
= 10,
575 0x00, // Unused (control bits, all off)
576 (BlockAddress
>> 24), // MSB of Block Address
577 (BlockAddress
>> 16),
579 (BlockAddress
& 0xFF), // LSB of Block Address
580 0x00, // Unused (reserved)
581 0x00, // MSB of Total Blocks to Write
582 Blocks
, // LSB of Total Blocks to Write
583 0x00 // Unused (control)
587 MS_CommandStatusWrapper_t SCSICommandStatus
;
589 if ((ErrorCode
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, BlockBuffer
)) != PIPE_RWSTREAM_NoError
)
592 if ((ErrorCode
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
)
595 return PIPE_RWSTREAM_NoError
;