summaryrefslogtreecommitdiffstats
path: root/Demos/Host/LowLevel/MassStorageHost
diff options
context:
space:
mode:
Diffstat (limited to 'Demos/Host/LowLevel/MassStorageHost')
-rw-r--r--Demos/Host/LowLevel/MassStorageHost/Config/LUFAConfig.h93
-rw-r--r--Demos/Host/LowLevel/MassStorageHost/ConfigDescriptor.c173
-rw-r--r--Demos/Host/LowLevel/MassStorageHost/ConfigDescriptor.h78
-rw-r--r--Demos/Host/LowLevel/MassStorageHost/Lib/MassStoreCommands.c635
-rw-r--r--Demos/Host/LowLevel/MassStorageHost/Lib/MassStoreCommands.h86
-rw-r--r--Demos/Host/LowLevel/MassStorageHost/MassStorageHost.c373
-rw-r--r--Demos/Host/LowLevel/MassStorageHost/MassStorageHost.h91
-rw-r--r--Demos/Host/LowLevel/MassStorageHost/MassStorageHost.txt68
-rw-r--r--Demos/Host/LowLevel/MassStorageHost/asf.xml53
-rw-r--r--Demos/Host/LowLevel/MassStorageHost/doxyfile2395
-rw-r--r--Demos/Host/LowLevel/MassStorageHost/makefile43
11 files changed, 4088 insertions, 0 deletions
diff --git a/Demos/Host/LowLevel/MassStorageHost/Config/LUFAConfig.h b/Demos/Host/LowLevel/MassStorageHost/Config/LUFAConfig.h
new file mode 100644
index 0000000000..e2b5166f25
--- /dev/null
+++ b/Demos/Host/LowLevel/MassStorageHost/Config/LUFAConfig.h
@@ -0,0 +1,93 @@
+/*
+ LUFA Library
+ Copyright (C) Dean Camera, 2017.
+
+ dean [at] fourwalledcubicle [dot] com
+ www.lufa-lib.org
+*/
+
+/*
+ Copyright 2017 Dean Camera (dean [at] fourwalledcubicle [dot] com)
+
+ Permission to use, copy, modify, distribute, and sell this
+ software and its documentation for any purpose is hereby granted
+ without fee, provided that the above copyright notice appear in
+ all copies and that both that the copyright notice and this
+ permission notice and warranty disclaimer appear in supporting
+ documentation, and that the name of the author not be used in
+ advertising or publicity pertaining to distribution of the
+ software without specific, written prior permission.
+
+ The author disclaims all warranties with regard to this
+ software, including all implied warranties of merchantability
+ and fitness. In no event shall the author be liable for any
+ special, indirect or consequential damages or any damages
+ whatsoever resulting from loss of use, data or profits, whether
+ in an action of contract, negligence or other tortious action,
+ arising out of or in connection with the use or performance of
+ this software.
+*/
+
+/** \file
+ * \brief LUFA Library Configuration Header File
+ *
+ * This header file is used to configure LUFA's compile time options,
+ * as an alternative to the compile time constants supplied through
+ * a makefile.
+ *
+ * For information on what each token does, refer to the LUFA
+ * manual section "Summary of Compile Tokens".
+ */
+
+#ifndef _LUFA_CONFIG_H_
+#define _LUFA_CONFIG_H_
+
+ #if (ARCH == ARCH_AVR8)
+
+ /* Non-USB Related Configuration Tokens: */
+// #define DISABLE_TERMINAL_CODES
+
+ /* USB Class Driver Related Tokens: */
+// #define HID_HOST_BOOT_PROTOCOL_ONLY
+// #define HID_STATETABLE_STACK_DEPTH {Insert Value Here}
+// #define HID_USAGE_STACK_DEPTH {Insert Value Here}
+// #define HID_MAX_COLLECTIONS {Insert Value Here}
+// #define HID_MAX_REPORTITEMS {Insert Value Here}
+// #define HID_MAX_REPORT_IDS {Insert Value Here}
+// #define NO_CLASS_DRIVER_AUTOFLUSH
+
+ /* General USB Driver Related Tokens: */
+// #define ORDERED_EP_CONFIG
+ #define USE_STATIC_OPTIONS (USB_OPT_REG_ENABLED | USB_OPT_AUTO_PLL)
+// #define USB_DEVICE_ONLY
+ #define USB_HOST_ONLY
+ #define USB_STREAM_TIMEOUT_MS 5000
+// #define NO_LIMITED_CONTROLLER_CONNECT
+// #define NO_SOF_EVENTS
+
+ /* USB Device Mode Driver Related Tokens: */
+// #define USE_RAM_DESCRIPTORS
+// #define USE_FLASH_DESCRIPTORS
+// #define USE_EEPROM_DESCRIPTORS
+// #define NO_INTERNAL_SERIAL
+// #define FIXED_CONTROL_ENDPOINT_SIZE {Insert Value Here}
+// #define DEVICE_STATE_AS_GPIOR {Insert Value Here}
+// #define FIXED_NUM_CONFIGURATIONS {Insert Value Here}
+// #define CONTROL_ONLY_DEVICE
+// #define INTERRUPT_CONTROL_ENDPOINT
+// #define NO_DEVICE_REMOTE_WAKEUP
+// #define NO_DEVICE_SELF_POWER
+
+ /* USB Host Mode Driver Related Tokens: */
+// #define HOST_STATE_AS_GPIOR {Insert Value Here}
+// #define USB_HOST_TIMEOUT_MS {Insert Value Here}
+// #define HOST_DEVICE_SETTLE_DELAY_MS {Insert Value Here}
+// #define NO_AUTO_VBUS_MANAGEMENT
+// #define INVERTED_VBUS_ENABLE_LINE
+
+ #else
+
+ #error Unsupported architecture for this LUFA configuration file.
+
+ #endif
+#endif
diff --git a/Demos/Host/LowLevel/MassStorageHost/ConfigDescriptor.c b/Demos/Host/LowLevel/MassStorageHost/ConfigDescriptor.c
new file mode 100644
index 0000000000..b65912d51a
--- /dev/null
+++ b/Demos/Host/LowLevel/MassStorageHost/ConfigDescriptor.c
@@ -0,0 +1,173 @@
+/*
+ LUFA Library
+ Copyright (C) Dean Camera, 2017.
+
+ dean [at] fourwalledcubicle [dot] com
+ www.lufa-lib.org
+*/
+
+/*
+ Copyright 2017 Dean Camera (dean [at] fourwalledcubicle [dot] com)
+
+ Permission to use, copy, modify, distribute, and sell this
+ software and its documentation for any purpose is hereby granted
+ without fee, provided that the above copyright notice appear in
+ all copies and that both that the copyright notice and this
+ permission notice and warranty disclaimer appear in supporting
+ documentation, and that the name of the author not be used in
+ advertising or publicity pertaining to distribution of the
+ software without specific, written prior permission.
+
+ The author disclaims all warranties with regard to this
+ software, including all implied warranties of merchantability
+ and fitness. In no event shall the author be liable for any
+ special, indirect or consequential damages or any damages
+ whatsoever resulting from loss of use, data or profits, whether
+ in an action of contract, negligence or other tortious action,
+ arising out of or in connection with the use or performance of
+ this software.
+*/
+
+/** \file
+ *
+ * USB Device Configuration Descriptor processing routines, to determine the correct pipe configurations
+ * needed to communication with an attached USB device. Descriptors are special computer-readable structures
+ * which the host requests upon device enumeration, to determine the device's capabilities and functions.
+ */
+
+#include "ConfigDescriptor.h"
+
+/** Reads and processes an attached device's descriptors, to determine compatibility and pipe configurations. This
+ * routine will read in the entire configuration descriptor, and configure the hosts pipes to correctly communicate
+ * with compatible devices.
+ *
+ * This routine searches for a MSD interface descriptor containing bulk IN and OUT data endpoints.
+ *
+ * \return An error code from the \ref MassStorageHost_GetConfigDescriptorDataCodes_t enum.
+ */
+uint8_t ProcessConfigurationDescriptor(void)
+{
+ uint8_t ConfigDescriptorData[512];
+ void* CurrConfigLocation = ConfigDescriptorData;
+ uint16_t CurrConfigBytesRem;
+
+ USB_Descriptor_Interface_t* MSInterface = NULL;
+ USB_Descriptor_Endpoint_t* DataINEndpoint = NULL;
+ USB_Descriptor_Endpoint_t* DataOUTEndpoint = NULL;
+
+ /* Retrieve the entire configuration descriptor into the allocated buffer */
+ switch (USB_Host_GetDeviceConfigDescriptor(1, &CurrConfigBytesRem, ConfigDescriptorData, sizeof(ConfigDescriptorData)))
+ {
+ case HOST_GETCONFIG_Successful:
+ break;
+ case HOST_GETCONFIG_InvalidData:
+ return InvalidConfigDataReturned;
+ case HOST_GETCONFIG_BuffOverflow:
+ return DescriptorTooLarge;
+ default:
+ return ControlError;
+ }
+
+ while (!(DataINEndpoint) || !(DataOUTEndpoint))
+ {
+ /* See if we've found a likely compatible interface, and if there is an endpoint within that interface */
+ if (!(MSInterface) ||
+ USB_GetNextDescriptorComp(&CurrConfigBytesRem, &CurrConfigLocation,
+ DComp_NextMSInterfaceBulkDataEndpoint) != DESCRIPTOR_SEARCH_COMP_Found)
+ {
+ /* Get the next Mass Storage interface from the configuration descriptor */
+ if (USB_GetNextDescriptorComp(&CurrConfigBytesRem, &CurrConfigLocation,
+ DComp_NextMSInterface) != DESCRIPTOR_SEARCH_COMP_Found)
+ {
+ /* Descriptor not found, error out */
+ return NoCompatibleInterfaceFound;
+ }
+
+ /* Save the interface in case we need to refer back to it later */
+ MSInterface = DESCRIPTOR_PCAST(CurrConfigLocation, USB_Descriptor_Interface_t);
+
+ /* Clear any found endpoints */
+ DataINEndpoint = NULL;
+ DataOUTEndpoint = NULL;
+
+ /* Skip the remainder of the loop as we have not found an endpoint yet */
+ continue;
+ }
+
+ /* Retrieve the endpoint address from the endpoint descriptor */
+ USB_Descriptor_Endpoint_t* EndpointData = DESCRIPTOR_PCAST(CurrConfigLocation, USB_Descriptor_Endpoint_t);
+
+ /* If the endpoint is a IN type endpoint */
+ if ((EndpointData->EndpointAddress & ENDPOINT_DIR_MASK) == ENDPOINT_DIR_IN)
+ DataINEndpoint = EndpointData;
+ else
+ DataOUTEndpoint = EndpointData;
+ }
+
+ /* Configure the Mass Storage data IN pipe */
+ Pipe_ConfigurePipe(MASS_STORE_DATA_IN_PIPE, EP_TYPE_BULK, DataINEndpoint->EndpointAddress, DataINEndpoint->EndpointSize, 1);
+
+ /* Configure the Mass Storage data OUT pipe */
+ Pipe_ConfigurePipe(MASS_STORE_DATA_OUT_PIPE, EP_TYPE_BULK, DataOUTEndpoint->EndpointAddress, DataOUTEndpoint->EndpointSize, 1);
+
+ /* Valid data found, return success */
+ return SuccessfulConfigRead;
+}
+
+/** Descriptor comparator function. This comparator function is can be called while processing an attached USB device's
+ * configuration descriptor, to search for a specific sub descriptor. It can also be used to abort the configuration
+ * descriptor processing if an incompatible descriptor configuration is found.
+ *
+ * This comparator searches for the next Interface descriptor of the correct Mass Storage Class, Subclass and Protocol values.
+ *
+ * \return A value from the DSEARCH_Return_ErrorCodes_t enum
+ */
+uint8_t DComp_NextMSInterface(void* CurrentDescriptor)
+{
+ USB_Descriptor_Header_t* Header = DESCRIPTOR_PCAST(CurrentDescriptor, USB_Descriptor_Header_t);
+
+ if (Header->Type == DTYPE_Interface)
+ {
+ USB_Descriptor_Interface_t* Interface = DESCRIPTOR_PCAST(CurrentDescriptor, USB_Descriptor_Interface_t);
+
+ /* Check the descriptor class and protocol, break out if correct class/protocol interface found */
+ if ((Interface->Class == MASS_STORE_CLASS) &&
+ (Interface->SubClass == MASS_STORE_SUBCLASS) &&
+ (Interface->Protocol == MASS_STORE_PROTOCOL))
+ {
+ return DESCRIPTOR_SEARCH_Found;
+ }
+ }
+
+ return DESCRIPTOR_SEARCH_NotFound;
+}
+
+/** Descriptor comparator function. This comparator function is can be called while processing an attached USB device's
+ * configuration descriptor, to search for a specific sub descriptor. It can also be used to abort the configuration
+ * descriptor processing if an incompatible descriptor configuration is found.
+ *
+ * This comparator searches for the next Bulk Endpoint descriptor of the correct MSD interface, aborting the search if
+ * another interface descriptor is found before the next endpoint.
+ *
+ * \return A value from the DSEARCH_Return_ErrorCodes_t enum
+ */
+uint8_t DComp_NextMSInterfaceBulkDataEndpoint(void* CurrentDescriptor)
+{
+ USB_Descriptor_Header_t* Header = DESCRIPTOR_PCAST(CurrentDescriptor, USB_Descriptor_Header_t);
+
+ if (Header->Type == DTYPE_Endpoint)
+ {
+ USB_Descriptor_Endpoint_t* Endpoint = DESCRIPTOR_PCAST(CurrentDescriptor, USB_Descriptor_Endpoint_t);
+
+ /* Check the endpoint type, break out if correct BULK type endpoint found */
+ if ((Endpoint->Attributes & EP_TYPE_MASK) == EP_TYPE_BULK)
+ return DESCRIPTOR_SEARCH_Found;
+ }
+ else if (Header->Type == DTYPE_Interface)
+ {
+ return DESCRIPTOR_SEARCH_Fail;
+ }
+
+ return DESCRIPTOR_SEARCH_NotFound;
+}
+
diff --git a/Demos/Host/LowLevel/MassStorageHost/ConfigDescriptor.h b/Demos/Host/LowLevel/MassStorageHost/ConfigDescriptor.h
new file mode 100644
index 0000000000..eaf4d28de6
--- /dev/null
+++ b/Demos/Host/LowLevel/MassStorageHost/ConfigDescriptor.h
@@ -0,0 +1,78 @@
+/*
+ LUFA Library
+ Copyright (C) Dean Camera, 2017.
+
+ dean [at] fourwalledcubicle [dot] com
+ www.lufa-lib.org
+*/
+
+/*
+ Copyright 2017 Dean Camera (dean [at] fourwalledcubicle [dot] com)
+
+ Permission to use, copy, modify, distribute, and sell this
+ software and its documentation for any purpose is hereby granted
+ without fee, provided that the above copyright notice appear in
+ all copies and that both that the copyright notice and this
+ permission notice and warranty disclaimer appear in supporting
+ documentation, and that the name of the author not be used in
+ advertising or publicity pertaining to distribution of the
+ software without specific, written prior permission.
+
+ The author disclaims all warranties with regard to this
+ software, including all implied warranties of merchantability
+ and fitness. In no event shall the author be liable for any
+ special, indirect or consequential damages or any damages
+ whatsoever resulting from loss of use, data or profits, whether
+ in an action of contract, negligence or other tortious action,
+ arising out of or in connection with the use or performance of
+ this software.
+*/
+
+/** \file
+ *
+ * Header file for ConfigDescriptor.c.
+ */
+
+#ifndef _CONFIGDESCRIPTOR_H_
+#define _CONFIGDESCRIPTOR_H_
+
+ /* Includes: */
+ #include <LUFA/Drivers/USB/USB.h>
+
+ #include "MassStorageHost.h"
+
+ /* Macros: */
+ /** Interface Class value for the Mass Storage Device class. */
+ #define MASS_STORE_CLASS 0x08
+
+ /** Interface Class value for the Mass Storage Device subclass. */
+ #define MASS_STORE_SUBCLASS 0x06
+
+ /** Interface Protocol value for the Bulk Only transport protocol. */
+ #define MASS_STORE_PROTOCOL 0x50
+
+ /** Pipe address of the Mass Storage data IN pipe. */
+ #define MASS_STORE_DATA_IN_PIPE (PIPE_DIR_IN | 1)
+
+ /** Pipe address of the Mass Storage data OUT pipe. */
+ #define MASS_STORE_DATA_OUT_PIPE (PIPE_DIR_OUT | 2)
+
+ /* Enums: */
+ /** Enum for the possible return codes of the \ref ProcessConfigurationDescriptor() function. */
+ enum MassStorageHost_GetConfigDescriptorDataCodes_t
+ {
+ SuccessfulConfigRead = 0, /**< Configuration Descriptor was processed successfully */
+ ControlError = 1, /**< A control request to the device failed to complete successfully */
+ DescriptorTooLarge = 2, /**< The device's Configuration Descriptor is too large to process */
+ InvalidConfigDataReturned = 3, /**< The device returned an invalid Configuration Descriptor */
+ NoCompatibleInterfaceFound = 4, /**< A compatible interface with the required endpoints was not found */
+ };
+
+ /* Function Prototypes: */
+ uint8_t ProcessConfigurationDescriptor(void);
+
+ uint8_t DComp_NextMSInterface(void* CurrentDescriptor);
+ uint8_t DComp_NextMSInterfaceBulkDataEndpoint(void* CurrentDescriptor);
+
+#endif
+
diff --git a/Demos/Host/LowLevel/MassStorageHost/Lib/MassStoreCommands.c b/Demos/Host/LowLevel/MassStorageHost/Lib/MassStoreCommands.c
new file mode 100644
index 0000000000..5f0dd6f1a4
--- /dev/null
+++ b/Demos/Host/LowLevel/MassStorageHost/Lib/MassStoreCommands.c
@@ -0,0 +1,635 @@
+/*
+ LUFA Library
+ Copyright (C) Dean Camera, 2017.
+
+ dean [at] fourwalledcubicle [dot] com
+ www.lufa-lib.org
+*/
+
+/*
+ Copyright 2017 Dean Camera (dean [at] fourwalledcubicle [dot] com)
+
+ Permission to use, copy, modify, distribute, and sell this
+ software and its documentation for any purpose is hereby granted
+ without fee, provided that the above copyright notice appear in
+ all copies and that both that the copyright notice and this
+ permission notice and warranty disclaimer appear in supporting
+ documentation, and that the name of the author not be used in
+ advertising or publicity pertaining to distribution of the
+ software without specific, written prior permission.
+
+ The author disclaims all warranties with regard to this
+ software, including all implied warranties of merchantability
+ and fitness. In no event shall the author be liable for any
+ special, indirect or consequential damages or any damages
+ whatsoever resulting from loss of use, data or profits, whether
+ in an action of contract, negligence or other tortious action,
+ arising out of or in connection with the use or performance of
+ this software.
+*/
+
+/** \file
+ *
+ * Mass Storage Device commands, to issue MSD commands to the device for
+ * reading device status, capacity, and other characteristics. This file
+ * also contains block read and write functions, so that device blocks
+ * can be read and written. In general, these functions would be chained
+ * to a FAT library to give file-level access to an attached device's contents.
+ *
+ * \note Many Mass Storage devices on the market are non-compliant to the
+ * specifications and thus can prove difficult to interface with. It
+ * may be necessary to retry the functions in the module several times
+ * after they have returned and error to successfully send the command
+ * to the device. Some devices may also need to have the stream function
+ * timeout period extended beyond 100ms (some badly designed devices exceeding
+ * 1.5 seconds occasionally) by defining USB_STREAM_TIMEOUT_MS to a
+ * larger value in the project makefile and passing it to the compiler
+ * via the -D switch.
+ */
+
+#define INCLUDE_FROM_MASSSTORE_COMMANDS_C
+#include "MassStoreCommands.h"
+
+/** Current Tag value used in issued CBWs to the device. This is automatically incremented
+ * each time a command is sent, and is not externally accessible.
+ */
+static uint32_t MassStore_Tag = 1;
+
+
+/** Routine to send the current CBW to the device, and increment the Tag value as needed.
+ *
+ * \param[in] SCSICommandBlock Pointer to a SCSI command block structure to send to the attached device
+ * \param[in,out] BufferPtr Pointer to a buffer for the data to send or receive to/from the device, or NULL if no data
+ *
+ * \return A value from the Pipe_Stream_RW_ErrorCodes_t enum
+ */
+static uint8_t MassStore_SendCommand(MS_CommandBlockWrapper_t* const SCSICommandBlock,
+ void* BufferPtr)
+{
+ uint8_t ErrorCode = PIPE_RWSTREAM_NoError;
+
+ /* Wrap Tag value when invalid - MS class defines tag values of 0 and 0xFFFFFFFF to be invalid */
+ if (++MassStore_Tag == 0xFFFFFFFF)
+ MassStore_Tag = 1;
+
+ /* Each transmission should have a unique tag value, increment before use */
+ SCSICommandBlock->Tag = MassStore_Tag;
+
+ /* Select the OUT data pipe for CBW transmission */
+ Pipe_SelectPipe(MASS_STORE_DATA_OUT_PIPE);
+ Pipe_Unfreeze();
+
+ /* Write the CBW command to the OUT pipe */
+ if ((ErrorCode = Pipe_Write_Stream_LE(SCSICommandBlock, sizeof(MS_CommandBlockWrapper_t), NULL)) !=
+ PIPE_RWSTREAM_NoError)
+ {
+ Pipe_Freeze();
+ return ErrorCode;
+ }
+
+ /* Send the data in the OUT pipe to the attached device */
+ Pipe_ClearOUT();
+
+ /* Wait until command has been sent */
+ Pipe_WaitUntilReady();
+
+ /* Freeze pipe after use */
+ Pipe_Freeze();
+
+ if (BufferPtr != NULL)
+ {
+ /* Transfer the requested data (if any) to or from the device */
+ ErrorCode = MassStore_SendReceiveData(SCSICommandBlock, (void*)BufferPtr);
+
+ /* Only fail completely if the transfer fails without a STALL, as a logical STALL can be recovered from */
+ if ((ErrorCode != PIPE_RWSTREAM_NoError) && (ErrorCode != PIPE_RWSTREAM_PipeStalled))
+ {
+ Pipe_Freeze();
+ return ErrorCode;
+ }
+ }
+
+ /* Retrieve the returned SCSI status from the device */
+ MS_CommandStatusWrapper_t SCSIStatusBlock;
+ return MassStore_GetReturnedStatus(&SCSIStatusBlock);
+}
+
+/** Waits until the attached device is ready to accept data following a CBW, checking
+ * to ensure that the device has not stalled the transaction.
+ *
+ * \return A value from the Pipe_Stream_RW_ErrorCodes_t enum
+ */
+static uint8_t MassStore_WaitForDataReceived(void)
+{
+ uint16_t TimeoutMSRem = COMMAND_DATA_TIMEOUT_MS;
+ uint16_t PreviousFrameNumber = USB_Host_GetFrameNumber();
+
+ /* Select the IN data pipe for data reception */
+ Pipe_SelectPipe(MASS_STORE_DATA_IN_PIPE);
+ Pipe_Unfreeze();
+
+ /* Wait until data received in the IN pipe */
+ while (!(Pipe_IsINReceived()))
+ {
+ uint16_t CurrentFrameNumber = USB_Host_GetFrameNumber();
+
+ /* Check to see if a new frame has been issued (1ms elapsed) */
+ if (CurrentFrameNumber != PreviousFrameNumber)
+ {
+ /* Save the new frame number and decrement the timeout period */
+ PreviousFrameNumber = CurrentFrameNumber;
+ TimeoutMSRem--;
+
+ /* Check to see if the timeout period for the command has elapsed */
+ if (!(TimeoutMSRem))
+ return PIPE_RWSTREAM_Timeout;
+ }
+
+ Pipe_Freeze();
+ Pipe_SelectPipe(MASS_STORE_DATA_OUT_PIPE);
+ Pipe_Unfreeze();
+
+ /* Check if pipe stalled (command failed by device) */
+ if (Pipe_IsStalled())
+ {
+ /* Clear the stall condition on the OUT pipe */
+ USB_Host_ClearEndpointStall(Pipe_GetBoundEndpointAddress());
+
+ return PIPE_RWSTREAM_PipeStalled;
+ }
+
+ Pipe_Freeze();
+ Pipe_SelectPipe(MASS_STORE_DATA_IN_PIPE);
+ Pipe_Unfreeze();
+
+ /* Check if pipe stalled (command failed by device) */
+ if (Pipe_IsStalled())
+ {
+ /* Clear the stall condition on the IN pipe */
+ USB_Host_ClearEndpointStall(Pipe_GetBoundEndpointAddress());
+
+ return PIPE_RWSTREAM_PipeStalled;
+ }
+
+ /* Check to see if the device was disconnected, if so exit function */
+ if (USB_HostState == HOST_STATE_Unattached)
+ return PIPE_RWSTREAM_DeviceDisconnected;
+ };
+
+ Pipe_SelectPipe(MASS_STORE_DATA_IN_PIPE);
+ Pipe_Freeze();
+
+ Pipe_SelectPipe(MASS_STORE_DATA_OUT_PIPE);
+ Pipe_Freeze();
+
+ return PIPE_RWSTREAM_NoError;
+}
+
+/** Sends or receives the transaction's data stage to or from the attached device, reading or
+ * writing to the nominated buffer.
+ *
+ * \param[in] SCSICommandBlock Pointer to a SCSI command block structure being sent to the attached device
+ * \param[in,out] BufferPtr Pointer to the data buffer to read from or write to
+ *
+ * \return A value from the Pipe_Stream_RW_ErrorCodes_t enum
+ */
+static uint8_t MassStore_SendReceiveData(MS_CommandBlockWrapper_t* const SCSICommandBlock,
+ void* BufferPtr)
+{
+ uint8_t ErrorCode = PIPE_RWSTREAM_NoError;
+ uint16_t BytesRem = SCSICommandBlock->DataTransferLength;
+
+ /* Check the direction of the SCSI command data stage */
+ if (SCSICommandBlock->Flags & MS_COMMAND_DIR_DATA_IN)
+ {
+ /* Wait until the device has replied with some data */
+ if ((ErrorCode = MassStore_WaitForDataReceived()) != PIPE_RWSTREAM_NoError)
+ return ErrorCode;
+
+ /* Select the IN data pipe for data reception */
+ Pipe_SelectPipe(MASS_STORE_DATA_IN_PIPE);
+ Pipe_Unfreeze();
+
+ /* Read in the block data from the pipe */
+ if ((ErrorCode = Pipe_Read_Stream_LE(BufferPtr, BytesRem, NULL)) != PIPE_RWSTREAM_NoError)
+ return ErrorCode;
+
+ /* Acknowledge the packet */
+ Pipe_ClearIN();
+ }
+ else
+ {
+ /* Select the OUT data pipe for data transmission */
+ Pipe_SelectPipe(MASS_STORE_DATA_OUT_PIPE);
+ Pipe_Unfreeze();
+
+ /* Write the block data to the pipe */
+ if ((ErrorCode = Pipe_Write_Stream_LE(BufferPtr, BytesRem, NULL)) != PIPE_RWSTREAM_NoError)
+ return ErrorCode;
+
+ /* Acknowledge the packet */
+ Pipe_ClearOUT();
+
+ while (!(Pipe_IsOUTReady()))
+ {
+ if (USB_HostState == HOST_STATE_Unattached)
+ return PIPE_RWSTREAM_DeviceDisconnected;
+ }
+ }
+
+ /* Freeze used pipe after use */
+ Pipe_Freeze();
+
+ return PIPE_RWSTREAM_NoError;
+}
+
+/** Routine to receive the current CSW from the device.
+ *
+ * \param[out] SCSICommandStatus Pointer to a destination where the returned status data should be stored
+ *
+ * \return A value from the Pipe_Stream_RW_ErrorCodes_t enum, or MASS_STORE_SCSI_COMMAND_FAILED if the SCSI command fails
+ */
+static uint8_t MassStore_GetReturnedStatus(MS_CommandStatusWrapper_t* const SCSICommandStatus)
+{
+ uint8_t ErrorCode = PIPE_RWSTREAM_NoError;
+
+ /* If an error in the command occurred, abort */
+ if ((ErrorCode = MassStore_WaitForDataReceived()) != PIPE_RWSTREAM_NoError)
+ return ErrorCode;
+
+ /* Select the IN data pipe for data reception */
+ Pipe_SelectPipe(MASS_STORE_DATA_IN_PIPE);
+ Pipe_Unfreeze();
+
+ /* Load in the CSW from the attached device */
+ if ((ErrorCode = Pipe_Read_Stream_LE(SCSICommandStatus, sizeof(MS_CommandStatusWrapper_t), NULL)) !=
+ PIPE_RWSTREAM_NoError)
+ {
+ return ErrorCode;
+ }
+
+ /* Clear the data ready for next reception */
+ Pipe_ClearIN();
+
+ /* Freeze the IN pipe after use */
+ Pipe_Freeze();
+
+ /* Check to see if command failed */
+ if (SCSICommandStatus->Status != MS_SCSI_COMMAND_Pass)
+ ErrorCode = MASS_STORE_SCSI_COMMAND_FAILED;
+
+ return ErrorCode;
+}
+
+/** Issues a Mass Storage class specific request to reset the attached device's Mass Storage interface,
+ * readying the device for the next CBW. The Data endpoints are cleared of any STALL condition once this
+ * command completes successfully.
+ *
+ * \return A value from the USB_Host_SendControlErrorCodes_t enum, or MASS_STORE_SCSI_COMMAND_FAILED if the SCSI command fails
+ */
+uint8_t MassStore_MassStorageReset(void)
+{
+ uint8_t ErrorCode;
+
+ USB_ControlRequest = (USB_Request_Header_t)
+ {
+ .bmRequestType = (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE),
+ .bRequest = MS_REQ_MassStorageReset,
+ .wValue = 0,
+ .wIndex = 0,
+ .wLength = 0,
+ };
+
+ /* Select the control pipe for the request transfer */
+ Pipe_SelectPipe(PIPE_CONTROLPIPE);
+
+ if ((ErrorCode = USB_Host_SendControlRequest(NULL)) != HOST_SENDCONTROL_Successful)
+ return ErrorCode;
+
+ /* Select first data pipe to clear STALL condition if one exists */
+ Pipe_SelectPipe(MASS_STORE_DATA_IN_PIPE);
+
+ if ((ErrorCode = USB_Host_ClearEndpointStall(Pipe_GetBoundEndpointAddress())) != HOST_SENDCONTROL_Successful)
+ return ErrorCode;
+
+ /* Select second data pipe to clear STALL condition if one exists */
+ Pipe_SelectPipe(MASS_STORE_DATA_OUT_PIPE);
+
+ if ((ErrorCode = USB_Host_ClearEndpointStall(Pipe_GetBoundEndpointAddress())) != HOST_SENDCONTROL_Successful)
+ return ErrorCode;
+
+ return HOST_SENDCONTROL_Successful;
+}
+
+/** Issues a Mass Storage class specific request to determine the index of the highest numbered Logical
+ * Unit in the attached device.
+ *
+ * \note Some devices do not support this request, and will STALL it when issued. To get around this,
+ * on unsupported devices the max LUN index will be reported as zero and no error will be returned
+ * if the device STALLs the request.
+ *
+ * \param[out] MaxLUNIndex Pointer to the location that the maximum LUN index value should be stored
+ *
+ * \return A value from the USB_Host_SendControlErrorCodes_t enum, or MASS_STORE_SCSI_COMMAND_FAILED if the SCSI command fails
+ */
+uint8_t MassStore_GetMaxLUN(uint8_t* const MaxLUNIndex)
+{
+ uint8_t ErrorCode;
+
+ USB_ControlRequest = (USB_Request_Header_t)
+ {
+ .bmRequestType = (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE),
+ .bRequest = MS_REQ_GetMaxLUN,
+ .wValue = 0,
+ .wIndex = 0,
+ .wLength = 1,
+ };
+
+ /* Select the control pipe for the request transfer */
+ Pipe_SelectPipe(PIPE_CONTROLPIPE);
+
+ if ((ErrorCode = USB_Host_SendControlRequest(MaxLUNIndex)) == HOST_SENDCONTROL_SetupStalled)
+ {
+ /* Clear the pipe stall */
+ Pipe_ClearStall();
+
+ /* Some faulty Mass Storage devices don't implement the GET_MAX_LUN request, so assume a single LUN */
+ *MaxLUNIndex = 0;
+
+ /* Clear the error, and pretend the request executed correctly if the device STALLed it */
+ ErrorCode = HOST_SENDCONTROL_Successful;
+ }
+
+ return ErrorCode;
+}
+
+/** Issues a SCSI Inquiry command to the attached device, to determine the device's information. This
+ * gives information on the device's capabilities.
+ *
+ * \param[in] LUNIndex Index of the LUN inside the device the command is being addressed to
+ * \param[out] InquiryPtr Pointer to the inquiry data structure where the inquiry data from the device is to be stored
+ *
+ * \return A value from the Pipe_Stream_RW_ErrorCodes_t enum, or MASS_STORE_SCSI_COMMAND_FAILED if the SCSI command fails
+ */
+uint8_t MassStore_Inquiry(const uint8_t LUNIndex,
+ SCSI_Inquiry_Response_t* const InquiryPtr)
+{
+ /* Create a CBW with a SCSI command to issue INQUIRY command */
+ MS_CommandBlockWrapper_t SCSICommandBlock = (MS_CommandBlockWrapper_t)
+ {
+ .Signature = MS_CBW_SIGNATURE,
+ .DataTransferLength = sizeof(SCSI_Inquiry_Response_t),
+ .Flags = MS_COMMAND_DIR_DATA_IN,
+ .LUN = LUNIndex,
+ .SCSICommandLength = 6,
+ .SCSICommandData =
+ {
+ SCSI_CMD_INQUIRY,
+ 0x00, // Reserved
+ 0x00, // Reserved
+ 0x00, // Reserved
+ sizeof(SCSI_Inquiry_Response_t), // Allocation Length
+ 0x00 // Unused (control)
+ }
+ };
+
+ /* Send the command and any data to the attached device */
+ return MassStore_SendCommand(&SCSICommandBlock, InquiryPtr);
+}
+
+/** Issues a SCSI Request Sense command to the attached device, to determine the current SCSI sense information. This
+ * gives error codes for the last issued SCSI command to the device.
+ *
+ * \param[in] LUNIndex Index of the LUN inside the device the command is being addressed to
+ * \param[out] SensePtr Pointer to the sense data structure where the sense data from the device is to be stored
+ *
+ * \return A value from the Pipe_Stream_RW_ErrorCodes_t enum, or MASS_STORE_SCSI_COMMAND_FAILED if the SCSI command fails
+ */
+uint8_t MassStore_RequestSense(const uint8_t LUNIndex,
+ SCSI_Request_Sense_Response_t* const SensePtr)
+{
+ /* Create a CBW with a SCSI command to issue REQUEST SENSE command */
+ MS_CommandBlockWrapper_t SCSICommandBlock = (MS_CommandBlockWrapper_t)
+ {
+ .Signature = MS_CBW_SIGNATURE,
+ .DataTransferLength = sizeof(SCSI_Request_Sense_Response_t),
+ .Flags = MS_COMMAND_DIR_DATA_IN,
+ .LUN = LUNIndex,
+ .SCSICommandLength = 6,
+ .SCSICommandData =
+ {
+ SCSI_CMD_REQUEST_SENSE,
+ 0x00, // Reserved
+ 0x00, // Reserved
+ 0x00, // Reserved
+ sizeof(SCSI_Request_Sense_Response_t), // Allocation Length
+ 0x00 // Unused (control)
+ }
+ };
+
+ /* Send the command and any data to the attached device */
+ return MassStore_SendCommand(&SCSICommandBlock, SensePtr);
+}
+
+/** Issues a SCSI Device Block Read command to the attached device, to read in one or more data blocks from the
+ * storage medium into a buffer.
+ *
+ * \param[in] LUNIndex Index of the LUN inside the device the command is being addressed to
+ * \param[in] BlockAddress Start block address to read from
+ * \param[in] Blocks Number of blocks to read from the device
+ * \param[in] BlockSize Size in bytes of each block to read
+ * \param[out] BufferPtr Pointer to the buffer where the read data is to be written to
+ *
+ * \return A value from the Pipe_Stream_RW_ErrorCodes_t enum, or MASS_STORE_SCSI_COMMAND_FAILED if the SCSI command fails
+ */
+uint8_t MassStore_ReadDeviceBlock(const uint8_t LUNIndex,
+ const uint32_t BlockAddress,
+ const uint8_t Blocks,
+ const uint16_t BlockSize,
+ void* BufferPtr)
+{
+ /* Create a CBW with a SCSI command to read in the given blocks from the device */
+ MS_CommandBlockWrapper_t SCSICommandBlock = (MS_CommandBlockWrapper_t)
+ {
+ .Signature = MS_CBW_SIGNATURE,
+ .DataTransferLength = ((uint32_t)Blocks * BlockSize),
+ .Flags = MS_COMMAND_DIR_DATA_IN,
+ .LUN = LUNIndex,
+ .SCSICommandLength = 10,
+ .SCSICommandData =
+ {
+ SCSI_CMD_READ_10,
+ 0x00, // Unused (control bits, all off)
+ (BlockAddress >> 24), // MSB of Block Address
+ (BlockAddress >> 16),
+ (BlockAddress >> 8),
+ (BlockAddress & 0xFF), // LSB of Block Address
+ 0x00, // Reserved
+ 0x00, // MSB of Total Blocks to Read
+ Blocks, // LSB of Total Blocks to Read
+ 0x00 // Unused (control)
+ }
+ };
+
+ /* Send the command and any data to the attached device */
+ return MassStore_SendCommand(&SCSICommandBlock, BufferPtr);
+}
+
+/** Issues a SCSI Device Block Write command to the attached device, to write one or more data blocks to the
+ * storage medium from a buffer.
+ *
+ * \param[in] LUNIndex Index of the LUN inside the device the command is being addressed to
+ * \param[in] BlockAddress Start block address to write to
+ * \param[in] Blocks Number of blocks to write to in the device
+ * \param[in] BlockSize Size in bytes of each block to write
+ * \param[in] BufferPtr Pointer to the buffer where the write data is to be sourced from
+ *
+ * \return A value from the Pipe_Stream_RW_ErrorCodes_t enum, or MASS_STORE_SCSI_COMMAND_FAILED if the SCSI command fails
+ */
+uint8_t MassStore_WriteDeviceBlock(const uint8_t LUNIndex,
+ const uint32_t BlockAddress,
+ const uint8_t Blocks,
+ const uint16_t BlockSize,
+ void* BufferPtr)
+{
+ /* Create a CBW with a SCSI command to write the given blocks to the device */
+ MS_CommandBlockWrapper_t SCSICommandBlock = (MS_CommandBlockWrapper_t)
+ {
+ .Signature = MS_CBW_SIGNATURE,
+ .DataTransferLength = ((uint32_t)Blocks * BlockSize),
+ .Flags = MS_COMMAND_DIR_DATA_OUT,
+ .LUN = LUNIndex,
+ .SCSICommandLength = 10,
+ .SCSICommandData =
+ {
+ SCSI_CMD_WRITE_10,
+ 0x00, // Unused (control bits, all off)
+ (BlockAddress >> 24), // MSB of Block Address
+ (BlockAddress >> 16),
+ (BlockAddress >> 8),
+ (BlockAddress & 0xFF), // LSB of Block Address
+ 0x00, // Unused (reserved)
+ 0x00, // MSB of Total Blocks to Write
+ Blocks, // LSB of Total Blocks to Write
+ 0x00 // Unused (control)
+ }
+ };
+
+ /* Send the command and any data to the attached device */
+ return MassStore_SendCommand(&SCSICommandBlock, BufferPtr);
+}
+
+/** Issues a SCSI Device Test Unit Ready command to the attached device, to determine if the device is ready to accept
+ * other commands.
+ *
+ * \param[in] LUNIndex Index of the LUN inside the device the command is being addressed to
+ *
+ * \return A value from the Pipe_Stream_RW_ErrorCodes_t enum, or MASS_STORE_SCSI_COMMAND_FAILED if the SCSI command fails
+ */
+uint8_t MassStore_TestUnitReady(const uint8_t LUNIndex)
+{
+ /* Create a CBW with a SCSI command to issue TEST UNIT READY command */
+ MS_CommandBlockWrapper_t SCSICommandBlock = (MS_CommandBlockWrapper_t)
+ {
+ .Signature = MS_CBW_SIGNATURE,
+ .DataTransferLength = 0,
+ .Flags = MS_COMMAND_DIR_DATA_IN,
+ .LUN = LUNIndex,
+ .SCSICommandLength = 6,
+ .SCSIComm