summaryrefslogtreecommitdiff
path: root/usblib/device/usbdmsc.c
diff options
context:
space:
mode:
authorYuval Adam <yuv.adm@gmail.com>2012-10-29 23:08:53 +0200
committerYuval Adam <yuv.adm@gmail.com>2012-10-29 23:08:53 +0200
commitc241dbd7e78c50327781a35d88d9f2db7ff2b271 (patch)
tree2c84fe512c0cd3ee328244bca2f8ed4f9622074d /usblib/device/usbdmsc.c
parent4ba8614c006f9828f0796c140bc3e13c9e67938c (diff)
Added usblib
Diffstat (limited to 'usblib/device/usbdmsc.c')
-rw-r--r--usblib/device/usbdmsc.c2130
1 files changed, 2130 insertions, 0 deletions
diff --git a/usblib/device/usbdmsc.c b/usblib/device/usbdmsc.c
new file mode 100644
index 0000000..e563423
--- /dev/null
+++ b/usblib/device/usbdmsc.c
@@ -0,0 +1,2130 @@
+//*****************************************************************************
+//
+// usbdmsc.c - USB mass storage device class driver.
+//
+// Copyright (c) 2009-2012 Texas Instruments Incorporated. All rights reserved.
+// Software License Agreement
+//
+// Texas Instruments (TI) is supplying this software for use solely and
+// exclusively on TI's microcontroller products. The software is owned by
+// TI and/or its suppliers, and is protected under applicable copyright
+// laws. You may not combine this software with "viral" open-source
+// software in order to form a larger program.
+//
+// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
+// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
+// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
+// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
+// DAMAGES, FOR ANY REASON WHATSOEVER.
+//
+// This is part of revision 9453 of the Stellaris USB Library.
+//
+//*****************************************************************************
+
+#include "inc/hw_memmap.h"
+#include "inc/hw_types.h"
+#include "driverlib/debug.h"
+#include "driverlib/rom.h"
+#include "driverlib/rom_map.h"
+#include "driverlib/sysctl.h"
+#include "driverlib/usb.h"
+#include "driverlib/udma.h"
+#include "usblib/usblib.h"
+#include "usblib/usbmsc.h"
+#include "usblib/device/usbdevice.h"
+#include "usblib/device/usbdmsc.h"
+
+//*****************************************************************************
+//
+//! \addtogroup msc_device_class_api
+//! @{
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// These are the internal flags used with the ulFlags member variable.
+//
+//*****************************************************************************
+#define USBD_FLAG_DMA_IN 0x00000001
+#define USBD_FLAG_DMA_OUT 0x00000002
+
+//*****************************************************************************
+//
+// The subset of endpoint status flags that we consider to be reception
+// errors. These are passed to the client via USB_EVENT_ERROR if seen.
+//
+//*****************************************************************************
+#define USB_RX_ERROR_FLAGS (USBERR_DEV_RX_DATA_ERROR | \
+ USBERR_DEV_RX_OVERRUN | \
+ USBERR_DEV_RX_FIFO_FULL)
+
+//*****************************************************************************
+//
+// These are fields that are used by the USB descriptors for the Mass Storage
+// Class.
+//
+//*****************************************************************************
+#define USB_MSC_SUBCLASS_SCSI 0x6
+#define USB_MSC_PROTO_BULKONLY 0x50
+
+//*****************************************************************************
+//
+// Endpoints to use for each of the required endpoints in the driver.
+//
+//*****************************************************************************
+#define DATA_IN_ENDPOINT USB_EP_1
+#define DATA_IN_DMA_CHANNEL UDMA_CHANNEL_USBEP1TX
+#define DATA_OUT_ENDPOINT USB_EP_1
+#define DATA_OUT_DMA_CHANNEL UDMA_CHANNEL_USBEP1RX
+
+//*****************************************************************************
+//
+// Maximum packet size for the bulk endpoints is 64 bytes.
+//
+//*****************************************************************************
+#define DATA_IN_EP_MAX_SIZE 64
+#define DATA_OUT_EP_MAX_SIZE 64
+
+//*****************************************************************************
+//
+// These defines control the sizes of USB transfers for data and commands.
+//
+//*****************************************************************************
+#define MAX_TRANSFER_SIZE 512
+#define COMMAND_BUFFER_SIZE 64
+
+//*****************************************************************************
+//
+// The local buffer used to read in commands and process them.
+//
+//*****************************************************************************
+static unsigned char g_pucCommand[COMMAND_BUFFER_SIZE];
+
+//*****************************************************************************
+//
+// The current transfer state is held in these variables.
+//
+//*****************************************************************************
+static tMSCCSW g_sSCSICSW;
+
+//*****************************************************************************
+//
+// The current state for the SCSI commands that are being handled and are
+// stored in the tMSCInstance.ucSCSIState structure member.
+//
+//*****************************************************************************
+
+//
+// No command in process.
+//
+#define STATE_SCSI_IDLE 0x00
+
+//
+// Sending and reading logical blocks.
+//
+#define STATE_SCSI_SEND_BLOCKS 0x01
+
+//
+// Receiving and writing logical blocks.
+//
+#define STATE_SCSI_RECEIVE_BLOCKS 0x02
+
+//
+// Send the status once the previous transfer is complete.
+//
+#define STATE_SCSI_SEND_STATUS 0x03
+
+//
+// Status was prepared to be sent and now waiting for it to have gone out.
+//
+#define STATE_SCSI_SENT_STATUS 0x04
+
+//*****************************************************************************
+//
+// Device Descriptor. This is stored in RAM to allow several fields to be
+// changed at runtime based on the client's requirements.
+//
+//*****************************************************************************
+static unsigned char g_pMSCDeviceDescriptor[] =
+{
+ 18, // Size of this structure.
+ USB_DTYPE_DEVICE, // Type of this structure.
+ USBShort(0x110), // USB version 1.1 (if we say 2.0, hosts assume
+ // high-speed - see USB 2.0 spec 9.2.6.6)
+ 0, // USB Device Class (spec 5.1.1)
+ 0, // USB Device Sub-class (spec 5.1.1)
+ 0, // USB Device protocol (spec 5.1.1)
+ 64, // Maximum packet size for default pipe.
+ USBShort(0), // Vendor ID (filled in during USBDCDCInit).
+ USBShort(0), // Product ID (filled in during USBDCDCInit).
+ USBShort(0x100), // Device Version BCD.
+ 1, // Manufacturer string identifier.
+ 2, // Product string identifier.
+ 3, // Product serial number.
+ 1 // Number of configurations.
+};
+
+//*****************************************************************************
+//
+// Mass storage device configuration descriptor.
+//
+// It is vital that the configuration descriptor bConfigurationValue field
+// (byte 6) is 1 for the first configuration and increments by 1 for each
+// additional configuration defined here. This relationship is assumed in the
+// device stack for simplicity even though the USB 2.0 specification imposes
+// no such restriction on the bConfigurationValue values.
+//
+// Note that this structure is deliberately located in RAM since we need to
+// be able to patch some values in it based on client requirements.
+//
+//*****************************************************************************
+static unsigned char g_pMSCDescriptor[] =
+{
+ //
+ // Configuration descriptor header.
+ //
+ 9, // Size of the configuration descriptor.
+ USB_DTYPE_CONFIGURATION, // Type of this descriptor.
+ USBShort(32), // The total size of this full structure.
+ 1, // The number of interfaces in this
+ // configuration.
+ 1, // The unique value for this configuration.
+ 0, // The string identifier that describes this
+ // configuration.
+ USB_CONF_ATTR_SELF_PWR, // Bus Powered, Self Powered, remote wake up.
+ 250, // The maximum power in 2mA increments.
+};
+
+//*****************************************************************************
+//
+// The remainder of the configuration descriptor is stored in flash since we
+// don't need to modify anything in it at runtime.
+//
+//*****************************************************************************
+const unsigned char g_pMSCInterface[] =
+{
+ //
+ // Vendor-specific Interface Descriptor.
+ //
+ 9, // Size of the interface descriptor.
+ USB_DTYPE_INTERFACE, // Type of this descriptor.
+ 0, // The index for this interface.
+ 0, // The alternate setting for this interface.
+ 2, // The number of endpoints used by this
+ // interface.
+ USB_CLASS_MASS_STORAGE, // The interface class
+ USB_MSC_SUBCLASS_SCSI, // The interface sub-class.
+ USB_MSC_PROTO_BULKONLY, // The interface protocol for the sub-class
+ // specified above.
+ 0, // The string index for this interface.
+
+ //
+ // Endpoint Descriptor
+ //
+ 7, // The size of the endpoint descriptor.
+ USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint.
+ USB_EP_DESC_IN | USB_EP_TO_INDEX(DATA_IN_ENDPOINT),
+ USB_EP_ATTR_BULK, // Endpoint is a bulk endpoint.
+ USBShort(DATA_IN_EP_MAX_SIZE), // The maximum packet size.
+ 0, // The polling interval for this endpoint.
+
+ //
+ // Endpoint Descriptor
+ //
+ 7, // The size of the endpoint descriptor.
+ USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint.
+ USB_EP_DESC_OUT | USB_EP_TO_INDEX(DATA_OUT_ENDPOINT),
+ USB_EP_ATTR_BULK, // Endpoint is a bulk endpoint.
+ USBShort(DATA_OUT_EP_MAX_SIZE), // The maximum packet size.
+ 0, // The polling interval for this endpoint.
+};
+
+//*****************************************************************************
+//
+// The mass storage configuration descriptor is defined as two sections,
+// one containing just the 9 byte USB configuration descriptor and the other
+// containing everything else that is sent to the host along with it.
+//
+//*****************************************************************************
+const tConfigSection g_sMSCConfigSection =
+{
+ sizeof(g_pMSCDescriptor),
+ g_pMSCDescriptor
+};
+
+const tConfigSection g_sMSCInterfaceSection =
+{
+ sizeof(g_pMSCInterface),
+ g_pMSCInterface
+};
+
+//*****************************************************************************
+//
+// This array lists all the sections that must be concatenated to make a
+// single, complete bulk device configuration descriptor.
+//
+//*****************************************************************************
+const tConfigSection *g_psMSCSections[] =
+{
+ &g_sMSCConfigSection,
+ &g_sMSCInterfaceSection
+};
+
+#define NUM_MSC_SECTIONS (sizeof(g_psMSCSections) / sizeof(tConfigSection *))
+
+//*****************************************************************************
+//
+// The header for the single configuration we support. This is the root of
+// the data structure that defines all the bits and pieces that are pulled
+// together to generate the configuration descriptor.
+//
+//*****************************************************************************
+const tConfigHeader g_sMSCConfigHeader =
+{
+ NUM_MSC_SECTIONS,
+ g_psMSCSections
+};
+
+//*****************************************************************************
+//
+// Configuration Descriptor.
+//
+//*****************************************************************************
+const tConfigHeader * const g_pMSCConfigDescriptors[] =
+{
+ &g_sMSCConfigHeader
+};
+
+//*****************************************************************************
+//
+// Various internal handlers needed by this class.
+//
+//*****************************************************************************
+static void HandleDisconnect(void *pvInstance);
+static void ConfigChangeHandler(void *pvInstance, unsigned long ulValue);
+static void HandleEndpoints(void *pvInstance, unsigned long ulStatus);
+static void HandleRequests(void *pvInstance, tUSBRequest *pUSBRequest);
+static void USBDSCSISendStatus(const tUSBDMSCDevice *psDevice);
+unsigned long USBDSCSICommand(const tUSBDMSCDevice *psDevice,
+ tMSCCBW *pSCSICBW);
+static void HandleDevice(void *pvInstance, unsigned long ulRequest,
+ void *pvRequestData);
+
+//*****************************************************************************
+//
+// The FIFO configuration for USB mass storage class device.
+//
+//*****************************************************************************
+const tFIFOConfig g_sUSBMSCFIFOConfig =
+{
+ //
+ // IN endpoints.
+ //
+ {
+ { false, USB_EP_DEV_IN | USB_EP_DMA_MODE_1 | USB_EP_AUTO_SET },
+ { false, USB_EP_DEV_IN },
+ { false, USB_EP_DEV_IN },
+ { false, USB_EP_DEV_IN },
+ { false, USB_EP_DEV_IN },
+ { false, USB_EP_DEV_IN },
+ { false, USB_EP_DEV_IN },
+ { false, USB_EP_DEV_IN },
+ { false, USB_EP_DEV_IN },
+ { false, USB_EP_DEV_IN },
+ { false, USB_EP_DEV_IN },
+ { false, USB_EP_DEV_IN },
+ { false, USB_EP_DEV_IN },
+ { false, USB_EP_DEV_IN },
+ { false, USB_EP_DEV_IN }
+ },
+
+ //
+ // OUT endpoints.
+ //
+ {
+ { false, USB_EP_DEV_OUT | USB_EP_DMA_MODE_1 | USB_EP_AUTO_CLEAR },
+ { false, USB_EP_DEV_OUT },
+ { false, USB_EP_DEV_OUT },
+ { false, USB_EP_DEV_OUT },
+ { false, USB_EP_DEV_OUT },
+ { false, USB_EP_DEV_OUT },
+ { false, USB_EP_DEV_OUT },
+ { false, USB_EP_DEV_OUT },
+ { false, USB_EP_DEV_OUT },
+ { false, USB_EP_DEV_OUT },
+ { false, USB_EP_DEV_OUT },
+ { false, USB_EP_DEV_OUT },
+ { false, USB_EP_DEV_OUT },
+ { false, USB_EP_DEV_OUT },
+ { false, USB_EP_DEV_OUT }
+ },
+};
+
+//*****************************************************************************
+//
+// The device information structure for the USB MSC device.
+//
+//*****************************************************************************
+tDeviceInfo g_sMSCDeviceInfo =
+{
+ //
+ // Device event handler callbacks.
+ //
+ {
+ //
+ // GetDescriptor
+ //
+ 0,
+
+ //
+ // RequestHandler
+ //
+ HandleRequests,
+
+ //
+ // InterfaceChange
+ //
+ 0,
+
+ //
+ // ConfigChange
+ //
+ ConfigChangeHandler,
+
+ //
+ // DataReceived
+ //
+ 0,
+
+ //
+ // DataSentCallback
+ //
+ 0,
+
+ //
+ // ResetHandler
+ //
+ 0,
+
+ //
+ // SuspendHandler
+ //
+ 0,
+
+ //
+ // ResumeHandler
+ //
+ 0,
+
+ //
+ // DisconnectHandler
+ //
+ HandleDisconnect,
+
+ //
+ // EndpointHandler
+ //
+ HandleEndpoints,
+
+ //
+ // Device handler
+ //
+ HandleDevice
+ },
+ g_pMSCDeviceDescriptor,
+ g_pMSCConfigDescriptors,
+ 0,
+ 0,
+ &g_sUSBMSCFIFOConfig
+};
+
+//*****************************************************************************
+//
+//! This function is used by an application if it can detect insertion or
+//! removal of the media.
+//!
+//! \param pvInstance is the mass storage device instance that had a media
+//! change.
+//! \param eMediaStatus is the updated status for the media.
+//!
+//! This function should be called by an application when it detects a change
+//! in the status of the media in use by the USB mass storage class. The
+//! \e eMediaStatus parameter will indicate the new status of the media and
+//! can also indicate that the application has no knowledge of the media state.
+//!
+//! There are currently the three following values for the \e eMediaStatus
+//! parameter:
+//! - USBDMSC_MEDIA_PRESENT indicates that the media is present or has been
+//! added.
+//! - USBDMSC_MEDIA_NOTPRESENT indicates that the media is not present or was
+//! removed.
+//! - USBDMSC_MEDIA_UNKNOWN indicates that the application has no knowledge of
+//! the media state and the USB mass storage class.
+//!
+//! It will be left up to the application to call this function whenever it
+//! detects a change or simply call it once with USBDMSC_MEDIA_UNKNOWN and
+//! allow the mass storage class to infer the state from the remaining device
+//! APIs.
+//!
+//! \note It is recommended that the application use this function to inform
+//! the mass storage class of media state changes as it will lead to a more
+//! responsive system.
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+USBDMSCMediaChange(void *pvInstance, tUSBDMSCMediaStatus eMediaStatus)
+{
+ const tUSBDMSCDevice *psDevice;
+
+ //
+ // Create a device instance pointer.
+ //
+ psDevice = pvInstance;
+
+ //
+ // Save the current media status.
+ //
+ psDevice->psPrivateData->eMediaStatus = eMediaStatus;
+}
+
+//*****************************************************************************
+//
+// This function is called to handle the interrupts on the Bulk endpoints for
+// the mass storage class.
+//
+//*****************************************************************************
+static void
+HandleEndpoints(void *pvInstance, unsigned long ulStatus)
+{
+ const tUSBDMSCDevice *psDevice;
+ tMSCInstance *psInst;
+ tMSCCBW *pSCSICBW;
+ unsigned long ulEPStatus;
+ unsigned long ulSize;
+
+ ASSERT(pvInstance != 0);
+
+ //
+ // Determine if the serial device is in single or composite mode because
+ // the meaning of ulIndex is different in both cases.
+ //
+ psDevice = pvInstance;
+
+ //
+ // Initialize the workspace in the passed instance structure.
+ //
+ psInst = psDevice->psPrivateData;
+
+ //
+ // Get the endpoints status.
+ //
+ ulEPStatus = MAP_USBEndpointStatus(USB0_BASE, psInst->ucOUTEndpoint);
+
+ //
+ // Handler for the bulk IN data endpoint.
+ //
+ if((ulStatus & (1 << USB_EP_TO_INDEX(psInst->ucINEndpoint))) ||
+ ((psInst->ulFlags & USBD_FLAG_DMA_IN) &&
+ (MAP_uDMAChannelModeGet(psInst->ucINDMA) == UDMA_MODE_STOP)))
+ {
+ switch(psInst->ucSCSIState)
+ {
+ //
+ // Handle the case where we are sending out data due to a read
+ // command.
+ //
+ case STATE_SCSI_SEND_BLOCKS:
+ {
+ //
+ // Decrement the number of bytes left to send.
+ //
+ psInst->ulBytesToTransfer -= MAX_TRANSFER_SIZE;
+
+ //
+ // If we are done then move on to the status phase.
+ //
+ if(psInst->ulBytesToTransfer == 0)
+ {
+ //
+ // Set the status so that it can be sent when this
+ // response has has be successfully sent.
+ //
+ g_sSCSICSW.bCSWStatus = 0;
+ g_sSCSICSW.dCSWDataResidue = 0;
+
+ //
+ // DMA has completed for the IN endpoint.
+ //
+ psInst->ulFlags &= ~USBD_FLAG_DMA_IN;
+
+ //
+ // Disable uDMA on the endpoint
+ //
+ MAP_USBEndpointDMADisable(USB0_BASE, psInst->ucINEndpoint,
+ USB_EP_DEV_IN);
+
+ //
+ // Send back the status once this transfer is complete.
+ //
+ psInst->ucSCSIState = STATE_SCSI_SEND_STATUS;
+
+ if(psDevice->pfnEventCallback)
+ {
+ psDevice->pfnEventCallback(0, USBD_MSC_EVENT_IDLE, 0,
+ 0);
+ }
+
+ //
+ // The transfer is complete so don't read anymore data.
+ //
+ break;
+ }
+
+ //
+ // Move on to the next Logical Block.
+ //
+ psInst->ulCurrentLBA++;
+
+ //
+ // Read the new data and send it out.
+ //
+ if(psDevice->sMediaFunctions.BlockRead(psInst->pvMedia,
+ (unsigned char *)psInst->pulBuffer,
+ psInst->ulCurrentLBA, 1) == 0)
+ {
+ }
+
+ //
+ // Reset the DMA transfer and enable the DMA channel.
+ //
+ MAP_uDMAChannelTransferSet(psInst->ucINDMA,
+ UDMA_MODE_BASIC,
+ psInst->pulBuffer,
+ (void *)USBFIFOAddrGet(USB0_BASE,
+ psInst->ucINEndpoint),
+ (MAX_TRANSFER_SIZE >> 2));
+ MAP_uDMAChannelEnable(psInst->ucINDMA);
+
+ break;
+ }
+
+ //
+ // Handle sending status.
+ //
+ case STATE_SCSI_SEND_STATUS:
+ {
+ //
+ // Indicate success and no extra data coming.
+ //
+ USBDSCSISendStatus(psDevice);
+
+ break;
+ }
+
+ //
+ // Handle completing sending status.
+ //
+ case STATE_SCSI_SENT_STATUS:
+ {
+ psInst->ucSCSIState = STATE_SCSI_IDLE;
+
+ break;
+ }
+
+ //
+ // These cases should not occur as the being in the IDLE state due
+ // to an IN interrupt is invalid.
+ //
+ case STATE_SCSI_IDLE:
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ //
+ // Handler for the bulk OUT data endpoint.
+ //
+ if((ulStatus & (0x10000 << USB_EP_TO_INDEX(psInst->ucOUTEndpoint))) ||
+ ((psInst->ulFlags & USBD_FLAG_DMA_OUT) &&
+ (MAP_uDMAChannelModeGet(psInst->ucOUTDMA) == UDMA_MODE_STOP)))
+ {
+ //
+ // Get the endpoint status to see why we were called.
+ //
+ ulEPStatus = MAP_USBEndpointStatus(USB0_BASE, psInst->ucOUTEndpoint);
+
+ switch(psInst->ucSCSIState)
+ {
+ //
+ // Receiving and writing bytes to the storage device.
+ //
+ case STATE_SCSI_RECEIVE_BLOCKS:
+ {
+ //
+ // Update the current status for the buffer.
+ //
+ psInst->ulBytesToTransfer -= MAX_TRANSFER_SIZE;
+
+ //
+ // Write the new data.
+ //
+ psDevice->sMediaFunctions.BlockWrite(psInst->pvMedia,
+ (unsigned char *)psInst->pulBuffer,
+ psInst->ulCurrentLBA, 1);
+
+ //
+ // Move on to the next Logical Block.
+ //
+ psInst->ulCurrentLBA++;
+
+ //
+ // Check if all bytes have been received.
+ //
+ if(psInst->ulBytesToTransfer == 0)
+ {
+ //
+ // Set the status so that it can be sent when this response
+ // has be successfully sent.
+ //
+ g_sSCSICSW.bCSWStatus = 0;
+ g_sSCSICSW.dCSWDataResidue = 0;
+
+ //
+ // DMA has completed for the OUT endpoint.
+ //
+ psInst->ulFlags &= ~USBD_FLAG_DMA_OUT;
+
+ //
+ // Indicate success and no extra data coming.
+ //
+ USBDSCSISendStatus(psDevice);
+
+ //
+ // Disable uDMA on the endpoint
+ //
+ MAP_USBEndpointDMADisable(USB0_BASE, psInst->ucOUTEndpoint,
+ USB_EP_DEV_OUT);
+
+ //
+ // If there is an event callback then call it to notify
+ // that last operation has completed.
+ //
+ if(psDevice->pfnEventCallback)
+ {
+ psDevice->pfnEventCallback(0, USBD_MSC_EVENT_IDLE, 0,
+ 0);
+ }
+ }
+ else
+ {
+ //
+ // Configure and enable DMA for the OUT transfer.
+ //
+ MAP_uDMAChannelTransferSet(psInst->ucOUTDMA,
+ UDMA_MODE_BASIC,
+ (void *)USBFIFOAddrGet(USB0_BASE,
+ psInst->ucOUTEndpoint),
+ psInst->pulBuffer,
+ (MAX_TRANSFER_SIZE >> 2));
+
+ //
+ // Start the DMA transfer.
+ //
+ MAP_uDMAChannelEnable(psInst->ucOUTDMA);
+ }
+
+ break;
+ }
+
+ //
+ // If there is an OUT transfer in idle state then it was a new
+ // command.
+ //
+ case STATE_SCSI_IDLE:
+ {
+ //
+ // Attempt to handle the new command.
+ //
+
+ //
+ // Receive the command.
+ //
+ ulSize = COMMAND_BUFFER_SIZE;
+ MAP_USBEndpointDataGet(psInst->ulUSBBase, psInst->ucOUTEndpoint,
+ g_pucCommand, &ulSize);
+ pSCSICBW = (tMSCCBW *)g_pucCommand;
+
+ //
+ // Acknowledge the OUT data packet.
+ //
+ MAP_USBDevEndpointDataAck(psInst->ulUSBBase,
+ psInst->ucOUTEndpoint,
+ false);
+
+ //
+ // If this is a valid CBW then handle it.
+ //
+ if(pSCSICBW->dCBWSignature == CBW_SIGNATURE)
+ {
+ g_sSCSICSW.dCSWSignature = CSW_SIGNATURE;
+ g_sSCSICSW.dCSWTag = pSCSICBW->dCBWTag;
+ g_sSCSICSW.dCSWDataResidue = 0;
+ g_sSCSICSW.bCSWStatus = 0;
+
+ USBDSCSICommand(psDevice, pSCSICBW);
+ }
+ else
+ {
+ //
+ // Just return to the idle state since we are now out of
+ // sync with the host. This should not happen, but this
+ // should allow the device to synchronize with the host
+ // controller.
+ //
+ psInst->ucSCSIState = STATE_SCSI_IDLE;
+ }
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ //
+ // Clear the status bits.
+ //
+ MAP_USBDevEndpointStatusClear(USB0_BASE, psInst->ucOUTEndpoint,
+ ulEPStatus);
+ }
+}
+
+//*****************************************************************************
+//
+// Device instance specific handler.
+//
+//*****************************************************************************
+static void
+HandleDevice(void *pvInstance, unsigned long ulRequest, void *pvRequestData)
+{
+ tMSCInstance *psInst;
+ unsigned char *pucData;
+
+ //
+ // Create the serial instance data.
+ //
+ psInst = ((tUSBDMSCDevice *)pvInstance)->psPrivateData;
+
+ //
+ // Create the char array used by the events supported by the USB CDC
+ // serial class.
+ //
+ pucData = (unsigned char *)pvRequestData;
+
+ switch(ulRequest)
+ {
+ //
+ // This was an interface change event.
+ //
+ case USB_EVENT_COMP_IFACE_CHANGE:
+ {
+ psInst->ucInterface = pucData[1];
+ break;
+ }
+
+ //
+ // This was an endpoint change event.
+ //
+ case USB_EVENT_COMP_EP_CHANGE:
+ {
+ //
+ // Determine if this is an IN or OUT endpoint that has changed.
+ //
+ if(pucData[0] & USB_EP_DESC_IN)
+ {
+ psInst->ucINEndpoint = INDEX_TO_USB_EP((pucData[1] & 0x7f));
+
+ psInst->ucINDMA = UDMA_CHANNEL_USBEP1TX +
+ (((pucData[1] & 0x7f) - 1) * 2);
+
+ //
+ // Basic configuration for DMA on the IN endpoint.
+ //
+ MAP_uDMAChannelControlSet(psInst->ucINDMA,
+ (UDMA_SIZE_32 | UDMA_SRC_INC_32|
+ UDMA_DST_INC_NONE | UDMA_ARB_16));
+
+ //
+ // Select this channel for this endpoint, this only affects
+ // devices that have this feature.
+ //
+ MAP_USBEndpointDMAChannel(USB0_BASE, psInst->ucINEndpoint,
+ psInst->ucINDMA);
+ }
+ else
+ {
+ //
+ // Extract the new endpoint number.
+ //
+ psInst->ucOUTEndpoint = INDEX_TO_USB_EP(pucData[1] & 0x7f);
+ psInst->ucOUTDMA = UDMA_CHANNEL_USBEP1RX +
+ (((pucData[1] & 0x7f) - 1) * 2);
+
+ //
+ // Basic configuration for DMA on the OUT endpoint.
+ //
+ MAP_uDMAChannelControlSet(psInst->ucOUTDMA,
+ (UDMA_SIZE_32 | UDMA_SRC_INC_NONE|
+ UDMA_DST_INC_32 | UDMA_ARB_16));
+
+ //
+ // Select this channel for this endpoint, this only affects
+ // devices that have this feature.
+ //
+ MAP_USBEndpointDMAChannel(USB0_BASE, psInst->ucOUTEndpoint,
+ psInst->ucOUTDMA);
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+//*****************************************************************************
+//
+// This function is called by the USB device stack whenever the device is
+// disconnected from the host.
+//
+//*****************************************************************************
+static void
+HandleDisconnect(void *pvInstance)
+{
+ const tUSBDMSCDevice *psDevice;
+
+ ASSERT(pvInstance != 0);
+
+ //
+ // Create the instance pointer.
+ //
+ psDevice = (const tUSBDMSCDevice *)pvInstance;
+
+ //
+ // Close the drive requested.
+ //
+ if(psDevice->psPrivateData->pvMedia != 0)
+ {
+ psDevice->psPrivateData->pvMedia = 0;
+ psDevice->sMediaFunctions.Close(0);
+ }
+
+ //
+ // If we have a control callback, let the client know we are open for
+ // business.
+ //
+ if(psDevice->pfnEventCallback)
+ {
+ //
+ // Pass the connected event to the client.
+ //
+ psDevice->pfnEventCallback(pvInstance, USB_EVENT_DISCONNECTED, 0, 0);
+ }
+}
+
+//*****************************************************************************
+//
+// This function is called by the USB device stack whenever the device
+// configuration changes.
+//
+//*****************************************************************************
+static void
+ConfigChangeHandler(void *pvInstance, unsigned long ulValue)
+{
+ tMSCInstance *psInst;
+ const tUSBDMSCDevice *psDevice;
+
+ ASSERT(pvInstance != 0);
+
+ //
+ // Create the instance pointer.
+ //
+ psDevice = (const tUSBDMSCDevice *)pvInstance;
+
+ //
+ // Create the serial instance data.
+ //
+ psInst = psDevice->psPrivateData;
+
+ //
+ // Insure that DMA is disable whenever the configuration is set.
+ //
+ MAP_USBEndpointDMADisable(USB0_BASE, psInst->ucINEndpoint, USB_EP_DEV_IN);
+ MAP_USBEndpointDMADisable(USB0_BASE, psInst->ucOUTEndpoint, USB_EP_DEV_OUT);
+
+ //
+ // Basic configuration for DMA on the OUT endpoint.
+ //
+ MAP_uDMAChannelControlSet(psInst->ucOUTDMA, UDMA_SIZE_32 |
+ UDMA_SRC_INC_NONE|
+ UDMA_DST_INC_32 |
+ UDMA_ARB_16);
+
+ //
+ // Select this channel for this endpoint, this only affects devices that
+ // have this feature.
+ //
+ MAP_USBEndpointDMAChannel(USB0_BASE, psInst->ucOUTEndpoint, psInst->ucOUTDMA);
+
+ //
+ // Basic configuration for DMA on the IN endpoint.
+ //
+ MAP_uDMAChannelControlSet(psInst->ucINDMA, UDMA_SIZE_32 | UDMA_SRC_INC_32|
+ UDMA_DST_INC_NONE | UDMA_ARB_16);
+
+ //
+ // Select this channel for this endpoint, this only affects devices that
+ // have this feature.
+ //
+ MAP_USBEndpointDMAChannel(USB0_BASE, psInst->ucINEndpoint, psInst->ucINDMA);
+
+ //
+ // If we have a control callback, let the client know we are open for
+ // business.
+ //
+ if(psDevice->pfnEventCallback)
+ {
+ //
+ // Pass the connected event to the client.
+ //
+ psDevice->pfnEventCallback(pvInstance, USB_EVENT_CONNECTED, 0, 0);
+ }
+}
+
+//*****************************************************************************
+//
+//! This function should be called once for the mass storage class device to
+//! initialized basic operation and prepare for enumeration.
+//!
+//! \param ulIndex is the index of the USB controller to initialize for
+//! mass storage class device operation.
+//! \param psDevice points to a structure containing parameters customizing
+//! the operation of the mass storage device.
+//!
+//! In order for an application to initialize the USB device mass storage
+//! class, it must first call this function with the a valid mass storage
+//! device class structure in the \e psDevice parameter. This allows this
+//! function to initialize the USB controller and device code to be prepared to
+//! enumerate and function as a USB mass storage device.
+//!
+//! This function returns a void pointer that must be passed in to all other
+//! APIs used by the mass storage class.
+//!
+//! See the documentation on the tUSBDMSCDevice structure for more information
+//! on how to properly fill the structure members.
+//!
+//! \return Returns 0 on failure or a non-zero void pointer on success.
+//
+//*****************************************************************************
+void *
+USBDMSCInit(unsigned long ulIndex, const tUSBDMSCDevice *psDevice)
+{
+ //
+ // Check parameter validity.
+ //
+ ASSERT(ulIndex == 0);
+ ASSERT(psDevice);
+ ASSERT(psDevice->ppStringDescriptors);
+ ASSERT(psDevice->psPrivateData);
+
+ USBDMSCCompositeInit(ulIndex, psDevice);
+
+ //
+ // All is well so now pass the descriptors to the lower layer and put
+ // the bulk device on the bus.
+ //
+ USBDCDInit(ulIndex, psDevice->psPrivateData->psDevInfo);
+
+ //
+ // Return the pointer to the instance indicating that everything went well.
+ //
+ return((void *)psDevice);
+}
+
+//*****************************************************************************
+//
+//! This function should be called once for the mass storage class device to
+//! initialized basic operation and prepare for enumeration.
+//!
+//! \param ulIndex is the index of the USB controller to initialize for
+//! mass storage class device operation.
+//! \param psDevice points to a structure containing parameters customizing
+//! the operation of the mass storage device.
+//!
+//! In order for an application to initialize the USB device mass storage
+//! class, it must first call this function with the a valid mass storage
+//! device class structure in the \e psDevice parameter. This allows this
+//! function to initialize the USB controller and device code to be prepared to
+//! enumerate and function as a USB mass storage device.
+//!
+//! This function returns a void pointer that must be passed in to all other
+//! APIs used by the mass storage class.
+//!
+//! See the documentation on the tUSBDMSCDevice structure for more information
+//! on how to properly fill the structure members.
+//!
+//! \return Returns 0 on failure or a non-zero void pointer on success.
+//
+//*****************************************************************************
+void *
+USBDMSCCompositeInit(unsigned long ulIndex, const tUSBDMSCDevice *psDevice)
+{
+ tMSCInstance *psInst;
+ tDeviceDescriptor *psDevDesc;
+
+ //
+ // Check parameter validity.
+ //
+ ASSERT(ulIndex == 0);
+ ASSERT(psDevice);
+ ASSERT(psDevice->ppStringDescriptors);
+ ASSERT(psDevice->psPrivateData);
+
+ //
+ // Initialize the workspace in the passed instance structure.
+ //
+ psInst = psDevice->psPrivateData;
+ psInst->psConfDescriptor = (tConfigDescriptor *)g_pMSCDescriptor;
+ psInst->psDevInfo = &g_sMSCDeviceInfo;
+ psInst->ulUSBBase = USB0_BASE;
+ psInst->bConnected = false;
+ psInst->eMediaStatus = USBDMSC_MEDIA_UNKNOWN;
+
+ //
+ // Set the initial interface and endpoints.
+ //
+ psInst->ucInterface = 0;
+ psInst->ucOUTEndpoint = DATA_OUT_ENDPOINT;
+ psInst->ucOUTDMA = DATA_OUT_DMA_CHANNEL;
+ psInst->ucINEndpoint = DATA_IN_ENDPOINT;
+ psInst->ucINDMA = DATA_IN_DMA_CHANNEL;
+
+ //
+ // Set the initial SCSI state to idle.
+ //
+ psInst->ucSCSIState = STATE_SCSI_IDLE;
+
+ //
+ // Fix up the device descriptor with the client-supplied values.
+ //
+ psDevDesc = (tDeviceDescriptor *)psInst->psDevInfo->pDeviceDescriptor;
+ psDevDesc->idVendor = psDevice->usVID;
+ psDevDesc->idProduct = psDevice->usPID;
+
+ //
+ // Fix up the configuration descriptor with client-supplied values.
+ //
+ psInst->psConfDescriptor->bmAttributes = psDevice->ucPwrAttributes;
+ psInst->psConfDescriptor->bMaxPower =
+ (unsigned char)(psDevice->usMaxPowermA / 2);
+
+ //
+ // Plug in the client's string stable to the device information
+ // structure.
+ //
+ psInst->psDevInfo->ppStringDescriptors = psDevice->ppStringDescriptors;
+ psInst->psDevInfo->ulNumStringDescriptors
+ = psDevice->ulNumStringDescriptors;
+ psInst->psDevInfo->pvInstance = (void *)psDevice;
+
+ //
+ // If DMA is in use then clear all DMA attributes.
+ //
+ MAP_uDMAChannelAttributeDisable(psInst->ucINDMA, UDMA_ATTR_ALL);
+ MAP_uDMAChannelAttributeDisable(psInst->ucOUTDMA, UDMA_ATTR_ALL);
+
+ //
+ // Open the drive requested.
+ //
+ psInst->pvMedia = psDevice->sMediaFunctions.Open(0);
+
+ if(psInst->pvMedia == 0)
+ {
+ //
+ // There is no media currently present.
+ //
+ psInst->ucSenseKey = SCSI_RS_KEY_NOT_READY;
+ psInst->usAddSenseCode = SCSI_RS_MED_NOT_PRSNT;
+ }
+ else
+ {
+ //
+ // Media is now ready for use.
+ //
+ psInst->ucSenseKey = SCSI_RS_KEY_UNIT_ATTN;
+ psInst->usAddSenseCode = SCSI_RS_MED_NOTRDY2RDY;
+ }
+
+ //
+ // Enable Clocking to the USB controller.
+ //
+ MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_USB0);
+
+ //
+ // Turn on USB Phy clock.
+ //
+ MAP_SysCtlUSBPLLEnable();
+
+ //
+ // Return the pointer to the instance indicating that everything went well.
+ //
+ return((void *)psDevice);
+}
+
+//*****************************************************************************
+//
+//! Shuts down the mass storage device.
+//!
+//! \param pvInstance is the pointer to the device instance structure as
+//! returned by USBDMSCInit() or USBDMSCInitComposite().
+//!
+//! This function terminates mass storage operation for the instance supplied
+//! and removes the device from the USB bus. Following this call, the
+//! \e psDevice instance may not me used in any other call to the mass storage
+//! device other than USBDMSCInit() or USBDMSCInitComposite().
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+USBDMSCTerm(void *pvInstance)
+{
+ const tUSBDMSCDevice *psDevice;
+
+ ASSERT(pvInstance != 0);
+
+ //
+ // Cleanly exit device mode.
+ //
+ USBDCDTerm(0);
+
+ //
+ // Create a device instance pointer.
+ //
+ psDevice = pvInstance;
+
+ //
+ // If the media was opened the close it out.
+ //
+ if(psDevice->psPrivateData->pvMedia != 0)
+ {
+ psDevice->psPrivateData->pvMedia = 0;
+ psDevice->sMediaFunctions.Close(0);
+ }
+}
+
+//*****************************************************************************
+//
+// This function is called by the USB device stack whenever a non-standard
+// request is received.
+//
+// \param pvInstance is instance data for this request.
+// \param pUSBRequest points to the request received.
+//
+// This call parses the provided request structure to determine the command.
+// The only mass storage command supported over endpoint 0 is the Get Max LUN
+// command.
+//
+// \return None.
+//
+//*****************************************************************************
+static void
+HandleRequests(void *pvInstance, tUSBRequest *pUSBRequest)
+{
+ //
+ // This class only support a single LUN.
+ //
+ const static unsigned char ucMaxLun = 0;
+
+ ASSERT(pvInstance != 0);
+
+ //
+ // Determine the type of request.
+ //
+ switch(pUSBRequest->bRequest)
+ {
+ //
+ // A Set Report request is received from the host when it sends an
+ // Output report via endpoint 0.
+ //
+ case USBREQ_GET_MAX_LUN:
+ {
+ //
+ // Send our response to the host.
+ //
+ USBDCDSendDataEP0(0, (unsigned char *)&ucMaxLun, 1);
+
+ break;
+ }
+
+ //
+ // This request was not recognized so stall.
+ //
+ default:
+ {
+ USBDCDStallEP0(0);
+ break;
+ }
+ }
+}
+
+//*****************************************************************************
+//
+// This function is used to handle the SCSI Inquiry command when it is received
+// from the host.
+//
+//*****************************************************************************
+static void
+USBDSCSIInquiry(const tUSBDMSCDevice *psDevice)
+{
+ long lIdx;
+ tMSCInstance *psInst;
+ unsigned long *pulData;
+
+ //
+ // Create a local unsigned long pointer to the command.
+ //
+ pulData = (unsigned long *)g_pucCommand;
+
+ //
+ // Create the serial instance data.
+ //
+ psInst = psDevice->psPrivateData;
+
+ //
+ // Direct Access device, Removable storage and SCSI 1 responses.
+ //
+ pulData[0] = SCSI_INQ_PDT_SBC | (SCSI_INQ_RMB << 8);
+
+ //
+ // Additional Length is fixed at 31 bytes.
+ //
+ pulData[1] = 31;
+
+ //
+ // Copy the Vendor string.
+ //
+ for(lIdx = 0; lIdx < 8; lIdx++)
+ {
+ g_pucCommand[lIdx + 8] = psDevice->pucVendor[lIdx];
+ }
+
+ //
+ // Copy the Product string.
+ //
+ for(lIdx = 0; lIdx < 16; lIdx++)
+ {
+ g_pucCommand[lIdx + 16] = psDevice->pucProduct[lIdx];
+ }
+
+ //
+ // Copy the Version string.
+ //
+ for(lIdx = 0; lIdx < 4; lIdx++)
+ {
+ g_pucCommand[lIdx + 32] = psDevice->pucVersion[lIdx];
+ }
+
+ //
+ // Send the SCSI Inquiry Response.
+ //
+ MAP_USBEndpointDataPut(USB0_BASE, psInst->ucINEndpoint, g_pucCommand, 36);
+
+ //
+ // Send the data to the host.
+ //
+ MAP_USBEndpointDataSend(USB0_BASE, psInst->ucINEndpoint, USB_TRANS_IN);
+
+ //
+ // Set the status so that it can be sent when this response has
+ // has be successfully sent.
+ //
+ g_sSCSICSW.bCSWStatus = 0;
+ g_sSCSICSW.dCSWDataResidue = 0;
+
+ psInst->ucSCSIState = STATE_SCSI_SEND_STATUS;
+}
+
+//*****************************************************************************
+//
+// This function is used to handle the SCSI Read Capacities command when it is
+// received from the host.
+//
+//*****************************************************************************
+static void
+USBDSCSIReadCapacities(const tUSBDMSCDevice *psDevice)
+{
+ unsigned long ulBlocks;
+ tMSCInstance *psInst;
+ unsigned long *pulData;
+
+ //
+ // Create a local unsigned long pointer to the command.
+ //
+ pulData = (unsigned long *)g_pucCommand;
+
+ //
+ // Get our instance data pointer.
+ //
+ psInst = psDevice->psPrivateData;
+
+ if(psInst->pvMedia != 0)
+ {
+ ulBlocks = psDevice->sMediaFunctions.NumBlocks(psInst->pvMedia);
+
+ pulData[0] = 0x08000000;
+
+ //
+ // Fill in the number of blocks, the bytes endianness must be changed.
+ //
+ g_pucCommand[4] = ulBlocks >> 24;
+ g_pucCommand[5] = 0xff & (ulBlocks >> 16);
+ g_pucCommand[6] = 0xff & (ulBlocks >> 8);
+ g_pucCommand[7] = 0xff & (ulBlocks);
+
+ //
+ // Current media capacity
+ //
+ g_pucCommand[8] = 0x2;
+
+ //
+ // Fill in the block size, which is fixed at DEVICE_BLOCK_SIZE.
+ //
+ g_pucCommand[9] = 0xff & (DEVICE_BLOCK_SIZE >> 16);
+ g_pucCommand[10] = 0xff & (DEVICE_BLOCK_SIZE >> 8);
+ g_pucCommand[11] = 0xff & DEVICE_BLOCK_SIZE;
+
+ //
+ // Send out the 12 bytes that are in this response.
+ //
+ MAP_USBEndpointDataPut(USB0_BASE, psInst->ucINEndpoint, g_pucCommand,
+ 12);
+ MAP_USBEndpointDataSend(USB0_BASE, psInst->ucINEndpoint, USB_TRANS_IN);
+
+ //
+ // Set the status so that it can be sent when this response has
+ // has be successfully sent.
+ //
+ g_sSCSICSW.bCSWStatus = 0;
+ g_sSCSICSW.dCSWDataResidue = 0;
+ }
+ else
+ {
+ //
+ // Set the status so that it can be sent when this response has
+ // has be successfully sent.
+ //
+ g_sSCSICSW.bCSWStatus = 1;
+ g_sSCSICSW.dCSWDataResidue = 0;
+
+ //
+ // Stall the IN endpoint
+ //
+ MAP_USBDevEndpointStall(USB0_BASE, psInst->ucINEndpoint, USB_EP_DEV_IN);
+
+ //
+ // Mark the sense code as valid and indicate that these is no media
+ // present.
+ //
+ psInst->ucErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS;
+ psInst->ucSenseKey = SCSI_RS_KEY_NOT_READY;
+ psInst->usAddSenseCode = SCSI_RS_MED_NOT_PRSNT;
+ }
+
+ psInst->ucSCSIState = STATE_SCSI_SEND_STATUS;
+}
+
+//*****************************************************************************
+//
+// This function is used to handle the SCSI Read Capacity command when it is
+// received from the host.
+//
+//*****************************************************************************
+static void
+USBDSCSIReadCapacity(const tUSBDMSCDevice *psDevice)
+{
+ unsigned long ulBlocks;
+ tMSCInstance *psInst;
+
+ //
+ // Get our instance data pointer.
+ //
+ psInst = psDevice->psPrivateData;
+
+ ulBlocks = psDevice->sMediaFunctions.NumBlocks(psInst->pvMedia);
+
+ //
+ // Only decrement if any blocks were found.
+ //
+ if(ulBlocks != 0)
+ {
+ //
+ // One less than the maximum number is the last addressable
+ // block.
+ //
+ ulBlocks--;
+ }
+
+ if(psInst->pvMedia != 0)
+ {
+ //
+ // Fill in the number of blocks, the bytes endianness must be changed.
+ //
+ g_pucCommand[0] = 0xff & (ulBlocks >> 24);
+ g_pucCommand[1] = 0xff & (ulBlocks >> 16);
+ g_pucCommand[2] = 0xff & (ulBlocks >> 8);
+ g_pucCommand[3] = 0xff & (ulBlocks);
+
+ g_pucCommand[4] = 0;
+
+ //
+ // Fill in the block size, which is fixed at DEVICE_BLOCK_SIZE.
+ //
+ g_pucCommand[5] = 0xff & (DEVICE_BLOCK_SIZE >> 16);
+ g_pucCommand[6] = 0xff & (DEVICE_BLOCK_SIZE >> 8);
+ g_pucCommand[7] = 0xff & DEVICE_BLOCK_SIZE;
+
+ //
+ // Send the SCSI Inquiry Response.
+ //
+ MAP_USBEndpointDataPut(USB0_BASE, psInst->ucINEndpoint, g_pucCommand,
+ 8);
+ MAP_USBEndpointDataSend(USB0_BASE, psInst->ucINEndpoint, USB_TRANS_IN);
+
+ //
+ // Set the status so that it can be sent when this response has
+ // has be successfully sent.
+ //
+ g_sSCSICSW.bCSWStatus = 0;
+ g_sSCSICSW.dCSWDataResidue = 0;
+ }
+ else
+ {
+ //
+ // Set the status so that it can be sent when this response has
+ // has be successfully sent.
+ //
+ g_sSCSICSW.bCSWStatus = 1;
+ g_sSCSICSW.dCSWDataResidue = 0;
+
+ //
+ // Stall the IN endpoint
+ //
+ MAP_USBDevEndpointStall(USB0_BASE, psInst->ucINEndpoint, USB_EP_DEV_IN);
+
+ //
+ // Mark the sense code as valid and indicate that these is no media
+ // present.
+ //
+ psInst->ucErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS;
+ psInst->ucSenseKey = SCSI_RS_KEY_NOT_READY;
+ psInst->usAddSenseCode = SCSI_RS_MED_NOT_PRSNT;
+ }
+
+ psInst->ucSCSIState = STATE_SCSI_SEND_STATUS;
+}
+
+//*****************************************************************************
+//
+// This function is used to handle the SCSI Request Sense command when it is
+// received from the host.
+//
+//*****************************************************************************
+static void
+USBDSCSIRequestSense(const tUSBDMSCDevice *psDevice)
+{
+ tMSCInstance *psInst;
+
+ //
+ // Get our instance data pointer.
+ //
+ psInst = psDevice->psPrivateData;
+
+ //
+ // The request sense response.
+ //
+ g_pucCommand[0] = psInst->ucErrorCode;
+ g_pucCommand[1] = 0;
+ g_pucCommand[2] = psInst->ucSenseKey;
+ *(unsigned long *)&g_pucCommand[3] = 0;
+
+ //
+ // There are 10 more bytes of data.
+ //
+ g_pucCommand[7] = 10;
+
+ *(unsigned long *)&g_pucCommand[8] = 0;
+
+ //
+ // Transition from not ready to ready.
+ //
+ *(unsigned short *)&g_pucCommand[12] = psInst->usAddSenseCode;
+ *(unsigned long *)&g_pucCommand[14] = 0;
+
+ //
+ // Send the SCSI Inquiry Response.
+ //
+ MAP_USBEndpointDataPut(USB0_BASE, psInst->ucINEndpoint, g_pucCommand, 18);
+ MAP_USBEndpointDataSend(USB0_BASE, psInst->ucINEndpoint, USB_TRANS_IN);
+
+ //
+ // Reset the valid flag on errors.
+ //
+ psInst->ucErrorCode = SCSI_RS_CUR_ERRORS;
+
+ //
+ // Set the status so that it can be sent when this response has
+ // has be successfully sent.
+ //
+ g_sSCSICSW.bCSWStatus = 0;
+ g_sSCSICSW.dCSWDataResidue = 0;
+
+ //
+ // Move on to the status phase.
+ //
+ psInst->ucSCSIState = STATE_SCSI_SEND_STATUS;
+}
+
+//*****************************************************************************
+//
+// This function is used to handle the SCSI Read 10 command when it is
+// received from the host.
+//
+//*****************************************************************************
+static void
+USBDSCSIRead10(const tUSBDMSCDevice *psDevice, tMSCCBW *pSCSICBW)
+{
+ unsigned short usNumBlocks;
+ tMSCInstance *psInst;
+
+ //
+ // Default the number of blocks.
+ //
+ usNumBlocks = 0;
+
+ //
+ // Get our instance data pointer.
+ //
+ psInst = psDevice->psPrivateData;
+
+ if(psInst->pvMedia != 0)
+ {
+ //
+ // Get the logical block from the CBW structure. This switching
+ // is required to convert from big to little endian.
+ //
+ psInst->ulCurrentLBA = (pSCSICBW->CBWCB[2] << 24) |
+ (pSCSICBW->CBWCB[3] << 16) |
+ (pSCSICBW->CBWCB[4] << 8) |
+ (pSCSICBW->CBWCB[5] << 0);
+
+ //
+ // More bytes to read.
+ //
+ usNumBlocks = (pSCSICBW->CBWCB[7] << 8) | pSCSICBW->CBWCB[8];
+
+ //
+ // Read the next logical block from the storage device.
+ //
+ if(psDevice->sMediaFunctions.BlockRead(psInst->pvMedia,
+ (unsigned char *)psInst->pulBuffer,
+ psInst->ulCurrentLBA, 1) == 0)
+ {
+ psInst->pvMedia = 0;
+ psDevice->sMediaFunctions.Close(0);
+ }
+ }
+
+ //
+ // If there is media present then start transferring the data.
+ //
+ if(psInst->pvMedia != 0)
+ {
+ //
+ // Enable DMA on the endpoint
+ //
+ MAP_USBEndpointDMAEnable(USB0_BASE, psInst->ucINEndpoint,
+ USB_EP_DEV_IN);
+
+ //
+ // Configure and DMA for the IN transfer.
+ //
+ MAP_uDMAChannelTransferSet(psInst->ucINDMA,
+ UDMA_MODE_BASIC,
+ psInst->pulBuffer,
+ (void *)USBFIFOAddrGet(USB0_BASE,
+ psInst->ucINEndpoint),
+ (MAX_TRANSFER_SIZE >> 2));
+
+ //
+ // Remember that a DMA is in progress.
+ //
+ psInst->ulFlags |= USBD_FLAG_DMA_IN;
+
+ //
+ // Schedule the remaining bytes to send.
+ //
+ psInst->ulBytesToTransfer = (DEVICE_BLOCK_SIZE * usNumBlocks);
+
+ //
+ // Start the DMA transfer.
+ //
+ MAP_uDMAChannelEnable(psInst->ucINDMA);
+
+ //
+ // Move on and start sending blocks.
+ //
+ psInst->ucSCSIState = STATE_SCSI_SEND_BLOCKS;
+
+ if(psDevice->pfnEventCallback)
+ {
+ psDevice->pfnEventCallback(0, USBD_MSC_EVENT_READING, 0, 0);
+ }
+ }
+ else
+ {
+ //
+ // Set the status so that it can be sent when this response has
+ // has be successfully sent.
+ //
+ g_sSCSICSW.bCSWStatus = 1;
+ g_sSCSICSW.dCSWDataResidue = 0;
+
+ //
+ // Stall the IN endpoint
+ //
+ MAP_USBDevEndpointStall(USB0_BASE, psInst->ucINEndpoint, USB_EP_DEV_IN);
+
+ //
+ // Mark the sense code as valid and indicate that these is no media
+ // present.
+ //
+ psInst->ucErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS;
+ psInst->ucSenseKey = SCSI_RS_KEY_NOT_READY;
+ psInst->usAddSenseCode = SCSI_RS_MED_NOT_PRSNT;
+
+ psInst->ucSCSIState = STATE_SCSI_SEND_STATUS;
+ }
+}
+
+//*****************************************************************************
+//
+// This function is used to handle the SCSI Read 10 command when it is
+// received from the host.
+//
+//*****************************************************************************
+static void
+USBDSCSIWrite10(const tUSBDMSCDevice *psDevice, tMSCCBW *pSCSICBW)
+{
+ unsigned short usNumBlocks;
+ tMSCInstance *psInst;
+
+ //
+ // Get our instance data pointer.
+ //
+ psInst = psDevice->psPrivateData;
+
+ //
+ // If there is media present then start transferring the data.
+ //
+ if(psInst->pvMedia != 0)
+ {
+ //
+ // Get the logical block from the CBW structure. This switching
+ // is required to convert from big to little endian.
+ //
+ psInst->ulCurrentLBA = (pSCSICBW->CBWCB[2] << 24) |
+ (pSCSICBW->CBWCB[3] << 16) |
+ (pSCSICBW->CBWCB[4] << 8) |
+ (pSCSICBW->CBWCB[5] << 0);
+
+ //
+ // More bytes to read.
+ //
+ usNumBlocks = (pSCSICBW->CBWCB[7] << 8) | pSCSICBW->CBWCB[8];
+
+ psInst->ulBytesToTransfer = DEVICE_BLOCK_SIZE * usNumBlocks;
+
+ //
+ // Start sending logical blocks, these are always multiples of
+ // DEVICE_BLOCK_SIZE bytes.
+ //
+ psInst->ucSCSIState = STATE_SCSI_RECEIVE_BLOCKS;
+
+ //
+ // Enable uDMA on the endpoint
+ //
+ MAP_USBEndpointDMAEnable(USB0_BASE, psInst->ucOUTEndpoint,
+ USB_EP_DEV_OUT);
+
+ //
+ // Configure the DMA for the OUT transfer.
+ //
+ MAP_uDMAChannelTransferSet(psInst->ucOUTDMA,
+ UDMA_MODE_BASIC,
+ (void *)USBFIFOAddrGet(USB0_BASE,
+ psInst->ucOUTEndpoint),
+ psInst->pulBuffer,
+ (MAX_TRANSFER_SIZE >> 2));
+
+ //
+ // Remember that a DMA is in progress.
+ //
+ psInst->ulFlags |= USBD_FLAG_DMA_OUT;
+
+ //
+ // Enable the OUT DMA transfer.
+ //
+ MAP_uDMAChannelEnable(psInst->ucOUTDMA);
+
+ //
+ // Notify the application of the write event.
+ //
+ if(psDevice->pfnEventCallback)
+ {
+ psDevice->pfnEventCallback(0, USBD_MSC_EVENT_WRITING, 0, 0);
+ }
+ }
+ else
+ {
+ //
+ // Set the status so that it can be sent when this response has
+ // has be successfully sent.
+ //
+ g_sSCSICSW.bCSWStatus = 1;
+ g_sSCSICSW.dCSWDataResidue = 0;
+
+ //
+ // Stall the IN endpoint
+ //
+ MAP_USBDevEndpointStall(USB0_BASE, psInst->ucOUTEndpoint,
+ USB_EP_DEV_OUT);
+
+ //
+ // Mark the sense code as valid and indicate that these is no media
+ // present.
+ //
+ psInst->ucErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS;
+ psInst->ucSenseKey = SCSI_RS_KEY_NOT_READY;
+ psInst->usAddSenseCode = SCSI_RS_MED_NOT_PRSNT;
+
+ psInst->ucSCSIState = STATE_SCSI_SEND_STATUS;
+ }
+}
+
+//*****************************************************************************
+//
+// This function is used to handle the SCSI Mode Sense 6 command when it is
+// received from the host.
+//
+//*****************************************************************************
+static void
+USBDSCSIModeSense6(const tUSBDMSCDevice *psDevice, tMSCCBW *pSCSICBW)
+{
+ tMSCInstance *psInst;
+
+ //
+ // Get our instance data pointer.
+ //
+ psInst = psDevice->psPrivateData;
+
+ //
+ // If there is media present send the response.
+ //
+ if(psInst->pvMedia != 0)
+ {
+ //
+ // Three extra bytes in this response.
+ //
+ g_pucCommand[0] = 3;
+ g_pucCommand[1] = 0;
+ g_pucCommand[2] = 0;
+ g_pucCommand[3] = 0;
+
+ //
+ // Manually send the response back to the host.
+ //
+ MAP_USBEndpointDataPut(USB0_BASE, psInst->ucINEndpoint, g_pucCommand,
+ 4);
+ MAP_USBEndpointDataSend(USB0_BASE, psInst->ucINEndpoint, USB_TRANS_IN);
+
+ //
+ // Set the status so that it can be sent when this response has
+ // has be successfully sent.
+ //
+ g_sSCSICSW.bCSWStatus = 0;
+ g_sSCSICSW.dCSWDataResidue = pSCSICBW->dCBWDataTransferLength - 4;
+ }
+ else
+ {
+ //
+ // Set the status so that it can be sent when this response has
+ // has be successfully sent.
+ //
+ g_sSCSICSW.bCSWStatus = 1;
+ g_sSCSICSW.dCSWDataResidue = 0;
+
+ //
+ // Stall the IN endpoint
+ //
+ MAP_USBDevEndpointStall(USB0_BASE, psInst->ucINEndpoint, USB_EP_DEV_IN);
+
+ //
+ // Mark the sense code as valid and indicate that these is no media
+ // present.
+ //
+ psInst->ucErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS;
+ psInst->ucSenseKey = SCSI_RS_KEY_NOT_READY;
+ psInst->usAddSenseCode = SCSI_RS_MED_NOT_PRSNT;
+ }
+
+ psInst->ucSCSIState = STATE_SCSI_SEND_STATUS;
+}
+
+//*****************************************************************************
+//
+// This function is used to send out the response data based on the current
+// status of the mass storage class.
+//
+//*****************************************************************************
+static void
+USBDSCSISendStatus(const tUSBDMSCDevice *psDevice)
+{
+ tMSCInstance *psInst;
+
+ //
+ // Get our instance data pointer.
+ //
+ psInst = psDevice->psPrivateData;
+
+ //
+ // Respond with the requested status.
+ //
+ MAP_USBEndpointDataPut(USB0_BASE, psInst->ucINEndpoint,
+ (unsigned char *)&g_sSCSICSW, 13);
+ MAP_USBEndpointDataSend(USB0_BASE, psInst->ucINEndpoint, USB_TRANS_IN);
+
+ //
+ // Move the state to status sent so that the next interrupt will move the
+ // statue to idle.
+ //
+ psInst->ucSCSIState = STATE_SCSI_SENT_STATUS;
+}
+
+//*****************************************************************************
+//
+// This function is used to handle all SCSI commands.
+//
+//*****************************************************************************
+unsigned long
+USBDSCSICommand(const tUSBDMSCDevice *psDevice, tMSCCBW *pSCSICBW)
+{
+ unsigned long ulRetCode;
+ unsigned long ulTransferLength;
+ tMSCInstance *psInst;
+
+ //
+ // Get our instance data pointer.
+ //
+ psInst = psDevice->psPrivateData;
+
+ //
+ // Initialize the return code.
+ //
+ ulRetCode = 1;
+
+ //
+ // Save the transfer length because it may be overwritten by some calls.
+ //
+ ulTransferLength = pSCSICBW->dCBWDataTransferLength;
+
+ switch(pSCSICBW->CBWCB[0])
+ {
+ //
+ // Respond to the SCSI Inquiry command.
+ //
+ case SCSI_INQUIRY_CMD:
+ {
+ USBDSCSIInquiry(psDevice);
+
+ break;
+ }
+
+ //
+ // Respond to the test unit ready command.
+ //
+ case SCSI_TEST_UNIT_READY:
+ {
+ g_sSCSICSW.dCSWDataResidue = 0;
+
+ if(psInst->pvMedia != 0)
+ {
+ //
+ // Set the status to success for now, this could be different
+ // if there is no media present.
+ //
+ g_sSCSICSW.bCSWStatus = 0;
+ }
+ else
+ {
+ //
+ // Since there was no media, check for media here.
+ //
+ psInst->pvMedia = psDevice->sMediaFunctions.Open(0);
+
+ //
+ // If it is still not present then fail this command.
+ //
+ if(psInst->pvMedia != 0)
+ {
+ g_sSCSICSW.bCSWStatus = 0;
+ }
+ else
+ {
+ g_sSCSICSW.bCSWStatus = 1;
+ }
+ }
+ break;
+ }
+
+ //
+ // Handle the Read Capacities command.
+ //
+ case SCSI_READ_CAPACITIES:
+ {
+ USBDSCSIReadCapacities(psDevice);
+
+ break;
+ }
+
+ //
+ // Handle the Read Capacity command.
+ //
+ case SCSI_READ_CAPACITY:
+ {
+ USBDSCSIReadCapacity(psDevice);
+
+ break;
+ }
+
+ //
+ // Handle the Request Sense command.
+ //
+ case SCSI_REQUEST_SENSE:
+ {
+ USBDSCSIRequestSense(psDevice);
+
+ break;
+ }
+
+ //
+ // Handle the Read 10 command.
+ //
+ case SCSI_READ_10:
+ {
+ USBDSCSIRead10(psDevice, pSCSICBW);
+
+ break;
+ }
+
+ //
+ // Handle the Write 10 command.
+ //
+ case SCSI_WRITE_10:
+ {
+ USBDSCSIWrite10(psDevice, pSCSICBW);
+
+ break;
+ }
+
+ //
+ // Handle the Mode Sense 6 command.
+ //
+ case SCSI_MODE_SENSE_6:
+ {
+ USBDSCSIModeSense6(psDevice, pSCSICBW);
+
+ break;
+ }
+ default:
+ {
+ //
+ // Set the status so that it can be sent when this response has
+ // has be successfully sent.
+ //
+ g_sSCSICSW.bCSWStatus = 1;
+ g_sSCSICSW.dCSWDataResidue = pSCSICBW->dCBWDataTransferLength;
+
+ //
+ // If there is data then there is more work to do.
+ //
+ if(pSCSICBW->dCBWDataTransferLength != 0)
+ {
+ if(pSCSICBW->bmCBWFlags & CBWFLAGS_DIR_IN)
+ {
+ //
+ // Stall the IN endpoint
+ //
+ MAP_USBDevEndpointStall(USB0_BASE, psInst->ucINEndpoint,
+ USB_EP_DEV_IN);
+ }
+ else
+ {
+ //
+ // Stall the OUT endpoint
+ //
+ MAP_USBDevEndpointStall(USB0_BASE, psInst->ucOUTEndpoint,
+ USB_EP_DEV_OUT);
+
+ }
+ //
+ // Send the status once the stall occurs.
+ //
+ psInst->ucSCSIState = STATE_SCSI_SEND_STATUS;
+ }
+
+ //
+ // Set the sense codes.
+ //
+ psInst->ucErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS;
+ psInst->ucSenseKey = SCSI_RS_KEY_ILGL_RQST;
+ psInst->usAddSenseCode = SCSI_RS_PV_INVALID;
+
+ break;
+ }
+ }
+
+ //
+ // If there is no data then send out the current status.
+ //
+ if(ulTransferLength == 0)
+ {
+ USBDSCSISendStatus(psDevice);
+ }
+ return(ulRetCode);
+}
+
+//*****************************************************************************
+//
+// Close the Doxygen group.
+//! @}
+//
+//*****************************************************************************