summaryrefslogtreecommitdiff
path: root/usblib/device/usbdmsc.c
diff options
context:
space:
mode:
authorYuval Adam <yuv.adm@gmail.com>2014-03-16 14:41:11 +0200
committerYuval Adam <yuv.adm@gmail.com>2014-03-16 14:41:11 +0200
commit990090a4cc9070837d31e66b58d40f0c3d038741 (patch)
treecf1b905082c364e9b223e0c5058566103138dae5 /usblib/device/usbdmsc.c
parent7f4da522479c0f00126219f0c23b804c3a93d7a6 (diff)
Add usblib and utils
Diffstat (limited to 'usblib/device/usbdmsc.c')
-rw-r--r--usblib/device/usbdmsc.c2441
1 files changed, 2441 insertions, 0 deletions
diff --git a/usblib/device/usbdmsc.c b/usblib/device/usbdmsc.c
new file mode 100644
index 0000000..daea1f0
--- /dev/null
+++ b/usblib/device/usbdmsc.c
@@ -0,0 +1,2441 @@
+//*****************************************************************************
+//
+// usbdmsc.c - USB mass storage device class driver.
+//
+// Copyright (c) 2009-2014 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 2.1.0.12573 of the Tiva USB Library.
+//
+//*****************************************************************************
+
+#include <stdbool.h>
+#include <stdint.h>
+#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 "usblib/usblib.h"
+#include "usblib/usblibpriv.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 ui32Flags member variable.
+//
+//*****************************************************************************
+#define USBD_FLAG_DMA_IN 0x00000001
+#define USBD_FLAG_DMA_OUT 0x00000002
+#define USBD_FLAG_ALLOW_REMOVAL 0x00000004
+
+//*****************************************************************************
+//
+// 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_OUT_ENDPOINT USB_EP_1
+
+//*****************************************************************************
+//
+// 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 size of USB transfers for commands.
+//
+//*****************************************************************************
+#define COMMAND_BUFFER_SIZE 64
+
+//*****************************************************************************
+//
+// The block size of a device. It defaults to DEVICE_BLOCK_SIZE
+//
+//*****************************************************************************
+static uint32_t g_pui32BlockSize = DEVICE_BLOCK_SIZE;
+
+//*****************************************************************************
+//
+// The local buffer used to read in commands and process them.
+//
+//*****************************************************************************
+static uint8_t g_pui8Command[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.ui8SCSIState 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 uint8_t g_pui8MSCDeviceDescriptor[] =
+{
+ 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 uint8_t g_pui8MSCDescriptor[] =
+{
+ //
+ // 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 uint8_t g_pui8MSCInterface[MSCINTERFACE_SIZE] =
+{
+ //
+ // 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 | USBEPToIndex(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 | USBEPToIndex(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_pui8MSCDescriptor),
+ g_pui8MSCDescriptor
+};
+
+const tConfigSection g_sMSCInterfaceSection =
+{
+ sizeof(g_pui8MSCInterface),
+ g_pui8MSCInterface
+};
+
+//*****************************************************************************
+//
+// 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(g_psMSCSections[0]))
+
+//*****************************************************************************
+//
+// 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_ppsMSCConfigDescriptors[] =
+{
+ &g_sMSCConfigHeader
+};
+
+//*****************************************************************************
+//
+// Various internal handlers needed by this class.
+//
+//*****************************************************************************
+static void HandleDisconnect(void *pvMSCDevice);
+static void ConfigChangeHandler(void *pvMSCDevice, uint32_t ui32Value);
+static void HandleEndpoints(void *pvMSCDevice, uint32_t ui32Status);
+static void HandleRequests(void *pvMSCDevice, tUSBRequest *psUSBRequest);
+static void USBDSCSISendStatus(tUSBDMSCDevice *psMSCDevice);
+uint32_t USBDSCSICommand(tUSBDMSCDevice *psMSCDevice, tMSCCBW *psSCSICBW);
+static void HandleDevice(void *pvMSCDevice, uint32_t ui32Request,
+ void *pvRequestData);
+
+//*****************************************************************************
+//
+// The device information structure for the USB MSC device.
+//
+//*****************************************************************************
+const tCustomHandlers g_sMSCHandlers =
+{
+ //
+ // 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
+};
+
+//*****************************************************************************
+//
+//! This function is used by an application if it can detect insertion or
+//! removal of the media.
+//!
+//! \param pvMSCDevice is the mass storage device instance that had a media
+//! change.
+//! \param iMediaStatus 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 iMediaStatus 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 iMediaStatus
+//! parameter:
+//! - \b eUSBDMSCMediaPresent indicates that the media is present or has been
+//! added.
+//! - \b eUSBDMSCMediaNotPresent indicates that the media is not present or was
+//! removed.
+//! - \b eUSBDMSCMediaUnknown 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 \b eUSBDMSCMediaUnknown 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 *pvMSCDevice, tUSBDMSCMediaStatus iMediaStatus)
+{
+ tUSBDMSCDevice *psMSCDevice;
+
+ //
+ // Create a device instance pointer.
+ //
+ psMSCDevice = pvMSCDevice;
+
+ //
+ // Save the current media status.
+ //
+ psMSCDevice->sPrivateData.iMediaStatus = iMediaStatus;
+}
+
+//*****************************************************************************
+//
+// This function is called to handle the interrupts on the Bulk endpoints for
+// the mass storage class.
+//
+//*****************************************************************************
+static void
+HandleEndpoints(void *pvMSCDevice, uint32_t ui32Status)
+{
+ tUSBDMSCDevice *psMSCDevice;
+ tMSCInstance *psInst;
+ tMSCCBW *psSCSICBW;
+ uint32_t ui32EPStatus, ui32Size;
+
+ ASSERT(pvMSCDevice != 0);
+
+ //
+ // Determine if the serial device is in single or composite mode because
+ // the meaning of ui32Index is different in both cases.
+ //
+ psMSCDevice = pvMSCDevice;
+
+ //
+ // Initialize the workspace in the passed instance structure.
+ //
+ psInst = &psMSCDevice->sPrivateData;
+
+ //
+ // Get the endpoints status.
+ //
+ ui32EPStatus = MAP_USBEndpointStatus(USB0_BASE, psInst->ui8OUTEndpoint);
+
+ //
+ // Handler for the bulk IN data endpoint.
+ //
+ if((ui32Status & (1 << USBEPToIndex(psInst->ui8INEndpoint))) ||
+ ((psInst->ui32Flags & USBD_FLAG_DMA_IN) &&
+ (USBLibDMAChannelStatus(psInst->psDMAInstance, psInst->ui8INDMA) &
+ USBLIBSTATUS_DMA_COMPLETE)))
+ {
+ switch(psInst->ui8SCSIState)
+ {
+ //
+ // 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->ui32BytesToTransfer -= g_pui32BlockSize;
+
+ //
+ // If we are done then move on to the status phase.
+ //
+ if(psInst->ui32BytesToTransfer == 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->ui32Flags &= ~USBD_FLAG_DMA_IN;
+
+ //
+ // Disable uDMA on the endpoint
+ //
+ MAP_USBEndpointDMADisable(USB0_BASE, psInst->ui8INEndpoint,
+ USB_EP_DEV_IN);
+
+ if(psMSCDevice->pfnEventCallback)
+ {
+ psMSCDevice->pfnEventCallback(0, USBD_MSC_EVENT_IDLE,
+ 0, 0);
+ }
+
+ //
+ // Make sure that the transfer has actually finished. If
+ // it has not there will be another interrupt to send
+ // out the status.
+ //
+ if(USBEndpointStatus(USB0_BASE,psInst->ui8INEndpoint) &
+ USB_DEV_TX_TXPKTRDY)
+ {
+ //
+ // Send back the status once this transfer is complete.
+ //
+ psInst->ui8SCSIState = STATE_SCSI_SEND_STATUS;
+ }
+ else
+ {
+ //
+ // Indicate success and no extra data coming.
+ //
+ USBDSCSISendStatus(psMSCDevice);
+ }
+
+ //
+ // The transfer is complete so don't read anymore data.
+ //
+ break;
+ }
+
+ //
+ // Move on to the next Logical Block.
+ //
+ psInst->ui32CurrentLBA++;
+
+ //
+ // Read the new data and send it out.
+ //
+ if(psMSCDevice->sMediaFunctions.pfnBlockRead(psInst->pvMedia,
+ (uint8_t *)psInst->pui32Buffer,
+ psInst->ui32CurrentLBA, 1) == 0)
+ {
+ }
+
+ //
+ // Configure and enable DMA for the IN transfer.
+ //
+ USBLibDMATransfer(psInst->psDMAInstance,
+ psInst->ui8INDMA, psInst->pui32Buffer,
+ g_pui32BlockSize);
+
+ //
+ // Start the DMA transfer.
+ //
+ USBLibDMAChannelEnable(psInst->psDMAInstance,
+ psInst->ui8INDMA);
+
+ break;
+ }
+
+ //
+ // Handle sending status.
+ //
+ case STATE_SCSI_SEND_STATUS:
+ {
+ //
+ // Indicate success and no extra data coming.
+ //
+ USBDSCSISendStatus(psMSCDevice);
+
+ break;
+ }
+
+ //
+ // Handle completing sending status.
+ //
+ case STATE_SCSI_SENT_STATUS:
+ {
+ psInst->ui8SCSIState = 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((ui32Status & (0x10000 << USBEPToIndex(psInst->ui8OUTEndpoint))) ||
+ ((psInst->ui32Flags & USBD_FLAG_DMA_OUT) &&
+ (USBLibDMAChannelStatus(psInst->psDMAInstance, psInst->ui8OUTDMA) &
+ USBLIBSTATUS_DMA_COMPLETE)))
+ {
+ //
+ // Get the endpoint status to see why we were called.
+ //
+ ui32EPStatus = MAP_USBEndpointStatus(USB0_BASE,
+ psInst->ui8OUTEndpoint);
+
+ switch(psInst->ui8SCSIState)
+ {
+ //
+ // Receiving and writing bytes to the storage device.
+ //
+ case STATE_SCSI_RECEIVE_BLOCKS:
+ {
+ //
+ // Update the current status for the buffer.
+ //
+ psInst->ui32BytesToTransfer -= g_pui32BlockSize;
+
+ //
+ // Write the new data.
+ //
+ psMSCDevice->sMediaFunctions.pfnBlockWrite(psInst->pvMedia,
+ (uint8_t *)psInst->pui32Buffer,
+ psInst->ui32CurrentLBA, 1);
+
+ //
+ // Move on to the next Logical Block.
+ //
+ psInst->ui32CurrentLBA++;
+
+ //
+ // Check if all bytes have been received.
+ //
+ if(psInst->ui32BytesToTransfer == 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->ui32Flags &= ~USBD_FLAG_DMA_OUT;
+
+ //
+ // Indicate success and no extra data coming.
+ //
+ USBDSCSISendStatus(psMSCDevice);
+
+ //
+ // Disable uDMA on the endpoint
+ //
+ MAP_USBEndpointDMADisable(USB0_BASE,
+ psInst->ui8OUTEndpoint,
+ USB_EP_DEV_OUT);
+
+ //
+ // If there is an event callback then call it to notify
+ // that last operation has completed.
+ //
+ if(psMSCDevice->pfnEventCallback)
+ {
+ psMSCDevice->pfnEventCallback(0, USBD_MSC_EVENT_IDLE,
+ 0, 0);
+ }
+ }
+ else
+ {
+ //
+ // Configure and enable DMA for the OUT transfer.
+ //
+ USBLibDMATransfer(psInst->psDMAInstance,
+ psInst->ui8OUTDMA, psInst->pui32Buffer,
+ g_pui32BlockSize);
+ }
+
+ 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.
+ //
+ ui32Size = COMMAND_BUFFER_SIZE;
+ MAP_USBEndpointDataGet(psInst->ui32USBBase,
+ psInst->ui8OUTEndpoint,
+ g_pui8Command, &ui32Size);
+ psSCSICBW = (tMSCCBW *)g_pui8Command;
+
+ //
+ // Acknowledge the OUT data packet.
+ //
+ MAP_USBDevEndpointDataAck(psInst->ui32USBBase,
+ psInst->ui8OUTEndpoint, false);
+
+ //
+ // If this is a valid CBW then handle it.
+ //
+ if(psSCSICBW->dCBWSignature == CBW_SIGNATURE)
+ {
+ g_sSCSICSW.dCSWSignature = CSW_SIGNATURE;
+ g_sSCSICSW.dCSWTag = psSCSICBW->dCBWTag;
+ g_sSCSICSW.dCSWDataResidue = 0;
+ g_sSCSICSW.bCSWStatus = 0;
+
+ USBDSCSICommand(psMSCDevice, psSCSICBW);
+ }
+ 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->ui8SCSIState = STATE_SCSI_IDLE;
+ }
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ //
+ // Clear the status bits.
+ //
+ MAP_USBDevEndpointStatusClear(USB0_BASE, psInst->ui8OUTEndpoint,
+ ui32EPStatus);
+ }
+}
+
+//*****************************************************************************
+//
+// Device instance specific handler.
+//
+//*****************************************************************************
+static void
+HandleDevice(void *pvMSCDevice, uint32_t ui32Request, void *pvRequestData)
+{
+ tMSCInstance *psInst;
+ uint8_t *pui8Data;
+ tUSBDMSCDevice *psMSCDevice;
+
+ psMSCDevice = (tUSBDMSCDevice *)pvMSCDevice;
+
+ //
+ // Get the instance data pointers.
+ //
+ psInst = &((tUSBDMSCDevice *)pvMSCDevice)->sPrivateData;
+
+ //
+ // Create the 8-bit array used by the events supported by the USB MSC
+ // class.
+ //
+ pui8Data = (uint8_t *)pvRequestData;
+
+ switch(ui32Request)
+ {
+ //
+ // This was an interface change event.
+ //
+ case USB_EVENT_COMP_IFACE_CHANGE:
+ {
+ psInst->ui8Interface = pui8Data[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(pui8Data[0] & USB_EP_DESC_IN)
+ {
+ psInst->ui8INEndpoint = IndexToUSBEP((pui8Data[1] & 0x7f));
+
+ //
+ // If the DMA channel has already been allocated then clear
+ // that channel and prepare to possibly use a new one.
+ //
+ if(psInst->ui8INDMA != 0)
+ {
+ USBLibDMAChannelRelease(psInst->psDMAInstance,
+ psInst->ui8INDMA);
+ }
+
+ //
+ // Allocate a DMA channel to the endpoint.
+ //
+ psInst->ui8INDMA =
+ USBLibDMAChannelAllocate(psInst->psDMAInstance,
+ psInst->ui8INEndpoint, 0,
+ USB_DMA_EP_TX |
+ USB_DMA_EP_DEVICE);
+
+ //
+ // Set the DMA individual transfer size.
+ //
+ USBLibDMAUnitSizeSet(psInst->psDMAInstance, psInst->ui8INDMA,
+ 32);
+
+ //
+ // Set the DMA arbitration size.
+ //
+ USBLibDMAArbSizeSet(psInst->psDMAInstance, psInst->ui8INDMA,
+ 16);
+ }
+ else
+ {
+ //
+ // If the DMA channel has already been allocated then clear
+ // that channel and prepare to possibly use a new one.
+ //
+ if(psInst->ui8OUTDMA != 0)
+ {
+ USBLibDMAChannelRelease(psInst->psDMAInstance,
+ psInst->ui8OUTDMA);
+ }
+
+ //
+ // Allocate a DMA channel to the endpoint.
+ //
+ psInst->ui8OUTDMA =
+ USBLibDMAChannelAllocate(psInst->psDMAInstance,
+ psInst->ui8OUTEndpoint, 0,
+ USB_DMA_EP_RX |
+ USB_DMA_EP_DEVICE);
+
+ //
+ // Set the DMA individual transfer size.
+ //
+ USBLibDMAUnitSizeSet(psInst->psDMAInstance, psInst->ui8OUTDMA,
+ 32);
+
+ //
+ // Set the DMA arbitration size.
+ //
+ USBLibDMAArbSizeSet(psInst->psDMAInstance, psInst->ui8OUTDMA,
+ 16);
+ }
+ break;
+ }
+ case USB_EVENT_LPM_RESUME:
+ {
+ if(psMSCDevice->pfnEventCallback)
+ {
+ //
+ // Pass the LPM resume event to the client.
+ //
+ psMSCDevice->pfnEventCallback(0, USB_EVENT_LPM_RESUME, 0,
+ (void *)0);
+ }
+ break;
+ }
+ case USB_EVENT_LPM_SLEEP:
+ {
+ if(psMSCDevice->pfnEventCallback)
+ {
+ //
+ // Pass the LPM sleep event to the client.
+ //
+ psMSCDevice->pfnEventCallback(0, USB_EVENT_LPM_RESUME, 0,
+ (void *)0);
+ }
+ break;
+ }
+ case USB_EVENT_LPM_ERROR:
+ {
+ if(psMSCDevice->pfnEventCallback)
+ {
+ //
+ // Pass the LPM error event to the client.
+ //
+ psMSCDevice->pfnEventCallback(0, USB_EVENT_LPM_RESUME, 0,
+ (void *)0);
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+//*****************************************************************************
+//
+// This function is called by the USB device stack whenever the device is
+// disconnected from the host.
+//
+//*****************************************************************************
+static void
+HandleDisconnect(void *pvMSCDevice)
+{
+ tUSBDMSCDevice *psMSCDevice;
+
+ ASSERT(pvMSCDevice != 0);
+
+ //
+ // Create the instance pointer.
+ //
+ psMSCDevice = (tUSBDMSCDevice *)pvMSCDevice;
+
+ //
+ // Close the drive requested.
+ //
+ if(psMSCDevice->sPrivateData.pvMedia != 0)
+ {
+ psMSCDevice->sPrivateData.pvMedia = 0;
+ psMSCDevice->sMediaFunctions.pfnClose(0);
+ }
+
+ //
+ // If we have a control callback, let the client know we are open for
+ // business.
+ //
+ if(psMSCDevice->pfnEventCallback)
+ {
+ //
+ // Pass the connected event to the client.
+ //
+ psMSCDevice->pfnEventCallback(pvMSCDevice, USB_EVENT_DISCONNECTED, 0,
+ 0);
+ }
+}
+
+//*****************************************************************************
+//
+// This function is called by the USB device stack whenever the device
+// configuration changes.
+//
+//*****************************************************************************
+static void
+ConfigChangeHandler(void *pvMSCDevice, uint32_t ui32Value)
+{
+ tUSBDMSCDevice *psMSCDevice;
+
+ ASSERT(pvMSCDevice != 0);
+
+ //
+ // Create the instance pointer.
+ //
+ psMSCDevice = (tUSBDMSCDevice *)pvMSCDevice;
+
+ //
+ // If the DMA channel has already been allocated then clear
+ // that channel and prepare to possibly use a new one.
+ //
+ if(psMSCDevice->sPrivateData.ui8OUTDMA != 0)
+ {
+ USBLibDMAChannelRelease(psMSCDevice->sPrivateData.psDMAInstance,
+ psMSCDevice->sPrivateData.ui8OUTDMA);
+ }
+
+ //
+ // Configure the DMA for the OUT endpoint.
+ //
+ psMSCDevice->sPrivateData.ui8OUTDMA =
+ USBLibDMAChannelAllocate(psMSCDevice->sPrivateData.psDMAInstance,
+ psMSCDevice->sPrivateData.ui8OUTEndpoint, 64,
+ USB_DMA_EP_RX | USB_DMA_EP_DEVICE);
+
+ USBLibDMAUnitSizeSet(psMSCDevice->sPrivateData.psDMAInstance,
+ psMSCDevice->sPrivateData.ui8OUTDMA, 32);
+
+ USBLibDMAArbSizeSet(psMSCDevice->sPrivateData.psDMAInstance,
+ psMSCDevice->sPrivateData.ui8OUTDMA, 16);
+
+ //
+ // If the DMA channel has already been allocated then clear
+ // that channel and prepare to possibly use a new one.
+ //
+ if(psMSCDevice->sPrivateData.ui8INDMA != 0)
+ {
+ USBLibDMAChannelRelease(psMSCDevice->sPrivateData.psDMAInstance,
+ psMSCDevice->sPrivateData.ui8INDMA);
+ }
+
+ //
+ // Configure the DMA for the IN endpoint.
+ //
+ psMSCDevice->sPrivateData.ui8INDMA =
+ USBLibDMAChannelAllocate(psMSCDevice->sPrivateData.psDMAInstance,
+ psMSCDevice->sPrivateData.ui8INEndpoint, 64,
+ USB_DMA_EP_TX | USB_DMA_EP_DEVICE);
+
+ USBLibDMAUnitSizeSet(psMSCDevice->sPrivateData.psDMAInstance,
+ psMSCDevice->sPrivateData.ui8INDMA, 32);
+
+ USBLibDMAArbSizeSet(psMSCDevice->sPrivateData.psDMAInstance,
+ psMSCDevice->sPrivateData.ui8INDMA, 16);
+
+ //
+ // If we have a control callback, let the client know we are open for
+ // business.
+ //
+ if(psMSCDevice->pfnEventCallback)
+ {
+ //
+ // Pass the connected event to the client.
+ //
+ psMSCDevice->pfnEventCallback(pvMSCDevice, 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 ui32Index is the index of the USB controller to initialize for
+//! mass storage class device operation.
+//! \param psMSCDevice 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 psMSCDevice 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(uint32_t ui32Index, tUSBDMSCDevice *psMSCDevice)
+{
+ tDeviceDescriptor *psDevDesc;
+ tConfigDescriptor *pConfDesc;
+
+ //
+ // Check parameter validity.
+ //
+ ASSERT(ui32Index == 0);
+ ASSERT(psMSCDevice);
+ ASSERT(psMSCDevice->ppui8StringDescriptors);
+
+ USBDMSCCompositeInit(ui32Index, psMSCDevice, 0);
+
+ //
+ // Fix up the device descriptor with the client-supplied values.
+ //
+ psDevDesc = (tDeviceDescriptor *)g_pui8MSCDeviceDescriptor;
+ psDevDesc->idVendor = psMSCDevice->ui16VID;
+ psDevDesc->idProduct = psMSCDevice->ui16PID;
+
+ //
+ // Fix up the configuration descriptor with client-supplied values.
+ //
+ pConfDesc = (tConfigDescriptor *)g_pui8MSCDescriptor;
+ pConfDesc->bmAttributes = psMSCDevice->ui8PwrAttributes;
+ pConfDesc->bMaxPower = (uint8_t)(psMSCDevice->ui16MaxPowermA / 2);
+
+ //
+ // All is well so now pass the descriptors to the lower layer and put
+ // the bulk device on the bus.
+ //
+ USBDCDInit(ui32Index, &psMSCDevice->sPrivateData.sDevInfo,
+ (void *)psMSCDevice);
+
+ //
+ // Return the pointer to the instance indicating that everything went well.
+ //
+ return((void *)psMSCDevice);
+}
+
+//*****************************************************************************
+//
+//! This function should be called once for the mass storage class device to
+//! initialized basic operation and prepare for enumeration.
+//!
+//! \param ui32Index is the index of the USB controller to initialize for
+//! mass storage class device operation.
+//! \param psMSCDevice points to a structure containing parameters customizing
+//! the operation of the mass storage device.
+//! \param psCompEntry is the composite device entry to initialize when
+//! creating a composite 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 psMSCDevice 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. If this mass storage
+//! device is part of a composite device, then the \e psCompEntry should
+//! point to the composite device entry to initialize. This is part of the
+//! array that is passed to the USBDCompositeInit() function.
+//!
+//! 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 zero on failure or a non-zero instance value that should be
+//! used with the remaining USB mass storage APIs.
+//
+//*****************************************************************************
+void *
+USBDMSCCompositeInit(uint32_t ui32Index, tUSBDMSCDevice *psMSCDevice,
+ tCompositeEntry *psCompEntry)
+{
+ tMSCInstance *psInst;
+
+ //
+ // Check parameter validity.
+ //
+ ASSERT(ui32Index == 0);
+ ASSERT(psMSCDevice);
+ ASSERT(psMSCDevice->ppui8StringDescriptors);
+ ASSERT(psCompEntry != 0);
+
+ //
+ // Initialize the workspace in the passed instance structure.
+ //
+ psInst = &psMSCDevice->sPrivateData;
+ psInst->ui32USBBase = USB0_BASE;
+ psInst->bConnected = false;
+ psInst->iMediaStatus = eUSBDMSCMediaUnknown;
+
+ //
+ // Initialize the composite entry that is used by the composite device
+ // class.
+ //
+ if(psCompEntry != 0)
+ {
+ psCompEntry->psDevInfo = &psInst->sDevInfo;
+ psCompEntry->pvInstance = (void *)psMSCDevice;
+ }
+
+ //
+ // Initialize the device information structure.
+ //
+ psInst->sDevInfo.psCallbacks = &g_sMSCHandlers;
+ psInst->sDevInfo.pui8DeviceDescriptor = g_pui8MSCDeviceDescriptor;
+ psInst->sDevInfo.ppsConfigDescriptors = g_ppsMSCConfigDescriptors;
+ psInst->sDevInfo.ppui8StringDescriptors = 0;
+ psInst->sDevInfo.ui32NumStringDescriptors = 0;
+
+ //
+ // Initialize the device info structure for the mass storage device.
+ //
+ USBDCDDeviceInfoInit(0, &psInst->sDevInfo);
+
+ //
+ // Set the initial interface and endpoints.
+ //
+ psInst->ui8Interface = 0;
+ psInst->ui8OUTEndpoint = DATA_OUT_ENDPOINT;
+ psInst->ui8INEndpoint = DATA_IN_ENDPOINT;
+
+ //
+ // Set the initial SCSI state to idle.
+ //
+ psInst->ui8SCSIState = STATE_SCSI_IDLE;
+
+ //
+ // Plug in the client's string stable to the device information
+ // structure.
+ //
+ psInst->sDevInfo.ppui8StringDescriptors =
+ psMSCDevice->ppui8StringDescriptors;
+ psInst->sDevInfo.ui32NumStringDescriptors =
+ psMSCDevice->ui32NumStringDescriptors;
+
+ //
+ // Open the drive requested.
+ //
+ psInst->pvMedia = psMSCDevice->sMediaFunctions.pfnOpen(0);
+
+ if(psInst->pvMedia == 0)
+ {
+ //
+ // There is no media currently present.
+ //
+ psInst->ui8SenseKey = SCSI_RS_KEY_NOT_READY;
+ psInst->ui16AddSenseCode = SCSI_RS_MED_NOT_PRSNT;
+ }
+ else
+ {
+ //
+ // Media is now ready for use.
+ //
+ psInst->ui8SenseKey = SCSI_RS_KEY_UNIT_ATTN;
+ psInst->ui16AddSenseCode = SCSI_RS_MED_NOTRDY2RDY;
+ }
+
+ //
+ // Enable Clocking to the USB controller.
+ //
+ MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_USB0);
+
+ //
+ // Turn on USB Phy clock.
+ //
+ MAP_SysCtlUSBPLLEnable();
+
+ //
+ // Get the DMA instance pointer.
+ //
+ psInst->psDMAInstance = USBLibDMAInit(0);
+
+ //
+ // Return the pointer to the instance indicating that everything went well.
+ //
+ return((void *)psMSCDevice);
+}
+
+//*****************************************************************************
+//
+//! Shuts down the mass storage device.
+//!
+//! \param pvMSCDevice is the pointer to the device instance structure as
+//! returned by USBDMSCInit() or USBDMSCCompositeInit().
+//!
+//! This function terminates mass storage operation for the instance supplied
+//! and removes the device from the USB bus. Following this call, the
+//! \e pvMSCDevice instance may not me used in any other call to the mass
+//! storage device other than USBDMSCInit() or USBDMSCCompositeInit().
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+USBDMSCTerm(void *pvMSCDevice)
+{
+ tUSBDMSCDevice *psMSCDevice;
+
+ ASSERT(pvMSCDevice != 0);
+
+ //
+ // Cleanly exit device mode.
+ //
+ USBDCDTerm(0);
+
+ //
+ // Create a device instance pointer.
+ //
+ psMSCDevice = pvMSCDevice;
+
+ //
+ // If the media was opened the close it out.
+ //
+ if(psMSCDevice->sPrivateData.pvMedia != 0)
+ {
+ psMSCDevice->sPrivateData.pvMedia = 0;
+ psMSCDevice->sMediaFunctions.pfnClose(0);
+ }
+}
+
+//*****************************************************************************
+//
+// This function is called by the USB device stack whenever a non-standard
+// request is received.
+//
+// \param pvMSCDevice 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 *pvMSCDevice, tUSBRequest *pUSBRequest)
+{
+ //
+ // This class only support a single LUN.
+ //
+ static const uint8_t ui8MaxLun = 0;
+
+ ASSERT(pvMSCDevice != 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:
+ {
+ //
+ // Need to ACK the data on end point 0 with last data since there
+ // is no more data expected.
+ //
+ USBDevEndpointDataAck(USB0_BASE, USB_EP_0, true);
+
+ //
+ // Send our response to the host.
+ //
+ USBDCDSendDataEP0(0, (uint8_t *)&ui8MaxLun, 1);
+
+ break;
+ }
+ case USBREQ_BULK_ONLY_RESET:
+ {
+ //
+ // Need to ACK the data on end point 0 with last data since there
+ // is no more data expected.
+ //
+ USBDevEndpointDataAck(USB0_BASE, USB_EP_0, true);
+
+ //
+ // Send a null packet to the host.
+ //
+ USBDCDSendDataEP0(0, (uint8_t *)&ui8MaxLun, 0);
+
+ 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(tUSBDMSCDevice *psMSCDevice)
+{
+ int32_t i32Idx;
+ tMSCInstance *psInst;
+ uint32_t *pui32Data;
+
+ //
+ // Create a local 32-bit pointer to the command.
+ //
+ pui32Data = (uint32_t *)g_pui8Command;
+
+ //
+ // Create the serial instance data.
+ //
+ psInst = &psMSCDevice->sPrivateData;
+
+ //
+ // Direct Access device, Removable storage and SCSI 1 responses.
+ //
+ pui32Data[0] = SCSI_INQ_PDT_SBC | (SCSI_INQ_RMB << 8);
+
+ //
+ // Additional Length is fixed at 31 bytes.
+ //
+ pui32Data[1] = 31;
+
+ //
+ // Copy the Vendor string.
+ //
+ for(i32Idx = 0; i32Idx < 8; i32Idx++)
+ {
+ g_pui8Command[i32Idx + 8] = psMSCDevice->pui8Vendor[i32Idx];
+ }
+
+ //
+ // Copy the Product string.
+ //
+ for(i32Idx = 0; i32Idx < 16; i32Idx++)
+ {
+ g_pui8Command[i32Idx + 16] = psMSCDevice->pui8Product[i32Idx];
+ }
+
+ //
+ // Copy the Version string.
+ //
+ for(i32Idx = 0; i32Idx < 4; i32Idx++)
+ {
+ g_pui8Command[i32Idx + 32] = psMSCDevice->pui8Version[i32Idx];
+ }
+
+ //
+ // Send the SCSI Inquiry Response.
+ //
+ MAP_USBEndpointDataPut(USB0_BASE, psInst->ui8INEndpoint, g_pui8Command,
+ 36);
+
+ //
+ // Send the data to the host.
+ //
+ MAP_USBEndpointDataSend(USB0_BASE, psInst->ui8INEndpoint, 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->ui8SCSIState = 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(tUSBDMSCDevice *psMSCDevice)
+{
+ uint32_t ui32Blocks;
+ tMSCInstance *psInst;
+ uint32_t *pui32Data;
+
+ //
+ // Create a local 32-bit pointer to the command.
+ //
+ pui32Data = (uint32_t *)g_pui8Command;
+
+ //
+ // Get our instance data pointer.
+ //
+ psInst = &psMSCDevice->sPrivateData;
+
+ if(psInst->pvMedia != 0)
+ {
+ if(psMSCDevice->sMediaFunctions.pfnBlockSize)
+ {
+ //
+ // Query the block size for the device
+ //
+ g_pui32BlockSize =
+ psMSCDevice->sMediaFunctions.pfnBlockSize(psInst->pvMedia);
+ }
+ ui32Blocks =
+ psMSCDevice->sMediaFunctions.pfnNumBlocks(psInst->pvMedia);
+
+ pui32Data[0] = 0x08000000;
+
+ //
+ // Fill in the number of blocks, the bytes endianness must be changed.
+ //
+ g_pui8Command[4] = ui32Blocks >> 24;
+ g_pui8Command[5] = 0xff & (ui32Blocks >> 16);
+ g_pui8Command[6] = 0xff & (ui32Blocks >> 8);
+ g_pui8Command[7] = 0xff & (ui32Blocks);
+
+ //
+ // Current media capacity
+ //
+ g_pui8Command[8] = 0x2;
+
+ //
+ // Fill in the block size, which is g_pui32BlockSize.
+ //
+ g_pui8Command[9] = 0xff & (g_pui32BlockSize >> 16);
+ g_pui8Command[10] = 0xff & (g_pui32BlockSize >> 8);
+ g_pui8Command[11] = 0xff & g_pui32BlockSize;
+
+ //
+ // Send out the 12 bytes that are in this response.
+ //
+ MAP_USBEndpointDataPut(USB0_BASE, psInst->ui8INEndpoint, g_pui8Command,
+ 12);
+ MAP_USBEndpointDataSend(USB0_BASE, psInst->ui8INEndpoint,
+ 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->ui8INEndpoint,
+ USB_EP_DEV_IN);
+
+ //
+ // Mark the sense code as valid and indicate that these is no media
+ // present.
+ //
+ psInst->ui8ErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS;
+ psInst->ui8SenseKey = SCSI_RS_KEY_NOT_READY;
+ psInst->ui16AddSenseCode = SCSI_RS_MED_NOT_PRSNT;
+ }
+
+ psInst->ui8SCSIState = 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(tUSBDMSCDevice *psMSCDevice)
+{
+ uint32_t ui32Blocks;
+ tMSCInstance *psInst;
+
+ //
+ // Get our instance data pointer.
+ //
+ psInst = &psMSCDevice->sPrivateData;
+
+ if(psMSCDevice->sMediaFunctions.pfnBlockSize)
+ {
+ //
+ // Query the block size for the device
+ //
+ g_pui32BlockSize =
+ psMSCDevice->sMediaFunctions.pfnBlockSize(psInst->pvMedia);
+ }
+
+ ui32Blocks = psMSCDevice->sMediaFunctions.pfnNumBlocks(psInst->pvMedia);
+
+ //
+ // Only decrement if any blocks were found.
+ //
+ if(ui32Blocks != 0)
+ {
+ //
+ // One less than the maximum number is the last addressable
+ // block.
+ //
+ ui32Blocks--;
+ }
+
+ if(psInst->pvMedia != 0)
+ {
+ //
+ // Fill in the number of blocks, the bytes endianness must be changed.
+ //
+ g_pui8Command[0] = 0xff & (ui32Blocks >> 24);
+ g_pui8Command[1] = 0xff & (ui32Blocks >> 16);
+ g_pui8Command[2] = 0xff & (ui32Blocks >> 8);
+ g_pui8Command[3] = 0xff & (ui32Blocks);
+
+ g_pui8Command[4] = 0;
+
+ //
+ // Fill in the block size, which is g_pui32BlockSize.
+ //
+ g_pui8Command[5] = 0xff & (g_pui32BlockSize >> 16);
+ g_pui8Command[6] = 0xff & (g_pui32BlockSize >> 8);
+ g_pui8Command[7] = 0xff & g_pui32BlockSize;
+
+ //
+ // Send the SCSI Inquiry Response.
+ //
+ MAP_USBEndpointDataPut(USB0_BASE, psInst->ui8INEndpoint, g_pui8Command,
+ 8);
+ MAP_USBEndpointDataSend(USB0_BASE, psInst->ui8INEndpoint,
+ 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->ui8INEndpoint,
+ USB_EP_DEV_IN);
+
+ //
+ // Mark the sense code as valid and indicate that these is no media
+ // present.
+ //
+ psInst->ui8ErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS;
+ psInst->ui8SenseKey = SCSI_RS_KEY_NOT_READY;
+ psInst->ui16AddSenseCode = SCSI_RS_MED_NOT_PRSNT;
+ }
+
+ psInst->ui8SCSIState = 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(tUSBDMSCDevice *psMSCDevice)
+{
+ tMSCInstance *psInst;
+ int32_t i32Idx;
+
+ //
+ // Zero out the response data.
+ //
+ for(i32Idx = 0; i32Idx < 18; i32Idx++)
+ {
+ g_pui8Command[i32Idx] = 0;
+ }
+
+ //
+ // Get our instance data pointer.
+ //
+ psInst = &psMSCDevice->sPrivateData;
+
+ //
+ // The request sense response.
+ //
+ g_pui8Command[0] = psInst->ui8ErrorCode;
+ g_pui8Command[2] = psInst->ui8SenseKey;
+
+ //
+ // There are 10 more bytes of data.
+ //
+ g_pui8Command[7] = 10;
+
+ //
+ // Transition from not ready to ready.
+ //
+ g_pui8Command[12] = (uint8_t)psInst->ui16AddSenseCode;
+ g_pui8Command[13] = (uint8_t)(psInst->ui16AddSenseCode >> 8);
+
+ //
+ // Send the SCSI Inquiry Response.
+ //
+ MAP_USBEndpointDataPut(USB0_BASE, psInst->ui8INEndpoint, g_pui8Command,
+ 18);
+ MAP_USBEndpointDataSend(USB0_BASE, psInst->ui8INEndpoint, USB_TRANS_IN);
+
+ //
+ // Reset the valid flag on errors.
+ //
+ psInst->ui8ErrorCode = 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->ui8SCSIState = 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(tUSBDMSCDevice *psMSCDevice, tMSCCBW *psSCSICBW)
+{
+ uint16_t ui16NumBlocks;
+ tMSCInstance *psInst;
+
+ //
+ // Default the number of blocks.
+ //
+ ui16NumBlocks = 0;
+
+ //
+ // Get our instance data pointer.
+ //
+ psInst = &psMSCDevice->sPrivateData;
+
+ if(psInst->pvMedia != 0)
+ {
+ //
+ // Get the logical block from the CBW structure. This switching
+ // is required to convert from big to little endian.
+ //
+ psInst->ui32CurrentLBA = (psSCSICBW->CBWCB[2] << 24) |
+ (psSCSICBW->CBWCB[3] << 16) |
+ (psSCSICBW->CBWCB[4] << 8) |
+ (psSCSICBW->CBWCB[5] << 0);
+
+ //
+ // More bytes to read.
+ //
+ ui16NumBlocks = (psSCSICBW->CBWCB[7] << 8) | psSCSICBW->CBWCB[8];
+
+ //
+ // Read the next logical block from the storage device.
+ //
+ if(psMSCDevice->sMediaFunctions.pfnBlockRead(psInst->pvMedia,
+ (uint8_t *)psInst->pui32Buffer, psInst->ui32CurrentLBA, 1) == 0)
+ {
+ psInst->pvMedia = 0;
+ psMSCDevice->sMediaFunctions.pfnClose(0);
+ }
+ }
+
+ //
+ // If there is media present then start transferring the data.
+ //
+ if(psInst->pvMedia != 0)
+ {
+ //
+ // Configure and DMA for the IN transfer.
+ //
+ USBLibDMATransfer(psInst->psDMAInstance, psInst->ui8INDMA,
+ psInst->pui32Buffer, g_pui32BlockSize);
+
+ //
+ // Remember that a DMA is in progress.
+ //
+ psInst->ui32Flags |= USBD_FLAG_DMA_IN;
+
+ //
+ // Schedule the remaining bytes to send.
+ //
+ psInst->ui32BytesToTransfer = (g_pui32BlockSize * ui16NumBlocks);
+
+ //
+ // Move on and start sending blocks.
+ //
+ psInst->ui8SCSIState = STATE_SCSI_SEND_BLOCKS;
+
+ if(psMSCDevice->pfnEventCallback)
+ {
+ psMSCDevice->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->ui8INEndpoint,
+ USB_EP_DEV_IN);
+
+ //
+ // Mark the sense code as valid and indicate that these is no media
+ // present.
+ //
+ psInst->ui8ErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS;
+ psInst->ui8SenseKey = SCSI_RS_KEY_NOT_READY;
+ psInst->ui16AddSenseCode = SCSI_RS_MED_NOT_PRSNT;
+
+ psInst->ui8SCSIState = 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(tUSBDMSCDevice *psMSCDevice, tMSCCBW *psSCSICBW)
+{
+ uint16_t ui16NumBlocks;
+ tMSCInstance *psInst;
+
+ //
+ // Get instance data pointers.
+ //
+ psInst = &psMSCDevice->sPrivateData;
+
+ //
+ // 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->ui32CurrentLBA = (psSCSICBW->CBWCB[2] << 24) |
+ (psSCSICBW->CBWCB[3] << 16) |
+ (psSCSICBW->CBWCB[4] << 8) |
+ (psSCSICBW->CBWCB[5] << 0);
+
+ //
+ // More bytes to read.
+ //
+ ui16NumBlocks = (psSCSICBW->CBWCB[7] << 8) | psSCSICBW->CBWCB[8];
+
+ psInst->ui32BytesToTransfer = g_pui32BlockSize * ui16NumBlocks;
+
+ //
+ // Start sending logical blocks, these are always multiples of
+ // g_pui32BlockSize bytes.
+ //
+ psInst->ui8SCSIState = STATE_SCSI_RECEIVE_BLOCKS;
+
+ //
+ // Configure and enable DMA for the OUT transfer.
+ //
+ USBLibDMATransfer(psInst->psDMAInstance, psInst->ui8OUTDMA,
+ psInst->pui32Buffer, g_pui32BlockSize);
+
+ //
+ // Remember that a DMA is in progress.
+ //
+ psInst->ui32Flags |= USBD_FLAG_DMA_OUT;
+
+ //
+ // Notify the application of the write event.
+ //
+ if(psMSCDevice->pfnEventCallback)
+ {
+ psMSCDevice->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->ui8OUTEndpoint,
+ USB_EP_DEV_OUT);
+
+ //
+ // Mark the sense code as valid and indicate that these is no media
+ // present.
+ //
+ psInst->ui8ErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS;
+ psInst->ui8SenseKey = SCSI_RS_KEY_NOT_READY;
+ psInst->ui16AddSenseCode = SCSI_RS_MED_NOT_PRSNT;
+
+ psInst->ui8SCSIState = 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(tUSBDMSCDevice *psMSCDevice, tMSCCBW *psSCSICBW)
+{
+ tMSCInstance *psInst;
+
+ //
+ // Get our instance data pointer.
+ //
+ psInst = &psMSCDevice->sPrivateData;
+
+ //
+ // If there is media present send the response.
+ //
+ if(psInst->pvMedia != 0)
+ {
+ //
+ // Three extra bytes in this response.
+ //
+ g_pui8Command[0] = 3;
+ g_pui8Command[1] = 0;
+ g_pui8Command[2] = 0;
+ g_pui8Command[3] = 0;
+
+ //
+ // Manually send the response back to the host.
+ //
+ MAP_USBEndpointDataPut(USB0_BASE, psInst->ui8INEndpoint, g_pui8Command,
+ 4);
+ MAP_USBEndpointDataSend(USB0_BASE, psInst->ui8INEndpoint,
+ 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 = psSCSICBW->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->ui8INEndpoint,
+ USB_EP_DEV_IN);
+
+ //
+ // Mark the sense code as valid and indicate that these is no media
+ // present.
+ //
+ psInst->ui8ErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS;
+ psInst->ui8SenseKey = SCSI_RS_KEY_NOT_READY;
+ psInst->ui16AddSenseCode = SCSI_RS_MED_NOT_PRSNT;
+ }
+
+ psInst->ui8SCSIState = 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(tUSBDMSCDevice *psMSCDevice)
+{
+ tMSCInstance *psInst;
+
+ //
+ // Get our instance data pointer.
+ //
+ psInst = &psMSCDevice->sPrivateData;
+
+ //
+ // Respond with the requested status.
+ //
+ MAP_USBEndpointDataPut(USB0_BASE, psInst->ui8INEndpoint,
+ (uint8_t *)&g_sSCSICSW, 13);
+ MAP_USBEndpointDataSend(USB0_BASE, psInst->ui8INEndpoint, USB_TRANS_IN);
+
+ //
+ // Move the state to status sent so that the next interrupt will move the
+ // statue to idle.
+ //
+ psInst->ui8SCSIState = STATE_SCSI_SENT_STATUS;
+}
+
+//*****************************************************************************
+//
+// This function is used to handle the Prevent/Allow Medium Removal command when it is
+// received from the host.
+//
+//*****************************************************************************
+static void
+USBDSCSIPreventAllowMediumRemoval(tUSBDMSCDevice *psMSCDevice, tMSCCBW *psSCSICBW)
+{
+ tMSCInstance *psInst;
+
+ //
+ // Get our instance data pointer.
+ //
+ psInst = &psMSCDevice->sPrivateData;
+
+ //
+ // If there is media present send the response.
+ //
+ if(psInst->pvMedia != 0)
+ {
+ //
+ // See if this was an allow or prevent removal request.
+ //
+ if((psSCSICBW->CBWCB[4] & SCSI_PE_MEDRMV_M) == SCSI_PE_MEDRMV_ALLOW)
+ {
+ psInst->ui32Flags |= USBD_FLAG_ALLOW_REMOVAL;
+ }
+ else
+ {
+ psInst->ui32Flags &= ~USBD_FLAG_ALLOW_REMOVAL;
+ }
+
+ //
+ // 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->ui8INEndpoint,
+ USB_EP_DEV_IN);
+
+ //
+ // Mark the sense code as valid and indicate that these is no media
+ // present.
+ //
+ psInst->ui8ErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS;
+ psInst->ui8SenseKey = SCSI_RS_KEY_NOT_READY;
+ psInst->ui16AddSenseCode = SCSI_RS_MED_NOT_PRSNT;
+ }
+
+ psInst->ui8SCSIState = STATE_SCSI_SEND_STATUS;
+}
+
+//*****************************************************************************
+//
+// This function is used to handle the SCSI Start/Stop Unit command when it is
+// received from the host.
+//
+//*****************************************************************************
+static void
+USBDSCSIStartStopUnit(tUSBDMSCDevice *psMSCDevice, tMSCCBW *psSCSICBW)
+{
+ tMSCInstance *psInst;
+
+ //
+ // Get our instance data pointer.
+ //
+ psInst = &psMSCDevice->sPrivateData;
+
+ //
+ // If there is media present send the response.
+ //
+ if(psInst->pvMedia != 0)
+ {
+ switch(psSCSICBW->CBWCB[4] & (SCSI_SS_UNIT_START | SCSI_SS_UNIT_LOEJ))
+ {
+ case 0:
+ {
+ //
+ // Media state is now stopped but not ejected.
+ //
+ psInst->iMediaStatus = eUSBDMSCMediaStopped;
+
+ g_sSCSICSW.bCSWStatus = 0;
+
+ break;
+ }
+ case SCSI_SS_UNIT_START:
+ {
+ //
+ // Return to Media present.
+ //
+ psInst->iMediaStatus = eUSBDMSCMediaPresent;
+
+ g_sSCSICSW.bCSWStatus = 0;
+
+ break;
+ }
+ case SCSI_SS_UNIT_LOEJ:
+ {
+ //
+ // Only allow eject if the Prevent/Allow Medium Removal has
+ // been sent and enabled medium removal.
+ //
+ if(psInst->ui32Flags & USBD_FLAG_ALLOW_REMOVAL)
+ {
+ psInst->iMediaStatus = eUSBDMSCMediaNotPresent;
+ psMSCDevice->sMediaFunctions.pfnClose(0);
+ psMSCDevice->sPrivateData.pvMedia = 0;
+ g_sSCSICSW.bCSWStatus = 0;
+ }
+ else
+ {
+ g_sSCSICSW.bCSWStatus = 1;
+ }
+
+ break;
+ }
+ case SCSI_SS_UNIT_START | SCSI_SS_UNIT_LOEJ:
+ {
+ //
+ // Since there was no media, check for media here.
+ //
+ psInst->pvMedia = psMSCDevice->sMediaFunctions.pfnOpen(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;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ //
+ // There is no further data to send.
+ //
+ 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->ui8INEndpoint,
+ USB_EP_DEV_IN);
+
+ //
+ // Mark the sense code as valid and indicate that these is no media
+ // present.
+ //
+ psInst->ui8ErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS;
+ psInst->ui8SenseKey = SCSI_RS_KEY_NOT_READY;
+ psInst->ui16AddSenseCode = SCSI_RS_MED_NOT_PRSNT;
+ }
+
+ psInst->ui8SCSIState = STATE_SCSI_SEND_STATUS;
+}
+
+//*****************************************************************************
+//
+// This function is used to handle all SCSI commands.
+//
+//*****************************************************************************
+uint32_t
+USBDSCSICommand(tUSBDMSCDevice *psMSCDevice, tMSCCBW *psSCSICBW)
+{
+ uint32_t ui32RetCode, ui32TransferLength;
+ tMSCInstance *psInst;
+
+ //
+ // Get our instance data pointer.
+ //
+ psInst = &psMSCDevice->sPrivateData;
+
+ //
+ // Initialize the return code.
+ //
+ ui32RetCode = 1;
+
+ //
+ // Save the transfer length because it may be overwritten by some calls.
+ //
+ ui32TransferLength = psSCSICBW->dCBWDataTransferLength;
+
+ switch(psSCSICBW->CBWCB[0])
+ {
+ //
+ // Respond to the SCSI Inquiry command.
+ //
+ case SCSI_INQUIRY_CMD:
+ {
+ USBDSCSIInquiry(psMSCDevice);
+
+ 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 if(psInst->iMediaStatus == eUSBDMSCMediaNotPresent)
+ {
+ //
+ // Set the status to success for now, this could be different
+ // if there is no media present.
+ //
+ g_sSCSICSW.bCSWStatus = 1;
+ psInst->ui8ErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS;
+ psInst->ui8SenseKey = SCSI_RS_KEY_NOT_READY;
+ psInst->ui16AddSenseCode = SCSI_RS_MED_NOT_PRSNT;
+ }
+ else
+ {
+ //
+ // Since there was no media, check for media here.
+ //
+ psInst->pvMedia = psMSCDevice->sMediaFunctions.pfnOpen(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(psMSCDevice);
+
+ break;
+ }
+
+ //
+ // Handle the Read Capacity command.
+ //
+ case SCSI_READ_CAPACITY:
+ {
+ USBDSCSIReadCapacity(psMSCDevice);
+
+ break;
+ }
+
+ //
+ // Handle the Request Sense command.
+ //
+ case SCSI_REQUEST_SENSE:
+ {
+ USBDSCSIRequestSense(psMSCDevice);
+
+ break;
+ }
+
+ //
+ // Handle the Read 10 command.
+ //
+ case SCSI_READ_10:
+ {
+ USBDSCSIRead10(psMSCDevice, psSCSICBW);
+
+ break;
+ }
+
+ //
+ // Handle the Write 10 command.
+ //
+ case SCSI_WRITE_10:
+ {
+ USBDSCSIWrite10(psMSCDevice, psSCSICBW);
+
+ break;
+ }
+
+ //
+ // Handle the Mode Sense 6 command.
+ //
+ case SCSI_MODE_SENSE_6:
+ {
+ USBDSCSIModeSense6(psMSCDevice, psSCSICBW);
+
+ break;
+ }
+
+ //
+ // Handle the Prevent/Allow Medium Removal command.
+ //
+ case SCSI_MEDIUM_REMOVAL:
+ {
+ USBDSCSIPreventAllowMediumRemoval(psMSCDevice, psSCSICBW);
+
+ break;
+ }
+
+ //
+ // Handle the Prevent/Allow Medium Removal command.
+ //
+ case SCSI_START_STOP_UNIT:
+ {
+ USBDSCSIStartStopUnit(psMSCDevice, psSCSICBW);
+ 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 = psSCSICBW->dCBWDataTransferLength;
+
+ //
+ // If there is data then there is more work to do.
+ //
+ if(psSCSICBW->dCBWDataTransferLength != 0)
+ {
+ if(psSCSICBW->bmCBWFlags & CBWFLAGS_DIR_IN)
+ {
+ //
+ // Stall the IN endpoint
+ //
+ MAP_USBDevEndpointStall(USB0_BASE, psInst->ui8INEndpoint,
+ USB_EP_DEV_IN);
+ }
+ else
+ {
+ //
+ // Stall the OUT endpoint
+ //
+ MAP_USBDevEndpointStall(USB0_BASE, psInst->ui8OUTEndpoint,
+ USB_EP_DEV_OUT);
+
+ }
+
+ //
+ // Go back to the idle state and wait for the host to clear
+ // the stall later.
+ //
+ psInst->ui8SCSIState = STATE_SCSI_IDLE;
+ }
+
+ //
+ // Set the sense codes.
+ //
+ psInst->ui8ErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS;
+ psInst->ui8SenseKey = SCSI_RS_KEY_ILGL_RQST;
+ psInst->ui16AddSenseCode = SCSI_RS_PV_INVALID;
+
+ break;
+ }
+ }
+
+ //
+ // If there is no data then send out the current status.
+ //
+ if(ui32TransferLength == 0)
+ {
+ USBDSCSISendStatus(psMSCDevice);
+ }
+ return(ui32RetCode);
+}
+
+//*****************************************************************************
+//
+// Close the Doxygen group.
+//! @}
+//
+//*****************************************************************************