3 Copyright (C) Dean Camera, 2014.
5 dean [at] fourwalledcubicle [dot] com
10 Copyright 2014 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 disclaims 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 MSInterfaceInfo->Config.DataINPipe.Size = le16_to_cpu(DataINEndpoint->EndpointSize);
82 MSInterfaceInfo->Config.DataINPipe.EndpointAddress = DataINEndpoint->EndpointAddress;
83 MSInterfaceInfo->Config.DataINPipe.Type = EP_TYPE_BULK;
85 MSInterfaceInfo->Config.DataOUTPipe.Size = le16_to_cpu(DataOUTEndpoint->EndpointSize);
86 MSInterfaceInfo->Config.DataOUTPipe.EndpointAddress = DataOUTEndpoint->EndpointAddress;
87 MSInterfaceInfo->Config.DataOUTPipe.Type = EP_TYPE_BULK;
89 if (!(Pipe_ConfigurePipeTable(&MSInterfaceInfo->Config.DataINPipe, 1)))
90 return MS_ENUMERROR_PipeConfigurationFailed;
92 if (!(Pipe_ConfigurePipeTable(&MSInterfaceInfo->Config.DataOUTPipe, 1)))
93 return MS_ENUMERROR_PipeConfigurationFailed;
95 MSInterfaceInfo->State.InterfaceNumber = MassStorageInterface->InterfaceNumber;
96 MSInterfaceInfo->State.IsActive = true;
98 return MS_ENUMERROR_NoError;
101 static uint8_t DCOMP_MS_Host_NextMSInterface(void* const CurrentDescriptor)
103 USB_Descriptor_Header_t* Header = DESCRIPTOR_PCAST(CurrentDescriptor, USB_Descriptor_Header_t);
105 if (Header->Type == DTYPE_Interface)
107 USB_Descriptor_Interface_t* Interface = DESCRIPTOR_PCAST(CurrentDescriptor, USB_Descriptor_Interface_t);
109 if ((Interface->Class == MS_CSCP_MassStorageClass) &&
110 (Interface->SubClass == MS_CSCP_SCSITransparentSubclass) &&
111 (Interface->Protocol == MS_CSCP_BulkOnlyTransportProtocol))
113 return DESCRIPTOR_SEARCH_Found;
117 return DESCRIPTOR_SEARCH_NotFound;
120 static uint8_t DCOMP_MS_Host_NextMSInterfaceEndpoint(void* const CurrentDescriptor)
122 USB_Descriptor_Header_t* Header = DESCRIPTOR_PCAST(CurrentDescriptor, USB_Descriptor_Header_t);
124 if (Header->Type == DTYPE_Endpoint)
126 USB_Descriptor_Endpoint_t* Endpoint = DESCRIPTOR_PCAST(CurrentDescriptor, USB_Descriptor_Endpoint_t);
128 uint8_t EndpointType = (Endpoint->Attributes & EP_TYPE_MASK);
130 if ((EndpointType == EP_TYPE_BULK) && (!(Pipe_IsEndpointBound(Endpoint->EndpointAddress))))
132 return DESCRIPTOR_SEARCH_Found;
135 else if (Header->Type == DTYPE_Interface)
137 return DESCRIPTOR_SEARCH_Fail;
140 return DESCRIPTOR_SEARCH_NotFound;
143 static uint8_t MS_Host_SendCommand(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
144 MS_CommandBlockWrapper_t* const SCSICommandBlock,
145 const void* const BufferPtr)
147 uint8_t ErrorCode = PIPE_RWSTREAM_NoError;
149 if (++MSInterfaceInfo->State.TransactionTag == 0xFFFFFFFF)
150 MSInterfaceInfo->State.TransactionTag = 1;
152 SCSICommandBlock->Signature = CPU_TO_LE32(MS_CBW_SIGNATURE);
153 SCSICommandBlock->Tag = cpu_to_le32(MSInterfaceInfo->State.TransactionTag);
155 Pipe_SelectPipe(MSInterfaceInfo->Config.DataOUTPipe.Address);
158 if ((ErrorCode = Pipe_Write_Stream_LE(SCSICommandBlock, sizeof(MS_CommandBlockWrapper_t),
159 NULL)) != PIPE_RWSTREAM_NoError)
165 Pipe_WaitUntilReady();
169 if (BufferPtr != NULL)
171 ErrorCode = MS_Host_SendReceiveData(MSInterfaceInfo, SCSICommandBlock, (void*)BufferPtr);
173 if ((ErrorCode != PIPE_RWSTREAM_NoError) && (ErrorCode != PIPE_RWSTREAM_PipeStalled))
180 MS_CommandStatusWrapper_t SCSIStatusBlock;
181 return MS_Host_GetReturnedStatus(MSInterfaceInfo, &SCSIStatusBlock);
184 static uint8_t MS_Host_WaitForDataReceived(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo)
186 uint16_t TimeoutMSRem = MS_COMMAND_DATA_TIMEOUT_MS;
187 uint16_t PreviousFrameNumber = USB_Host_GetFrameNumber();
189 Pipe_SelectPipe(MSInterfaceInfo->Config.DataINPipe.Address);
192 while (!(Pipe_IsINReceived()))
194 uint16_t CurrentFrameNumber = USB_Host_GetFrameNumber();
196 if (CurrentFrameNumber != PreviousFrameNumber)
198 PreviousFrameNumber = CurrentFrameNumber;
200 if (!(TimeoutMSRem--))
201 return PIPE_RWSTREAM_Timeout;
205 Pipe_SelectPipe(MSInterfaceInfo->Config.DataOUTPipe.Address);
208 if (Pipe_IsStalled())
210 USB_Host_ClearEndpointStall(Pipe_GetBoundEndpointAddress());
211 return PIPE_RWSTREAM_PipeStalled;
215 Pipe_SelectPipe(MSInterfaceInfo->Config.DataINPipe.Address);
218 if (Pipe_IsStalled())
220 USB_Host_ClearEndpointStall(Pipe_GetBoundEndpointAddress());
221 return PIPE_RWSTREAM_PipeStalled;
224 if (USB_HostState == HOST_STATE_Unattached)
225 return PIPE_RWSTREAM_DeviceDisconnected;
228 Pipe_SelectPipe(MSInterfaceInfo->Config.DataINPipe.Address);
231 Pipe_SelectPipe(MSInterfaceInfo->Config.DataOUTPipe.Address);
234 return PIPE_RWSTREAM_NoError;
237 static uint8_t MS_Host_SendReceiveData(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
238 MS_CommandBlockWrapper_t* const SCSICommandBlock,
241 uint8_t ErrorCode = PIPE_RWSTREAM_NoError;
242 uint16_t BytesRem = le32_to_cpu(SCSICommandBlock->DataTransferLength);
244 if (SCSICommandBlock->Flags & MS_COMMAND_DIR_DATA_IN)
246 if ((ErrorCode = MS_Host_WaitForDataReceived(MSInterfaceInfo)) != PIPE_RWSTREAM_NoError)
252 Pipe_SelectPipe(MSInterfaceInfo->Config.DataINPipe.Address);
255 if ((ErrorCode = Pipe_Read_Stream_LE(BufferPtr, BytesRem, NULL)) != PIPE_RWSTREAM_NoError)
262 Pipe_SelectPipe(MSInterfaceInfo->Config.DataOUTPipe.Address);
265 if ((ErrorCode = Pipe_Write_Stream_LE(BufferPtr, BytesRem, NULL)) != PIPE_RWSTREAM_NoError)
270 while (!(Pipe_IsOUTReady()))
272 if (USB_HostState == HOST_STATE_Unattached)
273 return PIPE_RWSTREAM_DeviceDisconnected;
282 static uint8_t MS_Host_GetReturnedStatus(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
283 MS_CommandStatusWrapper_t* const SCSICommandStatus)
285 uint8_t ErrorCode = PIPE_RWSTREAM_NoError;
287 if ((ErrorCode = MS_Host_WaitForDataReceived(MSInterfaceInfo)) != PIPE_RWSTREAM_NoError)
290 Pipe_SelectPipe(MSInterfaceInfo->Config.DataINPipe.Address);
293 if ((ErrorCode = Pipe_Read_Stream_LE(SCSICommandStatus, sizeof(MS_CommandStatusWrapper_t),
294 NULL)) != PIPE_RWSTREAM_NoError)
302 if (SCSICommandStatus->Status != MS_SCSI_COMMAND_Pass)
303 ErrorCode = MS_ERROR_LOGICAL_CMD_FAILED;
308 uint8_t MS_Host_ResetMSInterface(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo)
312 USB_ControlRequest = (USB_Request_Header_t)
314 .bmRequestType = (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE),
315 .bRequest = MS_REQ_MassStorageReset,
317 .wIndex = MSInterfaceInfo->State.InterfaceNumber,
321 Pipe_SelectPipe(PIPE_CONTROLPIPE);
323 if ((ErrorCode = USB_Host_SendControlRequest(NULL)) != HOST_SENDCONTROL_Successful)
326 Pipe_SelectPipe(MSInterfaceInfo->Config.DataINPipe.Address);
328 if ((ErrorCode = USB_Host_ClearEndpointStall(Pipe_GetBoundEndpointAddress())) != HOST_SENDCONTROL_Successful)
331 Pipe_SelectPipe(MSInterfaceInfo->Config.DataOUTPipe.Address);
333 if ((ErrorCode = USB_Host_ClearEndpointStall(Pipe_GetBoundEndpointAddress())) != HOST_SENDCONTROL_Successful)
336 return HOST_SENDCONTROL_Successful;
339 uint8_t MS_Host_GetMaxLUN(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
340 uint8_t* const MaxLUNIndex)
344 USB_ControlRequest = (USB_Request_Header_t)
346 .bmRequestType = (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE),
347 .bRequest = MS_REQ_GetMaxLUN,
349 .wIndex = MSInterfaceInfo->State.InterfaceNumber,
353 Pipe_SelectPipe(PIPE_CONTROLPIPE);
355 if ((ErrorCode = USB_Host_SendControlRequest(MaxLUNIndex)) == HOST_SENDCONTROL_SetupStalled)
358 ErrorCode = HOST_SENDCONTROL_Successful;
364 uint8_t MS_Host_GetInquiryData(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
365 const uint8_t LUNIndex,
366 SCSI_Inquiry_Response_t* const InquiryData)
368 if ((USB_HostState != HOST_STATE_Configured) || !(MSInterfaceInfo->State.IsActive))
369 return HOST_SENDCONTROL_DeviceDisconnected;
371 MS_CommandBlockWrapper_t SCSICommandBlock = (MS_CommandBlockWrapper_t)
373 .DataTransferLength = CPU_TO_LE32(sizeof(SCSI_Inquiry_Response_t)),
374 .Flags = MS_COMMAND_DIR_DATA_IN,
376 .SCSICommandLength = 6,
383 sizeof(SCSI_Inquiry_Response_t), // Allocation Length
384 0x00 // Unused (control)
388 return MS_Host_SendCommand(MSInterfaceInfo, &SCSICommandBlock, InquiryData);
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;
397 MS_CommandBlockWrapper_t SCSICommandBlock = (MS_CommandBlockWrapper_t)
399 .DataTransferLength = CPU_TO_LE32(0),
400 .Flags = MS_COMMAND_DIR_DATA_IN,
402 .SCSICommandLength = 6,
405 SCSI_CMD_TEST_UNIT_READY,
410 0x00 // Unused (control)
414 return MS_Host_SendCommand(MSInterfaceInfo, &SCSICommandBlock, NULL);
417 uint8_t MS_Host_ReadDeviceCapacity(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
418 const uint8_t LUNIndex,
419 SCSI_Capacity_t* const DeviceCapacity)
421 if ((USB_HostState != HOST_STATE_Configured) || !(MSInterfaceInfo->State.IsActive))
422 return HOST_SENDCONTROL_DeviceDisconnected;
426 MS_CommandBlockWrapper_t SCSICommandBlock = (MS_CommandBlockWrapper_t)
428 .DataTransferLength = CPU_TO_LE32(sizeof(SCSI_Capacity_t)),
429 .Flags = MS_COMMAND_DIR_DATA_IN,
431 .SCSICommandLength = 10,
434 SCSI_CMD_READ_CAPACITY_10,
436 0x00, // MSB of Logical block address
439 0x00, // LSB of Logical block address
442 0x00, // Partial Medium Indicator
443 0x00 // Unused (control)
447 if ((ErrorCode = MS_Host_SendCommand(MSInterfaceInfo, &SCSICommandBlock, DeviceCapacity)) != PIPE_RWSTREAM_NoError)
450 DeviceCapacity->Blocks = BE32_TO_CPU(DeviceCapacity->Blocks);
451 DeviceCapacity->BlockSize = BE32_TO_CPU(DeviceCapacity->BlockSize);
453 return PIPE_RWSTREAM_NoError;
456 uint8_t MS_Host_RequestSense(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
457 const uint8_t LUNIndex,
458 SCSI_Request_Sense_Response_t* const SenseData)
460 if ((USB_HostState != HOST_STATE_Configured) || !(MSInterfaceInfo->State.IsActive))
461 return HOST_SENDCONTROL_DeviceDisconnected;
463 MS_CommandBlockWrapper_t SCSICommandBlock = (MS_CommandBlockWrapper_t)
465 .DataTransferLength = CPU_TO_LE32(sizeof(SCSI_Request_Sense_Response_t)),
466 .Flags = MS_COMMAND_DIR_DATA_IN,
468 .SCSICommandLength = 6,
471 SCSI_CMD_REQUEST_SENSE,
475 sizeof(SCSI_Request_Sense_Response_t), // Allocation Length
476 0x00 // Unused (control)
480 return MS_Host_SendCommand(MSInterfaceInfo, &SCSICommandBlock, SenseData);
483 uint8_t MS_Host_PreventAllowMediumRemoval(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
484 const uint8_t LUNIndex,
485 const bool PreventRemoval)
487 if ((USB_HostState != HOST_STATE_Configured) || !(MSInterfaceInfo->State.IsActive))
488 return HOST_SENDCONTROL_DeviceDisconnected;
490 MS_CommandBlockWrapper_t SCSICommandBlock = (MS_CommandBlockWrapper_t)
492 .DataTransferLength = CPU_TO_LE32(0),
493 .Flags = MS_COMMAND_DIR_DATA_OUT,
495 .SCSICommandLength = 6,
498 SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL,
501 PreventRemoval, // Prevent flag
503 0x00 // Unused (control)
507 return MS_Host_SendCommand(MSInterfaceInfo, &SCSICommandBlock, NULL);
510 uint8_t MS_Host_ReadDeviceBlocks(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
511 const uint8_t LUNIndex,
512 const uint32_t BlockAddress,
513 const uint8_t Blocks,
514 const uint16_t BlockSize,
517 if ((USB_HostState != HOST_STATE_Configured) || !(MSInterfaceInfo->State.IsActive))
518 return HOST_SENDCONTROL_DeviceDisconnected;
520 MS_CommandBlockWrapper_t SCSICommandBlock = (MS_CommandBlockWrapper_t)
522 .DataTransferLength = cpu_to_le32((uint32_t)Blocks * BlockSize),
523 .Flags = MS_COMMAND_DIR_DATA_IN,
525 .SCSICommandLength = 10,
529 0x00, // Unused (control bits, all off)
530 (BlockAddress >> 24), // MSB of Block Address
531 (BlockAddress >> 16),
533 (BlockAddress & 0xFF), // LSB of Block Address
535 0x00, // MSB of Total Blocks to Read
536 Blocks, // LSB of Total Blocks to Read
537 0x00 // Unused (control)
541 return MS_Host_SendCommand(MSInterfaceInfo, &SCSICommandBlock, BlockBuffer);
544 uint8_t MS_Host_WriteDeviceBlocks(USB_ClassInfo_MS_Host_t* const MSInterfaceInfo,
545 const uint8_t LUNIndex,
546 const uint32_t BlockAddress,
547 const uint8_t Blocks,
548 const uint16_t BlockSize,
549 const void* BlockBuffer)
551 if ((USB_HostState != HOST_STATE_Configured) || !(MSInterfaceInfo->State.IsActive))
552 return HOST_SENDCONTROL_DeviceDisconnected;
554 MS_CommandBlockWrapper_t SCSICommandBlock = (MS_CommandBlockWrapper_t)
556 .DataTransferLength = cpu_to_le32((uint32_t)Blocks * BlockSize),
557 .Flags = MS_COMMAND_DIR_DATA_OUT,
559 .SCSICommandLength = 10,
563 0x00, // Unused (control bits, all off)
564 (BlockAddress >> 24), // MSB of Block Address
565 (BlockAddress >> 16),
567 (BlockAddress & 0xFF), // LSB of Block Address
569 0x00, // MSB of Total Blocks to Write
570 Blocks, // LSB of Total Blocks to Write
571 0x00 // Unused (control)
575 return MS_Host_SendCommand(MSInterfaceInfo, &SCSICommandBlock, BlockBuffer);