From c241dbd7e78c50327781a35d88d9f2db7ff2b271 Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Mon, 29 Oct 2012 23:08:53 +0200 Subject: Added usblib --- usblib/device/usbdmsc.c | 2130 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2130 insertions(+) create mode 100644 usblib/device/usbdmsc.c (limited to 'usblib/device/usbdmsc.c') 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. +//! @} +// +//***************************************************************************** -- cgit v1.3.1