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 #define __INCLUDE_FROM_USB_DRIVER
32 #include "../../HighLevel/USBMode.h"
33 #if defined(USB_CAN_BE_HOST)
35 #define __INCLUDE_FROM_MS_CLASS_HOST_C
36 #define __INCLUDE_FROM_MS_DRIVER
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
;
46 memset(&MSInterfaceInfo
->State
, 0x00, sizeof(MSInterfaceInfo
->State
));
48 if (DESCRIPTOR_TYPE(ConfigDescriptorData
) != DTYPE_Configuration
)
49 return MS_ENUMERROR_InvalidConfigDescriptor
;
51 if (USB_GetNextDescriptorComp(&ConfigDescriptorSize
, &ConfigDescriptorData
,
52 DCOMP_MS_Host_NextMSInterface
) != DESCRIPTOR_SEARCH_COMP_Found
)
54 return MS_ENUMERROR_NoCompatibleInterfaceFound
;
57 MSInterfaceInfo
->State
.InterfaceNumber
= DESCRIPTOR_PCAST(ConfigDescriptorData
, USB_Descriptor_Interface_t
)->InterfaceNumber
;
59 while (!(DataINEndpoint
) || !(DataOUTEndpoint
))
61 if (USB_GetNextDescriptorComp(&ConfigDescriptorSize
, &ConfigDescriptorData
,
62 DCOMP_MS_Host_NextMSInterfaceEndpoint
) != DESCRIPTOR_SEARCH_COMP_Found
)
64 DataINEndpoint
= NULL
;
65 DataOUTEndpoint
= NULL
;
67 if (USB_GetNextDescriptorComp(&ConfigDescriptorSize
, &ConfigDescriptorData
,
68 DCOMP_MS_Host_NextMSInterface
) != DESCRIPTOR_SEARCH_COMP_Found
)
70 return MS_ENUMERROR_NoCompatibleInterfaceFound
;
73 MSInterfaceInfo
->State
.InterfaceNumber
= DESCRIPTOR_PCAST(ConfigDescriptorData
,
74 USB_Descriptor_Interface_t
)->InterfaceNumber
;
79 USB_Descriptor_Endpoint_t
* EndpointData
= DESCRIPTOR_PCAST(ConfigDescriptorData
, USB_Descriptor_Endpoint_t
);
81 if (EndpointData
->EndpointAddress
& ENDPOINT_DESCRIPTOR_DIR_IN
)
82 DataINEndpoint
= EndpointData
;
84 DataOUTEndpoint
= EndpointData
;
87 for (uint8_t PipeNum
= 1; PipeNum
< PIPE_TOTAL_PIPES
; PipeNum
++)
89 if (PipeNum
== MSInterfaceInfo
->Config
.DataINPipeNumber
)
91 Pipe_ConfigurePipe(PipeNum
, EP_TYPE_BULK
, PIPE_TOKEN_IN
,
92 DataINEndpoint
->EndpointAddress
, DataINEndpoint
->EndpointSize
,
93 MSInterfaceInfo
->Config
.DataINPipeDoubleBank ? PIPE_BANK_DOUBLE
: PIPE_BANK_SINGLE
);
95 MSInterfaceInfo
->State
.DataINPipeSize
= DataINEndpoint
->EndpointSize
;
97 else if (PipeNum
== MSInterfaceInfo
->Config
.DataOUTPipeNumber
)
99 Pipe_ConfigurePipe(PipeNum
, EP_TYPE_BULK
, PIPE_TOKEN_OUT
,
100 DataOUTEndpoint
->EndpointAddress
, DataOUTEndpoint
->EndpointSize
,
101 MSInterfaceInfo
->Config
.DataOUTPipeDoubleBank ? PIPE_BANK_DOUBLE
: PIPE_BANK_SINGLE
);
103 MSInterfaceInfo
->State
.DataOUTPipeSize
= DataOUTEndpoint
->EndpointSize
;
107 MSInterfaceInfo
->State
.IsActive
= true;
109 return MS_ENUMERROR_NoError
;
112 static uint8_t DCOMP_MS_Host_NextMSInterface(void* const CurrentDescriptor
)
114 if (DESCRIPTOR_TYPE(CurrentDescriptor
) == DTYPE_Interface
)
116 USB_Descriptor_Interface_t
* CurrentInterface
= DESCRIPTOR_PCAST(CurrentDescriptor
,
117 USB_Descriptor_Interface_t
);
119 if ((CurrentInterface
->Class
== MASS_STORE_CLASS
) &&
120 (CurrentInterface
->SubClass
== MASS_STORE_SUBCLASS
) &&
121 (CurrentInterface
->Protocol
== MASS_STORE_PROTOCOL
))
123 return DESCRIPTOR_SEARCH_Found
;
127 return DESCRIPTOR_SEARCH_NotFound
;
130 static uint8_t DCOMP_MS_Host_NextMSInterfaceEndpoint(void* const CurrentDescriptor
)
132 if (DESCRIPTOR_TYPE(CurrentDescriptor
) == DTYPE_Endpoint
)
134 USB_Descriptor_Endpoint_t
* CurrentEndpoint
= DESCRIPTOR_PCAST(CurrentDescriptor
,
135 USB_Descriptor_Endpoint_t
);
137 uint8_t EndpointType
= (CurrentEndpoint
->Attributes
& EP_TYPE_MASK
);
139 if ((EndpointType
== EP_TYPE_BULK
) &&
140 (!(Pipe_IsEndpointBound(CurrentEndpoint
->EndpointAddress
))))
142 return DESCRIPTOR_SEARCH_Found
;
145 else if (DESCRIPTOR_TYPE(CurrentDescriptor
) == DTYPE_Interface
)
147 return DESCRIPTOR_SEARCH_Fail
;
150 return DESCRIPTOR_SEARCH_NotFound
;
153 static uint8_t MS_Host_SendCommand(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
,
154 MS_CommandBlockWrapper_t
* const SCSICommandBlock
,
155 const void* const BufferPtr
)
157 uint8_t ErrorCode
= PIPE_RWSTREAM_NoError
;
159 SCSICommandBlock
->Signature
= CBW_SIGNATURE
;
160 SCSICommandBlock
->Tag
= ++MSInterfaceInfo
->State
.TransactionTag
;
162 if (MSInterfaceInfo
->State
.TransactionTag
== 0xFFFFFFFF)
163 MSInterfaceInfo
->State
.TransactionTag
= 1;
165 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
);
168 if ((ErrorCode
= Pipe_Write_Stream_LE(SCSICommandBlock
, sizeof(MS_CommandBlockWrapper_t
),
169 NO_STREAM_CALLBACK
)) != PIPE_RWSTREAM_NoError
)
173 Pipe_WaitUntilReady();
177 if ((BufferPtr
!= NULL
) &&
178 ((ErrorCode
= MS_Host_SendReceiveData(MSInterfaceInfo
, SCSICommandBlock
, (void*)BufferPtr
)) != PIPE_RWSTREAM_NoError
))
187 static uint8_t MS_Host_WaitForDataReceived(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
)
189 uint16_t TimeoutMSRem
= COMMAND_DATA_TIMEOUT_MS
;
190 uint16_t PreviousFrameNumber
= USB_Host_GetFrameNumber();
192 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
);
195 while (!(Pipe_IsINReceived()))
197 uint16_t CurrentFrameNumber
= USB_Host_GetFrameNumber();
199 if (CurrentFrameNumber
!= PreviousFrameNumber
)
201 PreviousFrameNumber
= CurrentFrameNumber
;
203 if (!(TimeoutMSRem
--))
204 return PIPE_RWSTREAM_Timeout
;
208 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
);
211 if (Pipe_IsStalled())
213 USB_Host_ClearPipeStall(MSInterfaceInfo
->Config
.DataOUTPipeNumber
);
215 return PIPE_RWSTREAM_PipeStalled
;
219 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
);
222 if (Pipe_IsStalled())
224 USB_Host_ClearPipeStall(MSInterfaceInfo
->Config
.DataINPipeNumber
);
226 return PIPE_RWSTREAM_PipeStalled
;
229 if (USB_HostState
== HOST_STATE_Unattached
)
230 return PIPE_RWSTREAM_DeviceDisconnected
;
233 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
);
236 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
);
239 return PIPE_RWSTREAM_NoError
;
242 static uint8_t MS_Host_SendReceiveData(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
,
243 MS_CommandBlockWrapper_t
* const SCSICommandBlock
,
246 uint8_t ErrorCode
= PIPE_RWSTREAM_NoError
;
247 uint16_t BytesRem
= SCSICommandBlock
->DataTransferLength
;
249 if (SCSICommandBlock
->Flags
& COMMAND_DIRECTION_DATA_IN
)
251 if ((ErrorCode
= MS_Host_WaitForDataReceived(MSInterfaceInfo
)) != PIPE_RWSTREAM_NoError
)
257 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
);
260 if ((ErrorCode
= Pipe_Read_Stream_LE(BufferPtr
, BytesRem
, NO_STREAM_CALLBACK
)) != PIPE_RWSTREAM_NoError
)
267 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataOUTPipeNumber
);
270 if ((ErrorCode
= Pipe_Write_Stream_LE(BufferPtr
, BytesRem
, NO_STREAM_CALLBACK
)) != PIPE_RWSTREAM_NoError
)
275 while (!(Pipe_IsOUTReady()))
277 if (USB_HostState
== HOST_STATE_Unattached
)
278 return PIPE_RWSTREAM_DeviceDisconnected
;
287 static uint8_t MS_Host_GetReturnedStatus(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
,
288 MS_CommandStatusWrapper_t
* const SCSICommandStatus
)
290 uint8_t ErrorCode
= PIPE_RWSTREAM_NoError
;
292 if ((ErrorCode
= MS_Host_WaitForDataReceived(MSInterfaceInfo
)) != PIPE_RWSTREAM_NoError
)
295 Pipe_SelectPipe(MSInterfaceInfo
->Config
.DataINPipeNumber
);
298 if ((ErrorCode
= Pipe_Read_Stream_LE(SCSICommandStatus
, sizeof(MS_CommandStatusWrapper_t
),
299 NO_STREAM_CALLBACK
)) != PIPE_RWSTREAM_NoError
)
307 if (SCSICommandStatus
->Status
!= MS_SCSI_COMMAND_Pass
)
308 ErrorCode
= MS_ERROR_LOGICAL_CMD_FAILED
;
313 uint8_t MS_Host_ResetMSInterface(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
)
315 USB_ControlRequest
= (USB_Request_Header_t
)
317 .bmRequestType
= (REQDIR_HOSTTODEVICE
| REQTYPE_CLASS
| REQREC_INTERFACE
),
318 .bRequest
= REQ_MassStorageReset
,
320 .wIndex
= MSInterfaceInfo
->State
.InterfaceNumber
,
324 Pipe_SelectPipe(PIPE_CONTROLPIPE
);
326 return USB_Host_SendControlRequest(NULL
);
329 uint8_t MS_Host_GetMaxLUN(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
,
330 uint8_t* const MaxLUNIndex
)
332 uint8_t ErrorCode
= HOST_SENDCONTROL_Successful
;
334 USB_ControlRequest
= (USB_Request_Header_t
)
336 .bmRequestType
= (REQDIR_DEVICETOHOST
| REQTYPE_CLASS
| REQREC_INTERFACE
),
337 .bRequest
= REQ_GetMaxLUN
,
339 .wIndex
= MSInterfaceInfo
->State
.InterfaceNumber
,
343 Pipe_SelectPipe(PIPE_CONTROLPIPE
);
345 if ((ErrorCode
= USB_Host_SendControlRequest(MaxLUNIndex
)) != HOST_SENDCONTROL_Successful
)
348 ErrorCode
= HOST_SENDCONTROL_Successful
;
354 uint8_t MS_Host_GetInquiryData(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
,
355 const uint8_t LUNIndex
,
356 SCSI_Inquiry_Response_t
* const InquiryData
)
358 if ((USB_HostState
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
))
359 return HOST_SENDCONTROL_DeviceDisconnected
;
363 MS_CommandBlockWrapper_t SCSICommandBlock
= (MS_CommandBlockWrapper_t
)
365 .DataTransferLength
= sizeof(SCSI_Inquiry_Response_t
),
366 .Flags
= COMMAND_DIRECTION_DATA_IN
,
368 .SCSICommandLength
= 6,
375 sizeof(SCSI_Inquiry_Response_t
), // Allocation Length
376 0x00 // Unused (control)
380 MS_CommandStatusWrapper_t SCSICommandStatus
;
382 if ((ErrorCode
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, InquiryData
)) != PIPE_RWSTREAM_NoError
)
385 if ((ErrorCode
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
)
388 return PIPE_RWSTREAM_NoError
;
391 uint8_t MS_Host_TestUnitReady(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
,
392 const uint8_t LUNIndex
)
394 if ((USB_HostState
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
))
395 return HOST_SENDCONTROL_DeviceDisconnected
;
399 MS_CommandBlockWrapper_t SCSICommandBlock
= (MS_CommandBlockWrapper_t
)
401 .DataTransferLength
= 0,
402 .Flags
= COMMAND_DIRECTION_DATA_IN
,
404 .SCSICommandLength
= 6,
407 SCSI_CMD_TEST_UNIT_READY
,
412 0x00 // Unused (control)
416 MS_CommandStatusWrapper_t SCSICommandStatus
;
418 if ((ErrorCode
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, NULL
)) != PIPE_RWSTREAM_NoError
)
421 if ((ErrorCode
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
)
424 return PIPE_RWSTREAM_NoError
;
427 uint8_t MS_Host_ReadDeviceCapacity(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
,
428 const uint8_t LUNIndex
,
429 SCSI_Capacity_t
* const DeviceCapacity
)
431 if ((USB_HostState
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
))
432 return HOST_SENDCONTROL_DeviceDisconnected
;
436 MS_CommandBlockWrapper_t SCSICommandBlock
= (MS_CommandBlockWrapper_t
)
438 .DataTransferLength
= sizeof(SCSI_Capacity_t
),
439 .Flags
= COMMAND_DIRECTION_DATA_IN
,
441 .SCSICommandLength
= 10,
444 SCSI_CMD_READ_CAPACITY_10
,
446 0x00, // MSB of Logical block address
449 0x00, // LSB of Logical block address
452 0x00, // Partial Medium Indicator
453 0x00 // Unused (control)
457 MS_CommandStatusWrapper_t SCSICommandStatus
;
459 if ((ErrorCode
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, DeviceCapacity
)) != PIPE_RWSTREAM_NoError
)
462 SwapEndian_n(&DeviceCapacity
->Blocks
, sizeof(DeviceCapacity
->Blocks
));
463 SwapEndian_n(&DeviceCapacity
->BlockSize
, sizeof(DeviceCapacity
->BlockSize
));
465 if ((ErrorCode
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
)
468 return PIPE_RWSTREAM_NoError
;
471 uint8_t MS_Host_RequestSense(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
,
472 const uint8_t LUNIndex
,
473 SCSI_Request_Sense_Response_t
* const SenseData
)
475 if ((USB_HostState
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
))
476 return HOST_SENDCONTROL_DeviceDisconnected
;
480 MS_CommandBlockWrapper_t SCSICommandBlock
= (MS_CommandBlockWrapper_t
)
482 .DataTransferLength
= sizeof(SCSI_Request_Sense_Response_t
),
483 .Flags
= COMMAND_DIRECTION_DATA_IN
,
485 .SCSICommandLength
= 6,
488 SCSI_CMD_REQUEST_SENSE
,
492 sizeof(SCSI_Request_Sense_Response_t
), // Allocation Length
493 0x00 // Unused (control)
497 MS_CommandStatusWrapper_t SCSICommandStatus
;
499 if ((ErrorCode
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, SenseData
)) != PIPE_RWSTREAM_NoError
)
502 if ((ErrorCode
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
)
505 return PIPE_RWSTREAM_NoError
;
508 uint8_t MS_Host_PreventAllowMediumRemoval(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
,
509 const uint8_t LUNIndex
,
510 const bool PreventRemoval
)
512 if ((USB_HostState
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
))
513 return HOST_SENDCONTROL_DeviceDisconnected
;
517 MS_CommandBlockWrapper_t SCSICommandBlock
= (MS_CommandBlockWrapper_t
)
519 .DataTransferLength
= 0,
520 .Flags
= COMMAND_DIRECTION_DATA_OUT
,
522 .SCSICommandLength
= 6,
525 SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL
,
528 PreventRemoval
, // Prevent flag
530 0x00 // Unused (control)
534 MS_CommandStatusWrapper_t SCSICommandStatus
;
536 if ((ErrorCode
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, NULL
)) != PIPE_RWSTREAM_NoError
)
539 if ((ErrorCode
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
)
542 return PIPE_RWSTREAM_NoError
;
545 uint8_t MS_Host_ReadDeviceBlocks(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
,
546 const uint8_t LUNIndex
,
547 const uint32_t BlockAddress
,
548 const uint8_t Blocks
,
549 const uint16_t BlockSize
,
552 if ((USB_HostState
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
))
553 return HOST_SENDCONTROL_DeviceDisconnected
;
557 MS_CommandBlockWrapper_t SCSICommandBlock
= (MS_CommandBlockWrapper_t
)
559 .DataTransferLength
= ((uint32_t)Blocks
* BlockSize
),
560 .Flags
= COMMAND_DIRECTION_DATA_IN
,
562 .SCSICommandLength
= 10,
566 0x00, // Unused (control bits, all off)
567 (BlockAddress
>> 24), // MSB of Block Address
568 (BlockAddress
>> 16),
570 (BlockAddress
& 0xFF), // LSB of Block Address
572 0x00, // MSB of Total Blocks to Read
573 Blocks
, // LSB of Total Blocks to Read
574 0x00 // Unused (control)
578 MS_CommandStatusWrapper_t SCSICommandStatus
;
580 if ((ErrorCode
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, BlockBuffer
)) != PIPE_RWSTREAM_NoError
)
583 if ((ErrorCode
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
)
586 return PIPE_RWSTREAM_NoError
;
589 uint8_t MS_Host_WriteDeviceBlocks(USB_ClassInfo_MS_Host_t
* const MSInterfaceInfo
,
590 const uint8_t LUNIndex
,
591 const uint32_t BlockAddress
,
592 const uint8_t Blocks
,
593 const uint16_t BlockSize
,
594 const void* BlockBuffer
)
596 if ((USB_HostState
!= HOST_STATE_Configured
) || !(MSInterfaceInfo
->State
.IsActive
))
597 return HOST_SENDCONTROL_DeviceDisconnected
;
601 MS_CommandBlockWrapper_t SCSICommandBlock
= (MS_CommandBlockWrapper_t
)
603 .DataTransferLength
= ((uint32_t)Blocks
* BlockSize
),
604 .Flags
= COMMAND_DIRECTION_DATA_OUT
,
606 .SCSICommandLength
= 10,
610 0x00, // Unused (control bits, all off)
611 (BlockAddress
>> 24), // MSB of Block Address
612 (BlockAddress
>> 16),
614 (BlockAddress
& 0xFF), // LSB of Block Address
616 0x00, // MSB of Total Blocks to Write
617 Blocks
, // LSB of Total Blocks to Write
618 0x00 // Unused (control)
622 MS_CommandStatusWrapper_t SCSICommandStatus
;
624 if ((ErrorCode
= MS_Host_SendCommand(MSInterfaceInfo
, &SCSICommandBlock
, BlockBuffer
)) != PIPE_RWSTREAM_NoError
)
627 if ((ErrorCode
= MS_Host_GetReturnedStatus(MSInterfaceInfo
, &SCSICommandStatus
)) != PIPE_RWSTREAM_NoError
)
630 return PIPE_RWSTREAM_NoError
;