From 990090a4cc9070837d31e66b58d40f0c3d038741 Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Sun, 16 Mar 2014 14:41:11 +0200 Subject: Add usblib and utils --- usblib/device/usbdmsc.c | 2441 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2441 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..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 +#include +#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. +//! @} +// +//***************************************************************************** -- cgit v1.3.1