From c241dbd7e78c50327781a35d88d9f2db7ff2b271 Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Mon, 29 Oct 2012 23:08:53 +0200 Subject: Added usblib --- usblib/device/usbdaudio.c | 1485 ++++++++++++++++++++ usblib/device/usbdaudio.h | 362 +++++ usblib/device/usbdbulk.c | 1447 ++++++++++++++++++++ usblib/device/usbdbulk.h | 269 ++++ usblib/device/usbdcdc.c | 2972 +++++++++++++++++++++++++++++++++++++++++ usblib/device/usbdcdc.h | 353 +++++ usblib/device/usbdcdesc.c | 649 +++++++++ usblib/device/usbdcomp.c | 1543 +++++++++++++++++++++ usblib/device/usbdcomp.h | 297 ++++ usblib/device/usbdconfig.c | 654 +++++++++ usblib/device/usbddfu-rt.c | 589 ++++++++ usblib/device/usbddfu-rt.h | 163 +++ usblib/device/usbdenum.c | 2900 ++++++++++++++++++++++++++++++++++++++++ usblib/device/usbdevice.h | 223 ++++ usblib/device/usbdevicepriv.h | 223 ++++ usblib/device/usbdhandler.c | 82 ++ usblib/device/usbdhid.c | 2579 +++++++++++++++++++++++++++++++++++ usblib/device/usbdhid.h | 974 ++++++++++++++ usblib/device/usbdhidkeyb.c | 1119 ++++++++++++++++ usblib/device/usbdhidkeyb.h | 382 ++++++ usblib/device/usbdhidmouse.c | 843 ++++++++++++ usblib/device/usbdhidmouse.h | 321 +++++ usblib/device/usbdmsc.c | 2130 +++++++++++++++++++++++++++++ usblib/device/usbdmsc.h | 391 ++++++ 24 files changed, 22950 insertions(+) create mode 100644 usblib/device/usbdaudio.c create mode 100644 usblib/device/usbdaudio.h create mode 100644 usblib/device/usbdbulk.c create mode 100644 usblib/device/usbdbulk.h create mode 100644 usblib/device/usbdcdc.c create mode 100644 usblib/device/usbdcdc.h create mode 100644 usblib/device/usbdcdesc.c create mode 100644 usblib/device/usbdcomp.c create mode 100644 usblib/device/usbdcomp.h create mode 100644 usblib/device/usbdconfig.c create mode 100644 usblib/device/usbddfu-rt.c create mode 100644 usblib/device/usbddfu-rt.h create mode 100644 usblib/device/usbdenum.c create mode 100644 usblib/device/usbdevice.h create mode 100644 usblib/device/usbdevicepriv.h create mode 100644 usblib/device/usbdhandler.c create mode 100644 usblib/device/usbdhid.c create mode 100644 usblib/device/usbdhid.h create mode 100644 usblib/device/usbdhidkeyb.c create mode 100644 usblib/device/usbdhidkeyb.h create mode 100644 usblib/device/usbdhidmouse.c create mode 100644 usblib/device/usbdhidmouse.h create mode 100644 usblib/device/usbdmsc.c create mode 100644 usblib/device/usbdmsc.h (limited to 'usblib/device') diff --git a/usblib/device/usbdaudio.c b/usblib/device/usbdaudio.c new file mode 100644 index 0000000..a1ef199 --- /dev/null +++ b/usblib/device/usbdaudio.c @@ -0,0 +1,1485 @@ +//***************************************************************************** +// +// usbdaudio.c - USB audio device class driver. +// +// Copyright (c) 2009-2012 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/usb.h" +#include "driverlib/udma.h" +#include "usblib/usblib.h" +#include "usblib/usbaudio.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbdaudio.h" + +//***************************************************************************** +// +//! \addtogroup audio_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The following are the USB audio descriptor identifiers. +// +//***************************************************************************** +#define AUDIO_IN_TERMINAL_ID 1 +#define AUDIO_OUT_TERMINAL_ID 2 +#define AUDIO_CONTROL_ID 3 + +//***************************************************************************** +// +// The following are the USB interface numbers for this audio device. +// +//***************************************************************************** +#define AUDIO_INTERFACE_CONTROL 0 +#define AUDIO_INTERFACE_OUTPUT 1 + +//***************************************************************************** +// +// Endpoints to use for each of the required endpoints in the driver. +// +//***************************************************************************** +#define ISOC_OUT_ENDPOINT USB_EP_1 +#define ISOC_OUT_DMA_CHANNEL UDMA_CHANNEL_USBEP1RX + +//***************************************************************************** +// +// Max size is (48000 samples/sec * 4 bytes/sample) * 0.001 seconds/frame. +// +//***************************************************************************** +#define ISOC_OUT_EP_MAX_SIZE ((48000*4)/1000) + +//***************************************************************************** +// +// Device Descriptor. This is stored in RAM to allow several fields to be +// changed at runtime based on the client's requirements. +// +//***************************************************************************** +static unsigned char g_pAudioDeviceDescriptor[] = +{ + 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 USBDAudioInit). + USBShort(0), // Product ID (filled in during USBDAudioInit). + USBShort(0x100), // Device Version BCD. + 1, // Manufacturer string identifier. + 2, // Product string identifier. + 3, // Product serial number. + 1 // Number of configurations. +}; + +//***************************************************************************** +// +// Audio class device configuration descriptor. +// +// It is vital that the configuration descriptor bConfigurationValue field +// (byte 6) is 1 for the first configuration and increments by 1 for each +// additional configuration defined here. This relationship is assumed in the +// device stack for simplicity even though the USB 2.0 specification imposes +// no such restriction on the bConfigurationValue values. +// +// Note that this structure is deliberately located in RAM since we need to +// be able to patch some values in it based on client requirements. +// +//***************************************************************************** +static unsigned char g_pAudioDescriptor[] = +{ + // + // 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. + 2, // 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_BUS_PWR, // Bus Powered, Self Powered, remote wake up. + 250, // The maximum power in 2mA increments. +}; + +//***************************************************************************** +// +// This is the Interface Association Descriptor for the serial device used in +// composite devices. +// +//***************************************************************************** +unsigned char g_pIADAudioDescriptor[] = +{ + + 8, // Size of the interface descriptor. + USB_DTYPE_INTERFACE_ASC, // Interface Association Type. + 0x0, // Default starting interface is 0. + 0x2, // Number of interfaces in this association. + USB_CLASS_AUDIO, // The device class for this association. + USB_SUBCLASS_UNDEFINED, // The device subclass for this association. + USB_PROTOCOL_UNDEFINED, // The protocol for this association. + 0 // The string index for this association. +}; + +const tConfigSection g_sIADAudioConfigSection = +{ + sizeof(g_pIADAudioDescriptor), + g_pIADAudioDescriptor +}; + +//***************************************************************************** +// +// The remainder of the configuration descriptor is stored in flash since we +// don't need to modify anything in it at runtime. +// +//***************************************************************************** +const unsigned char g_pAudioControlInterface[] = +{ + // + // Vendor-specific Interface Descriptor. + // + 9, // Size of the interface descriptor. + USB_DTYPE_INTERFACE, // Type of this descriptor. + AUDIO_INTERFACE_CONTROL, // The index for this interface. + 0, // The alternate setting for this interface. + 0, // The number of endpoints used by this + // interface. + USB_CLASS_AUDIO, // The interface class + USB_ASC_AUDIO_CONTROL, // The interface sub-class. + 0, // The interface protocol for the sub-class + // specified above. + 0, // The string index for this interface. + + // + // Audio Header Descriptor. + // + 9, // The size of this descriptor. + USB_DTYPE_CS_INTERFACE, // Interface descriptor is class specific. + USB_ACDSTYPE_HEADER, // Descriptor sub-type is HEADER. + USBShort(0x0100), // Audio Device Class Specification Release + // Number in Binary-Coded Decimal. + // Total number of bytes in + // g_pAudioControlInterface + USBShort((9 + 9 + 12 + 13 + 9)), + 1, // Number of streaming interfaces. + 1, // Index of the first and only streaming + // interface. + + // + // Audio Input Terminal Descriptor. + // + 12, // The size of this descriptor. + USB_DTYPE_CS_INTERFACE, // Interface descriptor is class specific. + USB_ACDSTYPE_IN_TERMINAL, // Descriptor sub-type is INPUT_TERMINAL. + AUDIO_IN_TERMINAL_ID, // Terminal ID for this interface. + // USB streaming interface. + USBShort(USB_TTYPE_STREAMING), + 0, // ID of the Output Terminal to which this + // Input Terminal is associated. + 2, // Number of logical output channels in the + // Terminal's output audio channel cluster. + USBShort((USB_CHANNEL_L | // Describes the spatial location of the + USB_CHANNEL_R)), // logical channels. + 0, // Channel Name string index. + 0, // Terminal Name string index. + + // + // Audio Feature Unit Descriptor + // + 13, // The size of this descriptor. + USB_DTYPE_CS_INTERFACE, // Interface descriptor is class specific. + USB_ACDSTYPE_FEATURE_UNIT, // Descriptor sub-type is FEATURE_UNIT. + AUDIO_CONTROL_ID, // Unit ID for this interface. + AUDIO_IN_TERMINAL_ID, // ID of the Unit or Terminal to which this + // Feature Unit is connected. + 2, // Size in bytes of an element of the + // bmaControls() array that follows. + // Master Mute control. + USBShort(USB_ACONTROL_MUTE), + // Left channel volume control. + USBShort(USB_ACONTROL_VOLUME), + // Right channel volume control. + USBShort(USB_ACONTROL_VOLUME), + 0, // Feature unit string index. + + // + // Audio Output Terminal Descriptor. + // + 9, // The size of this descriptor. + USB_DTYPE_CS_INTERFACE, // Interface descriptor is class specific. + USB_ACDSTYPE_OUT_TERMINAL, // Descriptor sub-type is INPUT_TERMINAL. + AUDIO_OUT_TERMINAL_ID, // Terminal ID for this interface. + // Output type is a generic speaker. + USBShort(USB_ATTYPE_SPEAKER), + AUDIO_IN_TERMINAL_ID, // ID of the input terminal to which this + // output terminal is connected. + AUDIO_CONTROL_ID, // ID of the feature unit that this output + // terminal is connected to. + 0, // Output terminal string index. + +}; + +//***************************************************************************** +// +// The audio streaming interface descriptor. This describes the two valid +// interfaces for this class. The first interface has no endpoints and is used +// by host operating systems to put the device in idle mode, while the second +// is used when the audio device is active. +// +//***************************************************************************** +const unsigned char g_pAudioStreamInterface[] = +{ + // + // Vendor-specific Interface Descriptor. + // + 9, // Size of the interface descriptor. + USB_DTYPE_INTERFACE, // Type of this descriptor. + AUDIO_INTERFACE_OUTPUT, // The index for this interface. + 0, // The alternate setting for this interface. + 0, // The number of endpoints used by this + // interface. + USB_CLASS_AUDIO, // The interface class + USB_ASC_AUDIO_STREAMING, // The interface sub-class. + 0, // Unused must be 0. + 0, // The string index for this interface. + + // + // Vendor-specific Interface Descriptor. + // + 9, // Size of the interface descriptor. + USB_DTYPE_INTERFACE, // Type of this descriptor. + 1, // The index for this interface. + 1, // The alternate setting for this interface. + 1, // The number of endpoints used by this + // interface. + USB_CLASS_AUDIO, // The interface class + USB_ASC_AUDIO_STREAMING, // The interface sub-class. + 0, // Unused must be 0. + 0, // The string index for this interface. + + // + // Class specific Audio Streaming Interface descriptor. + // + 7, // Size of the interface descriptor. + USB_DTYPE_CS_INTERFACE, // Interface descriptor is class specific. + USB_ASDSTYPE_GENERAL, // General information. + AUDIO_IN_TERMINAL_ID, // ID of the terminal to which this streaming + // interface is connected. + 1, // One frame delay. + USBShort(USB_ADF_PCM), // + + // + // Format type Audio Streaming descriptor. + // + 11, // Size of the interface descriptor. + USB_DTYPE_CS_INTERFACE, // Interface descriptor is class specific. + USB_ASDSTYPE_FORMAT_TYPE, // Audio Streaming format type. + USB_AF_TYPE_TYPE_I, // Type I audio format type. + 2, // Two audio channels. + 2, // Two bytes per audio sub-frame. + 16, // 16 bits per sample. + 1, // One sample rate provided. + USB3Byte(48000), // Only 48000 sample rate supported. + + // + // Endpoint Descriptor + // + 9, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + // OUT endpoint with address + // ISOC_OUT_ENDPOINT. + USB_EP_DESC_OUT | USB_EP_TO_INDEX(ISOC_OUT_ENDPOINT), + USB_EP_ATTR_ISOC | // Endpoint is an adaptive isochronous data + USB_EP_ATTR_ISOC_ADAPT | // endpoint. + USB_EP_ATTR_USAGE_DATA, + USBShort(ISOC_OUT_EP_MAX_SIZE), // The maximum packet size. + 1, // The polling interval for this endpoint. + 0, // Refresh is unused. + 0, // Synch endpoint address. + + // + // Audio Streaming Isochronous Audio Data Endpoint Descriptor + // + 7, // The size of the descriptor. + USB_ACSDT_ENDPOINT, // Audio Class Specific Endpoint Descriptor. + USB_ASDSTYPE_GENERAL, // This is a general descriptor. + USB_EP_ATTR_ACG_SAMPLING, // Sampling frequency is supported. + USB_EP_LOCKDELAY_UNDEF, // Undefined lock delay units. + USBShort(0), // No lock delay. +}; + +//***************************************************************************** +// +// The audio device configuration descriptor is defined as three sections, +// one containing just the 9 byte USB configuration descriptor. The second +// holds the audio streaming interface and the third holds the audio control +// interface. +// +//***************************************************************************** +const tConfigSection g_sAudioConfigSection = +{ + sizeof(g_pAudioDescriptor), + g_pAudioDescriptor +}; + +const tConfigSection g_sAudioStreamInterfaceSection = +{ + sizeof(g_pAudioStreamInterface), + g_pAudioStreamInterface +}; + +const tConfigSection g_sAudioControlInterfaceSection = +{ + sizeof(g_pAudioControlInterface), + g_pAudioControlInterface +}; + +//***************************************************************************** +// +// This array lists all the sections that must be concatenated to make a +// single, complete audio device configuration descriptor. +// +//***************************************************************************** +const tConfigSection *g_psAudioSections[] = +{ + &g_sAudioConfigSection, + &g_sIADAudioConfigSection, + &g_sAudioControlInterfaceSection, + &g_sAudioStreamInterfaceSection +}; + +#define NUM_AUDIO_SECTIONS (sizeof(g_psAudioSections) / \ + sizeof(tConfigSection *)) + +//***************************************************************************** +// +// The header for the single configuration we support. This is the root of +// the data structure that defines all the bits and pieces that are pulled +// together to generate the configuration descriptor. +// +//***************************************************************************** +const tConfigHeader g_sAudioConfigHeader = +{ + NUM_AUDIO_SECTIONS, + g_psAudioSections +}; + +//***************************************************************************** +// +// Configuration Descriptor. +// +//***************************************************************************** +const tConfigHeader * const g_pAudioConfigDescriptors[] = +{ + &g_sAudioConfigHeader +}; + +//***************************************************************************** +// +// Various internal handlers needed by this class. +// +//***************************************************************************** +static void HandleDisconnect(void *pvInstance); +static void InterfaceChange(void *pvInstance, unsigned char ucInterface, + unsigned char ucAlternateSetting); +static void ConfigChangeHandler(void *pvInstance, unsigned long ulValue); +static void DataReceived(void *pvInstance, unsigned long ulInfo); +static void HandleEndpoints(void *pvInstance, unsigned long ulStatus); +static void HandleRequests(void *pvInstance, tUSBRequest *pUSBRequest); +static void HandleDevice(void *pvInstance, unsigned long ulRequest, + void *pvRequestData); + +//***************************************************************************** +// +// The FIFO configuration for USB audio device class. +// +//***************************************************************************** +const tFIFOConfig g_sUSBAudioFIFOConfig = +{ + // + // IN endpoints. + // + { + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN } + }, + + // + // OUT endpoints. + // + { + { false, USB_EP_DEV_OUT | USB_EP_DMA_MODE_1 | USB_EP_AUTO_CLEAR }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT } + }, +}; + +//***************************************************************************** +// +// The device information structure for the USB Audio device. +// +//***************************************************************************** +tDeviceInfo g_sAudioDeviceInfo = +{ + // + // Device event handler callbacks. + // + { + // + // GetDescriptor + // + 0, + + // + // RequestHandler + // + HandleRequests, + + // + // InterfaceChange + // + InterfaceChange, + + // + // ConfigChange + // + ConfigChangeHandler, + + // + // DataReceived + // + DataReceived, + + // + // DataSentCallback + // + 0, + + // + // ResetHandler + // + 0, + + // + // SuspendHandler + // + 0, + + // + // ResumeHandler + // + 0, + + // + // DisconnectHandler + // + HandleDisconnect, + + // + // EndpointHandler + // + HandleEndpoints, + + // + // Device handler + // + HandleDevice + }, + g_pAudioDeviceDescriptor, + g_pAudioConfigDescriptors, + 0, + 0, + &g_sUSBAudioFIFOConfig +}; + +//***************************************************************************** +// +// This function is called to handle data being received back from the host so +// that the application callback can be called when the new data is ready. +// +//***************************************************************************** +static void +DataReceived(void *pvInstance, unsigned long ulInfo) +{ + tAudioInstance *psInst; + const tUSBDAudioDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDAudioDevice *)pvInstance; + + // + // Make a copy of this pointer for ease of use in this function. + // + psInst = psDevice->psPrivateData; + + // + // If there is an update pending and the request was to set a current + // value then check which value was set. + // + if(psInst->usUpdate && (psInst->ucRequest == USB_AC_SET_CUR)) + { + // + // Only handling interface requests. + // + if((psInst->usRequestType & USB_RTYPE_RECIPIENT_M) == + USB_RTYPE_INTERFACE) + { + if(psInst->usUpdate == VOLUME_CONTROL) + { + // + // Inform the callback of the new volume. + // + psDevice->pfnCallback(0, USBD_AUDIO_EVENT_VOLUME, + psInst->sVolume, 0); + } + else if(psDevice->psPrivateData->usUpdate == MUTE_CONTROL) + { + // + // Inform the callback of the new data. + // + psDevice->pfnCallback(0, USBD_AUDIO_EVENT_MUTE, psInst->ucMute, + 0); + } + } + psInst->usUpdate = 0; + } +} + +//***************************************************************************** +// +// This function is called to handle the interrupts on the isochronous endpoint +// for the audio device class. +// +//***************************************************************************** +static void +HandleEndpoints(void *pvInstance, unsigned long ulStatus) +{ + unsigned long ulEPStatus; + tAudioInstance *psInst; + unsigned char *pucData; + const tUSBDAudioDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDAudioDevice *)pvInstance; + + // + // Make a copy of this pointer for ease of use later in this function. + // + psInst = psDevice->psPrivateData; + + // + // Make sure this was for the isochronous out endpoint. + // + if((psInst->sBuffer.pvData != 0) && + (MAP_uDMAChannelModeGet(psInst->ucOUTDMA) == UDMA_MODE_STOP)) + { + // + // Save the pointer to the data buffer. + // + pucData = psInst->sBuffer.pvData; + + // + // Clear out the buffer pointer to indicate it is no longer in use. + // + psInst->sBuffer.pvData = 0; + + // + // Inform the callback of the new data. + // + psInst->sBuffer.pfnCallback(pucData, psInst->sBuffer.ulSize, + USBD_AUDIO_EVENT_DATAOUT); + + // + // Read out the current endpoint status. + // + ulEPStatus = MAP_USBEndpointStatus(USB0_BASE, psInst->ucOUTEndpoint); + + // + // Acknowledge that the data was read, this will not cause a bus + // acknowledgment. + // + MAP_USBDevEndpointDataAck(USB0_BASE, psInst->ucOUTEndpoint, 0); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(USB0_BASE, psInst->ucOUTEndpoint, + ulEPStatus); + } +} + +//***************************************************************************** +// +// Device instance specific handler. +// +//***************************************************************************** +static void +HandleDevice(void *pvInstance, unsigned long ulRequest, void *pvRequestData) +{ + tAudioInstance *psInst; + unsigned char *pucData; + + // + // Create the serial instance data. + // + psInst = ((tUSBDAudioDevice *)pvInstance)->psPrivateData; + + // + // Create the char array used by the events supported by the USB CDC + // serial class. + // + pucData = (unsigned char *)pvRequestData; + + switch(ulRequest) + { + // + // This was an interface change event. + // + case USB_EVENT_COMP_IFACE_CHANGE: + { + // + // Save the change to the appropriate interface number. + // + if(pucData[0] == AUDIO_INTERFACE_CONTROL) + { + psInst->ucInterfaceControl = pucData[1]; + } + else if(pucData[0] == AUDIO_INTERFACE_OUTPUT) + { + psInst->ucInterfaceAudio = pucData[1]; + } + break; + } + + // + // This was an endpoint change event. + // + case USB_EVENT_COMP_EP_CHANGE: + { + // + // Determine if this is an IN or OUT endpoint that has changed. + // + if((pucData[0] & USB_EP_DESC_IN) == 0) + { + // + // Extract the new endpoint number without the DIR bit. + // + psInst->ucOUTEndpoint = INDEX_TO_USB_EP(pucData[1] & 0x7f); + + // + // Extract the new DMA channel. + // + psInst->ucOUTDMA = UDMA_CHANNEL_USBEP1RX + + (((pucData[1] & 0x7f) - 1) * 2); + + // + // Basic configuration for DMA on the OUT endpoint. + // + MAP_uDMAChannelControlSet(psInst->ucOUTDMA, + (UDMA_SIZE_32 | UDMA_SRC_INC_NONE| + UDMA_DST_INC_32 | UDMA_ARB_16)); + + // + // Select this channel for this endpoint, this only affects + // devices that have this feature. + // + MAP_USBEndpointDMAChannel(USB0_BASE, psInst->ucOUTEndpoint, + psInst->ucOUTDMA); + } + break; + } + + // + // Handle class specific reconfiguring of the configuration descriptor + // once the composite class has built the full descriptor. + // + case USB_EVENT_COMP_CONFIG: + { + // + // This sets the bFirstInterface of the Interface Association + // descriptor to the first interface which is the control + // interface used by this instance. + // + pucData[2] = psInst->ucInterfaceControl; + + break; + } + + default: + { + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device is +// disconnected from the host. +// +//***************************************************************************** +static void +HandleDisconnect(void *pvInstance) +{ + const tUSBDAudioDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDAudioDevice *)pvInstance; + + // + // Inform the application that the device has been disconnected. + // + psDevice->pfnCallback(0, USB_EVENT_DISCONNECTED, 0, 0); +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device +// interface changes. This occurs when the audio device transitions between +// being active and inactive. Interface AUDIO_INTERFACE_CONTROL is the +// inactive interface that has no endpoints, while interface +// AUDIO_INTERFACE_AUDIO has the single Isochronous OUT endpoint. +// +//***************************************************************************** +static void +InterfaceChange(void *pvInstance, unsigned char ucInterface, + unsigned char ucAlternateSetting) +{ + const tUSBDAudioDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDAudioDevice *)pvInstance; + + // + // Check which interface to change into. + // + if(ucAlternateSetting == 0) + { + // + // Alternate setting 0 is an inactive state. + // + if(psDevice->pfnCallback) + { + psDevice->pfnCallback(0, USBD_AUDIO_EVENT_IDLE, 0, 0); + } + } + else + { + // + // Alternate setting 1 is the active state. + // + if(psDevice->pfnCallback) + { + psDevice->pfnCallback(0, USBD_AUDIO_EVENT_ACTIVE, 0, 0); + } + + // + // Enable uDMA on the endpoint now that the active configuration + // has been selected. + // + MAP_USBEndpointDMAEnable(USB0_BASE, + psDevice->psPrivateData->ucOUTEndpoint, + USB_EP_DEV_OUT); + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device +// configuration changes. +// +//***************************************************************************** +static void +ConfigChangeHandler(void *pvInstance, unsigned long ulValue) +{ + const tUSBDAudioDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDAudioDevice *)pvInstance; + + // + // If we have a control callback, let the client know we are open for + // business. + // + if(psDevice->pfnCallback) + { + // + // Pass the connected event to the client. + // + psDevice->pfnCallback(pvInstance, USB_EVENT_CONNECTED, 0, 0); + } +} + +//***************************************************************************** +// +//! This function should be called once for the audio class device to +//! initialized basic operation and prepare for enumeration. +//! +//! \param ulIndex is the index of the USB controller to initialize for +//! audio class device operation. +//! \param psDevice points to a structure containing parameters customizing +//! the operation of the audio device. +//! +//! In order for an application to initialize the USB audio device class, it +//! must first call this function with the a valid audio device class structure +//! in the \e psDevice parameter. This allows this function to initialize the +//! USB controller and device code to be prepared to enumerate and function as +//! a USB audio device. +//! +//! This function returns a void pointer that must be passed in to all other +//! APIs used by the audio class. +//! +//! See the documentation on the tUSBDAudioDevice 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 * +USBDAudioInit(unsigned long ulIndex, const tUSBDAudioDevice *psDevice) +{ + // + // Check parameter validity. + // + ASSERT(ulIndex == 0); + ASSERT(psDevice); + ASSERT(psDevice->ppStringDescriptors); + ASSERT(psDevice->psPrivateData); + + USBDAudioCompositeInit(ulIndex, psDevice); + + // + // All is well so now pass the descriptors to the lower layer and put + // the bulk device on the bus. + // + USBDCDInit(ulIndex, psDevice->psPrivateData->psDevInfo); + + // + // Basic configuration for DMA on the OUT endpoint. + // + MAP_uDMAChannelControlSet(psDevice->psPrivateData->ucOUTDMA, + (UDMA_SIZE_32 | UDMA_SRC_INC_NONE| + UDMA_DST_INC_32 | UDMA_ARB_16)); + + // + // Select this channel for this endpoint, this only affects devices that + // have this feature. + // + MAP_USBEndpointDMAChannel(USB0_BASE, psDevice->psPrivateData->ucOUTEndpoint, + psDevice->psPrivateData->ucOUTDMA); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psDevice); +} + +//***************************************************************************** +// +//! This function should be called once for the audio class device to +//! initialized basic operation and prepare for enumeration. +//! +//! \param ulIndex is the index of the USB controller to initialize for +//! audio class device operation. +//! \param psDevice points to a structure containing parameters customizing +//! the operation of the audio device. +//! +//! In order for an application to initialize the USB audio device class, it +//! must first call this function with the a valid audio device class structure +//! in the \e psDevice parameter. This allows this function to initialize the +//! USB controller and device code to be prepared to enumerate and function as +//! a USB audio device. +//! +//! This function returns a void pointer that must be passed in to all other +//! APIs used by the audio class. +//! +//! See the documentation on the tUSBDAudioDevice 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 * +USBDAudioCompositeInit(unsigned long ulIndex, const tUSBDAudioDevice *psDevice) +{ + tAudioInstance *psInst; + tDeviceDescriptor *psDevDesc; + + // + // Check parameter validity. + // + ASSERT(ulIndex == 0); + ASSERT(psDevice); + ASSERT(psDevice->ppStringDescriptors); + ASSERT(psDevice->psPrivateData); + + // + // Initialize the workspace in the passed instance structure. + // + psInst = psDevice->psPrivateData; + psInst->psConfDescriptor = (tConfigDescriptor *)g_pAudioDescriptor; + psInst->psDevInfo = &g_sAudioDeviceInfo; + psInst->ulUSBBase = USB0_BASE; + + // + // The Control interface is at index 0. + // + psInst->ucInterfaceControl = AUDIO_INTERFACE_CONTROL; + + // + // The Audio interface is at index 1. + // + psInst->ucInterfaceAudio = AUDIO_INTERFACE_OUTPUT; + + // + // Set the default Isochronous OUT endpoint. + // + psInst->ucOUTEndpoint = ISOC_OUT_ENDPOINT; + psInst->ucOUTDMA = ISOC_OUT_DMA_CHANNEL; + + // + // Set the initial buffer to null. + // + psInst->sBuffer.pvData = 0; + + // + // Save the volume settings. + // + psInst->sVolumeMax = psDevice->sVolumeMax; + psInst->sVolumeMin = psDevice->sVolumeMin; + psInst->sVolumeStep = psDevice->sVolumeStep; + + // + // No update pending to any command. + // + psInst->usUpdate = 0; + + // + // Fix up the device descriptor with the client-supplied values. + // + psDevDesc = (tDeviceDescriptor *)psInst->psDevInfo->pDeviceDescriptor; + psDevDesc->idVendor = psDevice->usVID; + psDevDesc->idProduct = psDevice->usPID; + // + // Fix up the configuration descriptor with client-supplied values. + // + psInst->psConfDescriptor->bmAttributes = psDevice->ucPwrAttributes; + psInst->psConfDescriptor->bMaxPower = + (unsigned char)(psDevice->usMaxPowermA / 2); + + // + // Plug in the client's string stable to the device information + // structure. + // + psInst->psDevInfo->ppStringDescriptors = psDevice->ppStringDescriptors; + psInst->psDevInfo->ulNumStringDescriptors = + psDevice->ulNumStringDescriptors; + psInst->psDevInfo->pvInstance = (void *)psDevice; + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psDevice); +} + +//***************************************************************************** +// +//! Shuts down the audio device. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDAudioInit(). +//! +//! This function terminates audio interface for the instance supplied. This +//! function should not be called if the audio device is part of a composite +//! device and instead the USBDCompositeTerm() function should be called for +//! the full composite device. +//! Following this call, the \e pvInstance instance should not me used in any +//! other calls. +//! +//! \return None. +// +//***************************************************************************** +void +USBDAudioTerm(void *pvInstance) +{ + ASSERT(pvInstance != 0); + + // + // Cleanly exit device mode. + // + USBDCDTerm(0); +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever a non-standard +// request is received. +// +// \param pvInstance is the instance data for this request. +// \param pUSBRequest points to the request received. +// +// This call parses the provided request structure to the type of request and +// will respond to all commands that are understood by the class. +// +// \return None. +// +//***************************************************************************** +static void +HandleRequests(void *pvInstance, tUSBRequest *pUSBRequest) +{ + unsigned long ulControl; + unsigned long ulRecipient; + unsigned long ulStall; + tAudioInstance *psInst; + const tUSBDAudioDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDAudioDevice *)pvInstance; + + // + // Make a copy of this pointer for ease of use in this function. + // + psInst = psDevice->psPrivateData; + + // + // Make sure to acknowledge that the data was read, this will not send and + // ACK that has already been done at this point. This just tells the + // hardware that the data was read. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false); + + // + // Don't stall by default. + // + ulStall = 0; + + // + // Get the request type. + // + ulRecipient = pUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M; + + // + // Save the request type and request value. + // + psInst->usRequestType = pUSBRequest->bmRequestType; + psInst->ucRequest = pUSBRequest->bRequest; + + // + // Check if this is an endpoint request to the audio streaming endpoint. + // + if((ulRecipient == USB_RTYPE_ENDPOINT) && + (pUSBRequest->wIndex == USB_EP_TO_INDEX(psInst->ucOUTEndpoint))) + { + // + // Determine the type of request. + // + switch(psInst->ucRequest) + { + case USB_AC_SET_CUR: + { + // + // Handle retrieving the sample rate. + // + if(pUSBRequest->wValue == SAMPLING_FREQ_CONTROL) + { + // + // Retrieve the requested sample rate. + // + USBDCDRequestDataEP0(0, + (unsigned char *)&psInst->ulSampleRate, + 3); + + // + // Save what we are updating. + // + psInst->usUpdate = SAMPLING_FREQ_CONTROL; + } + break; + } + case USB_AC_GET_CUR: + { + // + // Handle retrieving the sample rate. + // + if(pUSBRequest->wValue == SAMPLING_FREQ_CONTROL) + { + // + // Send back the current sample rate. + // + USBDCDSendDataEP0(0, + (unsigned char *)&psInst->ulSampleRate, + 3); + } + break; + } + default: + { + // + // Stall on unknown commands. + // + ulStall = 1; + break; + } + } + } + else if(ulRecipient == USB_RTYPE_INTERFACE) + { + // + // Make sure the request was for the control interface. + // + if((unsigned char)pUSBRequest->wIndex != psInst->ucInterfaceControl) + { + return; + } + + // + // Extract the control value from the message. + // + ulControl = pUSBRequest->wValue & USB_CS_CONTROL_M; + + // + // Handle an audio control request to the feature control unit. + // + if((AUDIO_CONTROL_ID << 8) == (pUSBRequest->wIndex & USB_CS_CONTROL_M)) + { + // + // Determine the type of request. + // + switch(psInst->ucRequest) + { + case USB_AC_GET_MAX: + { + if(ulControl == VOLUME_CONTROL) + { + // + // Return the maximum volume setting. + // + USBDCDSendDataEP0(0, + (unsigned char *)&psInst->sVolumeMax, + 2); + } + else + { + // + // Stall on unknown commands. + // + ulStall = 1; + } + break; + } + case USB_AC_GET_MIN: + { + if(ulControl == VOLUME_CONTROL) + { + // + // Return the minimum volume setting. + // + USBDCDSendDataEP0(0, + (unsigned char *)&psInst->sVolumeMin, + 2); + } + else + { + // + // Stall on unknown commands. + // + ulStall = 1; + } + break; + } + case USB_AC_GET_RES: + { + if(ulControl == VOLUME_CONTROL) + { + // + // Return the volume step setting. + // + USBDCDSendDataEP0(0, + (unsigned char *)&psInst->sVolumeStep, + 2); + } + else + { + // + // Stall on unknown commands. + // + ulStall = 1; + } + break; + } + case USB_AC_GET_CUR: + { + if(ulControl == VOLUME_CONTROL) + { + // + // Send back the current volume level. + // + USBDCDSendDataEP0(0, + (unsigned char *)&psInst->sVolume, + 2); + } + else if(ulControl == MUTE_CONTROL) + { + // + // Send back the current mute value. + // + USBDCDSendDataEP0(0, + (unsigned char *)&psInst->ucMute, 1); + } + else + { + // + // Stall on unknown commands. + // + ulStall = 1; + } + break; + } + case USB_AC_SET_CUR: + { + if(ulControl == VOLUME_CONTROL) + { + // + // Read the new volume level. + // + USBDCDRequestDataEP0(0, + (unsigned char *)&psInst->sVolume, + 2); + + // + // Save what we are updating. + // + psInst->usUpdate = VOLUME_CONTROL; + } + else if(ulControl == MUTE_CONTROL) + { + // + // Read the new mute setting. + // + USBDCDRequestDataEP0(0, + (unsigned char *)&psInst->ucMute, + 1); + + // + // Save what we are updating. + // + psInst->usUpdate = MUTE_CONTROL; + } + else + { + // + // Stall on unknown commands. + // + ulStall = 1; + } + break; + } + case USB_AC_SET_RES: + { + if(ulControl == VOLUME_CONTROL) + { + // + // Read the new volume step setting. + // + USBDCDRequestDataEP0(0, + (unsigned char *)&psInst->sVolumeStep, 2); + + // + // Save what we are updating. + // + psInst->usUpdate = VOLUME_CONTROL; + } + else + { + // + // Stall on unknown commands. + // + ulStall = 1; + } + break; + } + default: + { + // + // Stall on unknown commands. + // + ulStall = 1; + break; + } + } + } + } + + // + // Stall on all unknown commands. + // + if(ulStall) + { + USBDCDStallEP0(0); + } +} + +//***************************************************************************** +// +//! This function is used to supply buffers to the audio class to be filled +//! from the USB host device. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDAudioInit() or USBDAudioInitComposite(). +//! \param pvBuffer is a pointer to the buffer to fill with audio data. +//! \param ulSize is the size in bytes of the buffer pointed to by the pvBuffer +//! parameter. +//! \param pfnCallback is a callback that will provide notification when this +//! buffer has valid data. +//! +//! This function fills the buffer pointed to by the \e pvBuffer parameter with +//! at most \e ulSize one packet of data from the host controller. The ulSize +//! has a minimum value of \b ISOC_OUT_EP_MAX_SIZE since each USB packet can be +//! at most \b ISOC_OUT_EP_MAX_SIZE bytes in size. Since the audio data may +//! not be received in amounts that evenly fit in the buffer provided, the +//! buffer may not be completely filled. The \e pfnCallback function will +//! provide the amount of valid data that was actually stored in the buffer +//! provided. The function will return zero if the buffer could be scheduled +//! to be filled, otherwise the function will return a non-zero value if there +//! was some reason that the buffer could not be added. +//! +//! \return Returns 0 to indicate success any other value indicates that the +//! buffer will not be filled. +// +//***************************************************************************** +long +USBAudioBufferOut(void *pvInstance, void *pvBuffer, unsigned long ulSize, + tUSBAudioBufferCallback pfnCallback) +{ + tAudioInstance *psInst; + const tUSBDAudioDevice *psDevice; + + // + // Make sure we were not passed NULL pointers. + // + ASSERT(pvInstance != 0); + ASSERT(pvBuffer != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDAudioDevice *)pvInstance; + + // + // Buffer must be at least one packet in size. + // + ASSERT(ulSize >= ISOC_OUT_EP_MAX_SIZE); + ASSERT(pfnCallback); + + // + // Create a pointer of the correct type from the private pointer. + // + psInst = psDevice->psPrivateData; + + // + // Initialize the buffer instance. + // + psInst->sBuffer.pvData = pvBuffer; + psInst->sBuffer.ulSize = ulSize; + psInst->sBuffer.ulNumBytes = 0; + psInst->sBuffer.pfnCallback = pfnCallback; + + // + // Configure and enable DMA for the OUT transfer. + // + MAP_uDMAChannelTransferSet(psInst->ucOUTDMA, UDMA_MODE_BASIC, + (void *)USBFIFOAddrGet(USB0_BASE, + psInst->ucOUTEndpoint), + psInst->sBuffer.pvData, ulSize >> 2); + + // + // Start the DMA transfer. + // + MAP_uDMAChannelEnable(psInst->ucOUTDMA); + + return(0); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + diff --git a/usblib/device/usbdaudio.h b/usblib/device/usbdaudio.h new file mode 100644 index 0000000..4406597 --- /dev/null +++ b/usblib/device/usbdaudio.h @@ -0,0 +1,362 @@ +//***************************************************************************** +// +// usbdaudio.h - USB audio device class driver. +// +// Copyright (c) 2009-2012 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#ifndef __USBDAUDIO_H__ +#define __USBDAUDIO_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup audio_device_class_api +//! @{ +// +//***************************************************************************** + +typedef void (* tUSBAudioBufferCallback)(void *pvBuffer, unsigned long ulParam, + unsigned long ulEvent); + +//***************************************************************************** +// +// PRIVATE +// +// This structure defines the private instance data and state variables for the +// audio device class. The memory for this structure is pointed to by +// the psPrivateData field in the tUSBDAudioDevice structure passed on +// USBDAudioInit() and should not be modified by any code outside of the audio +// device. +// +//***************************************************************************** +typedef struct +{ + unsigned long ulUSBBase; + tDeviceInfo *psDevInfo; + tConfigDescriptor *psConfDescriptor; + + // + // The maximum volume expressed as an 8.8 signed value. + // + short sVolumeMax; + + // + // The minimum volume expressed as an 8.8 signed value. + // + short sVolumeMin; + + // + // The minimum volume step expressed as an 8.8 signed value. + // + short sVolumeStep; + + struct + { + // + // Pointer to a buffer provided by caller. + // + void *pvData; + + // + // Size of the data area provided in pvData in bytes. + // + unsigned long ulSize; + + // + // Number of valid bytes copied into the pvData area. + // + unsigned long ulNumBytes; + + // + // The buffer callback for this function. + // + tUSBAudioBufferCallback pfnCallback; + } sBuffer; + + // + // Pending request type. + // + unsigned short usRequestType; + + // + // Pending request. + // + unsigned char ucRequest; + + // + // Pending update value. + // + unsigned short usUpdate; + + // + // Current Volume setting. + // + short sVolume; + + // + // Current Mute setting. + // + unsigned char ucMute; + + // + // Current Sample rate, this is not writable but the host will try. + // + unsigned long ulSampleRate; + + // + // The OUT endpoint in use by this instance. + // + unsigned char ucOUTEndpoint; + + // + // The OUT endpoint DMA channel in use by this instance. + // + unsigned char ucOUTDMA; + + // + // The control interface number associated with this instance. + // + unsigned char ucInterfaceControl; + + // + // The audio interface number associated with this instance. + // + unsigned char ucInterfaceAudio; +} +tAudioInstance; + +//***************************************************************************** +// +//! This is the size in bytes of the private data for the device audio class. +// +//***************************************************************************** +#define USB_AUDIO_INSTANCE_SIZE sizeof(tAudioInstance); + +//***************************************************************************** +// +//! The size of the memory that should be allocated to create a configuration +//! descriptor for a single instance of the USB Audio Device. +//! This does not include the configuration descriptor which is automatically +//! ignored by the composite device class. +// +// This value must be at least sizeof(g_pIADAudioDescriptor) + +// sizeof(g_pAudioControlInterface) + +// sizeof(g_sAudioStreamInterfaceSection) +// +//***************************************************************************** +#define COMPOSITE_DAUDIO_SIZE (8 + 52 + 52) + +//***************************************************************************** +// +//! The structure used by the application to define operating parameters for +//! the device audio class. +// +//***************************************************************************** +typedef struct +{ + // + //! The vendor ID that this device is to present in the device descriptor. + // + unsigned short usVID; + + // + //! The product ID that this device is to present in the device descriptor. + // + unsigned short usPID; + + // + //! 8 byte vendor string. + // + unsigned char pucVendor[8]; + + // + //! 16 byte vendor string. + // + unsigned char pucProduct[16]; + + // + //! 4 byte vendor string. + // + unsigned char pucVersion[4]; + + // + //! The maximum power consumption of the device, expressed in mA. + // + unsigned short usMaxPowermA; + + // + //! Indicates whether the device is self or bus-powered and whether or not + //! it supports remote wake up. Valid values are USB_CONF_ATTR_SELF_PWR or + //! USB_CONF_ATTR_BUS_PWR, optionally ORed with USB_CONF_ATTR_RWAKE. + // + unsigned char ucPwrAttributes; + + // + //! A pointer to the callback function which will be called to notify + //! the application of events relating to the operation of the audio + //! device. + // + tUSBCallback pfnCallback; + + // + //! A pointer to the string descriptor array for this device. This array + //! must contain the following string descriptor pointers in this order. + //! Language descriptor, Manufacturer name string (language 1), Product + //! name string (language 1), Serial number string (language 1), Audio + //! Interface description string (language 1), Configuration description + //! string (language 1). + //! + //! If supporting more than 1 language, the descriptor block (except for + //! string descriptor 0) must be repeated for each language defined in the + //! language descriptor. + //! + // + const unsigned char * const *ppStringDescriptors; + + // + //! The number of descriptors provided in the ppStringDescriptors + //! array. This must be 1 + ((5 + (number of strings)) * + //! (number of languages)). + // + unsigned long ulNumStringDescriptors; + + // + //! The maximum volume expressed as an 8.8 signed value. + // + short sVolumeMax; + + // + //! The minimum volume expressed as an 8.8 signed value. + // + short sVolumeMin; + + // + //! The minimum volume step expressed as an 8.8 signed value. + // + short sVolumeStep; + + // + //! A pointer to private instance data for the audio device. This memory + //! must remain accessible for as long as the audio device is in use and + //! must not be modified by any code outside the audio class driver. + // + tAudioInstance *psPrivateData; +} +tUSBDAudioDevice; + +//***************************************************************************** +// +// Audio specific device class driver events +// +//***************************************************************************** + +//***************************************************************************** +// +//! This USB audio event indicates that the device is connected but not active. +// +//***************************************************************************** +#define USBD_AUDIO_EVENT_IDLE (USBD_AUDIO_EVENT_BASE + 0) + +//***************************************************************************** +// +//! This USB audio event indicates that the device is connected and is now +//! active. +// +//***************************************************************************** +#define USBD_AUDIO_EVENT_ACTIVE (USBD_AUDIO_EVENT_BASE + 1) + +//***************************************************************************** +// +//! This USB audio event indicates that the device is returning a data buffer +//! provided by the USBAudioBufferOut() function back to the application with +//! valid audio data received from the USB host controller. The \e pvBuffer +//! parameter holds the pointer to the buffer with the new audio data and +//! the \e ulParam value holds the amount of valid data in bytes that are +//! contained in the \e pvBuffer parameter. +// +//***************************************************************************** +#define USBD_AUDIO_EVENT_DATAOUT (USBD_AUDIO_EVENT_BASE + 2) + +//***************************************************************************** +// +//! This USB audio event indicates that a volume change has occured. The +//! \e ulParam value contains a signed 8.8 fixed point value that represents +//! the current volume gain/attenuation in decibels(dB). The provided message +//! handler should be prepared to handle negative and positive values with the +//! value 0x8000 indicating maximum attenuation. The \e pvBuffer parameter +//! should be ignored. +// +//***************************************************************************** +#define USBD_AUDIO_EVENT_VOLUME (USBD_AUDIO_EVENT_BASE + 4) + +//***************************************************************************** +// +//! This USB audio event indicates that a mute request has occured. The +//! \e ulParam value will either be a 1 to indicate that the audio is now +//! muted, and a value of 0 indicates that the audio has been unmuted. +// +//***************************************************************************** +#define USBD_AUDIO_EVENT_MUTE (USBD_AUDIO_EVENT_BASE + 5) + +extern tDeviceInfo g_sAudioDeviceInfo; + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void *USBDAudioInit(unsigned long ulIndex, + const tUSBDAudioDevice *psAudioDevice); +extern void *USBDAudioCompositeInit(unsigned long ulIndex, + const tUSBDAudioDevice *psAudioDevice); +extern void USBDAudioTerm(void *pvInstance); +extern long USBAudioBufferOut(void *pvInstance, void *pvBuffer, + unsigned long ulSize, + tUSBAudioBufferCallback pfnCallback); + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/usblib/device/usbdbulk.c b/usblib/device/usbdbulk.c new file mode 100644 index 0000000..f623516 --- /dev/null +++ b/usblib/device/usbdbulk.c @@ -0,0 +1,1447 @@ +//***************************************************************************** +// +// usbdbulk.c - USB bulk device class driver. +// +// Copyright (c) 2008-2012 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbdbulk.h" +#include "usblib/usblibpriv.h" + +//***************************************************************************** +// +//! \addtogroup bulk_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// 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) + +//***************************************************************************** +// +// Flags that may appear in usDeferredOpFlags to indicate some operation that +// has been requested but could not be processed at the time it was received. +// Each deferred operation is defined as the bit number that should be set in +// tBulkInstance->usDeferredOpFlags to indicate that the operation is pending. +// +//***************************************************************************** +#define BULK_DO_PACKET_RX 5 + +//***************************************************************************** +// +// Macros to convert between USB controller base address and an index. These +// are currently trivial but are included to allow for the possibility of +// supporting more than one controller in the future. +// +//***************************************************************************** +#define USB_BASE_TO_INDEX(BaseAddr) (0) +#define USB_INDEX_TO_BASE(Index) (USB0_BASE) + +//***************************************************************************** +// +// 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 used for serial data +// transmission and reception and the associated FIFO sizes to set aside +// for each endpoint. +// +//***************************************************************************** +#define DATA_IN_EP_FIFO_SIZE USB_FIFO_SZ_64 +#define DATA_OUT_EP_FIFO_SIZE USB_FIFO_SZ_64 + +#define DATA_IN_EP_MAX_SIZE USB_FIFO_SZ_TO_BYTES(DATA_IN_EP_FIFO_SIZE) +#define DATA_OUT_EP_MAX_SIZE USB_FIFO_SZ_TO_BYTES(DATA_IN_EP_FIFO_SIZE) + +//***************************************************************************** +// +// Device Descriptor. This is stored in RAM to allow several fields to be +// changed at runtime based on the client's requirements. +// +//***************************************************************************** +unsigned char g_pBulkDeviceDescriptor[] = +{ + 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) + USB_CLASS_VEND_SPECIFIC, // USB Device Class + 0, // USB Device Sub-class + 0, // USB Device protocol + 64, // Maximum packet size for default pipe. + USBShort(0), // Vendor ID (VID). + USBShort(0), // Product ID (PID). + USBShort(0x100), // Device Version BCD. + 1, // Manufacturer string identifier. + 2, // Product string identifier. + 3, // Product serial number. + 1 // Number of configurations. +}; + +//***************************************************************************** +// +// Bulk 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. +// +//***************************************************************************** +unsigned char g_pBulkDescriptor[] = +{ + // + // 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. + 5, // The string identifier that describes this + // configuration. + USB_CONF_ATTR_SELF_PWR, // Bus Powered, Self Powered, remote wake up. + 250, // The maximum power in 2mA increments. +}; + +//***************************************************************************** +// +// The remainder of the configuration descriptor is stored in flash since we +// don't need to modify anything in it at runtime. +// +//***************************************************************************** +const unsigned char g_pBulkInterface[] = +{ + // + // 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_VEND_SPECIFIC, // The interface class + 0, // The interface sub-class. + 0, // The interface protocol for the sub-class + // specified above. + 4, // The string index for this interface. + + // + // Endpoint Descriptor + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_IN | USB_EP_TO_INDEX(DATA_IN_ENDPOINT), + USB_EP_ATTR_BULK, // Endpoint is a bulk endpoint. + USBShort(DATA_IN_EP_MAX_SIZE), // The maximum packet size. + 0, // The polling interval for this endpoint. + + // + // Endpoint Descriptor + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_OUT | USB_EP_TO_INDEX(DATA_OUT_ENDPOINT), + USB_EP_ATTR_BULK, // Endpoint is a bulk endpoint. + USBShort(DATA_OUT_EP_MAX_SIZE), // The maximum packet size. + 0, // The polling interval for this endpoint. +}; + +//***************************************************************************** +// +// The serial config 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_sBulkConfigSection = +{ + sizeof(g_pBulkDescriptor), + g_pBulkDescriptor +}; + +const tConfigSection g_sBulkInterfaceSection = +{ + sizeof(g_pBulkInterface), + g_pBulkInterface +}; + +//***************************************************************************** +// +// This array lists all the sections that must be concatenated to make a +// single, complete bulk device configuration descriptor. +// +//***************************************************************************** +const tConfigSection *g_psBulkSections[] = +{ + &g_sBulkConfigSection, + &g_sBulkInterfaceSection +}; + +#define NUM_BULK_SECTIONS (sizeof(g_psBulkSections) / \ + sizeof(tConfigSection *)) + +//***************************************************************************** +// +// The header for the single configuration we support. This is the root of +// the data structure that defines all the bits and pieces that are pulled +// together to generate the configuration descriptor. +// +//***************************************************************************** +const tConfigHeader g_sBulkConfigHeader = +{ + NUM_BULK_SECTIONS, + g_psBulkSections +}; + +//***************************************************************************** +// +// Configuration Descriptor. +// +//***************************************************************************** +const tConfigHeader * const g_pBulkConfigDescriptors[] = +{ + &g_sBulkConfigHeader +}; + +//***************************************************************************** +// +// Forward references for device handler callbacks +// +//***************************************************************************** +static void HandleConfigChange(void *pvInstance, unsigned long ulInfo); +static void HandleDisconnect(void *pvInstance); +static void HandleEndpoints(void *pvInstance, unsigned long ulStatus); +static void HandleSuspend(void *pvInstance); +static void HandleResume(void *pvInstance); +static void HandleDevice(void *pvInstance, unsigned long ulRequest, + void *pvRequestData); + +//***************************************************************************** +// +// The device information structure for the USB serial device. +// +//***************************************************************************** +tDeviceInfo g_sBulkDeviceInfo = +{ + // + // Device event handler callbacks. + // + { + 0, // GetDescriptor + 0, // RequestHandler + 0, // InterfaceChange + HandleConfigChange, // ConfigChange + 0, // DataReceived + 0, // DataSentCallback + 0, // ResetHandler + HandleSuspend, // SuspendHandler + HandleResume, // ResumeHandler + HandleDisconnect, // DisconnectHandler + HandleEndpoints, // EndpointHandler + HandleDevice // Device handler. + }, + g_pBulkDeviceDescriptor, + g_pBulkConfigDescriptors, + 0, // Will be completed during USBDBulkInit(). + 0, // Will be completed during USBDBulkInit(). + &g_sUSBDefaultFIFOConfig +}; + +//***************************************************************************** +// +// Set or clear deferred operation flags in an "atomic" manner. +// +// \param pusDeferredOp points to the flags variable which is to be modified. +// \param usBit indicates which bit number is to be set or cleared. +// \param bSet indicates the state that the flag must be set to. If \b true, +// the flag is set, if \b false, the flag is cleared. +// +// This function safely sets or clears a bit in a flag variable. The operation +// makes use of bitbanding to ensure that the operation is atomic (no read- +// modify-write is required). +// +// \return None. +// +//***************************************************************************** +static void +SetDeferredOpFlag(volatile unsigned short *pusDeferredOp, unsigned short usBit, + tBoolean bSet) +{ + // + // Set the flag bit to 1 or 0 using a bitband access. + // + HWREGBITH(pusDeferredOp, usBit) = bSet ? 1 : 0; +} + +//***************************************************************************** +// +// Receives notifications related to data received from the host. +// +// \param psDevice is the device instance whose endpoint is to be processed. +// \param ulStatus is the USB interrupt status that caused this function to +// be called. +// +// This function is called from HandleEndpoints for all interrupts signaling +// the arrival of data on the bulk OUT endpoint (in other words, whenever the +// host has sent us a packet of data). We inform the client that a packet +// is available and, on return, check to see if the packet has been read. If +// not, we schedule another notification to the client for a later time. +// +// \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +static tBoolean +ProcessDataFromHost(const tUSBDBulkDevice *psDevice, unsigned long ulStatus) +{ + unsigned long ulEPStatus; + unsigned long ulSize; + tBulkInstance *psInst; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateBulkData; + + // + // Get the endpoint status to see why we were called. + // + ulEPStatus = MAP_USBEndpointStatus(USB0_BASE, psInst->ucOUTEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(USB0_BASE, psInst->ucOUTEndpoint, ulEPStatus); + + // + // Has a packet been received? + // + if(ulEPStatus & USB_DEV_RX_PKT_RDY) + { + // + // Set the flag we use to indicate that a packet read is pending. This + // will be cleared if the packet is read. If the client doesn't read + // the packet in the context of the USB_EVENT_RX_AVAILABLE callback, + // the event will be signaled later during tick processing. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, BULK_DO_PACKET_RX, true); + + // + // How big is the packet we've just been sent? + // + ulSize = MAP_USBEndpointDataAvail(psInst->ulUSBBase, + psInst->ucOUTEndpoint); + + // + // The receive channel is not blocked so let the caller know + // that a packet is waiting. The parameters are set to indicate + // that the packet has not been read from the hardware FIFO yet. + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, + USB_EVENT_RX_AVAILABLE, ulSize, + (void *)0); + } + else + { + // + // No packet was received. Some error must have been reported. Check + // and pass this on to the client if necessary. + // + if(ulEPStatus & USB_RX_ERROR_FLAGS) + { + // + // This is an error we report to the client so... + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, + USB_EVENT_ERROR, + (ulEPStatus & USB_RX_ERROR_FLAGS), + (void *)0); + } + return(false); + } + + return(true); +} + +//***************************************************************************** +// +// Receives notifications related to data sent to the host. +// +// \param psDevice is the device instance whose endpoint is to be processed. +// \param ulStatus is the USB interrupt status that caused this function to +// be called. +// +// This function is called from HandleEndpoints for all interrupts originating +// from the bulk IN endpoint (in other words, whenever data has been +// transmitted to the USB host). We examine the cause of the interrupt and, +// if due to completion of a transmission, notify the client. +// +// \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +static tBoolean +ProcessDataToHost(const tUSBDBulkDevice *psDevice, unsigned long ulStatus) +{ + tBulkInstance *psInst; + unsigned long ulEPStatus; + unsigned long ulSize; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateBulkData; + + // + // Get the endpoint status to see why we were called. + // + ulEPStatus = MAP_USBEndpointStatus(psInst->ulUSBBase, psInst->ucINEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(psInst->ulUSBBase, psInst->ucINEndpoint, + ulEPStatus); + + // + // Our last transmission completed. Clear our state back to idle and + // see if we need to send any more data. + // + psInst->eBulkTxState = BULK_STATE_IDLE; + + // + // Notify the client that the last transmission completed. + // + ulSize = psInst->usLastTxSize; + psInst->usLastTxSize = 0; + psDevice->pfnTxCallback(psDevice->pvTxCBData, USB_EVENT_TX_COMPLETE, + ulSize, (void *)0); + + return(true); +} + +//***************************************************************************** +// +// Called by the USB stack for any activity involving one of our endpoints +// other than EP0. This function is a fan out that merely directs the call to +// the correct handler depending upon the endpoint and transaction direction +// signaled in ulStatus. +// +//***************************************************************************** +static void +HandleEndpoints(void *pvInstance, unsigned long ulStatus) +{ + const tUSBDBulkDevice *psBulkInst; + tBulkInstance *psInst; + + ASSERT(pvInstance != 0); + + // + // Determine if the serial device is in single or composite mode because + // the meaning of ulIndex is different in both cases. + // + psBulkInst = (const tUSBDBulkDevice *)pvInstance; + psInst = psBulkInst->psPrivateBulkData; + + // + // Handler for the bulk OUT data endpoint. + // + if(ulStatus & (0x10000 << USB_EP_TO_INDEX(psInst->ucOUTEndpoint))) + { + // + // Data is being sent to us from the host. + // + ProcessDataFromHost(pvInstance, ulStatus); + } + + // + // Handler for the bulk IN data endpoint. + // + if(ulStatus & (1 << USB_EP_TO_INDEX(psInst->ucINEndpoint))) + { + ProcessDataToHost(pvInstance, ulStatus); + } +} + +//***************************************************************************** +// +// Called by the USB stack whenever a configuration change occurs. +// +//***************************************************************************** +static void +HandleConfigChange(void *pvInstance, unsigned long ulInfo) +{ + tBulkInstance *psInst; + const tUSBDBulkDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create a device instance pointer. + // + psDevice = (const tUSBDBulkDevice *)pvInstance; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateBulkData; + + // + // Set all our endpoints to idle state. + // + psInst->eBulkRxState = BULK_STATE_IDLE; + psInst->eBulkTxState = BULK_STATE_IDLE; + + // + // If we have a control callback, let the client know we are open for + // business. + // + if(psDevice->pfnRxCallback) + { + // + // Pass the connected event to the client. + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, USB_EVENT_CONNECTED, 0, + (void *)0); + } + + // + // Remember that we are connected. + // + psInst->bConnected = true; +} + +//***************************************************************************** +// +// Device instance specific handler. +// +//***************************************************************************** +static void +HandleDevice(void *pvInstance, unsigned long ulRequest, void *pvRequestData) +{ + tBulkInstance *psInst; + unsigned char *pucData; + + // + // Create the serial instance data. + // + psInst = ((tUSBDBulkDevice *)pvInstance)->psPrivateBulkData; + + // + // Create the char array used by the events supported by the USB CDC + // serial class. + // + pucData = (unsigned char *)pvRequestData; + + switch(ulRequest) + { + // + // This was an interface change event. + // + case USB_EVENT_COMP_IFACE_CHANGE: + { + psInst->ucInterface = pucData[1]; + break; + } + + // + // This was an endpoint change event. + // + case USB_EVENT_COMP_EP_CHANGE: + { + // + // Determine if this is an IN or OUT endpoint that has changed. + // + if(pucData[0] & USB_EP_DESC_IN) + { + psInst->ucINEndpoint = INDEX_TO_USB_EP((pucData[1] & 0x7f)); + } + else + { + // + // Extract the new endpoint number. + // + psInst->ucOUTEndpoint = INDEX_TO_USB_EP(pucData[1] & 0x7f); + } + break; + } + default: + { + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device is +// disconnected from the host. +// +//***************************************************************************** +static void +HandleDisconnect(void *pvInstance) +{ + const tUSBDBulkDevice *psBulkDevice; + tBulkInstance *psInst; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psBulkDevice = (const tUSBDBulkDevice *)pvInstance; + + // + // Get a pointer to our instance data. + // + psInst = psBulkDevice->psPrivateBulkData; + + // + // If we are not currently connected so let the client know we are open + // for business. + // + if(psInst->bConnected) + { + // + // Pass the disconnected event to the client. + // + psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData, + USB_EVENT_DISCONNECTED, 0, (void *)0); + } + + // + // Remember that we are no longer connected. + // + psInst->bConnected = false; +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the bus is put into +// suspend state. +// +//***************************************************************************** +static void +HandleSuspend(void *pvInstance) +{ + const tUSBDBulkDevice *psBulkDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psBulkDevice = (const tUSBDBulkDevice *)pvInstance; + + // + // Pass the event on to the client. + // + psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData, USB_EVENT_SUSPEND, 0, + (void *)0); +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the bus is taken +// out of suspend state. +// +//***************************************************************************** +static void +HandleResume(void *pvInstance) +{ + const tUSBDBulkDevice *psBulkDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psBulkDevice = (const tUSBDBulkDevice *)pvInstance; + + // + // Pass the event on to the client. + // + psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData, USB_EVENT_RESUME, 0, + (void *)0); +} + +//***************************************************************************** +// +// This function is called periodically and provides us with a time reference +// and method of implementing delayed or time-dependent operations. +// +// \param ulIndex is the index of the USB controller for which this tick +// is being generated. +// \param ulTimemS is the elapsed time in milliseconds since the last call +// to this function. +// +// \return None. +// +//***************************************************************************** +static void +BulkTickHandler(void *pvInstance, unsigned long ulTimemS) +{ + tBulkInstance *psInst; + unsigned long ulSize; + const tUSBDBulkDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDBulkDevice *)pvInstance; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateBulkData; + + // + // Do we have a deferred receive waiting + // + if(psInst->usDeferredOpFlags & (1 << BULK_DO_PACKET_RX)) + { + // + // Yes - how big is the waiting packet? + // + ulSize = MAP_USBEndpointDataAvail(psInst->ulUSBBase, + psInst->ucOUTEndpoint); + + // + // Tell the client that there is a packet waiting for it. + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, USB_EVENT_RX_AVAILABLE, + ulSize, (void *)0); + } + + return; +} + +//***************************************************************************** +// +//! Initializes bulk device operation for a given USB controller. +//! +//! \param ulIndex is the index of the USB controller which is to be +//! initialized for bulk device operation. +//! \param psDevice points to a structure containing parameters customizing +//! the operation of the bulk device. +//! +//! An application wishing to make use of a USB bulk communication channel +//! must call this function to initialize the USB controller and attach the +//! device to the USB bus. This function performs all required USB +//! initialization. +//! +//! On successful completion, this function will return the \e psDevice pointer +//! passed to it. This must be passed on all future calls to the device driver +//! related to this device. +//! +//! The USBDBulk interface offers packet-based transmit and receive operation. +//! If the application would rather use block based communication with +//! transmit and receive buffers, USB buffers may be used above the bulk +//! transmit and receive channels to offer this functionality. +//! +//! Transmit Operation: +//! +//! Calls to USBDBulkPacketWrite must send no more than 64 bytes of data at a +//! time and may only be made when no other transmission is currently +//! outstanding. +//! +//! Once a packet of data has been acknowledged by the USB host, a +//! USB_EVENT_TX_COMPLETE event is sent to the application callback to inform +//! it that another packet may be transmitted. +//! +//! Receive Operation: +//! +//! An incoming USB data packet will result in a call to the application +//! callback with event USBD_EVENT_RX_AVAILABLE. The application must then +//! call USBDBulkPacketRead(), passing a buffer capable of holding 64 bytes, to +//! retrieve the data and acknowledge reception to the USB host. +//! +//! \note The application must not make any calls to the low level USB Device +//! API if interacting with USB via the USB bulk device class API. Doing so +//! will cause unpredictable (though almost certainly unpleasant) behavior. +//! +//! \return Returns NULL on failure or the psDevice pointer on success. +// +//***************************************************************************** +void * +USBDBulkInit(unsigned long ulIndex, const tUSBDBulkDevice *psDevice) +{ + void *pvInstance; + + // + // Check parameter validity. + // + ASSERT(ulIndex == 0); + ASSERT(psDevice); + + pvInstance = USBDBulkCompositeInit(ulIndex, psDevice); + + if(pvInstance) + { + // + // All is well so now pass the descriptors to the lower layer and put + // the bulk device on the bus. + // + USBDCDInit(ulIndex, psDevice->psPrivateBulkData->psDevInfo); + } + + // + // Return the pointer to the instance indicating that everything went well. + // + return(pvInstance); +} + +//***************************************************************************** +// +//! Initializes bulk device operation for a given USB controller. +//! +//! \param ulIndex is the index of the USB controller which is to be +//! initialized for bulk device operation. +//! \param psDevice points to a structure containing parameters customizing +//! the operation of the bulk device. +//! +//! This call is very similar to USBDBulkInit() except that it is used for +//! initializing an instance of the bulk device for use in a composite device. +//! +//! \return Returns zero on failure or a non-zero value that should be +//! used with the remaining USB HID Bulk APIs. +// +//***************************************************************************** +void * +USBDBulkCompositeInit(unsigned long ulIndex, const tUSBDBulkDevice *psDevice) +{ + tBulkInstance *psInst; + tDeviceDescriptor *psDevDesc; + + // + // Check parameter validity. + // + ASSERT(ulIndex == 0); + ASSERT(psDevice); + ASSERT(psDevice->ppStringDescriptors); + ASSERT(psDevice->psPrivateBulkData); + ASSERT(psDevice->pfnRxCallback); + ASSERT(psDevice->pfnTxCallback); + + // + // Initialize the workspace in the passed instance structure. + // + psInst = psDevice->psPrivateBulkData; + psInst->psConfDescriptor = (tConfigDescriptor *)g_pBulkDescriptor; + psInst->psDevInfo = &g_sBulkDeviceInfo; + psInst->ulUSBBase = USB0_BASE; + psInst->eBulkRxState = BULK_STATE_UNCONFIGURED; + psInst->eBulkTxState = BULK_STATE_UNCONFIGURED; + psInst->usDeferredOpFlags = 0; + psInst->bConnected = false; + + // + // Set the default endpoint and interface assignments. + // + psInst->ucINEndpoint = DATA_IN_ENDPOINT; + psInst->ucOUTEndpoint = DATA_OUT_ENDPOINT; + psInst->ucInterface = 0; + + // + // Fix up the device descriptor with the client-supplied values. + // + psDevDesc = (tDeviceDescriptor *)psInst->psDevInfo->pDeviceDescriptor; + psDevDesc->idVendor = psDevice->usVID; + psDevDesc->idProduct = psDevice->usPID; + + // + // Fix up the configuration descriptor with client-supplied values. + // + psInst->psConfDescriptor->bmAttributes = psDevice->ucPwrAttributes; + psInst->psConfDescriptor->bMaxPower = + (unsigned char)(psDevice->usMaxPowermA / 2); + + // + // Plug in the client's string stable to the device information + // structure. + // + psInst->psDevInfo->ppStringDescriptors = psDevice->ppStringDescriptors; + psInst->psDevInfo->ulNumStringDescriptors + = psDevice->ulNumStringDescriptors; + + // + // Set the device instance. + // + psInst->psDevInfo->pvInstance = (void *)psDevice; + + // + // Initialize the USB tick module, this will prevent it from being + // initialized later in the call to USBDCDInit(); + // + InternalUSBTickInit(); + + // + // Register our tick handler (this must be done after USBDCDInit). + // + InternalUSBRegisterTickHandler(BulkTickHandler, + (void *)psDevice); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psDevice); +} + +//***************************************************************************** +// +//! Shut down the bulk device. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDBulkInit(). +//! +//! This function terminates device operation for the instance supplied and +//! removes the device from the USB bus. This function should not be called +//! if the bulk device is part of a composite device and instead the +//! USBDCompositeTerm() function should be called for the full composite +//! device. +//! +//! Following this call, the \e pvInstance instance should not me used in any +//! other calls. +//! +//! \return None. +// +//***************************************************************************** +void +USBDBulkTerm(void *pvInstance) +{ + tBulkInstance *psInst; + + ASSERT(pvInstance); + + // + // Get a pointer to our instance data. + // + psInst = ((tUSBDBulkDevice *)pvInstance)->psPrivateBulkData; + + // + // Terminate the requested instance. + // + USBDCDTerm(USB_BASE_TO_INDEX(psInst->ulUSBBase)); + + psInst->ulUSBBase = 0; + psInst->psDevInfo = (tDeviceInfo *)0; + psInst->psConfDescriptor = (tConfigDescriptor *)0; + + return; +} + +//***************************************************************************** +// +//! Sets the client-specific pointer parameter for the receive channel +//! callback. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDBulkInit(). +//! \param pvCBData is the pointer that client wishes to be provided on each +//! event sent to the receive channel callback function. +//! +//! The client uses this function to change the callback pointer passed in +//! the first parameter on all callbacks to the \e pfnRxCallback function +//! passed on USBDBulkInit(). +//! +//! If a client wants to make runtime changes in the callback pointer, it must +//! ensure that the \e pvInstance structure passed to USBDBulkInit() resides in +//! RAM. If this structure is in flash, callback pointer changes will not be +//! possible. +//! +//! \return Returns the previous callback pointer that was being used for +//! this instance's receive callback. +// +//***************************************************************************** +void * +USBDBulkSetRxCBData(void *pvInstance, void *pvCBData) +{ + void *pvOldValue; + + ASSERT(pvInstance); + + // + // Set the callback data for the receive channel after remembering the + // previous value. + // + pvOldValue = ((tUSBDBulkDevice *)pvInstance)->pvRxCBData; + ((tUSBDBulkDevice *)pvInstance)->pvRxCBData = pvCBData; + + // + // Return the previous callback pointer. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Sets the client-specific pointer parameter for the transmit callback. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDBulkInit(). +//! \param pvCBData is the pointer that client wishes to be provided on each +//! event sent to the transmit channel callback function. +//! +//! The client uses this function to change the callback pointer passed in +//! the first parameter on all callbacks to the \e pfnTxCallback function +//! passed on USBDBulkInit(). +//! +//! If a client wants to make runtime changes in the callback pointer, it must +//! ensure that the \e pvInstance structure passed to USBDBulkInit() resides in +//! RAM. If this structure is in flash, callback pointer changes will not be +//! possible. +//! +//! \return Returns the previous callback pointer that was being used for +//! this instance's transmit callback. +// +//***************************************************************************** +void * +USBDBulkSetTxCBData(void *pvInstance, void *pvCBData) +{ + void *pvOldValue; + + ASSERT(pvInstance); + + // + // Set the callback pointer for the transmit channel after remembering the + // previous value. + // + pvOldValue = ((tUSBDBulkDevice *)pvInstance)->pvTxCBData; + ((tUSBDBulkDevice *)pvInstance)->pvTxCBData = pvCBData; + + // + // Return the previous callback pointer value. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Transmits a packet of data to the USB host via the bulk data interface. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDBulkInit(). +//! \param pcData points to the first byte of data which is to be transmitted. +//! \param ulLength is the number of bytes of data to transmit. +//! \param bLast indicates whether more data is to be written before a packet +//! should be scheduled for transmission. If \b true, the client will make +//! a further call to this function. If \b false, no further call will be +//! made and the driver should schedule transmission of a short packet. +//! +//! This function schedules the supplied data for transmission to the USB +//! host in a single USB packet. If no transmission is currently ongoing, +//! the data is immediately copied to the relevant USB endpoint FIFO for +//! transmission. Whenever a USB packet is acknowledged by the host, a +//! USB_EVENT_TX_COMPLETE event will be sent to the transmit channel callback +//! indicating that more data can now be transmitted. +//! +//! The maximum value for \e ulLength is 64 bytes (the maximum USB packet size +//! for the bulk endpoints in use by the device). Attempts to send more data +//! than this will result in a return code of 0 indicating that the data cannot +//! be sent. +//! +//! The \e bLast parameter allows a client to make multiple calls to this +//! function before scheduling transmission of the packet to the host. This +//! can be helpful if, for example, constructing a packet on the fly or +//! writing a packet which spans the wrap point in a ring buffer. +//! +//! \return Returns the number of bytes actually sent. At this level, this +//! will either be the number of bytes passed (if less than or equal to the +//! maximum packet size for the USB endpoint in use and no outstanding +//! transmission ongoing) or 0 to indicate a failure. +// +//***************************************************************************** +unsigned long +USBDBulkPacketWrite(void *pvInstance, unsigned char *pcData, + unsigned long ulLength, tBoolean bLast) +{ + tBulkInstance *psInst; + long lRetcode; + + ASSERT(pvInstance); + + // + // Get our instance data pointer + // + psInst = ((tUSBDBulkDevice *)pvInstance)->psPrivateBulkData; + + // + // Can we send the data provided? + // + if((ulLength > DATA_IN_EP_MAX_SIZE) || + (psInst->eBulkTxState != BULK_STATE_IDLE)) + { + // + // Either the packet was too big or we are in the middle of sending + // another packet. Return 0 to indicate that we can't send this data. + // + return(0); + } + + // + // Copy the data into the USB endpoint FIFO. + // + lRetcode = MAP_USBEndpointDataPut(psInst->ulUSBBase, psInst->ucINEndpoint, + pcData, ulLength); + + // + // Did we copy the data successfully? + // + if(lRetcode != -1) + { + // + // Remember how many bytes we sent. + // + psInst->usLastTxSize += (unsigned short)ulLength; + + // + // If this is the last call for this packet, schedule transmission. + // + if(bLast) + { + // + // Send the packet to the host if we have received all the data we + // can expect for this packet. + // + psInst->eBulkTxState = BULK_STATE_WAIT_DATA; + lRetcode = MAP_USBEndpointDataSend(psInst->ulUSBBase, + psInst->ucINEndpoint, + USB_TRANS_IN); + } + } + + // + // Did an error occur while trying to send the data? + // + if(lRetcode != -1) + { + // + // No - tell the caller we sent all the bytes provided. + // + return(ulLength); + } + else + { + // + // Yes - tell the caller we couldn't send the data. + // + return(0); + } +} + +//***************************************************************************** +// +//! Reads a packet of data received from the USB host via the bulk data +//! interface. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDBulkInit(). +//! \param pcData points to a buffer into which the received data will be +//! written. +//! \param ulLength is the size of the buffer pointed to by pcData. +//! \param bLast indicates whether the client will make a further call to +//! read additional data from the packet. +//! +//! This function reads up to \e ulLength bytes of data received from the USB +//! host into the supplied application buffer. If the driver detects that the +//! entire packet has been read, it is acknowledged to the host. +//! +//! The \e bLast parameter is ignored in this implementation since the end of +//! a packet can be determined without relying upon the client to provide +//! this information. +//! +//! \return Returns the number of bytes of data read. +// +//***************************************************************************** +unsigned long +USBDBulkPacketRead(void *pvInstance, unsigned char *pcData, + unsigned long ulLength, tBoolean bLast) +{ + unsigned long ulEPStatus, ulCount, ulPkt; + tBulkInstance *psInst; + long lRetcode; + + ASSERT(pvInstance); + + // + // Get our instance data pointer + // + psInst = ((tUSBDBulkDevice *)pvInstance)->psPrivateBulkData; + + // + // Does the relevant endpoint FIFO have a packet waiting for us? + // + ulEPStatus = MAP_USBEndpointStatus(psInst->ulUSBBase, + psInst->ucOUTEndpoint); + + if(ulEPStatus & USB_DEV_RX_PKT_RDY) + { + // + // How many bytes are available for us to receive? + // + ulPkt = MAP_USBEndpointDataAvail(psInst->ulUSBBase, + psInst->ucOUTEndpoint); + + // + // Get as much data as we can. + // + ulCount = ulLength; + lRetcode = MAP_USBEndpointDataGet(psInst->ulUSBBase, + psInst->ucOUTEndpoint, + pcData, &ulCount); + + // + // Did we read the last of the packet data? + // + if(ulCount == ulPkt) + { + // + // Clear the endpoint status so that we know no packet is + // waiting. + // + MAP_USBDevEndpointStatusClear(psInst->ulUSBBase, + psInst->ucOUTEndpoint, + ulEPStatus); + + // + // Acknowledge the data, thus freeing the host to send the + // next packet. + // + MAP_USBDevEndpointDataAck(psInst->ulUSBBase, psInst->ucOUTEndpoint, + true); + + // + // Clear the flag we set to indicate that a packet read is + // pending. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, BULK_DO_PACKET_RX, + false); + } + + // + // If all went well, tell the caller how many bytes they got. + // + if(lRetcode != -1) + { + return(ulCount); + } + } + + // + // No packet was available or an error occurred while reading so tell + // the caller no bytes were returned. + // + return(0); +} + +//***************************************************************************** +// +//! Returns the number of free bytes in the transmit buffer. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDBulkInit(). +//! +//! This function returns the maximum number of bytes that can be passed on a +//! call to USBDBulkPacketWrite and accepted for transmission. The value +//! returned will be the maximum USB packet size (64) if no transmission is +//! currently outstanding or 0 if a transmission is in progress. +//! +//! \return Returns the number of bytes available in the transmit buffer. +// +//***************************************************************************** +unsigned long +USBDBulkTxPacketAvailable(void *pvInstance) +{ + tBulkInstance *psInst; + + ASSERT(pvInstance); + + // + // Get our instance data pointer. + // + psInst = ((tUSBDBulkDevice *)pvInstance)->psPrivateBulkData; + + // + // Do we have a packet transmission currently ongoing? + // + if(psInst->eBulkTxState != BULK_STATE_IDLE) + { + // + // We are not ready to receive a new packet so return 0. + // + return(0); + } + else + { + // + // We can receive a packet so return the max packet size for the + // relevant endpoint. + // + return(DATA_IN_EP_MAX_SIZE); + } +} + +//***************************************************************************** +// +//! Determines whether a packet is available and, if so, the size of the +//! buffer required to read it. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDBulkInit(). +//! +//! This function may be used to determine if a received packet remains to be +//! read and allows the application to determine the buffer size needed to +//! read the data. +//! +//! \return Returns 0 if no received packet remains unprocessed or the +//! size of the packet if a packet is waiting to be read. +// +//***************************************************************************** +unsigned long +USBDBulkRxPacketAvailable(void *pvInstance) +{ + unsigned long ulEPStatus; + unsigned long ulSize; + tBulkInstance *psInst; + + ASSERT(pvInstance); + + // + // Get our instance data pointer + // + psInst = ((tUSBDBulkDevice *)pvInstance)->psPrivateBulkData; + + // + // Does the relevant endpoint FIFO have a packet waiting for us? + // + ulEPStatus = MAP_USBEndpointStatus(psInst->ulUSBBase, + psInst->ucOUTEndpoint); + + if(ulEPStatus & USB_DEV_RX_PKT_RDY) + { + // + // Yes - a packet is waiting. How big is it? + // + ulSize = MAP_USBEndpointDataAvail(psInst->ulUSBBase, + psInst->ucOUTEndpoint); + + return(ulSize); + } + else + { + // + // There is no packet waiting to be received. + // + return(0); + } +} + +//***************************************************************************** +// +//! Reports the device power status (bus- or self-powered) to the USB library. +//! +//! \param pvInstance is the pointer to the bulk device instance structure. +//! \param ucPower indicates the current power status, either \b +//! USB_STATUS_SELF_PWR or \b USB_STATUS_BUS_PWR. +//! +//! Applications which support switching between bus- or self-powered +//! operation should call this function whenever the power source changes +//! to indicate the current power status to the USB library. This information +//! is required by the USB library to allow correct responses to be provided +//! when the host requests status from the device. +//! +//! \return None. +// +//***************************************************************************** +void +USBDBulkPowerStatusSet(void *pvInstance, unsigned char ucPower) +{ + ASSERT(pvInstance); + + // + // Pass the request through to the lower layer. + // + USBDCDPowerStatusSet(0, ucPower); +} + +//***************************************************************************** +// +//! Requests a remote wake up to resume communication when in suspended state. +//! +//! \param pvInstance is the pointer to the bulk device instance structure. +//! +//! When the bus is suspended, an application which supports remote wake up +//! (advertised to the host via the configuration descriptor) may call this function +//! to initiate remote wake up signaling to the host. If the remote wake up +//! feature has not been disabled by the host, this will cause the bus to +//! resume operation within 20mS. If the host has disabled remote wake up, +//! \b false will be returned to indicate that the wake up request was not +//! successful. +//! +//! \return Returns \b true if the remote wake up is not disabled and the +//! signaling was started or \b false if remote wake up is disabled or if +//! signaling is currently ongoing following a previous call to this function. +// +//***************************************************************************** +tBoolean +USBDBulkRemoteWakeupRequest(void *pvInstance) +{ + ASSERT(pvInstance); + + // + // Pass the request through to the lower layer. + // + return(USBDCDRemoteWakeupRequest(0)); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/device/usbdbulk.h b/usblib/device/usbdbulk.h new file mode 100644 index 0000000..abfe85f --- /dev/null +++ b/usblib/device/usbdbulk.h @@ -0,0 +1,269 @@ +//***************************************************************************** +// +// usbdcdc.h - USBLib support for a generic bulk device. +// +// Copyright (c) 2008-2012 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#ifndef __USBDBULK_H__ +#define __USBDBULK_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup bulk_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// PRIVATE +// +// The first few sections of this header are private defines that are used by +// the USB Bulk example code and are here only to help with the application +// allocating the correct amount of memory for the Bulk example device code. +// +//***************************************************************************** + +//***************************************************************************** +// +// PRIVATE +// +// This enumeration holds the various states that the device can be in during +// normal operation. +// +//***************************************************************************** +typedef enum +{ + // + // Unconfigured. + // + BULK_STATE_UNCONFIGURED, + + // + // No outstanding transaction remains to be completed. + // + BULK_STATE_IDLE, + + // + // Waiting on completion of a send or receive transaction. + // + BULK_STATE_WAIT_DATA, + + // + // Waiting for client to process data. + // + BULK_STATE_WAIT_CLIENT +} tBulkState; + +//***************************************************************************** +// +// PRIVATE +// +// This structure defines the private instance data and state variables for the +// Bulk only example device. The memory for this structure is pointed to by +// the psPrivateBulkData field in the tUSBDBulkDevice structure passed on +// USBDBulkInit(). +// +//***************************************************************************** +typedef struct +{ + unsigned long ulUSBBase; + tDeviceInfo *psDevInfo; + tConfigDescriptor *psConfDescriptor; + volatile tBulkState eBulkRxState; + volatile tBulkState eBulkTxState; + volatile unsigned short usDeferredOpFlags; + unsigned short usLastTxSize; + volatile tBoolean bConnected; + unsigned char ucINEndpoint; + unsigned char ucOUTEndpoint; + unsigned char ucInterface; +} +tBulkInstance; + +#ifndef DEPRECATED +//***************************************************************************** +// +// The number of bytes of workspace required by the bulk device class driver. +// The client must provide a block of RAM of at least this size in the +// tBulkInstance field of the tUSBBulkDevice structure passed on USBDBulkInit. +// +// This value is deprecated and should not be used, any new code should just +// pass in a tBulkInstance structure in the psPrivateBulkData field. +// +//***************************************************************************** +#define USB_BULK_WORKSPACE_SIZE (sizeof(tBulkInstance)) +#endif + +//***************************************************************************** +// +//! The size of the memory that should be allocated to create a configuration +//! descriptor for a single instance of the USB Bulk Device. +//! This does not include the configuration descriptor which is automatically +//! ignored by the composite device class. +// +// For reference this is sizeof(g_sCDCSerIfaceHeaderSectionNOINT) + +// sizeof(g_sCDCSerInterfaceSection) + sizeof(g_sCDCSerIfaceEndpointsNOINT) +// +//***************************************************************************** +#define COMPOSITE_DBULK_SIZE (23) + +//***************************************************************************** +// +//! The structure used by the application to define operating parameters for +//! the bulk device. +// +//***************************************************************************** +typedef struct +{ + // + //! The vendor ID that this device is to present in the device descriptor. + // + unsigned short usVID; + + // + //! The product ID that this device is to present in the device descriptor. + // + unsigned short usPID; + + // + //! The maximum power consumption of the device, expressed in milliamps. + // + unsigned short usMaxPowermA; + + // + //! Indicates whether the device is self- or bus-powered and whether or not + //! it supports remote wakeup. Valid values are USB_CONF_ATTR_SELF_PWR or + //! USB_CONF_ATTR_BUS_PWR, optionally ORed with USB_CONF_ATTR_RWAKE. + // + unsigned char ucPwrAttributes; + + // + //! A pointer to the callback function which will be called to notify + //! the application of events related to the device's data receive channel. + // + tUSBCallback pfnRxCallback; + + // + //! A client-supplied pointer which will be sent as the first + //! parameter in all calls made to the receive channel callback, + //! pfnRxCallback. + // + void *pvRxCBData; + + // + //! A pointer to the callback function which will be called to notify + //! the application of events related to the device's data transmit + //! channel. + // + tUSBCallback pfnTxCallback; + + // + //! A client-supplied pointer which will be sent as the first + //! parameter in all calls made to the transmit channel callback, + //! pfnTxCallback. + // + void *pvTxCBData; + + // + //! A pointer to the string descriptor array for this device. This array + //! must contain pointers to the following string descriptors in this + //! order. Language descriptor, Manufacturer name string (language 1), + //! Product name string (language 1), Serial number string (language 1), + //! Interface description string (language 1) and Configuration description + //! string (language 1). + //! + //! If supporting more than 1 language, the strings for indices 1 through 5 + //! must be repeated for each of the other languages defined in the + //! language descriptor. + // + const unsigned char * const *ppStringDescriptors; + + // + //! The number of descriptors provided in the ppStringDescriptors array. + //! This must be 1 + (5 * number of supported languages). + // + unsigned long ulNumStringDescriptors; + + // + //! A pointer to private instance data for this device. This memory must + //! remain accessible for as long as the bulk device is in use and must not + //! be modified by any code outside the bulk class driver. + // + tBulkInstance *psPrivateBulkData; +} +tUSBDBulkDevice; + +extern tDeviceInfo g_sBulkDeviceInfo; + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void *USBDBulkInit(unsigned long ulIndex, + const tUSBDBulkDevice *psDevice); +extern void *USBDBulkCompositeInit(unsigned long ulIndex, + const tUSBDBulkDevice *psDevice); +extern void USBDBulkTerm(void *pvInstance); +extern void *USBDBulkSetRxCBData(void *pvInstance, void *pvCBData); +extern void *USBDBulkSetTxCBData(void *pvInstance, void *pvCBData); +extern unsigned long USBDBulkPacketWrite(void *pvInstance, + unsigned char *pcData, + unsigned long ulLength, + tBoolean bLast); +extern unsigned long USBDBulkPacketRead(void *pvInstance, + unsigned char *pcData, + unsigned long ulLength, + tBoolean bLast); +extern unsigned long USBDBulkTxPacketAvailable(void *pvInstance); +extern unsigned long USBDBulkRxPacketAvailable(void *pvInstance); +extern void USBDBulkPowerStatusSet(void *pvInstance, unsigned char ucPower); +extern tBoolean USBDBulkRemoteWakeupRequest(void *pvInstance); + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBDBULK_H__ diff --git a/usblib/device/usbdcdc.c b/usblib/device/usbdcdc.c new file mode 100644 index 0000000..f25b4bc --- /dev/null +++ b/usblib/device/usbdcdc.c @@ -0,0 +1,2972 @@ +//***************************************************************************** +// +// usbdcdc.c - USB CDC ACM (serial) device class driver. +// +// Copyright (c) 2008-2012 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usbcdc.h" +#include "usblib/usblibpriv.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbdcomp.h" +#include "usblib/device/usbdcdc.h" + +//***************************************************************************** +// +//! \addtogroup cdc_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Some assumptions and deviations from the CDC specification +// ---------------------------------------------------------- +// +// 1. Although the CDC specification indicates that the following requests +// should be supported by ACM CDC devices, these don't seem relevant to a +// virtual COM port implementation and are never seen when connecting to a +// Windows host and running either Hyperterminal or TeraTerm. As a result, +// this implementation does not support them and stalls endpoint 0 if they are +// received. +// - SEND_ENCAPSULATED_COMMAND +// - GET_ENCAPSULATED_RESPONSE +// - SET_COMM_FEATURE +// - GET_COMM_FEATURE +// - CLEAR_COMM_FEATURE +// +// 2. The CDC specification is very clear on the fact that an ACM device +// should offer two interfaces - a control interface offering an interrupt IN +// endpoint and a data interface offering bulk IN and OUT endpoints. Using +// this descriptor configuration, however, Windows insists on enumerating the +// device as two separate entities resulting in two virtual COM ports or one +// COM port and an Unknown Device (depending upon INF contents) appearing +// in Device Manager. This implementation, derived by experimentation and +// examination of other virtual COM and CDC solutions, uses only a single +// interface combining all three endpoints. This appears to satisfy +// Windows2000, XP and Vista and operates as intended using the Hyperterminal +// and TeraTerm terminal emulators. Your mileage may vary with other +// (untested) operating systems! +// +//***************************************************************************** + +//***************************************************************************** +// +// 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) + +//***************************************************************************** +// +// Size of the buffer to hold request-specific data read from the host. This +// must be sized to accommodate the largest request structure that we intend +// processing. +// +//***************************************************************************** +#define MAX_REQUEST_DATA_SIZE sizeof(tLineCoding) + +//***************************************************************************** +// +// Flags that may appear in usDeferredOpFlags to indicate some operation that +// has been requested but could not be processed at the time it was received. +// +//***************************************************************************** +#define CDC_DO_SERIAL_STATE_CHANGE 0 +#define CDC_DO_SEND_BREAK 1 +#define CDC_DO_CLEAR_BREAK 2 +#define CDC_DO_LINE_CODING_CHANGE 3 +#define CDC_DO_LINE_STATE_CHANGE 4 +#define CDC_DO_PACKET_RX 5 + +//***************************************************************************** +// +// The subset of deferred operations which result in the receive channel +// being blocked. +// +//***************************************************************************** +#define RX_BLOCK_OPS ((1 << CDC_DO_SEND_BREAK) | \ + (1 << CDC_DO_LINE_CODING_CHANGE) | \ + (1 << CDC_DO_LINE_STATE_CHANGE)) + +//***************************************************************************** +// +// Macros to convert between USB controller base address and an index. These +// are currently trivial but are included to allow for the possibility of +// supporting more than one controller in the future. +// +//***************************************************************************** +#define USB_BASE_TO_INDEX(BaseAddr) (0) +#define USB_INDEX_TO_BASE(Index) (USB0_BASE) + +//***************************************************************************** +// +// Endpoints to use for each of the required endpoints in the driver. +// +//***************************************************************************** +#define CONTROL_ENDPOINT USB_EP_1 +#define DATA_IN_ENDPOINT USB_EP_2 +#define DATA_OUT_ENDPOINT USB_EP_1 + +//***************************************************************************** +// +// The following are the USB interface numbers for the CDC serial device. +// +//***************************************************************************** +#define SERIAL_INTERFACE_CONTROL 0 +#define SERIAL_INTERFACE_DATA 1 + +//***************************************************************************** +// +// Maximum packet size for the bulk endpoints used for serial data +// transmission and reception and the associated FIFO sizes to set aside +// for each endpoint. +// +//***************************************************************************** +#define DATA_IN_EP_FIFO_SIZE USB_FIFO_SZ_64 +#define DATA_OUT_EP_FIFO_SIZE USB_FIFO_SZ_64 +#define CTL_IN_EP_FIFO_SIZE USB_FIFO_SZ_16 + +#define DATA_IN_EP_MAX_SIZE USB_FIFO_SZ_TO_BYTES(DATA_IN_EP_FIFO_SIZE) +#define DATA_OUT_EP_MAX_SIZE USB_FIFO_SZ_TO_BYTES(DATA_IN_EP_FIFO_SIZE) +#define CTL_IN_EP_MAX_SIZE USB_FIFO_SZ_TO_BYTES(CTL_IN_EP_FIFO_SIZE) + +//***************************************************************************** +// +// The collection of serial state flags indicating character errors. +// +//***************************************************************************** +#define USB_CDC_SERIAL_ERRORS (USB_CDC_SERIAL_STATE_OVERRUN | \ + USB_CDC_SERIAL_STATE_PARITY | \ + USB_CDC_SERIAL_STATE_FRAMING) + +//***************************************************************************** +// +// Device Descriptor. This is stored in RAM to allow several fields to be +// changed at runtime based on the client's requirements. +// +//***************************************************************************** +unsigned char g_pCDCSerDeviceDescriptor[] = +{ + 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) + USB_CLASS_CDC, // USB Device Class (spec 5.1.1) + 0, // USB Device Sub-class (spec 5.1.1) + USB_CDC_PROTOCOL_NONE, // 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. +}; + +//***************************************************************************** +// +// CDC Serial 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. +// +//***************************************************************************** +unsigned char g_pCDCSerDescriptor[] = +{ + // + // Configuration descriptor header. + // + 9, // Size of the configuration descriptor. + USB_DTYPE_CONFIGURATION, // Type of this descriptor. + USBShort(9), // The total size of this full structure, this + // will be patched so it is just set to the + // size of this structure. + 2, // The number of interfaces in this + // configuration. + 1, // The unique value for this configuration. + 5, // 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. +}; + +const tConfigSection g_sCDCSerConfigSection = +{ + sizeof(g_pCDCSerDescriptor), + g_pCDCSerDescriptor +}; + +//***************************************************************************** +// +// This is the Interface Association Descriptor for the serial device used in +// composite devices. +// +//***************************************************************************** +unsigned char g_pIADSerDescriptor[] = +{ + + 8, // Size of the interface descriptor. + USB_DTYPE_INTERFACE_ASC, // Interface Association Type. + 0x0, // Default starting interface is 0. + 0x2, // Number of interfaces in this association. + USB_CLASS_CDC, // The device class for this association. + USB_CDC_SUBCLASS_ABSTRACT_MODEL, // The device subclass for this + // association. + USB_CDC_PROTOCOL_V25TER, // The protocol for this association. + 0 // The string index for this association. +}; + +const tConfigSection g_sIADSerConfigSection = +{ + sizeof(g_pIADSerDescriptor), + g_pIADSerDescriptor +}; + +//***************************************************************************** +// +// This is the control interface for the serial device. +// +//***************************************************************************** +const unsigned char g_pCDCSerCommInterface[] = +{ + // + // Communication Class Interface Descriptor. + // + 9, // Size of the interface descriptor. + USB_DTYPE_INTERFACE, // Type of this descriptor. + SERIAL_INTERFACE_CONTROL, // The index for this interface. + 0, // The alternate setting for this interface. + 1, // The number of endpoints used by this + // interface. + USB_CLASS_CDC, // The interface class constant defined by + // USB-IF (spec 5.1.3). + USB_CDC_SUBCLASS_ABSTRACT_MODEL, // The interface sub-class constant + // defined by USB-IF (spec 5.1.3). + USB_CDC_PROTOCOL_V25TER, // The interface protocol for the sub-class + // specified above. + 4, // The string index for this interface. + + // + // Communication Class Interface Functional Descriptor - Header + // + 5, // Size of the functional descriptor. + USB_CDC_CS_INTERFACE, // CDC interface descriptor + USB_CDC_FD_SUBTYPE_HEADER, // Header functional descriptor + USBShort(0x110), // Complies with CDC version 1.1 + + // + // Communication Class Interface Functional Descriptor - ACM + // + 4, // Size of the functional descriptor. + USB_CDC_CS_INTERFACE, // CDC interface descriptor + USB_CDC_FD_SUBTYPE_ABSTRACT_CTL_MGMT, + USB_CDC_ACM_SUPPORTS_LINE_PARAMS | USB_CDC_ACM_SUPPORTS_SEND_BREAK, + + // + // Communication Class Interface Functional Descriptor - Unions + // + 5, // Size of the functional descriptor. + USB_CDC_CS_INTERFACE, // CDC interface descriptor + USB_CDC_FD_SUBTYPE_UNION, + SERIAL_INTERFACE_CONTROL, + SERIAL_INTERFACE_DATA, // Data interface number + + // + // Communication Class Interface Functional Descriptor - Call Management + // + 5, // Size of the functional descriptor. + USB_CDC_CS_INTERFACE, // CDC interface descriptor + USB_CDC_FD_SUBTYPE_CALL_MGMT, + USB_CDC_CALL_MGMT_HANDLED, + SERIAL_INTERFACE_DATA, // Data interface number + + // + // Endpoint Descriptor (interrupt, IN) + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_IN | USB_EP_TO_INDEX(CONTROL_ENDPOINT), + USB_EP_ATTR_INT, // Endpoint is an interrupt endpoint. + USBShort(CTL_IN_EP_MAX_SIZE), // The maximum packet size. + 1 // The polling interval for this endpoint. +}; + +const tConfigSection g_sCDCSerCommInterfaceSection = +{ + sizeof(g_pCDCSerCommInterface), + g_pCDCSerCommInterface +}; + +//***************************************************************************** +// +// This is the Data interface for the serial device. +// +//***************************************************************************** +const unsigned char g_pCDCSerDataInterface[] = +{ + // + // Communication Class Data Interface Descriptor. + // + 9, // Size of the interface descriptor. + USB_DTYPE_INTERFACE, // Type of this descriptor. + SERIAL_INTERFACE_DATA, // The index for this interface. + 0, // The alternate setting for this interface. + 2, // The number of endpoints used by this + // interface. + USB_CLASS_CDC_DATA, // The interface class constant defined by + // USB-IF (spec 5.1.3). + 0, // The interface sub-class constant + // defined by USB-IF (spec 5.1.3). + USB_CDC_PROTOCOL_NONE, // The interface protocol for the sub-class + // specified above. + 0, // The string index for this interface. + + // + // Endpoint Descriptor + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_IN | USB_EP_TO_INDEX(DATA_IN_ENDPOINT), + USB_EP_ATTR_BULK, // Endpoint is a bulk endpoint. + USBShort(DATA_IN_EP_MAX_SIZE), // The maximum packet size. + 0, // The polling interval for this endpoint. + + // + // Endpoint Descriptor + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_OUT | USB_EP_TO_INDEX(DATA_OUT_ENDPOINT), + USB_EP_ATTR_BULK, // Endpoint is a bulk endpoint. + USBShort(DATA_OUT_EP_MAX_SIZE), // The maximum packet size. + 0, // The polling interval for this endpoint. +}; + +const tConfigSection g_sCDCSerDataInterfaceSection = +{ + sizeof(g_pCDCSerDataInterface), + g_pCDCSerDataInterface +}; + +//***************************************************************************** +// +// This array lists all the sections that must be concatenated to make a +// single, complete CDC ACM configuration descriptor. +// +//***************************************************************************** +const tConfigSection *g_psCDCSerSections[] = +{ + &g_sCDCSerConfigSection, + &g_sCDCSerCommInterfaceSection, + &g_sCDCSerDataInterfaceSection, +}; + +#define NUM_CDCSER_SECTIONS (sizeof(g_psCDCSerSections) / \ + sizeof(tConfigSection *)) + +//***************************************************************************** +// +// The header for the single configuration. 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_sCDCSerConfigHeader = +{ + NUM_CDCSER_SECTIONS, + g_psCDCSerSections +}; + +//***************************************************************************** +// +// This array lists all the sections that must be concatenated to make a +// single, complete CDC ACM configuration descriptor used in composite devices. +// The only addition is the g_sIADSerConfigSection. +// +//***************************************************************************** +const tConfigSection *g_psCDCCompSerSections[] = +{ + &g_sCDCSerConfigSection, + &g_sIADSerConfigSection, + &g_sCDCSerCommInterfaceSection, + &g_sCDCSerDataInterfaceSection, +}; + +#define NUM_COMP_CDCSER_SECTIONS (sizeof(g_psCDCCompSerSections) / \ + sizeof(tConfigSection *)) + +//***************************************************************************** +// +// The header for the composite configuration. 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_sCDCCompSerConfigHeader = +{ + NUM_COMP_CDCSER_SECTIONS, + g_psCDCCompSerSections +}; + +//***************************************************************************** +// +// Configuration Descriptor for the CDC serial class device. +// +//***************************************************************************** +const tConfigHeader * const g_pCDCSerConfigDescriptors[] = +{ + &g_sCDCSerConfigHeader +}; + +//***************************************************************************** +// +// Configuration Descriptor for the CDC serial class device used in a composite +// device. +// +//***************************************************************************** +const tConfigHeader * const g_pCDCCompSerConfigDescriptors[] = +{ + &g_sCDCCompSerConfigHeader +}; + +//***************************************************************************** +// +// Forward references for device handler callbacks +// +//***************************************************************************** +static void HandleRequests(void *pvInstance, tUSBRequest *pUSBRequest); +static void HandleConfigChange(void *pvInstance, unsigned long ulInfo); +static void HandleEP0Data(void *pvInstance, unsigned long ulDataSize); +static void HandleDisconnect(void *pvInstance); +static void HandleEndpoints(void *pvInstance, unsigned long ulStatus); +static void HandleSuspend(void *pvInstance); +static void HandleResume(void *pvInstance); +static void HandleDevice(void *pvInstance, unsigned long ulRequest, + void *pvRequestData); + +//***************************************************************************** +// +// The device information structure for the USB serial device. +// +//***************************************************************************** +tDeviceInfo g_sCDCSerDeviceInfo = +{ + // + // Device event handler callbacks. + // + { + // + // GetDescriptor + // + 0, + + // + // RequestHandler + // + HandleRequests, + + // + // InterfaceChange + // + 0, + + // + // ConfigChange + // + HandleConfigChange, + + // + // DataReceived + // + HandleEP0Data, + + // + // DataSentCallback + // + 0, + + // + // ResetHandler + // + 0, + + // + // SuspendHandler + // + HandleSuspend, + + // + // ResumeHandler + // + HandleResume, + + // + // DisconnectHandler + // + HandleDisconnect, + + // + // EndpointHandler + // + HandleEndpoints, + + // + // Device handler. + // + HandleDevice + }, + + // + // The common device descriptor. + // + g_pCDCSerDeviceDescriptor, + + // + // Default to no interrupt endpoint. + // + g_pCDCCompSerConfigDescriptors, + + // + // String descriptors will be passed in. + // + 0, + 0, + + // + // Use the default USB FIFO configuration. + // + &g_sUSBDefaultFIFOConfig, + + // + // Zero out the instance pointer by default. + // + 0 +}; + +//***************************************************************************** +// +// Set or clear deferred operation flags in an "atomic" manner. +// +// \param pusDeferredOp points to the flags variable which is to be modified. +// \param usBit indicates which bit number is to be set or cleared. +// \param bSet indicates the state that the flag must be set to. If \b true, +// the flag is set, if \b false, the flag is cleared. +// +// This function safely sets or clears a bit in a flag variable. The operation +// makes use of bitbanding to ensure that the operation is atomic (no read- +// modify-write is required). +// +// \return None. +// +//***************************************************************************** +static void +SetDeferredOpFlag(volatile unsigned short *pusDeferredOp, + unsigned short usBit, tBoolean bSet) +{ + // + // Set the flag bit to 1 or 0 using a bitband access. + // + HWREGBITH(pusDeferredOp, usBit) = bSet ? 1 : 0; +} + +//***************************************************************************** +// +// Determines whether or not a client has consumed all received data previously +// passed to it. +// +//! \param psDevice is the pointer to the device instance structure as returned +//! by USBDCDCInit(). +// +// This function is called to determine whether or not a device has consumed +// all data previously passed to it via its receive callback. +// +// \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +static tBoolean +DeviceConsumedAllData(const tUSBDCDCDevice *psDevice) +{ + unsigned long ulRemaining; + + // + // Send the device an event requesting that it tell us how many bytes + // of data it still has to process. + // + ulRemaining = psDevice->pfnRxCallback(psDevice->pvRxCBData, + USB_EVENT_DATA_REMAINING, 0, (void *)0); + + // + // If any data remains to be processed, return false, else return true. + // + return(ulRemaining ? false : true); +} + +//***************************************************************************** +// +// Notifies the client that it should set or clear a break condition. +// +// \param psDevice is the pointer to the device instance structure as returned +// by USBDCDCInit(). +// \param bSend is \b true if a break condition is to be set or \b false if +// it is to be cleared. +// +// This function is called to instruct the client to start or stop sending a +// break condition on its serial transmit line. +// +// \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +static void +SendBreak(const tUSBDCDCDevice *psDevice, tBoolean bSend) +{ + tCDCSerInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Set the break state flags as necessary. If we are turning the break on, + // set the flag to tell ourselves that we need to notify the client when + // it is time to turn it off again. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, CDC_DO_SEND_BREAK, false); + SetDeferredOpFlag(&psInst->usDeferredOpFlags, CDC_DO_CLEAR_BREAK, bSend); + + // + // Tell the client to start or stop sending the break. + // + psDevice->pfnControlCallback(psDevice->pvControlCBData, + (bSend ? USBD_CDC_EVENT_SEND_BREAK : + USBD_CDC_EVENT_CLEAR_BREAK), 0, + (void *)0); +} + +//***************************************************************************** +// +// Notifies the client of a host request to set the serial communication +// parameters. +// +// \param psDevice is the device instance whose communication parameters are to +// be set. +// +// This function is called to notify the client when the host requests a change +// in the serial communication parameters (baud rate, parity, number of bits +// per character and number of stop bits) to use. +// +// \return None. +// +//***************************************************************************** +static void +SendLineCodingChange(const tUSBDCDCDevice *psDevice) +{ + tCDCSerInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Clear the flag we use to tell ourselves that the line coding change has + // yet to be notified to the client. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, CDC_DO_LINE_CODING_CHANGE, + false); + + // + // Tell the client to update their serial line coding parameters. + // + psDevice->pfnControlCallback(psDevice->pvControlCBData, + USBD_CDC_EVENT_SET_LINE_CODING, 0, + &(psInst->sLineCoding)); +} + +//***************************************************************************** +// +// Notifies the client of a host request to set the RTS and DTR handshake line +// states. +// +// \param psDevice is the device instance whose break condition is to be set or +// cleared. +// +// This function is called to notify the client when the host requests a change +// in the state of one or other of the RTS and DTR handshake lines. +// +// \return None. +// +//***************************************************************************** +static void +SendLineStateChange(const tUSBDCDCDevice *psDevice) +{ + tCDCSerInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Clear the flag we use to tell ourselves that the line coding change has + // yet to be notified to the client. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, CDC_DO_LINE_STATE_CHANGE, + false); + + // + // Tell the client to update their serial line coding parameters. + // + psDevice->pfnControlCallback(psDevice->pvControlCBData, + USBD_CDC_EVENT_SET_CONTROL_LINE_STATE, + psInst->usControlLineState, + (void *)0); +} + +//***************************************************************************** +// +// Notifies the client of a break request if no data remains to be processed. +// +// \param psDevice is the device instance that is to be commanded to send a +// break condition. +// +// This function is called when the host requests that the device set a break +// condition on the serial transmit line. If no data received from the host +// remains to be processed, the break request is passed to the control +// callback. If data is outstanding, the call is ignored (with the operation +// being retried on the next timer tick). +// +// \return Returns \b true if the break notification was sent, \b false +// otherwise. +// +//***************************************************************************** +static tBoolean +CheckAndSendBreak(const tUSBDCDCDevice *psDevice, unsigned short usDuration) +{ + tBoolean bCanSend; + + // + // Has the client consumed all data received from the host yet? + // + bCanSend = DeviceConsumedAllData(psDevice); + + // + // Can we send the break request? + // + if(bCanSend) + { + // + // Pass the break request on to the client since no data remains to be + // consumed. + // + SendBreak(psDevice, (usDuration ? true : false)); + } + + // + // Tell the caller whether or not we sent the notification. + // + return(bCanSend); +} + +//***************************************************************************** +// +// Notifies the client of a request to change the serial line parameters if no +// data remains to be processed. +// +// \param psDevice is the device instance whose line coding parameters are to +// be changed. +// +// This function is called when the host requests that the device change the +// serial line coding parameters. If no data received from the host remains +// to be processed, the request is passed to the control callback. If data is +// outstanding, the call is ignored (with the operation being retried on the +// next timer tick). +// +// \return Returns \b true if the notification was sent, \b false otherwise. +// +//***************************************************************************** +static tBoolean +CheckAndSendLineCodingChange(const tUSBDCDCDevice *psDevice) +{ + tBoolean bCanSend; + + // + // Has the client consumed all data received from the host yet? + // + bCanSend = DeviceConsumedAllData(psDevice); + + // + // Can we send the break request? + // + if(bCanSend) + { + // + // Pass the request on to the client since no data remains to be + // consumed. + // + SendLineCodingChange(psDevice); + } + + // + // Tell the caller whether or not we sent the notification. + // + return(bCanSend); +} + +//***************************************************************************** +// +// Notifies the client of a request to change the handshake line states if no +// data remains to be processed. +// +// \param psDevice is the device instance whose handshake line states are to +// be changed. +// +// This function is called when the host requests that the device change the +// state of one or other of the RTS or DTR handshake lines. If no data +// received from the host remains to be processed, the request is passed to +// the control callback. If data is outstanding, the call is ignored (with +// the operation being retried on the next timer tick). +// +// \return Returns \b true if the notification was sent, \b false otherwise. +// +//***************************************************************************** +static tBoolean +CheckAndSendLineStateChange(const tUSBDCDCDevice *psDevice) +{ + tBoolean bCanSend; + + // + // Has the client consumed all data received from the host yet? + // + bCanSend = DeviceConsumedAllData(psDevice); + + // + // Can we send the break request? + // + if(bCanSend) + { + // + // Pass the request on to the client since no data remains to be + // consumed. + // + SendLineStateChange(psDevice); + } + + // + // Tell the caller whether or not we sent the notification. + // + return(bCanSend); +} + +//***************************************************************************** +// +// Notifies the client of a change in the serial line state. +// +// \param psInst is the instance whose serial state is to be reported. +// +// This function is called to send the current serial state information to +// the host via the the interrupt IN endpoint. This notification informs the +// host of problems or conditions such as parity errors, breaks received, +// framing errors, etc. +// +// \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +static tBoolean +SendSerialState(const tUSBDCDCDevice *psDevice) +{ + tUSBRequest sRequest; + unsigned short usSerialState; + tCDCSerInstance *psInst; + long lRetcode; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Remember that we are in the middle of sending a notification. + // + psInst->eCDCInterruptState = CDC_STATE_WAIT_DATA; + + // + // Clear the flag we use to indicate that a send is required. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, CDC_DO_SERIAL_STATE_CHANGE, + false); + // + // Take a snapshot of the serial state. + // + usSerialState = psInst->usSerialState; + + // + // Build the request we will use to send the notification. + // + sRequest.bmRequestType = (USB_RTYPE_DIR_IN | USB_RTYPE_CLASS | + USB_RTYPE_INTERFACE); + sRequest.bRequest = USB_CDC_NOTIFY_SERIAL_STATE; + sRequest.wValue = 0; + sRequest.wIndex = 0; + sRequest.wLength = USB_CDC_NOTIFY_SERIAL_STATE_SIZE; + + // + // Write the request structure to the USB FIFO. + // + lRetcode = MAP_USBEndpointDataPut(psInst->ulUSBBase, + psInst->ucControlEndpoint, + (unsigned char *)&sRequest, + sizeof(tUSBRequest)); + lRetcode = MAP_USBEndpointDataPut(psInst->ulUSBBase, psInst->ucControlEndpoint, + (unsigned char *)&usSerialState, + USB_CDC_NOTIFY_SERIAL_STATE_SIZE); + + // + // Did we correctly write the data to the endpoint FIFO? + // + if(lRetcode != -1) + { + // + // We put the data into the FIFO so now schedule it to be + // sent. + // + lRetcode = MAP_USBEndpointDataSend(psInst->ulUSBBase, + psInst->ucControlEndpoint, + USB_TRANS_IN); + } + + // + // If an error occurred, mark the endpoint as idle (to prevent possible + // lockup) and return an error. + // + if(lRetcode == -1) + { + psInst->eCDCInterruptState = CDC_STATE_IDLE; + return(false); + } + else + { + // + // Everything went fine. Clear the error bits that we just notified + // and return true. + // + psInst->usSerialState &= ~(usSerialState & USB_CDC_SERIAL_ERRORS); + return(true); + } +} + +//***************************************************************************** +// +// Receives notifications related to data received from the host. +// +// \param psDevice is the device instance whose endpoint is to be processed. +// \param ulStatus is the USB interrupt status that caused this function to +// be called. +// +// This function is called from HandleEndpoints for all interrupts signaling +// the arrival of data on the bulk OUT endpoint (in other words, whenever the +// host has sent us a packet of data). We inform the client that a packet +// is available and, on return, check to see if the packet has been read. If +// not, we schedule another notification to the client for a later time. +// +// \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +tBoolean +ProcessDataFromHost(const tUSBDCDCDevice *psDevice, unsigned long ulStatus) +{ + unsigned long ulEPStatus; + unsigned long ulSize; + tCDCSerInstance *psInst; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Get the endpoint status to see why we were called. + // + ulEPStatus = MAP_USBEndpointStatus(psInst->ulUSBBase, + psInst->ucBulkOUTEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(psInst->ulUSBBase, psInst->ucBulkOUTEndpoint, + ulEPStatus); + + // + // Has a packet been received? + // + if(ulEPStatus & USB_DEV_RX_PKT_RDY) + { + // + // Set the flag we use to indicate that a packet read is pending. This + // will be cleared if the packet is read. If the client doesn't read + // the packet in the context of the USB_EVENT_RX_AVAILABLE callback, + // the event will be notified later during tick processing. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, CDC_DO_PACKET_RX, true); + + // + // Is the receive channel currently blocked? + // + if(!psInst->bControlBlocked && !psInst->bRxBlocked) + { + // + // How big is the packet we've just been sent? + // + ulSize = MAP_USBEndpointDataAvail(psInst->ulUSBBase, + psInst->ucBulkOUTEndpoint); + + // + // The receive channel is not blocked so let the caller know + // that a packet is waiting. The parameters are set to indicate + // that the packet has not been read from the hardware FIFO yet. + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, + USB_EVENT_RX_AVAILABLE, ulSize, + (void *)0); + } + } + else + { + // + // No packet was received. Some error must have been reported. Check + // and pass this on to the client if necessary. + // + if(ulEPStatus & USB_RX_ERROR_FLAGS) + { + // + // This is an error we report to the client so... + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, + USB_EVENT_ERROR, + (ulEPStatus & USB_RX_ERROR_FLAGS), + (void *)0); + } + + return(false); + } + + return(true); +} + +//***************************************************************************** +// +// Receives notifications related to interrupt messages sent to the host. +// +// \param psDevice is the device instance whose endpoint is to be processed. +// \param ulStatus is the USB interrupt status that caused this function to +// be called. +// +// This function is called from HandleEndpoints for all interrupts originating +// from the interrupt IN endpoint (in other words, whenever a notification has +// been transmitted to the USB host). +// +// \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +tBoolean +ProcessNotificationToHost(const tUSBDCDCDevice *psDevice, + unsigned long ulStatus) +{ + unsigned long ulEPStatus; + tCDCSerInstance *psInst; + tBoolean bRetcode; + + // + // Assume all will go well until we have reason to believe otherwise. + // + bRetcode = true; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Get the endpoint status to see why we were called. + // + ulEPStatus = MAP_USBEndpointStatus(psInst->ulUSBBase, + psInst->ucControlEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(psInst->ulUSBBase, + psInst->ucControlEndpoint, ulEPStatus); + + // + // Did the state change while we were waiting for the previous notification + // to complete? + // + if(psInst->usDeferredOpFlags & (1 << CDC_DO_SERIAL_STATE_CHANGE)) + { + // + // The state changed while we were waiting so we need to schedule + // another notification immediately. + // + bRetcode = SendSerialState(psDevice); + } + else + { + // + // Our last notification completed and we didn't have any new + // notifications to make so the interrupt channel is now idle again. + // + psInst->eCDCInterruptState = CDC_STATE_IDLE; + } + + // + // Tell the caller how things went. + // + return(bRetcode); +} + +//***************************************************************************** +// +// Receives notifications related to data sent to the host. +// +// \param psDevice is the device instance whose endpoint is to be processed. +// \param ulStatus is the USB interrupt status that caused this function to +// be called. +// +// This function is called from HandleEndpoints for all interrupts originating +// from the bulk IN endpoint (in other words, whenever data has been +// transmitted to the USB host). We examine the cause of the interrupt and, +// if due to completion of a transmission, notify the client. +// +// \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +tBoolean +ProcessDataToHost(const tUSBDCDCDevice *psDevice, unsigned long ulStatus) +{ + tCDCSerInstance *psInst; + unsigned long ulEPStatus, ulSize; + tBoolean bSentFullPacket; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Get the endpoint status to see why we were called. + // + ulEPStatus = MAP_USBEndpointStatus(psInst->ulUSBBase, + psInst->ucBulkINEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(psInst->ulUSBBase, + psInst->ucBulkINEndpoint, ulEPStatus); + + // + // Our last transmission completed. Clear our state back to idle and + // see if we need to send any more data. + // + psInst->eCDCTxState = CDC_STATE_IDLE; + + // + // If this notification isn't as a result of sending a zero-length packet, + // call back to the client to let it know we sent the last thing it passed + // us. + // + if(psInst->usLastTxSize) + { + // + // Have we just sent a 64 byte packet? + // + bSentFullPacket = (psInst->usLastTxSize == DATA_IN_EP_MAX_SIZE) ? + true : false; + + // + // Notify the client that the last transmission completed. + // + ulSize = (unsigned long)psInst->usLastTxSize; + psInst->usLastTxSize = 0; + psDevice->pfnTxCallback(psDevice->pvTxCBData, USB_EVENT_TX_COMPLETE, + ulSize, (void *)0); + + // + // If we had previously sent a full packet and the callback didn't + // schedule a new transmission, send a zero length packet to indicate + // the end of the transfer. + // + if(bSentFullPacket && !psInst->usLastTxSize) + { + // + // We can expect another transmit complete notification after doing + // this. + // + psInst->eCDCTxState = CDC_STATE_WAIT_DATA; + + // + // Send the zero-length packet. + // + MAP_USBEndpointDataSend(psInst->ulUSBBase, + psInst->ucBulkINEndpoint, + USB_TRANS_IN); + } + } + + return(true); +} + +//***************************************************************************** +// +// Called by the USB stack for any activity involving one of our endpoints +// other than EP0. This function is a fan out that merely directs the call to +// the correct handler depending upon the endpoint and transaction direction +// signaled in ulStatus. +// +//***************************************************************************** +static void +HandleEndpoints(void *pvInstance, unsigned long ulStatus) +{ + const tUSBDCDCDevice *psDeviceInst; + tCDCSerInstance *psInst; + + ASSERT(pvInstance != 0); + + // + // Determine if the serial device is in single or composite mode because + // the meaning of ulIndex is different in both cases. + // + psDeviceInst = pvInstance; + psInst = psDeviceInst->psPrivateCDCSerData; + + // + // Handler for the interrupt IN notification endpoint. + // + if(ulStatus & (1 << USB_EP_TO_INDEX(psInst->ucControlEndpoint))) + { + // + // We have sent an interrupt notification to the host. + // + ProcessNotificationToHost(psDeviceInst, ulStatus); + } + + // + // Handler for the bulk OUT data endpoint. + // + if(ulStatus & (0x10000 << USB_EP_TO_INDEX(psInst->ucBulkOUTEndpoint))) + { + // + // Data is being sent to us from the host. + // + ProcessDataFromHost(psDeviceInst, ulStatus); + } + + // + // Handler for the bulk IN data endpoint. + // + if(ulStatus & (1 << USB_EP_TO_INDEX(psInst->ucBulkINEndpoint))) + { + ProcessDataToHost(psDeviceInst, ulStatus); + } +} + +//***************************************************************************** +// +// Called by the USB stack whenever a configuration change occurs. +// +//***************************************************************************** +static void +HandleConfigChange(void *pvInstance, unsigned long ulInfo) +{ + tCDCSerInstance *psInst; + const tUSBDCDCDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create a device instance pointer. + // + psDevice = (const tUSBDCDCDevice *)pvInstance; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Set all our endpoints to idle state. + // + psInst->eCDCInterruptState = CDC_STATE_IDLE; + psInst->eCDCRequestState = CDC_STATE_IDLE; + psInst->eCDCRxState = CDC_STATE_IDLE; + psInst->eCDCTxState = CDC_STATE_IDLE; + + // + // If we are not currently connected so let the client know we are open + // for business. + // + if(!psInst->bConnected) + { + // + // Pass the connected event to the client. + // + psDevice->pfnControlCallback(psDevice->pvControlCBData, + USB_EVENT_CONNECTED, 0, (void *)0); + } + + // + // Remember that we are connected. + // + psInst->bConnected = true; +} + +//***************************************************************************** +// +// USB data received callback. +// +// This function is called by the USB stack whenever any data requested from +// EP0 is received. +// +//***************************************************************************** +static void +HandleEP0Data(void *pvInstance, unsigned long ulDataSize) +{ + const tUSBDCDCDevice *psDevice; + tCDCSerInstance *psInst; + tBoolean bRetcode; + + ASSERT(pvInstance != 0); + + // + // Create a device instance pointer. + // + psDevice = (const tUSBDCDCDevice *)pvInstance; + + // + // If we were not passed any data, just return. + // + if(ulDataSize == 0) + { + return; + } + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Make sure we are actually expecting something. + // + if(psInst->eCDCRequestState != CDC_STATE_WAIT_DATA) + { + return; + } + + // + // Process the data received. This will be a request-specific data + // block associated with the last request received. + // + switch (psInst->ucPendingRequest) + { + // + // We just got the line coding structure. Make sure the client has + // read all outstanding data then pass it back to initiate a change + // in the line state. + // + case USB_CDC_SET_LINE_CODING: + { + if(ulDataSize != sizeof(tLineCoding)) + { + USBDCDStallEP0(0); + } + else + { + // + // Set the flag telling us that we need to send a line coding + // notification to the client. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, + CDC_DO_LINE_CODING_CHANGE, true); + + // + // See if we can send the notification immediately. + // + bRetcode = CheckAndSendLineCodingChange(psDevice); + + // + // If we couldn't send the line coding change request to the + // client, block reception of more data from the host until + // previous data is processed and we send the change request. + // + if(!bRetcode) + { + psInst->bRxBlocked = true; + } + } + break; + } + + // + // Oops - we seem to be waiting on a request which has not yet been + // coded here. Flag the error and stall EP0 anyway (even though + // this would indicate a coding error). + // + default: + { + USBDCDStallEP0(0); + ASSERT(0); + break; + } + } + + // + // All is well. Set the state back to IDLE. + // + psInst->eCDCRequestState = CDC_STATE_IDLE; +} + +//***************************************************************************** +// +// Device instance specific handler. +// +//***************************************************************************** +static void +HandleDevice(void *pvInstance, unsigned long ulRequest, void *pvRequestData) +{ + tCDCSerInstance *psInst; + unsigned char *pucData; + + // + // Create the serial instance data. + // + psInst = ((tUSBDCDCDevice *)pvInstance)->psPrivateCDCSerData; + + // + // Create the char array used by the events supported by the USB CDC + // serial class. + // + pucData = (unsigned char *)pvRequestData; + + switch(ulRequest) + { + // + // This was an interface change event. + // + case USB_EVENT_COMP_IFACE_CHANGE: + { + // + // Save the change to the appropriate interface number. + // + if(pucData[0] == SERIAL_INTERFACE_CONTROL) + { + psInst->ucInterfaceControl = pucData[1]; + } + else if(pucData[0] == SERIAL_INTERFACE_DATA) + { + psInst->ucInterfaceData = pucData[1]; + } + break; + } + + // + // This was an endpoint change event. + // + case USB_EVENT_COMP_EP_CHANGE: + { + // + // Determine if this is an IN or OUT endpoint that has changed. + // + if(pucData[0] & USB_EP_DESC_IN) + { + // + // Determine which IN endpoint to modify. + // + if((pucData[0] & 0x7f) == USB_EP_TO_INDEX(CONTROL_ENDPOINT)) + { + psInst->ucControlEndpoint = + INDEX_TO_USB_EP((pucData[1] & 0x7f)); + } + else + { + psInst->ucBulkINEndpoint = + INDEX_TO_USB_EP((pucData[1] & 0x7f)); + } + } + else + { + // + // Extract the new endpoint number. + // + psInst->ucBulkOUTEndpoint = + INDEX_TO_USB_EP(pucData[1] & 0x7f); + } + break; + } + + // + // Handle class specific reconfiguring of the configuration descriptor + // once the composite class has built the full descriptor. + // + case USB_EVENT_COMP_CONFIG: + { + // + // This sets the bFirstInterface of the Interface Association + // descriptor to the first interface which is the control + // interface used by this instance. + // + pucData[2] = psInst->ucInterfaceControl; + + // + // This sets the bMasterInterface of the Union descriptor to the + // Control interface and the bSlaveInterface of the Union + // Descriptor to the Data interface used by this instance. + // + pucData[29] = psInst->ucInterfaceControl; + pucData[30] = psInst->ucInterfaceData; + + // + // This sets the bDataInterface of the Union descriptor to the + // Data interface used by this instance. + pucData[35] = psInst->ucInterfaceData; + break; + } + + default: + { + break; + } + } +} + +//***************************************************************************** +// +// USB non-standard request callback. +// +// This function is called by the USB stack whenever any non-standard request +// is made to the device. The handler should process any requests that it +// supports or stall EP0 in any unsupported cases. +// +//***************************************************************************** +static void +HandleRequests(void *pvInstance, tUSBRequest *pUSBRequest) +{ + const tUSBDCDCDevice *psDevice; + tCDCSerInstance *psInst; + tBoolean bRetcode; + + ASSERT(pvInstance != 0); + + // + // Create a device instance pointer. + // + psDevice = (const tUSBDCDCDevice *)pvInstance; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Only handle requests meant for this interface. + // + if(pUSBRequest->wIndex != psInst->ucInterfaceControl) + { + return; + } + + // + // Handle each of the requests that we expect from the host. + // + switch(pUSBRequest->bRequest) + { + case USB_CDC_SEND_ENCAPSULATED_COMMAND: + { + // + // This implementation makes use of no communication protocol so + // this request is meaningless. We stall endpoint 0 if we receive + // it. + // + USBDCDStallEP0(0); + break; + } + + case USB_CDC_GET_ENCAPSULATED_RESPONSE: + { + // + // This implementation makes use of no communication protocol so + // this request is meaningless. We stall endpoint 0 if we receive + // it. + // + USBDCDStallEP0(0); + break; + } + + case USB_CDC_SET_COMM_FEATURE: + { + // + // This request is apparently required by an ACM device but does + // not appear relevant to a virtual COM port and is never used by + // Windows (or, at least, is not seen when using Hyperterminal or + // TeraTerm via a Windows virtual COM port). We stall endpoint 0 + // to indicate that we do not support the request. + // + USBDCDStallEP0(0); + break; + } + + case USB_CDC_GET_COMM_FEATURE: + { + // + // This request is apparently required by an ACM device but does + // not appear relevant to a virtual COM port and is never used by + // Windows (or, at least, is not seen when using Hyperterminal or + // TeraTerm via a Windows virtual COM port). We stall endpoint 0 + // to indicate that we do not support the request. + // + USBDCDStallEP0(0); + break; + } + + case USB_CDC_CLEAR_COMM_FEATURE: + { + // + // This request is apparently required by an ACM device but does + // not appear relevant to a virtual COM port and is never used by + // Windows (or, at least, is not seen when using Hyperterminal or + // TeraTerm via a Windows virtual COM port). We stall endpoint 0 + // to indicate that we do not support the request. + // + USBDCDStallEP0(0); + break; + } + + // + // Set the serial communication parameters. + // + case USB_CDC_SET_LINE_CODING: + { + // + // Remember the request we are processing. + // + psInst->ucPendingRequest = USB_CDC_SET_LINE_CODING; + + // + // Set the state to indicate we are waiting for data. + // + psInst->eCDCRequestState = CDC_STATE_WAIT_DATA; + + // + // Now read the payload of the request. We handle the actual + // operation in the data callback once this data is received. + // + USBDCDRequestDataEP0(0, (unsigned char *)&psInst->sLineCoding, + sizeof(tLineCoding)); + + // + // ACK what we have already received. We must do this after + // requesting the data or we get into a race condition where the + // data may return before we have set the stack state appropriately + // to receive it. + // + MAP_USBDevEndpointDataAck(psInst->ulUSBBase, USB_EP_0, false); + + break; + } + + // + // Return the serial communication parameters. + // + case USB_CDC_GET_LINE_CODING: + { + tLineCoding sLineCoding; + + // + // ACK what we have already received + // + MAP_USBDevEndpointDataAck(psInst->ulUSBBase, USB_EP_0, false); + + // + // Ask the client for the current line coding. + // + psDevice->pfnControlCallback(psDevice->pvControlCBData, + USBD_CDC_EVENT_GET_LINE_CODING, 0, + &sLineCoding); + + // + // Send the line coding information back to the host. + // + USBDCDSendDataEP0(0, (unsigned char *)&sLineCoding, + sizeof(tLineCoding)); + + break; + } + + case USB_CDC_SET_CONTROL_LINE_STATE: + { + // + // ACK what we have already received + // + MAP_USBDevEndpointDataAck(psInst->ulUSBBase, USB_EP_0, false); + + // + // Set the handshake lines as required. + // + psInst->usControlLineState = pUSBRequest->wValue; + + // + // Remember that we are due to notify the client of a line + // state change. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, + CDC_DO_LINE_STATE_CHANGE, true); + + // + // See if we can notify now. + // + bRetcode = CheckAndSendLineStateChange(psDevice); + + // + // If we couldn't send the line state change request to the + // client, block reception of more data from the host until + // previous data is processed and we send the change request. + // + if(!bRetcode) + { + psInst->bRxBlocked = true; + } + + break; + } + + case USB_CDC_SEND_BREAK: + { + // + // ACK what we have already received + // + MAP_USBDevEndpointDataAck(psInst->ulUSBBase, USB_EP_0, false); + + // + // Keep a copy of the requested break duration. + // + psInst->usBreakDuration = pUSBRequest->wValue; + + // + // Remember that we need to send a break request. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, + CDC_DO_SEND_BREAK, true); + + // + // Send the break request if all outstanding receive data has been + // processed. + // + bRetcode = CheckAndSendBreak(psDevice, pUSBRequest->wValue); + + // + // If we couldn't send the line coding change request to the + // client, block reception of more data from the host until + // previous data is processed and we send the change request. + // + if(!bRetcode) + { + psInst->bRxBlocked = true; + } + + break; + } + + // + // These are valid CDC requests but not ones that an ACM device should + // receive. + // + case USB_CDC_SET_AUX_LINE_STATE: + case USB_CDC_SET_HOOK_STATE: + case USB_CDC_PULSE_SETUP: + case USB_CDC_SEND_PULSE: + case USB_CDC_SET_PULSE_TIME: + case USB_CDC_RING_AUX_JACK: + case USB_CDC_SET_RINGER_PARMS: + case USB_CDC_GET_RINGER_PARMS: + case USB_CDC_SET_OPERATION_PARMS: + case USB_CDC_GET_OPERATION_PARMS: + case USB_CDC_SET_LINE_PARMS: + case USB_CDC_GET_LINE_PARMS: + case USB_CDC_DIAL_DIGITS: + case USB_CDC_SET_UNIT_PARAMETER: + case USB_CDC_GET_UNIT_PARAMETER: + case USB_CDC_CLEAR_UNIT_PARAMETER: + case USB_CDC_GET_PROFILE: + case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS: + case USB_CDC_SET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER: + case USB_CDC_GET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER: + case USB_CDC_SET_ETHERNET_PACKET_FILTER: + case USB_CDC_GET_ETHERNET_STATISTIC: + case USB_CDC_SET_ATM_DATA_FORMAT: + case USB_CDC_GET_ATM_DEVICE_STATISTICS: + case USB_CDC_SET_ATM_DEFAULT_VC: + case USB_CDC_GET_ATM_VC_STATISTICS: + { + USBDCDStallEP0(0); + break; + } + + default: + { + // + // This request is not part of the CDC specification. + // + USBDCDStallEP0(0); + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device is +// disconnected from the host. +// +//***************************************************************************** +static void +HandleDisconnect(void *pvInstance) +{ + const tUSBDCDCDevice *psCDCDevice; + tCDCSerInstance *psInst; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psCDCDevice = (const tUSBDCDCDevice *)pvInstance; + + // + // Get a pointer to our instance data. + // + psInst = psCDCDevice->psPrivateCDCSerData; + + // + // If we are not currently connected and we have a control callback, + // let the client know we are open for business. + // + if(psInst->bConnected) + { + // + // Pass the disconnected event to the client. + // + psCDCDevice->pfnControlCallback(psCDCDevice->pvControlCBData, + USB_EVENT_DISCONNECTED, 0, (void *)0); + } + + // + // Remember that we are no longer connected. + // + psInst->bConnected = false; +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the bus is put into +// suspend state. +// +//***************************************************************************** +static void +HandleSuspend(void *pvInstance) +{ + const tUSBDCDCDevice *psCDCDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psCDCDevice = (const tUSBDCDCDevice *)pvInstance; + + // + // Pass the event on to the client. + // + psCDCDevice->pfnControlCallback(psCDCDevice->pvControlCBData, + USB_EVENT_SUSPEND, 0, (void *)0); +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the bus is taken +// out of suspend state. +// +//***************************************************************************** +static void +HandleResume(void *pvInstance) +{ + tUSBDCDCDevice *psCDCDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psCDCDevice = (tUSBDCDCDevice *)pvInstance; + + // + // Pass the event on to the client. + // + psCDCDevice->pfnControlCallback(psCDCDevice->pvControlCBData, + USB_EVENT_RESUME, 0, (void *)0); +} + +//***************************************************************************** +// +// This function is called periodically and provides us with a time reference +// and method of implementing delayed or time-dependent operations. +// +// \param ulIndex is the index of the USB controller for which this tick +// is being generated. +// \param ulTimemS is the elapsed time in milliseconds since the last call +// to this function. +// +// \return None. +// +//***************************************************************************** +static void +CDCTickHandler(void *pvInstance, unsigned long ulTimemS) +{ + tBoolean bCanSend; + const tUSBDCDCDevice *psDevice; + tCDCSerInstance *psInst; + unsigned long ulSize; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDCDCDevice *)pvInstance; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateCDCSerData; + + // + // Is there any outstanding operation that we should try to perform? + // + if(psInst->usDeferredOpFlags) + { + // + // Yes - we have at least one deferred operation pending. First check + // to see if it is time to turn off a break condition. + // + if(psInst->usDeferredOpFlags & (1 << CDC_DO_CLEAR_BREAK)) + { + // + // Will our break timer expire this time? + // + if(psInst->usBreakDuration <= ulTimemS) + { + // + // Yes - turn off the break condition. + // + SendBreak(psDevice, false); + } + else + { + // + // We have not timed out yet. Decrement the break timer. + // + psInst->usBreakDuration -= (unsigned short)ulTimemS; + } + } + + // Now check to see if the client has any data remaining to be + // processed. This information is needed by the remaining deferred + // operations which are waiting for the receive pipe to be emptied + // before they can be carried out. + // + bCanSend = DeviceConsumedAllData(psDevice); + + // + // Has all outstanding data been consumed? + // + if(bCanSend) + { + // + // Yes - go ahead and notify the client of the various things + // it has been asked to do while we waited for data to be + // consumed. + // + + // + // Do we need to start sending a break condition? + // + if(psInst->usDeferredOpFlags & (1 << CDC_DO_SEND_BREAK)) + { + SendBreak(psDevice, true); + } + + // + // Do we need to set the RTS/DTR states? + // + if(psInst->usDeferredOpFlags & (1 << CDC_DO_LINE_STATE_CHANGE)) + { + SendLineStateChange(psDevice); + } + + // + // Do we need to change the line coding parameters? + // + if(psInst->usDeferredOpFlags & (1 << CDC_DO_LINE_CODING_CHANGE)) + { + SendLineCodingChange(psDevice); + } + + // + // NOTE: We do not need to handle CDC_DO_SERIAL_STATE_CHANGE here + // since this is handled in the transmission complete notification + // for the control IN endpoint (ProcessNotificationToHost()). + // + + // + // If all the deferred operations which caused the receive channel + // to be blocked are now handled, we can unblock receive and handle + // any packet that is currently waiting to be received. + // + if(!(psInst->usDeferredOpFlags & RX_BLOCK_OPS)) + { + // + // We can remove the receive block. + // + psInst->bRxBlocked = false; + } + } + + // + // Is the receive channel unblocked? + // + if(!psInst->bRxBlocked) + { + // + // Do we have a deferred receive waiting + // + if(psInst->usDeferredOpFlags & (1 << CDC_DO_PACKET_RX)) + { + // + // Yes - how big is the waiting packet? + // + ulSize = MAP_USBEndpointDataAvail(psInst->ulUSBBase, + psInst->ucBulkOUTEndpoint); + + // Tell the client that there is a packet waiting for it. + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, + USB_EVENT_RX_AVAILABLE, ulSize, + (void *)0); + } + } + } + + return; +} + +//***************************************************************************** +// +//! Initializes CDC device operation when used with a composite device. +//! +//! \param ulIndex is the index of the USB controller in use. +//! \param psCDCDevice points to a structure containing parameters customizing +//! the operation of the CDC device. +//! +//! This call is very similar to USBDCDCInit() except that it is used for +//! initializing an instance of the serial device for use in a composite device. +//! +//! \return Returns NULL on failure or the psCDCDevice pointer on success. +// +//***************************************************************************** +void * +USBDCDCCompositeInit(unsigned long ulIndex, const tUSBDCDCDevice *psCDCDevice) +{ + tCDCSerInstance *psInst; + tDeviceDescriptor *psDevDesc; + + // + // Check parameter validity. + // + ASSERT(ulIndex == 0); + ASSERT(psCDCDevice); + ASSERT(psCDCDevice->psPrivateCDCSerData); + ASSERT(psCDCDevice->pfnControlCallback); + ASSERT(psCDCDevice->pfnRxCallback); + ASSERT(psCDCDevice->pfnTxCallback); + + // + // Create an instance pointer to the private data area. + // + psInst = psCDCDevice->psPrivateCDCSerData; + + // + // Set the default endpoint and interface assignments. + // + psInst->ucBulkINEndpoint = DATA_IN_ENDPOINT; + psInst->ucBulkOUTEndpoint = DATA_OUT_ENDPOINT; + psInst->ucInterfaceControl = SERIAL_INTERFACE_CONTROL; + psInst->ucInterfaceData = SERIAL_INTERFACE_DATA; + + // + // By default do not use the interrupt control endpoint. The single + // instance CDC serial device will turn this on in USBDCDCInit(); + // + psInst->ucControlEndpoint = CONTROL_ENDPOINT; + + // + // Initialize the workspace in the passed instance structure. + // + psInst->psConfDescriptor = (tConfigDescriptor *)g_pCDCSerDescriptor; + psInst->psDevInfo = &g_sCDCSerDeviceInfo; + psInst->ulUSBBase = USB0_BASE; + psInst->eCDCRxState = CDC_STATE_UNCONFIGURED; + psInst->eCDCTxState = CDC_STATE_UNCONFIGURED; + psInst->eCDCInterruptState = CDC_STATE_UNCONFIGURED; + psInst->eCDCRequestState = CDC_STATE_UNCONFIGURED; + psInst->ucPendingRequest = 0; + psInst->usBreakDuration = 0; + psInst->usSerialState = 0; + psInst->usDeferredOpFlags = 0; + psInst->usControlLineState = 0; + psInst->bRxBlocked = false; + psInst->bControlBlocked = false; + psInst->bConnected = false; + + // + // Fix up the device descriptor with the client-supplied values. + // + psDevDesc = (tDeviceDescriptor *)psInst->psDevInfo->pDeviceDescriptor; + psDevDesc->idVendor = psCDCDevice->usVID; + psDevDesc->idProduct = psCDCDevice->usPID; + + // + // Fix up the configuration descriptor with client-supplied values. + // + psInst->psConfDescriptor->bmAttributes = psCDCDevice->ucPwrAttributes; + psInst->psConfDescriptor->bMaxPower = + (unsigned char)(psCDCDevice->usMaxPowermA / 2); + + // + // Plug in the client's string stable to the device information + // structure. + // + psInst->psDevInfo->ppStringDescriptors = psCDCDevice->ppStringDescriptors; + psInst->psDevInfo->ulNumStringDescriptors + = psCDCDevice->ulNumStringDescriptors; + + // + // Initialize the USB tick module, this will prevent it from being + // initialized later in the call to USBDCDInit(); + // + InternalUSBTickInit(); + + // + // Register our tick handler (this must be done after USBDCDInit). + // + InternalUSBRegisterTickHandler(CDCTickHandler, + (void *)psCDCDevice); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psCDCDevice); +} + +//***************************************************************************** +// +//! Initializes CDC device operation for a given USB controller. +//! +//! \param ulIndex is the index of the USB controller which is to be +//! initialized for CDC device operation. +//! \param psCDCDevice points to a structure containing parameters customizing +//! the operation of the CDC device. +//! +//! An application wishing to make use of a USB CDC communication channel and +//! appear as a virtual serial port on the host system must call this function +//! to initialize the USB controller and attach the device to the USB bus. +//! This function performs all required USB initialization. +//! +//! The value returned by this function is the \e psCDCDevice pointer passed +//! to it if successful. This pointer must be passed to all later calls to the +//! CDC class driver to identify the device instance. +//! +//! The USB CDC device class driver offers packet-based transmit and receive +//! operation. If the application would rather use block based communication +//! with transmit and receive buffers, USB buffers on the transmit and receive +//! channels may be used to offer this functionality. +//! +//! Transmit Operation: +//! +//! Calls to USBDCDCPacketWrite() must send no more than 64 bytes of data at a +//! time and may only be made when no other transmission is currently +//! outstanding. +//! +//! Once a packet of data has been acknowledged by the USB host, a +//! \b USB_EVENT_TX_COMPLETE event is sent to the application callback to +//! inform it that another packet may be transmitted. +//! +//! Receive Operation: +//! +//! An incoming USB data packet will result in a call to the application +//! callback with event \b USB_EVENT_RX_AVAILABLE. The application must then +//! call USBDCDCPacketRead(), passing a buffer capable of holding the received +//! packet to retrieve the data and acknowledge reception to the USB host. The +//! size of the received packet may be queried by calling +//! USBDCDCRxPacketAvailable(). +//! +//! \note The application must not make any calls to the low level USB Device +//! API if interacting with USB via the CDC device class API. Doing so +//! will cause unpredictable (though almost certainly unpleasant) behavior. +//! +//! \return Returns NULL on failure or the psCDCDevice pointer on success. +// +//***************************************************************************** +void * +USBDCDCInit(unsigned long ulIndex, const tUSBDCDCDevice *psCDCDevice) +{ + void *pvRet; + tCDCSerInstance *psInst; + + // + // Initialize the internal state for this class. + // + pvRet = USBDCDCCompositeInit(ulIndex, psCDCDevice); + + if(pvRet) + { + // + // Create an instance pointer to the private data area. + // + psInst = psCDCDevice->psPrivateCDCSerData; + + // + // Set the instance data for this device so that USBDCDInit() call can + // have the instance data. + // + psInst->psDevInfo->pvInstance = (void *)psCDCDevice; + + // + // Enable the default interrupt control endpoint if this class is not + // being used in a composite device. + // + psInst->ucControlEndpoint = CONTROL_ENDPOINT; + + // + // Use the configuration descriptor with the interrupt control endpoint. + // + psInst->psDevInfo->ppConfigDescriptors = g_pCDCSerConfigDescriptors; + + // + // All is well so now pass the descriptors to the lower layer and put + // the CDC device on the bus. + // + USBDCDInit(ulIndex, psInst->psDevInfo); + } + + return(pvRet); +} + +//***************************************************************************** +// +//! Shuts down the CDC device instance. +//! +//! \param pvInstance is the pointer to the device instance structure as returned +//! by USBDCDCInit(). +//! +//! This function terminates CDC operation for the instance supplied and +//! removes the device from the USB bus. This function should not be called +//! if the CDC device is part of a composite device and instead the +//! USBDCompositeTerm() function should be called for the full composite +//! device. +//! +//! Following this call, the \e pvInstance instance should not me used in any +//! other calls. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDCTerm(void *pvInstance) +{ + tCDCSerInstance *psInst; + + ASSERT(pvInstance); + + // + // Get a pointer to our instance data. + // + psInst = ((tUSBDCDCDevice *)pvInstance)->psPrivateCDCSerData; + + // + // Terminate the requested instance. + // + USBDCDTerm(USB_BASE_TO_INDEX(psInst->ulUSBBase)); + + psInst->ulUSBBase = 0; + psInst->psDevInfo = (tDeviceInfo *)0; + psInst->psConfDescriptor = (tConfigDescriptor *)0; + + return; +} + +//***************************************************************************** +// +//! Sets the client-specific pointer for the control callback. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDCDCInit(). +//! \param pvCBData is the pointer that client wishes to be provided on each +//! event sent to the control channel callback function. +//! +//! The client uses this function to change the callback pointer passed in +//! the first parameter on all callbacks to the \e pfnControlCallback function +//! passed on USBDCDCInit(). +//! +//! If a client wants to make runtime changes in the callback pointer, it must +//! ensure that the psCDCDevice structure passed to USBDCDCInit() resides in +//! RAM. If this structure is in flash, callback pointer changes will not be +//! possible. +//! +//! \return Returns the previous callback pointer that was being used for +//! this instance's control callback. +// +//***************************************************************************** +void * +USBDCDCSetControlCBData(void *pvInstance, void *pvCBData) +{ + void *pvOldValue; + + ASSERT(pvInstance); + + // + // Set the callback pointer for the control channel after remembering the + // previous value. + // + pvOldValue = ((tUSBDCDCDevice *)pvInstance)->pvControlCBData; + ((tUSBDCDCDevice *)pvInstance)->pvControlCBData = pvCBData; + + // + // Return the previous callback data value. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Sets the client-specific data parameter for the receive channel callback. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDCDCInit(). +//! \param pvCBData is the pointer that client wishes to be provided on each +//! event sent to the receive channel callback function. +//! +//! The client uses this function to change the callback pointer passed in +//! the first parameter on all callbacks to the \e pfnRxCallback function +//! passed on USBDCDCInit(). +//! +//! If a client wants to make runtime changes in the callback pointer, it must +//! ensure that the psCDCDevice structure passed to USBDCDCInit() resides in +//! RAM. If this structure is in flash, callback data changes will not be +//! possible. +//! +//! \return Returns the previous callback pointer that was being used for +//! this instance's receive callback. +// +//***************************************************************************** +void * +USBDCDCSetRxCBData(void *pvInstance, void *pvCBData) +{ + void *pvOldValue; + + ASSERT(pvInstance); + + // + // Set the callback data for the receive channel after remembering the + // previous value. + // + pvOldValue = ((tUSBDCDCDevice *)pvInstance)->pvRxCBData; + ((tUSBDCDCDevice *)pvInstance)->pvRxCBData = pvCBData; + + // + // Return the previous callback pointer. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Sets the client-specific data parameter for the transmit callback. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDCDCInit(). +//! \param pvCBData is the pointer that client wishes to be provided on each +//! event sent to the transmit channel callback function. +//! +//! The client uses this function to change the callback pointer passed in +//! the first parameter on all callbacks to the \e pfnTxCallback function +//! passed on USBDCDCInit(). +//! +//! If a client wants to make runtime changes in the callback pointer, it must +//! ensure that the psCDCDevice structure passed to USBDCDCInit() resides in +//! RAM. If this structure is in flash, callback data changes will not be +//! possible. +//! +//! \return Returns the previous callback pointer that was being used for +//! this instance's transmit callback. +// +//***************************************************************************** +void * +USBDCDCSetTxCBData(void *pvInstance, void *pvCBData) +{ + void *pvOldValue; + + ASSERT(pvInstance); + + // + // Set the callback data for the transmit channel after remembering the + // previous value. + // + pvOldValue = ((tUSBDCDCDevice *)pvInstance)->pvTxCBData; + ((tUSBDCDCDevice *)pvInstance)->pvTxCBData = pvCBData; + + // + // Return the previous callback pointer. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Transmits a packet of data to the USB host via the CDC data interface. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDCDCInit(). +//! \param pcData points to the first byte of data which is to be transmitted. +//! \param ulLength is the number of bytes of data to transmit. +//! \param bLast indicates whether more data is to be written before a packet +//! should be scheduled for transmission. If \b true, the client will make +//! a further call to this function. If \b false, no further call will be +//! made and the driver should schedule transmission of a short packet. +//! +//! This function schedules the supplied data for transmission to the USB +//! host in a single USB packet. If no transmission is currently ongoing +//! the data is immediately copied to the relevant USB endpoint FIFO. If the +//! \e bLast parameter is \b true, the newly written packet is then scheduled +//! for transmission. Whenever a USB packet is acknowledged by the host, a +//! USB_EVENT_TX_COMPLETE event will be sent to the application transmit +//! callback indicating that more data can now be transmitted. +//! +//! The maximum value for ulLength is 64 bytes (the maximum USB packet size +//! for the bulk endpoints in use by CDC). Attempts to send more data than +//! this will result in a return code of 0 indicating that the data cannot be +//! sent. +//! +//! \return Returns the number of bytes actually sent. At this level, this +//! will either be the number of bytes passed (if less than or equal to the +//! maximum packet size for the USB endpoint in use and no outstanding +//! transmission ongoing) or 0 to indicate a failure. +// +//***************************************************************************** +unsigned long +USBDCDCPacketWrite(void *pvInstance, unsigned char *pcData, + unsigned long ulLength, tBoolean bLast) +{ + tCDCSerInstance *psInst; + long lRetcode; + + ASSERT(pvInstance); + + // + // Get our instance data pointer + // + psInst = ((tUSBDCDCDevice *)pvInstance)->psPrivateCDCSerData; + + // + // Can we send the data provided? + // + if((ulLength > DATA_IN_EP_MAX_SIZE) || + (psInst->eCDCTxState != CDC_STATE_IDLE)) + { + // + // Either the packet was too big or we are in the middle of sending + // another packet. Return 0 to indicate that we can't send this data. + // + return(0); + } + + // + // Copy the data into the USB endpoint FIFO. + // + lRetcode = MAP_USBEndpointDataPut(psInst->ulUSBBase, + psInst->ucBulkINEndpoint, pcData, + ulLength); + + // + // Did we copy the data successfully? + // + if(lRetcode != -1) + { + // + // Remember how many bytes we sent. + // + psInst->usLastTxSize += (unsigned short)ulLength; + + // + // If this is the last call for this packet, schedule transmission. + // + if(bLast) + { + // + // Send the packet to the host if we have received all the data we + // can expect for this packet. + // + psInst->eCDCTxState = CDC_STATE_WAIT_DATA; + lRetcode = MAP_USBEndpointDataSend(psInst->ulUSBBase, + psInst->ucBulkINEndpoint, + USB_TRANS_IN); + } + } + + // + // Did an error occur while trying to send the data? + // + if(lRetcode != -1) + { + // + // No - tell the caller we sent all the bytes provided. + // + return(ulLength); + } + else + { + // + // Yes - tell the caller we couldn't send the data. + // + return(0); + } +} + +//***************************************************************************** +// +//! Reads a packet of data received from the USB host via the CDC data +//! interface. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDCDCInit(). +//! \param pcData points to a buffer into which the received data will be +//! written. +//! \param ulLength is the size of the buffer pointed to by pcData. +//! \param bLast indicates whether the client will make a further call to +//! read additional data from the packet. +//! +//! This function reads up to ulLength bytes of data received from the USB +//! host into the supplied application buffer. +//! +//! \note The \e bLast parameter is ignored in this implementation since the +//! end of a packet can be determined without relying upon the client to +//! provide this information. +//! +//! \return Returns the number of bytes of data read. +// +//***************************************************************************** +unsigned long +USBDCDCPacketRead(void *pvInstance, unsigned char *pcData, + unsigned long ulLength, tBoolean bLast) +{ + unsigned long ulEPStatus, ulCount, ulPkt; + tCDCSerInstance *psInst; + long lRetcode; + + ASSERT(pvInstance); + + // + // Get our instance data pointer + // + psInst = ((tUSBDCDCDevice *)pvInstance)->psPrivateCDCSerData; + + // + // Does the relevant endpoint FIFO have a packet waiting for us? + // + ulEPStatus = MAP_USBEndpointStatus(psInst->ulUSBBase, + psInst->ucBulkOUTEndpoint); + + if(ulEPStatus & USB_DEV_RX_PKT_RDY) + { + // + // If receive is currently blocked or the buffer we were passed is + // (potentially) too small, set the flag telling us that we have a + // packet waiting but return 0. + // + if(psInst->bRxBlocked || psInst->bControlBlocked) + { + SetDeferredOpFlag(&psInst->usDeferredOpFlags, + CDC_DO_PACKET_RX, true); + return(0); + } + else + { + // + // It is OK to receive the new packet. How many bytes are + // available for us to receive? + // + ulPkt = MAP_USBEndpointDataAvail(psInst->ulUSBBase, + psInst->ucBulkOUTEndpoint); + + // + // Get as much data as we can. + // + ulCount = ulLength; + lRetcode = MAP_USBEndpointDataGet(psInst->ulUSBBase, + psInst->ucBulkOUTEndpoint, + pcData, &ulCount); + + // + // Did we read the last of the packet data? + // + if(ulCount == ulPkt) + { + // + // Clear the endpoint status so that we know no packet is + // waiting. + // + MAP_USBDevEndpointStatusClear(psInst->ulUSBBase, + psInst->ucBulkOUTEndpoint, + ulEPStatus); + + // + // Acknowledge the data, thus freeing the host to send the + // next packet. + // + MAP_USBDevEndpointDataAck(psInst->ulUSBBase, + psInst->ucBulkOUTEndpoint, + true); + + // + // Clear the flag we set to indicate that a packet read is + // pending. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, + CDC_DO_PACKET_RX, false); + + } + + // + // If all went well, tell the caller how many bytes they got. + // + if(lRetcode != -1) + { + return(ulCount); + } + } + } + + // + // No packet was available or an error occurred while reading so tell + // the caller no bytes were returned. + // + return(0); +} + +//***************************************************************************** +// +//! Returns the number of free bytes in the transmit buffer. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDCDCInit(). +//! +//! This function returns the maximum number of bytes that can be passed on a +//! call to USBDCDCPacketWrite and accepted for transmission. The value +//! returned will be the maximum USB packet size (64) if no transmission is +//! currently outstanding or 0 if a transmission is in progress. +//! +//! \return Returns the number of bytes available in the transmit buffer. +// +//***************************************************************************** +unsigned long +USBDCDCTxPacketAvailable(void *pvInstance) +{ + tCDCSerInstance *psInst; + + ASSERT(pvInstance); + + // + // Get our instance data pointer. + // + psInst = ((tUSBDCDCDevice *)pvInstance)->psPrivateCDCSerData; + + // + // Do we have a packet transmission currently ongoing? + // + if(psInst->eCDCTxState != CDC_STATE_IDLE) + { + // + // We are not ready to receive a new packet so return 0. + // + return(0); + } + else + { + // + // We can receive a packet so return the max packet size for the + // relevant endpoint. + // + return(DATA_IN_EP_MAX_SIZE); + } +} + +//***************************************************************************** +// +//! Determines whether a packet is available and, if so, the size of the +//! buffer required to read it. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDCDCInit(). +//! +//! This function may be used to determine if a received packet remains to be +//! read and allows the application to determine the buffer size needed to +//! read the data. +//! +//! \return Returns 0 if no received packet remains unprocessed or the +//! size of the packet if a packet is waiting to be read. +// +//***************************************************************************** +unsigned long +USBDCDCRxPacketAvailable(void *pvInstance) +{ + unsigned long ulEPStatus; + unsigned long ulSize; + tCDCSerInstance *psInst; + + ASSERT(pvInstance); + + // + // Get our instance data pointer + // + psInst = ((tUSBDCDCDevice *)pvInstance)->psPrivateCDCSerData; + + // + // If receive is currently blocked, return 0. + // + if(psInst->bRxBlocked || psInst->bControlBlocked) + { + return(0); + } + + // + // Does the relevant endpoint FIFO have a packet waiting for us? + // + ulEPStatus = MAP_USBEndpointStatus(psInst->ulUSBBase, + psInst->ucBulkOUTEndpoint); + + if(ulEPStatus & USB_DEV_RX_PKT_RDY) + { + // + // Yes - a packet is waiting. How big is it? + // + ulSize = MAP_USBEndpointDataAvail(psInst->ulUSBBase, + psInst->ucBulkOUTEndpoint); + + return(ulSize); + } + else + { + // + // There is no packet waiting to be received. + // + return(0); + } +} + +//***************************************************************************** +// +//! Informs the CDC module of changes in the serial control line states or +//! receive error conditions. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDCDCInit(). +//! \param usState indicates the states of the various control lines and +//! any receive errors detected. Bit definitions are as for the USB CDC +//! SerialState asynchronous notification and are defined in header file +//! usbcdc.h. +//! +//! The application should call this function whenever the state of any of +//! the incoming RS232 handshake signals changes or in response to a receive +//! error or break condition. The usState parameter is the ORed combination +//! of the following flags with each flag indicating the presence of that +//! condition. +//! +//! - USB_CDC_SERIAL_STATE_OVERRUN +//! - USB_CDC_SERIAL_STATE_PARITY +//! - USB_CDC_SERIAL_STATE_FRAMING +//! - USB_CDC_SERIAL_STATE_RING_SIGNAL +//! - USB_CDC_SERIAL_STATE_BREAK +//! - USB_CDC_SERIAL_STATE_TXCARRIER +//! - USB_CDC_SERIAL_STATE_RXCARRIER +//! +//! This function should be called only when the state of any flag changes. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDCSerialStateChange(void *pvInstance, unsigned short usState) +{ + tCDCSerInstance *psInst; + + ASSERT(pvInstance); + + // + // Get our instance data pointer + // + psInst = ((tUSBDCDCDevice *)pvInstance)->psPrivateCDCSerData; + + // + // Add the newly reported state bits to the current collection. We do this + // in case two state changes occur back-to-back before the first has been + // notified. There are two distinct types of signals that we report here + // and we deal with them differently: + // + // 1. Errors (overrun, parity, framing error) are ORed together so that + // any reported error is sent on the next notification. + // 2. Signal line states (RI, break, TX carrier, RX carrier) always + // report the last state notified to us. The implementation here will + // send an interrupt showing the last state but, if two state changes + // occur very quickly, the host may receive a notification containing + // the same state that was last reported (in other words, a short pulse + // will be lost). It would be possible to reduce the likelihood of + // this happening by building a queue of state changes and sending + // these in order but you are left with exactly the same problem if the + // queue fills up. For now, therefore, we run the risk of missing very + // short pulses on the "steady-state" signal lines. + // + psInst->usSerialState |= (usState & USB_CDC_SERIAL_ERRORS); + psInst->usSerialState &= ~USB_CDC_SERIAL_ERRORS; + psInst->usSerialState |= (usState & ~USB_CDC_SERIAL_ERRORS); + + // + // Set the flag indicating that a serial state change is to be sent. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, CDC_DO_SERIAL_STATE_CHANGE, + true); + + // + // Can we send the state change immediately? + // + if(psInst->eCDCInterruptState == CDC_STATE_IDLE) + { + // + // The interrupt channel is free so send the notification immediately. + // If we can't do this, the tick timer will catch this next time + // round. + // + psInst->eCDCInterruptState = CDC_STATE_WAIT_DATA; + SendSerialState(pvInstance); + } + + return; +} + +//***************************************************************************** +// +//! Reports the device power status (bus- or self-powered) to the USB library. +//! +//! \param pvInstance is the pointer to the CDC device instance structure. +//! \param ucPower indicates the current power status, either \b +//! USB_STATUS_SELF_PWR or \b USB_STATUS_BUS_PWR. +//! +//! Applications which support switching between bus- or self-powered +//! operation should call this function whenever the power source changes +//! to indicate the current power status to the USB library. This information +//! is required by the USB library to allow correct responses to be provided +//! when the host requests status from the device. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDCPowerStatusSet(void *pvInstance, unsigned char ucPower) +{ + ASSERT(pvInstance); + + // + // Pass the request through to the lower layer. + // + USBDCDPowerStatusSet(0, ucPower); +} + +//***************************************************************************** +// +//! Requests a remote wakeup to resume communication when in suspended state. +//! +//! \param pvInstance is the pointer to the CDC device instance structure. +//! +//! When the bus is suspended, an application which supports remote wakeup +//! (advertised to the host via the config descriptor) may call this function +//! to initiate remote wakeup signaling to the host. If the remote wakeup +//! feature has not been disabled by the host, this will cause the bus to +//! resume operation within 20mS. If the host has disabled remote wakeup, +//! \b false will be returned to indicate that the wakeup request was not +//! successful. +//! +//! \return Returns \b true if the remote wakeup is not disabled and the +//! signaling was started or \b false if remote wakeup is disabled or if +//! signaling is currently ongoing following a previous call to this function. +// +//***************************************************************************** +tBoolean +USBDCDCRemoteWakeupRequest(void *pvInstance) +{ + ASSERT(pvInstance); + + // + // Pass the request through to the lower layer. + // + return(USBDCDRemoteWakeupRequest(0)); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/device/usbdcdc.h b/usblib/device/usbdcdc.h new file mode 100644 index 0000000..f2f9b00 --- /dev/null +++ b/usblib/device/usbdcdc.h @@ -0,0 +1,353 @@ +//***************************************************************************** +// +// usbdcdc.h - USBLib support for generic CDC ACM (serial) device. +// +// Copyright (c) 2008-2012 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#ifndef __USBDCDC_H__ +#define __USBDCDC_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup cdc_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// PRIVATE +// +// The first few sections of this header are private defines that are used by +// the USB CDC Serial code and are here only to help with the application +// allocating the correct amount of memory for the CDC Serial device code. +// +//***************************************************************************** + +//***************************************************************************** +// +// PRIVATE +// +// This enumeration holds the various states that the device can be in during +// normal operation. +// +//***************************************************************************** +typedef enum +{ + // + // Unconfigured. + // + CDC_STATE_UNCONFIGURED, + + // + // No outstanding transaction remains to be completed. + // + CDC_STATE_IDLE, + + // + // Waiting on completion of a send or receive transaction. + // + CDC_STATE_WAIT_DATA, + + // + // Waiting for client to process data. + // + CDC_STATE_WAIT_CLIENT +} +tCDCState; + +//***************************************************************************** +// +// PRIVATE +// +// This structure defines the private instance data and state variables for the +// CDC Serial device. The memory for this structure is pointed to by the +// psPrivateCDCSerData field in the tUSBDCDCDevice structure passed on +// USBDCDCInit(). +// +//***************************************************************************** +typedef struct +{ + unsigned long ulUSBBase; + tDeviceInfo *psDevInfo; + tConfigDescriptor *psConfDescriptor; + volatile tCDCState eCDCRxState; + volatile tCDCState eCDCTxState; + volatile tCDCState eCDCRequestState; + volatile tCDCState eCDCInterruptState; + volatile unsigned char ucPendingRequest; + unsigned short usBreakDuration; + unsigned short usControlLineState; + unsigned short usSerialState; + volatile unsigned short usDeferredOpFlags; + unsigned short usLastTxSize; + tLineCoding sLineCoding; + volatile tBoolean bRxBlocked; + volatile tBoolean bControlBlocked; + volatile tBoolean bConnected; + unsigned char ucControlEndpoint; + unsigned char ucBulkINEndpoint; + unsigned char ucBulkOUTEndpoint; + unsigned char ucInterfaceControl; + unsigned char ucInterfaceData; +} +tCDCSerInstance; + + +#ifndef DEPRECATED +//***************************************************************************** +// +// The number of bytes of workspace required by the CDC device class driver. +// The client must provide a block of RAM of at least this size in the +// psPrivateCDCSerData field of the tUSBCDCDevice structure passed on +// USBDCDCInit(). +// +// This value is deprecated and should not be used, any new code should just +// pass in a tUSBCDCDevice structure in the psPrivateCDCSerData field. +// +//***************************************************************************** +#define USB_CDCSER_WORKSPACE_SIZE (sizeof(tCDCSerInstance)) +#endif + +//***************************************************************************** +// +// The following defines are used when working with composite devices. +// +//***************************************************************************** + +//***************************************************************************** +// +//! The size of the memory that should be allocated to create a configuration +//! descriptor for a single instance of the USB Serial CDC Device. +//! This does not include the configuration descriptor which is automatically +//! ignored by the composite device class. +// +// For reference this is sizeof(g_pIADSerDescriptor) + +// sizeof(g_pCDCSerCommInterface) + sizeof(g_pCDCSerDataInterface) +// +//***************************************************************************** +#define COMPOSITE_DCDC_SIZE (8 + 35 + 23) + +//***************************************************************************** +// +// CDC-specific events These events are provided to the application in the +// \e ulMsg parameter of the tUSBCallback function. +// +//***************************************************************************** + +// +//! The host requests that the device send a BREAK condition on its +//! serial communication channel. The BREAK should remain active until +//! a USBD_CDC_EVENT_CLEAR_BREAK event is received. +// +#define USBD_CDC_EVENT_SEND_BREAK (USBD_CDC_EVENT_BASE + 0) + +// +//! The host requests that the device stop sending a BREAK condition on its +//! serial communication channel. +// +#define USBD_CDC_EVENT_CLEAR_BREAK (USBD_CDC_EVENT_BASE + 1) + +// +//! The host requests that the device set the RS232 signaling lines to +//! a particular state. The ulMsgValue parameter contains the RTS and +//! DTR control line states as defined in table 51 of the USB CDC class +//! definition and is a combination of the following values: +//! +//! (RTS) USB_CDC_DEACTIVATE_CARRIER or USB_CDC_ACTIVATE_CARRIER +//! (DTR) USB_CDC_DTE_NOT_PRESENT or USB_CDC_DTE_PRESENT +// +#define USBD_CDC_EVENT_SET_CONTROL_LINE_STATE (USBD_CDC_EVENT_BASE + 2) + +// +//! The host requests that the device set the RS232 communication +//! parameters. The pvMsgData parameter points to a tLineCoding structure +//! defining the required number of bits per character, parity mode, +//! number of stop bits and the baud rate. +// +#define USBD_CDC_EVENT_SET_LINE_CODING (USBD_CDC_EVENT_BASE + 3) + +// +//! The host is querying the current RS232 communication parameters. The +//! pvMsgData parameter points to a tLineCoding structure that the +//! application must fill with the current settings prior to returning +//! from the callback. +// +#define USBD_CDC_EVENT_GET_LINE_CODING (USBD_CDC_EVENT_BASE + 4) + +//***************************************************************************** +// +//! The structure used by the application to define operating parameters for +//! the CDC device. +// +//***************************************************************************** +typedef struct +{ + // + //! The vendor ID that this device is to present in the device descriptor. + // + unsigned short usVID; + + // + //! The product ID that this device is to present in the device descriptor. + // + unsigned short usPID; + + // + //! The maximum power consumption of the device, expressed in milliamps. + // + unsigned short usMaxPowermA; + + // + //! Indicates whether the device is self- or bus-powered and whether or not + //! it supports remote wakeup. Valid values are USB_CONF_ATTR_SELF_PWR or + //! USB_CONF_ATTR_BUS_PWR, optionally ORed with USB_CONF_ATTR_RWAKE. + // + unsigned char ucPwrAttributes; + + // + //! A pointer to the callback function which will be called to notify + //! the application of all asynchronous control events related to the + //! operation of the device. + // + tUSBCallback pfnControlCallback; + + // + //! A client-supplied pointer which will be sent as the first + //! parameter in all calls made to the control channel callback, + //! pfnControlCallback. + // + void *pvControlCBData; + + // + //! A pointer to the callback function which will be called to notify + //! the application of events related to the device's data receive channel. + // + tUSBCallback pfnRxCallback; + + // + //! A client-supplied pointer which will be sent as the first + //! parameter in all calls made to the receive channel callback, + //! pfnRxCallback. + // + void *pvRxCBData; + + // + //! A pointer to the callback function which will be called to notify + //! the application of events related to the device's data transmit + //! channel. + // + tUSBCallback pfnTxCallback; + + // + //! A client-supplied pointer which will be sent as the first + //! parameter in all calls made to the transmit channel callback, + //! pfnTxCallback. + // + void *pvTxCBData; + + // + //! A pointer to the string descriptor array for this device. This array + //! must contain the following string descriptor pointers in this order. + //! Language descriptor, Manufacturer name string (language 1), Product + //! name string (language 1), Serial number string (language 1), + //! Control interface description string (language 1), Configuration + //! description string (language 1). + //! + //! If supporting more than 1 language, the strings for indices 1 through 5 + //! must be repeated for each of the other languages defined in the + //! language descriptor. + // + const unsigned char * const *ppStringDescriptors; + + // + //! The number of descriptors provided in the ppStringDescriptors + //! array. This must be 1 + (5 * number of supported languages). + // + unsigned long ulNumStringDescriptors; + + // + //! A pointer to the private instance data for this device. This memory + //! must remain accessible for as long as the CDC device is in use and must + //! not be modified by any code outside the CDC class driver. + // + tCDCSerInstance *psPrivateCDCSerData; +} +tUSBDCDCDevice; + +extern tDeviceInfo g_sCDCSerDeviceInfo; + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void * USBDCDCCompositeInit(unsigned long ulIndex, + const tUSBDCDCDevice *psCDCDevice); +extern void *USBDCDCInit(unsigned long ulIndex, + const tUSBDCDCDevice *psCDCDevice); +extern void USBDCDCTerm(void *pvInstance); +extern void *USBDCDCSetControlCBData(void *pvInstance, void *pvCBData); +extern void *USBDCDCSetRxCBData(void *pvInstance, void *pvCBData); +extern void *USBDCDCSetTxCBData(void *pvInstance, void *pvCBData); +extern unsigned long USBDCDCPacketWrite(void *pvInstance, + unsigned char *pcData, + unsigned long ulLength, + tBoolean bLast); +extern unsigned long USBDCDCPacketRead(void *pvInstance, + unsigned char *pcData, + unsigned long ulLength, + tBoolean bLast); +extern unsigned long USBDCDCTxPacketAvailable(void *pvInstance); +extern unsigned long USBDCDCRxPacketAvailable(void *pvInstance); +extern void USBDCDCSerialStateChange(void *pvInstance, + unsigned short usState); +extern void USBDCDCPowerStatusSet(void *pvInstance, unsigned char ucPower); +extern tBoolean USBDCDCRemoteWakeupRequest(void *pvInstance); + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBDCDC_H__ diff --git a/usblib/device/usbdcdesc.c b/usblib/device/usbdcdesc.c new file mode 100644 index 0000000..2bb1731 --- /dev/null +++ b/usblib/device/usbdcdesc.c @@ -0,0 +1,649 @@ +//***************************************************************************** +// +// usbcdesc.c - Config descriptor parsing functions. +// +// Copyright (c) 2007-2012 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/device/usbdevice.h" + +//***************************************************************************** +// +// The functions in this file mirror the descriptor parsing APIs available +// in usblib.h but parse config descriptors defined in terms of a list of +// sections rather than as a single block of descriptor data. +// +//***************************************************************************** + +//***************************************************************************** +// +//! \addtogroup device_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! \internal +//! +//! Walk to the next descriptor after the supplied one within a section-based +//! config descriptor. +//! +//! \param psConfig points to the header structure for the configuration +//! descriptor which contains \e psDesc. +//! \param pulSec points to a variable containing the section within \e +//! psConfig which contains \e psDesc. +//! \param psDesc points to the descriptor that we want to step past. +//! +//! This function walks forward one descriptor within a config descriptor. The +//! value returned is a pointer to the header of the next descriptor after the +//! descriptor supplied in \e psDesc. If the next descriptor is in the next +//! section, \e *pulSec will be incremented accordlingly. +//! +//! \return Returns a pointer to the next descriptor in the config descrptor. +// +//***************************************************************************** +static tDescriptorHeader * +NextConfigDescGet(const tConfigHeader *psConfig, unsigned long *pulSec, + tDescriptorHeader *psDesc) +{ + // + // Determine where the next descriptor after the supplied one should be + // assuming it is within the current section. + // + psDesc = NEXT_USB_DESCRIPTOR(psDesc); + + // + // Did we run off the end of the section? + // + if((unsigned char *)psDesc >= (psConfig->psSections[*pulSec]->pucData + + psConfig->psSections[*pulSec]->usSize)) + { + // + // Yes - move to the next section. + // + (*pulSec)++; + + // + // Are we still within the config descriptor? + // + if(*pulSec < psConfig->ucNumSections) + { + // + // Yes - the new descriptor is at the start of the new section. + // + psDesc = + (tDescriptorHeader *)psConfig->psSections[*pulSec]->pucData; + } + else + { + // + // No - we ran off the end of the descriptor so return NULL. + // + psDesc = (tDescriptorHeader *)0; + } + } + + // + // Return the new descriptor pointer. + // + return(psDesc); +} + +//***************************************************************************** +// +//! \internal +//! +//! Returns a pointer to the n-th interface descriptor in a config descriptor +//! with the supplied interface number. +//! +//! \param psConfig points to the header structure for the config descriptor +//! that is to be searched. +//! \param ucInterfaceNumber is the interface number of the descriptor that is +//! being queried. +//! \param ulIndex is the zero based index of the descriptor to return. +//! \param pulSection points to storage which will be written with the index +//! of the section containing the returned descriptor. +//! +//! This function returns a pointer to the n-th interface descriptor in the +//! supplied configuration which has the requested interface number. It may be +//! used by a client to retrieve the descriptors for each alternate setting +//! of a given interface within the configuration passed. +//! +//! \return Returns a pointer to the n-th interface descriptor with interface +//! number as specified or NULL of this descriptor does not exist. +// +//***************************************************************************** +static tInterfaceDescriptor * +ConfigAlternateInterfaceGet(const tConfigHeader *psConfig, + unsigned char ucInterfaceNumber, + unsigned long ulIndex, + unsigned long *pulSection) +{ + tDescriptorHeader *psDescCheck; + unsigned long ulCount; + unsigned long ulSec; + + // + // Set up for our descriptor counting loop. + // + psDescCheck = (tDescriptorHeader *)psConfig->psSections[0]->pucData; + ulCount = 0; + ulSec = 0; + + // + // Keep looking through the supplied data until we reach the end. + // + while(psDescCheck) + { + // + // Does this descriptor match the type passed (if a specific type + // has been specified)? + // + if((psDescCheck->bDescriptorType == USB_DTYPE_INTERFACE) && + (((tInterfaceDescriptor *)psDescCheck)->bInterfaceNumber == + ucInterfaceNumber)) + { + // + // This is an interface descriptor for interface ucInterfaceNumber. + // Determine if this is the n-th one we have found and, if so, + // return its pointer. + // + if(ulCount == ulIndex) + { + // + // Found it - return the pointer and section number. + // + *pulSection = ulSec; + return((tInterfaceDescriptor *)psDescCheck); + } + + // + // Increment our count of matching descriptors found and go back + // to look for another since we have not yet reached the n-th + // match. + // + ulCount++; + } + + // + // Move on to the next descriptor. + // + psDescCheck = NextConfigDescGet(psConfig, &ulSec, psDescCheck); + } + + // + // If we drop out the end of the loop, we did not find the requested + // descriptor so return NULL. + // + return((tInterfaceDescriptor *)0); +} + +//***************************************************************************** +// +//! \internal +//! +//! Determines the total length of a config descriptor defined in terms of a +//! collection of concatenated sections. +//! +//! \param psConfig points to the header structure for the configuration +//! descriptor whose size is to be determined. +//! +//! \return Returns the number of bytes in the config descriptor will result +//! from concatenating the required sections. +// +//***************************************************************************** +unsigned long +USBDCDConfigDescGetSize(const tConfigHeader *psConfig) +{ + unsigned long ulLoop; + unsigned long ulLen; + + ulLen = 0; + + // + // Determine the size of the whole descriptor by adding the sizes of + // each section which will be concatenated to produce it. + // + for(ulLoop = 0; ulLoop < psConfig->ucNumSections; ulLoop++) + { + ulLen += psConfig->psSections[ulLoop]->usSize; + } + + return(ulLen); +} + +//***************************************************************************** +// +//! \internal +//! +//! Determines the number of individual descriptors of a particular type within +//! a supplied configuration descriptor. +//! +//! \param psConfig points to the header structure for the configuration +//! descriptor that is to be searched. +//! \param ulType identifies the type of descriptor that is to be counted. If +//! the value is \b USB_DESC_ANY, the function returns the total number of +//! descriptors regardless of type. +//! +//! This function can be used to count the number of descriptors of a +//! particular type within a configuration descriptor. The caller can provide +//! a specific type value which the function matches against the second byte +//! of each descriptor or, alternatively, can specify \b USB_DESC_ANY to have +//! the function count all descriptors regardless of their type. +//! +//! The search performed by this function traverses through the list of +//! sections comprising the configuration descriptor. Note that the similar +//! top-level function, USBDescGetNum(), searches through a single, contiguous +//! block of data to perform the same enumeration. +//! +//! \return Returns the number of descriptors found in the supplied block of +//! data. +// +//***************************************************************************** +unsigned long +USBDCDConfigDescGetNum(const tConfigHeader *psConfig, unsigned long ulType) +{ + unsigned long ulSection; + unsigned long ulNumDescs; + + // + // Initialize our counts. + // + ulNumDescs = 0; + + // + // Determine the number of descriptors of the given type in each of the + // sections comprising the config descriptor. Note that this assumes each + // section contains only whole descriptors! + // + for(ulSection = 0; ulSection < (unsigned long)psConfig->ucNumSections; + ulSection++) + { + ulNumDescs += USBDescGetNum( + (tDescriptorHeader *)psConfig->psSections[ulSection]->pucData, + psConfig->psSections[ulSection]->usSize, + ulType); + } + + return(ulNumDescs); +} + +//***************************************************************************** +// +//! \internal +//! +//! Finds the n-th descriptor of a particular type within the supplied +//! configuration descriptor. +//! +//! \param psConfig points to the header structure for the configuration +//! descriptor that is to be searched. +//! \param ulType identifies the type of descriptor that is to be found. If +//! the value is \b USB_DESC_ANY, the function returns a pointer to the n-th +//! descriptor regardless of type. +//! \param ulIndex is the zero based index of the descriptor whose pointer is +//! to be returned. For example, passing value 1 in \e ulIndex returns the +//! second matching descriptor. +//! \param pulSection points to storage which will receive the section index +//! containing the requested descriptor. +//! +//! Return a pointer to the n-th descriptor of a particular type found in the +//! configuration descriptor passed. +//! +//! The search performed by this function traverses through the list of +//! sections comprising the configuration descriptor. Note that the similar +//! top-level function, USBDescGet(), searches through a single, contiguous +//! block of data to perform the same enumeration. +//! +//! \return Returns a pointer to the header of the required descriptor if +//! found or NULL otherwise. +// +//***************************************************************************** +tDescriptorHeader * +USBDCDConfigDescGet(const tConfigHeader *psConfig, unsigned long ulType, + unsigned long ulIndex, unsigned long *pulSection) +{ + unsigned long ulSection; + unsigned long ulTotalDescs; + unsigned long ulNumDescs; + + // + // Initialize our counts. + // + ulTotalDescs = 0; + + // + // Determine the number of descriptors of the given type in each of the + // sections comprising the config descriptor. This allows us to determine + // which section contains the descriptor we are being asked for. + // + for(ulSection = 0; ulSection < (unsigned long)psConfig->ucNumSections; + ulSection++) + { + // + // How many descriptors of the requested type exist in this section? + // + ulNumDescs = USBDescGetNum( + (tDescriptorHeader *)psConfig->psSections[ulSection]->pucData, + psConfig->psSections[ulSection]->usSize, + ulType); + + // + // Does this section contain the descriptor whose index we are looking + // for? + // + if((ulTotalDescs + ulNumDescs) > ulIndex) + { + // + // We know the requested descriptor exists in the current + // block so write the section number to the caller's storage. + // + *pulSection = ulSection; + + // + // Now find the actual descriptor requested and return its pointer. + // + return(USBDescGet( + (tDescriptorHeader *)psConfig->psSections[ulSection]->pucData, + psConfig->psSections[ulSection]->usSize, + ulType, + ulIndex - ulTotalDescs)); + } + + // + // We have not found the required descriptor yet. Update our running + // count of the number of type matches found so far then move on to + // the next section. + // + ulTotalDescs += ulNumDescs; + } + + // + // If we drop out of the loop, we can't find the requested descriptor + // so return NULL. + // + return((tDescriptorHeader *)0); +} + +//***************************************************************************** +// +//! \internal +//! +//! Determines the number of different alternate configurations for a given +//! interface within a config descriptor. +//! +//! \param psConfig points to the header structure for the configuration +//! descriptor that is to be searched. +//! \param ucInterfaceNumber is the interface number for which the number of +//! alternate configurations is to be counted. +//! +//! This function can be used to count the number of alternate settings for a +//! specific interface within a configuration. +//! +//! The search performed by this function traverses through the list of +//! sections comprising the configuration descriptor. Note that the similar +//! top-level function, USBDescGetNumAlternateInterfaces(), searches through +//! a single, contiguous block of data to perform the same enumeration. +//! +//! \return Returns the number of alternate versions of the specified interface +//! or 0 if the interface number supplied cannot be found in the config +//! descriptor. +// +//***************************************************************************** +unsigned long +USBDCDConfigGetNumAlternateInterfaces(const tConfigHeader *psConfig, + unsigned char ucInterfaceNumber) +{ + tDescriptorHeader *psDescCheck; + unsigned long ulCount; + unsigned long ulSec; + + // + // Set up for our descriptor counting loop. + // + psDescCheck = (tDescriptorHeader *)psConfig->psSections[0]->pucData; + ulSec = 0; + ulCount = 0; + + // + // Keep looking through the supplied data until we reach the end. + // + while(psDescCheck) + { + // + // Is this an interface descriptor with the required interface number? + // + if((psDescCheck->bDescriptorType == USB_DTYPE_INTERFACE) && + (((tInterfaceDescriptor *)psDescCheck)->bInterfaceNumber == + ucInterfaceNumber)) + { + // + // Yes - increment our count. + // + ulCount++; + } + + // + // Move on to the next descriptor. + // + psDescCheck = NextConfigDescGet(psConfig, &ulSec, psDescCheck); + } + + // + // Return the descriptor count to the caller. + // + return(ulCount); +} + +//***************************************************************************** +// +//! \internal +//! +//! Returns a pointer to the n-th interface descriptor in a configuration +//! descriptor that applies to the supplied alternate setting number. +//! +//! \param psConfig points to the header structure for the configuration +//! descriptor that is to be searched. +//! \param ulIndex is the zero based index of the interface that is to be +//! found. If \e ulAlt is set to a value other than \b USB_DESC_ANY, this will +//! be equivalent to the interface number being searched for. +//! \param ulAlt is the alternate setting number which is to be +//! searched for. If this value is \b USB_DESC_ANY, the alternate setting +//! is ignored and all interface descriptors are considered in the search. +//! \param pulSection points to storage which will receive the index of the +//! config descriptor section which contains the requested interface +//! descriptor. +//! +//! Return a pointer to the n-th interface descriptor found in the supplied +//! configuration descriptor. If \e ulAlt is not \b USB_DESC_ANY, only +//! interface descriptors which are part of the supplied alternate setting are +//! considered in the search otherwise all interface descriptors are +//! considered. +//! +//! Note that, although alternate settings can be applied on an interface-by- +//! interface basis, the number of interfaces offered is fixed for a given +//! config descriptor. Hence, this function will correctly find the unique +//! interface descriptor for that interface's alternate setting number \e +//! ulAlt if \e ulIndex is set to the required interface number and \e ulAlt +//! is set to a valid alternate setting number for that interface. +//! +//! The search performed by this function traverses through the list of +//! sections comprising the configuration descriptor. Note that the similar +//! top-level function, USBDescGetInterface(), searches through a single, +//! contiguous block of data to perform the same enumeration. +//! +//! \return Returns a pointer to the required interface descriptor if +//! found or NULL otherwise. +// +//***************************************************************************** +tInterfaceDescriptor * +USBDCDConfigGetInterface(const tConfigHeader *psConfig, unsigned long ulIndex, + unsigned long ulAlt, unsigned long *pulSection) +{ + // + // If we are being told to ignore the alternate configuration, this boils + // down to a very simple query. + // + if(ulAlt == USB_DESC_ANY) + { + // + // Return the ulIndex-th interface descriptor we find in the + // configuration descriptor. + // + return((tInterfaceDescriptor *)USBDCDConfigDescGet(psConfig, + USB_DTYPE_INTERFACE, + ulIndex, + pulSection)); + } + else + { + // + // In this case, a specific alternate setting number is required. + // Given that interface numbers are zero based indices, we can + // pass the supplied ulIndex parameter directly as the interface + // number to USBDescGetAlternateInterface to retrieve the requested + // interface descriptor pointer. + // + return(ConfigAlternateInterfaceGet(psConfig, ulIndex, ulAlt, + pulSection)); + } +} + +//***************************************************************************** +// +//! \internal +//! +//! Return a pointer to the n-th endpoint descriptor in a particular interface +//! within a configuration descriptor. +//! +//! \param psConfig points to the header structure for the configuration +//! descriptor that is to be searched. +//! \param ulInterfaceNumber is the interface number whose endpoint is to be +//! found. +//! \param ulAltCfg is the alternate setting number which is to be searched +//! for. This must be a valid alternate setting number for the requested +//! interface. +//! \param ulIndex is the zero based index of the endpoint that is to be +//! found within the appropriate alternate setting for the interface. +//! +//! Return a pointer to the n-th endpoint descriptor found in the supplied +//! interface descriptor. If the \e ulIndex parameter is invalid (greater +//! than or equal to the bNumEndpoints field of the interface descriptor) or +//! the endpoint descriptor cannot be found, the function will return NULL. +//! +//! The search performed by this function traverses through the list of +//! sections comprising the configuration descriptor. Note that the similar +//! top-level function, USBDescGetInterfaceEndpoint(), searches through a +//! single, contiguous block of data to perform the same enumeration. +//! +//! \return Returns a pointer to the requested endpoint descriptor if +//! found or NULL otherwise. +// +//***************************************************************************** +tEndpointDescriptor * +USBDCDConfigGetInterfaceEndpoint(const tConfigHeader *psConfig, + unsigned long ulInterfaceNumber, + unsigned long ulAltCfg, unsigned long ulIndex) +{ + tInterfaceDescriptor *psInterface; + tDescriptorHeader *psEndpoint; + unsigned long ulSection; + unsigned long ulCount; + + // + // Find the requested interface descriptor. + // + psInterface = USBDCDConfigGetInterface(psConfig, ulInterfaceNumber, + ulAltCfg, &ulSection); + + // + // Did we find the requested interface? + // + if(psInterface) + { + // + // Is the index passed valid? + // + if(ulIndex >= psInterface->bNumEndpoints) + { + // + // It's out of bounds so return a NULL. + // + return((tEndpointDescriptor *)0); + } + else + { + // + // Endpoint index is valid so find the descriptor. We start from + // the interface descriptor and look for following endpoint + // descriptors. + // + ulCount = 0; + psEndpoint = (tDescriptorHeader *)psInterface; + + while(psEndpoint) + { + if(psEndpoint->bDescriptorType == USB_DTYPE_ENDPOINT) + { + // + // We found an endpoint descriptor. Have we reached the + // one we want? + // + if(ulCount == ulIndex) + { + // + // Yes - return the descriptor pointer to the caller. + // + return((tEndpointDescriptor *)psEndpoint); + } + + // + // Move on to look for the next endpoint. + // + ulCount++; + } + + // + // Move to the next descriptor. + // + psEndpoint = NextConfigDescGet(psConfig, &ulSection, + psEndpoint); + } + } + } + + // + // We couldn't find the requested interface or we got to the end of the + // descriptor without finding the requested endpoint. + // + return((tEndpointDescriptor *)0); + +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/device/usbdcomp.c b/usblib/device/usbdcomp.c new file mode 100644 index 0000000..feb22bf --- /dev/null +++ b/usblib/device/usbdcomp.c @@ -0,0 +1,1543 @@ +//**************************************************************************** +// +// usbdcomp.c - USB composite device class driver. +// +// Copyright (c) 2010-2012 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 9453 of the Stellaris USB Library. +// +//**************************************************************************** + +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/sysctl.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usb-ids.h" +#include "usblib/usbcdc.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbdcdc.h" +#include "usblib/device/usbdcomp.h" + +//**************************************************************************** +// +//! \addtogroup composite_device_class_api +//! @{ +// +//**************************************************************************** + +//**************************************************************************** +// +// Device Descriptor. This is stored in RAM to allow several fields to be +// changed at runtime based on the client's requirements. +// +//**************************************************************************** +static unsigned char g_pCompDeviceDescriptor[] = +{ + 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) + USB_CLASS_MISC, // USB Device Class (spec 5.1.1) + USB_MISC_SUBCLASS_COMMON, // USB Device Sub-class (spec 5.1.1) + USB_MISC_PROTOCOL_IAD, // USB Device protocol (spec 5.1.1) + 64, // Maximum packet size for default pipe. + USBShort(0), // Vendor ID (filled in during USBDCompositeInit). + USBShort(0), // Product ID (filled in during USBDCompositeInit). + USBShort(0x100), // Device Version BCD. + 1, // Manufacturer string identifier. + 2, // Product string identifier. + 3, // Product serial number. + 1 // Number of configurations. +}; + +//**************************************************************************** +// +// Composite class 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 const unsigned char g_pCompConfigDescriptor[] = +{ + // + // Configuration descriptor header. + // + 9, // Size of the configuration descriptor. + USB_DTYPE_CONFIGURATION, // Type of this descriptor. + USBShort(0), // The total size of this full structure. + 0, // The number of interfaces in this + // configuration, this will be filled by + // the class as it discovers all classes + // supported. + 1, // The unique value for this configuration. + 0, // The string identifier that describes this + // configuration. + USB_CONF_ATTR_BUS_PWR, // . + 250, // The maximum power in 2mA increments. +}; + +//**************************************************************************** +// +// Byte offsets used to access various fields in our index/interface/endpoint +// lookup table (tUSBDCompositeDevice.pulDeviceWorkspace). This workspace +// contains one 4 byte entry per device. The LSB is the device index, next byte +// is the number of the first interface not within this device, next byte is the +// number of the first IN endpoint not within this device and the final byte is +// the number of the first OUT endpoint not within this device. Using this +// simple table we can reasonably quickly cross-reference index with interface +// and endpoint numbers. +// +//**************************************************************************** +#define LOOKUP_INDEX_BYTE 0 +#define LOOKUP_INTERFACE_BYTE 1 +#define LOOKUP_IN_END_BYTE 2 +#define LOOKUP_OUT_END_BYTE 3 + +//**************************************************************************** +// +// A marker used to indicate an invalid index into the device table. +// +//**************************************************************************** +#define INVALID_DEVICE_INDEX 0xFFFFFFFF + +//***************************************************************************** +// +// Macros to convert between USB controller base address and an index. These +// are currently trivial but are included to allow for the possibility of +// supporting more than one controller in the future. +// +//***************************************************************************** +#define USB_BASE_TO_INDEX(BaseAddr) (0) +#define USB_INDEX_TO_BASE(Index) (USB0_BASE) + +//**************************************************************************** +// +// Various internal handlers needed by this class. +// +//**************************************************************************** +static void HandleDisconnect(void *pvInstance); +static void InterfaceChange(void *pvInstance, unsigned char ucInterfaceNum, + unsigned char ucAlternateSetting); +static void ConfigChangeHandler(void *pvInstance, unsigned long ulValue); +static void DataSent(void *pvInstance, unsigned long ulInfo); +static void DataReceived(void *pvInstance, unsigned long ulInfo); +static void HandleEndpoints(void *pvInstance, unsigned long ulStatus); +static void HandleRequests(void *pvInstance, tUSBRequest *pUSBRequest); +static void SuspendHandler(void *pvInstance); +static void ResumeHandler(void *pvInstance); +static void ResetHandler(void *pvInstance); +static void GetDescriptor(void *pvInstance, tUSBRequest *pUSBRequest); + +//**************************************************************************** +// +// Configuration Descriptor. +// +//**************************************************************************** +tConfigHeader *g_pCompConfigDescriptors[1]; + +//**************************************************************************** +// +// The FIFO configuration for USB mass storage class device. +// +//**************************************************************************** +tFIFOConfig g_sUSBCompositeFIFOConfig = +{ + // + // IN endpoints. + // + { + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN } + }, + // + // OUT endpoints. + // + { + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT } + }, +}; + +//**************************************************************************** +// +// The device information structure for the USB Composite device. +// +//**************************************************************************** +tDeviceInfo g_sCompositeDeviceInfo = +{ + // + // Device event handler callbacks. + // + { + // + // GetDescriptor + // + GetDescriptor, + + // + // RequestHandler + // + HandleRequests, + + // + // InterfaceChange + // + InterfaceChange, + + // + // ConfigChange + // + ConfigChangeHandler, + + // + // DataReceived + // + DataReceived, + + // + // DataSentCallback + // + DataSent, + + // + // ResetHandler + // + ResetHandler, + + // + // SuspendHandler + // + SuspendHandler, + + // + // ResumeHandler + // + ResumeHandler, + + // + // DisconnectHandler + // + HandleDisconnect, + + // + // EndpointHandler + // + HandleEndpoints + }, + g_pCompDeviceDescriptor, + (const tConfigHeader **)g_pCompConfigDescriptors, + 0, + 0, + &g_sUSBCompositeFIFOConfig +}; + +//**************************************************************************** +// +// Use the lookup table from the field pulDeviceWorkspace in the +// tUSBDCompositeDevice structure to determine which device to call given a +// particular composite device interface number. +// +// The returned value is the index into psDevice->tCompositeEntry indicating +// the device which contains this interface or INVALID_DEVICE_INDEX if no +// device contains the passed interface number. +// +//**************************************************************************** +static unsigned long +InterfaceToIndex(tUSBDCompositeDevice *psDevice, unsigned long ulInterface) +{ + unsigned long ulLoop; + unsigned char *pucLookupEntry; + + // + // Get a pointer to the lookup table. + // + pucLookupEntry = (unsigned char *)psDevice->pulDeviceWorkspace; + + // + // Check each lookup entry in turn. + // + for(ulLoop = 0; ulLoop < psDevice->ulNumDevices; ulLoop++) + { + // + // If the desired interface number is lower than the value in the + // current lookup table entry, we have found the desired device so + // return its index. + // + if(ulInterface < (unsigned long)pucLookupEntry[LOOKUP_INTERFACE_BYTE]) + { + return(ulLoop); + } + + // + // Move to the next lookup table entry. + // + pucLookupEntry += sizeof(unsigned long); + } + + // + // If we get here, an invalid interface number was passed so return a + // marker to indicate this. + // + return(INVALID_DEVICE_INDEX); +} + +//**************************************************************************** +// +// Use the lookup table from the field pulDeviceWorkspace in the +// tUSBDCompositeDevice structure to determine which device to call given a +// particular composite device endpoint number. +// +// The returned value is the index into psDevice->tCompositeEntry indicating +// the device which contains this endpoint or INVALID_DEVICE_INDEX if no +// device contains the passed endpoint number. +// +//**************************************************************************** +static unsigned long +EndpointToIndex(tUSBDCompositeDevice *psDevice, unsigned long ulEndpoint, + tBoolean bInEndpoint) +{ + unsigned long ulLoop, ulEndpointByte; + unsigned char *pucLookupEntry; + + // + // Get a pointer to the lookup table. + // + pucLookupEntry = (unsigned char *)psDevice->pulDeviceWorkspace; + + // + // Are we considering an IN or OUT endpoint? + // + ulEndpointByte = bInEndpoint ? LOOKUP_IN_END_BYTE : LOOKUP_OUT_END_BYTE; + + // + // Check each lookup entry in turn. + // + for(ulLoop = 0; ulLoop < psDevice->ulNumDevices; ulLoop++) + { + // + // If the desired endpoint number is lower than the value in the + // current lookup table entry, we have found the desired device so + // return its index. + // + if(ulEndpoint < (unsigned long)pucLookupEntry[ulEndpointByte]) + { + return(ulLoop); + } + + // + // Move to the next lookup table entry. + // + pucLookupEntry += sizeof(unsigned long); + } + + // + // If we get here, an invalid endpoint number was passed so return a + // marker to indicate this. + // + return(INVALID_DEVICE_INDEX); +} + + +//**************************************************************************** +// +// This function will check if any device classes need a get descriptor +// handler called. +// +//**************************************************************************** +static void +GetDescriptor(void *pvInstance, tUSBRequest *pUSBRequest) +{ + unsigned long ulIdx; + const tDeviceInfo *pDeviceInfo; + tUSBDCompositeDevice *psDevice; + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + // + // Determine which device this request is intended for. We have to be + // careful here to send this to the callback for the correct device + // depending upon whether it is a request sent to the device, the interface + // or the endpoint. + // + switch(pUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M) + { + case USB_RTYPE_INTERFACE: + { + ulIdx = InterfaceToIndex(psDevice, (pUSBRequest->wIndex & 0xFF)); + break; + } + + case USB_RTYPE_ENDPOINT: + { + ulIdx = EndpointToIndex(psDevice, (pUSBRequest->wIndex & 0x0F), + (pUSBRequest->wIndex & 0x80) ? true : false); + break; + } + + // + // Requests sent to the device or any other recipient can't be + // handled here since we have no way of telling where they are + // supposed to be handled. As a result, we just stall them. + // + // If your composite device has some device-specific descriptors, + // you should add code here to handle them. + // + case USB_RTYPE_DEVICE: + case USB_RTYPE_OTHER: + default: + { + ulIdx = INVALID_DEVICE_INDEX; + break; + } + } + + // + // Did we find a device class to pass the request to? + // + if(ulIdx != INVALID_DEVICE_INDEX) + { + // + // Get a pointer to the individual device instance. + // + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + + // + // Does this device have a GetDescriptor callback? + // + if(pDeviceInfo->sCallbacks.pfnGetDescriptor) + { + // + // Remember this device index so that we can correctly route any + // data notification callbacks to it. + // + psDevice->psPrivateData->ulEP0Owner = ulIdx; + + // + // Call the device to retrieve the descriptor. + // + pDeviceInfo->sCallbacks.pfnGetDescriptor( + psDevice->psDevices[ulIdx].pvInstance, pUSBRequest); + } + else + { + // + // Oops - we can't satisfy the request so stall EP0 to indicate + // an error. + // + USBDCDStallEP0( + USB_BASE_TO_INDEX(psDevice->psPrivateData->ulUSBBase)); + } + } + else + { + // + // We are unable to satisfy the descriptor request so stall EP0 to + // indicate an error. + // + USBDCDStallEP0(USB_BASE_TO_INDEX(psDevice->psPrivateData->ulUSBBase)); + } +} + +//**************************************************************************** +// +// This function will check if any device classes need an suspend handler +// called. +// +//**************************************************************************** +static void +SuspendHandler(void *pvInstance) +{ + unsigned long ulIdx; + tUSBDCompositeDevice *psDevice; + const tDeviceInfo *pDeviceInfo; + void *pvDeviceInst; + + ASSERT(pvInstance != 0); + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + // + // Inform the application that the device has resumed. + // + if(psDevice->pfnCallback) + { + psDevice->pfnCallback(pvInstance, USB_EVENT_SUSPEND, 0, 0); + } + + for(ulIdx = 0; ulIdx < psDevice->ulNumDevices; ulIdx++) + { + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + pvDeviceInst = psDevice->psDevices[ulIdx].pvInstance; + + if(pDeviceInfo->sCallbacks.pfnSuspendHandler) + { + pDeviceInfo->sCallbacks.pfnSuspendHandler(pvDeviceInst); + } + } +} + +//**************************************************************************** +// +// This function will check if any device classes need an resume handler +// called. +// +//**************************************************************************** +static void +ResumeHandler(void *pvInstance) +{ + unsigned long ulIdx; + tUSBDCompositeDevice *psDevice; + const tDeviceInfo *pDeviceInfo; + void *pvDeviceInst; + + ASSERT(pvInstance != 0); + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + // + // Inform the application that the device has resumed. + // + if(psDevice->pfnCallback) + { + psDevice->pfnCallback(pvInstance, USB_EVENT_RESUME, 0, 0); + } + + for(ulIdx = 0; ulIdx < psDevice->ulNumDevices; ulIdx++) + { + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + pvDeviceInst = psDevice->psDevices[ulIdx].pvInstance; + + if(pDeviceInfo->sCallbacks.pfnResumeHandler) + { + pDeviceInfo->sCallbacks.pfnResumeHandler(pvDeviceInst); + } + } +} + +//**************************************************************************** +// +// This function will check if any device classes need an reset handler +// called. +// +//**************************************************************************** +static void +ResetHandler(void *pvInstance) +{ + unsigned long ulIdx; + tUSBDCompositeDevice *psDevice; + const tDeviceInfo *pDeviceInfo; + void *pvDeviceInst; + + ASSERT(pvInstance != 0); + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + // + // Inform the application that the device has been connected. + // + if(psDevice->pfnCallback) + { + psDevice->pfnCallback(pvInstance, USB_EVENT_CONNECTED, 0, 0); + } + + for(ulIdx = 0; ulIdx < psDevice->ulNumDevices; ulIdx++) + { + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + pvDeviceInst = psDevice->psDevices[ulIdx].pvInstance; + + if(pDeviceInfo->sCallbacks.pfnResetHandler) + { + pDeviceInfo->sCallbacks.pfnResetHandler(pvDeviceInst); + } + } +} + +//**************************************************************************** +// +// This function is called to handle data being set to the host so that the +// application callback can be called when the data has been transferred. +// +//**************************************************************************** +static void +DataSent(void *pvInstance, unsigned long ulInfo) +{ + unsigned long ulIdx; + const tDeviceInfo *pDeviceInfo; + tUSBDCompositeDevice *psDevice; + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + // + // Pass this notification on to the device which last handled a + // transaction on endpoint 0 (assuming we know who that was). + // + ulIdx = psDevice->psPrivateData->ulEP0Owner; + + if(ulIdx != INVALID_DEVICE_INDEX) + { + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + + if(pDeviceInfo->sCallbacks.pfnDataSent) + { + pDeviceInfo->sCallbacks.pfnDataSent( + psDevice->psDevices[ulIdx].pvInstance, ulInfo); + } + } +} + +//**************************************************************************** +// +// This function is called to handle data being received back from the host so +// that the application callback can be called when the new data is ready. +// +//**************************************************************************** +static void +DataReceived(void *pvInstance, unsigned long ulInfo) +{ + unsigned long ulIdx; + const tDeviceInfo *pDeviceInfo; + tUSBDCompositeDevice *psDevice; + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + // + // Pass this notification on to the device which last handled a + // transaction on endpoint 0 (assuming we know who that was). + // + ulIdx = psDevice->psPrivateData->ulEP0Owner; + + if(ulIdx != INVALID_DEVICE_INDEX) + { + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + + if(pDeviceInfo->sCallbacks.pfnDataReceived) + { + pDeviceInfo->sCallbacks.pfnDataReceived( + psDevice->psDevices[ulIdx].pvInstance, ulInfo); + } + } +} + +//**************************************************************************** +// +// This function will check if any device classes need an endpoint handler +// called. +// +//**************************************************************************** +static void +HandleEndpoints(void *pvInstance, unsigned long ulStatus) +{ + unsigned long ulIdx; + const tDeviceInfo *pDeviceInfo; + tUSBDCompositeDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + // + // Call each of the endpoint handlers. This may seem odd since we should + // only call the handler whose endpoint needs service. Unfortunately, if + // the device class driver is using uDMA, we have no way of knowing which + // handler to call (since ulStatus will be 0). Since the handlers are + // set up to ignore any callback that isn't for them, this is safe. + // + for(ulIdx = 0; ulIdx < psDevice->ulNumDevices; ulIdx++) + { + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + + if(pDeviceInfo->sCallbacks.pfnEndpointHandler) + { + pDeviceInfo->sCallbacks.pfnEndpointHandler( + psDevice->psDevices[ulIdx].pvInstance, ulStatus); + } + } +} + +//**************************************************************************** +// +// This function is called by the USB device stack whenever the device is +// disconnected from the host. +// +//**************************************************************************** +static void +HandleDisconnect(void *pvInstance) +{ + unsigned long ulIdx; + const tDeviceInfo *pDeviceInfo; + tUSBDCompositeDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + // + // Inform the application that the device has been disconnected. + // + if(psDevice->pfnCallback) + { + psDevice->pfnCallback(pvInstance, USB_EVENT_DISCONNECTED, 0, 0); + } + + for(ulIdx = 0; ulIdx < psDevice->ulNumDevices; ulIdx++) + { + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + + if(pDeviceInfo->sCallbacks.pfnDisconnectHandler) + { + pDeviceInfo->sCallbacks.pfnDisconnectHandler( + psDevice->psDevices[ulIdx].pvInstance); + } + } +} + +//**************************************************************************** +// +// This function is called by the USB device stack whenever the device +// interface changes. It will be passed on to the device classes if they have +// a handler for this function. +// +//**************************************************************************** +static void +InterfaceChange(void *pvInstance, unsigned char ucInterfaceNum, + unsigned char ucAlternateSetting) +{ + unsigned long ulIdx; + const tDeviceInfo *pDeviceInfo; + tUSBDCompositeDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + for(ulIdx = 0; ulIdx < psDevice->ulNumDevices; ulIdx++) + { + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + + if(pDeviceInfo->sCallbacks.pfnInterfaceChange) + { + pDeviceInfo->sCallbacks.pfnInterfaceChange( + psDevice->psDevices[ulIdx].pvInstance, ucInterfaceNum, + ucAlternateSetting); + } + } +} + +//**************************************************************************** +// +// This function is called by the USB device stack whenever the device +// configuration changes. It will be passed on to the device classes if they +// have a handler for this function. +// +//**************************************************************************** +static void +ConfigChangeHandler(void *pvInstance, unsigned long ulValue) +{ + unsigned long ulIdx; + const tDeviceInfo *pDeviceInfo; + tUSBDCompositeDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + for(ulIdx = 0; ulIdx < psDevice->ulNumDevices; ulIdx++) + { + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + + if(pDeviceInfo->sCallbacks.pfnConfigChange) + { + pDeviceInfo->sCallbacks.pfnConfigChange( + psDevice->psDevices[ulIdx].pvInstance, ulValue); + } + } +} + +//**************************************************************************** +// +// This function is called by the USB device stack whenever a non-standard +// request is received. +// +// \param pvInstance +// \param pUSBRequest points to the request received. +// +// This call will be passed on to the device classes if they have a handler +// for this function. +// +// \return None. +// +//**************************************************************************** +static void +HandleRequests(void *pvInstance, tUSBRequest *pUSBRequest) +{ + unsigned long ulIdx; + const tDeviceInfo *pDeviceInfo; + tUSBDCompositeDevice *psDevice; + + // + // Create the device instance pointer. + // + psDevice = (tUSBDCompositeDevice *)pvInstance; + + // + // Determine which device this request is intended for. We have to be + // careful here to send this to the callback for the correct device + // depending upon whether it is a request sent to the device, the interface + // or the endpoint. + // + switch(pUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M) + { + case USB_RTYPE_INTERFACE: + { + ulIdx = InterfaceToIndex(psDevice, (pUSBRequest->wIndex & 0xFF)); + break; + } + + case USB_RTYPE_ENDPOINT: + { + ulIdx = EndpointToIndex(psDevice, (pUSBRequest->wIndex & 0x0F), + (pUSBRequest->wIndex & 0x80) ? true : false); + break; + } + + // + // Requests sent to the device or any other recipient can't be + // handled here since we have no way of telling where they are + // supposed to be handled. As a result, we just stall them. + // + // If your composite device has some device-specific requests that need + // to be handled at the device (rather than interface or endpoint) + // level, you should add code here to handle them. + // + case USB_RTYPE_DEVICE: + case USB_RTYPE_OTHER: + default: + { + ulIdx = INVALID_DEVICE_INDEX; + break; + } + } + + // + // Did we find a device class to pass the request to? + // + if(ulIdx != INVALID_DEVICE_INDEX) + { + // + // Get a pointer to the individual device instance. + // + pDeviceInfo = psDevice->psDevices[ulIdx].psDevice; + + // + // Does this device have a RequestHandler callback? + // + if(pDeviceInfo->sCallbacks.pfnRequestHandler) + { + // + // Remember this device index so that we can correctly route any + // data notification callbacks to it. + // + psDevice->psPrivateData->ulEP0Owner = ulIdx; + + // + // Yes - call the device to retrieve the descriptor. + // + pDeviceInfo->sCallbacks.pfnRequestHandler( + psDevice->psDevices[ulIdx].pvInstance, pUSBRequest); + } + else + { + // + // Oops - we can't satisfy the request so stall EP0 to indicate + // an error. + // + USBDCDStallEP0( + USB_BASE_TO_INDEX(psDevice->psPrivateData->ulUSBBase)); + } + } + else + { + // + // We are unable to satisfy the descriptor request so stall EP0 to + // indicate an error. + // + USBDCDStallEP0(USB_BASE_TO_INDEX(psDevice->psPrivateData->ulUSBBase)); + } + +} + +//**************************************************************************** +// +// This function handles sending interface number changes to device instances. +// +//**************************************************************************** +static void +CompositeIfaceChange(tCompositeEntry *pCompDevice, unsigned char ucOld, + unsigned char ucNew) +{ + unsigned char pucInterfaces[2]; + + if(pCompDevice->psDevice->sCallbacks.pfnDeviceHandler) + { + // + // Create the data to pass to the device handler. + // + pucInterfaces[0] = ucOld; + pucInterfaces[1] = ucNew; + + // + // Call the device handler to inform the class of the interface number + // change. + // + pCompDevice->psDevice->sCallbacks.pfnDeviceHandler( + pCompDevice->pvInstance, USB_EVENT_COMP_IFACE_CHANGE, + (void *)pucInterfaces); + } +} + +//**************************************************************************** +// +// This function handles sending endpoint number changes to device instances. +// +//**************************************************************************** +static void +CompositeEPChange(tCompositeEntry *pCompDevice, unsigned char ucOld, + unsigned char ucNew) +{ + unsigned char pucInterfaces[2]; + unsigned char ucIndex; + + if(pCompDevice->psDevice->sCallbacks.pfnDeviceHandler) + { + // + // Create the data to pass to the device handler. + // + pucInterfaces[0] = ucOld; + pucInterfaces[1] = ucNew; + + ucNew--; + + if(ucOld & USB_RTYPE_DIR_IN) + { + ucIndex = (ucOld & ~USB_RTYPE_DIR_IN) - 1; + + g_sUSBCompositeFIFOConfig.sIn[ucNew].bDoubleBuffer = + pCompDevice->psDevice->psFIFOConfig->sIn[ucIndex].bDoubleBuffer; + + g_sUSBCompositeFIFOConfig.sIn[ucNew].usEPFlags = + pCompDevice->psDevice->psFIFOConfig->sIn[ucIndex].usEPFlags; + } + else + { + ucIndex = ucOld - 1; + + g_sUSBCompositeFIFOConfig.sOut[ucNew].bDoubleBuffer = + pCompDevice->psDevice->psFIFOConfig->sOut[ucIndex].bDoubleBuffer; + + g_sUSBCompositeFIFOConfig.sOut[ucNew].usEPFlags = + pCompDevice->psDevice->psFIFOConfig->sOut[ucIndex].usEPFlags; + } + // + // Call the device handler to inform the class of the interface number + // change. + // + pCompDevice->psDevice->sCallbacks.pfnDeviceHandler( + pCompDevice->pvInstance, USB_EVENT_COMP_EP_CHANGE, + (void *)pucInterfaces); + } +} + +//**************************************************************************** +// +// This function merges the configuration descriptors into a single multiple +// instance device. +// +//**************************************************************************** +unsigned long +BuildCompositeDescriptor(tUSBDCompositeDevice *psCompDevice) +{ + unsigned long ulIdx, ulOffset, ulCPIdx, ulFixINT, ulDev; + unsigned short usTotalLength, usBytes; + unsigned char ucInterface, ucINEndpoint, ucOUTEndpoint; + unsigned char *pucData, *pucConfig; + const tConfigHeader *pConfigHeader; + tDescriptorHeader *psHeader; + const unsigned char *pucDescriptor; + tInterfaceDescriptor *psInterface; + tEndpointDescriptor *psEndpoint; + const tDeviceInfo *psDevice; + + // + // Save the number of devices to look through. + // + ulDev = 0; + ulIdx = 0; + ucInterface = 0; + ucINEndpoint = 1; + ucOUTEndpoint = 1; + ulOffset = 0; + ulFixINT = 0; + + // + // This puts the first section pointer in the first entry in the list + // of sections. + // + psCompDevice->psPrivateData->ppsCompSections[0] = + &psCompDevice->psPrivateData->psCompSections[0]; + + // + // Put the pointer to this instances configuration descriptor into the + // front of the list. + // + psCompDevice->psPrivateData->ppsCompSections[0]->pucData = + (unsigned char *)&psCompDevice->psPrivateData->sConfigDescriptor; + + psCompDevice->psPrivateData->ppsCompSections[0]->usSize = + psCompDevice->psPrivateData->sConfigDescriptor.bLength; + + // + // The configuration descriptor is 9 bytes so initialize the total length + // to 9 bytes. + // + usTotalLength = 9; + + // + // Copy the section pointer into the section array for the composite + // device. This is awkward but is required given the definition + // of the structures. + // + psCompDevice->psPrivateData->ppsCompSections[1] = + &psCompDevice->psPrivateData->psCompSections[1]; + + // + // Copy the pointer to the application supplied space into the section + // list. + // + psCompDevice->psPrivateData->ppsCompSections[1]->usSize = 0; + psCompDevice->psPrivateData->ppsCompSections[1]->pucData = + psCompDevice->psPrivateData->pucData; + + // + // Create a local pointer to the data that is used to copy data from + // the other devices into the composite descriptor. + // + pucData = psCompDevice->psPrivateData->pucData; + + // + // Consider each device in turn. + // + while(ulDev < psCompDevice->ulNumDevices) + { + // + // Save the current starting address of this descriptor. + // + pucConfig = pucData + ulOffset; + + // + // Create a local pointer to the configuration header. + // + psDevice = psCompDevice->psDevices[ulDev].psDevice; + pConfigHeader = psDevice->ppConfigDescriptors[0]; + + // + // Loop through each of the sections in this device's configuration + // descriptor. + // + for(ulIdx = 0; ulIdx < pConfigHeader->ucNumSections; ulIdx++) + { + // + // Initialize the local offset in this descriptor. We include + // a special case here to ignore the initial 9 byte config + // descriptor since this has already been handled. + // + if(ulIdx) + { + // + // This is not the first section so we handle everything in + // it. + // + usBytes = 0; + } + else + { + // + // This is the first section for this device so skip the 9 + // byte config descriptor since we've already handled this. + // + usBytes = 9; + + // + // If this section includes only the config descriptor, skip + // it entirely. + // + if(pConfigHeader->psSections[ulIdx]->usSize <= usBytes) + { + continue; + } + } + + // + // Get a pointer to the configuration descriptor. + // + pucDescriptor = pConfigHeader->psSections[ulIdx]->pucData; + + // + // Bounds check the allocated space and return if there is not + // enough space. + // + if(ulOffset > psCompDevice->psPrivateData->ulDataSize) + { + return(1); + } + + // + // Copy the descriptor from the device into the descriptor list. + // + for(ulCPIdx = 0; + ulCPIdx < pConfigHeader->psSections[ulIdx]->usSize; + ulCPIdx++) + { + pucData[ulCPIdx + ulOffset] = pucDescriptor[ulCPIdx]; + } + + // + // Read out the descriptors in this section. + // + while(usBytes < pConfigHeader->psSections[ulIdx]->usSize) + { + // + // Create a descriptor header pointer. + // + psHeader = (tDescriptorHeader *)&pucData[ulOffset + usBytes]; + + // + // Check for interface descriptors and modify the numbering to + // match the composite device. + // + if(psHeader->bDescriptorType == USB_DTYPE_INTERFACE) + { + psInterface = (tInterfaceDescriptor *)psHeader; + + // + // See if this is an alternate setting or the initial + // setting. + // + if(psInterface->bAlternateSetting != 0) + { + // + // If this is an alternate setting then use the + // previous interface number because the current one + // has already been incremented. + // + psInterface->bInterfaceNumber = ucInterface - 1; + } + else + { + // + // Notify the class that it's interface number has + // changed. + // + CompositeIfaceChange(&psCompDevice->psDevices[ulDev], + psInterface->bInterfaceNumber, + ucInterface); + // + // This was the non-alternate setting so save the + // value and move to the next interface number. + // + psInterface->bInterfaceNumber = ucInterface; + + // + // No strings allowed on interface descriptors for + // composite devices. + // + psInterface->iInterface = 0; + + ucInterface++; + } + } + // + // Check for endpoint descriptors and modify the numbering to + // match the composite device. + // + else if(psHeader->bDescriptorType == USB_DTYPE_ENDPOINT) + { + psEndpoint = (tEndpointDescriptor *)psHeader; + + // + // Check if this is an IN or OUT endpoint. + // + if(psEndpoint->bEndpointAddress & USB_RTYPE_DIR_IN) + { + // + // Check if this is the special Fixed Interrupt class + // and this is the interrupt endpoint. + // + if(((psEndpoint->bmAttributes & USB_EP_ATTR_TYPE_M) == + USB_EP_ATTR_INT) && + (psCompDevice->usPID == USB_PID_COMP_SERIAL)) + { + // + // Check if the Fixed Interrupt endpoint has been + // set yet. + // + if(ulFixINT == 0) + { + // + // Allocate the fixed interrupt endpoint and + // save its number. + // + ulFixINT = ucINEndpoint++; + } + + CompositeEPChange(&psCompDevice->psDevices[ulDev], + psEndpoint->bEndpointAddress, + ulFixINT); + + psEndpoint->bEndpointAddress = ulFixINT | + USB_RTYPE_DIR_IN; + } + else + { + // + // Notify the class that it's interface number has + // changed. + // + CompositeEPChange(&psCompDevice->psDevices[ulDev], + psEndpoint->bEndpointAddress, + ucINEndpoint); + + psEndpoint->bEndpointAddress = ucINEndpoint++ | + USB_RTYPE_DIR_IN; + } + } + else + { + // + // Notify the class that it's interface number has + // changed. + // + CompositeEPChange(&psCompDevice->psDevices[ulDev], + psEndpoint->bEndpointAddress, + ucOUTEndpoint); + psEndpoint->bEndpointAddress = ucOUTEndpoint++; + } + } + + // + // Move on to the next descriptor. + // + usBytes += psHeader->bLength; + } + + ulOffset += pConfigHeader->psSections[ulIdx]->usSize; + + usTotalLength += usBytes; + } + + // + // Allow the device class to make adjustments to the configuration + // descriptor. + // + psCompDevice->psDevices[ulDev].psDevice->sCallbacks.pfnDeviceHandler( + psCompDevice->psDevices[ulDev].pvInstance, + USB_EVENT_COMP_CONFIG, (void *)pucConfig); + + // + // Add an entry into the device workspace array to allow us to quickly + // map interface and endpoint numbers to device instances later. + // + psCompDevice->pulDeviceWorkspace[ulDev] = + (ulDev << (LOOKUP_INDEX_BYTE * 8)) | + (ucInterface << (LOOKUP_INTERFACE_BYTE * 8)) | + (ucOUTEndpoint << (LOOKUP_OUT_END_BYTE * 8)) | + (ucINEndpoint << (LOOKUP_IN_END_BYTE * 8)); + + // + // Move on to the next device. + // + ulDev++; + } + + // + // Modify the configuration descriptor to match the number of interfaces + // and the new total size. + // + psCompDevice->psPrivateData->sCompConfigHeader.ucNumSections = 2; + psCompDevice->psPrivateData->ppsCompSections[1]->usSize = ulOffset; + psCompDevice->psPrivateData->sConfigDescriptor.bNumInterfaces = + ucInterface; + psCompDevice->psPrivateData->sConfigDescriptor.wTotalLength = + usTotalLength; + + + return(0); +} + +//**************************************************************************** +// +//! This function should be called once for the composite class device to +//! initialize basic operation and prepare for enumeration. +//! +//! \param ulIndex is the index of the USB controller to initialize for +//! composite device operation. +//! \param psDevice points to a structure containing parameters customizing +//! the operation of the composite device. +//! \param ulSize is the size in bytes of the data pointed to by the +//! \e pucData parameter. +//! \param pucData is the data area that the composite class can use to build +//! up descriptors. +//! +//! In order for an application to initialize the USB composite device class, +//! it must first call this function with the a valid composite device class +//! structure in the \e psDevice parameter. This allows this function to +//! initialize the USB controller and device code to be prepared to enumerate +//! and function as a USB composite device. The \e ulSize and \e pucData +//! parameters should be large enough to hold all of the class instances +//! passed in via the psDevice structure. This is typically the full size of +//! the configuration descriptor for a device minus its configuration +//! header(9 bytes). +//! +//! This function returns a void pointer that must be passed in to all other +//! APIs used by the composite class. +//! +//! See the documentation on the tUSBDCompositeDevice structure for more +//! information on how to properly fill the structure members. +//! +//! \return This function returns 0 on failure or a non-zero void pointer on +//! success. +// +//**************************************************************************** +void * +USBDCompositeInit(unsigned long ulIndex, tUSBDCompositeDevice *psDevice, + unsigned long ulSize, unsigned char *pucData) +{ + tCompositeInstance *psInst; + long lIdx; + unsigned char *pucTemp; + + // + // Check parameter validity. + // + ASSERT(ulIndex == 0); + ASSERT(psDevice); + ASSERT(psDevice->ppStringDescriptors); + ASSERT(psDevice->psPrivateData); + + // + // Initialize the work space in the passed instance structure. + // + psInst = psDevice->psPrivateData; + psInst->ulDataSize = ulSize; + psInst->pucData = pucData; + + // + // Save the base address of the USB controller. + // + psInst->ulUSBBase = USB_INDEX_TO_BASE(ulIndex); + + // + // No device is currently transfering data on EP0. + // + psInst->ulEP0Owner = INVALID_DEVICE_INDEX; + + // + // Set the device information for the composite device. + // + psInst->psDevInfo = &g_sCompositeDeviceInfo; + + g_pCompConfigDescriptors[0] = &psInst->sCompConfigHeader; + g_pCompConfigDescriptors[0]->ucNumSections = 0; + g_pCompConfigDescriptors[0]->psSections = + (const tConfigSection * const *)psDevice->psPrivateData->ppsCompSections; + + // + // Create a byte pointer to use with the copy. + // + pucTemp = (unsigned char *)&psInst->sConfigDescriptor; + + // + // Copy the default configuration descriptor into the instance data. + // + for(lIdx = 0; lIdx < g_pCompConfigDescriptor[0]; lIdx++) + { + pucTemp[lIdx] = g_pCompConfigDescriptor[lIdx]; + } + + // + // Create a byte pointer to use with the copy. + // + pucTemp = (unsigned char *)&psInst->sDeviceDescriptor; + + // + // Copy the default configuration descriptor into the instance data. + // + for(lIdx = 0; lIdx < g_pCompDeviceDescriptor[0]; lIdx++) + { + pucTemp[lIdx] = g_pCompDeviceDescriptor[lIdx]; + } + + // + // Fix up the device descriptor with the client-supplied values. + // + psInst->sDeviceDescriptor.idVendor = psDevice->usVID; + psInst->sDeviceDescriptor.idProduct = psDevice->usPID; + + // + // Fix up the configuration descriptor with client-supplied values. + // + psInst->sConfigDescriptor.bmAttributes = psDevice->ucPwrAttributes; + psInst->sConfigDescriptor.bMaxPower = + (unsigned char)(psDevice->usMaxPowermA>>1); + + g_sCompositeDeviceInfo.pDeviceDescriptor = + (const unsigned char *)&psInst->sDeviceDescriptor; + + // + // Plug in the client's string table to the device information + // structure. + // + psInst->psDevInfo->ppStringDescriptors = psDevice->ppStringDescriptors; + psInst->psDevInfo->ulNumStringDescriptors = + psDevice->ulNumStringDescriptors; + + // + // Enable Clocking to the USB controller so that changes to the USB + // controller can be made in the BuildCompositeDescriptor() function. + // + MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_USB0); + + // + // Create the combined descriptors. + // + if(BuildCompositeDescriptor(psDevice)) + { + return(0); + } + + // + // Set the instance data for this device. + // + psInst->psDevInfo->pvInstance = (void *)psDevice; + + // + // All is well so now pass the descriptors to the lower layer and put + // the bulk device on the bus. + // + USBDCDInit(ulIndex, psInst->psDevInfo); + + // + // Return the pointer to the instance indicating that everything went + // well. + // + return((void *)psDevice); +} + +//**************************************************************************** +// +//! Shuts down the composite device. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDCompositeInit(). +//! +//! This function terminates composite device interface for the instance +//! supplied. Following this call, the \e pvInstance instance should not me +//! used in any other calls. +//! +//! \return None. +// +//**************************************************************************** +void +USBDCompositeTerm(void *pvInstance) +{ + ASSERT(pvInstance != 0); + +} + +//**************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//**************************************************************************** + diff --git a/usblib/device/usbdcomp.h b/usblib/device/usbdcomp.h new file mode 100644 index 0000000..5d26352 --- /dev/null +++ b/usblib/device/usbdcomp.h @@ -0,0 +1,297 @@ +//***************************************************************************** +// +// usbdcomp.h - USB composite device class driver. +// +// Copyright (c) 2010-2012 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#ifndef __USBDCOMP_H__ +#define __USBDCOMP_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// Return to default packing when using the IAR Embedded Workbench compiler. +// +//***************************************************************************** +#ifdef ewarm +#pragma pack() +#endif + +//***************************************************************************** +// +//! \addtogroup composite_device_class_api +//! @{ +// +//***************************************************************************** + +// +// Defines a single entry in a table of device types supported by the composite +// device. +// +typedef struct +{ + // + // This is set internally by the composite class so it can be left + // uninitialized by the application. + // + const tDeviceInfo *pDeviceInfo; + + // + // This should be the header to the configuration header for a class. + // + const tConfigHeader *psConfigHeader; + + // + // The offset to this devices interface, filled in by the composite class. + // + unsigned char ucIfaceOffset; + +} tUSBDCompositeEntry; + +//***************************************************************************** +// +// PRIVATE +// +// This structure defines the private instance data and state variables for the +// composite device class. The memory for this structure is pointed to by +// the psPrivateData field in the tUSBDCompositeDevice structure passed on +// USBDCompositeInit() and should not be modified by any code outside of the +// composite device code. +// +//***************************************************************************** +typedef struct +{ + // + // Saves which USB controller is in use. + // + unsigned long ulUSBBase; + + // + // The device information pointer. + // + tDeviceInfo *psDevInfo; + + // + // This is the configuration descriptor for this instance. + // + tConfigDescriptor sConfigDescriptor; + + // + // This is the device descriptor for this instance. + // + tDeviceDescriptor sDeviceDescriptor; + + // + // The configuration header for this instance. + // + tConfigHeader sCompConfigHeader; + + // + // These are the configuration sections that will be built from the + // Configuration Descriptor header and the descriptors from the devices + // that are part of this composite device. + // + tConfigSection psCompSections[2]; + tConfigSection *ppsCompSections[2]; + + // + // The size and pointer to the data used by the instance. + // + unsigned long ulDataSize; + unsigned char *pucData; + + // + // The current "owner" of endpoint 0. This is used to track the device + // class which is currently transferring data on EP0. + // + unsigned long ulEP0Owner; +} +tCompositeInstance; + +//***************************************************************************** +// +//! This type is used by an application to describe and instance of a device +//! and an instance data pointer for that class. The psDevice pointer should +//! be a pointer to a valid device class to include in the composite device. +//! The pvInstance pointer should be a pointer to an instance pointer for the +//! device in the psDevice pointer. +//! +// +//***************************************************************************** +typedef struct +{ + // + //! This is the top level device information structure. + // + const tDeviceInfo *psDevice; + + // + //! This is the instance data for the device structure. + // + void *pvInstance; +} +tCompositeEntry; + +//***************************************************************************** +// +//! The structure used by the application to define operating parameters for +//! the composite device class. +// +//***************************************************************************** +typedef struct +{ + // + //! The vendor ID that this device is to present in the device descriptor. + // + unsigned short usVID; + + // + //! The product ID that this device is to present in the device descriptor. + // + unsigned short usPID; + + // + //! The maximum power consumption of the device, expressed in mA. + // + unsigned short usMaxPowermA; + + // + //! Indicates whether the device is self or bus-powered and whether or not + //! it supports remote wake up. Valid values are USB_CONF_ATTR_SELF_PWR or + //! USB_CONF_ATTR_BUS_PWR, optionally ORed with USB_CONF_ATTR_RWAKE. + // + unsigned char ucPwrAttributes; + + // + //! A pointer to the callback function which will be called to notify + //! the application of events relating to the operation of the composite + //! device. + // + tUSBCallback pfnCallback; + + // + //! A pointer to the string descriptor array for this device. This array + //! must contain the following string descriptor pointers in this order. + //! Language descriptor, Manufacturer name string (language 1), Product + //! name string (language 1), Serial number string (language 1), Composite + //! device interface description string (language 1), Configuration + //! description string (language 1). + //! + //! If supporting more than 1 language, the descriptor block (except for + //! string descriptor 0) must be repeated for each language defined in the + //! language descriptor. + //! + // + const unsigned char * const *ppStringDescriptors; + + // + //! The number of descriptors provided in the ppStringDescriptors + //! array. This must be 1 + ((5 + (number of strings)) * + //! (number of languages)). + // + unsigned long ulNumStringDescriptors; + + // + //! The number of devices in the psDevices array. + // + unsigned long ulNumDevices; + + // + //! This application supplied array holds the the top level device class + //! information as well as the Instance data for that class. + // + tCompositeEntry *psDevices; + + // + //! A pointer to per-device workspace used by the composite device. This + //! buffer must be sized to contain at least ulNumDevices long integers + //! (or 4 * ulNumDevices bytes). It must remain accessible for as long as + //! the composite device is in use and must not be modified by any code + //! outside the composite class driver. + // + unsigned long *pulDeviceWorkspace; + + // + //! A pointer to RAM work space for this device instance. The client + //! must fill in this field with a pointer to at least + //! sizeof(tCompositeInstance) bytes of read/write storage that the + //! library can use for driver work space. This memory must remain + //! accessible for as long as the composite device is in use and must not + //! be modified by any code outside the composite class driver. + // + tCompositeInstance *psPrivateData; +} +tUSBDCompositeDevice; + +//***************************************************************************** +// +// Return to default packing when using the IAR Embedded Workbench compiler. +// +//***************************************************************************** +#ifdef ewarm +#pragma pack() +#endif + +//***************************************************************************** +// +// Composite specific device class driver events +// +//***************************************************************************** + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void *USBDCompositeInit(unsigned long ulIndex, + tUSBDCompositeDevice *psCompDevice, + unsigned long ulSize, + unsigned char *pucData); +extern void USBDCompositeTerm(void *pvInstance); + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/usblib/device/usbdconfig.c b/usblib/device/usbdconfig.c new file mode 100644 index 0000000..9737607 --- /dev/null +++ b/usblib/device/usbdconfig.c @@ -0,0 +1,654 @@ +//***************************************************************************** +// +// usbdconfig.c - High level USB device configuration function. +// +// Copyright (c) 2008-2012 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbdevicepriv.h" + +//***************************************************************************** +// +//! \addtogroup device_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Mask used to preserve various endpoint configuration flags. +// +//***************************************************************************** +#define EP_FLAGS_MASK (USB_EP_MODE_MASK | USB_EP_DEV_IN | \ + USB_EP_DEV_OUT) + +//***************************************************************************** +// +// Structure used in compiling FIFO size and endpoint properties from a +// configuration descriptor. +// +//***************************************************************************** +typedef struct +{ + unsigned long ulSize[2]; +} +tUSBEndpointInfo; + +//***************************************************************************** +// +// Indices used when accessing the tUSBEndpointInfo structure. +// +//***************************************************************************** +#define EP_INFO_IN 0 +#define EP_INFO_OUT 1 + +//***************************************************************************** +// +// Given a maximum packet size and the user's FIFO scaling requirements, +// determine the flags to use to configure the endpoint FIFO and the number +// of bytes of FIFO space occupied. +// +//***************************************************************************** +static unsigned long +GetEndpointFIFOSize(unsigned long ulMaxPktSize, const tFIFOEntry *psFIFOParams, + unsigned long *pupBytesUsed) +{ + unsigned long ulBytes; + unsigned long ulLoop; + unsigned long ulFIFOSize; + + // + // What is the basic size required for a single buffered FIFO entry + // containing the required number of packets? + // + ulBytes = ulMaxPktSize; + + // + // Now we need to find the nearest supported size that accommodates the + // requested size. Step through each of the supported sizes until we + // find one that will do. + // + for(ulLoop = USB_FIFO_SZ_8; ulLoop <= USB_FIFO_SZ_4096; ulLoop++) + { + // + // How many bytes does this FIFO value represent? + // + ulFIFOSize = USB_FIFO_SZ_TO_BYTES(ulLoop); + + // + // Is this large enough to satisfy the request? + // + if(ulFIFOSize >= ulBytes) + { + // + // Yes - are we being asked to double-buffer the FIFO for this + // endpoint? + // + if(psFIFOParams->bDoubleBuffer) + { + // + // Yes - FIFO requirement is double in this case. + // + *pupBytesUsed = ulFIFOSize * 2; + return(ulLoop | USB_FIFO_SIZE_DB_FLAG); + } + else + { + // + // No double buffering so just return the size and associated + // flag. + // + *pupBytesUsed = ulFIFOSize; + return(ulLoop); + } + } + } + + // + // If we drop out, we can't support the FIFO size requested. Signal a + // problem by returning 0 in the pBytesUsed + // + *pupBytesUsed = 0; + + return(USB_FIFO_SZ_8); +} + +//***************************************************************************** +// +// Translate a USB endpoint descriptor into the values we need to pass to the +// USBDevEndpointConfigSet() API. +// +//***************************************************************************** +static void +GetEPDescriptorType(tEndpointDescriptor *psEndpoint, unsigned long *pulEPIndex, + unsigned long *pulMaxPktSize, unsigned long *pulFlags) +{ + // + // Get the endpoint index. + // + *pulEPIndex = psEndpoint->bEndpointAddress & USB_EP_DESC_NUM_M; + + // + // Extract the maximum packet size. + // + *pulMaxPktSize = psEndpoint->wMaxPacketSize & USB_EP_MAX_PACKET_COUNT_M; + + // + // Is this an IN or an OUT endpoint? + // + *pulFlags = (psEndpoint->bEndpointAddress & USB_EP_DESC_IN) ? + USB_EP_DEV_IN : USB_EP_DEV_OUT; + + // + // Set the endpoint mode. + // + switch(psEndpoint->bmAttributes & USB_EP_ATTR_TYPE_M) + { + case USB_EP_ATTR_CONTROL: + *pulFlags |= USB_EP_MODE_CTRL; + break; + + case USB_EP_ATTR_BULK: + *pulFlags |= USB_EP_MODE_BULK; + break; + + case USB_EP_ATTR_INT: + *pulFlags |= USB_EP_MODE_INT; + break; + + case USB_EP_ATTR_ISOC: + *pulFlags |= USB_EP_MODE_ISOC; + break; + } +} + +//***************************************************************************** +// +//! Configure the USB controller appropriately for the device whose config +//! descriptor is passed. +//! +//! \param psDevInst is a pointer to the device instance being configured. +//! \param psConfig is a pointer to the configuration descriptor that the +//! USB controller is to be set up to support. +//! +//! This function may be used to initialize a USB controller to operate as +//! the device whose configuration descriptor is passed. The function +//! enables the USB controller, partitions the FIFO appropriately and +//! configures each endpoint required by the configuration. If the supplied +//! configuration supports multiple alternate settings for any interface, +//! the USB FIFO is set up assuming the worst case use (largest packet size +//! for a given endpoint in any alternate setting using that endpoint) to +//! allow for on-the-fly alternate setting changes later. On return from this +//! function, the USB controller is configured for correct operation of +//! the default configuration of the device described by the descriptor passed. +//! +//! USBDCDConfig() is an optional call and applications may chose to make +//! direct calls to SysCtlPeripheralEnable(), SysCtlUSBPLLEnable(), +//! USBDevEndpointConfigSet() and USBFIFOConfigSet() instead of using this +//! function. If this function is used, it must be called prior to +//! USBDCDInit() since this call assumes that the low level hardware +//! configuration has been completed before it is made. +//! +//! \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +tBoolean +USBDeviceConfig(tDeviceInstance *psDevInst, const tConfigHeader *psConfig) +{ + unsigned long ulLoop; + unsigned long ulCount; + unsigned long ulNumInterfaces; + unsigned long ulEpIndex; + unsigned long ulEpType; + unsigned long ulMaxPkt; + unsigned long ulNumEndpoints; + unsigned long ulFlags; + unsigned long ulBytesUsed; + unsigned long ulSection; + tInterfaceDescriptor *psInterface; + tEndpointDescriptor *psEndpoint; + tFIFOConfig const *psFIFOConfig; + tUSBEndpointInfo psEPInfo[NUM_USB_EP - 1]; + + // + // A valid device instance is required. + // + ASSERT(psDevInst != 0); + + // + // Catch bad pointers in a debug build. + // + ASSERT(psConfig); + + psFIFOConfig = psDevInst->psInfo->psFIFOConfig; + + // + // Clear out our endpoint info. + // + for(ulLoop = 0; ulLoop < (NUM_USB_EP - 1); ulLoop++) + { + psEPInfo[ulLoop].ulSize[EP_INFO_IN] = 0; + psEPInfo[ulLoop].ulSize[EP_INFO_OUT] = 0; + } + + // + // How many (total) endpoints does this configuration describe? + // + ulNumEndpoints = USBDCDConfigDescGetNum(psConfig, + USB_DTYPE_ENDPOINT); + + // + // How many interfaces are included? + // + ulNumInterfaces = USBDCDConfigDescGetNum(psConfig, + USB_DTYPE_INTERFACE); + + // + // Look at each endpoint and determine the largest max packet size for + // each endpoint. This will determine how we partition the USB FIFO. + // + for(ulLoop = 0; ulLoop < ulNumEndpoints; ulLoop++) + { + // + // Get a pointer to the endpoint descriptor. + // + psEndpoint = (tEndpointDescriptor *)USBDCDConfigDescGet( + psConfig, USB_DTYPE_ENDPOINT, ulLoop, + &ulSection); + + // + // Extract the endpoint number and whether it is an IN or OUT + // endpoint. + // + ulEpIndex = (unsigned long) + psEndpoint->bEndpointAddress & USB_EP_DESC_NUM_M; + ulEpType = (psEndpoint->bEndpointAddress & USB_EP_DESC_IN) ? + EP_INFO_IN : EP_INFO_OUT; + + // + // Make sure the endpoint number is valid for our controller. If not, + // return false to indicate an error. Note that 0 is invalid since + // you shouldn't reference endpoint 0 in the config descriptor. + // + if((ulEpIndex >= NUM_USB_EP) || (ulEpIndex == 0)) + { + return(false); + } + + // + // Does this endpoint have a max packet size requirement larger than + // any previous use we have seen? + // + if(psEndpoint->wMaxPacketSize > + psEPInfo[ulEpIndex - 1].ulSize[ulEpType]) + { + // + // Yes - remember the new maximum packet size. + // + psEPInfo[ulEpIndex - 1].ulSize[ulEpType] = + psEndpoint->wMaxPacketSize; + } + } + + // + // At this point, we have determined the maximum packet size required + // for each endpoint by any possible alternate setting of any interface + // in this configuration. Now determine the endpoint settings required + // for the interface setting we are actually going to use. + // + for(ulLoop = 0; ulLoop < ulNumInterfaces; ulLoop++) + { + // + // Get the next interface descriptor in the config descriptor. + // + psInterface = USBDCDConfigGetInterface(psConfig, + ulLoop, + USB_DESC_ANY, + &ulSection); + + // + // Is this the default interface (bAlternateSetting set to 0)? + // + if(psInterface && (psInterface->bAlternateSetting == 0)) + { + // + // This is an interface we are interested in so gather the + // information on its endpoints. + // + ulNumEndpoints = (unsigned long)psInterface->bNumEndpoints; + + // + // Walk through each endpoint in this interface and configure + // it appropriately. + // + for(ulCount = 0; ulCount < ulNumEndpoints; ulCount++) + { + // + // Get a pointer to the endpoint descriptor. + // + psEndpoint = USBDCDConfigGetInterfaceEndpoint(psConfig, + psInterface->bInterfaceNumber, + psInterface->bAlternateSetting, + ulCount); + + // + // Make sure we got a good pointer. + // + if(psEndpoint) + { + // + // Determine maximum packet size and flags from the + // endpoint descriptor. + // + GetEPDescriptorType(psEndpoint, &ulEpIndex, &ulMaxPkt, + &ulFlags); + + // + // Make sure no-one is trying to configure endpoint 0. + // + if(!ulEpIndex) + { + return(false); + } + + // + // Include any additional flags that the user wants. + // + if((ulFlags & (USB_EP_DEV_IN | USB_EP_DEV_OUT)) == + USB_EP_DEV_IN) + { + // + // This is an IN endpoint. + // + ulFlags |= (unsigned long)( + psFIFOConfig->sIn[ulEpIndex - 1].usEPFlags); + } + else + { + // + // This is an OUT endpoint. + // + ulFlags |= (unsigned long)( + psFIFOConfig->sOut[ulEpIndex - 1].usEPFlags); + } + + // + // Set the endpoint configuration. + // + USBDevEndpointConfigSet(USB0_BASE, + INDEX_TO_USB_EP(ulEpIndex), + ulMaxPkt, ulFlags); + } + } + } + } + + // + // At this point, we have configured all the endpoints that are to be + // used by this configuration's alternate setting 0. Now we go on and + // partition the FIFO based on the maximum packet size information we + // extracted earlier. Endpoint 0 is automatically configured to use the + // first MAX_PACKET_SIZE_EP0 bytes of the FIFO so we start from there. + // + ulCount = MAX_PACKET_SIZE_EP0; + for(ulLoop = 1; ulLoop < NUM_USB_EP; ulLoop++) + { + // + // Configure the IN endpoint at this index if it is referred to + // anywhere. + // + if(psEPInfo[ulLoop - 1].ulSize[EP_INFO_IN]) + { + // + // What FIFO size flag do we use for this endpoint? + // + ulMaxPkt = GetEndpointFIFOSize( + psEPInfo[ulLoop - 1].ulSize[EP_INFO_IN], + &(psFIFOConfig->sIn[ulLoop - 1]), + &ulBytesUsed); + + // + // If we are told that 0 bytes of FIFO will be used, this implies + // that there is an error in psFIFOConfig or the descriptor + // somewhere so return an error indicator to the caller. + // + if(!ulBytesUsed) + { + return(false); + } + + // + // Now actually configure the FIFO for this endpoint. + // + USBFIFOConfigSet(USB0_BASE, INDEX_TO_USB_EP(ulLoop), ulCount, + ulMaxPkt, USB_EP_DEV_IN); + ulCount += ulBytesUsed; + } + + // + // Configure the OUT endpoint at this index. + // + if(psEPInfo[ulLoop - 1].ulSize[EP_INFO_OUT]) + { + // + // What FIFO size flag do we use for this endpoint? + // + ulMaxPkt = GetEndpointFIFOSize( + psEPInfo[ulLoop - 1].ulSize[EP_INFO_OUT], + &(psFIFOConfig->sOut[ulLoop - 1]), + &ulBytesUsed); + + // + // If we are told that 0 bytes of FIFO will be used, this implies + // that there is an error in psFIFOConfig or the descriptor + // somewhere so return an error indicator to the caller. + // + if(!ulBytesUsed) + { + return(false); + } + + // + // Now actually configure the FIFO for this endpoint. + // + USBFIFOConfigSet(USB0_BASE, INDEX_TO_USB_EP(ulLoop), ulCount, + ulMaxPkt, USB_EP_DEV_OUT); + ulCount += ulBytesUsed; + } + + } + + // + // If we get to the end, all is well. + // + return(true); +} + +//***************************************************************************** +// +//! Configure the affected USB endpoints appropriately for one alternate +//! interface setting. +//! +//! \param psDevInst is a pointer to the device instance being configured. +//! \param psConfig is a pointer to the configuration descriptor that contains +//! the interface whose alternate settings is to be configured. +//! \param ucInterfaceNum is the number of the interface whose alternate +//! setting is to be configured. This number corresponds to the +//! bInterfaceNumber field in the desired interface descriptor. +//! \param ucAlternateSetting is the alternate setting number for the desired +//! interface. This number corresponds to the bAlternateSetting field in the +//! desired interface descriptor. +//! +//! This function may be used to reconfigure the endpoints of an interface +//! for operation in one of the interface's alternate settings. Note that this +//! function assumes that the endpoint FIFO settings will not need to change +//! and only the endpoint mode is changed. This assumption is valid if the +//! USB controller was initialized using a previous call to USBDCDConfig(). +//! +//! In reconfiguring the interface endpoints, any additional configuration +//! bits set in the endpoint configuration other than the direction (\b +//! USB_EP_DEV_IN or \b USB_EP_DEV_OUT) and mode (\b USB_EP_MODE_MASK) are +//! preserved. +//! +//! \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +tBoolean +USBDeviceConfigAlternate(tDeviceInstance *psDevInst, const tConfigHeader *psConfig, + unsigned char ucInterfaceNum, + unsigned char ucAlternateSetting) +{ + unsigned long ulNumInterfaces; + unsigned long ulNumEndpoints; + unsigned long ulLoop; + unsigned long ulCount; + unsigned long ulMaxPkt; + unsigned long ulFlags; + unsigned long ulSection; + unsigned long ulEpIndex; + tInterfaceDescriptor *psInterface; + tEndpointDescriptor *psEndpoint; + tFIFOConfig const *psFIFOConfig; + + // + // How many interfaces are included in the descriptor? + // + ulNumInterfaces = USBDCDConfigDescGetNum(psConfig, + USB_DTYPE_INTERFACE); + + psFIFOConfig = psDevInst->psInfo->psFIFOConfig; + + // + // Find the interface descriptor for the supplied interface and alternate + // setting numbers. + // + + for(ulLoop = 0; ulLoop < ulNumInterfaces; ulLoop++) + { + // + // Get the next interface descriptor in the config descriptor. + // + psInterface = USBDCDConfigGetInterface(psConfig, ulLoop, USB_DESC_ANY, + &ulSection); + + // + // Is this the default interface (bAlternateSetting set to 0)? + // + if(psInterface && + (psInterface->bInterfaceNumber == ucInterfaceNum) && + (psInterface->bAlternateSetting == ucAlternateSetting)) + { + // + // This is an interface we are interested in and the descriptor + // representing the alternate setting we want so go ahead and + // reconfigure the endpoints. + // + + // + // How many endpoints does this interface have? + // + ulNumEndpoints = (unsigned long)psInterface->bNumEndpoints; + + // + // Walk through each endpoint in turn. + // + for(ulCount = 0; ulCount < ulNumEndpoints; ulCount++) + { + // + // Get a pointer to the endpoint descriptor. + // + psEndpoint = USBDCDConfigGetInterfaceEndpoint(psConfig, + psInterface->bInterfaceNumber, + psInterface->bAlternateSetting, + ulCount); + + // + // Make sure we got a good pointer. + // + if(psEndpoint) + { + // + // Determine maximum packet size and flags from the + // endpoint descriptor. + // + GetEPDescriptorType(psEndpoint, &ulEpIndex, &ulMaxPkt, + &ulFlags); + + // + // Make sure no-one is trying to configure endpoint 0. + // + if(!ulEpIndex) + { + return(false); + } + + // + // Include any additional flags that the user wants. + // + if((ulFlags & (USB_EP_DEV_IN | USB_EP_DEV_OUT)) == + USB_EP_DEV_IN) + { + // + // This is an IN endpoint. + // + ulFlags |= (unsigned long)( + psFIFOConfig->sIn[ulEpIndex - 1].usEPFlags); + } + else + { + // + // This is an OUT endpoint. + // + ulFlags |= (unsigned long)( + psFIFOConfig->sOut[ulEpIndex - 1].usEPFlags); + } + + // + // Set the endpoint configuration. + // + USBDevEndpointConfigSet(USB0_BASE, + INDEX_TO_USB_EP(ulEpIndex), + ulMaxPkt, ulFlags); + } + } + + // + // At this point, we have reconfigured the desired interface so + // return indicating all is well. + // + return(true); + } + } + + return(false); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/device/usbddfu-rt.c b/usblib/device/usbddfu-rt.c new file mode 100644 index 0000000..8060eca --- /dev/null +++ b/usblib/device/usbddfu-rt.c @@ -0,0 +1,589 @@ +//***************************************************************************** +// +// usbddfu-rt.c - USB Device Firmware Update runtime device class driver. +// +// Copyright (c) 2010-2012 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "inc/hw_nvic.h" +#include "driverlib/debug.h" +#include "driverlib/usb.h" +#include "driverlib/sysctl.h" +#include "driverlib/systick.h" +#include "driverlib/interrupt.h" +#include "driverlib/rom_map.h" +#include "usblib/usblib.h" +#include "usblib/usbdfu.h" +#include "usblib/usb-ids.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbddfu-rt.h" +#include "usblib/usblibpriv.h" + +//***************************************************************************** +// +//! \addtogroup dfu_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// DFU Device Descriptor. This is a dummy structure since runtime DFU must be +// a part of a composite device and cannot be instantiated on its own. +// +//***************************************************************************** +const unsigned char g_pDFUDeviceDescriptor[] = +{ + 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) + USB_CLASS_VEND_SPECIFIC, // USB Device Class + 0, // USB Device Sub-class + 0, // USB Device protocol + 64, // Maximum packet size for default pipe. + USBShort(0), // Vendor ID (VID). + USBShort(0), // Product ID (PID). + USBShort(0), // Device Release Number BCD. + 0, // Manufacturer string identifier. + 0, // Product string indentifier. + 0, // Product serial number. + 1 // Number of configurations. +}; + +//***************************************************************************** +// +// DFU device runtime configuration descriptor. This is also a dummy structure +// since the primary device class configuration will be used when DFU is added +// to the composite device. +// +//***************************************************************************** +unsigned char g_pDFUConfigDescriptor[] = +{ + // + // Configuration descriptor header. + // + 9, // Size of the configuration descriptor. + USB_DTYPE_CONFIGURATION, // Type of this descriptor. + USBShort(27), // 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 DFU runtime interface descriptor. +// +//***************************************************************************** +unsigned char g_pDFUInterface[] = +{ + // + // Interface descriptor for runtime DFU operation. + // + 9, // Length of this descriptor. + USB_DTYPE_INTERFACE, // This is an interface descriptor. + 0, // Interface number . + 0, // Alternate setting number. + 0, // Number of endpoints (only endpoint 0 used) + USB_CLASS_APP_SPECIFIC, // Application specific interface class + USB_DFU_SUBCLASS, // Device Firmware Upgrade subclass + USB_DFU_RUNTIME_PROTOCOL, // DFU runtime protocol + 0, // No string descriptor for this interface. +}; + +//***************************************************************************** +// +// The DFU functional descriptor. +// +//***************************************************************************** +unsigned char g_pDFUFunctionalDesc[] = +{ + // + // Device Firmware Upgrade functional descriptor. + // + 9, // Length of this descriptor. + USB_DFU_FUNC_DESCRIPTOR_TYPE, // DFU Functional descriptor type + (DFU_ATTR_CAN_DOWNLOAD | // DFU attributes. + DFU_ATTR_CAN_UPLOAD | + DFU_ATTR_WILL_DETACH | + DFU_ATTR_MANIFEST_TOLERANT), + USBShort(0xFFFF), // Detach timeout (set to maximum). + USBShort(DFU_TRANSFER_SIZE), // Transfer size 1KB. + USBShort(0x0110) // DFU Version 1.1 +}; + +//***************************************************************************** +// +// The DFU runtime configuration descriptor is defined as two sections. +// These sections are: +// +// 1. The 9 byte configuration descriptor. +// 2. The interface descriptor + DFU functional descriptor. +// +//***************************************************************************** +const tConfigSection g_sDFUConfigSection = +{ + sizeof(g_pDFUConfigDescriptor), + g_pDFUConfigDescriptor +}; + +const tConfigSection g_sDFUInterfaceSection = +{ + sizeof(g_pDFUInterface), + g_pDFUInterface +}; + +const tConfigSection g_sDFUFunctionalDescSection = +{ + sizeof(g_pDFUFunctionalDesc), + g_pDFUFunctionalDesc +}; + +//***************************************************************************** +// +// This array lists all the sections that must be concatenated to make a +// single, complete DFU runtime configuration descriptor. +// +//***************************************************************************** +const tConfigSection *g_psDFUSections[] = +{ + &g_sDFUConfigSection, + &g_sDFUInterfaceSection, + &g_sDFUFunctionalDescSection +}; + +#define NUM_DFU_SECTIONS (sizeof(g_psDFUSections) / \ + sizeof(tConfigSection *)) + +//***************************************************************************** +// +// The header for the single configuration we support. This is the root of +// the data structure that defines all the bits and pieces that are pulled +// together to generate the configuration descriptor. +// +//***************************************************************************** +tConfigHeader g_sDFUConfigHeader = +{ + NUM_DFU_SECTIONS, + g_psDFUSections +}; + +//***************************************************************************** +// +// Configuration Descriptor. +// +//***************************************************************************** +const tConfigHeader * const g_pDFUConfigDescriptors[] = +{ + &g_sDFUConfigHeader +}; + +//***************************************************************************** +// +// Forward references for device handler callbacks +// +//***************************************************************************** +static void HandleGetDescriptor(void *pvInstance, tUSBRequest *pUSBRequest); +static void HandleRequest(void *pvInstance, tUSBRequest *pUSBRequest); +static void HandleDevice(void *pvInstance, unsigned long ulRequest, + void *pvRequestData); + +//***************************************************************************** +// +// The device information structure for the USB DFU devices. +// +//***************************************************************************** +tDeviceInfo g_sDFUDeviceInfo = +{ + // + // Device event handler callbacks. + // + { + HandleGetDescriptor, // GetDescriptor + HandleRequest, // RequestHandler + 0, // InterfaceChange + 0, // ConfigChange + 0, // DataReceived + 0, // DataSentCallback + 0, // ResetHandler + 0, // SuspendHandler + 0, // ResumeHandler + 0, // DisconnectHandler + 0, // EndpointHandler + HandleDevice, // Device handler. + }, + 0, // Device descriptor (unused - composite device) + g_pDFUConfigDescriptors, + 0, // Completed during USBDDFUCompositeInit(). + 0, // Completed during USBDDFUCompositeInit(). + &g_sUSBDefaultFIFOConfig, + 0 +}; + +//***************************************************************************** +// +// Device instance specific handler. This callback received notifications of +// events related to handling interface, endpoint and string identifiers when +// a device is part of a composite device. In this case, the only resource we +// need which may be renumbered is the DFU runtime interface. +// +//***************************************************************************** +static void +HandleDevice(void *pvInstance, unsigned long ulRequest, void *pvRequestData) +{ + tDFUInstance *psInst; + unsigned char *pucData; + + // + // Get a pointer to our instance data. + // + psInst = ((tUSBDDFUDevice *)pvInstance)->psPrivateDFUData; + + // + // Get a byte pointer to the data. + // + pucData = (unsigned char *)pvRequestData; + + // + // Which request event have we been passed? + // + switch(ulRequest) + { + // + // This was an interface change event. + // + case USB_EVENT_COMP_IFACE_CHANGE: + { + // + // Save the change to the interface number. + // + psInst->ucInterface = pucData[1]; + break; + } + + // + // We are not interested in any other event. + // + default: + { + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever a request for a +// non-standard descriptor is received. +// +// \param pvInstance is the instance data for this request. +// \param pUSBRequest points to the request received. +// +// This call parses the provided request structure and determines which +// descriptor is being requested. Assuming the descriptor can be found, it is +// scheduled for transmission via endpoint zero. If the descriptor cannot be +// found, the endpoint is stalled to indicate an error to the host. +// +//***************************************************************************** +static void +HandleGetDescriptor(void *pvInstance, tUSBRequest *pUSBRequest) +{ + unsigned long ulSize; + + ASSERT(pvInstance != 0); + + // + // Which type of class descriptor are we being asked for? We only support + // 1 type - the DFU functional descriptor. + // + if(((pUSBRequest->wValue >> 8) == USB_DFU_FUNC_DESCRIPTOR_TYPE) && + ((pUSBRequest->wValue & 0xFF) == 0)) + { + // + // If there is more data to send than the host requested then just + // send the requested amount of data. + // + if((unsigned short)g_pDFUFunctionalDesc[0] > pUSBRequest->wLength) + { + ulSize = (unsigned long)pUSBRequest->wLength; + } + else + { + ulSize = (unsigned long)g_pDFUFunctionalDesc[0]; + } + + // + // Send the data via endpoint 0. + // + USBDCDSendDataEP0(0, g_pDFUFunctionalDesc, ulSize); + } + else + { + // + // This was an unknown or invalid request so stall. + // + USBDCDStallEP0(0); + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever a non-standard +// request is received. +// +// \param pvInstance is the instance data for this HID device. +// \param pUSBRequest points to the request received. +// +// This call parses the provided request structure. Assuming the request is +// understood, it is handled and any required response generated. If the +// request cannot be handled by this device class, endpoint zero is stalled to +// indicate an error to the host. +// +//***************************************************************************** +static void +HandleRequest(void *pvInstance, tUSBRequest *pUSBRequest) +{ + tDFUInstance *psInst; + const tUSBDDFUDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Which device are we dealing with? + // + psDevice = pvInstance; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateDFUData; + + // + // Make sure the request was for this interface. + // + if(pUSBRequest->wIndex != psInst->ucInterface) + { + return; + } + + // + // Determine the type of request. + // + switch(pUSBRequest->bRequest) + { + // + // We have been asked to detach. In this case, we call back to the + // application telling it to tidy up and re-enter the boot loader. We + // rely upon it doing this on our behalf since this must be done from a + // non-interrupt context and this call is most likely in interrupt + // context. + // + case USBD_DFU_REQUEST_DETACH: + { + // + // Tell the application it's time to reenter the boot loader. + // + psDevice->pfnCallback(psDevice->pvCBData, USBD_DFU_EVENT_DETACH, + 0, (void *)0); + break; + } + + // + // This request was not recognized so stall. + // + default: + { + USBDCDStallEP0(0); + break; + } + } +} + +//***************************************************************************** +// +//! Initializes DFU device operation for a given USB controller. +//! +//! \param ulIndex is the index of the USB controller which is to be +//! initialized for DFU runtime device operation. +//! \param psDevice points to a structure containing parameters customizing +//! the operation of the DFU device. +//! +//! \return Returns NULL on failure or the \e psDevice pointer on success. +// +//***************************************************************************** +void * +USBDDFUCompositeInit(unsigned long ulIndex, const tUSBDDFUDevice *psDevice) +{ + tDFUInstance *psInst; + + // + // Check parameter validity. + // + ASSERT(ulIndex == 0); + ASSERT(psDevice); + ASSERT(psDevice->psPrivateDFUData); + + // + // Initialize the workspace in the passed instance structure. + // + psInst = psDevice->psPrivateDFUData; + psInst->psDevInfo = &g_sDFUDeviceInfo; + psInst->ulUSBBase = USB0_BASE; + psInst->bConnected = false; + psInst->ucInterface = 0; + + // + // Tag the device information with our device data structure pointer. + // + psInst->psDevInfo->pvInstance = (void *)psDevice; + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psDevice); +} + +//***************************************************************************** +// +//! Shuts down the DFU device. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDDFUCompositeInit(). +//! +//! This function terminates DFU operation for the instance supplied and +//! removes the device from the USB bus. +//! +//! Following this call, the \e pvInstance instance should not me used in any +//! other calls. +//! +//! \return None. +// +//***************************************************************************** +void +USBDDFUCompositeTerm(void *pvInstance) +{ + tDFUInstance *psInst; + + ASSERT(pvInstance); + + // + // Get a pointer to our instance data. + // + psInst = ((tUSBDDFUDevice *)pvInstance)->psPrivateDFUData; + + // + // Terminate the requested instance. + // + USBDCDTerm(0); + + psInst->ulUSBBase = 0; + psInst->psDevInfo = (tDeviceInfo *)0; +} + +//***************************************************************************** +// +//! Removes the current USB device from the bus and transfers control to the +//! DFU boot loader. +//! +//! This function should be called from the application's main loop (i.e. not +//! in interrupt context) following a callback to the USB DFU callback function +//! notifying the application of a DETACH request from the host. The function +//! will prepare the system to switch to DFU mode and transfer control to the +//! boot loader in preparation for a firmware upgrade from the host. +//! +//! The application must ensure that it has completed all necessary shutdown +//! activities (saved any required data, etc.) before making this call since +//! the function will not return. +//! +//! \return This function does not return. +// +//***************************************************************************** +void +USBDDFUUpdateBegin(void) +{ + // + // Terminate the USB device and take us off the bus. + // + USBDCDTerm(0); + + // + // Disable all interrupts. + // + MAP_IntMasterDisable(); + + // + // We must make sure we turn off SysTick and its interrupt + // before entering the boot loader! + // + MAP_SysTickIntDisable(); + MAP_SysTickDisable(); + + // + // Disable all processor interrupts. Instead of disabling them + // one at a time, a direct write to NVIC is done to disable all + // peripheral interrupts. + // + HWREG(NVIC_DIS0) = 0xffffffff; + HWREG(NVIC_DIS1) = 0xffffffff; + + // + // Reset the USB peripheral + // + MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_USB0); + MAP_SysCtlPeripheralReset(SYSCTL_PERIPH_USB0); + MAP_SysCtlPeripheralDisable(SYSCTL_PERIPH_USB0); + + // + // Wait for about a second. + // + MAP_SysCtlDelay(MAP_SysCtlClockGet() / 3); + + // + // Re-enable interrupts at the NVIC level. + // + MAP_IntMasterEnable(); + + // + // Return control to the boot loader. This is a call to the SVC + // handler in the boot loader. + // + (*((void (*)(void))(*(unsigned long *)0x2c)))(); + + // + // Should never get here, but just in case. + // + while(1) + { + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/device/usbddfu-rt.h b/usblib/device/usbddfu-rt.h new file mode 100644 index 0000000..561d1a9 --- /dev/null +++ b/usblib/device/usbddfu-rt.h @@ -0,0 +1,163 @@ +//***************************************************************************** +// +// usbddfu-rt.h - Definitions used by runtime DFU class devices. +// +// Copyright (c) 2010-2012 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#ifndef __USBDDFURT_H__ +#define __USBDDFURT_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup dfu_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! The size of the memory that should be allocated to create a configuration +//! descriptor for a single instance of the DFU runtime device. This does not +//! include the configuration descriptor which is automatically ignored by the +//! composite device class. +//! +//! This label is used to compute the value which will be passed to the +//! USBDCompositeInit function in the ulSize parameter. +// +// For reference this is sizeof(g_pDFUInterface) + sizeof(g_pDFUFunctionalDesc) +// +//***************************************************************************** +#define COMPOSITE_DDFU_SIZE (9 + 9) + +//***************************************************************************** +// +//! This value is passed to the client via the callback function provided in +//! the tUSBDDFUDevice structure and indicates that the host has sent a DETACH +//! request to the DFU interface. This request indicates that the device detach +//! from the USB bus and reattach in DFU mode in preparation for a firmware +//! upgrade. Currently, this is the only event that the DFU runtime class +//! reports to the client. +//! +//! When this event is received, the client should call USBDDFUUpdateBegin() +//! from a non-interrupt context at its earliest opportunity. +// +//***************************************************************************** +#define USBD_DFU_EVENT_DETACH (USBD_DFU_EVENT_BASE + 0) + +//***************************************************************************** +// +// PRIVATE +// +// This structure defines the private instance data and state variables for +// DFU devices. The memory for this structure is pointed to by the +// psPrivateDFUData field in the tUSBDDFUDevice structure passed in the +// USBDDFUCompositeInit() function. +// +//***************************************************************************** +typedef struct +{ + unsigned long ulUSBBase; + tDeviceInfo *psDevInfo; + unsigned char ucInterface; + tBoolean bConnected; +} +tDFUInstance; + +//***************************************************************************** +// +//! The structure used by the application to define operating parameters for +//! the DFU device. Note that, unlike all other devices, this structure does +//! not contain any fields which configure the device descriptor sent back to +//! the host. The DFU runtime device class must be used as part of a composite +//! device since all it provides is the capability to signal the device to +//! switch into DFU mode in preparation for a firmware upgrade. Creating a +//! device with nothing but DFU runtime mode capability is rather pointless +//! so this is not supported. +// +//***************************************************************************** +typedef struct +{ + // + //! A pointer to the callback function which will be called to notify + //! the application of DETACH requests. + // + tUSBCallback pfnCallback; + + // + //! A client-supplied pointer which will be sent as the first + //! parameter in all calls made to the pfnCallback function. + // + void *pvCBData; + + // + //! A pointer to private instance data for this device instance. This + //! memory must remain accessible for as long as the DFU device is in use + //! and must not be modified by any code outside the DFU class driver. + // + tDFUInstance *psPrivateDFUData; +} +tUSBDDFUDevice; + +//***************************************************************************** +// +// Device information structure required to construct the composite device. +// +//***************************************************************************** +extern tDeviceInfo g_sDFUDeviceInfo; + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void *USBDDFUCompositeInit(unsigned long ulIndex, + const tUSBDDFUDevice *psDevice); +extern void USBDDFUCompositeTerm(void *pvInstance); +extern void USBDDFUUpdateBegin(void); + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBDDFURT_H__ diff --git a/usblib/device/usbdenum.c b/usblib/device/usbdenum.c new file mode 100644 index 0000000..e8aa64e --- /dev/null +++ b/usblib/device/usbdenum.c @@ -0,0 +1,2900 @@ +//***************************************************************************** +// +// usbenum.c - Enumeration code to handle all endpoint zero traffic. +// +// Copyright (c) 2007-2012 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#include "inc/hw_ints.h" +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/interrupt.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/sysctl.h" +#include "driverlib/usb.h" +#include "driverlib/rtos_bindings.h" +#include "usblib/usblib.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbdevicepriv.h" +#include "usblib/usblibpriv.h" + +//***************************************************************************** +// +// External prototypes. +// +//***************************************************************************** +extern tUSBMode g_eUSBMode; + +//***************************************************************************** +// +// Local functions prototypes. +// +//***************************************************************************** +static void USBDGetStatus(void *pvInstance, tUSBRequest *pUSBRequest); +static void USBDClearFeature(void *pvInstance, tUSBRequest *pUSBRequest); +static void USBDSetFeature(void *pvInstance, tUSBRequest *pUSBRequest); +static void USBDSetAddress(void *pvInstance, tUSBRequest *pUSBRequest); +static void USBDGetDescriptor(void *pvInstance, tUSBRequest *pUSBRequest); +static void USBDSetDescriptor(void *pvInstance, tUSBRequest *pUSBRequest); +static void USBDGetConfiguration(void *pvInstance, + tUSBRequest *pUSBRequest); +static void USBDSetConfiguration(void *pvInstance, + tUSBRequest *pUSBRequest); +static void USBDGetInterface(void *pvInstance, tUSBRequest *pUSBRequest); +static void USBDSetInterface(void *pvInstance, tUSBRequest *pUSBRequest); +static void USBDSyncFrame(void *pvInstance, tUSBRequest *pUSBRequest); +static void USBDEP0StateTx(unsigned long ulIndex); +static void USBDEP0StateTxConfig(unsigned long ulIndex); +static long USBDStringIndexFromRequest(unsigned short usLang, + unsigned short usIndex); + +//***************************************************************************** +// +//! \addtogroup device_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! The default USB endpoint FIFO configuration structure. This structure +//! contains definitions to set all USB FIFOs into single buffered mode with +//! no DMA use. Each endpoint's FIFO is sized to hold the largest maximum +//! packet size for any interface alternate setting in the current +//! configuration descriptor. A pointer to this structure may be passed in the +//! psFIFOConfig field of the tDeviceInfo structure passed to USBCDCInit if the +//! application does not require any special handling of the USB controller +//! FIFO. +// +//***************************************************************************** +const tFIFOConfig g_sUSBDefaultFIFOConfig = +{ + { + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 } + }, + { + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 } + }, +}; + +//***************************************************************************** +// +// Indices into the ucHalt array to select the IN or OUT endpoint group. +// +//***************************************************************************** +#define HALT_EP_IN 0 +#define HALT_EP_OUT 1 + +//***************************************************************************** +// +// Define the max packet size for endpoint zero. +// +//***************************************************************************** +#define EP0_MAX_PACKET_SIZE 64 + +//***************************************************************************** +// +// This is a flag used with g_sUSBDeviceState.ulDevAddress to indicate that a +// device address change is pending. +// +//***************************************************************************** +#define DEV_ADDR_PENDING 0x80000000 + +//***************************************************************************** +// +// This label defines the default configuration number to use after a bus +// reset. This may be overridden by calling USBDCDSetDefaultConfiguration() +// during processing of the device reset handler if required. +// +//***************************************************************************** +#define DEFAULT_CONFIG_ID 1 + +//***************************************************************************** +// +// This label defines the number of milliseconds that the remote wake up signal +// must remain asserted before removing it. Section 7.1.7.7 of the USB 2.0 spec +// states that "the remote wake up device must hold the resume signaling for at +// least 1ms but for no more than 15ms" so 10mS seems a reasonable choice. +// +//***************************************************************************** +#define REMOTE_WAKEUP_PULSE_MS 10 + +//***************************************************************************** +// +// This label defines the number of milliseconds between the point where we +// assert the remote wake up signal and calling the client back to tell it that +// bus operation has been resumed. This value is based on the timings provided +// in section 7.1.7.7 of the USB 2.0 specification which indicates that the host +// (which takes over resume signaling when the device's initial signal is +// detected) must hold the resume signaling for at least 20mS. +// +//***************************************************************************** +#define REMOTE_WAKEUP_READY_MS 20 + +//***************************************************************************** +// +// The buffer for reading data coming into EP0 +// +//***************************************************************************** +static unsigned char g_pucDataBufferIn[EP0_MAX_PACKET_SIZE]; + +tDeviceInstance g_psUSBDevice[1]; + +//***************************************************************************** +// +// Function table to handle standard requests. +// +//***************************************************************************** +static const tStdRequest g_psUSBDStdRequests[] = +{ + USBDGetStatus, + USBDClearFeature, + 0, + USBDSetFeature, + 0, + USBDSetAddress, + USBDGetDescriptor, + USBDSetDescriptor, + USBDGetConfiguration, + USBDSetConfiguration, + USBDGetInterface, + USBDSetInterface, + USBDSyncFrame +}; + +//***************************************************************************** +// +// Functions accessible by USBLIB clients. +// +//***************************************************************************** + +//***************************************************************************** +// +//! Initialize the USB library device control driver for a given hardware +//! controller. +//! +//! \param ulIndex is the index of the USB controller which is to be +//! initialized. +//! \param psDevice is a pointer to a structure containing information that +//! the USB library requires to support operation of this application's +//! device. The structure contains event handler callbacks and pointers to the +//! various standard descriptors that the device wishes to publish to the +//! host. +//! +//! This function must be called by any application which wishes to operate +//! as a USB device. It initializes the USB device control driver for the +//! given controller and saves the device information for future use. Prior to +//! returning from this function, the device is connected to the USB bus. +//! Following return, the caller can expect to receive a callback to the +//! supplied pfnResetHandler function when a host connects to the +//! device. +//! +//! The device information structure passed in \e psDevice must remain +//! unchanged between this call and any matching call to USBDCDTerm() since +//! it is not copied by the USB library. +//! +//! The USBStackModeSet() function can be called with USB_MODE_FORCE_DEVICE in +//! order to cause the USB library to force the USB operating mode to a device +//! controller. This allows the application to used the USBVBUS and USBID pins +//! as GPIOs on devices that support forcing OTG to operate as a device only +//! controller. By default the USB library will assume that the USBVBUS and +//! USBID pins are configured as USB pins and not GPIOs. +//! +//! \note Forcing of the USB controller mode feature is not available on all +//! Stellaris microcontrollers. Consult the data sheet for the microcontroller +//! that the application is using to determine if this feature is available. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDInit(unsigned long ulIndex, tDeviceInfo *psDevice) +{ + const tConfigHeader *psHdr; + const tConfigDescriptor *psDesc; + + // + // Check the arguments. + // + ASSERT(ulIndex == 0); + ASSERT(psDevice != 0); + + // + // Should not call this if the stack is in host mode. + // + ASSERT(g_eUSBMode != USB_MODE_HOST); + ASSERT(g_eUSBMode != USB_MODE_FORCE_HOST); + + // + // Initialize a couple of fields in the device state structure. + // + g_psUSBDevice[0].ulConfiguration = DEFAULT_CONFIG_ID; + g_psUSBDevice[0].ulDefaultConfiguration = DEFAULT_CONFIG_ID; + + // + // Remember the device information pointer. + // + g_psUSBDevice[0].psInfo = psDevice; + g_psUSBDevice[0].pvInstance = psDevice->pvInstance; + g_psUSBDevice[0].eEP0State = USB_STATE_IDLE; + + // + // Default to device mode if no mode was set. + // + if(g_eUSBMode == USB_MODE_NONE) + { + g_eUSBMode = USB_MODE_DEVICE; + } + + // + // Only do hardware update if the stack is in not in OTG mode. + // + if(g_eUSBMode != USB_MODE_OTG) + { + // + // Reset the USB controller. + // + MAP_SysCtlPeripheralReset(SYSCTL_PERIPH_USB0); + + // + // Enable Clocking to the USB controller. + // + MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_USB0); + + // + // Turn on USB Phy clock. + // + MAP_SysCtlUSBPLLEnable(); + + // + // If the application is requesting OTG or DEVICE mode then set the mode + // detection to OTG. If the mode was device then the rest of the library + // should make sure that no OTG signaling actually occurs. + // + if((g_eUSBMode == USB_MODE_DEVICE) || (g_eUSBMode == USB_MODE_OTG)) + { + // + // Switch to OTG mode to detect VBUS changes. + // + MAP_USBOTGMode(USB0_BASE); + } + else + { + // + // Force device mode on devices that support forcing mode. + // + MAP_USBDevMode(USB0_BASE); + } + + // + // In all other cases, set the mode to device this function should not + // be called in OTG mode. + // + g_eUSBMode = USB_MODE_DEVICE; + } + + // + // Initialize the USB tick module. + // + InternalUSBTickInit(); + + // + // Get a pointer to the default configuration descriptor. + // + psHdr = psDevice->ppConfigDescriptors[ + g_psUSBDevice[0].ulDefaultConfiguration - 1]; + psDesc = (const tConfigDescriptor *)(psHdr->psSections[0]->pucData); + + // + // Default to the state where remote wake up is disabled. + // + g_psUSBDevice[0].ucStatus = 0; + g_psUSBDevice[0].bRemoteWakeup = false; + + // + // Determine the self- or bus-powered state based on the flags the + // user provided. + // + g_psUSBDevice[0].bPwrSrcSet = false; + + if((psDesc->bmAttributes & USB_CONF_ATTR_PWR_M) == USB_CONF_ATTR_SELF_PWR) + { + g_psUSBDevice[0].ucStatus |= USB_STATUS_SELF_PWR; + } + else + { + g_psUSBDevice[0].ucStatus &= ~USB_STATUS_SELF_PWR; + } + + // + // Only do hardware update if the stack is not in OTG mode. + // + if(g_eUSBMode != USB_MODE_OTG) + { + // + // Get the current interrupt status.to clear all pending USB interrupts. + // + MAP_USBIntStatusControl(USB0_BASE); + MAP_USBIntStatusEndpoint(USB0_BASE); + + // + // Enable USB Interrupts. + // + MAP_USBIntEnableControl(USB0_BASE, USB_INTCTRL_RESET | + USB_INTCTRL_DISCONNECT | + USB_INTCTRL_RESUME | + USB_INTCTRL_SUSPEND | + USB_INTCTRL_SOF); + MAP_USBIntEnableEndpoint(USB0_BASE, USB_INTEP_ALL); + + // + // Make sure we disconnect from the host for a while. This ensures + // that the host will enumerate us even if we were previously + // connected to the bus. + // + MAP_USBDevDisconnect(USB0_BASE); + + // + // Wait about 100mS. + // + OS_DELAY(MAP_SysCtlClockGet() / 30); + + // + // Attach the device using the soft connect. + // + MAP_USBDevConnect(USB0_BASE); + + // + // Enable the USB interrupt. + // + OS_INT_ENABLE(INT_USB0); + } +} + +//***************************************************************************** +// +//! Free the USB library device control driver for a given hardware controller. +//! +//! \param ulIndex is the index of the USB controller which is to be +//! freed. +//! +//! This function should be called by an application if it no longer requires +//! the use of a given USB controller to support its operation as a USB device. +//! It frees the controller for use by another client. +//! +//! It is the caller's responsibility to remove its device from the USB bus +//! prior to calling this function. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDTerm(unsigned long ulIndex) +{ + // + // Check the arguments. + // + ASSERT(ulIndex == 0); + + // + // Disable the USB interrupts. + // + OS_INT_DISABLE(INT_USB0); + + // + // Reset the tick handlers so that they can be reconfigured when and if + // USBDCDInit() is called. + // + InternalUSBTickReset(); + + g_psUSBDevice[0].psInfo = (tDeviceInfo *)0; + g_psUSBDevice[0].pvInstance = 0; + + MAP_USBIntDisableControl(USB0_BASE, USB_INTCTRL_ALL); + MAP_USBIntDisableEndpoint(USB0_BASE, USB_INTEP_ALL); + + // + // Detach the device using the soft connect. + // + MAP_USBDevDisconnect(USB0_BASE); + + // + // Clear any pending interrupts. + // + MAP_USBIntStatusControl(USB0_BASE); + MAP_USBIntStatusEndpoint(USB0_BASE); + + // + // Turn off USB Phy clock. + // + MAP_SysCtlUSBPLLDisable(); + + // + // Disable the USB peripheral + // + MAP_SysCtlPeripheralDisable(SYSCTL_PERIPH_USB0); +} + +//***************************************************************************** +// +//! This function starts the request for data from the host on endpoint zero. +//! +//! \param ulIndex is the index of the USB controller from which the data +//! is being requested. +//! \param pucData is a pointer to the buffer to fill with data from the USB +//! host. +//! \param ulSize is the size of the buffer or data to return from the USB +//! host. +//! +//! This function handles retrieving data from the host when a custom command +//! has been issued on endpoint zero. If the application needs notification +//! when the data has been received, +//! tDeviceInfo.sCallbacks.pfnDataReceived should contain valid +//! function pointer. In nearly all cases this is necessary because the caller +//! of this function would likely need to know that the data requested was +//! received. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDRequestDataEP0(unsigned long ulIndex, unsigned char *pucData, + unsigned long ulSize) +{ + ASSERT(ulIndex == 0); + + // + // Enter the RX state on end point 0. + // + g_psUSBDevice[0].eEP0State = USB_STATE_RX; + + // + // Save the pointer to the data. + // + g_psUSBDevice[0].pEP0Data = pucData; + + // + // Location to save the current number of bytes received. + // + g_psUSBDevice[0].ulOUTDataSize = ulSize; + + // + // Bytes remaining to be received. + // + g_psUSBDevice[0].ulEP0DataRemain = ulSize; +} + +//***************************************************************************** +// +//! This function requests transfer of data to the host on endpoint zero. +//! +//! \param ulIndex is the index of the USB controller which is to be used to +//! send the data. +//! \param pucData is a pointer to the buffer to send via endpoint zero. +//! \param ulSize is the amount of data to send in bytes. +//! +//! This function handles sending data to the host when a custom command is +//! issued or non-standard descriptor has been requested on endpoint zero. If +//! the application needs notification when this is complete, +//! tDeviceInfo.sCallbacks.pfnDataSent should contain a valid function +//! pointer. This callback could be used to free up the buffer passed into +//! this function in the \e pucData parameter. The contents of the \e pucData +//! buffer must remain unchanged until the pfnDataSent callback is +//! received. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDSendDataEP0(unsigned long ulIndex, unsigned char *pucData, + unsigned long ulSize) +{ + ASSERT(ulIndex == 0); + + // + // Return the externally provided device descriptor. + // + g_psUSBDevice[0].pEP0Data = pucData; + + // + // The size of the device descriptor is in the first byte. + // + g_psUSBDevice[0].ulEP0DataRemain = ulSize; + + // + // Save the total size of the data sent. + // + g_psUSBDevice[0].ulOUTDataSize = ulSize; + + // + // Now in the transmit data state. + // + USBDEP0StateTx(0); +} + +//***************************************************************************** +// +//! This function sets the default configuration for the device. +//! +//! \param ulIndex is the index of the USB controller whose default +//! configuration is to be set. +//! \param ulDefaultConfig is the configuration identifier (byte 6 of the +//! standard configuration descriptor) which is to be presented to the host +//! as the default configuration in cases where the configuration descriptor is +//! queried prior to any specific configuration being set. +//! +//! This function allows a device to override the default configuration +//! descriptor that will be returned to a host whenever it is queried prior +//! to a specific configuration having been set. The parameter passed must +//! equal one of the configuration identifiers found in the +//! ppConfigDescriptors array for the device. +//! +//! If this function is not called, the USB library will return the first +//! configuration in the ppConfigDescriptors array as the default +//! configuration. +//! +//! \note The USB device stack assumes that the configuration IDs (byte 6 of +//! the configuration descriptor, bConfigurationValue) stored within +//! the configuration descriptor array, ppConfigDescriptors, +//! are equal to the array index + 1. In other words, the first entry in the +//! array must contain a descriptor with bConfigurationValue 1, the +//! second must have bConfigurationValue 2 and so on. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDSetDefaultConfiguration(unsigned long ulIndex, + unsigned long ulDefaultConfig) +{ + ASSERT(ulIndex == 0); + + g_psUSBDevice[0].ulDefaultConfiguration = ulDefaultConfig; +} + +//***************************************************************************** +// +//! This function generates a stall condition on endpoint zero. +//! +//! \param ulIndex is the index of the USB controller whose endpoint zero is to +//! be stalled. +//! +//! This function is typically called to signal an error condition to the host +//! when an unsupported request is received by the device. It should be +//! called from within the callback itself (in interrupt context) and not +//! deferred until later since it affects the operation of the endpoint zero +//! state machine in the USB library. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDStallEP0(unsigned long ulIndex) +{ + ASSERT(ulIndex == 0); + + // + // Stall the endpoint in question. + // + MAP_USBDevEndpointStall(USB0_BASE, USB_EP_0, USB_EP_DEV_OUT); + + // + // Enter the stalled state. + // + g_psUSBDevice[0].eEP0State = USB_STATE_STALL; +} + +//***************************************************************************** +// +//! Reports the device power status (bus- or self-powered) to the library. +//! +//! \param ulIndex is the index of the USB controller whose device power +//! status is being reported. +//! \param ucPower indicates the current power status, either \b +//! USB_STATUS_SELF_PWR or \b USB_STATUS_BUS_PWR. +//! +//! Applications which support switching between bus- or self-powered +//! operation should call this function whenever the power source changes +//! to indicate the current power status to the USB library. This information +//! is required by the library to allow correct responses to be provided when +//! the host requests status from the device. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDPowerStatusSet(unsigned long ulIndex, unsigned char ucPower) +{ + // + // Check for valid parameters. + // + ASSERT((ucPower == USB_STATUS_BUS_PWR) || + (ucPower == USB_STATUS_SELF_PWR)); + ASSERT(ulIndex == 0); + + // + // Update the device status with the new power status flag. + // + g_psUSBDevice[0].bPwrSrcSet = true; + g_psUSBDevice[0].ucStatus &= ~USB_STATUS_PWR_M; + g_psUSBDevice[0].ucStatus |= ucPower; +} + +//***************************************************************************** +// +//! Requests a remote wake up to resume communication when in suspended state. +//! +//! \param ulIndex is the index of the USB controller that will request +//! a bus wake up. +//! +//! When the bus is suspended, an application which supports remote wake up +//! (advertised to the host via the configuration descriptor) may call this +//! function to initiate remote wake up signaling to the host. If the remote +//! wake up feature has not been disabled by the host, this will cause the bus +//! to resume operation within 20mS. If the host has disabled remote wake up, +//! \b false will be returned to indicate that the wake up request was not +//! successful. +//! +//! \return Returns \b true if the remote wake up is not disabled and the +//! signaling was started or \b false if remote wake up is disabled or if +//! signaling is currently ongoing following a previous call to this function. +// +//***************************************************************************** +tBoolean +USBDCDRemoteWakeupRequest(unsigned long ulIndex) +{ + // + // Check for parameter validity. + // + ASSERT(ulIndex == 0); + + // + // Is remote wake up signaling currently enabled? + // + if(g_psUSBDevice[0].ucStatus & USB_STATUS_REMOTE_WAKE) + { + // + // The host has not disabled remote wake up. Are we still in the + // middle of a previous wake up sequence? + // + if(!g_psUSBDevice[0].bRemoteWakeup) + { + // + // No - we are not in the middle of a wake up sequence so start + // one here. + // + g_psUSBDevice[0].ucRemoteWakeupCount = 0; + g_psUSBDevice[0].bRemoteWakeup = true; + MAP_USBHostResume(USB0_BASE, true); + return(true); + } + } + + // + // If we drop through to here, signaling was not initiated so return + // false. + return(false); +} + +//***************************************************************************** +// +// Internal Functions, not to be called by applications +// +//***************************************************************************** + +//***************************************************************************** +// +// This internal function is called on the SOF interrupt to process any +// outstanding remote wake up requests. +// +// \return None. +// +//***************************************************************************** +void +USBDeviceResumeTickHandler(tDeviceInstance *psDevInst) +{ + if(g_psUSBDevice[0].bRemoteWakeup) + { + // + // Increment the millisecond counter we use to time the resume + // signaling. + // + g_psUSBDevice[0].ucRemoteWakeupCount++; + + // + // Have we reached the 10mS mark? If so, we need to turn the signaling + // off again. + // + if(g_psUSBDevice[0].ucRemoteWakeupCount == REMOTE_WAKEUP_PULSE_MS) + { + MAP_USBHostResume(USB0_BASE, false); + } + + // + // Have we reached the point at which we can tell the client that the + // bus has resumed? The controller doesn't give us an interrupt if we + // initiated the wake up signaling so we just wait until 20mS have + // passed then tell the client all is well. + // + if(g_psUSBDevice[0].ucRemoteWakeupCount == REMOTE_WAKEUP_READY_MS) + { + // + // We are now finished with the remote wake up signaling. + // + g_psUSBDevice[0].bRemoteWakeup = false; + + // + // If the client has registered a resume callback, call it. In the + // case of a remote wake up request, we do not get a resume + // interrupt from the controller so we need to fake it here. + // + if(g_psUSBDevice[0].psInfo->sCallbacks.pfnResumeHandler) + { + g_psUSBDevice[0].psInfo->sCallbacks.pfnResumeHandler( + g_psUSBDevice[0].pvInstance); + } + } + } +} + +//***************************************************************************** +// +// This internal function reads a request data packet and dispatches it to +// either a standard request handler or the registered device request +// callback depending upon the request type. +// +// \return None. +// +//***************************************************************************** +static void +USBDReadAndDispatchRequest(unsigned long ulIndex) +{ + unsigned long ulSize; + tUSBRequest *pRequest; + + // + // Cast the buffer to a request structure. + // + pRequest = (tUSBRequest *)g_pucDataBufferIn; + + // + // Set the buffer size. + // + ulSize = EP0_MAX_PACKET_SIZE; + + // + // Get the data from the USB controller end point 0. + // + MAP_USBEndpointDataGet(USB0_BASE, + USB_EP_0, + g_pucDataBufferIn, + &ulSize); + + // + // If there was a null setup packet then just return. + // + if(!ulSize) + { + return; + } + + // + // See if this is a standard request or not. + // + if((pRequest->bmRequestType & USB_RTYPE_TYPE_M) != USB_RTYPE_STANDARD) + { + // + // Since this is not a standard request, see if there is + // an external handler present. + // + if(g_psUSBDevice[0].psInfo->sCallbacks.pfnRequestHandler) + { + g_psUSBDevice[0].psInfo->sCallbacks.pfnRequestHandler( + g_psUSBDevice[0].pvInstance, pRequest); + } + else + { + // + // If there is no handler then stall this request. + // + USBDCDStallEP0(0); + } + } + else + { + // + // Assure that the jump table is not out of bounds. + // + if((pRequest->bRequest < + (sizeof(g_psUSBDStdRequests) / sizeof(tStdRequest))) && + (g_psUSBDStdRequests[pRequest->bRequest] != 0)) + { + // + // Jump table to the appropriate handler. + // + g_psUSBDStdRequests[pRequest->bRequest](&g_psUSBDevice[0], + pRequest); + } + else + { + // + // If there is no handler then stall this request. + // + USBDCDStallEP0(0); + } + } +} + +//***************************************************************************** +// +// This is interrupt handler for endpoint zero. +// +// This function handles all interrupts on endpoint zero in order to maintain +// the state needed for the control endpoint on endpoint zero. In order to +// successfully enumerate and handle all USB standard requests, all requests +// on endpoint zero must pass through this function. The endpoint has the +// following states: \b USB_STATE_IDLE, \b USB_STATE_TX, \b USB_STATE_RX, +// \b USB_STATE_STALL, and \b USB_STATE_STATUS. In the \b USB_STATE_IDLE +// state the USB controller has not received the start of a request, and once +// it does receive the data for the request it will either enter the +// \b USB_STATE_TX, \b USB_STATE_RX, or \b USB_STATE_STALL depending on the +// command. If the controller enters the \b USB_STATE_TX or \b USB_STATE_RX +// then once all data has been sent or received, it must pass through the +// \b USB_STATE_STATUS state to allow the host to acknowledge completion of +// the request. The \b USB_STATE_STALL is entered from \b USB_STATE_IDLE in +// the event that the USB request was not valid. Both the \b USB_STATE_STALL +// and \b USB_STATE_STATUS are transitional states that return to the +// \b USB_STATE_IDLE state. +// +// \return None. +// +// USB_STATE_IDLE -*--> USB_STATE_TX -*-> USB_STATE_STATUS -*->USB_STATE_IDLE +// | | | +// |--> USB_STATE_RX - | +// | | +// |--> USB_STATE_STALL ---------->--------- +// +// ---------------------------------------------------------------- +// | Current State | State 0 | State 1 | +// | --------------------|-------------------|---------------------- +// | USB_STATE_IDLE | USB_STATE_TX/RX | USB_STATE_STALL | +// | USB_STATE_TX | USB_STATE_STATUS | | +// | USB_STATE_RX | USB_STATE_STATUS | | +// | USB_STATE_STATUS | USB_STATE_IDLE | | +// | USB_STATE_STALL | USB_STATE_IDLE | | +// ---------------------------------------------------------------- +// +//***************************************************************************** +void +USBDeviceEnumHandler(tDeviceInstance *pDevInstance) +{ + unsigned long ulEPStatus; + + // + // Get the end point 0 status. + // + ulEPStatus = MAP_USBEndpointStatus(USB0_BASE, USB_EP_0); + + switch(pDevInstance->eEP0State) + { + // + // Handle the status state, this is a transitory state from + // USB_STATE_TX or USB_STATE_RX back to USB_STATE_IDLE. + // + case USB_STATE_STATUS: + { + // + // Just go back to the idle state. + // + pDevInstance->eEP0State = USB_STATE_IDLE; + + // + // If there is a pending address change then set the address. + // + if(pDevInstance->ulDevAddress & DEV_ADDR_PENDING) + { + // + // Clear the pending address change and set the address. + // + pDevInstance->ulDevAddress &= ~DEV_ADDR_PENDING; + MAP_USBDevAddrSet(USB0_BASE, pDevInstance->ulDevAddress); + } + + // + // If a new packet is already pending, we need to read it + // and handle whatever request it contains. + // + if(ulEPStatus & USB_DEV_EP0_OUT_PKTRDY) + { + // + // Process the newly arrived packet. + // + USBDReadAndDispatchRequest(0); + } + break; + } + + // + // In the IDLE state the code is waiting to receive data from the host. + // + case USB_STATE_IDLE: + { + // + // Is there a packet waiting for us? + // + if(ulEPStatus & USB_DEV_EP0_OUT_PKTRDY) + { + // + // Yes - process it. + // + USBDReadAndDispatchRequest(0); + } + break; + } + + // + // Data is still being sent to the host so handle this in the + // EP0StateTx() function. + // + case USB_STATE_TX: + { + USBDEP0StateTx(0); + break; + } + + // + // We are still in the middle of sending the configuration descriptor + // so handle this in the EP0StateTxConfig() function. + // + case USB_STATE_TX_CONFIG: + { + USBDEP0StateTxConfig(0); + break; + } + + // + // Handle the receive state for commands that are receiving data on + // endpoint zero. + // + case USB_STATE_RX: + { + unsigned long ulDataSize; + + // + // Set the number of bytes to get out of this next packet. + // + if(pDevInstance->ulEP0DataRemain > EP0_MAX_PACKET_SIZE) + { + // + // Don't send more than EP0_MAX_PACKET_SIZE bytes. + // + ulDataSize = EP0_MAX_PACKET_SIZE; + } + else + { + // + // There was space so send the remaining bytes. + // + ulDataSize = pDevInstance->ulEP0DataRemain; + } + + // + // Get the data from the USB controller end point 0. + // + MAP_USBEndpointDataGet(USB0_BASE, USB_EP_0, pDevInstance->pEP0Data, + &ulDataSize); + + // + // If there we not more that EP0_MAX_PACKET_SIZE or more bytes + // remaining then this transfer is complete. If there were exactly + // EP0_MAX_PACKET_SIZE remaining then there still needs to be + // null packet sent before this is complete. + // + if(pDevInstance->ulEP0DataRemain < EP0_MAX_PACKET_SIZE) + { + // + // Need to ACK the data on end point 0 in this case and set the + // data end as this is the last of the data. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, true); + + // + // Return to the idle state. + // + pDevInstance->eEP0State = USB_STATE_STATUS; + + // + // If there is a receive callback then call it. + // + if((pDevInstance->psInfo->sCallbacks.pfnDataReceived) && + (pDevInstance->ulOUTDataSize != 0)) + { + // + // Call the custom receive handler to handle the data + // that was received. + // + pDevInstance->psInfo->sCallbacks.pfnDataReceived( + pDevInstance->pvInstance, + pDevInstance->ulOUTDataSize); + + // + // Indicate that there is no longer any data being waited + // on. + // + pDevInstance->ulOUTDataSize = 0; + } + } + else + { + // + // Need to ACK the data on end point 0 in this case + // without setting data end because more data is coming. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false); + } + + // + // Advance the pointer. + // + pDevInstance->pEP0Data += ulDataSize; + + // + // Decrement the number of bytes that are being waited on. + // + pDevInstance->ulEP0DataRemain -= ulDataSize; + + break; + } + // + // The device stalled endpoint zero so check if the stall needs to be + // cleared once it has been successfully sent. + // + case USB_STATE_STALL: + { + // + // If we sent a stall then acknowledge this interrupt. + // + if(ulEPStatus & USB_DEV_EP0_SENT_STALL) + { + // + // Clear the Setup End condition. + // + MAP_USBDevEndpointStatusClear(USB0_BASE, USB_EP_0, + USB_DEV_EP0_SENT_STALL); + + // + // Reset the global end point 0 state to IDLE. + // + pDevInstance->eEP0State = USB_STATE_IDLE; + + } + break; + } + // + // Halt on an unknown state, but only in DEBUG mode builds. + // + default: + { + ASSERT(0); + } + } +} + +//***************************************************************************** +// +// This function handles bus reset notifications. +// +// This function is called from the low level USB interrupt handler whenever +// a bus reset is detected. It performs tidy-up as required and resets the +// configuration back to defaults in preparation for descriptor queries from +// the host. +// +// \return None. +// +//***************************************************************************** +void +USBDeviceEnumResetHandler(tDeviceInstance *pDevInstance) +{ + unsigned long ulLoop; + + // + // Disable remote wake up signaling (as per USB 2.0 spec 9.1.1.6). + // + pDevInstance->ucStatus &= ~USB_STATUS_REMOTE_WAKE; + pDevInstance->bRemoteWakeup = false; + + // + // Call the device dependent code to indicate a bus reset has occurred. + // + if(pDevInstance->psInfo->sCallbacks.pfnResetHandler) + { + pDevInstance->psInfo->sCallbacks.pfnResetHandler( + pDevInstance->pvInstance); + } + + // + // Reset the default configuration identifier and alternate function + // selections. + // + pDevInstance->ulConfiguration = pDevInstance->ulDefaultConfiguration; + + for(ulLoop = 0; ulLoop < USB_MAX_INTERFACES_PER_DEVICE; ulLoop++) + { + pDevInstance->pucAltSetting[ulLoop] = (unsigned char)0; + } +} + +//***************************************************************************** +// +// This function handles the GET_STATUS standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param pUSBRequest holds the request type and endpoint number if endpoint +// status is requested. +// +// This function handles responses to a Get Status request from the host +// controller. A status request can be for the device, an interface or an +// endpoint. If any other type of request is made this function will cause +// a stall condition to indicate that the command is not supported. The +// \e pUSBRequest structure holds the type of the request in the +// bmRequestType field. If the type indicates that this is a request for an +// endpoint's status, then the wIndex field holds the endpoint number. +// +// \return None. +// +//***************************************************************************** +static void +USBDGetStatus(void *pvInstance, tUSBRequest *pUSBRequest) +{ + unsigned short usData; + tDeviceInstance *psUSBControl; + + ASSERT(pUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDeviceInstance *)pvInstance; + + // + // Need to ACK the data on end point 0 without setting last data as there + // will be a data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false); + + // + // Determine what type of status was requested. + // + switch(pUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M) + { + // + // This was a Device Status request. + // + case USB_RTYPE_DEVICE: + { + // + // Return the current status for the device. + // + usData = (unsigned short)psUSBControl->ucStatus; + + break; + } + + // + // This was a Interface status request. + // + case USB_RTYPE_INTERFACE: + { + // + // Interface status always returns 0. + // + usData = (unsigned short)0; + + break; + } + + // + // This was an endpoint status request. + // + case USB_RTYPE_ENDPOINT: + { + unsigned short usIndex; + unsigned long ulDir; + + // + // Which endpoint are we dealing with? + // + usIndex = pUSBRequest->wIndex & USB_REQ_EP_NUM_M; + + // + // Check if this was a valid endpoint request. + // + if((usIndex == 0) || (usIndex >= NUM_USB_EP)) + { + USBDCDStallEP0(0); + return; + } + else + { + // + // Are we dealing with an IN or OUT endpoint? + // + ulDir = ((pUSBRequest->wIndex & USB_REQ_EP_DIR_M) == + USB_REQ_EP_DIR_IN) ? HALT_EP_IN : HALT_EP_OUT; + + // + // Get the current halt status for this endpoint. + // + usData = + (unsigned short)psUSBControl->ucHalt[ulDir][usIndex - 1]; + } + break; + } + + // + // This was an unknown request. + // + default: + { + // + // Anything else causes a stall condition to indicate that the + // command was not supported. + // + USBDCDStallEP0(0); + return; + } + } + + // + // Send the two byte status response. + // + psUSBControl->ulEP0DataRemain = 2; + psUSBControl->pEP0Data = (unsigned char *)&usData; + + // + // Send the response. + // + USBDEP0StateTx(0); +} + +//***************************************************************************** +// +// This function handles the CLEAR_FEATURE standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param pUSBRequest holds the options for the Clear Feature USB request. +// +// This function handles device or endpoint clear feature requests. The +// \e pUSBRequest structure holds the type of the request in the bmRequestType +// field and the feature is held in the wValue field. The device can only +// clear the Remote Wake feature. This device request should only be made if +// the descriptor indicates that Remote Wake is implemented by the device. +// Endpoints can only clear a halt on a given endpoint. If any other +// requests are made, then the device will stall the request to indicate to +// the host that the command was not supported. +// +// \return None. +// +//***************************************************************************** +static void +USBDClearFeature(void *pvInstance, tUSBRequest *pUSBRequest) +{ + tDeviceInstance *psUSBControl; + + ASSERT(pUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDeviceInstance *)pvInstance; + + // + // Need to ACK the data on end point 0 with last data set as this has no + // data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, true); + + // + // Determine what type of status was requested. + // + switch(pUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M) + { + // + // This is a clear feature request at the device level. + // + case USB_RTYPE_DEVICE: + { + // + // Only remote wake is can be cleared by this function. + // + if(USB_FEATURE_REMOTE_WAKE & pUSBRequest->wValue) + { + // + // Clear the remote wake up state. + // + psUSBControl->ucStatus &= ~USB_STATUS_REMOTE_WAKE; + } + else + { + USBDCDStallEP0(0); + } + break; + } + + // + // This is a clear feature request at the endpoint level. + // + case USB_RTYPE_ENDPOINT: + { + unsigned long ulDir; + unsigned short usIndex; + + // + // Which endpoint are we dealing with? + // + usIndex = pUSBRequest->wIndex & USB_REQ_EP_NUM_M; + + // + // Not a valid endpoint. + // + if((usIndex == 0) || (usIndex > NUM_USB_EP)) + { + USBDCDStallEP0(0); + } + else + { + // + // Only the halt feature is supported. + // + if(USB_FEATURE_EP_HALT == pUSBRequest->wValue) + { + // + // Are we dealing with an IN or OUT endpoint? + // + ulDir = ((pUSBRequest->wIndex & USB_REQ_EP_DIR_M) == + USB_REQ_EP_DIR_IN) ? HALT_EP_IN : HALT_EP_OUT; + + // + // Clear the halt condition on this endpoint. + // + psUSBControl->ucHalt[ulDir][usIndex - 1] = 0; + + if(ulDir == HALT_EP_IN) + { + MAP_USBDevEndpointStallClear(USB0_BASE, + INDEX_TO_USB_EP(usIndex), + USB_EP_DEV_IN); + } + else + { + MAP_USBDevEndpointStallClear(USB0_BASE, + INDEX_TO_USB_EP(usIndex), + USB_EP_DEV_OUT); + } + } + else + { + // + // If any other feature is requested, this is an error. + // + USBDCDStallEP0(0); + return; + } + } + break; + } + + // + // This is an unknown request. + // + default: + { + USBDCDStallEP0(0); + return; + } + } +} + +//***************************************************************************** +// +// This function handles the SET_FEATURE standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param pUSBRequest holds the feature in the wValue field of the USB +// request. +// +// This function handles device or endpoint set feature requests. The +// \e pUSBRequest structure holds the type of the request in the bmRequestType +// field and the feature is held in the wValue field. The device can only +// set the Remote Wake feature. This device request should only be made if the +// descriptor indicates that Remote Wake is implemented by the device. +// Endpoint requests can only issue a halt on a given endpoint. If any other +// requests are made, then the device will stall the request to indicate to the +// host that the command was not supported. +// +// \return None. +// +//***************************************************************************** +static void +USBDSetFeature(void *pvInstance, tUSBRequest *pUSBRequest) +{ + tDeviceInstance *psUSBControl; + + ASSERT(pUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDeviceInstance *)pvInstance; + + // + // Need to ACK the data on end point 0 with last data set as this has no + // data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, true); + + // + // Determine what type of status was requested. + // + switch(pUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M) + { + // + // This is a set feature request at the device level. + // + case USB_RTYPE_DEVICE: + { + // + // Only remote wake is the only feature that can be set by this + // function. + // + if(USB_FEATURE_REMOTE_WAKE & pUSBRequest->wValue) + { + // + // Set the remote wake up state. + // + psUSBControl->ucStatus |= USB_STATUS_REMOTE_WAKE; + } + else + { + USBDCDStallEP0(0); + } + break; + } + + // + // This is a set feature request at the endpoint level. + // + case USB_RTYPE_ENDPOINT: + { + unsigned short usIndex; + unsigned long ulDir; + + // + // Which endpoint are we dealing with? + // + usIndex = pUSBRequest->wIndex & USB_REQ_EP_NUM_M; + + // + // Not a valid endpoint? + // + if((usIndex == 0) || (usIndex >= NUM_USB_EP)) + { + USBDCDStallEP0(0); + } + else + { + // + // Only the Halt feature can be set. + // + if(USB_FEATURE_EP_HALT == pUSBRequest->wValue) + { + // + // Are we dealing with an IN or OUT endpoint? + // + ulDir = ((pUSBRequest->wIndex & USB_REQ_EP_DIR_M) == + USB_REQ_EP_DIR_IN) ? HALT_EP_IN : HALT_EP_OUT; + + // + // Clear the halt condition on this endpoint. + // + psUSBControl->ucHalt[ulDir][usIndex - 1] = 1; + } + else + { + // + // No other requests are supported. + // + USBDCDStallEP0(0); + return; + } + } + break; + } + + // + // This is an unknown request. + // + default: + { + USBDCDStallEP0(0); + return; + } + } +} + +//***************************************************************************** +// +// This function handles the SET_ADDRESS standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param pUSBRequest holds the new address to use in the wValue field of the +// USB request. +// +// This function is called to handle the change of address request from the +// host controller. This can only start the sequence as the host must +// acknowledge that the device has changed address. Thus this function sets +// the address change as pending until the status phase of the request has +// been completed successfully. This prevents the devices address from +// changing and not properly responding to the status phase. +// +// \return None. +// +//***************************************************************************** +static void +USBDSetAddress(void *pvInstance, tUSBRequest *pUSBRequest) +{ + tDeviceInstance *psUSBControl; + + ASSERT(pUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDeviceInstance *)pvInstance; + + // + // Need to ACK the data on end point 0 with last data set as this has no + // data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, true); + + // + // Save the device address as we cannot change address until the status + // phase is complete. + // + psUSBControl->ulDevAddress = pUSBRequest->wValue | DEV_ADDR_PENDING; + + // + // Transition directly to the status state since there is no data phase + // for this request. + // + psUSBControl->eEP0State = USB_STATE_STATUS; +} + +//***************************************************************************** +// +// This function handles the GET_DESCRIPTOR standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param pUSBRequest holds the data for this request. +// +// This function will return most of the descriptors requested by the host +// controller. The descriptor specified by \e +// pvInstance->psInfo->pDeviceDescriptor will be returned when the device +// descriptor is requested. If a request for a specific configuration +// descriptor is made, then the appropriate descriptor from the \e +// g_pConfigDescriptors will be returned. When a request for a string +// descriptor is made, the appropriate string from the +// \e pvInstance->psInfo->pStringDescriptors will be returned. If the \e +// pvInstance->psInfo->sCallbacks.GetDescriptor is specified it will be +// called to handle the request. In this case it must call the +// USBDCDSendDataEP0() function to send the data to the host controller. If +// the callback is not specified, and the descriptor request is not for a +// device, configuration, or string descriptor then this function will stall +// the request to indicate that the request was not supported by the device. +// +// \return None. +// +//***************************************************************************** +static void +USBDGetDescriptor(void *pvInstance, tUSBRequest *pUSBRequest) +{ + tBoolean bConfig; + tDeviceInstance *psUSBControl; + tDeviceInfo *psDevice; + + ASSERT(pUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDeviceInstance *)pvInstance; + psDevice = psUSBControl->psInfo; + + // + // Need to ACK the data on end point 0 without setting last data as there + // will be a data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false); + + // + // Assume we are not sending the configuration descriptor until we + // determine otherwise. + // + bConfig = false; + + // + // Which descriptor are we being asked for? + // + switch(pUSBRequest->wValue >> 8) + { + // + // This request was for a device descriptor. + // + case USB_DTYPE_DEVICE: + { + // + // Return the externally provided device descriptor. + // + psUSBControl->pEP0Data = + (unsigned char *)psDevice->pDeviceDescriptor; + + // + // The size of the device descriptor is in the first byte. + // + psUSBControl->ulEP0DataRemain = psDevice->pDeviceDescriptor[0]; + + break; + } + + // + // This request was for a configuration descriptor. + // + case USB_DTYPE_CONFIGURATION: + { + const tConfigHeader *psConfig; + const tDeviceDescriptor *psDeviceDesc; + unsigned char ucIndex; + + // + // Which configuration are we being asked for? + // + ucIndex = (unsigned char)(pUSBRequest->wValue & 0xFF); + + // + // Is this valid? + // + psDeviceDesc = + (const tDeviceDescriptor *)psDevice->pDeviceDescriptor; + + if(ucIndex >= psDeviceDesc->bNumConfigurations) + { + // + // This is an invalid configuration index. Stall EP0 to + // indicate a request error. + // + USBDCDStallEP0(0); + psUSBControl->pEP0Data = 0; + psUSBControl->ulEP0DataRemain = 0; + } + else + { + // + // Return the externally specified configuration descriptor. + // + psConfig = psDevice->ppConfigDescriptors[ucIndex]; + + // + // Start by sending data from the beginning of the first + // descriptor. + // + psUSBControl->ucConfigSection = 0; + psUSBControl->ucSectionOffset = 0; + psUSBControl->pEP0Data = (unsigned char *) + psConfig->psSections[0]->pucData; + + // + // Determine the total size of the configuration descriptor + // by counting the sizes of the sections comprising it. + // + psUSBControl->ulEP0DataRemain = + USBDCDConfigDescGetSize(psConfig); + + // + // Remember that we need to send the configuration descriptor + // and which descriptor we need to send. + // + psUSBControl->ucConfigIndex = ucIndex; + + bConfig = true; + } + break; + } + + // + // This request was for a string descriptor. + // + case USB_DTYPE_STRING: + { + long lIndex; + + // + // Determine the correct descriptor index based on the requested + // language ID and index. + // + lIndex = USBDStringIndexFromRequest(pUSBRequest->wIndex, + pUSBRequest->wValue & 0xFF); + + // + // If the mapping function returned -1 then stall the request to + // indicate that the request was not valid. + // + if(lIndex == -1) + { + USBDCDStallEP0(0); + break; + } + + // + // Return the externally specified configuration descriptor. + // + psUSBControl->pEP0Data = + (unsigned char *)psDevice->ppStringDescriptors[lIndex]; + + // + // The total size of a string descriptor is in byte 0. + // + psUSBControl->ulEP0DataRemain = + psDevice->ppStringDescriptors[lIndex][0]; + + break; + } + + // + // Any other request is not handled by the default enumeration handler + // so see if it needs to be passed on to another handler. + // + default: + { + // + // If there is a handler for requests that are not handled then + // call it. + // + if(psDevice->sCallbacks.pfnGetDescriptor) + { + psDevice->sCallbacks.pfnGetDescriptor(psUSBControl->pvInstance, + pUSBRequest); + } + else + { + // + // Whatever this was this handler does not understand it so + // just stall the request. + // + USBDCDStallEP0(0); + } + + return; + } + } + + // + // If this request has data to send, then send it. + // + if(psUSBControl->pEP0Data) + { + // + // If there is more data to send than is requested then just + // send the requested amount of data. + // + if(psUSBControl->ulEP0DataRemain > pUSBRequest->wLength) + { + psUSBControl->ulEP0DataRemain = pUSBRequest->wLength; + } + + // + // Now in the transmit data state. Be careful to call the correct + // function since we need to handle the configuration descriptor + // differently from the others. + // + if(!bConfig) + { + USBDEP0StateTx(0); + } + else + { + USBDEP0StateTxConfig(0); + } + } +} + +//***************************************************************************** +// +// This function determines which string descriptor to send to satisfy a +// request for a given index and language. +// +// \param usLang is the requested string language ID. +// \param usIndex is the requested string descriptor index. +// +// When a string descriptor is requested, the host provides a language ID and +// index to identify the string ("give me string number 5 in French"). This +// function maps these two parameters to an index within our device's string +// descriptor array which is arranged as multiple groups of strings with +// one group for each language advertised via string descriptor 0. +// +// We assume that there are an equal number of strings per language and +// that the first descriptor is the language descriptor and use this fact to +// perform the mapping. +// +// \return The index of the string descriptor to return or -1 if the string +// could not be found. +// +//***************************************************************************** +static long +USBDStringIndexFromRequest(unsigned short usLang, unsigned short usIndex) +{ + tString0Descriptor *pLang; + unsigned long ulNumLangs; + unsigned long ulNumStringsPerLang; + unsigned long ulLoop; + + // + // Make sure we have a string table at all. + // + if((g_psUSBDevice[0].psInfo == 0) || + (g_psUSBDevice[0].psInfo->ppStringDescriptors == 0)) + { + return(-1); + } + + // + // First look for the trivial case where descriptor 0 is being + // requested. This is the special case since descriptor 0 contains the + // language codes supported by the device. + // + if(usIndex == 0) + { + return(0); + } + + // + // How many languages does this device support? This is determined by + // looking at the length of the first descriptor in the string table, + // subtracting 2 for the header and dividing by two (the size of each + // language code). + // + ulNumLangs = (g_psUSBDevice[0].psInfo->ppStringDescriptors[0][0] - 2) / 2; + + // + // We assume that the table includes the same number of strings for each + // supported language. We know the number of entries in the string table, + // so how many are there for each language? This may seem an odd way to + // do this (why not just have the application tell us in the device info + // structure?) but it's needed since we didn't want to change the API + // after the first release which did not support multiple languages. + // + ulNumStringsPerLang = ((g_psUSBDevice[0].psInfo->ulNumStringDescriptors - 1) / + ulNumLangs); + + // + // Just to be sure, make sure that the calculation indicates an equal + // number of strings per language. We expect the string table to contain + // (1 + (strings_per_language * languages)) entries. + // + if((1 + (ulNumStringsPerLang * ulNumLangs)) != + g_psUSBDevice[0].psInfo->ulNumStringDescriptors) + { + return(-1); + } + + // + // Now determine which language we are looking for. It is assumed that + // the order of the groups of strings per language in the table is the + // same as the order of the language IDs listed in the first descriptor. + // + pLang = (tString0Descriptor *)(g_psUSBDevice[0].psInfo->ppStringDescriptors[0]); + + // + // Look through the supported languages looking for the one we were asked + // for. + // + for(ulLoop = 0; ulLoop < ulNumLangs; ulLoop++) + { + // + // Have we found the requested language? + // + if(pLang->wLANGID[ulLoop] == usLang) + { + // + // Yes - calculate the index of the descriptor to send. + // + return((ulNumStringsPerLang * ulLoop) + usIndex); + } + } + + // + // If we drop out of the loop, the requested language was not found so + // return -1 to indicate the error. + // + return(-1); +} + +//***************************************************************************** +// +// This function handles the SET_DESCRIPTOR standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param pUSBRequest holds the data for this request. +// +// This function currently is not supported and will respond with a Stall +// to indicate that this command is not supported by the device. +// +// \return None. +// +//***************************************************************************** +static void +USBDSetDescriptor(void *pvInstance, tUSBRequest *pUSBRequest) +{ + // + // Need to ACK the data on end point 0 without setting last data as there + // will be a data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false); + + // + // This function is not handled by default. + // + USBDCDStallEP0(0); +} + +//***************************************************************************** +// +// This function handles the GET_CONFIGURATION standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param pUSBRequest holds the data for this request. +// +// This function responds to a host request to return the current +// configuration of the USB device. The function will send the configuration +// response to the host and return. This value will either be 0 or the last +// value received from a call to SetConfiguration(). +// +// \return None. +// +//***************************************************************************** +static void +USBDGetConfiguration(void *pvInstance, tUSBRequest *pUSBRequest) +{ + unsigned char ucValue; + tDeviceInstance *psUSBControl; + + ASSERT(pUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDeviceInstance *)pvInstance; + + // + // Need to ACK the data on end point 0 without setting last data as there + // will be a data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false); + + // + // If we still have an address pending then the device is still not + // configured. + // + if(psUSBControl->ulDevAddress & DEV_ADDR_PENDING) + { + ucValue = 0; + } + else + { + ucValue = (unsigned char)psUSBControl->ulConfiguration; + } + + psUSBControl->ulEP0DataRemain = 1; + psUSBControl->pEP0Data = &ucValue; + + // + // Send the single byte response. + // + USBDEP0StateTx(0); +} + +//***************************************************************************** +// +// This function handles the SET_CONFIGURATION standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param pUSBRequest holds the data for this request. +// +// This function responds to a host request to change the current +// configuration of the USB device. The actual configuration number is taken +// from the structure passed in via \e pUSBRequest. This number should be one +// of the configurations that was specified in the descriptors. If the +// \e ConfigChange callback is specified in \e pvInstance->psInfo->sCallbacks, +// it will be called so that the application can respond to a change in +// configuration. +// +// \return None. +// +//***************************************************************************** +static void +USBDSetConfiguration(void *pvInstance, tUSBRequest *pUSBRequest) +{ + tDeviceInstance *psUSBControl; + tDeviceInfo *psDevice; + + // + // Create the device information pointer. + // + psUSBControl = (tDeviceInstance *)pvInstance; + psDevice = psUSBControl->psInfo; + + // + // Need to ACK the data on end point 0 with last data set as this has no + // data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, true); + + // + // Cannot set the configuration to one that does not exist so check the + // enumeration structure to see how many valid configurations are present. + // + if(pUSBRequest->wValue > psUSBControl->psInfo->pDeviceDescriptor[17]) + { + // + // The passed configuration number is not valid. Stall the endpoint to + // signal the error to the host. + // + USBDCDStallEP0(0); + } + else + { + // + // Save the configuration. + // + psUSBControl->ulConfiguration = pUSBRequest->wValue; + + // + // If passed a configuration other than 0 (which tells us that we are + // not currently configured), configure the endpoints (other than EP0) + // appropriately. + // + if(psUSBControl->ulConfiguration) + { + const tConfigHeader *psHdr; + const tConfigDescriptor *psDesc; + + // + // Get a pointer to the configuration descriptor. This will always + // be the first section in the current configuration. + // + psHdr = psDevice->ppConfigDescriptors[pUSBRequest->wValue - 1]; + psDesc = (const tConfigDescriptor *)(psHdr->psSections[0]->pucData); + + // + // Remember the new self- or bus-powered state if the user has not + // already called us to tell us the state to report. + // + if(!psUSBControl->bPwrSrcSet) + { + if((psDesc->bmAttributes & USB_CONF_ATTR_PWR_M) == + USB_CONF_ATTR_SELF_PWR) + { + psUSBControl->ucStatus |= USB_STATUS_SELF_PWR; + } + else + { + psUSBControl->ucStatus &= ~USB_STATUS_SELF_PWR; + } + } + + // + // Configure endpoints for the new configuration. + // + USBDeviceConfig(psUSBControl, + psDevice->ppConfigDescriptors[pUSBRequest->wValue - 1]); + } + + // + // If there is a configuration change callback then call it. + // + if(psDevice->sCallbacks.pfnConfigChange) + { + psDevice->sCallbacks.pfnConfigChange( + psUSBControl->pvInstance, psUSBControl->ulConfiguration); + } + } +} + +//***************************************************************************** +// +// This function handles the GET_INTERFACE standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param pUSBRequest holds the data for this request. +// +// This function is called when the host controller request the current +// interface that is in use by the device. This simply returns the value set +// by the last call to SetInterface(). +// +// \return None. +// +//***************************************************************************** +static void +USBDGetInterface(void *pvInstance, tUSBRequest *pUSBRequest) +{ + unsigned char ucValue; + tDeviceInstance *psUSBControl; + + ASSERT(pUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDeviceInstance *)pvInstance; + + // + // Need to ACK the data on end point 0 without setting last data as there + // will be a data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false); + + // + // If we still have an address pending then the device is still not + // configured. + // + if(psUSBControl->ulDevAddress & DEV_ADDR_PENDING) + { + ucValue = (unsigned char)0; + } + else + { + // + // Is the interface number valid? + // + if(pUSBRequest->wIndex < USB_MAX_INTERFACES_PER_DEVICE) + { + // + // Read the current alternate setting for the required interface. + // + ucValue = psUSBControl->pucAltSetting[pUSBRequest->wIndex]; + } + else + { + // + // An invalid interface number was specified. + // + USBDCDStallEP0(0); + return; + } + } + + // + // Send the single byte response. + // + psUSBControl->ulEP0DataRemain = 1; + psUSBControl->pEP0Data = &ucValue; + + // + // Send the single byte response. + // + USBDEP0StateTx(0); +} + +//***************************************************************************** +// +// This function handles the SET_INTERFACE standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param pUSBRequest holds the data for this request. +// +// This function is called when a standard request for changing the interface +// is received from the host controller. If this is a valid request the +// function will call the function specified by the InterfaceChange in the +// \e pvInstance->psInfo->sCallbacks variable to notify the application that the +// interface has changed and will pass it the new alternate interface number. +// +// \return None. +// +//***************************************************************************** +static void +USBDSetInterface(void *pvInstance, tUSBRequest *pUSBRequest) +{ + const tConfigHeader *psConfig; + tInterfaceDescriptor *psInterface; + unsigned long ulLoop; + unsigned long ulSection; + unsigned long ulNumInterfaces; + unsigned char ucInterface; + tBoolean bRetcode; + tDeviceInstance *psUSBControl; + tDeviceInfo *psDevice; + + ASSERT(pUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDeviceInstance *)pvInstance; + psDevice = psUSBControl->psInfo; + + // + // Need to ACK the data on end point 0 with last data set as this has no + // data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, true); + + // + // Use the current configuration. + // + psConfig = psDevice->ppConfigDescriptors[psUSBControl->ulConfiguration - 1]; + + // + // How many interfaces are included in the descriptor? + // + ulNumInterfaces = USBDCDConfigDescGetNum(psConfig, + USB_DTYPE_INTERFACE); + + // + // Find the interface descriptor for the supplied interface and alternate + // setting numbers. + // + for(ulLoop = 0; ulLoop < ulNumInterfaces; ulLoop++) + { + // + // Get the next interface descriptor in the configuration descriptor. + // + psInterface = USBDCDConfigGetInterface(psConfig, ulLoop, USB_DESC_ANY, + &ulSection); + + // + // Is this the required interface with the correct alternate setting? + // + if(psInterface && + (psInterface->bInterfaceNumber == pUSBRequest->wIndex) && + (psInterface->bAlternateSetting == pUSBRequest->wValue)) + { + ucInterface = psInterface->bInterfaceNumber; + + // + // Make sure we don't write outside the bounds of the pucAltSetting + // array (in a debug build, anyway, since this indicates an error + // in the device descriptor). + // + ASSERT(ucInterface < USB_MAX_INTERFACES_PER_DEVICE); + + // + // This is the correct interface descriptor so save the + // setting. + // + psUSBControl->pucAltSetting[ucInterface] = + psInterface->bAlternateSetting; + + // + // Reconfigure the endpoints to match the requirements of the + // new alternate setting for the interface. + // + bRetcode = USBDeviceConfigAlternate(psUSBControl, psConfig, ucInterface, + psInterface->bAlternateSetting); + + // + // If there is a callback then notify the application of the + // change to the alternate interface. + // + if(bRetcode && psDevice->sCallbacks.pfnInterfaceChange) + { + psDevice->sCallbacks.pfnInterfaceChange( + psUSBControl->pvInstance, + pUSBRequest->wIndex, + pUSBRequest->wValue); + } + + // + // All done. + // + return; + } + } + + // + // If we drop out of the loop, we didn't find an interface descriptor + // matching the requested number and alternate setting or there was an + // error while trying to set up for the new alternate setting. + // + USBDCDStallEP0(0); +} + +//***************************************************************************** +// +// This function handles the SYNC_FRAME standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param pUSBRequest holds the data for this request. +// +// This is currently a stub function that will stall indicating that the +// command is not supported. +// +// \return None. +// +//***************************************************************************** +static void +USBDSyncFrame(void *pvInstance, tUSBRequest *pUSBRequest) +{ + // + // Need to ACK the data on end point 0 with last data set as this has no + // data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, true); + + // + // Not handled yet so stall this request. + // + USBDCDStallEP0(0); +} + +//***************************************************************************** +// +// This internal function handles sending data on endpoint zero. +// +// \param ulIndex is the index of the USB controller which is to be +// initialized. +// +// \return None. +// +//***************************************************************************** +static void +USBDEP0StateTx(unsigned long ulIndex) +{ + unsigned long ulNumBytes; + unsigned char *pData; + + ASSERT(ulIndex == 0); + + // + // In the TX state on endpoint zero. + // + g_psUSBDevice[0].eEP0State = USB_STATE_TX; + + // + // Set the number of bytes to send this iteration. + // + ulNumBytes = g_psUSBDevice[0].ulEP0DataRemain; + + // + // Limit individual transfers to 64 bytes. + // + if(ulNumBytes > EP0_MAX_PACKET_SIZE) + { + ulNumBytes = EP0_MAX_PACKET_SIZE; + } + + // + // Save the pointer so that it can be passed to the USBEndpointDataPut() + // function. + // + pData = (unsigned char *)g_psUSBDevice[0].pEP0Data; + + // + // Advance the data pointer and counter to the next data to be sent. + // + g_psUSBDevice[0].ulEP0DataRemain -= ulNumBytes; + g_psUSBDevice[0].pEP0Data += ulNumBytes; + + // + // Put the data in the correct FIFO. + // + MAP_USBEndpointDataPut(USB0_BASE, USB_EP_0, pData, ulNumBytes); + + // + // If this is exactly 64 then don't set the last packet yet. + // + if(ulNumBytes == EP0_MAX_PACKET_SIZE) + { + // + // There is more data to send or exactly 64 bytes were sent, this + // means that there is either more data coming or a null packet needs + // to be sent to complete the transaction. + // + MAP_USBEndpointDataSend(USB0_BASE, USB_EP_0, USB_TRANS_IN); + } + else + { + // + // Now go to the status state and wait for the transmit to complete. + // + g_psUSBDevice[0].eEP0State = USB_STATE_STATUS; + + // + // Send the last bit of data. + // + MAP_USBEndpointDataSend(USB0_BASE, USB_EP_0, USB_TRANS_IN_LAST); + + // + // If there is a sent callback then call it. + // + if((g_psUSBDevice[0].psInfo->sCallbacks.pfnDataSent) && + (g_psUSBDevice[0].ulOUTDataSize != 0)) + { + // + // Call the custom handler. + // + g_psUSBDevice[0].psInfo->sCallbacks.pfnDataSent( + g_psUSBDevice[0].pvInstance, g_psUSBDevice[0].ulOUTDataSize); + + // + // There is no longer any data pending to be sent. + // + g_psUSBDevice[0].ulOUTDataSize = 0; + } + } +} + +//***************************************************************************** +// +// This internal function handles sending the configuration descriptor on +// endpoint zero. +// +// \param ulIndex is the index of the USB controller which is to be used. +// +// +// \return None. +// +//***************************************************************************** +static void +USBDEP0StateTxConfig(unsigned long ulIndex) +{ + unsigned long ulNumBytes; + unsigned long ulSecBytes; + unsigned long ulToSend; + unsigned char *pData; + tConfigDescriptor sConfDesc; + const tConfigHeader *psConfig; + const tConfigSection *psSection; + + ASSERT(ulIndex == 0); + + // + // In the TX state on endpoint zero. + // + g_psUSBDevice[0].eEP0State = USB_STATE_TX_CONFIG; + + // + // Find the current configuration descriptor definition. + // + psConfig = g_psUSBDevice[0].psInfo->ppConfigDescriptors[ + g_psUSBDevice[0].ucConfigIndex]; + + // + // Set the number of bytes to send this iteration. + // + ulNumBytes = g_psUSBDevice[0].ulEP0DataRemain; + + // + // Limit individual transfers to 64 bytes. + // + if(ulNumBytes > EP0_MAX_PACKET_SIZE) + { + ulNumBytes = EP0_MAX_PACKET_SIZE; + } + + // + // If this is the first call, we need to fix up the total length of the + // configuration descriptor. This has already been determined and set in + // g_sUSBDeviceState.ulEP0DataRemain. + // + if((g_psUSBDevice[0].ucSectionOffset == 0) && + (g_psUSBDevice[0].ucConfigSection == 0)) + { + // + // Copy the USB configuration descriptor from the beginning of the + // first section of the current configuration. + // + sConfDesc = *(tConfigDescriptor *)g_psUSBDevice[0].pEP0Data; + + // + // Update the total size. + // + sConfDesc.wTotalLength = (unsigned short)USBDCDConfigDescGetSize( + psConfig); + + // + // Write the descriptor to the USB FIFO. + // + ulToSend = (ulNumBytes < sizeof(tConfigDescriptor)) ? ulNumBytes : + sizeof(tConfigDescriptor); + MAP_USBEndpointDataPut(USB0_BASE, USB_EP_0, (unsigned char *)&sConfDesc, + ulToSend); + + // + // Did we reach the end of the first section? + // + if(psConfig->psSections[0]->usSize == ulToSend) + { + // + // Update our tracking indices to point to the start of the next + // section. + // + g_psUSBDevice[0].ucSectionOffset = 0; + g_psUSBDevice[0].ucConfigSection = 1; + } + else + { + // + // Note that we have sent the first few bytes of the descriptor. + // + g_psUSBDevice[0].ucSectionOffset = (unsigned char)ulToSend; + } + + // + // How many bytes do we have remaining to send on this iteration? + // + ulToSend = ulNumBytes - ulToSend; + } + else + { + // + // Set the number of bytes we still have to send on this call. + // + ulToSend = ulNumBytes; + } + + // + // Add the relevant number of bytes to the USB FIFO + // + while(ulToSend) + { + // + // Get a pointer to the current configuration section. + // + psSection = psConfig->psSections[g_psUSBDevice[0].ucConfigSection]; + + // + // Calculate bytes are available in the current configuration section. + // + ulSecBytes = (unsigned long)(psSection->usSize - + g_psUSBDevice[0].ucSectionOffset); + + // + // Save the pointer so that it can be passed to the + // USBEndpointDataPut() function. + // + pData = (unsigned char *)psSection->pucData + + g_psUSBDevice[0].ucSectionOffset; + + // + // Are there more bytes in this section that we still have to send? + // + if(ulSecBytes > ulToSend) + { + // + // Yes - send only the remaining bytes in the transfer. + // + ulSecBytes = ulToSend; + } + + // + // Put the data in the correct FIFO. + // + MAP_USBEndpointDataPut(USB0_BASE, USB_EP_0, pData, ulSecBytes); + + // + // Fix up our pointers for the next iteration. + // + ulToSend -= ulSecBytes; + g_psUSBDevice[0].ucSectionOffset += (unsigned char)ulSecBytes; + + // + // Have we reached the end of a section? + // + if(g_psUSBDevice[0].ucSectionOffset == psSection->usSize) + { + // + // Yes - move to the next one. + // + g_psUSBDevice[0].ucConfigSection++; + g_psUSBDevice[0].ucSectionOffset = 0; + } + } + + // + // Fix up the number of bytes remaining to be sent and the start pointer. + // + g_psUSBDevice[0].ulEP0DataRemain -= ulNumBytes; + + // + // If we ran out of bytes in the configuration section, bail and just + // send out what we have. + // + if(psConfig->ucNumSections <= g_psUSBDevice[0].ucConfigSection) + { + g_psUSBDevice[0].ulEP0DataRemain = 0; + } + + // + // If there is no more data don't keep looking or ucConfigSection might + // overrun the available space. + // + if(g_psUSBDevice[0].ulEP0DataRemain != 0) + { + pData =(unsigned char *) + psConfig->psSections[g_psUSBDevice[0].ucConfigSection]->pucData; + ulToSend = g_psUSBDevice[0].ucSectionOffset; + g_psUSBDevice[0].pEP0Data = (pData + ulToSend); + } + + // + // If this is exactly 64 then don't set the last packet yet. + // + if(ulNumBytes == EP0_MAX_PACKET_SIZE) + { + // + // There is more data to send or exactly 64 bytes were sent, this + // means that there is either more data coming or a null packet needs + // to be sent to complete the transaction. + // + MAP_USBEndpointDataSend(USB0_BASE, USB_EP_0, USB_TRANS_IN); + } + else + { + // + // Send the last bit of data. + // + MAP_USBEndpointDataSend(USB0_BASE, USB_EP_0, USB_TRANS_IN_LAST); + + // + // If there is a sent callback then call it. + // + if((g_psUSBDevice[0].psInfo->sCallbacks.pfnDataSent) && + (g_psUSBDevice[0].ulOUTDataSize != 0)) + { + // + // Call the custom handler. + // + g_psUSBDevice[0].psInfo->sCallbacks.pfnDataSent( + g_psUSBDevice[0].pvInstance, g_psUSBDevice[0].ulOUTDataSize); + + // + // There is no longer any data pending to be sent. + // + g_psUSBDevice[0].ulOUTDataSize = 0; + } + + // + // Now go to the status state and wait for the transmit to complete. + // + g_psUSBDevice[0].eEP0State = USB_STATE_STATUS; + } +} + +//***************************************************************************** +// +// The internal USB device interrupt handler. +// +// \param ulIndex is the USB controller associated with this interrupt. +// \param ulStatus is the current interrupt status as read via a call to +// USBIntStatusControl(). +// +// This function is called from either \e USB0DualModeIntHandler() or +// \e USB0DeviceIntHandler() to process USB interrupts when in device mode. +// This handler will branch the interrupt off to the appropriate application or +// stack handlers depending on the current status of the USB controller. +// +// The two-tiered structure for the interrupt handler ensures that it is +// possible to use the same handler code in both device and OTG modes and +// means that host code can be excluded from applications that only require +// support for USB device mode operation. +// +// \return None. +// +//***************************************************************************** +void +USBDeviceIntHandlerInternal(unsigned long ulIndex, unsigned long ulStatus) +{ + static unsigned long ulSOFDivide = 0; + tDeviceInfo *psInfo; + void *pvInstance; + + // + // If device initialization has not been performed then just disconnect + // from the USB bus and return from the handler. + // + if(g_psUSBDevice[0].psInfo == 0) + { + MAP_USBDevDisconnect(USB0_BASE); + return; + } + + psInfo = g_psUSBDevice[0].psInfo; + pvInstance = g_psUSBDevice[0].pvInstance; + + // + // Received a reset from the host. + // + if(ulStatus & USB_INTCTRL_RESET) + { + USBDeviceEnumResetHandler(&g_psUSBDevice[0]); + } + + // + // Suspend was signaled on the bus. + // + if(ulStatus & USB_INTCTRL_SUSPEND) + { + // + // Call the SuspendHandler() if it was specified. + // + if(psInfo->sCallbacks.pfnSuspendHandler) + { + psInfo->sCallbacks.pfnSuspendHandler(pvInstance); + } + } + + // + // Resume was signaled on the bus. + // + if(ulStatus & USB_INTCTRL_RESUME) + { + // + // Call the ResumeHandler() if it was specified. + // + if(psInfo->sCallbacks.pfnResumeHandler) + { + psInfo->sCallbacks.pfnResumeHandler(pvInstance); + } + } + + // + // USB device was disconnected. + // + if(ulStatus & USB_INTCTRL_DISCONNECT) + { + // + // Call the DisconnectHandler() if it was specified. + // + if(psInfo->sCallbacks.pfnDisconnectHandler) + { + psInfo->sCallbacks.pfnDisconnectHandler(pvInstance); + } + } + + // + // Start of Frame was received. + // + if(ulStatus & USB_INTCTRL_SOF) + { + // + // Increment the global Start of Frame counter. + // + g_ulUSBSOFCount++; + + // + // Increment our SOF divider. + // + ulSOFDivide++; + + // + // Handle resume signaling if required. + // + USBDeviceResumeTickHandler(&g_psUSBDevice[0]); + + // + // Have we counted enough SOFs to allow us to call the tick function? + // + if(ulSOFDivide == USB_SOF_TICK_DIVIDE) + { + // + // Yes - reset the divider and call the SOF tick handler. + // + ulSOFDivide = 0; + InternalUSBStartOfFrameTick(USB_SOF_TICK_DIVIDE); + } + } + + // + // Get the controller interrupt status. + // + ulStatus = MAP_USBIntStatusEndpoint(USB0_BASE); + + // + // Handle end point 0 interrupts. + // + if(ulStatus & USB_INTEP_0) + { + USBDeviceEnumHandler(&g_psUSBDevice[0]); + } + + // + // Because there is no way to detect if a uDMA interrupt has occurred, + // check for an endpoint callback and call it if it is available. + // + if(psInfo->sCallbacks.pfnEndpointHandler) + { + psInfo->sCallbacks.pfnEndpointHandler(pvInstance, ulStatus); + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/device/usbdevice.h b/usblib/device/usbdevice.h new file mode 100644 index 0000000..edffb1d --- /dev/null +++ b/usblib/device/usbdevice.h @@ -0,0 +1,223 @@ +//***************************************************************************** +// +// usbdevice.h - types and definitions used during USB enumeration. +// +// Copyright (c) 2008-2012 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#ifndef __USBDEVICE_H__ +#define __USBDEVICE_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup device_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! The maximum number of independent interfaces that any single device +//! implementation can support. Independent interfaces means interface +//! descriptors with different bInterfaceNumber values - several interface +//! descriptors offering different alternative settings but the same interface +//! number count as a single interface. +// +//***************************************************************************** +#define USB_MAX_INTERFACES_PER_DEVICE 8 + +#include "./usbdevicepriv.h" + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +//! This structure is passed to the USB library on a call to USBDCDInit and +//! provides the library with information about the device that the +//! application is implementing. It contains functions pointers for the +//! various USB event handlers and pointers to each of the standard device +//! descriptors. +// +//***************************************************************************** +struct tDeviceInfo +{ + // + //! A pointer to a structure containing pointers to event handler functions + //! provided by the client to support the operation of this device. + // + tCustomHandlers sCallbacks; + + // + //! A pointer to the device descriptor for this device. + // + const unsigned char *pDeviceDescriptor; + + // + //! A pointer to an array of configuration descriptor pointers. Each entry + //! in the array corresponds to one configuration that the device may be set + //! to use by the USB host. The number of entries in the array must + //! match the bNumConfigurations value in the device descriptor + //! array, pDeviceDescriptor. + // + const tConfigHeader * const *ppConfigDescriptors; + + // + //! A pointer to the string descriptor array for this device. This array + //! must be arranged as follows: + //! + //! - [0] - Standard descriptor containing supported language codes. + //! - [1] - String 1 for the first language listed in descriptor 0. + //! - [2] - String 2 for the first language listed in descriptor 0. + //! - ... + //! - [n] - String n for the first language listed in descriptor 0. + //! - [n+1] - String 1 for the second language listed in descriptor 0. + //! - ... + //! - [2n] - String n for the second language listed in descriptor 0. + //! - [2n+1]- String 1 for the third language listed in descriptor 0. + //! - ... + //! - [3n] - String n for the third language listed in descriptor 0. + //! + //! and so on. + // + const unsigned char * const *ppStringDescriptors; + + // + //! The total number of descriptors provided in the ppStringDescriptors + //! array. + // + unsigned long ulNumStringDescriptors; + + // + //! A structure defining how the USB controller FIFO is to be partitioned + //! between the various endpoints. This member can be set to point to + //! g_sUSBDefaultFIFOConfig if the default FIFO configuration is acceptable + //! This configuration sets each endpoint FIFO to be single buffered and + //! sized to hold the maximum packet size for the endpoint. + // + const tFIFOConfig *psFIFOConfig; + + // + //! This value will be passed back to all call back functions so that + //! they have access to individual instance data based on the this pointer. + // + void *pvInstance; + + // + //! The generic device instance data for this device. This will be set + //! by the call to DCDInit() which is usually made when the applicaiton + //! calls the class specific initialization function. + // + tDeviceInstance *psDevice; +}; + +//***************************************************************************** +// +// The default USB endpoint FIFO configuration structure. This structure +// contains definitions to set all USB FIFOs into single buffered mode with +// no DMA use. Each endpoint's FIFO is sized to hold the largest maximum +// packet size for any interface alternate setting in the current config +// descriptor. A pointer to this structure may be passed in the psFIFOConfig +// field of the tDeviceInfo structure passed to USBCDCInit if the application +// does not require any special handling of the USB controller FIFO. +// +//***************************************************************************** +extern const tFIFOConfig g_sUSBDefaultFIFOConfig; + +//***************************************************************************** +// +// Public APIs offered by the USB library device control driver. +// +//***************************************************************************** +extern void USBDCDInit(unsigned long ulIndex, tDeviceInfo *psDevice); +extern void USBDCDTerm(unsigned long ulIndex); +extern void USBDCDStallEP0(unsigned long ulIndex); +extern void USBDCDRequestDataEP0(unsigned long ulIndex, unsigned char *pucData, + unsigned long ulSize); +extern void USBDCDSendDataEP0(unsigned long ulIndex, unsigned char *pucData, + unsigned long ulSize); +extern void USBDCDSetDefaultConfiguration(unsigned long ulIndex, + unsigned long ulDefaultConfig); +extern unsigned long USBDCDConfigDescGetSize(const tConfigHeader *psConfig); +extern unsigned long USBDCDConfigDescGetNum(const tConfigHeader *psConfig, + unsigned long ulType); +extern tDescriptorHeader *USBDCDConfigDescGet(const tConfigHeader *psConfig, + unsigned long ulType, + unsigned long ulIndex, + unsigned long *pulSection); +extern unsigned long + USBDCDConfigGetNumAlternateInterfaces(const tConfigHeader *psConfig, + unsigned char ucInterfaceNumber); +extern tInterfaceDescriptor * + USBDCDConfigGetInterface(const tConfigHeader *psConfig, + unsigned long ulIndex, unsigned long ulAltCfg, + unsigned long *pulSection); +extern tEndpointDescriptor * + USBDCDConfigGetInterfaceEndpoint(const tConfigHeader *psConfig, + unsigned long ulInterfaceNumber, + unsigned long ulAltCfg, + unsigned long ulIndex); +extern void USBDCDPowerStatusSet(unsigned long ulIndex, unsigned char ucPower); +extern tBoolean USBDCDRemoteWakeupRequest(unsigned long ulIndex); + +//***************************************************************************** +// +// Early releases of the USB library had the following function named +// incorrectly. This macro ensures that any code which used the previous name +// will still operate as expected. +// +//***************************************************************************** +#ifndef DEPRECATED +#define USBCDCConfigGetInterfaceEndpoint(a, b, c, d) \ + USBDCDConfigGetInterfaceEndpoint((a), (b), (c), (d)) +#endif + +//***************************************************************************** +// +// Device mode interrupt handler for controller index 0. +// +//***************************************************************************** +extern void USB0DeviceIntHandler(void); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBENUM_H__ diff --git a/usblib/device/usbdevicepriv.h b/usblib/device/usbdevicepriv.h new file mode 100644 index 0000000..341fb1b --- /dev/null +++ b/usblib/device/usbdevicepriv.h @@ -0,0 +1,223 @@ +//***************************************************************************** +// +// usbdevicepriv.h - Private header file used to share internal variables and +// function prototypes between the various device-related +// modules in the USB library. This header MUST NOT be +// used by application code. +// +// Copyright (c) 2008-2012 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#ifndef __USBDEVICEPRIV_H__ +#define __USBDEVICEPRIV_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +// The states for endpoint zero during enumeration. +// +//***************************************************************************** +typedef enum +{ + // + // The USB device is waiting on a request from the host controller on + // endpoint zero. + // + USB_STATE_IDLE, + + // + // The USB device is sending data back to the host due to an IN request. + // + USB_STATE_TX, + + // + // The USB device is sending the configuration descriptor back to the host + // due to an IN request. + // + USB_STATE_TX_CONFIG, + + // + // The USB device is receiving data from the host due to an OUT + // request from the host. + // + USB_STATE_RX, + + // + // The USB device has completed the IN or OUT request and is now waiting + // for the host to acknowledge the end of the IN/OUT transaction. This + // is the status phase for a USB control transaction. + // + USB_STATE_STATUS, + + // + // This endpoint has signaled a stall condition and is waiting for the + // stall to be acknowledged by the host controller. + // + USB_STATE_STALL +} +tEP0State; + +typedef struct tDeviceInfo tDeviceInfo; +typedef struct tDeviceInstance tDeviceInstance; + +//***************************************************************************** +// +// The USB controller device information. +// +//***************************************************************************** +struct tDeviceInstance +{ + // + // The device information for the USB device. + // + tDeviceInfo *psInfo; + + // + // The instance data for the USB device. + // + void *pvInstance; + + // + // The current state of endpoint zero. + // + volatile tEP0State eEP0State; + + // + // The devices current address, this also has a change pending bit in the + // MSB of this value specified by DEV_ADDR_PENDING. + // + volatile unsigned long ulDevAddress; + + // + // This holds the current active configuration for this device. + // + unsigned long ulConfiguration; + + // + // This holds the configuration id that will take effect after a reset. + // + unsigned long ulDefaultConfiguration; + + // + // This holds the current alternate interface for this device. + // + unsigned char pucAltSetting[USB_MAX_INTERFACES_PER_DEVICE]; + + // + // This is the pointer to the current data being sent out or received + // on endpoint zero. + // + unsigned char *pEP0Data; + + // + // This is the number of bytes that remain to be sent from or received + // into the g_sUSBDeviceState.pEP0Data data buffer. + // + volatile unsigned long ulEP0DataRemain; + + // + // The amount of data being sent/received due to a custom request. + // + unsigned long ulOUTDataSize; + + // + // Holds the current device status. + // + unsigned char ucStatus; + + // + // Holds the endpoint status for the HALT condition. This array is sized + // to hold halt status for all IN and OUT endpoints. + // + unsigned char ucHalt[2][USBLIB_NUM_EP - 1]; + + // + // Holds the configuration descriptor section number currently being sent + // to the host. + // + unsigned char ucConfigSection; + + // + // Holds the offset within the configuration descriptor section currently + // being sent to the host. + // + unsigned char ucSectionOffset; + + // + // Holds the index of the configuration that we are currently sending back + // to the host. + // + unsigned char ucConfigIndex; + + // + // This flag is set to true if the client has called USBDPowerStatusSet + // and tells the USB library not to try to determine the current power + // status from the configuration descriptor. + // + tBoolean bPwrSrcSet; + + // + // This flag indicates whether or not remote wake up signaling is in + // progress. + // + tBoolean bRemoteWakeup; + + // + // During remote wake up signaling, this counter is used to track the + // number of milliseconds since the signaling was initiated. + // + unsigned char ucRemoteWakeupCount; +}; + +extern tDeviceInstance g_psUSBDevice[]; + +//***************************************************************************** +// +// Device enumeration functions provided by device/usbenum.c and called from +// the interrupt handler in device/usbhandler.c +// +//***************************************************************************** +extern tBoolean USBDeviceConfig(tDeviceInstance *psDevInst, + const tConfigHeader *psConfig); +extern tBoolean USBDeviceConfigAlternate(tDeviceInstance *psDevInst, + const tConfigHeader *psConfig, + unsigned char ucInterfaceNum, + unsigned char ucAlternateSetting); + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBDEVICEPRIV_H__ diff --git a/usblib/device/usbdhandler.c b/usblib/device/usbdhandler.c new file mode 100644 index 0000000..5ba33e5 --- /dev/null +++ b/usblib/device/usbdhandler.c @@ -0,0 +1,82 @@ +//***************************************************************************** +// +// usbhandler.c - General USB handling routines. +// +// Copyright (c) 2007-2012 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbdevicepriv.h" +#include "usblib/usblibpriv.h" + +//***************************************************************************** +// +//! \addtogroup device_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! The USB device interrupt handler. +//! +//! This the main USB interrupt handler entry point for use in USB device +//! applications. This top-level handler will branch the interrupt off to the +//! appropriate application or stack handlers depending on the current status +//! of the USB controller. +//! +//! Applications which operate purely as USB devices (rather than dual mode +//! applications which can operate in either device or host mode at different +//! times) must ensure that a pointer to this function is installed in the +//! interrupt vector table entry for the USB0 interrupt. For dual mode +//! operation, the vector should be set to point to \e USB0DualModeIntHandler() +//! instead. +//! +//! \return None. +// +//***************************************************************************** +void +USB0DeviceIntHandler(void) +{ + unsigned long ulStatus; + + // + // Get the controller interrupt status. + // + ulStatus = MAP_USBIntStatusControl(USB0_BASE); + + // + // Call the internal handler. + // + USBDeviceIntHandlerInternal(0, ulStatus); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/device/usbdhid.c b/usblib/device/usbdhid.c new file mode 100644 index 0000000..ccfca59 --- /dev/null +++ b/usblib/device/usbdhid.c @@ -0,0 +1,2579 @@ +//***************************************************************************** +// +// usbdhid.c - USB HID device class driver. +// +// Copyright (c) 2008-2012 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/usb.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "usblib/usblib.h" +#include "usblib/usbhid.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbdhid.h" +#include "usblib/usblibpriv.h" + +//***************************************************************************** +// +//! \addtogroup hid_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// 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) + +//***************************************************************************** +// +// Marker used to indicate that a given HID descriptor cannot be found in the +// client-supplied list. +// +//***************************************************************************** +#define HID_NOT_FOUND 0xFFFFFFFF + +//***************************************************************************** +// +// Flags that may appear in usDeferredOpFlags to indicate some operation that +// has been requested but could not be processed at the time it was received. +// Each deferred operation is defined as the bit number that should be set in +// tHIDInstance->usDeferredOpFlags to indicate that the operation is pending. +// +//***************************************************************************** +#define HID_DO_PACKET_RX 5 +#define HID_DO_SEND_IDLE_REPORT 6 + +//***************************************************************************** +// +// Macros to convert between USB controller base address and an index. These +// are currently trivial but are included to allow for the possibility of +// supporting more than one controller in the future. +// +//***************************************************************************** +#define USB_BASE_TO_INDEX(BaseAddr) (0) +#define USB_INDEX_TO_BASE(Index) (USB0_BASE) + +//***************************************************************************** +// +// Endpoints to use for each of the required endpoints in the driver. +// +//***************************************************************************** +#define INT_IN_ENDPOINT USB_EP_3 +#define INT_OUT_ENDPOINT USB_EP_3 + +//***************************************************************************** +// +// Maximum packet size for the interrupt endpoints used for report transmission +// and reception and the associated FIFO sizes to set aside for each endpoint. +// +//***************************************************************************** +#define INT_IN_EP_FIFO_SIZE USB_FIFO_SZ_64 +#define INT_OUT_EP_FIFO_SIZE USB_FIFO_SZ_64 + +#define INT_IN_EP_MAX_SIZE USB_FIFO_SZ_TO_BYTES(INT_IN_EP_FIFO_SIZE) +#define INT_OUT_EP_MAX_SIZE USB_FIFO_SZ_TO_BYTES(INT_IN_EP_FIFO_SIZE) + +//***************************************************************************** +// +// Device Descriptor. This is stored in RAM to allow several fields to be +// changed at runtime based on the client's requirements. +// +//***************************************************************************** +unsigned char g_pHIDDeviceDescriptor[] = +{ + 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) + USB_CLASS_DEVICE, // USB Device Class + 0, // USB Device Sub-class + USB_HID_PROTOCOL_NONE, // USB Device protocol + 64, // Maximum packet size for default pipe. + USBShort(0), // Vendor ID (VID). + USBShort(0), // Product ID (PID). + USBShort(0x100), // Device Version BCD. + 1, // Manufacturer string identifier. + 2, // Product string identifier. + 3, // Product serial number. + 1 // Number of configurations. +}; + +//***************************************************************************** +// +// HID 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. +// +//***************************************************************************** +unsigned char g_pHIDDescriptor[] = +{ + // + // Configuration descriptor header. + // + 9, // Size of the configuration descriptor. + USB_DTYPE_CONFIGURATION, // Type of this descriptor. + USBShort(34), // The total size of this full structure. + 1, // The number of interfaces in this + // configuration. + 1, // The unique value for this configuration. + 5, // 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. +// +//***************************************************************************** +unsigned char g_pHIDInterface[] = +{ + // + // HID Device Class 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_HID, // The interface class + 0, // The interface sub-class. + 0, // The interface protocol for the sub-class + // specified above. + 4, // The string index for this interface. +}; + +const unsigned char g_pHIDInEndpoint[] = +{ + // + // Interrupt IN endpoint descriptor + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_IN | USB_EP_TO_INDEX(INT_IN_ENDPOINT), + USB_EP_ATTR_INT, // Endpoint is an interrupt endpoint. + USBShort(INT_IN_EP_MAX_SIZE), // The maximum packet size. + 16, // The polling interval for this endpoint. +}; + +const unsigned char g_pHIDOutEndpoint[] = +{ + // + // Interrupt OUT endpoint descriptor + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_OUT | USB_EP_TO_INDEX(INT_OUT_ENDPOINT), + USB_EP_ATTR_INT, // Endpoint is an interrupt endpoint. + USBShort(INT_OUT_EP_MAX_SIZE), // The maximum packet size. + 16, // The polling interval for this endpoint. +}; + +//***************************************************************************** +// +// The HID configuration descriptor is defined as four or five sections +// depending upon the client's configuration choice. These sections are: +// +// 1. The 9 byte configuration descriptor (RAM). +// 2. The interface descriptor (RAM). +// 3. The HID report and physical descriptors (provided by the client) +// (FLASH). +// 4. The mandatory interrupt IN endpoint descriptor (FLASH). +// 5. The optional interrupt OUT endpoint descriptor (FLASH). +// +//***************************************************************************** +const tConfigSection g_sHIDConfigSection = +{ + sizeof(g_pHIDDescriptor), + g_pHIDDescriptor +}; + +const tConfigSection g_sHIDInterfaceSection = +{ + sizeof(g_pHIDInterface), + g_pHIDInterface +}; + +const tConfigSection g_sHIDInEndpointSection = +{ + sizeof(g_pHIDInEndpoint), + g_pHIDInEndpoint +}; + +const tConfigSection g_sHIDOutEndpointSection = +{ + sizeof(g_pHIDOutEndpoint), + g_pHIDOutEndpoint +}; + +//***************************************************************************** +// +// Place holder for the user's HID descriptor block. +// +//***************************************************************************** +tConfigSection g_sHIDDescriptorSection = +{ + 0, (void *)0 +}; + +//***************************************************************************** +// +// This array lists all the sections that must be concatenated to make a +// single, complete HID configuration descriptor. +// +//***************************************************************************** +const tConfigSection *g_psHIDSections[] = +{ + &g_sHIDConfigSection, + &g_sHIDInterfaceSection, + &g_sHIDDescriptorSection, + &g_sHIDInEndpointSection, + &g_sHIDOutEndpointSection +}; + +#define NUM_HID_SECTIONS (sizeof(g_psHIDSections) / \ + sizeof(tConfigSection *)) + +//***************************************************************************** +// +// The header for the single configuration we support. This is the root of +// the data structure that defines all the bits and pieces that are pulled +// together to generate the configuration descriptor. Note that this must be +// in RAM since we need to include or exclude the final section based on +// client supplied initialization parameters. +// +//***************************************************************************** +tConfigHeader g_sHIDConfigHeader = +{ + NUM_HID_SECTIONS, + g_psHIDSections +}; + +//***************************************************************************** +// +// Configuration Descriptor. +// +//***************************************************************************** +const tConfigHeader * const g_pHIDConfigDescriptors[] = +{ + &g_sHIDConfigHeader +}; + +//***************************************************************************** +// +// Forward references for device handler callbacks +// +//***************************************************************************** +static void HandleGetDescriptor(void *pvInstance, tUSBRequest *pUSBRequest); +static void HandleRequest(void *pvInstance, tUSBRequest *pUSBRequest); +static void HandleConfigChange(void *pvInstance, unsigned long ulInfo); +static void HandleEP0DataReceived(void *pvInstance, unsigned long ulInfo); +static void HandleEP0DataSent(void *pvInstance, unsigned long ulInfo); +static void HandleReset(void *pvInstance); +static void HandleSuspend(void *pvInstance); +static void HandleResume(void *pvInstance); +static void HandleDisconnect(void *pvInstance); +static void HandleEndpoints(void *pvInstance, unsigned long ulStatus); +static void HandleDevice(void *pvInstance, unsigned long ulRequest, + void *pvRequestData); + +//***************************************************************************** +// +// The device information structure for the USB HID devices. +// +//***************************************************************************** +tDeviceInfo g_sHIDDeviceInfo = +{ + // + // Device event handler callbacks. + // + { + HandleGetDescriptor, // GetDescriptor + HandleRequest, // RequestHandler + 0, // InterfaceChange + HandleConfigChange, // ConfigChange + HandleEP0DataReceived, // DataReceived + HandleEP0DataSent, // DataSentCallback + HandleReset, // ResetHandler + HandleSuspend, // SuspendHandler + HandleResume, // ResumeHandler + HandleDisconnect, // DisconnectHandler + HandleEndpoints, // EndpointHandler + HandleDevice // Device handler. + }, + g_pHIDDeviceDescriptor, + g_pHIDConfigDescriptors, + 0, // Will be completed during USBDHIDInit(). + 0, // Will be completed during USBDHIDInit(). + &g_sUSBDefaultFIFOConfig +}; + +//***************************************************************************** +// +// Set or clear deferred operation flags in an "atomic" manner. +// +// \param pusDeferredOp points to the flags variable which is to be modified. +// \param usBit indicates which bit number is to be set or cleared. +// \param bSet indicates the state that the flag must be set to. If \b true, +// the flag is set, if \b false, the flag is cleared. +// +// This function safely sets or clears a bit in a flag variable. The operation +// makes use of bitbanding to ensure that the operation is atomic (no read- +// modify-write is required). +// +// \return None. +// +//***************************************************************************** +static void +SetDeferredOpFlag(volatile unsigned short *pusDeferredOp, + unsigned short usBit, tBoolean bSet) +{ + // + // Set the flag bit to 1 or 0 using a bitband access. + // + HWREGBITH(pusDeferredOp, usBit) = bSet ? 1 : 0; +} + +//***************************************************************************** +// +// This function is called to clear the counter used to keep track of the time +// elapsed since a given report was last sent. +// +// \param psDevice points to the HID device structure whose report timer is to +// be cleared. +// \param ucReportID is the first byte of the report to be sent. If this +// device offers more than one input report, this value is used to find the +// relevant report timer structure in the psDevice structure. +// +// \return None. +// +//***************************************************************************** +static void +ClearReportTimer(const tUSBDHIDDevice *psDevice, unsigned char ucReportID) +{ + unsigned long ulLoop; + + if(psDevice->ucNumInputReports > 1) + { + // + // We have more than 1 input report so the report must begin with a + // byte containing the report ID. Scan the table we were provided + // when the device was initialized to find the entry for this report. + // + for(ulLoop = 0; ulLoop < psDevice->ucNumInputReports; ulLoop++) + { + if(psDevice->psReportIdle[ulLoop].ucReportID == ucReportID) + { + break; + } + } + } + else + { + ulLoop = 0; + } + + // + // If we drop out of the loop with an index less than ucNumInputReports, + // we found the relevant report so clear its timer. + // + if(ulLoop < psDevice->ucNumInputReports) + { + psDevice->psReportIdle[ulLoop].ulTimeSinceReportmS = 0; + } +} + +//***************************************************************************** +// +// This function is called to clear the idle period timers for each input +// report supported by the device. +// +// \param psDevice points to the HID device structure whose timers are to be +// cleared. +// \param ulTimemS is the elapsed time in milliseconds since the last call +// to this function. +// +// \return None. +// +//***************************************************************************** +static void +ClearIdleTimers(const tUSBDHIDDevice *psDevice) +{ + unsigned long ulLoop; + + // + // Clear the "time till next report" counters for each input report. + // + for(ulLoop = 0; ulLoop < psDevice->ucNumInputReports; ulLoop++) + { + psDevice->psReportIdle[ulLoop].usTimeTillNextmS = + psDevice->psReportIdle[ulLoop].ucDuration4mS * 4; + } +} + +//***************************************************************************** +// +// This function is called periodically to allow us to process the report idle +// timers. +// +// \param psDevice points to the HID device structure whose timers are to be +// updated. +// \param ulElapsedmS indicates the number of milliseconds that have elapsed +// since the last call to this function. +// +// \return None. +// +//***************************************************************************** +static void +ProcessIdleTimers(const tUSBDHIDDevice *psDevice, unsigned long ulElapsedmS) +{ + unsigned long ulLoop; + unsigned long ulSizeReport; + void *pvReport; + tHIDInstance *psInst; + tBoolean bDeferred; + + // + // Get our instance data pointer + // + psInst = ((tUSBDHIDDevice *)psDevice)->psPrivateHIDData; + + // + // We have not had to defer any report transmissions yet. + // + bDeferred = false; + + // + // Look at each of the input report idle timers in turn. + // + for(ulLoop = 0; ulLoop < psDevice->ucNumInputReports; ulLoop++) + { + // + // Update the time since the last report was sent. + // + psDevice->psReportIdle[ulLoop].ulTimeSinceReportmS += ulElapsedmS; + + // + // Is this timer running? + // + if(psDevice->psReportIdle[ulLoop].ucDuration4mS) + { + // + // Yes - is it about to expire? + // + if(psDevice->psReportIdle[ulLoop].usTimeTillNextmS <= ulElapsedmS) + { + // + // The timer is about to expire. Can we send a report right + // now? + // + if((psInst->eHIDTxState == HID_STATE_IDLE) && + (psInst->bSendInProgress == false)) + { + // + // We can send a report so send a message to the + // application to retrieve its latest report for + // transmission to the host. + // + ulSizeReport = psDevice->pfnRxCallback( + psDevice->pvRxCBData, + USBD_HID_EVENT_IDLE_TIMEOUT, + psDevice->psReportIdle[ulLoop].ucReportID, + &pvReport); + + // + // Schedule the report for transmission. + // + USBDHIDReportWrite((void *)psDevice, pvReport, + ulSizeReport, true); + + // + // Reload the timer for the next period. + // + psDevice->psReportIdle[ulLoop].usTimeTillNextmS = + psDevice->psReportIdle[ulLoop].ucDuration4mS * 4; + } + else + { + // + // We can't send the report straight away so flag it for + // transmission as soon as the previous transmission ends. + // + psDevice->psReportIdle[ulLoop].usTimeTillNextmS = 0; + bDeferred = true; + } + } + else + { + // + // The timer is not about to expire. Update the time till the + // next report transmission. + // + psDevice->psReportIdle[ulLoop].usTimeTillNextmS -= ulElapsedmS; + } + } + } + + // + // If we had to defer transmission of any report, remember this so that we + // will process it as soon as possible. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, + HID_DO_SEND_IDLE_REPORT, bDeferred); +} + +static void +SetIdleTimeout(const tUSBDHIDDevice *psDevice, unsigned char ucReportID, + unsigned char ucTimeout4mS) +{ + unsigned long ulLoop; + tBoolean bReportNeeded; + tHIDReportIdle *psIdle; + + // + // Remember that we have not found any report that needs to be sent + // immediately. + // + bReportNeeded = false; + + // + // Search through all the input reports looking for ones that fit the + // requirements. + // + for(ulLoop = 0; ulLoop < psDevice->ucNumInputReports; ulLoop++) + { + psIdle = &psDevice->psReportIdle[ulLoop]; + + // + // If the report ID passed matches the report ID in the idle timer + // control structure or we were passed a report ID of zero, which + // indicates that all timers are to be set... + // + if(!ucReportID || (ucReportID == psIdle->ucReportID)) + { + // + // Save the new duration for the idle timer. + // + psIdle->ucDuration4mS = ucTimeout4mS; + + // + // Are we enabling the idle timer? If so, fix up the time until it + // needs to fire. + // + if(ucTimeout4mS) + { + // + // Determine what the timeout is for this report given the time + // since the last report of this type was sent. + // + if(psIdle->ulTimeSinceReportmS >= + ((unsigned long)ucTimeout4mS * 4)) + { + psIdle->usTimeTillNextmS = 0; + bReportNeeded = true; + } + else + { + psIdle->usTimeTillNextmS = + (((unsigned short)ucTimeout4mS * 4) - + psIdle->ulTimeSinceReportmS); + } + } + } + } + + // + // If we get to here and bReportNeeded is true, this means we need to + // send back at least one of the input reports as soon as possible. Try + // to do this immediately. + // + if(bReportNeeded) + { + ProcessIdleTimers(psDevice, 0); + } +} + +//***************************************************************************** +// +// Find the idle timeout for a given HID input report. +// +// \param psDevice points to the HID device whose report idle timeout is to be +// found. +// \param ucReportID identifies the report whose timeout is requested. If 0, +// the timeout for the first report is returns, regardless of its ID (or +// whether it has one). +// +// This function returns the current idle timeout for a given HID input report. +// The value returned is expressed in terms of 4mS intervals. Convert to +// milliseconds by multiplying by 4. If the return value is 0, this indicates +// that an infinite timeout is currently set and the device will not send the +// report unless a state change occurs. +// +// \return Returns the current idle timeout for the given report. +// +//***************************************************************************** +static unsigned long +GetIdleTimeout(const tUSBDHIDDevice *psDevice, unsigned char ucReportID) +{ + unsigned long ulLoop; + tHIDReportIdle *psIdle; + + // + // Search through all the input reports looking for ones that fit the + // requirements. + // + for(ulLoop = 0; ulLoop < psDevice->ucNumInputReports; ulLoop++) + { + psIdle = &psDevice->psReportIdle[ulLoop]; + + // + // If the report ID passed matches the report ID in the idle timer + // control structure or we were passed a report ID of zero, which + // indicates that all timers are to be set... + // + if(!ucReportID || (ucReportID == psIdle->ucReportID)) + { + // + // We found a report matching the required ID or we were not passed + // an ID and we are looking at the first report information. + // + return((unsigned long)psIdle->ucDuration4mS); + } + } + + // + // If we drop out, the report couldn't be found so we need to indicate + // an error. + // + return(HID_NOT_FOUND); +} + +//***************************************************************************** +// +// Find the n-th HID class descriptor of a given type in the client-provided +// descriptor table. +// +// \param psDevice points to the HID device which is to be searched for the +// required class descriptor. +// \param ucType is the type of class descriptor being requested. This will +// be either USB_HID_DTYPE_REPORT or USB_HID_DTYPE_PHYSICAL. +// \param ulIndex is the zero-based index of the descriptor that is being +// requested. +// +// This function parses the supplied HID descriptor to find the index into the +// sClassDescriptor array that corresponds to the requested descriptor. If +// a descriptor with the requested index does not exist, HID_NOT_FOUND will be +// returned unless the request is for a physical descriptor and at least one +// such descriptor exists. In this case, the index returned will be for the +// last physical descriptor (as required by the HID spec 7.1.1). +// +// \return Returns the index of the descriptor within the sClassDescriptor +// of the tHIDDevice structure if found or HID_NOT_FOUND otherwise. +// +//***************************************************************************** +static unsigned long +FindHIDDescriptor(const tUSBDHIDDevice *psDevice, unsigned char ucType, + unsigned long ulIndex, unsigned long *pulLen) +{ + tBoolean bFoundType; + unsigned long ulLoop; + unsigned long ulCount; + unsigned long ulLastFound; + const tHIDClassDescriptorInfo *psDesc; + + // + // Remember that we have not found any descriptor with a matching type yet. + // + bFoundType = false; + ulCount = 0; + ulLastFound = 0; + + // + // Walk through all the class descriptors looking for the one which + // matches the requested index and type. + // + for(ulLoop = 0; ulLoop < psDevice->psHIDDescriptor->bNumDescriptors; + ulLoop++) + { + psDesc = &(psDevice->psHIDDescriptor->sClassDescriptor[ulLoop]); + if(psDesc->bDescriptorType == ucType) + { + // + // We found a descriptor of the correct type. Is this the + // correct index? + // + bFoundType = true; + + // + // Is this the descriptor we are looking for? + // + if(ulCount == ulIndex) + { + // + // Yes - we found it so return the index and size to the + // caller. + // + *pulLen = (unsigned long)psDesc->wDescriptorLength; + return(ulLoop); + } + else + { + // + // Update our count and keep looking. Remember where we were + // when we found this descriptor in case we need to return the + // last physical descriptor. + // + ulCount++; + ulLastFound = ulLoop; + } + } + } + + // + // If we drop out, we didn't find the requested descriptor. Now handle + // the special case of a physical descriptor - if we found any physical + // descriptors, return the last one. + // + if((ucType == USB_HID_DTYPE_PHYSICAL) && bFoundType) + { + // + // Get the length of the last descriptor we found. + // + psDesc = &(psDevice->psHIDDescriptor->sClassDescriptor[ulLastFound]); + *pulLen = (unsigned long)psDesc->wDescriptorLength; + + // + // Return the index to the caller. + // + return(ulLastFound); + } + else + { + // + // We couldn't find the descriptor so return an appropriate error. + // + return(HID_NOT_FOUND); + } +} + +//***************************************************************************** +// +// Schedule transmission of the next packet forming part of an input report. +// +// \param psInst points to the device instance whose input report is to be +// sent. +// +// This function is called to transmit the next packet of an input report +// passed to the driver via a call to USBDHIDReportWrite. If any data remains +// to be sent, a USB packet is written to the FIFO and scheduled for +// transmission to the host. The function ensures that reports are sent as +// a sequence of full packets followed by either a single short packet or a +// packet with no data to indicate the end of the transaction. +// +//***************************************************************************** +static long +ScheduleReportTransmission(tHIDInstance *psInst) +{ + unsigned long ulNumBytes; + unsigned char *pucData; + long lRetcode; + + // + // Set the number of bytes to send this iteration. + // + ulNumBytes = (unsigned long)(psInst->usInReportSize - + psInst->usInReportIndex); + + // + // Limit individual transfers to the maximum packet size for the endpoint. + // + if(ulNumBytes > INT_IN_EP_MAX_SIZE) + { + ulNumBytes = INT_IN_EP_MAX_SIZE; + } + + // + // Where are we sending this data from? + // + pucData = psInst->pucInReportData + psInst->usInReportIndex; + + // + // Put the data in the correct FIFO. + // + lRetcode = MAP_USBEndpointDataPut(psInst->ulUSBBase, psInst->ucINEndpoint, + pucData, ulNumBytes); + + if(lRetcode != -1) + { + // + // Update the count and index ready for the next time round. + // + psInst->usInReportIndex += ulNumBytes; + + // + // Send out the current data. + // + lRetcode = MAP_USBEndpointDataSend(psInst->ulUSBBase, + psInst->ucINEndpoint, + USB_TRANS_IN); + } + + // + // Tell the caller how we got on. + // + return(lRetcode); +} + +//***************************************************************************** +// +// Receives notifications related to data received from the host. +// +// \param psDevice is the device instance whose endpoint is to be processed. +// \param ulStatus is the USB interrupt status that caused this function to +// be called. +// +// This function is called from HandleEndpoints for all interrupts signaling +// the arrival of data on the interrupt OUT endpoint (in other words, whenever +// the host has sent us a packet of data). We inform the client that a packet +// is available and, on return, check to see if the packet has been read. If +// not, we schedule another notification to the client for a later time. +// +// \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +static tBoolean +ProcessDataFromHost(const tUSBDHIDDevice *psDevice, unsigned long ulStatus) +{ + unsigned long ulEPStatus; + unsigned long ulSize; + tHIDInstance *psInst; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateHIDData; + + // + // Get the endpoint status to see why we were called. + // + ulEPStatus = MAP_USBEndpointStatus(USB0_BASE, psInst->ucOUTEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(USB0_BASE, psInst->ucOUTEndpoint, ulEPStatus); + + // + // Has a packet been received? + // + if(ulEPStatus & USB_DEV_RX_PKT_RDY) + { + // + // Set the flag we use to indicate that a packet read is pending. This + // will be cleared if the packet is read. If the client doesn't read + // the packet in the context of the USB_EVENT_RX_AVAILABLE callback, + // the event will be signaled later during tick processing. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, HID_DO_PACKET_RX, true); + + // + // How big is the packet we've just been sent? + // + ulSize = MAP_USBEndpointDataAvail(psInst->ulUSBBase, + psInst->ucOUTEndpoint); + + // + // The receive channel is not blocked so let the caller know + // that a packet is waiting. The parameters are set to indicate + // that the packet has not been read from the hardware FIFO yet. + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, + USB_EVENT_RX_AVAILABLE, ulSize, + (void *)0); + } + else + { + // + // No packet was received. Some error must have been reported. Check + // and pass this on to the client if necessary. + // + if(ulEPStatus & USB_RX_ERROR_FLAGS) + { + // + // This is an error we report to the client so... + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, + USB_EVENT_ERROR, + (ulEPStatus & USB_RX_ERROR_FLAGS), + (void *)0); + } + return(false); + } + + return(true); +} + +//***************************************************************************** +// +// Receives notifications related to data sent to the host. +// +// \param psDevice is the device instance whose endpoint is to be processed. +// \param ulStatus is the USB interrupt status that caused this function to +// be called. +// +// This function is called from HandleEndpoints for all interrupts originating +// from the interrupt IN endpoint (in other words, whenever data has been +// transmitted to the USB host). We examine the cause of the interrupt and, +// if due to completion of a transmission, notify the client. +// +// \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +static tBoolean +ProcessDataToHost(const tUSBDHIDDevice *psDevice, unsigned long ulStatus) +{ + tHIDInstance *psInst; + unsigned long ulEPStatus; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateHIDData; + + // + // Get the endpoint status to see why we were called. + // + ulEPStatus = MAP_USBEndpointStatus(psInst->ulUSBBase, psInst->ucINEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(psInst->ulUSBBase, psInst->ucINEndpoint, + ulEPStatus); + + // + // Our last packet was transmitted successfully. Is there any more data to + // send or have we finished sending the whole report? We know we finished + // if the usInReportIndex has reached the usInReportSize value. + // + if(psInst->usInReportSize == psInst->usInReportIndex) + { + // + // We finished sending the last report so are idle once again. + // + psInst->eHIDTxState = HID_STATE_IDLE; + + // + // Notify the client that the report transmission completed. + // + psDevice->pfnTxCallback(psDevice->pvTxCBData, USB_EVENT_TX_COMPLETE, + psInst->usInReportSize, (void *)0); + + // + // Do we have any reports to send as a result of idle timer timeouts? + // + if(psInst->usDeferredOpFlags & (1 << HID_DO_SEND_IDLE_REPORT)) + { + // + // Yes - send reports for any timers that expired recently. + // + ProcessIdleTimers(psDevice, 0); + } + } + else + { + // + // There must be more data or a zero length packet waiting to be sent + // so go ahead and do this. + // + ScheduleReportTransmission(psInst); + } + + return(true); +} + +//***************************************************************************** +// +// Called by the USB stack for any activity involving one of our endpoints +// other than EP0. This function is a fan out that merely directs the call to +// the correct handler depending upon the endpoint and transaction direction +// signaled in ulStatus. +// +//***************************************************************************** +static void +HandleEndpoints(void *pvInstance, unsigned long ulStatus) +{ + const tUSBDHIDDevice *psHIDInst; + tHIDInstance *psInst; + + ASSERT(pvInstance != 0); + + // + // Determine if the serial device is in single or composite mode because + // the meaning of ulIndex is different in both cases. + // + psHIDInst = (const tUSBDHIDDevice *)pvInstance; + psInst = psHIDInst->psPrivateHIDData; + + // + // Handler for the interrupt OUT data endpoint. + // + if(ulStatus & (0x10000 << USB_EP_TO_INDEX(psInst->ucOUTEndpoint))) + { + // + // Data is being sent to us from the host. + // + ProcessDataFromHost(pvInstance, ulStatus); + } + + // + // Handler for the interrupt IN data endpoint. + // + if(ulStatus & (1 << USB_EP_TO_INDEX(psInst->ucINEndpoint))) + { + ProcessDataToHost(pvInstance, ulStatus); + } +} + +//***************************************************************************** +// +// Called by the USB stack whenever a configuration change occurs. +// +//***************************************************************************** +static void +HandleConfigChange(void *pvInstance, unsigned long ulInfo) +{ + tHIDInstance *psInst; + const tUSBDHIDDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = pvInstance; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateHIDData; + + // + // Set all our endpoints to idle state. + // + psInst->eHIDRxState = HID_STATE_IDLE; + psInst->eHIDTxState = HID_STATE_IDLE; + + // + // If we are not currently connected let the client know we are open for + // business. + // + if(!psInst->bConnected) + { + // + // Pass the connected event to the client. + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, USB_EVENT_CONNECTED, 0, + (void *)0); + } + + // + // Clear the idle timers for each input report. + // + ClearIdleTimers(psDevice); + + // + // Remember that we are connected. + // + psInst->bConnected = true; +} + +//***************************************************************************** +// +// Device instance specific handler. +// +//***************************************************************************** +static void +HandleDevice(void *pvInstance, unsigned long ulRequest, void *pvRequestData) +{ + tHIDInstance *psInst; + unsigned char *pucData; + + // + // Create the serial instance data. + // + psInst = ((tUSBDHIDDevice *)pvInstance)->psPrivateHIDData; + + // + // Create the char array used by the events supported by the USB CDC + // serial class. + // + pucData = (unsigned char *)pvRequestData; + + switch(ulRequest) + { + // + // This was an interface change event. + // + case USB_EVENT_COMP_IFACE_CHANGE: + { + psInst->ucInterface = pucData[1]; + break; + } + + // + // This was an endpoint change event. + // + case USB_EVENT_COMP_EP_CHANGE: + { + // + // Determine if this is an IN or OUT endpoint that has changed. + // + if(pucData[0] & USB_EP_DESC_IN) + { + psInst->ucINEndpoint = + INDEX_TO_USB_EP((pucData[1] & 0x7f)); + } + else + { + // + // Extract the new endpoint number. + // + psInst->ucOUTEndpoint = + INDEX_TO_USB_EP(pucData[1] & 0x7f); + } + break; + } + default: + { + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device is +// disconnected from the host. +// +//***************************************************************************** +static void +HandleDisconnect(void *pvInstance) +{ + const tUSBDHIDDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDHIDDevice *)pvInstance; + + // + // If we are not currently connected so let the client know we are open + // for business. + // + if(psDevice->psPrivateHIDData->bConnected) + { + // + // Pass the disconnected event to the client. + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, USB_EVENT_DISCONNECTED, + 0, (void *)0); + } + + // + // Remember that we are no longer connected. + // + psDevice->psPrivateHIDData->bConnected = false; +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever a request for a +// non-standard descriptor is received. +// +// \param pvInstance is the instance data for this request. +// \param pUSBRequest points to the request received. +// +// This call parses the provided request structure and determines which +// descriptor is being requested. Assuming the descriptor can be found, it is +// scheduled for transmission via endpoint zero. If the descriptor cannot be +// found, the endpoint is stalled to indicate an error to the host. +// +//***************************************************************************** +static void +HandleGetDescriptor(void *pvInstance, tUSBRequest *pUSBRequest) +{ + unsigned long ulSize; + unsigned long ulDesc; + const tUSBDHIDDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Which device are we dealing with? + // + psDevice = pvInstance; + + // + // Which type of class descriptor are we being asked for? + // + switch(pUSBRequest->wValue >> 8) + { + // + // This is a request for a HID report or physical descriptor. + // + case USB_HID_DTYPE_REPORT: + case USB_HID_DTYPE_PHYSICAL: + { + // + // Find the index to the descriptor that is being queried. + // + ulSize = 0; + ulDesc = FindHIDDescriptor(psDevice, pUSBRequest->wValue >> 8, + pUSBRequest->wValue & 0xFF, + &ulSize); + + // + // Did we find the descriptor? + // + if(ulDesc == HID_NOT_FOUND) + { + // + // No - stall the endpoint and return. + // + USBDCDStallEP0(0); + return; + } + + // + // If there is more data to send than the host requested then just + // send the requested amount of data. + // + if(ulSize > pUSBRequest->wLength) + { + ulSize = pUSBRequest->wLength; + } + + // + // Send the data via endpoint 0. + // + USBDCDSendDataEP0(0, + (unsigned char *)psDevice->ppClassDescriptors[ulDesc], ulSize); + + break; + } + + // + // This is a request for the HID descriptor (as found in the + // configuration descriptor following the relevant interface). + // + case USB_HID_DTYPE_HID: + { + // + // How big is the HID descriptor? + // + ulSize = (unsigned long)psDevice->psHIDDescriptor->bLength; + + // + // If there is more data to send than the host requested then just + // send the requested amount of data. + // + if(ulSize > pUSBRequest->wLength) + { + ulSize = pUSBRequest->wLength; + } + + // + // Send the data via endpoint 0. + // + USBDCDSendDataEP0(0, (unsigned char *)psDevice->psHIDDescriptor, + ulSize); + break; + } + + // + // This was an unknown request so stall. + // + default: + { + USBDCDStallEP0(0); + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever a non-standard +// request is received. +// +// \param pvInstance is the instance data for this HID device. +// \param pUSBRequest points to the request received. +// +// This call parses the provided request structure. Assuming the request is +// understood, it is handled and any required response generated. If the +// request cannot be handled by this device class, endpoint zero is stalled to +// indicate an error to the host. +// +//***************************************************************************** +static void +HandleRequest(void *pvInstance, tUSBRequest *pUSBRequest) +{ + tHIDInstance *psInst; + unsigned char ucProtocol; + const tUSBDHIDDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Which device are we dealing with? + // + psDevice = pvInstance; + + // + // Get a pointer to our instance data. + // + psInst = psDevice->psPrivateHIDData; + + // + // Make sure the request was for this interface. + // + if(pUSBRequest->wIndex != psInst->ucInterface) + { + return; + } + + // + // 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_SET_REPORT: + { + // + // Ask the application for a buffer large enough to hold the + // report we are to be sent. + // + psInst->usOutReportSize = pUSBRequest->wLength; + psInst->pucOutReportData = + (unsigned char *)psDevice->pfnRxCallback( + psDevice->pvRxCBData, + USBD_HID_EVENT_GET_REPORT_BUFFER, + pUSBRequest->wValue, + (void *)(unsigned long)(pUSBRequest->wLength)); + + // + // Did the client provide us a buffer? + // + if(!psInst->pucOutReportData) + { + // + // The application couldn't provide us a buffer so stall the + // request. + // + USBDCDStallEP0(0); + } + else + { + // + // The client provided us a buffer to read the report into + // so request the data from the host. + // + + // + // Set the state to indicate we are waiting for data. + // + psInst->eHIDRxState = HID_STATE_WAIT_DATA; + + // + // Now read the payload of the request. We handle the actual + // operation in the data callback once this data is received. + // + USBDCDRequestDataEP0(0, psInst->pucOutReportData, + (unsigned long)pUSBRequest->wLength); + + // + // Need to ACK the data on end point 0 in this case. Do this + // after requesting the data to prevent race conditions that + // occur if you acknowledge before setting up to receive the + // request data. + // + MAP_USBDevEndpointDataAck(psInst->ulUSBBase, USB_EP_0, false); + } + + break; + } + + // + // A Get Report request is used by the host to poll a device for its + // current state. + // + case USBREQ_GET_REPORT: + { + unsigned long ulSize; + unsigned char *pucReport; + + // + // Get the latest report from the application. + // + ulSize = psDevice->pfnRxCallback(psDevice->pvRxCBData, + USBD_HID_EVENT_GET_REPORT, + pUSBRequest->wValue, &pucReport); + + // + // Need to ACK the data on end point 0 in this case. + // + MAP_USBDevEndpointDataAck(psInst->ulUSBBase, USB_EP_0, true); + + // + // ..then send back the requested report. + // + psInst->bGetRequestPending = true; + USBDCDSendDataEP0(0, pucReport, ulSize); + + break; + } + + // + // A set IDLE request has been made. This indicates to us how often a + // given report should be sent back to the host in the absence of any + // change in state of the device. + // + case USBREQ_SET_IDLE: + { + // + // Set the idle timeout for the requested report(s). + // + SetIdleTimeout(psDevice, pUSBRequest->wValue & 0xFF, + (pUSBRequest->wValue >> 8) & 0xFF); + + // + // Need to ACK the data on end point 0 in this case. + // + MAP_USBDevEndpointDataAck(psInst->ulUSBBase, USB_EP_0, true); + + break; + } + + // + // A get IDLE request has been made. This request queries the current + // idle timeout for a given report. + // + case USBREQ_GET_IDLE: + { + unsigned long ulTimeout; + + // + // Determine the timeout for the requested report. + // + ulTimeout = GetIdleTimeout(psDevice, pUSBRequest->wValue); + + if(ulTimeout != HID_NOT_FOUND) + { + // + // Need to ACK the data on end point 0 in this case. + // + MAP_USBDevEndpointDataAck(psInst->ulUSBBase, USB_EP_0, true); + + // + // Send our response to the host. + // + USBDCDSendDataEP0(0, (unsigned char *)&ulTimeout, 1); + } + else + { + // + // The report ID was not found so stall the endpoint. + // + USBDCDStallEP0(0); + } + break; + } + + // + // Set either boot or report protocol for reports sent from the device. + // This is only supported by devices in the boot subclass. + // + case USBREQ_SET_PROTOCOL: + { + if(psDevice->ucSubclass == USB_HID_SCLASS_BOOT) + { + // + // We need to ACK the data on end point 0 in this case. + // + MAP_USBDevEndpointDataAck(psInst->ulUSBBase, USB_EP_0, true); + + // + // We are a boot subclass device so pass this on to the + // application. + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, + USBD_HID_EVENT_SET_PROTOCOL, + pUSBRequest->wValue, + (void *)0); + } + else + { + // + // This is not a boot subclass device so stall the endpoint to + // show that we don't support this request. + // + USBDCDStallEP0(0); + } + break; + } + + // + // Inform the host of the protocol, boot or report, that is currently + // in use. This is only supported by devices in the boot subclass. + // + case USBREQ_GET_PROTOCOL: + { + if(psDevice->ucSubclass == USB_HID_SCLASS_BOOT) + { + // + // We need to ACK the data on end point 0 in this case. + // + MAP_USBDevEndpointDataAck(psInst->ulUSBBase, USB_EP_0, true); + + // + // We are a boot subclass device so pass this on to the + // application callback to get the answer. + // + ucProtocol = (unsigned char)psDevice->pfnRxCallback( + psDevice->pvRxCBData, USBD_HID_EVENT_GET_PROTOCOL, 0, + (void *)0); + + // + // Send our response to the host. + // + USBDCDSendDataEP0(0, (unsigned char *)&ucProtocol, 1); + } + else + { + // + // This is not a boot subclass device so stall the endpoint to + // show that we don't support this request. + // + USBDCDStallEP0(0); + } + break; + } + + // + // This request was not recognized so stall. + // + default: + { + USBDCDStallEP0(0); + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the data requested +// on endpoint zero is received. +// +//***************************************************************************** +static void +HandleEP0DataReceived(void *pvInstance, unsigned long ulDataSize) +{ + tHIDInstance *psInst; + const tUSBDHIDDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Which device are we dealing with? + // + psDevice = pvInstance; + + // + // If we were not passed any data, just return. + // + if(ulDataSize == 0) + { + return; + } + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateHIDData; + + // + // Make sure we are actually expecting something. + // + if(psInst->eHIDRxState != HID_STATE_WAIT_DATA) + { + return; + } + + // + // Change the endpoint state back to idle now that we have been passed + // the data we were waiting for. + // + psInst->eHIDRxState = HID_STATE_IDLE; + + // + // The only things we ever request via endpoint zero are reports sent to + // us via a Set_Report request. Pass the newly received report on to + // the client. + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, USBD_HID_EVENT_SET_REPORT, + psInst->usOutReportSize, + psInst->pucOutReportData); +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the data sent on +// endpoint zero is received and acknowledged by the host. +// +//***************************************************************************** +static void +HandleEP0DataSent(void *pvInstance, unsigned long ulInfo) +{ + tHIDInstance *psInst; + const tUSBDHIDDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Which device are we dealing with? + // + psDevice = pvInstance; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateHIDData; + + // + // If we just sent a report in response to a Get_Report request, send an + // event to the application telling it that the transmission completed. + // + if(psInst->bGetRequestPending) + { + // + // Clear the flag now that we are sending the application callback. + // + psInst->bGetRequestPending = false; + + psDevice->pfnRxCallback(psDevice->pvRxCBData, + USBD_HID_EVENT_REPORT_SENT, 0, (void *)0); + } + + return; +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device is +// reset. If we are currently connected, send a disconnect event at this +// point. +// +//***************************************************************************** +static void +HandleReset(void *pvInstance) +{ + ASSERT(pvInstance != 0); + + // + // Merely call the disconnect handler. This causes a disconnect message to + // be sent to the client if we think we are currently connected. + // + HandleDisconnect(pvInstance); +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the bus is put into +// suspend state. +// +//***************************************************************************** +static void +HandleSuspend(void *pvInstance) +{ + const tUSBDHIDDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDHIDDevice *)pvInstance; + + // + // Pass the event on to the client. + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, USB_EVENT_SUSPEND, 0, + (void *)0); +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the bus is taken +// out of suspend state. +// +//***************************************************************************** +static void +HandleResume(void *pvInstance) +{ + const tUSBDHIDDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDHIDDevice *)pvInstance; + + // + // Pass the event on to the client. + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, + USB_EVENT_RESUME, 0, (void *)0); +} + +//***************************************************************************** +// +// This function is called periodically and provides us with a time reference +// and method of implementing delayed or time-dependent operations. +// +// \param pvInstance is the instance data for this request. +// \param ulTimemS is the elapsed time in milliseconds since the last call +// to this function. +// +// \return None. +// +//***************************************************************************** +static void +HIDTickHandler(void *pvInstance, unsigned long ulTimemS) +{ + tHIDInstance *psInst; + unsigned long ulSize; + const tUSBDHIDDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDHIDDevice *)pvInstance; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateHIDData; + + // + // If we are connected, process our idle timers. + // + if(psInst->bConnected) + { + ProcessIdleTimers(psDevice, ulTimemS); + } + + // + // Do we have a deferred receive waiting + // + if(psInst->usDeferredOpFlags & (1 << HID_DO_PACKET_RX)) + { + // + // Yes - how big is the waiting packet? + // + ulSize = MAP_USBEndpointDataAvail(USB0_BASE, psInst->ucOUTEndpoint); + + // + // Tell the client that there is a packet waiting for it. + // + psDevice->pfnRxCallback(psDevice->pvRxCBData, + USB_EVENT_RX_AVAILABLE, ulSize, (void *)0); + } + + return; +} + +//***************************************************************************** +// +//! Initializes HID device operation for a given USB controller. +//! +//! \param ulIndex is the index of the USB controller which is to be +//! initialized for HID device operation. +//! \param psDevice points to a structure containing parameters customizing +//! the operation of the HID device. +//! +//! An application wishing to offer a USB HID interface to a host system +//! must call this function to initialize the USB controller and attach the +//! device to the USB bus. This function performs all required USB +//! initialization. +//! +//! On successful completion, this function will return the \e psDevice pointer +//! passed to it. This must be passed on all future calls from the application +//! to the HID device class driver. +//! +//! The USB HID device class API offers the application a report-based transmit +//! interface for Input reports. Output reports may be received via the +//! control endpoint or via a dedicated Interrupt OUT endpoint. If using the +//! dedicated endpoint, report data is delivered to the application packet-by- +//! packet. If the application uses reports longer than 64 bytes and would +//! rather receive full reports, it may use a USB buffer above the receive +//! channel to allow full reports to be read. +//! +//! Transmit Operation: +//! +//! Calls to USBDHIDReportWrite() pass complete reports to the driver for +//! transmission. These will be transmitted to the host using as many USB +//! packets as are necessary to complete the transmission. +//! +//! Once a full Input report has been acknowledged by the USB host, a +//! USB_EVENT_TX_COMPLETE event is sent to the application transmit callback to +//! inform it that another report may be transmitted. +//! +//! Receive Operation (when using a dedicated interrupt OUT endpoint): +//! +//! An incoming USB data packet will result in a call to the application +//! callback with event USB_EVENT_RX_AVAILABLE. The application must then +//! call USBDHIDPacketRead(), passing a buffer capable of holding the received +//! packet. The size of the packet may be determined by calling function +//! USBDHIDRxPacketAvailable() prior to reading the packet. +//! +//! Receive Operation (when not using a dedicated OUT endpoint): +//! +//! If no dedicated OUT endpoint is used, Output and Feature reports are sent +//! from the host using the control endpoint, endpoint zero. When such a +//! report is received, USBD_HID_EVENT_GET_REPORT_BUFFER is sent to the +//! application which must respond with a buffer large enough to hold the +//! report. The device class driver will then copy the received report into +//! the supplied buffer before sending USBD_HID_EVENT_SET_REPORT to indicate +//! that the report is now available. +//! +//! \note The application must not make any calls to the low level USB device +//! interface if interacting with USB via the USB HID device class API. Doing +//! so will cause unpredictable (though almost certainly unpleasant) behavior. +//! +//! \return Returns NULL on failure or the \e psDevice pointer on success. +// +//***************************************************************************** +void * +USBDHIDInit(unsigned long ulIndex, const tUSBDHIDDevice *psDevice) +{ + // + // Check parameter validity. + // + ASSERT(ulIndex == 0); + ASSERT(psDevice); + ASSERT(psDevice->ppStringDescriptors); + ASSERT(psDevice->psPrivateHIDData); + ASSERT(psDevice->pfnRxCallback); + ASSERT(psDevice->pfnTxCallback); + ASSERT(psDevice->ppClassDescriptors); + ASSERT(psDevice->psHIDDescriptor); + ASSERT((psDevice->ucNumInputReports == 0) || psDevice->psReportIdle); + + USBDHIDCompositeInit(ulIndex, psDevice); + + // + // All is well so now pass the descriptors to the lower layer and put + // the HID device on the bus. + // + USBDCDInit(ulIndex, psDevice->psPrivateHIDData->psDevInfo); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psDevice); +} + +//***************************************************************************** +// +//! Initializes HID device operation for a given USB controller. +//! +//! \param ulIndex is the index of the USB controller which is to be +//! initialized for HID device operation. +//! \param psDevice points to a structure containing parameters customizing +//! the operation of the HID device. +//! +//! +//! \return Returns NULL on failure or the \e psDevice pointer on success. +// +//***************************************************************************** +void * +USBDHIDCompositeInit(unsigned long ulIndex, const tUSBDHIDDevice *psDevice) +{ + tHIDInstance *psInst; + tDeviceDescriptor *psDevDesc; + tInterfaceDescriptor *psDevIf; + + // + // Check parameter validity. + // + ASSERT(ulIndex == 0); + ASSERT(psDevice); + ASSERT(psDevice->ppStringDescriptors); + ASSERT(psDevice->psPrivateHIDData); + ASSERT(psDevice->pfnRxCallback); + ASSERT(psDevice->pfnTxCallback); + ASSERT(psDevice->ppClassDescriptors); + ASSERT(psDevice->psHIDDescriptor); + ASSERT((psDevice->ucNumInputReports == 0) || psDevice->psReportIdle); + + // + // Initialize the workspace in the passed instance structure. + // + psInst = psDevice->psPrivateHIDData; + psInst->psConfDescriptor = (tConfigDescriptor *)g_pHIDDescriptor; + psInst->psDevInfo = &g_sHIDDeviceInfo; + psInst->ulUSBBase = USB0_BASE; + psInst->eHIDRxState = HID_STATE_UNCONFIGURED; + psInst->eHIDTxState = HID_STATE_UNCONFIGURED; + psInst->usDeferredOpFlags = 0; + psInst->bConnected = false; + psInst->bGetRequestPending = false; + psInst->bSendInProgress = false; + psInst->usInReportIndex = 0; + psInst->usInReportSize = 0; + psInst->pucInReportData = (unsigned char *)0; + psInst->usOutReportSize = 0; + psInst->pucOutReportData = (unsigned char *)0; + + // + // Set the default endpoint and interface assignments. + // + psInst->ucINEndpoint = INT_IN_ENDPOINT; + psInst->ucOUTEndpoint = INT_OUT_ENDPOINT; + psInst->ucInterface = 0; + + // + // Fix up the device descriptor with the client-supplied values. + // + psDevDesc = (tDeviceDescriptor *)psInst->psDevInfo->pDeviceDescriptor; + psDevDesc->idVendor = psDevice->usVID; + psDevDesc->idProduct = psDevice->usPID; + + // + // Fix up the configuration descriptor with client-supplied values. + // + psInst->psConfDescriptor->bmAttributes = psDevice->ucPwrAttributes; + psInst->psConfDescriptor->bMaxPower = + (unsigned char)(psDevice->usMaxPowermA / 2); + + // + // Slot the client's HID descriptor into our standard configuration + // descriptor. + // + g_sHIDDescriptorSection.usSize = psDevice->psHIDDescriptor->bLength; + g_sHIDDescriptorSection.pucData = + (unsigned char *)psDevice->psHIDDescriptor; + + // + // Fix up the interface and endpoint descriptors depending upon client + // choices. + // + psDevIf = (tInterfaceDescriptor *)g_pHIDInterface; + psDevIf->bNumEndpoints = psDevice->bUseOutEndpoint ? 2 : 1; + psDevIf->bInterfaceSubClass = psDevice->ucSubclass; + psDevIf->bInterfaceProtocol = psDevice->ucProtocol; + + // + // If necessary, remove the interrupt OUT endpoint from the configuration + // descriptor. + // + if(psDevice->bUseOutEndpoint == false) + { + g_sHIDConfigHeader.ucNumSections = (NUM_HID_SECTIONS - 1); + } + else + { + g_sHIDConfigHeader.ucNumSections = NUM_HID_SECTIONS; + } + + // + // Plug in the client's string table to the device information + // structure. + // + psInst->psDevInfo->ppStringDescriptors = psDevice->ppStringDescriptors; + psInst->psDevInfo->ulNumStringDescriptors + = psDevice->ulNumStringDescriptors; + psInst->psDevInfo->pvInstance = (void *)psDevice; + + // + // Initialize the input report idle timers if any input reports exist. + // + ClearIdleTimers(psDevice); + + // + // Initialize the USB tick module, this will prevent it from being + // initialized later in the call to USBDCDInit(); + // + InternalUSBTickInit(); + + // + // Register our tick handler (this must be done after USBDCDInit). + // + InternalUSBRegisterTickHandler(HIDTickHandler, + (void *)psDevice); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psDevice); +} + +//***************************************************************************** +// +//! Shuts down the HID device. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDHIDInit(). +//! +//! This function terminates HID operation for the instance supplied and +//! removes the device from the USB bus. This function should not be called +//! if the HID device is part of a composite device and instead the +//! USBDCompositeTerm() function should be called for the full composite +//! device. +//! +//! Following this call, the \e pvInstance instance should not me used in any +//! other calls. +//! +//! \return None. +// +//***************************************************************************** +void +USBDHIDTerm(void *pvInstance) +{ + tHIDInstance *psInst; + + ASSERT(pvInstance); + + // + // Get a pointer to our instance data. + // + psInst = ((tUSBDHIDDevice *)pvInstance)->psPrivateHIDData; + + // + // Terminate the requested instance. + // + USBDCDTerm(USB_BASE_TO_INDEX(psInst->ulUSBBase)); + + psInst->ulUSBBase = 0; + psInst->psDevInfo = (tDeviceInfo *)0; + psInst->psConfDescriptor = (tConfigDescriptor *)0; +} + +//***************************************************************************** +// +//! Sets the client-specific pointer parameter for the receive channel +//! callback. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDHIDInit(). +//! \param pvCBData is the pointer that client wishes to be provided on each +//! event sent to the receive channel callback function. +//! +//! The client uses this function to change the callback pointer passed in +//! the first parameter on all callbacks to the \e pfnRxCallback function +//! passed on USBDHIDInit(). +//! +//! If a client wants to make runtime changes in the callback pointer, it must +//! ensure that the pvInstance structure passed to USBDHIDInit() resides in +//! RAM. If this structure is in flash, callback data changes will not be +//! possible. +//! +//! \return Returns the previous callback pointer that was being used for +//! this instance's receive callback. +// +//***************************************************************************** +void * +USBDHIDSetRxCBData(void *pvInstance, void *pvCBData) +{ + void *pvOldValue; + + ASSERT(pvInstance); + + // + // Set the callback data for the receive channel after remembering the + // previous value. + // + pvOldValue = ((tUSBDHIDDevice *)pvInstance)->pvRxCBData; + ((tUSBDHIDDevice *)pvInstance)->pvRxCBData = pvCBData; + + // + // Return the previous callback data value. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Sets the client-specific data pointer for the transmit callback. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDHIDInit(). +//! \param pvCBData is the pointer that client wishes to be provided on each +//! event sent to the transmit channel callback function. +//! +//! The client uses this function to change the callback data pointer passed in +//! the first parameter on all callbacks to the \e pfnTxCallback function +//! passed on USBDHIDInit(). +//! +//! If a client wants to make runtime changes in the callback data, it must +//! ensure that the pvInstance structure passed to USBDHIDInit() resides in +//! RAM. If this structure is in flash, callback data changes will not be +//! possible. +//! +//! \return Returns the previous callback data pointer that was being used for +//! this instance's transmit callback. +// +//***************************************************************************** +void * +USBDHIDSetTxCBData(void *pvInstance, void *pvCBData) +{ + void *pvOldValue; + + ASSERT(pvInstance); + + // + // Set the callback data for the transmit channel after remembering the + // previous value. + // + pvOldValue = ((tUSBDHIDDevice *)pvInstance)->pvTxCBData; + ((tUSBDHIDDevice *)pvInstance)->pvTxCBData = pvCBData; + + // + // Return the previous callback data value. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Transmits a HID device report to the USB host via the HID interrupt IN +//! endpoint. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDHIDInit(). +//! \param pcData points to the first byte of data which is to be transmitted. +//! \param ulLength is the number of bytes of data to transmit. +//! \param bLast is ignored in this implementation. This parameter is required +//! to ensure compatibility with other device class drivers and USB buffers. +//! +//! This function schedules the supplied data for transmission to the USB +//! host in a single USB transaction using as many packets as it takes to send +//! all the data in the report. If no transmission is currently ongoing, +//! the first packet of data is immediately copied to the relevant USB endpoint +//! FIFO for transmission. Whenever all the report data has been acknowledged +//! by the host, a \b USB_EVENT_TX_COMPLETE event will be sent to the +//! application transmit callback indicating that another report can now be +//! transmitted. +//! +//! The caller must ensure that the data pointed to by pucData remains +//! accessible and unaltered until the \b USB_EVENT_TX_COMPLETE is received. +//! +//! \return Returns the number of bytes actually scheduled for transmission. +//! At this level, this will either be the number of bytes passed or 0 to +//! indicate a failure. +// +//***************************************************************************** +unsigned long +USBDHIDReportWrite(void *pvInstance, unsigned char *pcData, + unsigned long ulLength, tBoolean bLast) +{ + tHIDInstance *psInst; + long lRetcode; + + ASSERT(pvInstance); + + // + // Get our instance data pointer + // + psInst = ((tUSBDHIDDevice *)pvInstance)->psPrivateHIDData; + + // + // Set a flag indicating that we are currently in the process of sending + // a packet. + // + psInst->bSendInProgress = true; + + // + // Can we send the data provided? + // + if(psInst->eHIDTxState != HID_STATE_IDLE) + { + // + // We are in the middle of sending another report. Return 0 to + // indicate that we can't send this report until the previous one + // finishes. + // + psInst->bSendInProgress = false; + return(0); + } + + // + // Clear the elapsed time since this report was last sent. + // + if(ulLength) + { + ClearReportTimer(pvInstance, *pcData); + } + + // + // Keep track of the whereabouts of the report so that we can send it in + // multiple packets if necessary. + // + psInst->pucInReportData = pcData; + psInst->usInReportIndex = 0; + psInst->usInReportSize = ulLength; + + // + // Schedule transmission of the first packet of the report. + // + psInst->eHIDTxState = HID_STATE_WAIT_DATA; + lRetcode = ScheduleReportTransmission(psInst); + + // + // Clear the flag we use to indicate that we are in the midst of sending + // a packet. + // + psInst->bSendInProgress = false; + + // + // Did an error occur while trying to send the data? + // + if(lRetcode != -1) + { + // + // No - tell the caller we sent all the bytes provided. + // + return(ulLength); + } + else + { + // + // Yes - tell the caller we couldn't send the data. + // + return(0); + } +} + +//***************************************************************************** +// +//! Reads a packet of data received from the USB host via the interrupt OUT +//! endpoint (if in use). +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDHIDInit(). +//! \param pcData points to a buffer into which the received data will be +//! written. +//! \param ulLength is the size of the buffer pointed to by pcData. +//! \param bLast indicates whether the client will make a further call to +//! read additional data from the packet. +//! +//! This function reads up to ulLength bytes of data received from the USB +//! host into the supplied application buffer. If the driver detects that the +//! entire packet has been read, it is acknowledged to the host. +//! +//! The \e bLast parameter is ignored in this implementation since the end of +//! a packet can be determined without relying upon the client to provide +//! this information. +//! +//! \return Returns the number of bytes of data read. +// +//***************************************************************************** +unsigned long +USBDHIDPacketRead(void *pvInstance, unsigned char *pcData, + unsigned long ulLength, tBoolean bLast) +{ + unsigned long ulEPStatus, ulCount, ulPkt; + tHIDInstance *psInst; + long lRetcode; + + ASSERT(pvInstance); + + // + // Get our instance data pointer + // + psInst = ((tUSBDHIDDevice *)pvInstance)->psPrivateHIDData; + + // + // Does the relevant endpoint FIFO have a packet waiting for us? + // + ulEPStatus = MAP_USBEndpointStatus(psInst->ulUSBBase, + psInst->ucOUTEndpoint); + if(ulEPStatus & USB_DEV_RX_PKT_RDY) + { + // + // How many bytes are available for us to receive? + // + ulPkt = MAP_USBEndpointDataAvail(psInst->ulUSBBase, + psInst->ucOUTEndpoint); + + // + // Get as much data as we can. + // + ulCount = ulLength; + lRetcode = MAP_USBEndpointDataGet(psInst->ulUSBBase, + psInst->ucOUTEndpoint, + pcData, &ulCount); + + // + // Did we read the last of the packet data? + // + if(ulCount == ulPkt) + { + // + // Clear the endpoint status so that we know no packet is + // waiting. + // + MAP_USBDevEndpointStatusClear(psInst->ulUSBBase, + psInst->ucOUTEndpoint, + ulEPStatus); + + // + // Acknowledge the data, thus freeing the host to send the + // next packet. + // + MAP_USBDevEndpointDataAck(psInst->ulUSBBase, psInst->ucOUTEndpoint, + true); + + // + // Clear the flag we set to indicate that a packet read is + // pending. + // + SetDeferredOpFlag(&psInst->usDeferredOpFlags, + HID_DO_PACKET_RX, false); + } + + // + // If all went well, tell the caller how many bytes they got. + // + if(lRetcode != -1) + { + return(ulCount); + } + } + + // + // No packet was available or an error occurred while reading so tell + // the caller no bytes were returned. + // + return(0); +} + +//***************************************************************************** +// +//! Returns the number of free bytes in the transmit buffer. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDHIDInit(). +//! +//! This function indicates to the caller whether or not it is safe to send a +//! new report using a call to USBDHIDReportWrite(). The value returned will +//! be the maximum USB packet size (64) if no transmission is currently +//! outstanding or 0 if a transmission is in progress. Since the function +//! USBDHIDReportWrite() can accept full reports longer than a single USB +//! packet, the caller should be aware that the returned value from this +//! class driver, unlike others, does not indicate the maximum size of report +//! that can be written but is merely an indication that another report can be +//! written. +//! +//! \return Returns 0 if an outgoing report is still being transmitted or 64 +//! if no transmission is currently in progress. +// +//***************************************************************************** +unsigned long +USBDHIDTxPacketAvailable(void *pvInstance) +{ + tHIDInstance *psInst; + + ASSERT(pvInstance); + + // + // Get our instance data pointer. + // + psInst = ((tUSBDHIDDevice *)pvInstance)->psPrivateHIDData; + + // + // Do we have a packet transmission currently ongoing? + // + if(psInst->eHIDTxState != HID_STATE_IDLE) + { + // + // We are not ready to receive a new packet so return 0. + // + return(0); + } + else + { + // + // We can receive a packet so return the max packet size for the + // relevant endpoint. + // + return(INT_IN_EP_MAX_SIZE); + } +} + +//***************************************************************************** +// +//! Determines whether a packet is available and, if so, the size of the +//! buffer required to read it. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDHIDInit(). +//! +//! This function may be used to determine if a received packet remains to be +//! read and allows the application to determine the buffer size needed to +//! read the data. +//! +//! \return Returns 0 if no received packet remains unprocessed or the +//! size of the packet if a packet is waiting to be read. +// +//***************************************************************************** +unsigned long +USBDHIDRxPacketAvailable(void *pvInstance) +{ + unsigned long ulEPStatus; + unsigned long ulSize; + tHIDInstance *psInst; + + ASSERT(pvInstance); + + // + // Get our instance data pointer + // + psInst = ((tUSBDHIDDevice *)pvInstance)->psPrivateHIDData; + + // + // Does the relevant endpoint FIFO have a packet waiting for us? + // + ulEPStatus = MAP_USBEndpointStatus(psInst->ulUSBBase, + psInst->ucOUTEndpoint); + if(ulEPStatus & USB_DEV_RX_PKT_RDY) + { + // + // Yes - a packet is waiting. How big is it? + // + ulSize = MAP_USBEndpointDataAvail(psInst->ulUSBBase, + psInst->ucOUTEndpoint); + + return(ulSize); + } + else + { + // + // There is no packet waiting to be received. + // + return(0); + } +} + +//***************************************************************************** +// +//! Reports the device power status (bus- or self-powered) to the USB library. +//! +//! \param pvInstance is the pointer to the HID device instance structure. +//! \param ucPower indicates the current power status, either \b +//! USB_STATUS_SELF_PWR or \b USB_STATUS_BUS_PWR. +//! +//! Applications which support switching between bus- or self-powered +//! operation should call this function whenever the power source changes +//! to indicate the current power status to the USB library. This information +//! is required by the USB library to allow correct responses to be provided +//! when the host requests status from the device. +//! +//! \return None. +// +//***************************************************************************** +void +USBDHIDPowerStatusSet(void *pvInstance, unsigned char ucPower) +{ + ASSERT(pvInstance); + + // + // Pass the request through to the lower layer. + // + USBDCDPowerStatusSet(0, ucPower); +} + +//***************************************************************************** +// +//! Requests a remote wake up to resume communication when in suspended state. +//! +//! \param pvInstance is the pointer to the HID device instance structure. +//! +//! When the bus is suspended, an application which supports remote wake up +//! (advertised to the host via the configuration descriptor) may call this +//! function to initiate remote wake up signaling to the host. If the remote +//! wake up feature has not been disabled by the host, this will cause the bus +//! to resume operation within 20mS. If the host has disabled remote wake up, +//! \b false will be returned to indicate that the wake up request was not +//! successful. +//! +//! \return Returns \b true if the remote wake up is not disabled and the +//! signaling was started or \b false if remote wake up is disabled or if +//! signaling is currently ongoing following a previous call to this function. +// +//***************************************************************************** +tBoolean +USBDHIDRemoteWakeupRequest(void *pvInstance) +{ + ASSERT(pvInstance); + + // + // Pass the request through to the lower layer. + // + return(USBDCDRemoteWakeupRequest(0)); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/device/usbdhid.h b/usblib/device/usbdhid.h new file mode 100644 index 0000000..80b1126 --- /dev/null +++ b/usblib/device/usbdhid.h @@ -0,0 +1,974 @@ +//***************************************************************************** +// +// usbdhid.h - Definitions used by HID class devices. +// +// Copyright (c) 2008-2012 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#ifndef __USBDHID_H__ +#define __USBDHID_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup hid_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! The size of the memory that should be allocated to create a configuration +//! descriptor for a single instance of the USB HID Device. +//! This does not include the configuration descriptor which is automatically +//! ignored by the composite device class. +// +// For reference this is +// sizeof(g_sHIDInterfaceSection) + sizeof(g_sHIDDescriptorSection) + +// sizeof(g_sHIDInEndpointSection) + sizeof(g_sHIDOutEndpointSection) +// +//***************************************************************************** +#define COMPOSITE_DHID_SIZE (32) + +//***************************************************************************** +// +// Macros used to create the static Report Descriptors. +// +//***************************************************************************** + +//***************************************************************************** +// +//! This is a macro to assist adding Usage Page entries in HID report +//! descriptors. +//! +//! \param ucValue is the Usage Page value. +//! +//! This macro takes a value and prepares it to be placed as a Usage Page entry +//! into a HID report structure. These are defined by the USB HID +//! specification. +//! +//! \return Not a function. +// +//***************************************************************************** +#define UsagePage(ucValue) 0x05, ((ucValue) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Usage entries in HID report descriptors. +//! +//! \param ucValue is the Usage value. +//! +//! This macro takes a value and prepares it to be placed as a Usage entry into +//! a HID report structure. These are defined by the USB HID specification. +//! +//! \return Not a function. +// +//***************************************************************************** +#define Usage(ucValue) 0x09, ((ucValue) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Usage Minimum entries in HID report +//! descriptors. +//! +//! \param ucValue is the Usage Minimum value. +//! +//! This macro takes a value and prepares it to be placed as a Usage Minimum +//! entry into a HID report structure. This is the first or minimum value +//! associated with a usage value. +//! +//! \return Not a function. +// +//***************************************************************************** +#define UsageMinimum(ucValue) 0x19, ((ucValue) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Usage Maximum entries in HID report +//! descriptors. +//! +//! \param ucValue is the Usage Maximum value. +//! +//! This macro takes a value and prepares it to be placed as a Usage Maximum +//! entry into a HID report structure. This is the last or maximum value +//! associated with a usage value. +//! +//! \return Not a function. +// +//***************************************************************************** +#define UsageMaximum(ucValue) 0x29, ((ucValue) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Logical Minimum entries in HID report +//! descriptors. +//! +//! \param cValue is the Logical Minimum value. +//! +//! This macro takes a value and prepares it to be placed as a Logical Minimum +//! entry into a HID report structure. This is the actual minimum value for a +//! range of values associated with a field. +//! +//! \return Not a function. +// +//***************************************************************************** +#define LogicalMinimum(cValue) 0x15, ((cValue) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Logical Maximum entries in HID report +//! descriptors. +//! +//! \param cValue is the Logical Maximum value. +//! +//! This macro takes a value and prepares it to be placed as a Logical Maximum +//! entry into a HID report structure. This is the actual maximum value for a +//! range of values associated with a field. +//! +//! \return Not a function. +// +//***************************************************************************** +#define LogicalMaximum(cValue) 0x25, ((cValue) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Physical Minimum entries in HID report +//! descriptors. +//! +//! \param sValue is the Physical Minimum value. It is a signed, 16 bit +//! number. +//! +//! This macro takes a value and prepares it to be placed as a Physical Minimum +//! entry into a HID report structure. This is value is used in conversion of +//! the control logical value, as returned to the host in the relevant report, +//! to a physical measurement in the appropriate units. +//! +//! \return Not a function. +// +//***************************************************************************** +#define PhysicalMinimum(sValue) 0x36, ((sValue) & 0xFF), \ + (((sValue) >> 8) & 0xFF) + +//***************************************************************************** +// +//! This is a macro to assist adding Physical Maximum entries in HID report +//! descriptors. +//! +//! \param sValue is the Physical Maximum value. It is a signed, 16 bit +//! number. +//! +//! This macro takes a value and prepares it to be placed as a Physical Maximum +//! entry into a HID report structure. This is value is used in conversion of +//! the control logical value, as returned to the host in the relevant report, +//! to a physical measurement in the appropriate units. +//! +//! \return Not a function. +// +//***************************************************************************** +#define PhysicalMaximum(sValue) 0x46, ((sValue) & 0xFF), \ + (((sValue) >> 8) & 0xFF) + +//***************************************************************************** +// +//! This is a macro to assist adding Collection entries in HID report +//! descriptors. +//! +//! \param ucValue is the type of Collection. +//! +//! This macro takes a value and prepares it to be placed as a Collection +//! entry into a HID report structure. This is the type of values that are +//! being grouped together, for instance input, output or features can be +//! grouped together as a collection. +//! +//! \return Not a function. +// +//***************************************************************************** +#define Collection(ucValue) 0xa1, ((ucValue) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding End Collection entries in HID report +//! descriptors. +//! +//! This macro can be used to place an End Collection entry into a HID report +//! structure. This is a tag to indicate that a collection of entries has +//! ended in the HID report structure. This terminates a previous Collection() +//! entry. +//! +//! \return Not a function. +// +//***************************************************************************** +#define EndCollection 0xc0 + +//***************************************************************************** +// +//! This is a macro to assist adding Report Count entries in HID report +//! descriptors. +//! +//! \param ucValue is the number of items in a report item. +//! +//! This macro takes a value and prepares it to be placed as a Report Count +//! entry into a HID report structure. This is number of entries of Report +//! Size for a given item. +//! +//! \return Not a function. +// +//***************************************************************************** +#define ReportCount(ucValue) 0x95, ((ucValue) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Report ID entries in HID report +//! descriptors. +//! +//! \param ucValue is the identifier prefix for the current report. +//! +//! This macro takes a value and prepares it to be placed as a Report ID +//! entry into a HID report structure. This value is used as a 1 byte prefix +//! for the report it is contained within. +//! +//! \return Not a function. +// +//***************************************************************************** +#define ReportID(ucValue) 0x85, ((ucValue) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Report Size entries in HID report +//! descriptors. +//! +//! \param ucValue is the size, in bits, of items in a report item. +//! +//! This macro takes a value and prepares it to be placed as a Report Size +//! entry into a HID report structure. This is size in bits of the entries of +//! of a report entry. The Report Count specifies how many entries of Report +//! Size are in a given item. These can be individual bits or bit fields. +//! +//! \return Not a function. +// +//***************************************************************************** +#define ReportSize(ucValue) 0x75, ((ucValue) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Input entries in HID report descriptors. +//! +//! \param ucValue is bit mask to specify the type of a set of input report +//! items. Note that if the USB_HID_INPUT_BITF flag is required, the Input2 +//! macro (which uses a 2 byte version of the Input item tag) must be used +//! instead of this macro. +//! +//! This macro takes a value and prepares it to be placed as an Input entry +//! into a HID report structure. This specifies the type of an input item in +//! a report structure. These refer to a bit mask of flags that indicate the +//! type of input for a set of items. +//! +//! \return Not a function. +// +//***************************************************************************** +#define Input(ucValue) 0x81, ((ucValue) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Input entries in HID report descriptors. +//! +//! \param usValue is bit mask to specify the type of a set of input report +//! items. Note that this macro uses a version of the Input item tag with a +//! two byte payload and allows any of the 8 possible data bits for the tag to +//! be used. If USB_HID_INPUT_BITF (bit 8) is not required, the Input macro +//! may be used instead. +//! +//! This macro takes a value and prepares it to be placed as an Input entry +//! into a HID report structure. This specifies the type of an input item in +//! a report structure. These refer to a bit mask of flags that indicate the +//! type of input for a set of items. +//! +//! \return Not a function. +// +//***************************************************************************** +#define Input2(usValue) 0x82, ((usValue) & 0xff), \ + (((usValue) >> 8) & 0xFF) + +//***************************************************************************** +// +//! This is a macro to assist adding Feature entries in HID report descriptors. +//! +//! \param ucValue is bit mask to specify the type of a set of feature report +//! items. Note that if the USB_HID_FEATURE_BITF flag is required, the +//! Feature2 macro (which uses a 2 byte version of the Feature item tag) must +//! be used instead of this macro. +//! +//! This macro takes a value and prepares it to be placed as a Feature entry +//! into a HID report structure. This specifies the type of a feature item in +//! a report structure. These refer to a bit mask of flags that indicate the +//! type of feature for a set of items. +//! +//! \return Not a function. +// +//***************************************************************************** +#define Feature(ucValue) 0xB1, ((ucValue) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Feature entries in HID report descriptors. +//! +//! \param usValue is bit mask to specify the type of a set of feature report +//! items. Note that this macro uses a version of the Feature item tag with a +//! two byte payload and allows any of the 8 possible data bits for the tag to +//! be used. If USB_HID_FEATURE_BITF (bit 8) is not required, the Feature +//! macro may be used instead. +//! +//! This macro takes a value and prepares it to be placed as a Feature entry +//! into a HID report structure. This specifies the type of a feature item in +//! a report structure. These refer to a bit mask of flags that indicate the +//! type of feature for a set of items. +//! +//! \return Not a function. +// +//***************************************************************************** +#define Feature2(usValue) 0xB2, ((usValue) & 0xff), \ + (((usValue) >> 8) & 0xFF) + +//***************************************************************************** +// +//! This is a macro to assist adding Output entries in HID report descriptors. +//! +//! \param ucValue is bit mask to specify the type of a set of output report +//! items. Note that if the USB_HID_OUTPUT_BITF flag is required, the Output2 +//! macro (which uses a 2 byte version of the Output item tag) must be used +//! instead of this macro. +//! +//! This macro takes a value and prepares it to be placed as an Output entry +//! into a HID report structure. This specifies the type of an output item in +//! a report structure. These refer to a bit mask of flags that indicate the +//! type of output for a set of items. +//! +//! \return Not a function. +// +//***************************************************************************** +#define Output(ucValue) 0x91, ((ucValue) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Output entries in HID report descriptors. +//! +//! \param usValue is bit mask to specify the type of a set of output report +//! items. Note that this macro uses a version of the Output item tag with a +//! two byte payload and allows any of the 8 possible data bits for the tag to +//! be used. If USB_HID_OUTPUT_BITF (bit 8) is not required, the Output macro +//! may be used instead. +//! +//! This macro takes a value and prepares it to be placed as an Output entry +//! into a HID report structure. This specifies the type of an output item in +//! a report structure. These refer to a bit mask of flags that indicate the +//! type of output for a set of items. +//! +//! \return Not a function. +// +//***************************************************************************** +#define Output2(usValue) 0x92, ((usValue) & 0xff), \ + (((usValue) >> 8) & 0xFF) + +//***************************************************************************** +// +//! This is a macro to assist adding Unit Exponent entries in HID report +//! descriptors. +//! +//! \param cValue is the required exponent in the range [-8, 7]. +//! +//! This macro takes a value and prepares it to be placed as a Unit Exponent +//! entry into a HID report structure. This is the exponent applied to +//! PhysicalMinimum and PhysicalMaximum when scaling and converting control +//! values to "real" units. +//! +//! \return Not a function. +// +//***************************************************************************** +#define UnitExponent(cValue) 0x55, ((cValue) & 0x0f) + +//***************************************************************************** +// +//! This is a macro to assist adding Unit entries for uncommon units in HID +//! report descriptors. +//! +//! \param ulValue is the definition of the unit required as defined in section +//! 6.2.2.7 of the USB HID device class definition document. +//! +//! This macro takes a value and prepares it to be placed as a Unit entry into +//! a HID report structure. Note that individual macros are defined for common +//! units and this macro is intended for use when a complex or uncommon unit +//! is needed. It allows entry of a 5 nibble unit definition into the report +//! descriptor. +//! +//! \return Not a function. +// +//***************************************************************************** +#define Unit(ulValue) 0x67, (ulValue) & 0x0f), \ + (((ulValue) >> 8) & 0xFF), \ + (((ulValue) >> 16) & 0xFF), \ + (((ulValue) >> 24) & 0xFF) + +//***************************************************************************** +// +//! This macro inserts a Unit entry for centimeters into a report descriptor. +//! +//***************************************************************************** +#define UnitDistance_cm 0x66, 0x11, 0x00 + +//***************************************************************************** +// +//! This macro inserts a Unit entry for inches into a report descriptor. +//! +//***************************************************************************** +#define UnitDistance_i 0x66, 0x13, 0x00 + +//***************************************************************************** +// +//! This macro inserts a Unit entry for degrees into a report descriptor. +//! +//***************************************************************************** +#define UnitRotation_deg 0x66, 0x14, 0x00 + +//***************************************************************************** +// +//! This macro inserts a Unit entry for radians into a report descriptor. +//! +//***************************************************************************** +#define UnitRotation_rad 0x66, 0x12, 0x00 + +//***************************************************************************** +// +//! This macro inserts a Unit entry for grams into a report descriptor. +//! +//***************************************************************************** +#define UnitMass_g 0x66, 0x01, 0x01 + +//***************************************************************************** +// +//! This macro inserts a Unit entry for seconds into a report descriptor. +//! +//***************************************************************************** +#define UnitTime_s 0x66, 0x01, 0x10 + +//***************************************************************************** +// +//! This macro inserts a Unit entry for temperature in Kelvin into a report +//! descriptor. +//! +//***************************************************************************** +#define UnitTemp_K 0x67, 0x01, 0x00, 0x01, 0x00 + +//***************************************************************************** +// +//! This macro inserts a Unit entry for temperature in Fahrenheit into a report +//! descriptor. +//! +//***************************************************************************** +#define UnitTemp_F 0x67, 0x03, 0x00, 0x01, 0x00 + +//***************************************************************************** +// +//! This macro inserts a Unit entry for velocity in cm/s into a report +//! descriptor. +//! +//***************************************************************************** +#define UnitVelocitySI 0x66, 0x11, 0xF0 + +//***************************************************************************** +// +//! This macro inserts a Unit entry for momentum in (grams * cm)/s into a +//! report descriptor. +//! +//***************************************************************************** +#define UnitMomentumSI 0x66, 0x11, 0xF1 + +//***************************************************************************** +// +//! This macro inserts a Unit entry for acceleration in cm/s**2 into a +//! report descriptor. +//! +//***************************************************************************** +#define UnitAccelerationSI 0x66, 0x11, 0xE0 + +//***************************************************************************** +// +//! This macro inserts a Unit entry for force in (cm * grams)/s**2 into a +//! report descriptor. +//! +//***************************************************************************** +#define UnitForceSI 0x66, 0x11, 0xE1 + +//***************************************************************************** +// +//! This macro inserts a Unit entry for energy in (grams * cm**2)/s**2 into a +//! report descriptor. +//! +//***************************************************************************** +#define UnitEnergySI 0x66, 0x21, 0xE1 + +//***************************************************************************** +// +//! This macro inserts a Unit entry for angular acceleration in degrees/s**2 +//! into a report descriptor. +//! +//***************************************************************************** +#define UnitAngAccelerationSI 0x66, 0x12, 0xE0 + +//***************************************************************************** +// +//! This macro inserts a Unit entry for voltage into a a report descriptor. +//! +//***************************************************************************** +#define UnitVoltage 0x67, 0x21, 0xD1, 0xF0, 0x00 + +//***************************************************************************** +// +//! This macro inserts a Unit entry for voltage into a a report descriptor. +//! +//***************************************************************************** +#define UnitCurrent_A 0x67, 0x01, 0x00, 0x10, 0x00 + +//***************************************************************************** +// +// PRIVATE +// +// The first few sections of this header are private defines that are used by +// the USB HID code and are here only to help with the application +// allocating the correct amount of memory for the HID device code. +// +//***************************************************************************** + +//***************************************************************************** +// +// PRIVATE +// +// This enumeration holds the various states that the device can be in during +// normal operation. +// +//***************************************************************************** +typedef enum +{ + // + // Unconfigured. + // + HID_STATE_UNCONFIGURED, + + // + // No outstanding transaction remains to be completed. + // + HID_STATE_IDLE, + + // + // Waiting on completion of a send or receive transaction. + // + HID_STATE_WAIT_DATA +} +tHIDState; + +//***************************************************************************** +// +// PRIVATE +// +// This structure defines the private instance data and state variables for +// HID devices. The memory for this structure is pointed to by the +// psPrivateHIDData field in the tUSBDHIDDevice structure passed in the +// USBDHIDInit() function. +// +//***************************************************************************** +typedef struct +{ + unsigned long ulUSBBase; + tDeviceInfo *psDevInfo; + tConfigDescriptor *psConfDescriptor; + volatile tHIDState eHIDRxState; + volatile tHIDState eHIDTxState; + volatile unsigned short usDeferredOpFlags; + unsigned short usInReportSize; + unsigned short usInReportIndex; + unsigned short usOutReportSize; + unsigned char *pucInReportData; + unsigned char *pucOutReportData; + volatile tBoolean bConnected; + volatile tBoolean bSendInProgress; + tBoolean bGetRequestPending; + unsigned char ucINEndpoint; + unsigned char ucOUTEndpoint; + unsigned char ucInterface; +} +tHIDInstance; + +#ifndef DEPRECATED +//***************************************************************************** +// +// The number of bytes of workspace required by the HID device class driver. +// The client must provide a block of RAM of at least this size in the +// pvWorkspace field of the tUSBDHIDDevice structure passed on USBDHIDInit. +// +//***************************************************************************** +#define USB_HID_WORKSPACE_SIZE sizeof(tHIDInstance) +#endif + +//***************************************************************************** +// +//! The structure used to track idle time for reports. An array of these +//! structures is passed to the HID device class driver during USBDHIDInit and +//! is used to track automatic resending of each report (if not disabled by +//! the host). +// +//***************************************************************************** +typedef struct +{ + // + //! The idle duration for the report expressed in units of 4mS. 0 + //! indicates infinite and informs the class driver not to send the report + //! unless a state change occurs. + // + unsigned char ucDuration4mS; + + // + //! The ID of the report which this structure applies to. This is the + //! report ID as specified using a ReportID tag in the report descriptor + //! rather than the index of the report in the HID class descriptor array. + //! If only a single Input report is supported and, thus, no ReportID tag + //! is present, this field should be set to 0. + // + unsigned char ucReportID; + + // + //! The number of milliseconds before we need to send a copy of a given + //! report back to the host. This field is updated by the HID driver and + //! used to time sending of USBD_HID_EVENT_IDLE_TIMEOUT. + // + unsigned short usTimeTillNextmS; + + // + //! The number of milliseconds that have passed since the last time this + //! report was sent. The HID class driver needs to track this since + //! Set_Idle requests are required to take effect as if issued immediately + //! after the last transmission of the report to which they refer. + // + unsigned long ulTimeSinceReportmS; +} +tHIDReportIdle; + +//***************************************************************************** +// +//! The structure used by the application to define operating parameters for +//! the HID device. +// +//***************************************************************************** +typedef struct +{ + // + //! The vendor ID that this device is to present in the device descriptor. + // + unsigned short usVID; + + // + //! The product ID that this device is to present in the device descriptor. + // + unsigned short usPID; + + // + //! The maximum power consumption of the device, expressed in milliamps. + // + unsigned short usMaxPowermA; + + // + //! Indicates whether the device is self- or bus-powered and whether or not + //! it supports remote wakeup. Valid values are USB_CONF_ATTR_SELF_PWR or + //! USB_CONF_ATTR_BUS_PWR, optionally ORed with USB_CONF_ATTR_RWAKE. + // + unsigned char ucPwrAttributes; + + // + //! The interface subclass to publish to the server for this HID device. + // + unsigned char ucSubclass; + + // + //! The interface protocol to publish to the server for this HID device. + // + unsigned char ucProtocol; + + // + //! The number of Input reports that this device supports. This field + //! must equal the number of reports published in the HID class descriptors + //! for the device and also the number of entries in the array whose first + //! element is pointed to by field psReportIdle below. + // + unsigned char ucNumInputReports; + + // + //! A pointer to the first element in an array of structures used to track + //! idle time for each Input report. When USBDHIDInit is called, the + //! ucDuration4mS and ucReportID fields of each of these array members + //! should be initialized to indicate the default idle timeout for each + //! input report. This array must be in RAM since the HID device class + //! driver will update values in it in response to requests from the host + //! and to track elapsed time. The number of elements in the array must + //! match the number supplied in the ucNumInputReports field above. + // + tHIDReportIdle *psReportIdle; + + //! A pointer to the callback function which will be called to notify + //! the application of general events, events related to report transfers + //! on endpoint zero and events related to reception of Output and Feature + //! reports via the (optional) interrupt OUT endpoint. + // + tUSBCallback pfnRxCallback; + + // + //! A client-supplied pointer which will be sent as the first + //! parameter in all calls made to the receive channel callback, + //! pfnRxCallback. + // + void *pvRxCBData; + + // + //! A pointer to the callback function which will be called to notify + //! the application of events related to transmission of Input reports + //! via the interrupt IN endpoint. + // + tUSBCallback pfnTxCallback; + + // + //! A client-supplied pointer which will be sent as the first + //! parameter in all calls made to the transmit channel callback, + //! pfnTxCallback. + // + void *pvTxCBData; + + // + //! If set to true, this field indicates that the device should use a + //! dedicated interrupt OUT endpoint to receive reports from the host. In + //! this case, reports from the host are passed to the application via the + //! receive callback using USB_EVENT_RX_AVAILABLE events. If false, + //! reports from the host are received via endpoint zero and passed to the + //! application via USBD_HID_EVENT_REPORT_SENT events. + // + tBoolean bUseOutEndpoint; + + // + //! The HID descriptor that the device is to publish (following the + //! standard interface descriptor and prior to the endpoint descriptors for + //! the interface). + // + const tHIDDescriptor *psHIDDescriptor; + + // + //! The HID class descriptors offered by the device are defined in an + //! array of byte pointers and this field points to that array. The + //! order and number of elements in the array must match the associated + //! information provided in the HID descriptor in field by psHIDDescriptor. + // + const unsigned char * const *ppClassDescriptors; + + // + //! A pointer to the string descriptor array for this device. This array + //! must contain the following string descriptor pointers in this order. + //! Language descriptor, Manufacturer name string (language 1), Product + //! name string (language 1), Serial number string (language 1),HID + //! Interface description string (language 1), Configuration description + //! string (language 1), (optionally) First HID device-specific string + //! (language 1), (optionally) Second HID device-specific string (language + //! 1), etc. + //! + //! If supporting more than 1 language, the descriptor block (except for + //! string descriptor 0) must be repeated for each language defined in the + //! language descriptor. + //! + //! The number of HID device-specific strings is dependent upon the content + //! of the report descriptor passed to the interface and is, thus, + //! application controlled. + // + const unsigned char * const *ppStringDescriptors; + + // + //! The number of descriptors provided in the ppStringDescriptors + //! array. This must be 1 + ((5 + (num HID strings)) * (num languages)). + // + unsigned long ulNumStringDescriptors; + + // + //! A pointer to private instance data for this device instance. This + //! memory must remain accessible for as long as the HID device is in use + //! and must not be modified by any code outside the HID class driver. + // + tHIDInstance *psPrivateHIDData; +} +tUSBDHIDDevice; + +//***************************************************************************** +// +// HID-specific device class driver events +// +//***************************************************************************** + +//***************************************************************************** +// +//! This event indicates that the host is requesting a particular report be +//! returned via endpoint 0, the control endpoint. The ulMsgValue parameter +//! contains the requested report type in the high byte and report ID in the +//! low byte (as passed in the wValue field of the USB request structure). +//! The pvMsgData parameter contains a pointer which must be written with the +//! address of the first byte of the requested report. The callback must +//! return the size in bytes of the report pointed to by *pvMsgData. The +//! memory returned in response to this event must remain unaltered until +//! USBD_HID_EVENT_REPORT_SENT is sent. +// +//***************************************************************************** +#define USBD_HID_EVENT_GET_REPORT \ + (USBD_HID_EVENT_BASE + 0) + +//***************************************************************************** +// +//! This event indicates that a report previously requested via a +//! USBD_HID_EVENT_GET_REPORT has been successfully transmitted to the host. +//! The application may now free or reuse the report memory passed on the +//! previous event. Although this would seem to be an event that would be +//! passed to the transmit channel callback, it is actually passed to the +//! receive channel callback. This ensures that all events related to the +//! request and transmission of reports via endpoint zero can be handled in +//! a single function. +// +//***************************************************************************** +#define USBD_HID_EVENT_REPORT_SENT \ + (USBD_HID_EVENT_BASE + 1) + +//***************************************************************************** +// +//! This event indicates that the host has sent a Set_Report request to +//! the device and requests that the device provide a buffer into which the +//! report can be written. The ulMsgValue parameter contains the received +//! report type in the high byte and report ID in the low byte (as passed in +//! the wValue field of the USB request structure). The pvMsgData parameter +//! contains the length of buffer requested. Note that this is the actual +//! length value cast to a "void *" type and not a pointer in this case. +//! The callback must return a pointer to a suitable buffer (cast to the +//! standard "unsigned long" return type for the callback). +// +//***************************************************************************** +#define USBD_HID_EVENT_GET_REPORT_BUFFER \ + (USBD_HID_EVENT_BASE + 2) + +//***************************************************************************** +// +//! This event indicates that the host has sent the device a report via +//! endpoint 0, the control endpoint. The ulMsgValue field indicates the +//! size of the report and pvMsgData points to the first byte of the report. +//! The report buffer will previously have been returned in response to an +//! earlier USBD_HID_EVENT_GET_REPORT_BUFFER callback. The HID device class +//! driver will not access the memory pointed to by pvMsgData after this +//! callback is made so the application is free to reuse or free it at this +//! point. +// +//***************************************************************************** +#define USBD_HID_EVENT_SET_REPORT \ + (USBD_HID_EVENT_BASE + 3) + +//***************************************************************************** +// +//! This event is sent in response to a Get_Protocol request from the host. +//! The callback should provide the current protocol via the return code, +//! USB_HID_PROTOCOL_BOOT or USB_HID_PROTOCOL_REPORT. +// +//***************************************************************************** +#define USBD_HID_EVENT_GET_PROTOCOL \ + (USBD_HID_EVENT_BASE + 4) + +//***************************************************************************** +// +//! This event is sent in response to a Set_Protocol request from the host. +//! The ulMsgData value will contain the requested protocol, +//! USB_HID_PROTOCOL_BOOT or USB_HID_PROTOCOL_REPORT. +// +//***************************************************************************** +#define USBD_HID_EVENT_SET_PROTOCOL \ + (USBD_HID_EVENT_BASE + 5) + +//***************************************************************************** +// +//! This event indicates to an application that a report idle timeout has +//! occurred and requests a pointer to the report that must be sent back to +//! the host. The ulMsgData value will contain the requested report ID and +//! pvMsgData contains a pointer that must be written with a pointer to the +//! report data that is to be sent. The callback must return the number of +//! bytes in the report pointed to by *pvMsgData. +// +//***************************************************************************** +#define USBD_HID_EVENT_IDLE_TIMEOUT \ + (USBD_HID_EVENT_BASE + 6) + +extern tDeviceInfo g_sHIDDeviceInfo; + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void *USBDHIDInit(unsigned long ulIndex, + const tUSBDHIDDevice *psHIDDevice); +extern void *USBDHIDCompositeInit(unsigned long ulIndex, + const tUSBDHIDDevice *psDevice); + +extern void USBDHIDTerm(void *pvInstance); +extern void *USBDHIDSetRxCBData(void *pvInstance, void *pvCBData); +extern void *USBDHIDSetTxCBData(void *pvInstance, void *pvCBData); +extern unsigned long USBDHIDReportWrite(void *pvInstance, + unsigned char *pcData, + unsigned long ulLength, + tBoolean bLast); +extern unsigned long USBDHIDPacketRead(void *pvInstance, + unsigned char *pcData, + unsigned long ulLength, + tBoolean bLast); +extern unsigned long USBDHIDTxPacketAvailable(void *pvInstance); +extern unsigned long USBDHIDRxPacketAvailable(void *pvInstance); +extern void USBDHIDPowerStatusSet(void *pvInstance, unsigned char ucPower); +extern tBoolean USBDHIDRemoteWakeupRequest(void *pvInstance); + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBDHID_H__ diff --git a/usblib/device/usbdhidkeyb.c b/usblib/device/usbdhidkeyb.c new file mode 100644 index 0000000..8b4aa95 --- /dev/null +++ b/usblib/device/usbdhidkeyb.c @@ -0,0 +1,1119 @@ +//***************************************************************************** +// +// usbdhidkeyb.c - USB HID Keyboard device class driver. +// +// Copyright (c) 2008-2012 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/device/usbdevice.h" +#include "usblib/usbhid.h" +#include "usblib/device/usbdhid.h" +#include "usblib/device/usbdhidkeyb.h" + +//***************************************************************************** +// +//! \addtogroup hid_keyboard_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The following is the HID report structure definition that is passed back +// to the host. +// +//***************************************************************************** +static const unsigned char g_pucKeybReportDescriptor[]= +{ + UsagePage(USB_HID_GENERIC_DESKTOP), + Usage(USB_HID_KEYBOARD), + Collection(USB_HID_APPLICATION), + + // + // Modifier keys. + // 8 - 1 bit values indicating the modifier keys (ctrl, shift...) + // + ReportSize(1), + ReportCount(8), + UsagePage(USB_HID_USAGE_KEYCODES), + UsageMinimum(224), + UsageMaximum(231), + LogicalMinimum(0), + LogicalMaximum(1), + Input(USB_HID_INPUT_DATA | USB_HID_INPUT_VARIABLE | USB_HID_INPUT_ABS), + + // + // One byte of rsvd data required by HID spec. + // + ReportCount(1), + ReportSize(8), + Input(USB_HID_INPUT_CONSTANT), + + // + // Keyboard LEDs. + // 5 - 1 bit values. + // + ReportCount(5), + ReportSize(1), + UsagePage(USB_HID_USAGE_LEDS), + UsageMinimum(1), + UsageMaximum(5), + Output(USB_HID_OUTPUT_DATA | USB_HID_OUTPUT_VARIABLE | + USB_HID_OUTPUT_ABS), + // + // 1 - 3 bit value to pad out to a full byte. + // + ReportCount(1), + ReportSize(3), + Output(USB_HID_OUTPUT_CONSTANT), //LED report padding + + // + // The Key buffer. + // 6 - 8 bit values to store the current key state. + // + ReportCount(6), + ReportSize(8), + LogicalMinimum(0), + LogicalMaximum(101), + UsagePage(USB_HID_USAGE_KEYCODES), + UsageMinimum (0), + UsageMaximum (101), + Input(USB_HID_INPUT_DATA | USB_HID_INPUT_ARRAY), + EndCollection +}; + +//***************************************************************************** +// +// The HID class descriptor table. For the keyboard class, we have only a +// single report descriptor. +// +//***************************************************************************** +static const unsigned char * const g_pKeybClassDescriptors[] = +{ + g_pucKeybReportDescriptor +}; + +//***************************************************************************** +// +// The HID descriptor for the keyboard device. +// +//***************************************************************************** +static const tHIDDescriptor g_sKeybHIDDescriptor = +{ + 9, // bLength + USB_HID_DTYPE_HID, // bDescriptorType + 0x111, // bcdHID (version 1.11 compliant) + 0, // bCountryCode (not localized) + 1, // bNumDescriptors + { + { + USB_HID_DTYPE_REPORT, // Report descriptor + sizeof(g_pucKeybReportDescriptor) // Size of report descriptor + } + } +}; + +//***************************************************************************** +// +// Forward references for keyboard device callback functions. +// +//***************************************************************************** +static unsigned long HIDKeyboardRxHandler(void *pvCBData, + unsigned long ulEvent, + unsigned long ulMsgData, + void *pvMsgData); +static unsigned long HIDKeyboardTxHandler(void *pvCBData, + unsigned long ulEvent, + unsigned long ulMsgData, + void *pvMsgData); + +//***************************************************************************** +// +// Main HID device class event handler function. +// +// \param pvCBData is the event callback pointer provided during USBDHIDInit(). +// This is a pointer to our HID device structure (&g_sHIDKeybDevice). +// \param ulEvent identifies the event we are being called back for. +// \param ulMsgData is an event-specific value. +// \param pvMsgData is an event-specific pointer. +// +// This function is called by the HID device class driver to inform the +// application of particular asynchronous events related to operation of the +// keyboard HID device. +// +// \return Returns a value which is event-specific. +// +//***************************************************************************** +static unsigned long +HIDKeyboardRxHandler(void *pvCBData, unsigned long ulEvent, + unsigned long ulMsgData, void *pvMsgData) +{ + tHIDKeyboardInstance *psInst; + tUSBDHIDKeyboardDevice *psDevice; + + // + // Make sure we didn't get a NULL pointer. + // + ASSERT(pvCBData); + + // + // Get a pointer to our instance data + // + psDevice = (tUSBDHIDKeyboardDevice *)pvCBData; + psInst = psDevice->psPrivateHIDKbdData; + + // + // Which event were we sent? + // + switch (ulEvent) + { + // + // The host has connected to us and configured the device. + // + case USB_EVENT_CONNECTED: + { + psInst->ucUSBConfigured = true; + + // + // Pass the information on to the client. + // + psDevice->pfnCallback(psDevice->pvCBData, USB_EVENT_CONNECTED, + 0, (void *)0); + + break; + } + + // + // The host has disconnected from us. + // + case USB_EVENT_DISCONNECTED: + { + psInst->ucUSBConfigured = false; + + // + // Pass the information on to the client. + // + psDevice->pfnCallback(psDevice->pvCBData, USB_EVENT_DISCONNECTED, + 0, (void *)0); + + break; + } + + // + // The host is polling us for a particular report and the HID driver + // is asking for the latest version to transmit. + // + case USBD_HID_EVENT_IDLE_TIMEOUT: + case USBD_HID_EVENT_GET_REPORT: + { + // + // We only support a single input report so we don't need to check + // the ulMsgValue parameter in this case. Set the report pointer + // in *pvMsgData and return the length of the report in bytes. + // + *(unsigned char **)pvMsgData = psInst->pucReport; + return(KEYB_IN_REPORT_SIZE); + } + + // + // The device class driver has completed sending a report to the + // host in response to a Get_Report request. + // + case USBD_HID_EVENT_REPORT_SENT: + { + // + // We have nothing to do here. + // + break; + } + + // + // This event is sent in response to a host Set_Report request. We + // must return a pointer to a buffer large enough to receive the + // report into. + // + case USBD_HID_EVENT_GET_REPORT_BUFFER: + { + // + // Are we being asked for a report that is shorter than the storage + // we have set aside for this? The only output report we define is + // 8 bits long so we really expect to see a length of 1 passed. + // + if((unsigned long)pvMsgData == KEYB_OUT_REPORT_SIZE ) + { + // + // Yes - return our pointer. + // + return((unsigned long)psInst->pucDataBuffer); + } + else + { + // + // We are being passed a report that is longer than the + // only report we expect so return NULL. This causes the + // device class driver to stall the request. + // + return(0); + } + } + + // + // This event indicates that the host has sent us an Output or + // Feature report and that the report is now in the buffer we provided + // on the previous USBD_HID_EVENT_GET_REPORT_BUFFER callback. + // + case USBD_HID_EVENT_SET_REPORT: + { + // + // Inform the application if the keyboard LEDs have changed. + // + if(psInst->ucLEDStates != psInst->pucDataBuffer[0]) + { + // + // Note the new LED states. + // + psInst->ucLEDStates = psInst->pucDataBuffer[0]; + + // + // Pass the information on to the client. + // + psDevice->pfnCallback(psDevice->pvCBData, + USBD_HID_KEYB_EVENT_SET_LEDS, + psInst->pucDataBuffer[0], (void *)0); + } + break; + } + + // + // The host is asking us to set either boot or report protocol (not + // that it makes any difference to this particular mouse). + // + case USBD_HID_EVENT_SET_PROTOCOL: + { + psInst->ucProtocol = ulMsgData; + break; + } + + // + // The host is asking us to tell it which protocol we are currently + // using, boot or request. + // + case USBD_HID_EVENT_GET_PROTOCOL: + { + return(psInst->ucProtocol); + } + + // + // Pass ERROR, SUSPEND and RESUME to the client unchanged. + // + case USB_EVENT_ERROR: + case USB_EVENT_SUSPEND: + case USB_EVENT_RESUME: + { + return(psDevice->pfnCallback(psDevice->pvCBData, ulEvent, + ulMsgData, pvMsgData)); + } + + // + // We ignore all other events. + // + default: + { + break; + } + } + return(0); +} + +//***************************************************************************** +// +// HID device class transmit channel event handler function. +// +// \param pvCBData is the event callback pointer provided during USBDHIDInit(). +// This is a pointer to our HID device structure (&g_sHIDKeybDevice). +// \param ulEvent identifies the event we are being called back for. +// \param ulMsgData is an event-specific value. +// \param pvMsgData is an event-specific pointer. +// +// This function is called by the HID device class driver to inform the +// application of particular asynchronous events related to report +// transmissions made using the interrupt IN endpoint. +// +// \return Returns a value which is event-specific. +// +//***************************************************************************** +static unsigned long +HIDKeyboardTxHandler(void *pvCBData, unsigned long ulEvent, + unsigned long ulMsgData, void *pvMsgData) +{ + tHIDKeyboardInstance *psInst; + tUSBDHIDKeyboardDevice *psDevice; + tUSBDHIDDevice *psHIDDevice; + unsigned long ulCount; + + // + // Make sure we didn't get a NULL pointer. + // + ASSERT(pvCBData); + + // + // Get a pointer to our instance data + // + psDevice = (tUSBDHIDKeyboardDevice *)pvCBData; + psInst = psDevice->psPrivateHIDKbdData; + psHIDDevice = &psDevice->psPrivateHIDKbdData->sHIDDevice; + + // + // Which event were we sent? + // + switch (ulEvent) + { + // + // A report transmitted via the interrupt IN endpoint was acknowledged + // by the host. + // + case USB_EVENT_TX_COMPLETE: + { + // + // Do we have any pending changes needing transmitted? + // + if(psInst->bChangeMade) + { + // + // Yes - go ahead and send another report immediately. + // + ulCount = USBDHIDReportWrite((void *)psHIDDevice, + psInst->pucReport, + KEYB_IN_REPORT_SIZE, true); + + // + // If we scheduled the report for transmission, clear the + // change flag. + // + if(ulCount != 0) + { + psInst->bChangeMade = false; + } + } + else + { + // + // Our last transmission is complete and we have nothing more + // to send. + // + psInst->eKeyboardState = HID_KEYBOARD_STATE_IDLE; + } + + // + // Pass the event on to the client. + // + psDevice->pfnCallback(psDevice->pvCBData, USB_EVENT_TX_COMPLETE, + ulMsgData, (void *)0); + + break; + } + + // + // We ignore all other events related to transmission of reports via + // the interrupt IN endpoint. + // + default: + { + break; + } + } + + return(0); +} + +//***************************************************************************** +// +// Add the supplied usage code to the list of keys currently in the pressed +// state. +// +// \param ucUsageCode is the HID usage code of the newly pressed key. +// +// This function adds the supplied usage code to the global list of keys which +// are currently pressed (assuming it is not already noted as pressed and that +// there is space in the list to hold the new information). The return code +// indicates success if the list did not overflow and failure if the list +// already contains as many pressed keys as can be reported. +// +// \return Returns \b true if the usage code was successfully added to the +// list or \b false if there was insufficient space to hold the new key +// press (in which case the caller should report a roll over error to the host). +// +//***************************************************************************** +static tBoolean +AddKeyToPressedList(tHIDKeyboardInstance *psInst, unsigned char ucUsageCode) +{ + unsigned long ulLoop; + tBoolean bRetcode; + + // + // Assume all is well until we determine otherwise. + // + bRetcode = true; + + // + // Look through the list of existing pressed keys to see if the new one + // is already there. + // + for(ulLoop = 0; ulLoop < (unsigned long)psInst->ucKeyCount; ulLoop++) + { + // + // Is this key already included in the list of keys in the pressed + // state? + // + if(ucUsageCode == psInst->pucKeysPressed[ulLoop]) + { + // + // Yes - drop out. + // + break; + } + } + + // + // If we exited the loop at the end of the existing key presses, this + // key does not exist already so add it if space exists. + // + if(ulLoop >= psInst->ucKeyCount) + { + if(psInst->ucKeyCount < KEYB_MAX_CHARS_PER_REPORT) + { + // + // We have room so store the new key press in the list. + // + psInst->pucKeysPressed[psInst->ucKeyCount] = ucUsageCode; + psInst->ucKeyCount++; + bRetcode = true; + } + else + { + // + // We have no room for the new key - declare a rollover error. + // + bRetcode = false; + } + } + + return(bRetcode); +} + +//***************************************************************************** +// +// Remove the supplied usage code from the list of keys currently in the +// pressed state. +// +// \param ucUsageCode is the HID usage code of the newly released key. +// +// This function removes the supplied usage code from the global list of keys +// which are currently pressed. The return code indicates whether the key was +// found in the list. On exit, the list will have been cleaned up to ensure +// that all key presses are contiguous starting at the first entry. +// +// \return Returns \b true if the usage code was found and removed from the +// list or \b false if the code was not found. The caller need not pass a new +// report to the host if \b false is returned since the key list will not have +// changed. +// +//***************************************************************************** +static tBoolean +RemoveKeyFromPressedList(tHIDKeyboardInstance *psInst, + unsigned char ucUsageCode) +{ + unsigned long ulLoop; + unsigned long ulPos; + + // + // Keep the compiler happy by setting ulPos to something. + // + ulPos = 0; + + // + // Find the usage code in the current list. + // + for(ulLoop = 0; ulLoop < KEYB_MAX_CHARS_PER_REPORT; ulLoop++) + { + if(psInst->pucKeysPressed[ulLoop] == ucUsageCode) + { + ulPos = ulLoop; + break; + } + } + + // + // If we dropped out at the end of the loop, we couldn't find the code so + // just return false. + // + if(ulLoop == KEYB_MAX_CHARS_PER_REPORT) + { + return(false); + } + + // + // Now shuffle all the values to the right of the usage code we found + // down one position to fill the gap left by removing it. + // + for(ulLoop = (ulPos + 1); ulLoop < KEYB_MAX_CHARS_PER_REPORT; ulLoop++) + { + psInst->pucKeysPressed[ulLoop - 1] = psInst->pucKeysPressed[ulLoop]; + } + + // + // Clear the last entry in the array and adjust the number of keys in the + // array. + // + psInst->pucKeysPressed[KEYB_MAX_CHARS_PER_REPORT - 1] = + HID_KEYB_USAGE_RESERVED; + psInst->ucKeyCount--; + + // + // Tell the caller we were successful. + // + return(true); +} + +//***************************************************************************** +// +//! Initializes HID keyboard device operation for a given USB controller. +//! +//! \param ulIndex is the index of the USB controller which is to be +//! initialized for HID keyboard device operation. +//! \param psDevice points to a structure containing parameters customizing +//! the operation of the HID keyboard device. +//! +//! An application wishing to offer a USB HID keyboard interface to a USB host +//! must call this function to initialize the USB controller and attach the +//! keyboard device to the USB bus. This function performs all required USB +//! initialization. +//! +//! On successful completion, this function will return the \e psDevice pointer +//! passed to it. This must be passed on all future calls to the HID keyboard +//! device driver. +//! +//! When a host connects and configures the device, the application callback +//! will receive \b USB_EVENT_CONNECTED after which calls can be made to +//! USBDHIDKeyboardKeyStateChange() to report key presses and releases to the +//! USB host. +//! +//! \note The application must not make any calls to the lower level USB device +//! interfaces if interacting with USB via the USB HID keyboard device class +//! API. Doing so will cause unpredictable (though almost certainly +//! unpleasant) behavior. +//! +//! \return Returns NULL on failure or the psDevice pointer on success. +// +//***************************************************************************** +void * +USBDHIDKeyboardInit(unsigned long ulIndex, + const tUSBDHIDKeyboardDevice *psDevice) +{ + void *pvRetcode; + tUSBDHIDDevice *psHIDDevice; + + // + // Check parameter validity. + // + ASSERT(psDevice); + ASSERT(psDevice->ppStringDescriptors); + ASSERT(psDevice->psPrivateHIDKbdData); + ASSERT(psDevice->pfnCallback); + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psDevice->psPrivateHIDKbdData->sHIDDevice; + + // + // Call the common initialization routine. + // + pvRetcode = USBDHIDKeyboardCompositeInit(ulIndex, psDevice); + + // + // If we initialized the HID layer successfully, pass our device pointer + // back as the return code, otherwise return NULL to indicate an error. + // + if(pvRetcode) + { + // + // Initialize the lower layer HID driver and pass it the various + // structures and descriptors necessary to declare that we are a + // keyboard. + // + pvRetcode = USBDHIDInit(ulIndex, psHIDDevice); + + return((void *)psDevice); + } + else + { + return((void *)0); + } +} + +//***************************************************************************** +// +//! Initializes HID keyboard device operation for a given USB controller. +//! +//! \param ulIndex is the index of the USB controller which is to be +//! initialized for HID keyboard device operation. +//! \param psDevice points to a structure containing parameters customizing +//! the operation of the HID keyboard device. +//! +//! This call is very similar to USBDKeyboardInit() except that it is used for +//! initializing an instance of the HID keyboard device for use in a composite +//! device. +//! +//! \return Returns zero on failure or a non-zero instance value that should be +//! used with the remaining USB HID Keyboard APIs. +// +//***************************************************************************** +void * +USBDHIDKeyboardCompositeInit(unsigned long ulIndex, + const tUSBDHIDKeyboardDevice *psDevice) +{ + tHIDKeyboardInstance *psInst; + unsigned long ulLoop; + tUSBDHIDDevice *psHIDDevice; + + // + // Check parameter validity. + // + ASSERT(psDevice); + ASSERT(psDevice->ppStringDescriptors); + ASSERT(psDevice->psPrivateHIDKbdData); + ASSERT(psDevice->pfnCallback); + + // + // Get a pointer to our instance data + // + psInst = psDevice->psPrivateHIDKbdData; + + // + // Initialize the various fields in our instance structure. + // + psInst->ucUSBConfigured = 0; + psInst->ucProtocol = USB_HID_PROTOCOL_REPORT; + psInst->sReportIdle.ucDuration4mS = 125; + psInst->sReportIdle.ucReportID = 0; + psInst->sReportIdle.ulTimeSinceReportmS = 0; + psInst->sReportIdle.usTimeTillNextmS = 0; + psInst->ucLEDStates = 0; + psInst->ucKeyCount = 0; + for(ulLoop = 0; ulLoop < KEYB_MAX_CHARS_PER_REPORT; ulLoop++) + { + psInst->pucKeysPressed[ulLoop] = HID_KEYB_USAGE_RESERVED; + } + + psInst->eKeyboardState = HID_KEYBOARD_STATE_UNCONFIGURED; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psDevice->psPrivateHIDKbdData->sHIDDevice; + + // + // Initialize the HID device class instance structure based on input from + // the caller. + // + psHIDDevice->usPID = psDevice->usPID; + psHIDDevice->usVID = psDevice->usVID; + psHIDDevice->usMaxPowermA = psDevice->usMaxPowermA; + psHIDDevice->ucPwrAttributes = psDevice->ucPwrAttributes; + psHIDDevice->ucSubclass = USB_HID_SCLASS_BOOT; + psHIDDevice->ucProtocol = USB_HID_PROTOCOL_KEYB; + psHIDDevice->ucNumInputReports = 1; + psHIDDevice->psReportIdle = 0; + psHIDDevice->pfnRxCallback = HIDKeyboardRxHandler; + psHIDDevice->pvRxCBData = (void *)psDevice; + psHIDDevice->pfnTxCallback = HIDKeyboardTxHandler; + psHIDDevice->pvTxCBData = (void *)psDevice; + psHIDDevice->bUseOutEndpoint = false, + psHIDDevice->psHIDDescriptor = &g_sKeybHIDDescriptor; + psHIDDevice->ppClassDescriptors = g_pKeybClassDescriptors; + psHIDDevice->ppStringDescriptors = psDevice->ppStringDescriptors; + psHIDDevice->ulNumStringDescriptors = psDevice->ulNumStringDescriptors; + psHIDDevice->psPrivateHIDData = &psInst->sHIDInstance; + psHIDDevice->psReportIdle = &psInst->sReportIdle; + + // + // Initialize the lower layer HID driver and pass it the various structures + // and descriptors necessary to declare that we are a keyboard. + // + return(USBDHIDCompositeInit(ulIndex, psHIDDevice)); +} + +//***************************************************************************** +// +//! Shuts down the HID keyboard device. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDHIDKeyboardInit(). +//! +//! This function terminates HID keyboard operation for the instance supplied +//! and removes the device from the USB bus. Following this call, the \e +//! pvInstance instance may not me used in any other call to the HID keyboard +//! device other than USBDHIDKeyboardInit(). +//! +//! \return None. +// +//***************************************************************************** +void +USBDHIDKeyboardTerm(void *pvInstance) +{ + tUSBDHIDKeyboardDevice *psDevice; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvInstance); + + // + // Get a pointer to the device. + // + psDevice = (tUSBDHIDKeyboardDevice *)pvInstance; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psDevice->psPrivateHIDKbdData->sHIDDevice; + + // + // Mark the device as no longer configured. + // + psDevice->psPrivateHIDKbdData->ucUSBConfigured = 0; + + // + // Terminate the low level HID driver. + // + USBDHIDTerm(psHIDDevice); +} + +//***************************************************************************** +// +//! Sets the client-specific pointer parameter for the keyboard callback. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDHIDKeyboardInit(). +//! \param pvCBData is the pointer that client wishes to be provided on each +//! event sent to the keyboard callback function. +//! +//! The client uses this function to change the callback pointer passed in +//! the first parameter on all callbacks to the \e pfnCallback function +//! passed on USBDHIDKeyboardInit(). +//! +//! If a client wants to make runtime changes in the callback pointer, it must +//! ensure that the pvInstance structure passed to USBDHIDKeyboardInit() resides +//! in RAM. If this structure is in flash, callback data changes will not be +//! possible. +//! +//! \return Returns the previous callback pointer that was set for this +//! instance. +// +//***************************************************************************** +void * +USBDHIDKeyboardSetCBData(void *pvInstance, void *pvCBData) +{ + void *pvOldCBData; + tUSBDHIDKeyboardDevice *psKeyboard; + + // + // Check for a NULL pointer in the device parameter. + // + ASSERT(pvInstance); + + // + // Get a pointer to our keyboard device. + // + psKeyboard = (tUSBDHIDKeyboardDevice *)pvInstance; + + // + // Save the old callback pointer and replace it with the new value. + // + pvOldCBData = psKeyboard->pvCBData; + psKeyboard->pvCBData = pvCBData; + + // + // Pass the old callback pointer back to the caller. + // + return(pvOldCBData); +} + +//***************************************************************************** +// +//! Reports a key state change to the USB host. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDHIDKeyboardInit(). +//! \param ucModifiers contains the states of each of the keyboard modifiers +//! (left/right shift, ctrl, alt or GUI keys). Valid values are logical OR +//! combinations of the labels \b HID_KEYB_LEFT_CTRL, \b HID_KEYB_LEFT_SHIFT, +//! \b HID_KEYB_LEFT_ALT, \b HID_KEYB_LEFT_GUI, \b HID_KEYB_RIGHT_CTRL, \b +//! HID_KEYB_RIGHT_SHIFT, \b HID_KEYB_RIGHT_ALT and \b HID_KEYB_RIGHT_GUI. +//! Presence of one of these bit flags indicates that the relevant modifier +//! key is pressed and absence indicates that it is released. +//! \param ucUsageCode is the usage code of the key whose state has changed. +//! If only modifier keys have changed, \b HID_KEYB_USAGE_RESERVED should be +//! passed in this parameter. +//! \param bPress is \b true if the key has been pressed or \b false if it has +//! been released. If only modifier keys have changed state, this parameter is +//! ignored. +//! +//! This function adds or removes a key usage code from the list of keys +//! currently pressed and schedules a report transmission to the host to +//! inform it of the new keyboard state. If the maximum number of simultaneous +//! key presses are already recorded, the report to the host will contain the +//! rollover error code, HID_KEYB_USAGE_ROLLOVER instead of key usage codes +//! and the caller will receive return code KEYB_ERR_TOO_MANY_KEYS. +//! +//! \return Returns \b KEYB_SUCCESS if the key usage code was added to or +//! removed from the current list successfully. \b KEYB_ERR_TOO_MANY_KEYS is +//! returned if an attempt is made to press a 7th key (the BIOS keyboard +//! protocol can report no more than 6 simultaneously pressed keys). If called +//! before the USB host has configured the device, \b KEYB_ERR_NOT_CONFIGURED +//! is returned and, if an error is reported while attempting to transmit the +//! report, \b KEYB_ERR_TX_ERROR is returned. If an attempt is made to remove +//! a key from the pressed list (by setting parameter \e bPressed to \b false) +//! but the key usage code is not found, \b KEYB_ERR_NOT_FOUND is returned. +// +//***************************************************************************** +unsigned long +USBDHIDKeyboardKeyStateChange(void *pvInstance, unsigned char ucModifiers, + unsigned char ucUsageCode, tBoolean bPress) +{ + tBoolean bRetcode; + unsigned long ulLoop; + unsigned long ulCount; + tHIDKeyboardInstance *psInst; + tUSBDHIDKeyboardDevice *psDevice; + tUSBDHIDDevice *psHIDDevice; + + psDevice = (tUSBDHIDKeyboardDevice *)pvInstance; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psDevice->psPrivateHIDKbdData->sHIDDevice; + + // + // Assume all is well until we determine otherwise. + // + bRetcode = true; + + // + // Get a pointer to our instance data + // + psInst = psDevice->psPrivateHIDKbdData; + + // + // Update the global keyboard report with the information passed. + // + psInst->pucReport[0] = ucModifiers; + psInst->pucReport[1] = 0; + + // + // Were we passed a usage code for a new key press or release or was + // this call just telling us about a modifier change? + // + if(ucUsageCode != HID_KEYB_USAGE_RESERVED) + { + // + // Has a key been pressed or released? + // + if(bPress) + { + // + // A key has been pressed - add it to the list if there is space an + // and the key is not already in the list. + // + bRetcode = AddKeyToPressedList(psInst, ucUsageCode); + } + else + { + // + // A key has been released - remove it from the list. + // + bRetcode = RemoveKeyFromPressedList(psInst, ucUsageCode); + + // + // The return code here indicates whether the key was found. If it + // wasn't, the list has not changes so merely exit at this point + // without sending anything to the host. + // + if(!bRetcode) + { + return(KEYB_ERR_NOT_FOUND); + } + } + + // + // Build the report from the current list of keys. If we added a key + // and got a bad return code indicating a roll over error, we need to + // send a roll over report + // + for(ulLoop = 0; ulLoop < KEYB_MAX_CHARS_PER_REPORT; ulLoop++) + { + psInst->pucReport[2 + ulLoop] = (bRetcode ? + psInst->pucKeysPressed[ulLoop] : HID_KEYB_USAGE_ROLLOVER); + } + } + + // + // If we are not configured, return an error here before trying to send + // anything. + // + if(!psInst->ucUSBConfigured) + { + return(KEYB_ERR_NOT_CONFIGURED); + } + + // + // Only send a report if the transmitter is currently free. + // + if(USBDHIDTxPacketAvailable((void *)psHIDDevice)) + { + // + // Send the report to the host. + // + psInst->eKeyboardState = HID_KEYBOARD_STATE_SEND; + ulCount = USBDHIDReportWrite((void *)psHIDDevice, + psInst->pucReport, KEYB_IN_REPORT_SIZE, + true); + + // + // Did we schedule a packet for transmission correctly? + // + if(!ulCount) + { + // + // No - report the error to the caller. + // + return(KEYB_ERR_TX_ERROR); + } + } + else + { + // + // We can't send the report immediately so mark the instance so that + // it is sent next time the transmitter is free. + // + psInst->bChangeMade = true; + } + + // + // If we get this far, the key information was sent successfully. Are + // too many keys currently pressed, though? + // + return(bRetcode ? KEYB_SUCCESS : KEYB_ERR_TOO_MANY_KEYS); +} + +//***************************************************************************** +// +//! Reports the device power status (bus or self powered) to the USB library. +//! +//! \param pvInstance is the pointer to the keyboard device instance structure. +//! \param ucPower indicates the current power status, either \b +//! USB_STATUS_SELF_PWR or \b USB_STATUS_BUS_PWR. +//! +//! Applications which support switching between bus or self powered +//! operation should call this function whenever the power source changes +//! to indicate the current power status to the USB library. This information +//! is required by the USB library to allow correct responses to be provided +//! when the host requests status from the device. +//! +//! \return None. +// +//***************************************************************************** +void +USBDHIDKeyboardPowerStatusSet(void *pvInstance, unsigned char ucPower) +{ + tUSBDHIDKeyboardDevice *psDevice; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvInstance); + + // + // Get the keyboard device pointer. + // + psDevice = (tUSBDHIDKeyboardDevice *)pvInstance; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psDevice->psPrivateHIDKbdData->sHIDDevice; + + // + // Pass the request through to the lower layer. + // + USBDHIDPowerStatusSet((void *)psHIDDevice, ucPower); +} + +//***************************************************************************** +// +//! Requests a remote wake up to resume communication when in suspended state. +//! +//! \param pvInstance is the pointer to the keyboard device instance structure. +//! +//! When the bus is suspended, an application which supports remote wake up +//! (advertised to the host via the configuration descriptor) may call this +//! function to initiate remote wake up signaling to the host. If the remote +//! wake up feature has not been disabled by the host, this will cause the bus +//! to resume operation within 20mS. If the host has disabled remote wake up, +//! \b false will be returned to indicate that the wake up request was not +//! successful. +//! +//! \return Returns \b true if the remote wake up is not disabled and the +//! signaling was started or \b false if remote wake up is disabled or if +//! signaling is currently ongoing following a previous call to this function. +// +//***************************************************************************** +tBoolean +USBDHIDKeyboardRemoteWakeupRequest(void *pvInstance) +{ + tUSBDHIDKeyboardDevice *psDevice; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvInstance); + + // + // Get the keyboard device pointer. + // + psDevice = (tUSBDHIDKeyboardDevice *)pvInstance; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psDevice->psPrivateHIDKbdData->sHIDDevice; + + // + // Pass the request through to the lower layer. + // + return(USBDHIDRemoteWakeupRequest((void *)psHIDDevice)); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/device/usbdhidkeyb.h b/usblib/device/usbdhidkeyb.h new file mode 100644 index 0000000..1f03332 --- /dev/null +++ b/usblib/device/usbdhidkeyb.h @@ -0,0 +1,382 @@ +//***************************************************************************** +// +// usbdhidkeyb.h - Definitions used by HID keyboard class devices. +// +// Copyright (c) 2008-2012 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#ifndef __USBDHIDKEYB_H__ +#define __USBDHIDKEYB_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup hid_keyboard_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! The maximum number of simultaneously-pressed, non-modifier keys that the +//! HID BIOS keyboard protocol can send at once. Attempts to send more pressed +//! keys than this will result in a rollover error being reported to the host +//! and KEYB_ERR_TOO_MANY_KEYS being returned from +//! USBDHIDKeyboardKeyStateChange. +// +//***************************************************************************** +#define KEYB_MAX_CHARS_PER_REPORT \ + 6 + +//***************************************************************************** +// +// PRIVATE +// +// The first few sections of this header are private defines that are used by +// the USB HID keyboard code and are here only to help with the application +// allocating the correct amount of memory for the USB HID Keyboard device +// code. +// +//***************************************************************************** + +//***************************************************************************** +// +// PRIVATE +// +// This enumeration holds the various states that the keyboard can be in during +// normal operation. +// +//***************************************************************************** +typedef enum +{ + // + // Unconfigured. + // + HID_KEYBOARD_STATE_UNCONFIGURED, + + // + // No keys to send and not waiting on data. + // + HID_KEYBOARD_STATE_IDLE, + + // + // Waiting on report data from the host. + // + HID_KEYBOARD_STATE_WAIT_DATA, + + // + // Waiting on data to be sent out. + // + HID_KEYBOARD_STATE_SEND +} +tKeyboardState; + +//***************************************************************************** +// +// PRIVATE +// +// The size of the keyboard input and output reports. +// +//***************************************************************************** +#define KEYB_IN_REPORT_SIZE 8 +#define KEYB_OUT_REPORT_SIZE 1 + +//***************************************************************************** +// +// PRIVATE +// +// This structure defines the private instance data structure for the USB HID +// keyboard device. This structure forms the RAM workspace used by each +// instance of the keyboard. +// +//***************************************************************************** +typedef struct +{ + // + // The USB configuration number set by the host or 0 of the device is + // currently unconfigured. + // + unsigned char ucUSBConfigured; + + // + // The protocol requested by the host, USB_HID_PROTOCOL_BOOT or + // USB_HID_PROTOCOL_REPORT. + // + unsigned char ucProtocol; + + // + // The current states that the keyboard LEDs are to be set to. + // + volatile unsigned char ucLEDStates; + + // + // The total number of keys currently pressed. This indicates the number + // of key press entries in the pucKeysPressed array. + // + unsigned char ucKeyCount; + + // + // The current state of the keyboard interrupt IN endpoint. + // + volatile tKeyboardState eKeyboardState; + + // + // A flag to indicate that the application pressed or released a key + // but that we couldn't send the report immediately. + // + volatile tBoolean bChangeMade; + + // + // A buffer used to receive output reports from the host. + // + unsigned char pucDataBuffer[KEYB_OUT_REPORT_SIZE]; + + // + // A buffer used to hold the last input report sent to the host. + // + unsigned char pucReport[KEYB_IN_REPORT_SIZE]; + + // + // A buffer containing the usage codes of all non-modifier keys currently + // in the pressed state. + // + unsigned char pucKeysPressed[KEYB_MAX_CHARS_PER_REPORT]; + + // + // The idle timeout control structure for our input report. This is + // required by the lower level HID driver. + // + tHIDReportIdle sReportIdle; + + // + // The lower level HID driver's instance data. + // + tHIDInstance sHIDInstance; + + // + // This is needed for the lower level HID driver. + // + tUSBDHIDDevice sHIDDevice; +} +tHIDKeyboardInstance; + +#ifndef DEPRECATED +//***************************************************************************** +// +// The number of bytes of workspace required by the HID keyboard driver. +// The client must provide a block of RAM of at least this size in the +// psPrivateHIDKbdData field of the tUSBHIDKeyboardDevice structure passed on +// USBDHIDKeyboardInit(). +// +// This value is deprecated and should not be used, any new code should just +// pass in a psPrivateHIDKbdData structure in the psPrivateHIDKbdData field. +// +//***************************************************************************** +#define USB_HID_KEYB_WORKSPACE_SIZE (sizeof(tHIDKeyboardInstance)) +#endif + +//***************************************************************************** +// +//! This structure is used by the application to define operating parameters +//! for the HID keyboard device. +// +//***************************************************************************** +typedef struct +{ + // + //! The vendor ID that this device is to present in the device descriptor. + // + unsigned short usVID; + + // + //! The product ID that this device is to present in the device descriptor. + // + unsigned short usPID; + + // + //! The maximum power consumption of the device, expressed in milliamps. + // + unsigned short usMaxPowermA; + + // + //! Indicates whether the device is self- or bus-powered and whether or not + //! it supports remote wakeup. Valid values are USB_CONF_ATTR_SELF_PWR or + //! USB_CONF_ATTR_BUS_PWR, optionally ORed with USB_CONF_ATTR_RWAKE. + // + unsigned char ucPwrAttributes; + + //! A pointer to the callback function which will be called to notify + //! the application of general events and those related to reception of + //! Output and Feature reports via the (optional) interrupt OUT endpoint. + // + tUSBCallback pfnCallback; + + // + //! A client-supplied pointer which will be sent as the first + //! parameter in all calls made to the keyboard callback, + //! pfnCallback. + // + void *pvCBData; + + // + //! A pointer to the string descriptor array for this device. This array + //! must contain the following string descriptor pointers in this order. + //! Language descriptor, Manufacturer name string (language 1), Product + //! name string (language 1), Serial number string (language 1),HID + //! Interface description string (language 1), Configuration description + //! string (language 1). + //! + //! If supporting more than 1 language, the descriptor block (except for + //! string descriptor 0) must be repeated for each language defined in the + //! language descriptor. + // + const unsigned char * const *ppStringDescriptors; + + // + //! The number of descriptors provided in the ppStringDescriptors + //! array. This must be (1 + (5 * (num languages))). + // + unsigned long ulNumStringDescriptors; + + // + //! A pointer to private instance data for this device. This memory must + //! remain accessible for as long as the keyboard device is in use and must + //! not be modified by any code outside the HID keyboard driver. + // + tHIDKeyboardInstance *psPrivateHIDKbdData; +} +tUSBDHIDKeyboardDevice; + +//***************************************************************************** +// +// Keyboard-specific device class driver events +// +//***************************************************************************** + +//***************************************************************************** +// +//! This event indicates that the keyboard LED states are to be set. The +//! ulMsgValue parameter contains the requested state for each of the LEDs +//! defined as a collection of ORed bits where a 1 indicates that the LED is +//! to be turned on and a 0 indicates that it should be turned off. The +//! individual LED bits are defined using labels HID_KEYB_NUM_LOCK, +//! HID_KEYB_CAPS_LOCK, HID_KEYB_SCROLL_LOCK, HID_KEYB_COMPOSE and +//! HID_KEYB_KANA. +// +//***************************************************************************** +#define USBD_HID_KEYB_EVENT_SET_LEDS \ + USBD_HID_KEYB_EVENT_BASE + +//***************************************************************************** +// +//! This return code from USBDHIDKeyboardKeyStateChange indicates success. +// +//***************************************************************************** +#define KEYB_SUCCESS 0 + +//***************************************************************************** +// +//! This return code from USBDHIDKeyboardKeyStateChange indicates that an +//! attempt has been made to record more than 6 simultaneously pressed, +//! non-modifier keys. The USB HID BIOS keyboard protocol allows no more than +//! 6 pressed keys to be reported at one time. Until at least one key is +//! released, the device will report a roll over error to the host each time it +//! is asked for the keyboard input report. +// +//***************************************************************************** +#define KEYB_ERR_TOO_MANY_KEYS 1 + +//***************************************************************************** +// +//! This return code from USBDHIDKeyboardKeyStateChange indicates that an +//! error was reported while attempting to send a report to the host. A client +//! should assume that the host has disconnected if this return code is seen. +// +//***************************************************************************** +#define KEYB_ERR_TX_ERROR 2 + +//***************************************************************************** +// +//! USBDHIDKeyboardKeyStateChange returns this value if it is called with the +//! bPress parameter set to false but with a ucUsageCode parameter which does +//! does not indicate a key that is currently recorded as being pressed. This +//! may occur if an attempt was previously made to report more than 6 pressed +//! keys and the earlier pressed keys are released before the later ones. This +//! condition is benign and should not be used to indicate a host disconnection +//! or serious error. +// +//***************************************************************************** +#define KEYB_ERR_NOT_FOUND 3 + +//***************************************************************************** +// +//! USBDHIDKeyboardKeyStateChange returns this value if it is called before the +//! USB host has connected and configured the device. Any key usage code +//! passed will be stored and passed to the host once configuration completes. +// +//***************************************************************************** +#define KEYB_ERR_NOT_CONFIGURED 4 + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void *USBDHIDKeyboardInit(unsigned long ulIndex, + const tUSBDHIDKeyboardDevice *psDevice); +extern void *USBDHIDKeyboardCompositeInit(unsigned long ulIndex, + const tUSBDHIDKeyboardDevice *psDevice); +extern void USBDHIDKeyboardTerm(void *pvInstance); +extern void *USBDHIDKeyboardSetCBData(void *pvInstance, void *pvCBData); +extern unsigned long USBDHIDKeyboardKeyStateChange(void *pvInstance, + unsigned char ucModifiers, + unsigned char ucUsageCode, + tBoolean bPressed); +extern void USBDHIDKeyboardPowerStatusSet(void *pvInstance, + unsigned char ucPower); +extern tBoolean USBDHIDKeyboardRemoteWakeupRequest(void *pvInstance); + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBDHIDKEYB_H__ diff --git a/usblib/device/usbdhidmouse.c b/usblib/device/usbdhidmouse.c new file mode 100644 index 0000000..ebf7a9f --- /dev/null +++ b/usblib/device/usbdhidmouse.c @@ -0,0 +1,843 @@ +//***************************************************************************** +// +// usbdhidmouse.c - USB HID Mouse device class driver. +// +// Copyright (c) 2008-2012 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/device/usbdevice.h" +#include "usblib/usbhid.h" +#include "usblib/device/usbdhid.h" +#include "usblib/device/usbdhidmouse.h" + +//***************************************************************************** +// +//! \addtogroup hid_mouse_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The report descriptor for the mouse class device. +// +//***************************************************************************** +static const unsigned char g_pucMouseReportDescriptor[]= +{ + UsagePage(USB_HID_GENERIC_DESKTOP), + Usage(USB_HID_MOUSE), + Collection(USB_HID_APPLICATION), + Usage(USB_HID_POINTER), + Collection(USB_HID_PHYSICAL), + + // + // The buttons. + // + UsagePage(USB_HID_BUTTONS), + UsageMinimum(1), + UsageMaximum(3), + LogicalMinimum(0), + LogicalMaximum(1), + + // + // 3 - 1 bit values for the buttons. + // + ReportSize(1), + ReportCount(3), + Input(USB_HID_INPUT_DATA | USB_HID_INPUT_VARIABLE | + USB_HID_INPUT_ABS), + + // + // 1 - 5 bit unused constant value to fill the 8 bits. + // + ReportSize(5), + ReportCount(1), + Input(USB_HID_INPUT_CONSTANT | USB_HID_INPUT_ARRAY | + USB_HID_INPUT_ABS), + + // + // The X and Y axis. + // + UsagePage(USB_HID_GENERIC_DESKTOP), + Usage(USB_HID_X), + Usage(USB_HID_Y), + LogicalMinimum(-127), + LogicalMaximum(127), + + // + // 2 - 8 bit Values for x and y. + // + ReportSize(8), + ReportCount(2), + Input(USB_HID_INPUT_DATA | USB_HID_INPUT_VARIABLE | + USB_HID_INPUT_RELATIVE), + + // + // 2 - 8 bit Values for x and y. // Padding + // + ReportSize(8), + ReportCount(MOUSE_REPORT_SIZE - 3), + Input(USB_HID_INPUT_CONSTANT | USB_HID_INPUT_ARRAY | + USB_HID_INPUT_ABS), + + EndCollection, + EndCollection, +}; + +//***************************************************************************** +// +// The HID class descriptor table. For the mouse class, we have only a single +// report descriptor. +// +//***************************************************************************** +static const unsigned char * const g_pMouseClassDescriptors[] = +{ + g_pucMouseReportDescriptor +}; + +//***************************************************************************** +// +// The HID descriptor for the mouse device. +// +//***************************************************************************** +static const tHIDDescriptor g_sMouseHIDDescriptor = +{ + 9, // bLength + USB_HID_DTYPE_HID, // bDescriptorType + 0x111, // bcdHID (version 1.11 compliant) + 0, // bCountryCode (not localized) + 1, // bNumDescriptors + { + { + USB_HID_DTYPE_REPORT, // Report descriptor + sizeof(g_pucMouseReportDescriptor) // Size of report descriptor + } + } +}; + +//***************************************************************************** +// +// Forward references for mouse device callback functions. +// +//***************************************************************************** +static unsigned long HIDMouseRxHandler(void *pvCBData, + unsigned long ulEvent, + unsigned long ulMsgData, + void *pvMsgData); +static unsigned long HIDMouseTxHandler(void *pvCBData, + unsigned long ulEvent, + unsigned long ulMsgData, + void *pvMsgData); + +//***************************************************************************** +// +// The HID mouse report offsets for this mouse application. +// +//***************************************************************************** +#define HID_REPORT_BUTTONS 0 +#define HID_REPORT_X 1 +#define HID_REPORT_Y 2 + +//***************************************************************************** +// +// Main HID device class event handler function. +// +// \param pvCBData is the event callback pointer provided during USBDHIDInit(). +// This is a pointer to our HID device structure (&g_sHIDMouseDevice). +// \param ulEvent identifies the event we are being called back for. +// \param ulMsgData is an event-specific value. +// \param pvMsgData is an event-specific pointer. +// +// This function is called by the HID device class driver to inform the +// application of particular asynchronous events related to operation of the +// mouse HID device. +// +// \return Returns a value which is event-specific. +// +//***************************************************************************** +static unsigned long +HIDMouseRxHandler(void *pvCBData, unsigned long ulEvent, + unsigned long ulMsgData, void *pvMsgData) +{ + tHIDMouseInstance *psInst; + tUSBDHIDMouseDevice *psDevice; + + // + // Make sure we didn't get a NULL pointer. + // + ASSERT(pvCBData); + + // + // Get a pointer to our instance data + // + psDevice = (tUSBDHIDMouseDevice *)pvCBData; + psInst = psDevice->psPrivateHIDMouseData; + + // + // Which event were we sent? + // + switch (ulEvent) + { + // + // The host has connected to us and configured the device. + // + case USB_EVENT_CONNECTED: + { + psInst->ucUSBConfigured = true; + + // + // Pass the information on to the client. + // + psDevice->pfnCallback(psDevice->pvCBData, USB_EVENT_CONNECTED, + 0, (void *)0); + + break; + } + + // + // The host has disconnected from us. + // + case USB_EVENT_DISCONNECTED: + { + psInst->ucUSBConfigured = false; + + // + // Pass the information on to the client. + // + psDevice->pfnCallback(psDevice->pvCBData, USB_EVENT_DISCONNECTED, + 0, (void *)0); + + break; + } + + // + // The host is polling us for a particular report and the HID driver + // is asking for the latest version to transmit. + // + case USBD_HID_EVENT_IDLE_TIMEOUT: + case USBD_HID_EVENT_GET_REPORT: + { + // + // We only support a single input report so we don't need to check + // the ulMsgValue parameter in this case. Set the report pointer + // in *pvMsgData and return the length of the report in bytes. + // + *(unsigned char **)pvMsgData = psInst->pucReport; + return(8); + } + + // + // The device class driver has completed sending a report to the + // host in response to a Get_Report request. + // + case USBD_HID_EVENT_REPORT_SENT: + { + // + // We have nothing to do here. + // + break; + } + + // + // This event is sent in response to a host Set_Report request. The + // mouse device has no output reports so we return a NULL pointer and + // zero length to cause this request to be stalled. + // + case USBD_HID_EVENT_GET_REPORT_BUFFER: + { + // + // We are being asked for a report that does not exist for + // this device. Return 0 to indicate that we are not providing + // a buffer. + // + return(0); + } + + // + // The host is asking us to set either boot or report protocol (not + // that it makes any difference to this particular mouse). + // + case USBD_HID_EVENT_SET_PROTOCOL: + { + psInst->ucProtocol = ulMsgData; + break; + } + + // + // The host is asking us to tell it which protocol we are currently + // using, boot or request. + // + case USBD_HID_EVENT_GET_PROTOCOL: + { + return(psInst->ucProtocol); + } + + // + // Pass ERROR, SUSPEND and RESUME to the client unchanged. + // + case USB_EVENT_ERROR: + case USB_EVENT_SUSPEND: + case USB_EVENT_RESUME: + { + return(psDevice->pfnCallback(psDevice->pvCBData, ulEvent, + ulMsgData, pvMsgData)); + } + + // + // We ignore all other events. + // + default: + { + break; + } + } + return(0); +} + +//***************************************************************************** +// +// HID device class transmit channel event handler function. +// +// \param pvCBData is the event callback pointer provided during USBDHIDInit(). +// This is a pointer to our HID device structure (&g_sHIDMouseDevice). +// \param ulEvent identifies the event we are being called back for. +// \param ulMsgData is an event-specific value. +// \param pvMsgData is an event-specific pointer. +// +// This function is called by the HID device class driver to inform the +// application of particular asynchronous events related to report +// transmissions made using the interrupt IN endpoint. +// +// \return Returns a value which is event-specific. +// +//***************************************************************************** +static unsigned long +HIDMouseTxHandler(void *pvCBData, unsigned long ulEvent, + unsigned long ulMsgData, void *pvMsgData) +{ + tHIDMouseInstance *psInst; + tUSBDHIDMouseDevice *psDevice; + + // + // Make sure we didn't get a NULL pointer. + // + ASSERT(pvCBData); + + // + // Get a pointer to our instance data + // + psDevice = (tUSBDHIDMouseDevice *)pvCBData; + psInst = psDevice->psPrivateHIDMouseData; + + // + // Which event were we sent? + // + switch (ulEvent) + { + // + // A report transmitted via the interrupt IN endpoint was acknowledged + // by the host. + // + case USB_EVENT_TX_COMPLETE: + { + // + // Our last transmission is complete. + // + psInst->eMouseState = HID_MOUSE_STATE_IDLE; + + // + // Pass the event on to the client. + // + psDevice->pfnCallback(psDevice->pvCBData, USB_EVENT_TX_COMPLETE, + ulMsgData, (void *)0); + + break; + } + + // + // We ignore all other events related to transmission of reports via + // the interrupt IN endpoint. + // + default: + { + break; + } + } + + return(0); +} + +//***************************************************************************** +// +//! Initializes HID mouse device operation for a given USB controller. +//! +//! \param ulIndex is the index of the USB controller which is to be +//! initialized for HID mouse device operation. +//! \param psDevice points to a structure containing parameters customizing +//! the operation of the HID mouse device. +//! +//! An application wishing to offer a USB HID mouse interface to a USB host +//! must call this function to initialize the USB controller and attach the +//! mouse device to the USB bus. This function performs all required USB +//! initialization. +//! +//! On successful completion, this function will return the \e psDevice pointer +//! passed to it. This must be passed on all future calls to the HID mouse +//! device driver. +//! +//! When a host connects and configures the device, the application callback +//! will receive \b USB_EVENT_CONNECTED after which calls can be made to +//! USBDHIDMouseStateChange() to report pointer movement and button presses +//! to the host. +//! +//! \note The application must not make any calls to the lower level USB device +//! interfaces if interacting with USB via the USB HID mouse device API. +//! Doing so will cause unpredictable (though almost certainly unpleasant) +//! behavior. +//! +//! \return Returns NULL on failure or the psDevice pointer on success. +// +//***************************************************************************** +void * +USBDHIDMouseInit(unsigned long ulIndex, const tUSBDHIDMouseDevice *psDevice) +{ + void *pvRetcode; + tUSBDHIDDevice *psHIDDevice; + + // + // Check parameter validity. + // + ASSERT(psDevice); + ASSERT(psDevice->ppStringDescriptors); + ASSERT(psDevice->psPrivateHIDMouseData); + ASSERT(psDevice->pfnCallback); + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psDevice->psPrivateHIDMouseData->sHIDDevice; + + // + // Call the common initialization routine. + // + pvRetcode = USBDHIDMouseCompositeInit(ulIndex, psDevice); + + // + // If we initialized the HID layer successfully, pass our device pointer + // back as the return code, otherwise return NULL to indicate an error. + // + if(pvRetcode) + { + // + // Initialize the lower layer HID driver and pass it the various + // structures and descriptors necessary to declare that we are a + // keyboard. + // + pvRetcode = USBDHIDInit(ulIndex, psHIDDevice); + + return((void *)psDevice); + } + else + { + return((void *)0); + } +} + +//***************************************************************************** +// +//! Initializes HID mouse device operation for a given USB controller. +//! +//! \param ulIndex is the index of the USB controller which is to be +//! initialized for HID mouse device operation. +//! \param psDevice points to a structure containing parameters customizing +//! the operation of the HID mouse device. +//! +//! This call is very similar to USBDHIDMouseInit() except that it is used for +//! initializing an instance of the HID mouse device for use in a composite +//! device. +//! +//! \return Returns zero on failure or a non-zero instance value that should be +//! used with the remaining USB HID Mouse APIs. +// +//***************************************************************************** +void * +USBDHIDMouseCompositeInit(unsigned long ulIndex, + const tUSBDHIDMouseDevice *psDevice) +{ + tHIDMouseInstance *psInst; + tUSBDHIDDevice *psHIDDevice; + + + // + // Check parameter validity. + // + ASSERT(psDevice); + ASSERT(psDevice->ppStringDescriptors); + ASSERT(psDevice->psPrivateHIDMouseData); + ASSERT(psDevice->pfnCallback); + + // + // Get a pointer to our instance data + // + psInst = psDevice->psPrivateHIDMouseData; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psDevice->psPrivateHIDMouseData->sHIDDevice; + + // + // Initialize the various fields in our instance structure. + // + psInst->ucUSBConfigured = 0; + psInst->ucProtocol = USB_HID_PROTOCOL_REPORT; + psInst->sReportIdle.ucDuration4mS = 0; + psInst->sReportIdle.ucReportID = 0; + psInst->sReportIdle.ulTimeSinceReportmS = 0; + psInst->sReportIdle.usTimeTillNextmS = 0; + psInst->eMouseState = HID_MOUSE_STATE_UNCONFIGURED; + + // + // Initialize the HID device class instance structure based on input from + // the caller. + // + psHIDDevice->usPID = psDevice->usPID; + psHIDDevice->usVID = psDevice->usVID; + psHIDDevice->usMaxPowermA = psDevice->usMaxPowermA; + psHIDDevice->ucPwrAttributes = psDevice->ucPwrAttributes; + psHIDDevice->ucSubclass = USB_HID_SCLASS_BOOT; + psHIDDevice->ucProtocol = USB_HID_PROTOCOL_MOUSE; + psHIDDevice->ucNumInputReports = 1; + psHIDDevice->psReportIdle = &psInst->sReportIdle; + psHIDDevice->pfnRxCallback = HIDMouseRxHandler; + psHIDDevice->pvRxCBData = (void *)psDevice; + psHIDDevice->pfnTxCallback = HIDMouseTxHandler; + psHIDDevice->pvTxCBData = (void *)psDevice; + psHIDDevice->bUseOutEndpoint = false; + psHIDDevice->psHIDDescriptor = &g_sMouseHIDDescriptor; + psHIDDevice->ppClassDescriptors= g_pMouseClassDescriptors; + psHIDDevice->ppStringDescriptors = psDevice->ppStringDescriptors; + psHIDDevice->ulNumStringDescriptors = psDevice->ulNumStringDescriptors; + psHIDDevice->psPrivateHIDData = &psInst->sHIDInstance; + + // + // Initialize the lower layer HID driver and pass it the various structures + // and descriptors necessary to declare that we are a keyboard. + // + return(USBDHIDCompositeInit(ulIndex, psHIDDevice)); +} + +//***************************************************************************** +// +//! Shuts down the HID mouse device. +//! +//! \param pvInstance is the pointer to the device instance structure. +//! +//! This function terminates HID mouse operation for the instance supplied +//! and removes the device from the USB bus. Following this call, the \e +//! pvInstance instance may not me used in any other call to the HID mouse +//! device other than USBDHIDMouseInit(). +//! +//! \return None. +// +//***************************************************************************** +void +USBDHIDMouseTerm(void *pvInstance) +{ + tUSBDHIDMouseDevice *psDevice; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvInstance); + + // + // Get a pointer to the device. + // + psDevice = (tUSBDHIDMouseDevice *)pvInstance; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psDevice->psPrivateHIDMouseData->sHIDDevice; + + // + // Mark our device as no longer configured. + // + psDevice->psPrivateHIDMouseData->ucUSBConfigured = 0; + + // + // Terminate the low level HID driver. + // + USBDHIDTerm(psHIDDevice); +} + +//***************************************************************************** +// +//! Sets the client-specific pointer parameter for the mouse callback. +//! +//! \param pvInstance is the pointer to the mouse device instance structure. +//! \param pvCBData is the pointer that client wishes to be provided on each +//! event sent to the mouse callback function. +//! +//! The client uses this function to change the callback pointer passed in +//! the first parameter on all callbacks to the \e pfnCallback function +//! passed on USBDHIDMouseInit(). +//! +//! If a client wants to make runtime changes in the callback pointer, it must +//! ensure that the pvInstance structure passed to USBDHIDMouseInit() resides +//! in RAM. If this structure is in flash, callback data changes will not be +//! possible. +//! +//! \return Returns the previous callback pointer that was set for this +//! instance. +// +//***************************************************************************** +void * +USBDHIDMouseSetCBData(void *pvInstance, void *pvCBData) +{ + void *pvOldCBData; + tUSBDHIDMouseDevice *psMouse; + + // + // Check for a NULL pointer in the device parameter. + // + ASSERT(pvInstance); + + // + // Get a pointer to our mouse device. + // + psMouse = (tUSBDHIDMouseDevice *)pvInstance; + + // + // Save the old callback pointer and replace it with the new value. + // + pvOldCBData = psMouse->pvCBData; + psMouse->pvCBData = pvCBData; + + // + // Pass the old callback pointer back to the caller. + // + return(pvOldCBData); +} + +//***************************************************************************** +// +//! Reports a mouse state change, pointer movement or button press, to the USB +//! host. +//! +//! \param pvInstance is the pointer to the mouse device instance structure. +//! \param cDeltaX is the relative horizontal pointer movement that the +//! application wishes to report. Valid values are in the range [-127, 127] +//! with positive values indicating movement to the right. +//! \param cDeltaY is the relative vertical pointer movement that the +//! application wishes to report. Valid values are in the range [-127, 127] +//! with positive values indicating downward movement. +//! \param ucButtons is a bit mask indicating which (if any) of the three +//! mouse buttons is pressed. Valid values are logical OR combinations of +//! \e MOUSE_REPORT_BUTTON_1, \e MOUSE_REPORT_BUTTON_2 and \e +//! MOUSE_REPORT_BUTTON_3. +//! +//! This function is called to report changes in the mouse state to the USB +//! host. These changes can be movement of the pointer, reported relative to +//! its previous position, or changes in the states of up to 3 buttons that +//! the mouse may support. The return code indicates whether or not the +//! mouse report could be sent to the host. In cases where a previous +//! report is still being transmitted, \b MOUSE_ERR_TX_ERROR will be returned +//! and the state change will be ignored. +//! +//! \return Returns \b MOUSE_SUCCESS on success, \b MOUSE_ERR_TX_ERROR if an +//! error occurred while attempting to schedule transmission of the mouse +//! report to the host (typically due to a previous report which has not yet +//! completed transmission or due to disconnection of the host) or \b +//! MOUSE_ERR_NOT_CONFIGURED if called before a host has connected to and +//! configured the device. +// +//***************************************************************************** +unsigned long +USBDHIDMouseStateChange(void *pvInstance, char cDeltaX, char cDeltaY, + unsigned char ucButtons) +{ + unsigned long ulRetcode; + unsigned long ulCount; + tHIDMouseInstance *psInst; + tUSBDHIDMouseDevice *psDevice; + tUSBDHIDDevice *psHIDDevice; + + // + // Get a pointer to the device. + // + psDevice = (tUSBDHIDMouseDevice *)pvInstance; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psDevice->psPrivateHIDMouseData->sHIDDevice; + + // + // Get a pointer to our instance data + // + psInst = psDevice->psPrivateHIDMouseData; + + // + // Update the global mouse report with the information passed. + // + psInst->pucReport[HID_REPORT_BUTTONS] = ucButtons; + psInst->pucReport[HID_REPORT_X] = (unsigned char)cDeltaX; + psInst->pucReport[HID_REPORT_Y] = (unsigned char)cDeltaY; + + // + // If we are not configured, return an error here before trying to send + // anything. + // + if(!psInst->ucUSBConfigured) + { + return(MOUSE_ERR_NOT_CONFIGURED); + } + + // + // Only send a report if the transmitter is currently free. + // + if(USBDHIDTxPacketAvailable((void *)psHIDDevice)) + { + // + // Send the report to the host. + // + psInst->eMouseState = HID_MOUSE_STATE_SEND; + ulCount = USBDHIDReportWrite((void *)psHIDDevice, + psInst->pucReport, MOUSE_REPORT_SIZE, + true); + + // + // Did we schedule a packet for transmission correctly? + // + if(!ulCount) + { + // + // No - report the error to the caller. + // + ulRetcode = MOUSE_ERR_TX_ERROR; + } + else + { + ulRetcode = MOUSE_SUCCESS; + } + } + else + { + ulRetcode = MOUSE_ERR_TX_ERROR; + } + // + // Return the relevant error code to the caller. + // + return(ulRetcode); +} + +//***************************************************************************** +// +//! Reports the device power status (bus- or self-powered) to the USB library. +//! +//! \param pvInstance is the pointer to the mouse device instance structure. +//! \param ucPower indicates the current power status, either \b +//! USB_STATUS_SELF_PWR or \b USB_STATUS_BUS_PWR. +//! +//! Applications which support switching between bus- or self-powered +//! operation should call this function whenever the power source changes +//! to indicate the current power status to the USB library. This information +//! is required by the USB library to allow correct responses to be provided +//! when the host requests status from the device. +//! +//! \return None. +// +//***************************************************************************** +void +USBDHIDMousePowerStatusSet(void *pvInstance, unsigned char ucPower) +{ + tUSBDHIDMouseDevice *psDevice; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvInstance); + + // + // Get the keyboard device pointer. + // + psDevice = (tUSBDHIDMouseDevice *)pvInstance; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psDevice->psPrivateHIDMouseData->sHIDDevice; + + // + // Pass the request through to the lower layer. + // + USBDHIDPowerStatusSet((void *)psHIDDevice, ucPower); +} + +//***************************************************************************** +// +//! Requests a remote wake up to resume communication when in suspended state. +//! +//! \param pvInstance is the pointer to the mouse device instance structure. +//! +//! When the bus is suspended, an application which supports remote wake up +//! (advertised to the host via the configuration descriptor) may call this +//! function to initiate remote wake up signaling to the host. If the remote +//! wake up feature has not been disabled by the host, this will cause the bus +//! to resume operation within 20mS. If the host has disabled remote wake up, +//! \b false will be returned to indicate that the wake up request was not +//! successful. +//! +//! \return Returns \b true if the remote wake up is not disabled and the +//! signaling was started or \b false if remote wake up is disabled or if +//! signaling is currently ongoing following a previous call to this function. +// +//***************************************************************************** +tBoolean +USBDHIDMouseRemoteWakeupRequest(void *pvInstance) +{ + tUSBDHIDMouseDevice *psDevice; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvInstance); + + // + // Get the keyboard device pointer. + // + psDevice = (tUSBDHIDMouseDevice *)pvInstance; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psDevice->psPrivateHIDMouseData->sHIDDevice; + + // + // Pass the request through to the lower layer. + // + return(USBDHIDRemoteWakeupRequest((void *)&psHIDDevice)); +} +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/device/usbdhidmouse.h b/usblib/device/usbdhidmouse.h new file mode 100644 index 0000000..8ca80e6 --- /dev/null +++ b/usblib/device/usbdhidmouse.h @@ -0,0 +1,321 @@ +//***************************************************************************** +// +// usbdhidmouse.h - Public header file for the USB HID Mouse device class +// driver +// +// Copyright (c) 2008-2012 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#ifndef __USBDHIDMOUSE_H__ +#define __USBDHIDMOUSE_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup hid_mouse_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// PRIVATE +// +// The first few sections of this header are private defines that are used by +// the USB HID mouse code and are here only to help with the application +// allocating the correct amount of memory for the HID mouse device code. +// +//***************************************************************************** + +//***************************************************************************** +// +// PRIVATE +// +// The size of the mouse input report sent to the host. +// +//***************************************************************************** +#define MOUSE_REPORT_SIZE 3 + +//***************************************************************************** +// +// PRIVATE +// +// This enumeration holds the various states that the mouse can be in during +// normal operation. +// +//***************************************************************************** +typedef enum +{ + // + // Unconfigured. + // + HID_MOUSE_STATE_UNCONFIGURED, + + // + // No keys to send and not waiting on data. + // + HID_MOUSE_STATE_IDLE, + + // + // Waiting on report data from the host. + // + HID_MOUSE_STATE_WAIT_DATA, + + // + // Waiting on data to be sent out. + // + HID_MOUSE_STATE_SEND +} +tMouseState; + +//***************************************************************************** +// +// PRIVATE +// +// This structure provides the private instance data structure for the USB +// HID Mouse device. This structure forms the RAM workspace used by each +// instance of the mouse. +// +//***************************************************************************** +typedef struct +{ + // + // The USB configuration number set by the host or 0 of the device is + // currently unconfigured. + // + unsigned char ucUSBConfigured; + + // + // The protocol requested by the host, USB_HID_PROTOCOL_BOOT or + // USB_HID_PROTOCOL_REPORT. + // + unsigned char ucProtocol; + + // + // A buffer used to hold the last input report sent to the host. + // + unsigned char pucReport[MOUSE_REPORT_SIZE]; + + // + // The current state of the mouse interrupt IN endpoint. + // + volatile tMouseState eMouseState; + + // + // The idle timeout control structure for our input report. This is + // required by the lower level HID driver. + // + tHIDReportIdle sReportIdle; + + // + // The lower level HID driver's instance data. + // + tHIDInstance sHIDInstance; + + // + // This is needed for the lower level HID driver. + // + tUSBDHIDDevice sHIDDevice; +} +tHIDMouseInstance; + +#ifdef DEPRECATED +//***************************************************************************** +// +// The number of bytes of workspace required by the HID mouse driver. +// The client must provide a block of RAM of at least this size in the +// tHIDMouseInstance field of the tUSBHIDMouseDevice structure passed on +// USBDHIDMouseInit(). The HID mouse driver needs space for the generic HID +// interface + the Mouse Report Buffer + HID mouse interface. +// +// This value is deprecated and should not be used, any new code should just +// pass in a tHIDMouseInstance structure in the psPrivateHIDMouseData field. +// +//***************************************************************************** +#define USB_HID_MOUSE_WORKSPACE_SIZE \ + (sizeof(tHIDMouseInstance)) +#endif + +//***************************************************************************** +// +//! This structure is used by the application to define operating parameters +//! for the HID mouse device. +// +//***************************************************************************** +typedef struct +{ + // + //! The vendor ID that this device is to present in the device descriptor. + // + unsigned short usVID; + + // + //! The product ID that this device is to present in the device descriptor. + // + unsigned short usPID; + + // + //! The maximum power consumption of the device, expressed in milliamps. + // + unsigned short usMaxPowermA; + + // + //! Indicates whether the device is self- or bus-powered and whether or not + //! it supports remote wakeup. Valid values are USB_CONF_ATTR_SELF_PWR or + //! USB_CONF_ATTR_BUS_PWR, optionally ORed with USB_CONF_ATTR_RWAKE. + // + unsigned char ucPwrAttributes; + + // + //! A pointer to the callback function which will be called to notify + //! the application of events relating to the operation of the mouse. + // + tUSBCallback pfnCallback; + + // + //! A client-supplied pointer which will be sent as the first + //! parameter in all calls made to the mouse callback, pfnCallback. + // + void *pvCBData; + + // + //! A pointer to the string descriptor array for this device. This array + //! must contain the following string descriptor pointers in this order. + //! Language descriptor, Manufacturer name string (language 1), Product + //! name string (language 1), Serial number string (language 1),HID + //! Interface description string (language 1), Configuration description + //! string (language 1). + //! + //! If supporting more than 1 language, the descriptor block (except for + //! string descriptor 0) must be repeated for each language defined in the + //! language descriptor. + // + const unsigned char * const *ppStringDescriptors; + + // + //! The number of descriptors provided in the ppStringDescriptors + //! array. This must be (1 + (5 * (num languages))). + // + unsigned long ulNumStringDescriptors; + + // + //! A pointer to private instance data for this device. This memory must + //! remain accessible for as long as the mouse device is in use and must + //! not be modified by any code outside the HID mouse driver. + // + tHIDMouseInstance *psPrivateHIDMouseData; +} +tUSBDHIDMouseDevice; + +//***************************************************************************** +// +//! This return code from USBDHIDMouseStateChange indicates success. +// +//***************************************************************************** +#define MOUSE_SUCCESS 0 + +//***************************************************************************** +// +//! This return code from USBDHIDMouseStateChange indicates that an error was +//! reported while attempting to send a report to the host. A client should +//! assume that the host has disconnected if this return code is seen. +// +//***************************************************************************** +#define MOUSE_ERR_TX_ERROR 2 + +//***************************************************************************** +// +//! USBDHIDMouseStateChange returns this value if it is called before the +//! USB host has connected and configured the device. All mouse state +//! information passed on the call will have been ignored. +// +//***************************************************************************** +#define MOUSE_ERR_NOT_CONFIGURED \ + 4 + +//***************************************************************************** +// +//! Setting this bit in the ucButtons parameter to USBDHIDMouseStateChange +//! indicates to the USB host that button 1 on the mouse is pressed. +// +//***************************************************************************** +#define MOUSE_REPORT_BUTTON_1 0x01 + +//***************************************************************************** +// +//! Setting this bit in the ucButtons parameter to USBDHIDMouseStateChange +//! indicates to the USB host that button 2 on the mouse is pressed. +// +//***************************************************************************** +#define MOUSE_REPORT_BUTTON_2 0x02 + +//***************************************************************************** +// +//! Setting this bit in the ucButtons parameter to USBDHIDMouseStateChange +//! indicates to the USB host that button 3 on the mouse is pressed. +// +//***************************************************************************** +#define MOUSE_REPORT_BUTTON_3 0x04 + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void *USBDHIDMouseInit(unsigned long ulIndex, + const tUSBDHIDMouseDevice *psDevice); +extern void *USBDHIDMouseCompositeInit(unsigned long ulIndex, + const tUSBDHIDMouseDevice *psDevice); +extern void USBDHIDMouseTerm(void *pvInstance); +extern void *USBDHIDMouseSetCBData(void *pvInstance, void *pvCBData); +extern unsigned long USBDHIDMouseStateChange(void *pvInstance, char cDeltaX, + char cDeltaY, + unsigned char ucButtons); +extern void USBDHIDMousePowerStatusSet(void *pvInstance, + unsigned char ucPower); +extern tBoolean USBDHIDMouseRemoteWakeupRequest(void *pvInstance); + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBDHIDMOUSE_H__ diff --git a/usblib/device/usbdmsc.c b/usblib/device/usbdmsc.c new file mode 100644 index 0000000..e563423 --- /dev/null +++ b/usblib/device/usbdmsc.c @@ -0,0 +1,2130 @@ +//***************************************************************************** +// +// usbdmsc.c - USB mass storage device class driver. +// +// Copyright (c) 2009-2012 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/sysctl.h" +#include "driverlib/usb.h" +#include "driverlib/udma.h" +#include "usblib/usblib.h" +#include "usblib/usbmsc.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbdmsc.h" + +//***************************************************************************** +// +//! \addtogroup msc_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// These are the internal flags used with the ulFlags member variable. +// +//***************************************************************************** +#define USBD_FLAG_DMA_IN 0x00000001 +#define USBD_FLAG_DMA_OUT 0x00000002 + +//***************************************************************************** +// +// The subset of endpoint status flags that we consider to be reception +// errors. These are passed to the client via USB_EVENT_ERROR if seen. +// +//***************************************************************************** +#define USB_RX_ERROR_FLAGS (USBERR_DEV_RX_DATA_ERROR | \ + USBERR_DEV_RX_OVERRUN | \ + USBERR_DEV_RX_FIFO_FULL) + +//***************************************************************************** +// +// These are fields that are used by the USB descriptors for the Mass Storage +// Class. +// +//***************************************************************************** +#define USB_MSC_SUBCLASS_SCSI 0x6 +#define USB_MSC_PROTO_BULKONLY 0x50 + +//***************************************************************************** +// +// Endpoints to use for each of the required endpoints in the driver. +// +//***************************************************************************** +#define DATA_IN_ENDPOINT USB_EP_1 +#define DATA_IN_DMA_CHANNEL UDMA_CHANNEL_USBEP1TX +#define DATA_OUT_ENDPOINT USB_EP_1 +#define DATA_OUT_DMA_CHANNEL UDMA_CHANNEL_USBEP1RX + +//***************************************************************************** +// +// Maximum packet size for the bulk endpoints is 64 bytes. +// +//***************************************************************************** +#define DATA_IN_EP_MAX_SIZE 64 +#define DATA_OUT_EP_MAX_SIZE 64 + +//***************************************************************************** +// +// These defines control the sizes of USB transfers for data and commands. +// +//***************************************************************************** +#define MAX_TRANSFER_SIZE 512 +#define COMMAND_BUFFER_SIZE 64 + +//***************************************************************************** +// +// The local buffer used to read in commands and process them. +// +//***************************************************************************** +static unsigned char g_pucCommand[COMMAND_BUFFER_SIZE]; + +//***************************************************************************** +// +// The current transfer state is held in these variables. +// +//***************************************************************************** +static tMSCCSW g_sSCSICSW; + +//***************************************************************************** +// +// The current state for the SCSI commands that are being handled and are +// stored in the tMSCInstance.ucSCSIState structure member. +// +//***************************************************************************** + +// +// No command in process. +// +#define STATE_SCSI_IDLE 0x00 + +// +// Sending and reading logical blocks. +// +#define STATE_SCSI_SEND_BLOCKS 0x01 + +// +// Receiving and writing logical blocks. +// +#define STATE_SCSI_RECEIVE_BLOCKS 0x02 + +// +// Send the status once the previous transfer is complete. +// +#define STATE_SCSI_SEND_STATUS 0x03 + +// +// Status was prepared to be sent and now waiting for it to have gone out. +// +#define STATE_SCSI_SENT_STATUS 0x04 + +//***************************************************************************** +// +// Device Descriptor. This is stored in RAM to allow several fields to be +// changed at runtime based on the client's requirements. +// +//***************************************************************************** +static unsigned char g_pMSCDeviceDescriptor[] = +{ + 18, // Size of this structure. + USB_DTYPE_DEVICE, // Type of this structure. + USBShort(0x110), // USB version 1.1 (if we say 2.0, hosts assume + // high-speed - see USB 2.0 spec 9.2.6.6) + 0, // USB Device Class (spec 5.1.1) + 0, // USB Device Sub-class (spec 5.1.1) + 0, // USB Device protocol (spec 5.1.1) + 64, // Maximum packet size for default pipe. + USBShort(0), // Vendor ID (filled in during USBDCDCInit). + USBShort(0), // Product ID (filled in during USBDCDCInit). + USBShort(0x100), // Device Version BCD. + 1, // Manufacturer string identifier. + 2, // Product string identifier. + 3, // Product serial number. + 1 // Number of configurations. +}; + +//***************************************************************************** +// +// Mass storage device configuration descriptor. +// +// It is vital that the configuration descriptor bConfigurationValue field +// (byte 6) is 1 for the first configuration and increments by 1 for each +// additional configuration defined here. This relationship is assumed in the +// device stack for simplicity even though the USB 2.0 specification imposes +// no such restriction on the bConfigurationValue values. +// +// Note that this structure is deliberately located in RAM since we need to +// be able to patch some values in it based on client requirements. +// +//***************************************************************************** +static unsigned char g_pMSCDescriptor[] = +{ + // + // Configuration descriptor header. + // + 9, // Size of the configuration descriptor. + USB_DTYPE_CONFIGURATION, // Type of this descriptor. + USBShort(32), // The total size of this full structure. + 1, // The number of interfaces in this + // configuration. + 1, // The unique value for this configuration. + 0, // The string identifier that describes this + // configuration. + USB_CONF_ATTR_SELF_PWR, // Bus Powered, Self Powered, remote wake up. + 250, // The maximum power in 2mA increments. +}; + +//***************************************************************************** +// +// The remainder of the configuration descriptor is stored in flash since we +// don't need to modify anything in it at runtime. +// +//***************************************************************************** +const unsigned char g_pMSCInterface[] = +{ + // + // Vendor-specific Interface Descriptor. + // + 9, // Size of the interface descriptor. + USB_DTYPE_INTERFACE, // Type of this descriptor. + 0, // The index for this interface. + 0, // The alternate setting for this interface. + 2, // The number of endpoints used by this + // interface. + USB_CLASS_MASS_STORAGE, // The interface class + USB_MSC_SUBCLASS_SCSI, // The interface sub-class. + USB_MSC_PROTO_BULKONLY, // The interface protocol for the sub-class + // specified above. + 0, // The string index for this interface. + + // + // Endpoint Descriptor + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_IN | USB_EP_TO_INDEX(DATA_IN_ENDPOINT), + USB_EP_ATTR_BULK, // Endpoint is a bulk endpoint. + USBShort(DATA_IN_EP_MAX_SIZE), // The maximum packet size. + 0, // The polling interval for this endpoint. + + // + // Endpoint Descriptor + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_OUT | USB_EP_TO_INDEX(DATA_OUT_ENDPOINT), + USB_EP_ATTR_BULK, // Endpoint is a bulk endpoint. + USBShort(DATA_OUT_EP_MAX_SIZE), // The maximum packet size. + 0, // The polling interval for this endpoint. +}; + +//***************************************************************************** +// +// The mass storage configuration descriptor is defined as two sections, +// one containing just the 9 byte USB configuration descriptor and the other +// containing everything else that is sent to the host along with it. +// +//***************************************************************************** +const tConfigSection g_sMSCConfigSection = +{ + sizeof(g_pMSCDescriptor), + g_pMSCDescriptor +}; + +const tConfigSection g_sMSCInterfaceSection = +{ + sizeof(g_pMSCInterface), + g_pMSCInterface +}; + +//***************************************************************************** +// +// This array lists all the sections that must be concatenated to make a +// single, complete bulk device configuration descriptor. +// +//***************************************************************************** +const tConfigSection *g_psMSCSections[] = +{ + &g_sMSCConfigSection, + &g_sMSCInterfaceSection +}; + +#define NUM_MSC_SECTIONS (sizeof(g_psMSCSections) / sizeof(tConfigSection *)) + +//***************************************************************************** +// +// The header for the single configuration we support. This is the root of +// the data structure that defines all the bits and pieces that are pulled +// together to generate the configuration descriptor. +// +//***************************************************************************** +const tConfigHeader g_sMSCConfigHeader = +{ + NUM_MSC_SECTIONS, + g_psMSCSections +}; + +//***************************************************************************** +// +// Configuration Descriptor. +// +//***************************************************************************** +const tConfigHeader * const g_pMSCConfigDescriptors[] = +{ + &g_sMSCConfigHeader +}; + +//***************************************************************************** +// +// Various internal handlers needed by this class. +// +//***************************************************************************** +static void HandleDisconnect(void *pvInstance); +static void ConfigChangeHandler(void *pvInstance, unsigned long ulValue); +static void HandleEndpoints(void *pvInstance, unsigned long ulStatus); +static void HandleRequests(void *pvInstance, tUSBRequest *pUSBRequest); +static void USBDSCSISendStatus(const tUSBDMSCDevice *psDevice); +unsigned long USBDSCSICommand(const tUSBDMSCDevice *psDevice, + tMSCCBW *pSCSICBW); +static void HandleDevice(void *pvInstance, unsigned long ulRequest, + void *pvRequestData); + +//***************************************************************************** +// +// The FIFO configuration for USB mass storage class device. +// +//***************************************************************************** +const tFIFOConfig g_sUSBMSCFIFOConfig = +{ + // + // IN endpoints. + // + { + { false, USB_EP_DEV_IN | USB_EP_DMA_MODE_1 | USB_EP_AUTO_SET }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN } + }, + + // + // OUT endpoints. + // + { + { false, USB_EP_DEV_OUT | USB_EP_DMA_MODE_1 | USB_EP_AUTO_CLEAR }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT } + }, +}; + +//***************************************************************************** +// +// The device information structure for the USB MSC device. +// +//***************************************************************************** +tDeviceInfo g_sMSCDeviceInfo = +{ + // + // Device event handler callbacks. + // + { + // + // GetDescriptor + // + 0, + + // + // RequestHandler + // + HandleRequests, + + // + // InterfaceChange + // + 0, + + // + // ConfigChange + // + ConfigChangeHandler, + + // + // DataReceived + // + 0, + + // + // DataSentCallback + // + 0, + + // + // ResetHandler + // + 0, + + // + // SuspendHandler + // + 0, + + // + // ResumeHandler + // + 0, + + // + // DisconnectHandler + // + HandleDisconnect, + + // + // EndpointHandler + // + HandleEndpoints, + + // + // Device handler + // + HandleDevice + }, + g_pMSCDeviceDescriptor, + g_pMSCConfigDescriptors, + 0, + 0, + &g_sUSBMSCFIFOConfig +}; + +//***************************************************************************** +// +//! This function is used by an application if it can detect insertion or +//! removal of the media. +//! +//! \param pvInstance is the mass storage device instance that had a media +//! change. +//! \param eMediaStatus is the updated status for the media. +//! +//! This function should be called by an application when it detects a change +//! in the status of the media in use by the USB mass storage class. The +//! \e eMediaStatus parameter will indicate the new status of the media and +//! can also indicate that the application has no knowledge of the media state. +//! +//! There are currently the three following values for the \e eMediaStatus +//! parameter: +//! - USBDMSC_MEDIA_PRESENT indicates that the media is present or has been +//! added. +//! - USBDMSC_MEDIA_NOTPRESENT indicates that the media is not present or was +//! removed. +//! - USBDMSC_MEDIA_UNKNOWN indicates that the application has no knowledge of +//! the media state and the USB mass storage class. +//! +//! It will be left up to the application to call this function whenever it +//! detects a change or simply call it once with USBDMSC_MEDIA_UNKNOWN and +//! allow the mass storage class to infer the state from the remaining device +//! APIs. +//! +//! \note It is recommended that the application use this function to inform +//! the mass storage class of media state changes as it will lead to a more +//! responsive system. +//! +//! \return None. +// +//***************************************************************************** +void +USBDMSCMediaChange(void *pvInstance, tUSBDMSCMediaStatus eMediaStatus) +{ + const tUSBDMSCDevice *psDevice; + + // + // Create a device instance pointer. + // + psDevice = pvInstance; + + // + // Save the current media status. + // + psDevice->psPrivateData->eMediaStatus = eMediaStatus; +} + +//***************************************************************************** +// +// This function is called to handle the interrupts on the Bulk endpoints for +// the mass storage class. +// +//***************************************************************************** +static void +HandleEndpoints(void *pvInstance, unsigned long ulStatus) +{ + const tUSBDMSCDevice *psDevice; + tMSCInstance *psInst; + tMSCCBW *pSCSICBW; + unsigned long ulEPStatus; + unsigned long ulSize; + + ASSERT(pvInstance != 0); + + // + // Determine if the serial device is in single or composite mode because + // the meaning of ulIndex is different in both cases. + // + psDevice = pvInstance; + + // + // Initialize the workspace in the passed instance structure. + // + psInst = psDevice->psPrivateData; + + // + // Get the endpoints status. + // + ulEPStatus = MAP_USBEndpointStatus(USB0_BASE, psInst->ucOUTEndpoint); + + // + // Handler for the bulk IN data endpoint. + // + if((ulStatus & (1 << USB_EP_TO_INDEX(psInst->ucINEndpoint))) || + ((psInst->ulFlags & USBD_FLAG_DMA_IN) && + (MAP_uDMAChannelModeGet(psInst->ucINDMA) == UDMA_MODE_STOP))) + { + switch(psInst->ucSCSIState) + { + // + // Handle the case where we are sending out data due to a read + // command. + // + case STATE_SCSI_SEND_BLOCKS: + { + // + // Decrement the number of bytes left to send. + // + psInst->ulBytesToTransfer -= MAX_TRANSFER_SIZE; + + // + // If we are done then move on to the status phase. + // + if(psInst->ulBytesToTransfer == 0) + { + // + // Set the status so that it can be sent when this + // response has has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 0; + g_sSCSICSW.dCSWDataResidue = 0; + + // + // DMA has completed for the IN endpoint. + // + psInst->ulFlags &= ~USBD_FLAG_DMA_IN; + + // + // Disable uDMA on the endpoint + // + MAP_USBEndpointDMADisable(USB0_BASE, psInst->ucINEndpoint, + USB_EP_DEV_IN); + + // + // Send back the status once this transfer is complete. + // + psInst->ucSCSIState = STATE_SCSI_SEND_STATUS; + + if(psDevice->pfnEventCallback) + { + psDevice->pfnEventCallback(0, USBD_MSC_EVENT_IDLE, 0, + 0); + } + + // + // The transfer is complete so don't read anymore data. + // + break; + } + + // + // Move on to the next Logical Block. + // + psInst->ulCurrentLBA++; + + // + // Read the new data and send it out. + // + if(psDevice->sMediaFunctions.BlockRead(psInst->pvMedia, + (unsigned char *)psInst->pulBuffer, + psInst->ulCurrentLBA, 1) == 0) + { + } + + // + // Reset the DMA transfer and enable the DMA channel. + // + MAP_uDMAChannelTransferSet(psInst->ucINDMA, + UDMA_MODE_BASIC, + psInst->pulBuffer, + (void *)USBFIFOAddrGet(USB0_BASE, + psInst->ucINEndpoint), + (MAX_TRANSFER_SIZE >> 2)); + MAP_uDMAChannelEnable(psInst->ucINDMA); + + break; + } + + // + // Handle sending status. + // + case STATE_SCSI_SEND_STATUS: + { + // + // Indicate success and no extra data coming. + // + USBDSCSISendStatus(psDevice); + + break; + } + + // + // Handle completing sending status. + // + case STATE_SCSI_SENT_STATUS: + { + psInst->ucSCSIState = STATE_SCSI_IDLE; + + break; + } + + // + // These cases should not occur as the being in the IDLE state due + // to an IN interrupt is invalid. + // + case STATE_SCSI_IDLE: + default: + { + break; + } + } + } + + // + // Handler for the bulk OUT data endpoint. + // + if((ulStatus & (0x10000 << USB_EP_TO_INDEX(psInst->ucOUTEndpoint))) || + ((psInst->ulFlags & USBD_FLAG_DMA_OUT) && + (MAP_uDMAChannelModeGet(psInst->ucOUTDMA) == UDMA_MODE_STOP))) + { + // + // Get the endpoint status to see why we were called. + // + ulEPStatus = MAP_USBEndpointStatus(USB0_BASE, psInst->ucOUTEndpoint); + + switch(psInst->ucSCSIState) + { + // + // Receiving and writing bytes to the storage device. + // + case STATE_SCSI_RECEIVE_BLOCKS: + { + // + // Update the current status for the buffer. + // + psInst->ulBytesToTransfer -= MAX_TRANSFER_SIZE; + + // + // Write the new data. + // + psDevice->sMediaFunctions.BlockWrite(psInst->pvMedia, + (unsigned char *)psInst->pulBuffer, + psInst->ulCurrentLBA, 1); + + // + // Move on to the next Logical Block. + // + psInst->ulCurrentLBA++; + + // + // Check if all bytes have been received. + // + if(psInst->ulBytesToTransfer == 0) + { + // + // Set the status so that it can be sent when this response + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 0; + g_sSCSICSW.dCSWDataResidue = 0; + + // + // DMA has completed for the OUT endpoint. + // + psInst->ulFlags &= ~USBD_FLAG_DMA_OUT; + + // + // Indicate success and no extra data coming. + // + USBDSCSISendStatus(psDevice); + + // + // Disable uDMA on the endpoint + // + MAP_USBEndpointDMADisable(USB0_BASE, psInst->ucOUTEndpoint, + USB_EP_DEV_OUT); + + // + // If there is an event callback then call it to notify + // that last operation has completed. + // + if(psDevice->pfnEventCallback) + { + psDevice->pfnEventCallback(0, USBD_MSC_EVENT_IDLE, 0, + 0); + } + } + else + { + // + // Configure and enable DMA for the OUT transfer. + // + MAP_uDMAChannelTransferSet(psInst->ucOUTDMA, + UDMA_MODE_BASIC, + (void *)USBFIFOAddrGet(USB0_BASE, + psInst->ucOUTEndpoint), + psInst->pulBuffer, + (MAX_TRANSFER_SIZE >> 2)); + + // + // Start the DMA transfer. + // + MAP_uDMAChannelEnable(psInst->ucOUTDMA); + } + + break; + } + + // + // If there is an OUT transfer in idle state then it was a new + // command. + // + case STATE_SCSI_IDLE: + { + // + // Attempt to handle the new command. + // + + // + // Receive the command. + // + ulSize = COMMAND_BUFFER_SIZE; + MAP_USBEndpointDataGet(psInst->ulUSBBase, psInst->ucOUTEndpoint, + g_pucCommand, &ulSize); + pSCSICBW = (tMSCCBW *)g_pucCommand; + + // + // Acknowledge the OUT data packet. + // + MAP_USBDevEndpointDataAck(psInst->ulUSBBase, + psInst->ucOUTEndpoint, + false); + + // + // If this is a valid CBW then handle it. + // + if(pSCSICBW->dCBWSignature == CBW_SIGNATURE) + { + g_sSCSICSW.dCSWSignature = CSW_SIGNATURE; + g_sSCSICSW.dCSWTag = pSCSICBW->dCBWTag; + g_sSCSICSW.dCSWDataResidue = 0; + g_sSCSICSW.bCSWStatus = 0; + + USBDSCSICommand(psDevice, pSCSICBW); + } + else + { + // + // Just return to the idle state since we are now out of + // sync with the host. This should not happen, but this + // should allow the device to synchronize with the host + // controller. + // + psInst->ucSCSIState = STATE_SCSI_IDLE; + } + + break; + } + default: + { + break; + } + } + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(USB0_BASE, psInst->ucOUTEndpoint, + ulEPStatus); + } +} + +//***************************************************************************** +// +// Device instance specific handler. +// +//***************************************************************************** +static void +HandleDevice(void *pvInstance, unsigned long ulRequest, void *pvRequestData) +{ + tMSCInstance *psInst; + unsigned char *pucData; + + // + // Create the serial instance data. + // + psInst = ((tUSBDMSCDevice *)pvInstance)->psPrivateData; + + // + // Create the char array used by the events supported by the USB CDC + // serial class. + // + pucData = (unsigned char *)pvRequestData; + + switch(ulRequest) + { + // + // This was an interface change event. + // + case USB_EVENT_COMP_IFACE_CHANGE: + { + psInst->ucInterface = pucData[1]; + break; + } + + // + // This was an endpoint change event. + // + case USB_EVENT_COMP_EP_CHANGE: + { + // + // Determine if this is an IN or OUT endpoint that has changed. + // + if(pucData[0] & USB_EP_DESC_IN) + { + psInst->ucINEndpoint = INDEX_TO_USB_EP((pucData[1] & 0x7f)); + + psInst->ucINDMA = UDMA_CHANNEL_USBEP1TX + + (((pucData[1] & 0x7f) - 1) * 2); + + // + // Basic configuration for DMA on the IN endpoint. + // + MAP_uDMAChannelControlSet(psInst->ucINDMA, + (UDMA_SIZE_32 | UDMA_SRC_INC_32| + UDMA_DST_INC_NONE | UDMA_ARB_16)); + + // + // Select this channel for this endpoint, this only affects + // devices that have this feature. + // + MAP_USBEndpointDMAChannel(USB0_BASE, psInst->ucINEndpoint, + psInst->ucINDMA); + } + else + { + // + // Extract the new endpoint number. + // + psInst->ucOUTEndpoint = INDEX_TO_USB_EP(pucData[1] & 0x7f); + psInst->ucOUTDMA = UDMA_CHANNEL_USBEP1RX + + (((pucData[1] & 0x7f) - 1) * 2); + + // + // Basic configuration for DMA on the OUT endpoint. + // + MAP_uDMAChannelControlSet(psInst->ucOUTDMA, + (UDMA_SIZE_32 | UDMA_SRC_INC_NONE| + UDMA_DST_INC_32 | UDMA_ARB_16)); + + // + // Select this channel for this endpoint, this only affects + // devices that have this feature. + // + MAP_USBEndpointDMAChannel(USB0_BASE, psInst->ucOUTEndpoint, + psInst->ucOUTDMA); + } + break; + } + default: + { + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device is +// disconnected from the host. +// +//***************************************************************************** +static void +HandleDisconnect(void *pvInstance) +{ + const tUSBDMSCDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDMSCDevice *)pvInstance; + + // + // Close the drive requested. + // + if(psDevice->psPrivateData->pvMedia != 0) + { + psDevice->psPrivateData->pvMedia = 0; + psDevice->sMediaFunctions.Close(0); + } + + // + // If we have a control callback, let the client know we are open for + // business. + // + if(psDevice->pfnEventCallback) + { + // + // Pass the connected event to the client. + // + psDevice->pfnEventCallback(pvInstance, USB_EVENT_DISCONNECTED, 0, 0); + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device +// configuration changes. +// +//***************************************************************************** +static void +ConfigChangeHandler(void *pvInstance, unsigned long ulValue) +{ + tMSCInstance *psInst; + const tUSBDMSCDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDMSCDevice *)pvInstance; + + // + // Create the serial instance data. + // + psInst = psDevice->psPrivateData; + + // + // Insure that DMA is disable whenever the configuration is set. + // + MAP_USBEndpointDMADisable(USB0_BASE, psInst->ucINEndpoint, USB_EP_DEV_IN); + MAP_USBEndpointDMADisable(USB0_BASE, psInst->ucOUTEndpoint, USB_EP_DEV_OUT); + + // + // Basic configuration for DMA on the OUT endpoint. + // + MAP_uDMAChannelControlSet(psInst->ucOUTDMA, UDMA_SIZE_32 | + UDMA_SRC_INC_NONE| + UDMA_DST_INC_32 | + UDMA_ARB_16); + + // + // Select this channel for this endpoint, this only affects devices that + // have this feature. + // + MAP_USBEndpointDMAChannel(USB0_BASE, psInst->ucOUTEndpoint, psInst->ucOUTDMA); + + // + // Basic configuration for DMA on the IN endpoint. + // + MAP_uDMAChannelControlSet(psInst->ucINDMA, UDMA_SIZE_32 | UDMA_SRC_INC_32| + UDMA_DST_INC_NONE | UDMA_ARB_16); + + // + // Select this channel for this endpoint, this only affects devices that + // have this feature. + // + MAP_USBEndpointDMAChannel(USB0_BASE, psInst->ucINEndpoint, psInst->ucINDMA); + + // + // If we have a control callback, let the client know we are open for + // business. + // + if(psDevice->pfnEventCallback) + { + // + // Pass the connected event to the client. + // + psDevice->pfnEventCallback(pvInstance, USB_EVENT_CONNECTED, 0, 0); + } +} + +//***************************************************************************** +// +//! This function should be called once for the mass storage class device to +//! initialized basic operation and prepare for enumeration. +//! +//! \param ulIndex is the index of the USB controller to initialize for +//! mass storage class device operation. +//! \param psDevice points to a structure containing parameters customizing +//! the operation of the mass storage device. +//! +//! In order for an application to initialize the USB device mass storage +//! class, it must first call this function with the a valid mass storage +//! device class structure in the \e psDevice parameter. This allows this +//! function to initialize the USB controller and device code to be prepared to +//! enumerate and function as a USB mass storage device. +//! +//! This function returns a void pointer that must be passed in to all other +//! APIs used by the mass storage class. +//! +//! See the documentation on the tUSBDMSCDevice structure for more information +//! on how to properly fill the structure members. +//! +//! \return Returns 0 on failure or a non-zero void pointer on success. +// +//***************************************************************************** +void * +USBDMSCInit(unsigned long ulIndex, const tUSBDMSCDevice *psDevice) +{ + // + // Check parameter validity. + // + ASSERT(ulIndex == 0); + ASSERT(psDevice); + ASSERT(psDevice->ppStringDescriptors); + ASSERT(psDevice->psPrivateData); + + USBDMSCCompositeInit(ulIndex, psDevice); + + // + // All is well so now pass the descriptors to the lower layer and put + // the bulk device on the bus. + // + USBDCDInit(ulIndex, psDevice->psPrivateData->psDevInfo); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psDevice); +} + +//***************************************************************************** +// +//! This function should be called once for the mass storage class device to +//! initialized basic operation and prepare for enumeration. +//! +//! \param ulIndex is the index of the USB controller to initialize for +//! mass storage class device operation. +//! \param psDevice points to a structure containing parameters customizing +//! the operation of the mass storage device. +//! +//! In order for an application to initialize the USB device mass storage +//! class, it must first call this function with the a valid mass storage +//! device class structure in the \e psDevice parameter. This allows this +//! function to initialize the USB controller and device code to be prepared to +//! enumerate and function as a USB mass storage device. +//! +//! This function returns a void pointer that must be passed in to all other +//! APIs used by the mass storage class. +//! +//! See the documentation on the tUSBDMSCDevice structure for more information +//! on how to properly fill the structure members. +//! +//! \return Returns 0 on failure or a non-zero void pointer on success. +// +//***************************************************************************** +void * +USBDMSCCompositeInit(unsigned long ulIndex, const tUSBDMSCDevice *psDevice) +{ + tMSCInstance *psInst; + tDeviceDescriptor *psDevDesc; + + // + // Check parameter validity. + // + ASSERT(ulIndex == 0); + ASSERT(psDevice); + ASSERT(psDevice->ppStringDescriptors); + ASSERT(psDevice->psPrivateData); + + // + // Initialize the workspace in the passed instance structure. + // + psInst = psDevice->psPrivateData; + psInst->psConfDescriptor = (tConfigDescriptor *)g_pMSCDescriptor; + psInst->psDevInfo = &g_sMSCDeviceInfo; + psInst->ulUSBBase = USB0_BASE; + psInst->bConnected = false; + psInst->eMediaStatus = USBDMSC_MEDIA_UNKNOWN; + + // + // Set the initial interface and endpoints. + // + psInst->ucInterface = 0; + psInst->ucOUTEndpoint = DATA_OUT_ENDPOINT; + psInst->ucOUTDMA = DATA_OUT_DMA_CHANNEL; + psInst->ucINEndpoint = DATA_IN_ENDPOINT; + psInst->ucINDMA = DATA_IN_DMA_CHANNEL; + + // + // Set the initial SCSI state to idle. + // + psInst->ucSCSIState = STATE_SCSI_IDLE; + + // + // Fix up the device descriptor with the client-supplied values. + // + psDevDesc = (tDeviceDescriptor *)psInst->psDevInfo->pDeviceDescriptor; + psDevDesc->idVendor = psDevice->usVID; + psDevDesc->idProduct = psDevice->usPID; + + // + // Fix up the configuration descriptor with client-supplied values. + // + psInst->psConfDescriptor->bmAttributes = psDevice->ucPwrAttributes; + psInst->psConfDescriptor->bMaxPower = + (unsigned char)(psDevice->usMaxPowermA / 2); + + // + // Plug in the client's string stable to the device information + // structure. + // + psInst->psDevInfo->ppStringDescriptors = psDevice->ppStringDescriptors; + psInst->psDevInfo->ulNumStringDescriptors + = psDevice->ulNumStringDescriptors; + psInst->psDevInfo->pvInstance = (void *)psDevice; + + // + // If DMA is in use then clear all DMA attributes. + // + MAP_uDMAChannelAttributeDisable(psInst->ucINDMA, UDMA_ATTR_ALL); + MAP_uDMAChannelAttributeDisable(psInst->ucOUTDMA, UDMA_ATTR_ALL); + + // + // Open the drive requested. + // + psInst->pvMedia = psDevice->sMediaFunctions.Open(0); + + if(psInst->pvMedia == 0) + { + // + // There is no media currently present. + // + psInst->ucSenseKey = SCSI_RS_KEY_NOT_READY; + psInst->usAddSenseCode = SCSI_RS_MED_NOT_PRSNT; + } + else + { + // + // Media is now ready for use. + // + psInst->ucSenseKey = SCSI_RS_KEY_UNIT_ATTN; + psInst->usAddSenseCode = SCSI_RS_MED_NOTRDY2RDY; + } + + // + // Enable Clocking to the USB controller. + // + MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_USB0); + + // + // Turn on USB Phy clock. + // + MAP_SysCtlUSBPLLEnable(); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psDevice); +} + +//***************************************************************************** +// +//! Shuts down the mass storage device. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDMSCInit() or USBDMSCInitComposite(). +//! +//! This function terminates mass storage operation for the instance supplied +//! and removes the device from the USB bus. Following this call, the +//! \e psDevice instance may not me used in any other call to the mass storage +//! device other than USBDMSCInit() or USBDMSCInitComposite(). +//! +//! \return None. +// +//***************************************************************************** +void +USBDMSCTerm(void *pvInstance) +{ + const tUSBDMSCDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Cleanly exit device mode. + // + USBDCDTerm(0); + + // + // Create a device instance pointer. + // + psDevice = pvInstance; + + // + // If the media was opened the close it out. + // + if(psDevice->psPrivateData->pvMedia != 0) + { + psDevice->psPrivateData->pvMedia = 0; + psDevice->sMediaFunctions.Close(0); + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever a non-standard +// request is received. +// +// \param pvInstance is instance data for this request. +// \param pUSBRequest points to the request received. +// +// This call parses the provided request structure to determine the command. +// The only mass storage command supported over endpoint 0 is the Get Max LUN +// command. +// +// \return None. +// +//***************************************************************************** +static void +HandleRequests(void *pvInstance, tUSBRequest *pUSBRequest) +{ + // + // This class only support a single LUN. + // + const static unsigned char ucMaxLun = 0; + + ASSERT(pvInstance != 0); + + // + // Determine the type of request. + // + switch(pUSBRequest->bRequest) + { + // + // A Set Report request is received from the host when it sends an + // Output report via endpoint 0. + // + case USBREQ_GET_MAX_LUN: + { + // + // Send our response to the host. + // + USBDCDSendDataEP0(0, (unsigned char *)&ucMaxLun, 1); + + break; + } + + // + // This request was not recognized so stall. + // + default: + { + USBDCDStallEP0(0); + break; + } + } +} + +//***************************************************************************** +// +// This function is used to handle the SCSI Inquiry command when it is received +// from the host. +// +//***************************************************************************** +static void +USBDSCSIInquiry(const tUSBDMSCDevice *psDevice) +{ + long lIdx; + tMSCInstance *psInst; + unsigned long *pulData; + + // + // Create a local unsigned long pointer to the command. + // + pulData = (unsigned long *)g_pucCommand; + + // + // Create the serial instance data. + // + psInst = psDevice->psPrivateData; + + // + // Direct Access device, Removable storage and SCSI 1 responses. + // + pulData[0] = SCSI_INQ_PDT_SBC | (SCSI_INQ_RMB << 8); + + // + // Additional Length is fixed at 31 bytes. + // + pulData[1] = 31; + + // + // Copy the Vendor string. + // + for(lIdx = 0; lIdx < 8; lIdx++) + { + g_pucCommand[lIdx + 8] = psDevice->pucVendor[lIdx]; + } + + // + // Copy the Product string. + // + for(lIdx = 0; lIdx < 16; lIdx++) + { + g_pucCommand[lIdx + 16] = psDevice->pucProduct[lIdx]; + } + + // + // Copy the Version string. + // + for(lIdx = 0; lIdx < 4; lIdx++) + { + g_pucCommand[lIdx + 32] = psDevice->pucVersion[lIdx]; + } + + // + // Send the SCSI Inquiry Response. + // + MAP_USBEndpointDataPut(USB0_BASE, psInst->ucINEndpoint, g_pucCommand, 36); + + // + // Send the data to the host. + // + MAP_USBEndpointDataSend(USB0_BASE, psInst->ucINEndpoint, USB_TRANS_IN); + + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 0; + g_sSCSICSW.dCSWDataResidue = 0; + + psInst->ucSCSIState = STATE_SCSI_SEND_STATUS; +} + +//***************************************************************************** +// +// This function is used to handle the SCSI Read Capacities command when it is +// received from the host. +// +//***************************************************************************** +static void +USBDSCSIReadCapacities(const tUSBDMSCDevice *psDevice) +{ + unsigned long ulBlocks; + tMSCInstance *psInst; + unsigned long *pulData; + + // + // Create a local unsigned long pointer to the command. + // + pulData = (unsigned long *)g_pucCommand; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateData; + + if(psInst->pvMedia != 0) + { + ulBlocks = psDevice->sMediaFunctions.NumBlocks(psInst->pvMedia); + + pulData[0] = 0x08000000; + + // + // Fill in the number of blocks, the bytes endianness must be changed. + // + g_pucCommand[4] = ulBlocks >> 24; + g_pucCommand[5] = 0xff & (ulBlocks >> 16); + g_pucCommand[6] = 0xff & (ulBlocks >> 8); + g_pucCommand[7] = 0xff & (ulBlocks); + + // + // Current media capacity + // + g_pucCommand[8] = 0x2; + + // + // Fill in the block size, which is fixed at DEVICE_BLOCK_SIZE. + // + g_pucCommand[9] = 0xff & (DEVICE_BLOCK_SIZE >> 16); + g_pucCommand[10] = 0xff & (DEVICE_BLOCK_SIZE >> 8); + g_pucCommand[11] = 0xff & DEVICE_BLOCK_SIZE; + + // + // Send out the 12 bytes that are in this response. + // + MAP_USBEndpointDataPut(USB0_BASE, psInst->ucINEndpoint, g_pucCommand, + 12); + MAP_USBEndpointDataSend(USB0_BASE, psInst->ucINEndpoint, USB_TRANS_IN); + + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 0; + g_sSCSICSW.dCSWDataResidue = 0; + } + else + { + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 1; + g_sSCSICSW.dCSWDataResidue = 0; + + // + // Stall the IN endpoint + // + MAP_USBDevEndpointStall(USB0_BASE, psInst->ucINEndpoint, USB_EP_DEV_IN); + + // + // Mark the sense code as valid and indicate that these is no media + // present. + // + psInst->ucErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS; + psInst->ucSenseKey = SCSI_RS_KEY_NOT_READY; + psInst->usAddSenseCode = SCSI_RS_MED_NOT_PRSNT; + } + + psInst->ucSCSIState = STATE_SCSI_SEND_STATUS; +} + +//***************************************************************************** +// +// This function is used to handle the SCSI Read Capacity command when it is +// received from the host. +// +//***************************************************************************** +static void +USBDSCSIReadCapacity(const tUSBDMSCDevice *psDevice) +{ + unsigned long ulBlocks; + tMSCInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateData; + + ulBlocks = psDevice->sMediaFunctions.NumBlocks(psInst->pvMedia); + + // + // Only decrement if any blocks were found. + // + if(ulBlocks != 0) + { + // + // One less than the maximum number is the last addressable + // block. + // + ulBlocks--; + } + + if(psInst->pvMedia != 0) + { + // + // Fill in the number of blocks, the bytes endianness must be changed. + // + g_pucCommand[0] = 0xff & (ulBlocks >> 24); + g_pucCommand[1] = 0xff & (ulBlocks >> 16); + g_pucCommand[2] = 0xff & (ulBlocks >> 8); + g_pucCommand[3] = 0xff & (ulBlocks); + + g_pucCommand[4] = 0; + + // + // Fill in the block size, which is fixed at DEVICE_BLOCK_SIZE. + // + g_pucCommand[5] = 0xff & (DEVICE_BLOCK_SIZE >> 16); + g_pucCommand[6] = 0xff & (DEVICE_BLOCK_SIZE >> 8); + g_pucCommand[7] = 0xff & DEVICE_BLOCK_SIZE; + + // + // Send the SCSI Inquiry Response. + // + MAP_USBEndpointDataPut(USB0_BASE, psInst->ucINEndpoint, g_pucCommand, + 8); + MAP_USBEndpointDataSend(USB0_BASE, psInst->ucINEndpoint, USB_TRANS_IN); + + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 0; + g_sSCSICSW.dCSWDataResidue = 0; + } + else + { + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 1; + g_sSCSICSW.dCSWDataResidue = 0; + + // + // Stall the IN endpoint + // + MAP_USBDevEndpointStall(USB0_BASE, psInst->ucINEndpoint, USB_EP_DEV_IN); + + // + // Mark the sense code as valid and indicate that these is no media + // present. + // + psInst->ucErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS; + psInst->ucSenseKey = SCSI_RS_KEY_NOT_READY; + psInst->usAddSenseCode = SCSI_RS_MED_NOT_PRSNT; + } + + psInst->ucSCSIState = STATE_SCSI_SEND_STATUS; +} + +//***************************************************************************** +// +// This function is used to handle the SCSI Request Sense command when it is +// received from the host. +// +//***************************************************************************** +static void +USBDSCSIRequestSense(const tUSBDMSCDevice *psDevice) +{ + tMSCInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateData; + + // + // The request sense response. + // + g_pucCommand[0] = psInst->ucErrorCode; + g_pucCommand[1] = 0; + g_pucCommand[2] = psInst->ucSenseKey; + *(unsigned long *)&g_pucCommand[3] = 0; + + // + // There are 10 more bytes of data. + // + g_pucCommand[7] = 10; + + *(unsigned long *)&g_pucCommand[8] = 0; + + // + // Transition from not ready to ready. + // + *(unsigned short *)&g_pucCommand[12] = psInst->usAddSenseCode; + *(unsigned long *)&g_pucCommand[14] = 0; + + // + // Send the SCSI Inquiry Response. + // + MAP_USBEndpointDataPut(USB0_BASE, psInst->ucINEndpoint, g_pucCommand, 18); + MAP_USBEndpointDataSend(USB0_BASE, psInst->ucINEndpoint, USB_TRANS_IN); + + // + // Reset the valid flag on errors. + // + psInst->ucErrorCode = SCSI_RS_CUR_ERRORS; + + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 0; + g_sSCSICSW.dCSWDataResidue = 0; + + // + // Move on to the status phase. + // + psInst->ucSCSIState = STATE_SCSI_SEND_STATUS; +} + +//***************************************************************************** +// +// This function is used to handle the SCSI Read 10 command when it is +// received from the host. +// +//***************************************************************************** +static void +USBDSCSIRead10(const tUSBDMSCDevice *psDevice, tMSCCBW *pSCSICBW) +{ + unsigned short usNumBlocks; + tMSCInstance *psInst; + + // + // Default the number of blocks. + // + usNumBlocks = 0; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateData; + + if(psInst->pvMedia != 0) + { + // + // Get the logical block from the CBW structure. This switching + // is required to convert from big to little endian. + // + psInst->ulCurrentLBA = (pSCSICBW->CBWCB[2] << 24) | + (pSCSICBW->CBWCB[3] << 16) | + (pSCSICBW->CBWCB[4] << 8) | + (pSCSICBW->CBWCB[5] << 0); + + // + // More bytes to read. + // + usNumBlocks = (pSCSICBW->CBWCB[7] << 8) | pSCSICBW->CBWCB[8]; + + // + // Read the next logical block from the storage device. + // + if(psDevice->sMediaFunctions.BlockRead(psInst->pvMedia, + (unsigned char *)psInst->pulBuffer, + psInst->ulCurrentLBA, 1) == 0) + { + psInst->pvMedia = 0; + psDevice->sMediaFunctions.Close(0); + } + } + + // + // If there is media present then start transferring the data. + // + if(psInst->pvMedia != 0) + { + // + // Enable DMA on the endpoint + // + MAP_USBEndpointDMAEnable(USB0_BASE, psInst->ucINEndpoint, + USB_EP_DEV_IN); + + // + // Configure and DMA for the IN transfer. + // + MAP_uDMAChannelTransferSet(psInst->ucINDMA, + UDMA_MODE_BASIC, + psInst->pulBuffer, + (void *)USBFIFOAddrGet(USB0_BASE, + psInst->ucINEndpoint), + (MAX_TRANSFER_SIZE >> 2)); + + // + // Remember that a DMA is in progress. + // + psInst->ulFlags |= USBD_FLAG_DMA_IN; + + // + // Schedule the remaining bytes to send. + // + psInst->ulBytesToTransfer = (DEVICE_BLOCK_SIZE * usNumBlocks); + + // + // Start the DMA transfer. + // + MAP_uDMAChannelEnable(psInst->ucINDMA); + + // + // Move on and start sending blocks. + // + psInst->ucSCSIState = STATE_SCSI_SEND_BLOCKS; + + if(psDevice->pfnEventCallback) + { + psDevice->pfnEventCallback(0, USBD_MSC_EVENT_READING, 0, 0); + } + } + else + { + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 1; + g_sSCSICSW.dCSWDataResidue = 0; + + // + // Stall the IN endpoint + // + MAP_USBDevEndpointStall(USB0_BASE, psInst->ucINEndpoint, USB_EP_DEV_IN); + + // + // Mark the sense code as valid and indicate that these is no media + // present. + // + psInst->ucErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS; + psInst->ucSenseKey = SCSI_RS_KEY_NOT_READY; + psInst->usAddSenseCode = SCSI_RS_MED_NOT_PRSNT; + + psInst->ucSCSIState = STATE_SCSI_SEND_STATUS; + } +} + +//***************************************************************************** +// +// This function is used to handle the SCSI Read 10 command when it is +// received from the host. +// +//***************************************************************************** +static void +USBDSCSIWrite10(const tUSBDMSCDevice *psDevice, tMSCCBW *pSCSICBW) +{ + unsigned short usNumBlocks; + tMSCInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateData; + + // + // If there is media present then start transferring the data. + // + if(psInst->pvMedia != 0) + { + // + // Get the logical block from the CBW structure. This switching + // is required to convert from big to little endian. + // + psInst->ulCurrentLBA = (pSCSICBW->CBWCB[2] << 24) | + (pSCSICBW->CBWCB[3] << 16) | + (pSCSICBW->CBWCB[4] << 8) | + (pSCSICBW->CBWCB[5] << 0); + + // + // More bytes to read. + // + usNumBlocks = (pSCSICBW->CBWCB[7] << 8) | pSCSICBW->CBWCB[8]; + + psInst->ulBytesToTransfer = DEVICE_BLOCK_SIZE * usNumBlocks; + + // + // Start sending logical blocks, these are always multiples of + // DEVICE_BLOCK_SIZE bytes. + // + psInst->ucSCSIState = STATE_SCSI_RECEIVE_BLOCKS; + + // + // Enable uDMA on the endpoint + // + MAP_USBEndpointDMAEnable(USB0_BASE, psInst->ucOUTEndpoint, + USB_EP_DEV_OUT); + + // + // Configure the DMA for the OUT transfer. + // + MAP_uDMAChannelTransferSet(psInst->ucOUTDMA, + UDMA_MODE_BASIC, + (void *)USBFIFOAddrGet(USB0_BASE, + psInst->ucOUTEndpoint), + psInst->pulBuffer, + (MAX_TRANSFER_SIZE >> 2)); + + // + // Remember that a DMA is in progress. + // + psInst->ulFlags |= USBD_FLAG_DMA_OUT; + + // + // Enable the OUT DMA transfer. + // + MAP_uDMAChannelEnable(psInst->ucOUTDMA); + + // + // Notify the application of the write event. + // + if(psDevice->pfnEventCallback) + { + psDevice->pfnEventCallback(0, USBD_MSC_EVENT_WRITING, 0, 0); + } + } + else + { + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 1; + g_sSCSICSW.dCSWDataResidue = 0; + + // + // Stall the IN endpoint + // + MAP_USBDevEndpointStall(USB0_BASE, psInst->ucOUTEndpoint, + USB_EP_DEV_OUT); + + // + // Mark the sense code as valid and indicate that these is no media + // present. + // + psInst->ucErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS; + psInst->ucSenseKey = SCSI_RS_KEY_NOT_READY; + psInst->usAddSenseCode = SCSI_RS_MED_NOT_PRSNT; + + psInst->ucSCSIState = STATE_SCSI_SEND_STATUS; + } +} + +//***************************************************************************** +// +// This function is used to handle the SCSI Mode Sense 6 command when it is +// received from the host. +// +//***************************************************************************** +static void +USBDSCSIModeSense6(const tUSBDMSCDevice *psDevice, tMSCCBW *pSCSICBW) +{ + tMSCInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateData; + + // + // If there is media present send the response. + // + if(psInst->pvMedia != 0) + { + // + // Three extra bytes in this response. + // + g_pucCommand[0] = 3; + g_pucCommand[1] = 0; + g_pucCommand[2] = 0; + g_pucCommand[3] = 0; + + // + // Manually send the response back to the host. + // + MAP_USBEndpointDataPut(USB0_BASE, psInst->ucINEndpoint, g_pucCommand, + 4); + MAP_USBEndpointDataSend(USB0_BASE, psInst->ucINEndpoint, USB_TRANS_IN); + + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 0; + g_sSCSICSW.dCSWDataResidue = pSCSICBW->dCBWDataTransferLength - 4; + } + else + { + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 1; + g_sSCSICSW.dCSWDataResidue = 0; + + // + // Stall the IN endpoint + // + MAP_USBDevEndpointStall(USB0_BASE, psInst->ucINEndpoint, USB_EP_DEV_IN); + + // + // Mark the sense code as valid and indicate that these is no media + // present. + // + psInst->ucErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS; + psInst->ucSenseKey = SCSI_RS_KEY_NOT_READY; + psInst->usAddSenseCode = SCSI_RS_MED_NOT_PRSNT; + } + + psInst->ucSCSIState = STATE_SCSI_SEND_STATUS; +} + +//***************************************************************************** +// +// This function is used to send out the response data based on the current +// status of the mass storage class. +// +//***************************************************************************** +static void +USBDSCSISendStatus(const tUSBDMSCDevice *psDevice) +{ + tMSCInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateData; + + // + // Respond with the requested status. + // + MAP_USBEndpointDataPut(USB0_BASE, psInst->ucINEndpoint, + (unsigned char *)&g_sSCSICSW, 13); + MAP_USBEndpointDataSend(USB0_BASE, psInst->ucINEndpoint, USB_TRANS_IN); + + // + // Move the state to status sent so that the next interrupt will move the + // statue to idle. + // + psInst->ucSCSIState = STATE_SCSI_SENT_STATUS; +} + +//***************************************************************************** +// +// This function is used to handle all SCSI commands. +// +//***************************************************************************** +unsigned long +USBDSCSICommand(const tUSBDMSCDevice *psDevice, tMSCCBW *pSCSICBW) +{ + unsigned long ulRetCode; + unsigned long ulTransferLength; + tMSCInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = psDevice->psPrivateData; + + // + // Initialize the return code. + // + ulRetCode = 1; + + // + // Save the transfer length because it may be overwritten by some calls. + // + ulTransferLength = pSCSICBW->dCBWDataTransferLength; + + switch(pSCSICBW->CBWCB[0]) + { + // + // Respond to the SCSI Inquiry command. + // + case SCSI_INQUIRY_CMD: + { + USBDSCSIInquiry(psDevice); + + break; + } + + // + // Respond to the test unit ready command. + // + case SCSI_TEST_UNIT_READY: + { + g_sSCSICSW.dCSWDataResidue = 0; + + if(psInst->pvMedia != 0) + { + // + // Set the status to success for now, this could be different + // if there is no media present. + // + g_sSCSICSW.bCSWStatus = 0; + } + else + { + // + // Since there was no media, check for media here. + // + psInst->pvMedia = psDevice->sMediaFunctions.Open(0); + + // + // If it is still not present then fail this command. + // + if(psInst->pvMedia != 0) + { + g_sSCSICSW.bCSWStatus = 0; + } + else + { + g_sSCSICSW.bCSWStatus = 1; + } + } + break; + } + + // + // Handle the Read Capacities command. + // + case SCSI_READ_CAPACITIES: + { + USBDSCSIReadCapacities(psDevice); + + break; + } + + // + // Handle the Read Capacity command. + // + case SCSI_READ_CAPACITY: + { + USBDSCSIReadCapacity(psDevice); + + break; + } + + // + // Handle the Request Sense command. + // + case SCSI_REQUEST_SENSE: + { + USBDSCSIRequestSense(psDevice); + + break; + } + + // + // Handle the Read 10 command. + // + case SCSI_READ_10: + { + USBDSCSIRead10(psDevice, pSCSICBW); + + break; + } + + // + // Handle the Write 10 command. + // + case SCSI_WRITE_10: + { + USBDSCSIWrite10(psDevice, pSCSICBW); + + break; + } + + // + // Handle the Mode Sense 6 command. + // + case SCSI_MODE_SENSE_6: + { + USBDSCSIModeSense6(psDevice, pSCSICBW); + + break; + } + default: + { + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 1; + g_sSCSICSW.dCSWDataResidue = pSCSICBW->dCBWDataTransferLength; + + // + // If there is data then there is more work to do. + // + if(pSCSICBW->dCBWDataTransferLength != 0) + { + if(pSCSICBW->bmCBWFlags & CBWFLAGS_DIR_IN) + { + // + // Stall the IN endpoint + // + MAP_USBDevEndpointStall(USB0_BASE, psInst->ucINEndpoint, + USB_EP_DEV_IN); + } + else + { + // + // Stall the OUT endpoint + // + MAP_USBDevEndpointStall(USB0_BASE, psInst->ucOUTEndpoint, + USB_EP_DEV_OUT); + + } + // + // Send the status once the stall occurs. + // + psInst->ucSCSIState = STATE_SCSI_SEND_STATUS; + } + + // + // Set the sense codes. + // + psInst->ucErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS; + psInst->ucSenseKey = SCSI_RS_KEY_ILGL_RQST; + psInst->usAddSenseCode = SCSI_RS_PV_INVALID; + + break; + } + } + + // + // If there is no data then send out the current status. + // + if(ulTransferLength == 0) + { + USBDSCSISendStatus(psDevice); + } + return(ulRetCode); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/device/usbdmsc.h b/usblib/device/usbdmsc.h new file mode 100644 index 0000000..6719827 --- /dev/null +++ b/usblib/device/usbdmsc.h @@ -0,0 +1,391 @@ +//***************************************************************************** +// +// usbdmsc.h - USB mass storage device class driver. +// +// Copyright (c) 2009-2012 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#ifndef __USBDMSC_H__ +#define __USBDMSC_H__ + +//***************************************************************************** +// +// If building with a C++ compiler, make all of the definitions in this header +// have a C binding. +// +//***************************************************************************** +#ifdef __cplusplus +extern "C" +{ +#endif + +//***************************************************************************** +// +//! \addtogroup msc_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// USBDMSCMediaChange() ulStatus values. +// +//***************************************************************************** +typedef enum +{ + USBDMSC_MEDIA_PRESENT, + USBDMSC_MEDIA_NOTPRESENT, + USBDMSC_MEDIA_UNKNOWN +} +tUSBDMSCMediaStatus; + +//***************************************************************************** +// +//! Media Access functions. +// +//***************************************************************************** +typedef struct +{ + // + //! This function is used to initialize and open the physical drive number + //! associated with the parameter ulDrive. The function will return zero if + //! the drive could not be opened for some reason. In the case of removable + //! device like an SD card this function should return zero if the SD card + //! is not present. + //! The function returns a pointer to data that should be passed to other + //! APIs or it will return 0 if no drive was found. + // + void *(* Open)(unsigned long ulDrive); + + //***************************************************************************** + // + // This function close the drive number in use by the mass storage class device. + // + // /param pvDrive is the pointer that was returned from a call to + // USBDMSCStorageOpen(). + // + // This function is used to close the physical drive number associated with the + // parameter /e pvDrive. This function will return 0 if the drive was closed + // successfully and any other value will indicate a failure. + // + // /return Returns 0 if the drive was successfully closed or non-zero for a + // failure. + // + //***************************************************************************** + void (* Close)(void * pvDrive); + + //***************************************************************************** + // + // This function will read a block from a device opened by the + // USBDMSCStorageOpen() call. + // + // /param pvDrive is the pointer that was returned from a call to + // USBDMSCStorageOpen(). + // /param pucData is the buffer that data will be written into. + // /param ulSector is the block address to read. + // /param ulNumBlocks is the number of blocks to read. + // + // This function is use to read blocks from a physical device and return them + // in the /e pucData buffer. The data area pointed to by /e pucData should be + // at least /e ulNumBlocks * Block Size bytes to prevent overwriting data. + // + // /return Returns the number of bytes that were read from the device. + // + //***************************************************************************** + unsigned long (* BlockRead)(void * pvDrive, unsigned char *pucData, + unsigned long ulSector, + unsigned long ulNumBlocks); + + //***************************************************************************** + // + // This function will write a block to a device opened by the + // USBDMSCStorageOpen() call. + // + // /param pvDrive is the pointer that was returned from a call to + // USBDMSCStorageOpen(). + // /param pucData is the buffer that data will be used for writing. + // /param ulNumBlocks is the number of blocks to write. + // + // This function is use to write blocks to a physical device from the buffer + // pointed to by the /e pucData buffer. If the number of blocks is greater than + // one then the block address will increment and write to the next block until + // /e ulNumBlocks * Block Size bytes have been written. + // + // /return Returns the number of bytes that were written to the device. + // + //***************************************************************************** + unsigned long (* BlockWrite)(void * pvDrive, unsigned char *pucData, + unsigned long ulSector, + unsigned long ulNumBlocks); + + //***************************************************************************** + // + // This function will return the number of blocks present on a device. + // + // /param pvDrive is the pointer that was returned from a call to + // USBDMSCStorageOpen(). + // + // This function is used to return the total number of blocks on a physical + // device based on the /e pvDrive parameter. + // + // /return Returns the number of blocks that are present in a device. + // + //***************************************************************************** + unsigned long (* NumBlocks)(void * pvDrive); + +} +tMSCDMedia; + +//***************************************************************************** +// +// These defines control the sizes of USB transfers for data and commands. +// +//***************************************************************************** +#define DEVICE_BLOCK_SIZE 512 + +//***************************************************************************** +// +// PRIVATE +// +// This structure defines the private instance data and state variables for the +// mass storage class. The memory for this structure is pointed to by +// the psPrivateData field in the tUSBDMSCDevice structure passed on +// USBDMSCInit() and should not be modified by any code outside of the mass +// storage device code. +// +//***************************************************************************** +typedef struct +{ + unsigned long ulUSBBase; + tDeviceInfo *psDevInfo; + tConfigDescriptor *psConfDescriptor; + + // + // These three values are used to return the current sense data for an + // instance of the mass storage class. + // + unsigned char ucErrorCode; + unsigned char ucSenseKey; + unsigned short usAddSenseCode; + + // + // The pointer to the instance returned from the Open call to the media. + // + void *pvMedia; + + volatile tBoolean bConnected; + + // + // Holds the flag settings for this instance. + // + unsigned long ulFlags; + + tUSBDMSCMediaStatus eMediaStatus; + + unsigned long pulBuffer[DEVICE_BLOCK_SIZE>>2]; + unsigned long ulBytesToTransfer; + unsigned long ulCurrentLBA; + + unsigned char ucINEndpoint; + unsigned char ucINDMA; + unsigned char ucOUTEndpoint; + unsigned char ucOUTDMA; + unsigned char ucInterface; + unsigned char ucSCSIState; +} +tMSCInstance; + +//***************************************************************************** +// +// +//***************************************************************************** +#ifdef DEPRECATED +//***************************************************************************** +// +// This is the size in bytes of the private data for the mass storage class. +// +// This value is deprecated and should not be used, any new code should just +// pass in a tMSCInstance structure in the psPrivateData field. +// +//***************************************************************************** +#define USB_MSC_WORKSPACE_SIZE sizeof(tMSCInstance); +#endif + +//***************************************************************************** +// +//! The size of the memory that should be allocated to create a configuration +//! descriptor for a single instance of the USB Audio Device. +//! This does not include the configuration descriptor which is automatically +//! ignored by the composite device class. +// +// For reference this is sizeof(g_pAudioControlInterface) + +// sizeof(g_pAudioStreamInterface +// +//***************************************************************************** +#define COMPOSITE_DMSC_SIZE (23) + +//***************************************************************************** +// +//! The structure used by the application to define operating parameters for +//! the mass storage device. +// +//***************************************************************************** +typedef struct +{ + // + //! The vendor ID that this device is to present in the device descriptor. + // + unsigned short usVID; + + // + //! The product ID that this device is to present in the device descriptor. + // + unsigned short usPID; + + // + //! 8 byte vendor string. + // + unsigned char pucVendor[8]; + + // + //! 16 byte vendor string. + // + unsigned char pucProduct[16]; + + // + //! 4 byte vendor string. + // + unsigned char pucVersion[4]; + + // + //! The maximum power consumption of the device, expressed in milliamps. + // + unsigned short usMaxPowermA; + + // + //! Indicates whether the device is self or bus-powered and whether or not + //! it supports remote wakeup. Valid values are USB_CONF_ATTR_SELF_PWR or + //! USB_CONF_ATTR_BUS_PWR, optionally ORed with USB_CONF_ATTR_RWAKE. + // + unsigned char ucPwrAttributes; + + // + //! A pointer to the string descriptor array for this device. This array + //! must contain the following string descriptor pointers in this order. + //! Language descriptor, Manufacturer name string (language 1), Product + //! name string (language 1), Serial number string (language 1), MSC + //! Interface description string (language 1), Configuration description + //! string (language 1). + //! + //! If supporting more than 1 language, the descriptor block (except for + //! string descriptor 0) must be repeated for each language defined in the + //! language descriptor. + //! + // + const unsigned char * const *ppStringDescriptors; + + // + //! The number of descriptors provided in the ppStringDescriptors + //! array. This must be 1 + ((5 + (num HID strings)) * (num languages)). + // + unsigned long ulNumStringDescriptors; + + // + //! This structure holds the access functions for the media used by this + //! instance of the mass storage class device. All of the functions in this + //! structure are required to be filled out with valid functions. + // + tMSCDMedia sMediaFunctions; + + // + //! This is the callback function for various events that occur during + //! mass storage class operation. + // + tUSBCallback pfnEventCallback; + + // + //! A pointer to the private instance data for this device. This memory + //! must remain accessible for as long as the MSC device is in use and must + //! not be modified by any code outside the MSC class driver. + // + tMSCInstance *psPrivateData; +} +tUSBDMSCDevice; + +//***************************************************************************** +// +// MSC-specific device class driver events +// +//***************************************************************************** + +//***************************************************************************** +// +//! This event indicates that the host has completed other operations and is +//! no longer accessing the device. +// +//***************************************************************************** +#define USBD_MSC_EVENT_IDLE (USBD_MSC_EVENT_BASE + 0) + +//***************************************************************************** +// +//! This event indicates that the host is reading the storage media. +// +//***************************************************************************** +#define USBD_MSC_EVENT_READING (USBD_MSC_EVENT_BASE + 1) + +//***************************************************************************** +// +//! This event indicates that the host is writing to the storage media. +// +//***************************************************************************** +#define USBD_MSC_EVENT_WRITING (USBD_MSC_EVENT_BASE + 2) + +extern tDeviceInfo g_sMSCDeviceInfo; + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void *USBDMSCInit(unsigned long ulIndex, + const tUSBDMSCDevice *psMSCDevice); +extern void *USBDMSCCompositeInit(unsigned long ulIndex, + const tUSBDMSCDevice *psMSCDevice); +extern void USBDMSCTerm(void *pvInstance); +extern void USBDMSCMediaChange(void *pvInstance, + tUSBDMSCMediaStatus eMediaStatus); + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif -- cgit v1.3.1