2130 lines
67 KiB
C
2130 lines
67 KiB
C
//*****************************************************************************
|
|
//
|
|
// usbdmsc.c - USB mass storage device class driver.
|
|
//
|
|
// Copyright (c) 2009-2013 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 1.1 of the Tiva USB Library.
|
|
//
|
|
//*****************************************************************************
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include "inc/hw_memmap.h"
|
|
#include "inc/hw_types.h"
|
|
#include "driverlib/debug.h"
|
|
#include "driverlib/rom.h"
|
|
#include "driverlib/rom_map.h"
|
|
#include "driverlib/sysctl.h"
|
|
#include "driverlib/usbdrv.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
|
|
|
|
//*****************************************************************************
|
|
//
|
|
// 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 sizes of USB transfers for data and commands.
|
|
//
|
|
//*****************************************************************************
|
|
#define MAX_TRANSFER_SIZE 512
|
|
#define COMMAND_BUFFER_SIZE 64
|
|
|
|
//*****************************************************************************
|
|
//
|
|
// The local buffer used to read in commands and process them.
|
|
//
|
|
//*****************************************************************************
|
|
static 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:
|
|
//! - eUSBDMSCMediaPresent indicates that the media is present or has been
|
|
//! added.
|
|
//! - eUSBDMSCMediaNotPresent indicates that the media is not present or was
|
|
//! removed.
|
|
//! - 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 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 -= MAX_TRANSFER_SIZE;
|
|
|
|
//
|
|
// 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,
|
|
MAX_TRANSFER_SIZE);
|
|
|
|
//
|
|
// 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 -= MAX_TRANSFER_SIZE;
|
|
|
|
//
|
|
// 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,
|
|
MAX_TRANSFER_SIZE);
|
|
}
|
|
|
|
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;
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
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 USBDMSCInitComposite().
|
|
//!
|
|
//! 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 USBDMSCInitComposite().
|
|
//!
|
|
//! \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:
|
|
{
|
|
//
|
|
// Send our response to the host.
|
|
//
|
|
USBDCDSendDataEP0(0, (uint8_t *)&ui8MaxLun, 1);
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// This request was not recognized so stall.
|
|
//
|
|
default:
|
|
{
|
|
USBDCDStallEP0(0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
// This function is used to handle the SCSI Inquiry command when it is received
|
|
// from the host.
|
|
//
|
|
//*****************************************************************************
|
|
static void
|
|
USBDSCSIInquiry(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)
|
|
{
|
|
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 fixed at DEVICE_BLOCK_SIZE.
|
|
//
|
|
g_pui8Command[9] = 0xff & (DEVICE_BLOCK_SIZE >> 16);
|
|
g_pui8Command[10] = 0xff & (DEVICE_BLOCK_SIZE >> 8);
|
|
g_pui8Command[11] = 0xff & DEVICE_BLOCK_SIZE;
|
|
|
|
//
|
|
// 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;
|
|
|
|
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 fixed at DEVICE_BLOCK_SIZE.
|
|
//
|
|
g_pui8Command[5] = 0xff & (DEVICE_BLOCK_SIZE >> 16);
|
|
g_pui8Command[6] = 0xff & (DEVICE_BLOCK_SIZE >> 8);
|
|
g_pui8Command[7] = 0xff & DEVICE_BLOCK_SIZE;
|
|
|
|
//
|
|
// 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;
|
|
|
|
//
|
|
// Get our instance data pointer.
|
|
//
|
|
psInst = &psMSCDevice->sPrivateData;
|
|
|
|
//
|
|
// The request sense response.
|
|
//
|
|
g_pui8Command[0] = psInst->ui8ErrorCode;
|
|
g_pui8Command[1] = 0;
|
|
g_pui8Command[2] = psInst->ui8SenseKey;
|
|
*(uint32_t *)&g_pui8Command[3] = 0;
|
|
|
|
//
|
|
// There are 10 more bytes of data.
|
|
//
|
|
g_pui8Command[7] = 10;
|
|
|
|
*(uint32_t *)&g_pui8Command[8] = 0;
|
|
|
|
//
|
|
// Transition from not ready to ready.
|
|
//
|
|
*(uint16_t *)&g_pui8Command[12] = psInst->ui16AddSenseCode;
|
|
*(uint32_t *)&g_pui8Command[14] = 0;
|
|
|
|
//
|
|
// 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, MAX_TRANSFER_SIZE);
|
|
|
|
//
|
|
// Remember that a DMA is in progress.
|
|
//
|
|
psInst->ui32Flags |= USBD_FLAG_DMA_IN;
|
|
|
|
//
|
|
// Schedule the remaining bytes to send.
|
|
//
|
|
psInst->ui32BytesToTransfer = (DEVICE_BLOCK_SIZE * 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 = DEVICE_BLOCK_SIZE * ui16NumBlocks;
|
|
|
|
//
|
|
// Start sending logical blocks, these are always multiples of
|
|
// DEVICE_BLOCK_SIZE bytes.
|
|
//
|
|
psInst->ui8SCSIState = STATE_SCSI_RECEIVE_BLOCKS;
|
|
|
|
//
|
|
// Configure and enable DMA for the OUT transfer.
|
|
//
|
|
USBLibDMATransfer(psInst->psDMAInstance, psInst->ui8OUTDMA,
|
|
psInst->pui32Buffer, MAX_TRANSFER_SIZE);
|
|
|
|
//
|
|
// 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 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
|
|
{
|
|
//
|
|
// 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;
|
|
}
|
|
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);
|
|
|
|
}
|
|
//
|
|
// Send the status once the stall occurs.
|
|
//
|
|
psInst->ui8SCSIState = STATE_SCSI_SEND_STATUS;
|
|
}
|
|
|
|
//
|
|
// 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.
|
|
//! @}
|
|
//
|
|
//*****************************************************************************
|