//***************************************************************************** // // 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. //! @} // //*****************************************************************************