From 990090a4cc9070837d31e66b58d40f0c3d038741 Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Sun, 16 Mar 2014 14:41:11 +0200 Subject: Add usblib and utils --- usblib/device/usbdaudio.c | 1510 +++++++++++++++++++ usblib/device/usbdaudio.h | 382 +++++ usblib/device/usbdbulk.c | 1562 ++++++++++++++++++++ usblib/device/usbdbulk.h | 300 ++++ usblib/device/usbdcdc.c | 3043 ++++++++++++++++++++++++++++++++++++++ usblib/device/usbdcdc.h | 447 ++++++ usblib/device/usbdcdesc.c | 643 ++++++++ usblib/device/usbdcomp.c | 1544 +++++++++++++++++++ usblib/device/usbdcomp.h | 261 ++++ usblib/device/usbdconfig.c | 555 +++++++ usblib/device/usbddfu-rt.c | 661 +++++++++ usblib/device/usbddfu-rt.h | 184 +++ usblib/device/usbdenum.c | 3190 ++++++++++++++++++++++++++++++++++++++++ usblib/device/usbdevice.h | 232 +++ usblib/device/usbdevicepriv.h | 248 ++++ usblib/device/usbdhandler.c | 85 ++ usblib/device/usbdhid.c | 2502 +++++++++++++++++++++++++++++++ usblib/device/usbdhid.h | 1102 ++++++++++++++ usblib/device/usbdhidgamepad.c | 845 +++++++++++ usblib/device/usbdhidgamepad.h | 274 ++++ usblib/device/usbdhidkeyb.c | 1321 +++++++++++++++++ usblib/device/usbdhidkeyb.h | 365 +++++ usblib/device/usbdhidmouse.c | 1008 +++++++++++++ usblib/device/usbdhidmouse.h | 299 ++++ usblib/device/usbdmsc.c | 2441 ++++++++++++++++++++++++++++++ usblib/device/usbdmsc.h | 420 ++++++ 26 files changed, 25424 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/usbdhidgamepad.c create mode 100644 usblib/device/usbdhidgamepad.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..18c90b5 --- /dev/null +++ b/usblib/device/usbdaudio.c @@ -0,0 +1,1510 @@ +//***************************************************************************** +// +// usbdaudio.c - USB audio device class driver. +// +// Copyright (c) 2009-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.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 + +//***************************************************************************** +// +// 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 uint8_t g_pui8AudioDeviceDescriptor[] = +{ + 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 uint8_t g_pui8AudioDescriptor[] = +{ + // + // 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. +// +//***************************************************************************** +uint8_t g_pui8IADAudioDescriptor[AUDIODESCRIPTOR_SIZE] = +{ + + 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_pui8IADAudioDescriptor), + g_pui8IADAudioDescriptor +}; + +//***************************************************************************** +// +// The remainder of the configuration descriptor is stored in flash since we +// don't need to modify anything in it at runtime. +// +//***************************************************************************** +const uint8_t g_pui8AudioControlInterface[CONTROLINTERFACE_SIZE] = +{ + // + // 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_pui8AudioControlInterface + 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 uint8_t g_pui8AudioStreamInterface[STREAMINTERFACE_SIZE] = +{ + // + // 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 | USBEPToIndex(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_pui8AudioDescriptor), + g_pui8AudioDescriptor +}; + +const tConfigSection g_sAudioStreamInterfaceSection = +{ + sizeof(g_pui8AudioStreamInterface), + g_pui8AudioStreamInterface +}; + +const tConfigSection g_sAudioControlInterfaceSection = +{ + sizeof(g_pui8AudioControlInterface), + g_pui8AudioControlInterface +}; + +//***************************************************************************** +// +// 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(g_psAudioSections[0])) + +//***************************************************************************** +// +// The header for the single configuration we support. This is the root of +// the data structure that defines all the bits and pieces that are pulled +// together to generate the configuration descriptor. +// +//***************************************************************************** +const tConfigHeader g_sAudioConfigHeader = +{ + NUM_AUDIO_SECTIONS, + g_psAudioSections +}; + +//***************************************************************************** +// +// Configuration Descriptor. +// +//***************************************************************************** +const tConfigHeader * const g_ppAudioConfigDescriptors[] = +{ + &g_sAudioConfigHeader +}; + +//***************************************************************************** +// +// Various internal handlers needed by this class. +// +//***************************************************************************** +static void HandleDisconnect(void *pvAudioDevice); +static void InterfaceChange(void *pvAudioDevice, uint8_t ui8Interface, + uint8_t ui8AlternateSetting); +static void ConfigChangeHandler(void *pvAudioDevice, uint32_t ui32Value); +static void DataReceived(void *pvAudioDevice, uint32_t ui32Info); +static void HandleEndpoints(void *pvAudioDevice, uint32_t ui32Status); +static void HandleRequests(void *pvAudioDevice, tUSBRequest *psUSBRequest); +static void HandleDevice(void *pvAudioDevice, uint32_t ui32Request, + void *pvRequestData); + +//***************************************************************************** +// +// The device information structure for the USB Audio device. +// +//***************************************************************************** +static const tCustomHandlers g_sAudioHandlers = +{ + // + // 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 +}; + +//***************************************************************************** +// +// 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 *pvAudioDevice, uint32_t ui32Info) +{ + tAudioInstance *psInst; + tUSBDAudioDevice *psAudioDevice; + + ASSERT(pvAudioDevice != 0); + + // + // Create the instance pointer. + // + psAudioDevice = (tUSBDAudioDevice *)pvAudioDevice; + + // + // Make a copy of this pointer for ease of use in this function. + // + psInst = &psAudioDevice->sPrivateData; + + // + // If there is an update pending and the request was to set a current + // value then check which value was set. + // + if(psInst->ui16Update && (psInst->ui8Request == USB_AC_SET_CUR)) + { + // + // Only handling interface requests. + // + if((psInst->ui16RequestType & USB_RTYPE_RECIPIENT_M) == + USB_RTYPE_INTERFACE) + { + if(psInst->ui16Update == VOLUME_CONTROL) + { + // + // Inform the callback of the new volume. + // + psAudioDevice->pfnCallback(0, USBD_AUDIO_EVENT_VOLUME, + psInst->i16Volume, 0); + } + else if(psAudioDevice->sPrivateData.ui16Update == MUTE_CONTROL) + { + // + // Inform the callback of the new data. + // + psAudioDevice->pfnCallback(0, USBD_AUDIO_EVENT_MUTE, + psInst->ui8Mute, 0); + } + } + psInst->ui16Update = 0; + } +} + +//***************************************************************************** +// +// This function is called to handle the interrupts on the isochronous endpoint +// for the audio device class. +// +//***************************************************************************** +static void +HandleEndpoints(void *pvAudioDevice, uint32_t ui32Status) +{ + uint32_t ui32EPStatus; + tAudioInstance *psInst; + tUSBDAudioDevice *psAudioDevice; + uint32_t ui32Size; + + ASSERT(pvAudioDevice != 0); + + // + // The audio device structure pointer. + // + psAudioDevice = (tUSBDAudioDevice *)pvAudioDevice; + + // + // Create a pointer to the audio instance data. + // + psInst = &psAudioDevice->sPrivateData; + + // + // Read out the current endpoint status. + // + ui32EPStatus = MAP_USBEndpointStatus(USB0_BASE, psInst->ui8OUTEndpoint); + + // + // See if there is a receive interrupt pending. + // + if(ui32Status & (0x10000 << USBEPToIndex(psInst->ui8OUTEndpoint))) + { + // + // Get the amount of data available in the FIFO. + // + ui32Size = USBEndpointDataAvail(psInst->ui32USBBase, + psInst->ui8OUTEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(USB0_BASE, psInst->ui8OUTEndpoint, + ui32EPStatus); + + // + // Configure the next DMA transfer. + // + USBLibDMATransfer(psInst->psDMAInstance, psInst->ui8OUTDMA, + psInst->sBuffer.pvData, ui32Size); + } + else if((USBLibDMAChannelStatus(psInst->psDMAInstance, + psInst->ui8OUTDMA) == + USBLIBSTATUS_DMA_COMPLETE)) + { + USBEndpointDMADisable(USB0_BASE, + psInst->ui8OUTEndpoint, USB_EP_DEV_OUT); + + // + // Acknowledge that the data was read, this will not cause a bus + // acknowledgment. + // + MAP_USBDevEndpointDataAck(USB0_BASE, psInst->ui8OUTEndpoint, 0); + + // + // Inform the callback of the new data. + // + psInst->sBuffer.pfnCallback(psInst->sBuffer.pvData, + psInst->sBuffer.ui32Size, + USBD_AUDIO_EVENT_DATAOUT); + } +} + +//***************************************************************************** +// +// Device instance specific handler. +// +//***************************************************************************** +static void +HandleDevice(void *pvAudioDevice, uint32_t ui32Request, void *pvRequestData) +{ + tAudioInstance *psInst; + uint8_t *pui8Data; + tUSBDAudioDevice *psAudioDevice; + + // + // The audio device structure pointer. + // + psAudioDevice = (tUSBDAudioDevice *)pvAudioDevice; + + // + // Create a pointer to the audio instance data. + // + psInst = &psAudioDevice->sPrivateData; + + // + // Create the 8-bit array used by the events supported by the USB CDC + // serial class. + // + pui8Data = (uint8_t *)pvRequestData; + + switch(ui32Request) + { + // + // This was an interface change event. + // + case USB_EVENT_COMP_IFACE_CHANGE: + { + // + // Save the change to the appropriate interface number. + // + if(pui8Data[0] == AUDIO_INTERFACE_CONTROL) + { + psInst->ui8InterfaceControl = pui8Data[1]; + } + else if(pui8Data[0] == AUDIO_INTERFACE_OUTPUT) + { + psInst->ui8InterfaceAudio = pui8Data[1]; + } + break; + } + + // + // This was an endpoint change event. + // + case USB_EVENT_COMP_EP_CHANGE: + { + // + // Determine if this is an IN or OUT endpoint that has changed. + // + if((pui8Data[0] & USB_EP_DESC_IN) == 0) + { + // + // Extract the new endpoint number without the DIR bit. + // + psInst->ui8OUTEndpoint = IndexToUSBEP(pui8Data[1] & 0x7f); + + // + // If the DMA channel has already been allocated then clear + // that channel and prepare to possibly use a new one. + // + if(psInst->ui8OUTDMA != 0) + { + USBLibDMAChannelRelease(psInst->psDMAInstance, + psInst->ui8OUTDMA); + } + + // + // Allocate a DMA channel to the endpoint. + // + psInst->ui8OUTDMA = + USBLibDMAChannelAllocate(psInst->psDMAInstance, + psInst->ui8OUTEndpoint, + ISOC_OUT_EP_MAX_SIZE, + (USB_DMA_EP_RX | + USB_DMA_EP_TYPE_ISOC | + USB_DMA_EP_DEVICE)); + + // + // Set the DMA individual transfer size. + // + USBLibDMAUnitSizeSet(psInst->psDMAInstance, psInst->ui8OUTDMA, + 32); + + // + // Set the DMA arbitration size. + // + USBLibDMAArbSizeSet(psInst->psDMAInstance, psInst->ui8OUTDMA, + 16); + } + break; + } + + // + // 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. + // + pui8Data[2] = psInst->ui8InterfaceControl; + + break; + } + case USB_EVENT_LPM_RESUME: + { + if(psAudioDevice->pfnCallback) + { + // + // Pass the LPM resume event to the client. + // + psAudioDevice->pfnCallback(0, USB_EVENT_LPM_RESUME, 0, + (void *)0); + } + break; + } + case USB_EVENT_LPM_SLEEP: + { + if(psAudioDevice->pfnCallback) + { + // + // Pass the LPM sleep event to the client. + // + psAudioDevice->pfnCallback(0, USB_EVENT_LPM_SLEEP, 0, + (void *)0); + } + break; + } + case USB_EVENT_LPM_ERROR: + { + if(psAudioDevice->pfnCallback) + { + // + // Pass the LPM error event to the client. + // + psAudioDevice->pfnCallback(0, USB_EVENT_LPM_ERROR, 0, + (void *)0); + } + break; + } + default: + { + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device is +// disconnected from the host. +// +//***************************************************************************** +static void +HandleDisconnect(void *pvAudioDevice) +{ + const tUSBDAudioDevice *psAudioDevice; + + ASSERT(pvAudioDevice != 0); + + // + // The audio device structure pointer. + // + psAudioDevice = (const tUSBDAudioDevice *)pvAudioDevice; + + // + // Inform the application that the device has been disconnected. + // + psAudioDevice->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 *pvAudioDevice, uint8_t ui8Interface, + uint8_t ui8AlternateSetting) +{ + const tUSBDAudioDevice *psAudioDevice; + + ASSERT(pvAudioDevice != 0); + + // + // The audio device structure pointer. + // + psAudioDevice = (const tUSBDAudioDevice *)pvAudioDevice; + + // + // Check which interface to change into. + // + if(ui8AlternateSetting == 0) + { + // + // Alternate setting 0 is an inactive state. + // + if(psAudioDevice->pfnCallback) + { + psAudioDevice->pfnCallback(0, USBD_AUDIO_EVENT_IDLE, 0, 0); + } + } + else + { + // + // Alternate setting 1 is the active state. + // + if(psAudioDevice->pfnCallback) + { + psAudioDevice->pfnCallback(0, USBD_AUDIO_EVENT_ACTIVE, 0, 0); + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device +// configuration changes. +// +//***************************************************************************** +static void +ConfigChangeHandler(void *pvAudioDevice, uint32_t ui32Value) +{ + const tUSBDAudioDevice *psAudioDevice; + + ASSERT(pvAudioDevice != 0); + + // + // The audio device structure pointer. + // + psAudioDevice = (const tUSBDAudioDevice *)pvAudioDevice; + + // + // If we have a control callback, let the client know we are open for + // business. + // + if(psAudioDevice->pfnCallback) + { + // + // Pass the connected event to the client. + // + psAudioDevice->pfnCallback(pvAudioDevice, 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 ui32Index is the index of the USB controller to initialize for +//! audio class device operation. +//! \param psAudioDevice 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 psAudioDevice 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(uint32_t ui32Index, tUSBDAudioDevice *psAudioDevice) +{ + tConfigDescriptor *psConfigDesc; + tDeviceDescriptor *psDevDesc; + + // + // Check parameter validity. + // + ASSERT(ui32Index == 0); + ASSERT(psAudioDevice); + ASSERT(psAudioDevice->ppui8StringDescriptors); + + // + // Composite Init handles all initialization that is not specific to a + // multiple instance device. + // + USBDAudioCompositeInit(ui32Index, psAudioDevice, 0); + + // + // Fix up the device descriptor with the client-supplied values. + // + psDevDesc = (tDeviceDescriptor *)g_pui8AudioDeviceDescriptor; + psDevDesc->idVendor = psAudioDevice->ui16VID; + psDevDesc->idProduct = psAudioDevice->ui16PID; + + // + // Fix up the configuration descriptor with client-supplied values. + // + psConfigDesc = (tConfigDescriptor *)g_pui8AudioDescriptor; + psConfigDesc->bmAttributes = psAudioDevice->ui8PwrAttributes; + psConfigDesc->bMaxPower = (uint8_t)(psAudioDevice->ui16MaxPowermA / 2); + + // + // All is well so now pass the descriptors to the lower layer and put + // the bulk device on the bus. + // + USBDCDInit(ui32Index, &psAudioDevice->sPrivateData.sDevInfo, + (void *)psAudioDevice); + + // + // Configure the DMA for the OUT endpoint. + // + psAudioDevice->sPrivateData.ui8OUTDMA = + USBLibDMAChannelAllocate(psAudioDevice->sPrivateData.psDMAInstance, + psAudioDevice->sPrivateData.ui8OUTEndpoint, + ISOC_OUT_EP_MAX_SIZE, + USB_DMA_EP_RX | USB_DMA_EP_TYPE_ISOC | + USB_DMA_EP_DEVICE); + + USBLibDMAUnitSizeSet(psAudioDevice->sPrivateData.psDMAInstance, + psAudioDevice->sPrivateData.ui8OUTDMA, 32); + USBLibDMAArbSizeSet(psAudioDevice->sPrivateData.psDMAInstance, + psAudioDevice->sPrivateData.ui8OUTDMA, 16); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psAudioDevice); +} + +//***************************************************************************** +// +//! This function should be called once for the audio class device to +//! initialized basic operation and prepare for enumeration. +//! +//! \param ui32Index is the index of the USB controller to initialize for +//! audio class device operation. +//! \param psAudioDevice points to a structure containing parameters +//! customizing the operation of the audio device. +//! \param psCompEntry is the composite device entry to initialize when +//! creating a composite 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 psAudioDevice 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. When this audio device is part of a composite +//! device, then the \e psCompEntry should point to the composite device entry +//! to initialize. This is part of the array that is passed to the +//! USBDCompositeInit() function. +//! +//! This function returns a void pointer that must be passed in to all other +//! APIs used by the audio class. +//! +//! See the documentation on the tUSBDAudioDevice structure for more +//! information on how to properly fill the structure members. +//! +//! \return Returns zero on failure or a non-zero instance value that should be +//! used with the remaining USB audio APIs. +// +//***************************************************************************** +void * +USBDAudioCompositeInit(uint32_t ui32Index, tUSBDAudioDevice *psAudioDevice, + tCompositeEntry *psCompEntry) +{ + tAudioInstance *psInst; + + // + // Check parameter validity. + // + ASSERT(ui32Index == 0); + ASSERT(psAudioDevice); + ASSERT(psAudioDevice->ppui8StringDescriptors); + + // + // Initialize the workspace in the passed instance structure. + // + psInst = &psAudioDevice->sPrivateData; + psInst->ui32USBBase = USB0_BASE; + + // + // Initialize the composite entry that is used by the composite device + // class. + // + if(psCompEntry != 0) + { + psCompEntry->psDevInfo = &psInst->sDevInfo; + psCompEntry->pvInstance = (void *)psAudioDevice; + } + + // + // Initialize the device information structure. + // + psInst->sDevInfo.psCallbacks = &g_sAudioHandlers; + psInst->sDevInfo.pui8DeviceDescriptor = g_pui8AudioDeviceDescriptor; + psInst->sDevInfo.ppsConfigDescriptors = g_ppAudioConfigDescriptors; + psInst->sDevInfo.ppui8StringDescriptors = 0; + psInst->sDevInfo.ui32NumStringDescriptors = 0; + + // + // Initialize the device info structure for the HID device. + // + USBDCDDeviceInfoInit(0, &psInst->sDevInfo); + + // + // The Control interface is at index 0. + // + psInst->ui8InterfaceControl = AUDIO_INTERFACE_CONTROL; + + // + // The Audio interface is at index 1. + // + psInst->ui8InterfaceAudio = AUDIO_INTERFACE_OUTPUT; + + // + // Set the default Isochronous OUT endpoint. + // + psInst->ui8OUTEndpoint = ISOC_OUT_ENDPOINT; + psInst->ui8OUTDMA = 0; + + // + // Set the initial buffer to null. + // + psInst->sBuffer.pvData = 0; + + // + // Save the volume settings. + // + psInst->i16VolumeMax = psAudioDevice->i16VolumeMax; + psInst->i16VolumeMin = psAudioDevice->i16VolumeMin; + psInst->i16VolumeStep = psAudioDevice->i16VolumeStep; + + // + // No update pending to any command. + // + psInst->ui16Update = 0; + + // + // Plug in the client's string stable to the device information + // structure. + // + psInst->sDevInfo.ppui8StringDescriptors = + psAudioDevice->ppui8StringDescriptors; + psInst->sDevInfo.ui32NumStringDescriptors = + psAudioDevice->ui32NumStringDescriptors; + + // + // Get the DMA instance pointer. + // + psInst->psDMAInstance = USBLibDMAInit(0); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psAudioDevice); +} + +//***************************************************************************** +// +//! Shuts down the audio device. +//! +//! \param pvAudioDevice 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 pvAudioDevice instance should not me used in +//! any other calls. +//! +//! \return None. +// +//***************************************************************************** +void +USBDAudioTerm(void *pvAudioDevice) +{ + ASSERT(pvAudioDevice != 0); + + // + // Cleanly exit device mode. + // + USBDCDTerm(0); +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever a non-standard +// request is received. +// +// \param pvAudioDevice is the instance data for this request. +// \param psUSBRequest 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 *pvAudioDevice, tUSBRequest *psUSBRequest) +{ + uint32_t ui32Control, ui32Recipient, ui32Stall; + tAudioInstance *psInst; + tUSBDAudioDevice *psAudioDevice; + + ASSERT(pvAudioDevice != 0); + + // + // The audio device structure pointer. + // + psAudioDevice = (tUSBDAudioDevice *)pvAudioDevice; + + // + // Create a pointer to the audio instance data. + // + psInst = &psAudioDevice->sPrivateData; + + // + // 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. + // + ui32Stall = 0; + + // + // Get the request type. + // + ui32Recipient = psUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M; + + // + // Save the request type and request value. + // + psInst->ui16RequestType = psUSBRequest->bmRequestType; + psInst->ui8Request = psUSBRequest->bRequest; + + // + // Check if this is an endpoint request to the audio streaming endpoint. + // + if((ui32Recipient == USB_RTYPE_ENDPOINT) && + (psUSBRequest->wIndex == USBEPToIndex(psInst->ui8OUTEndpoint))) + { + // + // Determine the type of request. + // + switch(psInst->ui8Request) + { + case USB_AC_SET_CUR: + { + // + // Handle retrieving the sample rate. + // + if(psUSBRequest->wValue == SAMPLING_FREQ_CONTROL) + { + // + // Retrieve the requested sample rate. + // + USBDCDRequestDataEP0(0, + (uint8_t *)&psInst->ui32SampleRate, + 3); + + // + // Save what we are updating. + // + psInst->ui16Update = SAMPLING_FREQ_CONTROL; + } + break; + } + case USB_AC_GET_CUR: + { + // + // Handle retrieving the sample rate. + // + if(psUSBRequest->wValue == SAMPLING_FREQ_CONTROL) + { + // + // Send back the current sample rate. + // + USBDCDSendDataEP0(0, + (uint8_t *)&psInst->ui32SampleRate, + 3); + } + break; + } + default: + { + // + // Stall on unknown commands. + // + ui32Stall = 1; + break; + } + } + } + else if(ui32Recipient == USB_RTYPE_INTERFACE) + { + // + // Make sure the request was for the control interface. + // + if((uint8_t)psUSBRequest->wIndex != psInst->ui8InterfaceControl) + { + return; + } + + // + // Extract the control value from the message. + // + ui32Control = psUSBRequest->wValue & USB_CS_CONTROL_M; + + // + // Handle an audio control request to the feature control unit. + // + if((AUDIO_CONTROL_ID << 8) == + (psUSBRequest->wIndex & USB_CS_CONTROL_M)) + { + // + // Determine the type of request. + // + switch(psInst->ui8Request) + { + case USB_AC_GET_MAX: + { + if(ui32Control == VOLUME_CONTROL) + { + // + // Return the maximum volume setting. + // + USBDCDSendDataEP0(0, + (uint8_t *)&psInst->i16VolumeMax, + 2); + } + else + { + // + // Stall on unknown commands. + // + ui32Stall = 1; + } + break; + } + case USB_AC_GET_MIN: + { + if(ui32Control == VOLUME_CONTROL) + { + // + // Return the minimum volume setting. + // + USBDCDSendDataEP0(0, + (uint8_t *)&psInst->i16VolumeMin, + 2); + } + else + { + // + // Stall on unknown commands. + // + ui32Stall = 1; + } + break; + } + case USB_AC_GET_RES: + { + if(ui32Control == VOLUME_CONTROL) + { + // + // Return the volume step setting. + // + USBDCDSendDataEP0(0, + (uint8_t *)&psInst->i16VolumeStep, + 2); + } + else + { + // + // Stall on unknown commands. + // + ui32Stall = 1; + } + break; + } + case USB_AC_GET_CUR: + { + if(ui32Control == VOLUME_CONTROL) + { + // + // Send back the current volume level. + // + USBDCDSendDataEP0(0, + (uint8_t *)&psInst->i16Volume, + 2); + } + else if(ui32Control == MUTE_CONTROL) + { + // + // Send back the current mute value. + // + USBDCDSendDataEP0(0, + (uint8_t *)&psInst->ui8Mute, 1); + } + else + { + // + // Stall on unknown commands. + // + ui32Stall = 1; + } + break; + } + case USB_AC_SET_CUR: + { + if(ui32Control == VOLUME_CONTROL) + { + // + // Read the new volume level. + // + USBDCDRequestDataEP0(0, + (uint8_t *)&psInst->i16Volume, + 2); + + // + // Save what we are updating. + // + psInst->ui16Update = VOLUME_CONTROL; + } + else if(ui32Control == MUTE_CONTROL) + { + // + // Read the new mute setting. + // + USBDCDRequestDataEP0(0, + (uint8_t *)&psInst->ui8Mute, + 1); + + // + // Save what we are updating. + // + psInst->ui16Update = MUTE_CONTROL; + } + else + { + // + // Stall on unknown commands. + // + ui32Stall = 1; + } + break; + } + case USB_AC_SET_RES: + { + if(ui32Control == VOLUME_CONTROL) + { + // + // Read the new volume step setting. + // + USBDCDRequestDataEP0(0, + (uint8_t *)&psInst->i16VolumeStep, 2); + + // + // Save what we are updating. + // + psInst->ui16Update = VOLUME_CONTROL; + } + else + { + // + // Stall on unknown commands. + // + ui32Stall = 1; + } + break; + } + default: + { + // + // Stall on unknown commands. + // + ui32Stall = 1; + break; + } + } + } + } + + // + // Stall on all unknown commands. + // + if(ui32Stall) + { + USBDCDStallEP0(0); + } +} + +//***************************************************************************** +// +//! This function is used to supply buffers to the audio class to be filled +//! from the USB host device. +//! +//! \param pvAudioDevice is the pointer to the device instance structure as +//! returned by USBDAudioInit() or USBDAudioCompositeInit(). +//! \param pvBuffer is a pointer to the buffer to fill with audio data. +//! \param ui32Size is the size in bytes of the buffer pointed to by the +//! \e 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 ui32Size one packet of data from the host controller. The +//! \e ui32Size 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. +// +//***************************************************************************** +int32_t +USBAudioBufferOut(void *pvAudioDevice, void *pvBuffer, uint32_t ui32Size, + tUSBAudioBufferCallback pfnCallback) +{ + tAudioInstance *psInst; + tUSBDAudioDevice *psAudioDevice; + + // + // Make sure we were not passed NULL pointers. + // + ASSERT(pvAudioDevice != 0); + ASSERT(pvBuffer != 0); + + // + // Buffer must be at least one packet in size. + // + ASSERT(ui32Size >= ISOC_OUT_EP_MAX_SIZE); + ASSERT(pfnCallback); + + // + // The audio device structure pointer. + // + psAudioDevice = (tUSBDAudioDevice *)pvAudioDevice; + + // + // Create a pointer to the audio instance data. + // + psInst = &psAudioDevice->sPrivateData; + + // + // Initialize the buffer instance. + // + psInst->sBuffer.pvData = pvBuffer; + psInst->sBuffer.ui32Size = ui32Size; + psInst->sBuffer.ui32NumBytes = 0; + psInst->sBuffer.pfnCallback = pfnCallback; + + return(0); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + diff --git a/usblib/device/usbdaudio.h b/usblib/device/usbdaudio.h new file mode 100644 index 0000000..e934914 --- /dev/null +++ b/usblib/device/usbdaudio.h @@ -0,0 +1,382 @@ +//***************************************************************************** +// +// usbdaudio.h - USB audio device class driver. +// +// Copyright (c) 2009-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#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, uint32_t ui32Param, + uint32_t ui32Event); + +//***************************************************************************** +// +// 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 pi16PrivateData field in the tUSBDAudioDevice structure passed on +// USBDAudioInit() and should not be modified by any code outside of the audio +// device. +// +//***************************************************************************** +typedef struct +{ + // + // Base address for the USB controller. + // + uint32_t ui32USBBase; + + // + // The device info to interact with the lower level DCD code. + // + tDeviceInfo sDevInfo; + + // + // The maximum volume expressed as an 8.8 signed value. + // + int16_t i16VolumeMax; + + // + // The minimum volume expressed as an 8.8 signed value. + // + int16_t i16VolumeMin; + + // + // The minimum volume step expressed as an 8.8 signed value. + // + int16_t i16VolumeStep; + + struct + { + // + // Pointer to a buffer provided by caller. + // + void *pvData; + + // + // Size of the data area provided in pvData in bytes. + // + uint32_t ui32Size; + + // + // Number of valid bytes copied into the pvData area. + // + uint32_t ui32NumBytes; + + // + // The buffer callback for this function. + // + tUSBAudioBufferCallback pfnCallback; + } + sBuffer; + + // + // Pending request type. + // + uint16_t ui16RequestType; + + // + // Pending request. + // + uint8_t ui8Request; + + // + // Pending update value. + // + uint16_t ui16Update; + + // + // Current Volume setting. + // + int16_t i16Volume; + + // + // Current Mute setting. + // + uint8_t ui8Mute; + + // + // Current Sample rate, this is not writable but the host will try. + // + uint32_t ui32SampleRate; + + // + // The OUT endpoint in use by this instance. + // + uint8_t ui8OUTEndpoint; + + // + // The OUT endpoint DMA channel in use by this instance. + // + uint8_t ui8OUTDMA; + + // + // The control interface number associated with this instance. + // + uint8_t ui8InterfaceControl; + + // + // The audio interface number associated with this instance. + // + uint8_t ui8InterfaceAudio; + + // + // A copy of the DMA instance data used with calls to USBLibDMA functions. + // + tUSBDMAInstance *psDMAInstance; +} +tAudioInstance; + +//***************************************************************************** +// +// This is the size of the g_pui8IADAudioDescriptor array in bytes. +// +//***************************************************************************** +#define AUDIODESCRIPTOR_SIZE (8) + +//***************************************************************************** +// +// This is the size of the g_pui8AudioControlInterface array in bytes. +// +//***************************************************************************** +#define CONTROLINTERFACE_SIZE (52) + +//***************************************************************************** +// +// This is the size of the g_pui8AudioStreamInterface array in bytes. +// +//***************************************************************************** +#define STREAMINTERFACE_SIZE (52) + +//***************************************************************************** +// +//! 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. +// +//***************************************************************************** +#define COMPOSITE_DAUDIO_SIZE (AUDIODESCRIPTOR_SIZE + \ + CONTROLINTERFACE_SIZE + STREAMINTERFACE_SIZE) + +//***************************************************************************** +// +//! 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. + // + const uint16_t ui16VID; + + // + //! The product ID that this device is to present in the device descriptor. + // + const uint16_t ui16PID; + + // + //! 8 byte vendor string. + // + const char pcVendor[8]; + + // + //! 16 byte vendor string. + // + const char pcProduct[16]; + + // + //! 4 byte vendor string. + // + const char pcVersion[4]; + + // + //! The maximum power consumption of the device, expressed in mA. + // + const uint16_t ui16MaxPowermA; + + // + //! 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. + // + const uint8_t ui8PwrAttributes; + + // + //! A pointer to the callback function which will be called to notify + //! the application of events relating to the operation of the audio + //! device. + // + const 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 uint8_t * const *ppui8StringDescriptors; + + // + //! The number of descriptors provided in the ppStringDescriptors + //! array. This must be 1 + ((5 + (number of strings)) * + //! (number of languages)). + // + const uint32_t ui32NumStringDescriptors; + + // + //! The maximum volume expressed as an 8.8 signed value. + // + const int16_t i16VolumeMax; + + // + //! The minimum volume expressed as an 8.8 signed value. + // + const int16_t i16VolumeMin; + + // + //! The minimum volume step expressed as an 8.8 signed value. + // + const int16_t i16VolumeStep; + + // + //! The private instance data for the audio device. + // + tAudioInstance sPrivateData; +} +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 ui32Param 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 occurred. The +//! \e ui32Param 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 occurred. The +//! \e ui32Param 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) + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void *USBDAudioInit(uint32_t ui32Index, + tUSBDAudioDevice *psAudioDevice); +extern void *USBDAudioCompositeInit(uint32_t ui32Index, + tUSBDAudioDevice *psAudioDevice, + tCompositeEntry *psCompEntry); +extern void USBDAudioTerm(void *pvAudioDevice); +extern int32_t USBAudioBufferOut(void *pvAudioDevice, void *pvBuffer, + uint32_t ui32Size, + 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..1e4cf0b --- /dev/null +++ b/usblib/device/usbdbulk.c @@ -0,0 +1,1562 @@ +//***************************************************************************** +// +// usbdbulk.c - USB bulk device class driver. +// +// Copyright (c) 2008-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbdbulk.h" +#include "usblib/device/usbdcomp.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 ui16DeferredOpFlags 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->ui16DeferredOpFlags to indicate that the operation is +// pending. +// +//***************************************************************************** +#define BULK_DO_PACKET_RX 5 + +//***************************************************************************** +// +// 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 bulk 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 USBFIFOSizeToBytes(DATA_IN_EP_FIFO_SIZE) +#define DATA_OUT_EP_MAX_SIZE USBFIFOSizeToBytes(DATA_OUT_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. +// +//***************************************************************************** +uint8_t g_pui8BulkDeviceDescriptor[] = +{ + 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. +// +//***************************************************************************** +uint8_t g_pui8BulkDescriptor[] = +{ + // + // 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 uint8_t g_pui8BulkInterface[BULKINTERFACE_SIZE] = +{ + // + // Vendor-specific Interface Descriptor. + // + 9, // Size of the interface descriptor. + USB_DTYPE_INTERFACE, // Type of this descriptor. + 0, // The index for this interface. + 0, // The alternate setting for this + // interface. + 2, // The number of endpoints used by this + // interface. + USB_CLASS_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 | USBEPToIndex(DATA_IN_ENDPOINT), + USB_EP_ATTR_BULK, // Endpoint is a bulk endpoint. + USBShort(DATA_IN_EP_MAX_SIZE), // The maximum packet size. + 0, // The polling interval for this endpoint. + + // + // Endpoint Descriptor + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_OUT | USBEPToIndex(DATA_OUT_ENDPOINT), + USB_EP_ATTR_BULK, // Endpoint is a bulk endpoint. + USBShort(DATA_OUT_EP_MAX_SIZE), // The maximum packet size. + 0, // The polling interval for this endpoint. +}; + +//***************************************************************************** +// +// The bulk 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_sBulkConfigSection = +{ + sizeof(g_pui8BulkDescriptor), + g_pui8BulkDescriptor +}; + +const tConfigSection g_sBulkInterfaceSection = +{ + sizeof(g_pui8BulkInterface), + g_pui8BulkInterface +}; + +//***************************************************************************** +// +// 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(g_psBulkSections[0])) + +//***************************************************************************** +// +// The header for the single configuration we support. This is the root of +// the data structure that defines all the bits and pieces that are pulled +// together to generate the configuration descriptor. +// +//***************************************************************************** +const tConfigHeader g_sBulkConfigHeader = +{ + NUM_BULK_SECTIONS, + g_psBulkSections +}; + +//***************************************************************************** +// +// Configuration Descriptor. +// +//***************************************************************************** +const tConfigHeader * const g_ppBulkConfigDescriptors[] = +{ + &g_sBulkConfigHeader +}; + +//***************************************************************************** +// +// Forward references for device handler callbacks +// +//***************************************************************************** +static void HandleConfigChange(void *pvBulkDevice, uint32_t ui32Info); +static void HandleDisconnect(void *pvBulkDevice); +static void HandleEndpoints(void *pvBulkDevice, uint32_t ui32Status); +static void HandleSuspend(void *pvBulkDevice); +static void HandleResume(void *pvBulkDevice); +static void HandleDevice(void *pvBulkDevice, uint32_t ui32Request, + void *pvRequestData); + +//***************************************************************************** +// +// Device event handler callbacks. +// +//***************************************************************************** +const tCustomHandlers g_sBulkHandlers = +{ + // + // GetDescriptor + // + 0, + + // + // RequestHandler + // + 0, + + // + // InterfaceChange + // + 0, + + // + // ConfigChange + // + HandleConfigChange, + + // + // DataReceived + // + 0, + + // + // DataSentCallback + // + 0, + + // + // ResetHandler + // + 0, + + // + // SuspendHandler + // + HandleSuspend, + + // + // ResumeHandler + // + HandleResume, + + // + // DisconnectHandler + // + HandleDisconnect, + + // + // EndpointHandler + // + HandleEndpoints, + + // + // Device handler + // + HandleDevice +}; + +//***************************************************************************** +// +// Set or clear deferred operation flags in an "atomic" manner. +// +// \param pui16DeferredOp points to the flags variable which is to be modified. +// \param ui16Bit 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 uint16_t *pui16DeferredOp, uint16_t ui16Bit, + bool bSet) +{ + // + // Set the flag bit to 1 or 0 using a bitband access. + // + HWREGBITH(pui16DeferredOp, ui16Bit) = bSet ? 1 : 0; +} + +//***************************************************************************** +// +// Receives notifications related to data received from the host. +// +// \param psBulkDevice is the device instance whose endpoint is to be +// processed. +// \param ui32Status 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 bool +ProcessDataFromHost(tUSBDBulkDevice *psBulkDevice, uint32_t ui32Status) +{ + uint32_t ui32EPStatus; + uint32_t ui32Size; + tBulkInstance *psInst; + + // + // Get a pointer to the bulk device instance data pointer + // + psInst = &psBulkDevice->sPrivateData; + + // + // Get the endpoint status to see why we were called. + // + ui32EPStatus = MAP_USBEndpointStatus(USB0_BASE, psInst->ui8OUTEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(USB0_BASE, psInst->ui8OUTEndpoint, + ui32EPStatus); + + // + // Has a packet been received? + // + if(ui32EPStatus & 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 does not read + // the packet in the context of the USB_EVENT_RX_AVAILABLE callback, + // the event will be signaled later during tick processing. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, BULK_DO_PACKET_RX, + true); + + // + // How big is the packet we have just received? + // + ui32Size = MAP_USBEndpointDataAvail(psInst->ui32USBBase, + psInst->ui8OUTEndpoint); + + // + // 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. + // + psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData, + USB_EVENT_RX_AVAILABLE, + ui32Size, (void *)0); + } + else + { + // + // No packet was received. Some error must have been reported. Check + // and pass this on to the client if necessary. + // + if(ui32EPStatus & USB_RX_ERROR_FLAGS) + { + // + // This is an error we report to the client so allow the callback + // to handle it. + // + psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData, + USB_EVENT_ERROR, + (ui32EPStatus & USB_RX_ERROR_FLAGS), + (void *)0); + } + return(false); + } + + return(true); +} + +//***************************************************************************** +// +// Receives notifications related to data sent to the host. +// +// \param psBulkDevice is the device instance whose endpoint is to be +// processed. +// \param ui32Status 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 bool +ProcessDataToHost(tUSBDBulkDevice *psBulkDevice, uint32_t ui32Status) +{ + tBulkInstance *psInst; + uint32_t ui32EPStatus, ui32Size; + + // + // Get a pointer to the bulk device instance data pointer + // + psInst = &psBulkDevice->sPrivateData; + + // + // Get the endpoint status to see why we were called. + // + ui32EPStatus = MAP_USBEndpointStatus(psInst->ui32USBBase, + psInst->ui8INEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(psInst->ui32USBBase, psInst->ui8INEndpoint, + ui32EPStatus); + + // + // Our last transmission completed. Clear our state back to idle and + // see if we need to send any more data. + // + psInst->iBulkTxState = eBulkStateIdle; + + // + // Notify the client that the last transmission completed. + // + ui32Size = psInst->ui16LastTxSize; + psInst->ui16LastTxSize = 0; + psBulkDevice->pfnTxCallback(psBulkDevice->pvTxCBData, + USB_EVENT_TX_COMPLETE, ui32Size, (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 ui32Status. +// +//***************************************************************************** +static void +HandleEndpoints(void *pvBulkDevice, uint32_t ui32Status) +{ + tUSBDBulkDevice *psBulkDevice; + tBulkInstance *psInst; + + ASSERT(pvBulkDevice != 0); + + // + // The bulk device structure pointer. + // + psBulkDevice = (tUSBDBulkDevice *)pvBulkDevice; + + // + // Get a pointer to the bulk device instance data pointer + // + psInst = &psBulkDevice->sPrivateData; + + // + // Handler for the bulk OUT data endpoint. + // + if(ui32Status & (0x10000 << USBEPToIndex(psInst->ui8OUTEndpoint))) + { + // + // Data is being sent to us from the host. + // + ProcessDataFromHost(psBulkDevice, ui32Status); + } + + // + // Handler for the bulk IN data endpoint. + // + if(ui32Status & (1 << USBEPToIndex(psInst->ui8INEndpoint))) + { + ProcessDataToHost(psBulkDevice, ui32Status); + } +} + +//***************************************************************************** +// +// Called by the USB stack whenever a configuration change occurs. +// +//***************************************************************************** +static void +HandleConfigChange(void *pvBulkDevice, uint32_t ui32Info) +{ + tBulkInstance *psInst; + tUSBDBulkDevice *psBulkDevice; + + ASSERT(pvBulkDevice != 0); + + // + // The bulk device structure pointer. + // + psBulkDevice = (tUSBDBulkDevice *)pvBulkDevice; + + // + // Get a pointer to the bulk device instance data pointer + // + psInst = &psBulkDevice->sPrivateData; + + // + // Set all our endpoints to idle state. + // + psInst->iBulkRxState = eBulkStateIdle; + psInst->iBulkTxState = eBulkStateIdle; + + // + // If we have a control callback, let the client know we are open for + // business. + // + if(psBulkDevice->pfnRxCallback) + { + // + // Pass the connected event to the client. + // + psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData, + USB_EVENT_CONNECTED, 0, (void *)0); + } + + // + // Remember that we are connected. + // + psInst->bConnected = true; +} + +//***************************************************************************** +// +// Device instance specific handler. +// +//***************************************************************************** +static void +HandleDevice(void *pvBulkDevice, uint32_t ui32Request, void *pvRequestData) +{ + tBulkInstance *psInst; + uint8_t *pui8Data; + tUSBDBulkDevice *psBulkDevice; + + // + // The bulk device structure pointer. + // + psBulkDevice = (tUSBDBulkDevice *)pvBulkDevice; + + // + // Get a pointer to the bulk device instance data pointer + // + psInst = &psBulkDevice->sPrivateData; + + // + // Create the 8-bit array used by the events supported by the USB Bulk + // class. + // + pui8Data = (uint8_t *)pvRequestData; + + switch(ui32Request) + { + // + // This was an interface change event. + // + case USB_EVENT_COMP_IFACE_CHANGE: + { + psInst->ui8Interface = pui8Data[1]; + break; + } + + // + // This was an endpoint change event. + // + case USB_EVENT_COMP_EP_CHANGE: + { + // + // Determine if this is an IN or OUT endpoint that has changed. + // + if(pui8Data[0] & USB_EP_DESC_IN) + { + psInst->ui8INEndpoint = IndexToUSBEP((pui8Data[1] & 0x7f)); + } + else + { + // + // Extract the new endpoint number. + // + psInst->ui8OUTEndpoint = IndexToUSBEP(pui8Data[1] & 0x7f); + } + break; + } + case USB_EVENT_LPM_RESUME: + { + if(psBulkDevice->pfnRxCallback) + { + // + // Pass the LPM resume event to the client. + // + psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData, + USB_EVENT_LPM_RESUME, 0, + (void *)0); + } + break; + } + case USB_EVENT_LPM_SLEEP: + { + if(psBulkDevice->pfnRxCallback) + { + // + // Pass the LPM sleep event to the client. + // + psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData, + USB_EVENT_LPM_SLEEP, 0, (void *)0); + } + break; + } + case USB_EVENT_LPM_ERROR: + { + if(psBulkDevice->pfnRxCallback) + { + // + // Pass the LPM error event to the client. + // + psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData, + USB_EVENT_LPM_ERROR, 0, (void *)0); + } + break; + } + default: + { + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device is +// disconnected from the host. +// +//***************************************************************************** +static void +HandleDisconnect(void *pvBulkDevice) +{ + tUSBDBulkDevice *psBulkDevice; + tBulkInstance *psInst; + + ASSERT(pvBulkDevice != 0); + + // + // The bulk device structure pointer. + // + psBulkDevice = (tUSBDBulkDevice *)pvBulkDevice; + + // + // Get a pointer to the bulk device instance data pointer + // + psInst = &psBulkDevice->sPrivateData; + + // + // 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 *pvBulkDevice) +{ + const tUSBDBulkDevice *psBulkDevice; + + ASSERT(pvBulkDevice != 0); + + // + // The bulk device structure pointer. + // + psBulkDevice = (const tUSBDBulkDevice *)pvBulkDevice; + + // + // 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 *pvBulkDevice) +{ + const tUSBDBulkDevice *psBulkDevice; + + ASSERT(pvBulkDevice != 0); + + // + // The bulk device structure pointer. + // + psBulkDevice = (const tUSBDBulkDevice *)pvBulkDevice; + + // + // 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 ui32Index is the index of the USB controller for which this tick +// is being generated. +// \param ui32TimemS is the elapsed time in milliseconds since the last call +// to this function. +// +// \return None. +// +//***************************************************************************** +static void +BulkTickHandler(void *pvBulkDevice, uint32_t ui32TimemS) +{ + tBulkInstance *psInst; + uint32_t ui32Size; + tUSBDBulkDevice *psBulkDevice; + + ASSERT(pvBulkDevice != 0); + + // + // The bulk device structure pointer. + // + psBulkDevice = (tUSBDBulkDevice *)pvBulkDevice; + + // + // Get a pointer to the bulk device instance data pointer + // + psInst = &psBulkDevice->sPrivateData; + + // + // Do we have a deferred receive waiting + // + if(psInst->ui16DeferredOpFlags & (1 << BULK_DO_PACKET_RX)) + { + // + // Yes - how big is the waiting packet? + // + ui32Size = MAP_USBEndpointDataAvail(psInst->ui32USBBase, + psInst->ui8OUTEndpoint); + + // + // Tell the client that there is a packet waiting for it. + // + psBulkDevice->pfnRxCallback(psBulkDevice->pvRxCBData, + USB_EVENT_RX_AVAILABLE, ui32Size, + (void *)0); + } + + return; +} + +//***************************************************************************** +// +//! Initializes bulk device operation for a given USB controller. +//! +//! \param ui32Index is the index of the USB controller which is to be +//! initialized for bulk device operation. +//! \param psBulkDevice 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 psBulkDevice +//! 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 +//! \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 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 void pointer that should be used with +//! the remaining USB bulk class APSs. +// +//***************************************************************************** +void * +USBDBulkInit(uint32_t ui32Index, tUSBDBulkDevice *psBulkDevice) +{ + void *pvBulkDevice; + tDeviceDescriptor *psDevDesc; + tConfigDescriptor *psConfigDesc; + + // + // Check parameter validity. + // + ASSERT(ui32Index == 0); + ASSERT(psBulkDevice); + + pvBulkDevice = USBDBulkCompositeInit(ui32Index, psBulkDevice, 0); + + if(pvBulkDevice) + { + // + // Fix up the device descriptor with the client-supplied values. + // + psDevDesc = (tDeviceDescriptor *)g_pui8BulkDeviceDescriptor; + psDevDesc->idVendor = psBulkDevice->ui16VID; + psDevDesc->idProduct = psBulkDevice->ui16PID; + + // + // Fix up the configuration descriptor with client-supplied values. + // + psConfigDesc = (tConfigDescriptor *)g_pui8BulkDescriptor; + psConfigDesc->bmAttributes = psBulkDevice->ui8PwrAttributes; + psConfigDesc->bMaxPower = (uint8_t)(psBulkDevice->ui16MaxPowermA / 2); + + // + // All is well so now pass the descriptors to the lower layer and put + // the bulk device on the bus. + // + USBDCDInit(ui32Index, &psBulkDevice->sPrivateData.sDevInfo, + (void *)psBulkDevice); + } + + // + // Return the pointer to the instance indicating that everything went well. + // + return(pvBulkDevice); +} + +//***************************************************************************** +// +//! Initializes bulk device operation for a given USB controller. +//! +//! \param ui32Index is the index of the USB controller which is to be +//! initialized for bulk device operation. +//! \param psBulkDevice points to a structure containing parameters customizing +//! the operation of the bulk device. +//! \param psCompEntry is the composite device entry to initialize when +//! creating a composite 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. +//! When this bulk device is part of a composite device, then the +//! \e psCompEntry should point to the composite device entry to initialize. +//! This is part of the array that is passed to the USBDCompositeInit() +//! function. +//! +//! \return Returns zero on failure or a non-zero value that should be +//! used with the remaining USB Bulk APIs. +// +//***************************************************************************** +void * +USBDBulkCompositeInit(uint32_t ui32Index, tUSBDBulkDevice *psBulkDevice, + tCompositeEntry *psCompEntry) +{ + tBulkInstance *psInst; + + // + // Check parameter validity. + // + ASSERT(ui32Index == 0); + ASSERT(psBulkDevice); + ASSERT(psBulkDevice->ppui8StringDescriptors); + ASSERT(psBulkDevice->pfnRxCallback); + ASSERT(psBulkDevice->pfnTxCallback); + + // + // Initialize the workspace in the passed instance structure. + // + psInst = &psBulkDevice->sPrivateData; + + // + // Initialize the composite entry that is used by the composite device + // class. + // + if(psCompEntry != 0) + { + psCompEntry->psDevInfo = &psInst->sDevInfo; + psCompEntry->pvInstance = (void *)psBulkDevice; + } + + // + // Initialize the device information structure. + // + psInst->sDevInfo.psCallbacks = &g_sBulkHandlers; + psInst->sDevInfo.pui8DeviceDescriptor = g_pui8BulkDeviceDescriptor; + psInst->sDevInfo.ppsConfigDescriptors = g_ppBulkConfigDescriptors; + psInst->sDevInfo.ppui8StringDescriptors = 0; + psInst->sDevInfo.ui32NumStringDescriptors = 0; + + // + // Set the basic state information for the class. + // + psInst->ui32USBBase = USB0_BASE; + psInst->iBulkRxState = eBulkStateUnconfigured; + psInst->iBulkTxState = eBulkStateUnconfigured; + psInst->ui16DeferredOpFlags = 0; + psInst->bConnected = false; + + // + // Initialize the device info structure for the Bulk device. + // + USBDCDDeviceInfoInit(0, &psInst->sDevInfo); + + // + // Set the default endpoint and interface assignments. + // + psInst->ui8INEndpoint = DATA_IN_ENDPOINT; + psInst->ui8OUTEndpoint = DATA_OUT_ENDPOINT; + psInst->ui8Interface = 0; + + // + // Plug in the client's string stable to the device information + // structure. + // + psInst->sDevInfo.ppui8StringDescriptors = + psBulkDevice->ppui8StringDescriptors; + psInst->sDevInfo.ui32NumStringDescriptors = + psBulkDevice->ui32NumStringDescriptors; + + // + // 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 *)psBulkDevice); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psBulkDevice); +} + +//***************************************************************************** +// +//! Shut down the bulk device. +//! +//! \param pvBulkDevice 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 pvBulkDevice instance should not me used in any +//! other calls. +//! +//! \return None. +// +//***************************************************************************** +void +USBDBulkTerm(void *pvBulkDevice) +{ + tBulkInstance *psInst; + + ASSERT(pvBulkDevice); + + // + // Get a pointer to the bulk device instance data pointer + // + psInst = &((tUSBDBulkDevice *)pvBulkDevice)->sPrivateData; + + // + // Terminate the requested instance. + // + USBDCDTerm(USBBaseToIndex(psInst->ui32USBBase)); + + psInst->ui32USBBase = 0; + + return; +} + +//***************************************************************************** +// +//! Sets the client-specific pointer parameter for the receive channel +//! callback. +//! +//! \param pvBulkDevice 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 pvBulkDevice structure passed to USBDBulkInit() resides +//! in RAM. If this structure is in flash, callback pointer changes are not +//! possible. +//! +//! \return Returns the previous callback pointer that was being used for +//! this instance's receive callback. +// +//***************************************************************************** +void * +USBDBulkSetRxCBData(void *pvBulkDevice, void *pvCBData) +{ + void *pvOldValue; + + ASSERT(pvBulkDevice); + + // + // Set the callback data for the receive channel after remembering the + // previous value. + // + pvOldValue = ((tUSBDBulkDevice *)pvBulkDevice)->pvRxCBData; + ((tUSBDBulkDevice *)pvBulkDevice)->pvRxCBData = pvCBData; + + // + // Return the previous callback pointer. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Sets the client-specific pointer parameter for the transmit callback. +//! +//! \param pvBulkDevice 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 pvBulkDevice structure passed to USBDBulkInit() resides +//! in RAM. If this structure is in flash, callback pointer changes are not +//! possible. +//! +//! \return Returns the previous callback pointer that was being used for +//! this instance's transmit callback. +// +//***************************************************************************** +void * +USBDBulkSetTxCBData(void *pvBulkDevice, void *pvCBData) +{ + void *pvOldValue; + + ASSERT(pvBulkDevice); + + // + // Set the callback pointer for the transmit channel after remembering the + // previous value. + // + pvOldValue = ((tUSBDBulkDevice *)pvBulkDevice)->pvTxCBData; + ((tUSBDBulkDevice *)pvBulkDevice)->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 pvBulkDevice is the pointer to the device instance structure as +//! returned by USBDBulkInit(). +//! \param pi8Data points to the first byte of data which is to be transmitted. +//! \param ui32Length 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 +//! \b 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 ui32Length 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. +// +//***************************************************************************** +uint32_t +USBDBulkPacketWrite(void *pvBulkDevice, uint8_t *pi8Data, uint32_t ui32Length, + bool bLast) +{ + tBulkInstance *psInst; + int32_t i32Retcode; + + ASSERT(pvBulkDevice); + + // + // Get a pointer to the bulk device instance data pointer + // + psInst = &((tUSBDBulkDevice *)pvBulkDevice)->sPrivateData; + + // + // Can we send the data provided? + // + if((ui32Length > DATA_IN_EP_MAX_SIZE) || + (psInst->iBulkTxState != eBulkStateIdle)) + { + // + // 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. + // + i32Retcode = MAP_USBEndpointDataPut(psInst->ui32USBBase, + psInst->ui8INEndpoint, + pi8Data, ui32Length); + + // + // Did we copy the data successfully? + // + if(i32Retcode != -1) + { + // + // Remember how many bytes we sent. + // + psInst->ui16LastTxSize += (uint16_t)ui32Length; + + // + // 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->iBulkTxState = eBulkStateWaitData; + i32Retcode = MAP_USBEndpointDataSend(psInst->ui32USBBase, + psInst->ui8INEndpoint, + USB_TRANS_IN); + } + } + + // + // Did an error occur while trying to send the data? + // + if(i32Retcode != -1) + { + // + // No - tell the caller we sent all the bytes provided. + // + return(ui32Length); + } + else + { + // + // Yes - tell the caller we could not send the data. + // + return(0); + } +} + +//***************************************************************************** +// +//! Reads a packet of data received from the USB host via the bulk data +//! interface. +//! +//! \param pvBulkDevice is the pointer to the device instance structure as +//! returned by USBDBulkInit(). +//! \param pi8Data points to a buffer into which the received data will be +//! written. +//! \param ui32Length is the size of the buffer pointed to by pi8Data. +//! \param bLast indicates whether the client will make a further call to +//! read additional data from the packet. +//! +//! This function reads up to \e ui32Length 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. +// +//***************************************************************************** +uint32_t +USBDBulkPacketRead(void *pvBulkDevice, uint8_t *pi8Data, uint32_t ui32Length, + bool bLast) +{ + uint32_t ui32EPStatus, ui32Count, ui32Pkt; + tBulkInstance *psInst; + int32_t i32Retcode; + + ASSERT(pvBulkDevice); + + // + // Get our instance data pointer + // + psInst = &((tUSBDBulkDevice *)pvBulkDevice)->sPrivateData; + + // + // Does the relevant endpoint FIFO have a packet waiting for us? + // + ui32EPStatus = MAP_USBEndpointStatus(psInst->ui32USBBase, + psInst->ui8OUTEndpoint); + + if(ui32EPStatus & USB_DEV_RX_PKT_RDY) + { + // + // How many bytes are available for us to receive? + // + ui32Pkt = MAP_USBEndpointDataAvail(psInst->ui32USBBase, + psInst->ui8OUTEndpoint); + + // + // Get as much data as we can. + // + ui32Count = ui32Length; + i32Retcode = MAP_USBEndpointDataGet(psInst->ui32USBBase, + psInst->ui8OUTEndpoint, + pi8Data, &ui32Count); + + // + // Did we read the last of the packet data? + // + if(ui32Count == ui32Pkt) + { + // + // Clear the endpoint status so that we know no packet is + // waiting. + // + MAP_USBDevEndpointStatusClear(psInst->ui32USBBase, + psInst->ui8OUTEndpoint, + ui32EPStatus); + + // + // Acknowledge the data, thus freeing the host to send the + // next packet. + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, + psInst->ui8OUTEndpoint, true); + + // + // Clear the flag we set to indicate that a packet read is + // pending. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, BULK_DO_PACKET_RX, + false); + } + + // + // If all went well, tell the caller how many bytes they got. + // + if(i32Retcode != -1) + { + return(ui32Count); + } + } + + // + // 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 pvBulkDevice 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. +// +//***************************************************************************** +uint32_t +USBDBulkTxPacketAvailable(void *pvBulkDevice) +{ + tBulkInstance *psInst; + + ASSERT(pvBulkDevice); + + // + // Get our instance data pointer. + // + psInst = &((tUSBDBulkDevice *)pvBulkDevice)->sPrivateData; + + // + // Do we have a packet transmission currently ongoing? + // + if(psInst->iBulkTxState != eBulkStateIdle) + { + // + // 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 pvBulkDevice 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. +// +//***************************************************************************** +uint32_t +USBDBulkRxPacketAvailable(void *pvBulkDevice) +{ + uint32_t ui32EPStatus, ui32Size; + tBulkInstance *psInst; + + ASSERT(pvBulkDevice); + + // + // Get a pointer to the bulk device instance data pointer + // + psInst = &((tUSBDBulkDevice *)pvBulkDevice)->sPrivateData; + + // + // Does the relevant endpoint FIFO have a packet waiting for us? + // + ui32EPStatus = MAP_USBEndpointStatus(psInst->ui32USBBase, + psInst->ui8OUTEndpoint); + + if(ui32EPStatus & USB_DEV_RX_PKT_RDY) + { + // + // Yes - a packet is waiting. How big is it? + // + ui32Size = MAP_USBEndpointDataAvail(psInst->ui32USBBase, + psInst->ui8OUTEndpoint); + + return(ui32Size); + } + else + { + // + // There is no packet waiting to be received. + // + return(0); + } +} +#ifndef DEPRECATED + +//***************************************************************************** +// +//! Reports the device power status (bus- or self-powered) to the USB library. +//! +//! \param pvBulkDevice is the pointer to the bulk device instance structure. +//! \param ui8Power 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 *pvBulkDevice, uint8_t ui8Power) +{ + ASSERT(pvBulkDevice); + + // + // Pass the request through to the lower layer. + // + USBDCDPowerStatusSet(0, ui8Power); +} +#endif + +//***************************************************************************** +// +//! Requests a remote wake up to resume communication when in suspended state. +//! +//! \param pvBulkDevice 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. +// +//***************************************************************************** +bool +USBDBulkRemoteWakeupRequest(void *pvBulkDevice) +{ + ASSERT(pvBulkDevice); + + // + // 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..0f6887d --- /dev/null +++ b/usblib/device/usbdbulk.h @@ -0,0 +1,300 @@ +//***************************************************************************** +// +// usbdcdc.h - USBLib support for a generic bulk device. +// +// Copyright (c) 2008-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#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 +{ + // + // Not configured. + // + eBulkStateUnconfigured, + + // + // No outstanding transaction remains to be completed. + // + eBulkStateIdle, + + // + // Waiting on completion of a send or receive transaction. + // + eBulkStateWaitData, + + // + // Waiting for client to process data. + // + eBulkStateWaitClient +} +tBulkState; + +//***************************************************************************** +// +// PRIVATE +// +// This structure defines the private instance data and state variables for the +// Bulk only example device. The memory for this structure is inlcluded in +// the sPrivateData field in the tUSBDBulkDevice structure passed on +// USBDBulkInit(). +// +//***************************************************************************** +typedef struct +{ + // + // Base address for the USB controller. + // + uint32_t ui32USBBase; + + // + // The device info to interact with the lower level DCD code. + // + tDeviceInfo sDevInfo; + + // + // The state of the bulk receive channel. + // + volatile tBulkState iBulkRxState; + + // + // The state of the bulk transmit channel. + // + volatile tBulkState iBulkTxState; + + // + // State of any pending operations that could not be handled immediately + // upon receipt. + // + volatile uint16_t ui16DeferredOpFlags; + + // + // Size of the last transmit. + // + uint16_t ui16LastTxSize; + + // + // The connection status of the device. + // + volatile bool bConnected; + + // + // The IN endpoint number, this is modified in composite devices. + // + uint8_t ui8INEndpoint; + + // + // The OUT endpoint number, this is modified in composite devices. + // + uint8_t ui8OUTEndpoint; + + // + // The bulk class interface number, this is modified in composite devices. + // + uint8_t ui8Interface; +} +tBulkInstance; + +//***************************************************************************** +// +// This is the size of the g_pui8BulkInterface array in bytes. +// +//***************************************************************************** +#define BULKINTERFACE_SIZE (23) + +//***************************************************************************** +// +//! 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. +// +//***************************************************************************** +#define COMPOSITE_DBULK_SIZE (BULKINTERFACE_SIZE) + +//***************************************************************************** +// +//! 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. + // + const uint16_t ui16VID; + + // + //! The product ID that this device is to present in the device descriptor. + // + const uint16_t ui16PID; + + // + //! The maximum power consumption of the device, expressed in milliamps. + // + const uint16_t ui16MaxPowermA; + + // + //! 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. + // + const uint8_t ui8PwrAttributes; + + // + //! A pointer to the callback function which will be called to notify + //! the application of events related to the device's data receive channel. + // + const 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. + // + const 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 uint8_t * const *ppui8StringDescriptors; + + // + //! The number of descriptors provided in the ppStringDescriptors array. + //! This must be 1 + (5 * number of supported languages). + // + const uint32_t ui32NumStringDescriptors; + + // + //! The private instance data for this device. This memory must + //! not be modified by any code outside the bulk class driver. + // + tBulkInstance sPrivateData; +} +tUSBDBulkDevice; + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void *USBDBulkInit(uint32_t ui32Index, tUSBDBulkDevice *psBulkDevice); +extern void *USBDBulkCompositeInit(uint32_t ui32Index, + tUSBDBulkDevice *psBulkDevice, + tCompositeEntry *psCompEntry); +extern void USBDBulkTerm(void *pvBulkInstance); +extern void *USBDBulkSetRxCBData(void *pvBulkInstance, void *pvCBData); +extern void *USBDBulkSetTxCBData(void *pvBulkInstance, void *pvCBData); +extern uint32_t USBDBulkPacketWrite(void *pvBulkInstance, uint8_t *pi8Data, + uint32_t ui32Length, bool bLast); +extern uint32_t USBDBulkPacketRead(void *pvBulkInstance, uint8_t *pi8Data, + uint32_t ui32Length, bool bLast); +extern uint32_t USBDBulkTxPacketAvailable(void *pvBulkInstance); +extern uint32_t USBDBulkRxPacketAvailable(void *pvBulkInstance); +extern bool USBDBulkRemoteWakeupRequest(void *pvBulkInstance); + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// The following APIs are deprecated. +// +//***************************************************************************** +#ifndef DEPRECATED +extern void USBDBulkPowerStatusSet(void *pvBulkInstance, uint8_t ui8Power); +#endif + +//***************************************************************************** +// +// 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..a4dc73a --- /dev/null +++ b/usblib/device/usbdcdc.c @@ -0,0 +1,3043 @@ +//***************************************************************************** +// +// usbdcdc.c - USB CDC ACM (serial) device class driver. +// +// Copyright (c) 2008-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/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 ui16DeferredOpFlags 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)) + +//***************************************************************************** +// +// 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 USBFIFOSizeToBytes(DATA_IN_EP_FIFO_SIZE) +#define DATA_OUT_EP_MAX_SIZE USBFIFOSizeToBytes(DATA_IN_EP_FIFO_SIZE) +#define CTL_IN_EP_MAX_SIZE USBFIFOSizeToBytes(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. +// +//***************************************************************************** +uint8_t g_pui8CDCSerDeviceDescriptor[] = +{ + 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. +// +//***************************************************************************** +uint8_t g_pui8CDCSerDescriptor[] = +{ + // + // 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_pui8CDCSerDescriptor), + g_pui8CDCSerDescriptor +}; + +//***************************************************************************** +// +// This is the Interface Association Descriptor for the serial device used in +// composite devices. +// +//***************************************************************************** +uint8_t g_pui8IADSerDescriptor[SERDESCRIPTOR_SIZE] = +{ + + 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_pui8IADSerDescriptor), + g_pui8IADSerDescriptor +}; + +//***************************************************************************** +// +// This is the control interface for the serial device. +// +//***************************************************************************** +const uint8_t g_pui8CDCSerCommInterface[SERCOMMINTERFACE_SIZE] = +{ + // + // 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 | USBEPToIndex(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_pui8CDCSerCommInterface), + g_pui8CDCSerCommInterface +}; + +//***************************************************************************** +// +// This is the Data interface for the serial device. +// +//***************************************************************************** +const uint8_t g_pui8CDCSerDataInterface[SERDATAINTERFACE_SIZE] = +{ + // + // 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 | USBEPToIndex(DATA_IN_ENDPOINT), + USB_EP_ATTR_BULK, // Endpoint is a bulk endpoint. + USBShort(DATA_IN_EP_MAX_SIZE), // The maximum packet size. + 0, // The polling interval for this endpoint. + + // + // Endpoint Descriptor + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_OUT | USBEPToIndex(DATA_OUT_ENDPOINT), + USB_EP_ATTR_BULK, // Endpoint is a bulk endpoint. + USBShort(DATA_OUT_EP_MAX_SIZE), // The maximum packet size. + 0, // The polling interval for this endpoint. +}; + +const tConfigSection g_sCDCSerDataInterfaceSection = +{ + sizeof(g_pui8CDCSerDataInterface), + g_pui8CDCSerDataInterface +}; + +//***************************************************************************** +// +// 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(g_psCDCSerSections[0])) + +//***************************************************************************** +// +// 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(g_psCDCCompSerSections[0])) + +//***************************************************************************** +// +// 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_ppCDCSerConfigDescriptors[] = +{ + &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 *pvCDCDevice, tUSBRequest *pUSBRequest); +static void HandleConfigChange(void *pvCDCDevice, uint32_t ui32Info); +static void HandleEP0Data(void *pvCDCDevice, uint32_t ui32DataSize); +static void HandleDisconnect(void *pvCDCDevice); +static void HandleEndpoints(void *pvCDCDevice, uint32_t ui32Status); +static void HandleSuspend(void *pvCDCDevice); +static void HandleResume(void *pvCDCDevice); +static void HandleDevice(void *pvCDCDevice, uint32_t ui32Request, + void *pvRequestData); + +//***************************************************************************** +// +// The device information structure for the USB serial device. +// +//***************************************************************************** +const tCustomHandlers g_sCDCHandlers = +{ + // + // 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 +}; + +//***************************************************************************** +// +// Set or clear deferred operation flags in an "atomic" manner. +// +// \param pui16DeferredOp points to the flags variable which is to be modified. +// \param ui16Bit 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 uint16_t *pui16DeferredOp, uint16_t ui16Bit, + bool bSet) +{ + // + // Set the flag bit to 1 or 0 using a bitband access. + // + HWREGBITH(pui16DeferredOp, ui16Bit) = bSet ? 1 : 0; +} + +//***************************************************************************** +// +// Determines whether or not a client has consumed all received data previously +// passed to it. +// +//! \param psCDCDevice 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 bool +DeviceConsumedAllData(const tUSBDCDCDevice *psCDCDevice) +{ + uint32_t ui32Remaining; + + // + // Send the device an event requesting that it tell us how many bytes + // of data it still has to process. + // + ui32Remaining = psCDCDevice->pfnRxCallback(psCDCDevice->pvRxCBData, + USB_EVENT_DATA_REMAINING, 0, (void *)0); + + // + // If any data remains to be processed, return false, else return true. + // + return(ui32Remaining ? false : true); +} + +//***************************************************************************** +// +// Notifies the client that it should set or clear a break condition. +// +// \param psCDCDevice 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(tUSBDCDCDevice *psCDCDevice, bool bSend) +{ + tCDCSerInstance *psInst; + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &psCDCDevice->sPrivateData; + + // + // 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->ui16DeferredOpFlags, CDC_DO_SEND_BREAK, false); + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, CDC_DO_CLEAR_BREAK, bSend); + + // + // Tell the client to start or stop sending the break. + // + psCDCDevice->pfnControlCallback(psCDCDevice->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 psCDCDevice 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(tUSBDCDCDevice *psCDCDevice) +{ + tCDCSerInstance *psInst; + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &psCDCDevice->sPrivateData; + + // + // Clear the flag we use to tell ourselves that the line coding change has + // yet to be notified to the client. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, CDC_DO_LINE_CODING_CHANGE, + false); + + // + // Tell the client to update their serial line coding parameters. + // + psCDCDevice->pfnControlCallback(psCDCDevice->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 psCDCDevice 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(tUSBDCDCDevice *psCDCDevice) +{ + tCDCSerInstance *psInst; + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &psCDCDevice->sPrivateData; + + // + // Clear the flag we use to tell ourselves that the line coding change has + // yet to be notified to the client. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, CDC_DO_LINE_STATE_CHANGE, + false); + + // + // Tell the client to update their serial line coding parameters. + // + psCDCDevice->pfnControlCallback(psCDCDevice->pvControlCBData, + USBD_CDC_EVENT_SET_CONTROL_LINE_STATE, + psInst->ui16ControlLineState, + (void *)0); +} + +//***************************************************************************** +// +// Notifies the client of a break request if no data remains to be processed. +// +// \param psCDCDevice 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 bool +CheckAndSendBreak(tUSBDCDCDevice *psCDCDevice, uint16_t ui16Duration) +{ + bool bCanSend; + + // + // Has the client consumed all data received from the host yet? + // + bCanSend = DeviceConsumedAllData(psCDCDevice); + + // + // Can we send the break request? + // + if(bCanSend) + { + // + // Pass the break request on to the client since no data remains to be + // consumed. + // + SendBreak(psCDCDevice, (ui16Duration ? 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 psCDCDevice 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 bool +CheckAndSendLineCodingChange(tUSBDCDCDevice *psCDCDevice) +{ + bool bCanSend; + + // + // Has the client consumed all data received from the host yet? + // + bCanSend = DeviceConsumedAllData(psCDCDevice); + + // + // Can we send the break request? + // + if(bCanSend) + { + // + // Pass the request on to the client since no data remains to be + // consumed. + // + SendLineCodingChange(psCDCDevice); + } + + // + // 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 psCDCDevice 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 bool +CheckAndSendLineStateChange(tUSBDCDCDevice *psCDCDevice) +{ + bool bCanSend; + + // + // Has the client consumed all data received from the host yet? + // + bCanSend = DeviceConsumedAllData(psCDCDevice); + + // + // Can we send the break request? + // + if(bCanSend) + { + // + // Pass the request on to the client since no data remains to be + // consumed. + // + SendLineStateChange(psCDCDevice); + } + + // + // 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 bool +SendSerialState(tUSBDCDCDevice *psCDCDevice) +{ + tUSBRequest sRequest; + uint16_t ui16SerialState; + tCDCSerInstance *psInst; + int32_t i32Retcode; + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &psCDCDevice->sPrivateData; + + // + // Remember that we are in the middle of sending a notification. + // + psInst->iCDCInterruptState = eCDCStateWaitData; + + // + // Clear the flag we use to indicate that a send is required. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, CDC_DO_SERIAL_STATE_CHANGE, + false); + // + // Take a snapshot of the serial state. + // + ui16SerialState = psInst->ui16SerialState; + + // + // 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. + // + i32Retcode = MAP_USBEndpointDataPut(psInst->ui32USBBase, + psInst->ui8ControlEndpoint, + (uint8_t *)&sRequest, + sizeof(tUSBRequest)); + i32Retcode = MAP_USBEndpointDataPut(psInst->ui32USBBase, + psInst->ui8ControlEndpoint, + (uint8_t *)&ui16SerialState, + USB_CDC_NOTIFY_SERIAL_STATE_SIZE); + + // + // Did we correctly write the data to the endpoint FIFO? + // + if(i32Retcode != -1) + { + // + // We put the data into the FIFO so now schedule it to be + // sent. + // + i32Retcode = MAP_USBEndpointDataSend(psInst->ui32USBBase, + psInst->ui8ControlEndpoint, + USB_TRANS_IN); + } + + // + // If an error occurred, mark the endpoint as idle (to prevent possible + // lockup) and return an error. + // + if(i32Retcode == -1) + { + psInst->iCDCInterruptState = eCDCStateIdle; + return(false); + } + else + { + // + // Everything went fine. Clear the error bits that we just notified + // and return true. + // + psInst->ui16SerialState &= ~(ui16SerialState & USB_CDC_SERIAL_ERRORS); + return(true); + } +} + +//***************************************************************************** +// +// Receives notifications related to data received from the host. +// +// \param psCDCDevice is the device instance whose endpoint is to be processed. +// \param ui32Status 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. +// +//***************************************************************************** +bool +ProcessDataFromHost(tUSBDCDCDevice *psCDCDevice, uint32_t ui32Status) +{ + uint32_t ui32EPStatus, ui32Size; + tCDCSerInstance *psInst; + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &psCDCDevice->sPrivateData; + + // + // Get the endpoint status to see why we were called. + // + ui32EPStatus = MAP_USBEndpointStatus(psInst->ui32USBBase, + psInst->ui8BulkOUTEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(psInst->ui32USBBase, + psInst->ui8BulkOUTEndpoint, + ui32EPStatus); + + // + // Has a packet been received? + // + if(ui32EPStatus & 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->ui16DeferredOpFlags, CDC_DO_PACKET_RX, + true); + + // + // Is the receive channel currently blocked? + // + if(!psInst->bControlBlocked && !psInst->bRxBlocked) + { + // + // How big is the packet we have just been received? + // + ui32Size = MAP_USBEndpointDataAvail(psInst->ui32USBBase, + psInst->ui8BulkOUTEndpoint); + + // + // 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. + // + psCDCDevice->pfnRxCallback(psCDCDevice->pvRxCBData, + USB_EVENT_RX_AVAILABLE, ui32Size, + (void *)0); + } + } + else + { + // + // No packet was received. Some error must have been reported. Check + // and pass this on to the client if necessary. + // + if(ui32EPStatus & USB_RX_ERROR_FLAGS) + { + // + // This is an error we report to the client so... + // + psCDCDevice->pfnRxCallback(psCDCDevice->pvRxCBData, USB_EVENT_ERROR, + (ui32EPStatus & USB_RX_ERROR_FLAGS), + (void *)0); + } + + return(false); + } + + return(true); +} + +//***************************************************************************** +// +// Receives notifications related to interrupt messages sent to the host. +// +// \param psCDCDevice is the device instance whose endpoint is to be processed. +// \param ui32Status 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. +// +//***************************************************************************** +bool +ProcessNotificationToHost(tUSBDCDCDevice *psCDCDevice, uint32_t ui32Status) +{ + uint32_t ui32EPStatus; + tCDCSerInstance *psInst; + bool bRetcode; + + // + // Assume all will go well until we have reason to believe otherwise. + // + bRetcode = true; + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &psCDCDevice->sPrivateData; + + // + // Get the endpoint status to see why we were called. + // + ui32EPStatus = MAP_USBEndpointStatus(psInst->ui32USBBase, + psInst->ui8ControlEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(psInst->ui32USBBase, + psInst->ui8ControlEndpoint, ui32EPStatus); + + // + // Did the state change while we were waiting for the previous notification + // to complete? + // + if(psInst->ui16DeferredOpFlags & (1 << CDC_DO_SERIAL_STATE_CHANGE)) + { + // + // The state changed while we were waiting so we need to schedule + // another notification immediately. + // + bRetcode = SendSerialState(psCDCDevice); + } + else + { + // + // Our last notification completed and we did not have any new + // notifications to make so the interrupt channel is now idle again. + // + psInst->iCDCInterruptState = eCDCStateIdle; + } + + // + // Tell the caller how things went. + // + return(bRetcode); +} + +//***************************************************************************** +// +// Receives notifications related to data sent to the host. +// +// \param psCDCDevice is the device instance whose endpoint is to be processed. +// \param ui32Status 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. +// +//***************************************************************************** +bool +ProcessDataToHost(tUSBDCDCDevice *psCDCDevice, uint32_t ui32Status) +{ + tCDCSerInstance *psInst; + uint32_t ui32EPStatus, ui32Size; + bool bSentFullPacket; + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &psCDCDevice->sPrivateData; + + // + // Get the endpoint status to see why we were called. + // + ui32EPStatus = MAP_USBEndpointStatus(psInst->ui32USBBase, + psInst->ui8BulkINEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(psInst->ui32USBBase, + psInst->ui8BulkINEndpoint, ui32EPStatus); + + // + // Our last transmission completed. Clear our state back to idle and + // see if we need to send any more data. + // + psInst->iCDCTxState = eCDCStateIdle; + + // + // If this notification is not 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->ui16LastTxSize) + { + // + // Have we just sent a 64 byte packet? + // + bSentFullPacket = (psInst->ui16LastTxSize == DATA_IN_EP_MAX_SIZE) ? + true : false; + + // + // Notify the client that the last transmission completed. + // + ui32Size = (uint32_t)psInst->ui16LastTxSize; + psInst->ui16LastTxSize = 0; + psCDCDevice->pfnTxCallback(psCDCDevice->pvTxCBData, USB_EVENT_TX_COMPLETE, + ui32Size, (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->ui16LastTxSize) + { + // + // We can expect another transmit complete notification after doing + // this. + // + psInst->iCDCTxState = eCDCStateWaitData; + + // + // Send the zero-length packet. + // + MAP_USBEndpointDataSend(psInst->ui32USBBase, + psInst->ui8BulkINEndpoint, + 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 ui32Status. +// +//***************************************************************************** +static void +HandleEndpoints(void *pvCDCDevice, uint32_t ui32Status) +{ + tUSBDCDCDevice *psCDCDeviceInst; + tCDCSerInstance *psInst; + + ASSERT(pvCDCDevice != 0); + + // + // Determine if the serial device is in single or composite mode because + // the meaning of ui32Index is different in both cases. + // + psCDCDeviceInst = pvCDCDevice; + psInst = &psCDCDeviceInst->sPrivateData; + + // + // Handler for the interrupt IN notification endpoint. + // + if(ui32Status & (1 << USBEPToIndex(psInst->ui8ControlEndpoint))) + { + // + // We have sent an interrupt notification to the host. + // + ProcessNotificationToHost(psCDCDeviceInst, ui32Status); + } + + // + // Handler for the bulk OUT data endpoint. + // + if(ui32Status & (0x10000 << USBEPToIndex(psInst->ui8BulkOUTEndpoint))) + { + // + // Data is being sent to us from the host. + // + ProcessDataFromHost(psCDCDeviceInst, ui32Status); + } + + // + // Handler for the bulk IN data endpoint. + // + if(ui32Status & (1 << USBEPToIndex(psInst->ui8BulkINEndpoint))) + { + ProcessDataToHost(psCDCDeviceInst, ui32Status); + } +} + +//***************************************************************************** +// +// Called by the USB stack whenever a configuration change occurs. +// +//***************************************************************************** +static void +HandleConfigChange(void *pvCDCDevice, uint32_t ui32Info) +{ + tCDCSerInstance *psInst; + tUSBDCDCDevice *psCDCDevice; + + ASSERT(pvCDCDevice != 0); + + // + // The CDC device structure pointer. + // + psCDCDevice = (tUSBDCDCDevice *)pvCDCDevice; + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &psCDCDevice->sPrivateData; + + // + // Set all our endpoints to idle state. + // + psInst->iCDCInterruptState = eCDCStateIdle; + psInst->iCDCRequestState = eCDCStateIdle; + psInst->iCDCRxState = eCDCStateIdle; + psInst->iCDCTxState = eCDCStateIdle; + + // + // 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. + // + psCDCDevice->pfnControlCallback(psCDCDevice->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 *pvCDCDevice, uint32_t ui32DataSize) +{ + tUSBDCDCDevice *psCDCDevice; + tCDCSerInstance *psInst; + bool bRetcode; + + ASSERT(pvCDCDevice != 0); + + // + // The CDC device structure pointer. + // + psCDCDevice = (tUSBDCDCDevice *)pvCDCDevice; + + // + // If we were not passed any data, just return. + // + if(ui32DataSize == 0) + { + return; + } + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &psCDCDevice->sPrivateData; + + // + // Make sure we are actually expecting something. + // + if(psInst->iCDCRequestState != eCDCStateWaitData) + { + return; + } + + // + // Process the data received. This will be a request-specific data + // block associated with the last request received. + // + switch (psInst->ui8PendingRequest) + { + // + // 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(ui32DataSize != sizeof(tLineCoding)) + { + USBDCDStallEP0(0); + } + else + { + // + // Set the flag telling us that we need to send a line coding + // notification to the client. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, + CDC_DO_LINE_CODING_CHANGE, true); + + // + // See if we can send the notification immediately. + // + bRetcode = CheckAndSendLineCodingChange(psCDCDevice); + + // + // If we could not 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->iCDCRequestState = eCDCStateIdle; +} + +//***************************************************************************** +// +// Device instance specific handler. +// +//***************************************************************************** +static void +HandleDevice(void *pvCDCDevice, uint32_t ui32Request, void *pvRequestData) +{ + tCDCSerInstance *psInst; + uint8_t *pui8Data; + tUSBDCDCDevice *psCDCDevice; + + // + // The CDC device structure pointer. + // + psCDCDevice = (tUSBDCDCDevice *)pvCDCDevice; + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &psCDCDevice->sPrivateData; + + // + // Create the 8-bit array used by the events supported by the USB CDC + // serial class. + // + pui8Data = (uint8_t *)pvRequestData; + + switch(ui32Request) + { + // + // This was an interface change event. + // + case USB_EVENT_COMP_IFACE_CHANGE: + { + // + // Save the change to the appropriate interface number. + // + if(pui8Data[0] == SERIAL_INTERFACE_CONTROL) + { + psInst->ui8InterfaceControl = pui8Data[1]; + } + else if(pui8Data[0] == SERIAL_INTERFACE_DATA) + { + psInst->ui8InterfaceData = pui8Data[1]; + } + break; + } + + // + // This was an endpoint change event. + // + case USB_EVENT_COMP_EP_CHANGE: + { + // + // Determine if this is an IN or OUT endpoint that has changed. + // + if(pui8Data[0] & USB_EP_DESC_IN) + { + // + // Determine which IN endpoint to modify. + // + if((pui8Data[0] & 0x7f) == USBEPToIndex(CONTROL_ENDPOINT)) + { + psInst->ui8ControlEndpoint = + IndexToUSBEP((pui8Data[1] & 0x7f)); + } + else + { + psInst->ui8BulkINEndpoint = + IndexToUSBEP((pui8Data[1] & 0x7f)); + } + } + else + { + // + // Extract the new endpoint number. + // + psInst->ui8BulkOUTEndpoint = + IndexToUSBEP(pui8Data[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. + // + pui8Data[2] = psInst->ui8InterfaceControl; + + // + // 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. + // + pui8Data[29] = psInst->ui8InterfaceControl; + pui8Data[30] = psInst->ui8InterfaceData; + + // + // This sets the bDataInterface of the Union descriptor to the + // Data interface used by this instance. + pui8Data[35] = psInst->ui8InterfaceData; + break; + } + case USB_EVENT_LPM_RESUME: + { + if(psCDCDevice->pfnControlCallback) + { + // + // Pass the LPM resume event to the client. + // + psCDCDevice->pfnControlCallback(psCDCDevice->pvControlCBData, + USB_EVENT_LPM_RESUME, 0, + (void *)0); + } + break; + } + case USB_EVENT_LPM_SLEEP: + { + if(psCDCDevice->pfnControlCallback) + { + // + // Pass the LPM sleep event to the client. + // + psCDCDevice->pfnControlCallback(psCDCDevice->pvControlCBData, + USB_EVENT_LPM_SLEEP, 0, + (void *)0); + } + break; + } + case USB_EVENT_LPM_ERROR: + { + if(psCDCDevice->pfnControlCallback) + { + // + // Pass the LPM error event to the client. + // + psCDCDevice->pfnControlCallback(psCDCDevice->pvControlCBData, + USB_EVENT_LPM_ERROR, 0, + (void *)0); + } + 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 *pvCDCDevice, tUSBRequest *pUSBRequest) +{ + tUSBDCDCDevice *psCDCDevice; + tCDCSerInstance *psInst; + bool bRetcode; + + ASSERT(pvCDCDevice != 0); + + // + // The CDC device structure pointer. + // + psCDCDevice = (tUSBDCDCDevice *)pvCDCDevice; + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &psCDCDevice->sPrivateData; + + // + // Only handle requests meant for this interface. + // + if(pUSBRequest->wIndex != psInst->ui8InterfaceControl) + { + 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->ui8PendingRequest = USB_CDC_SET_LINE_CODING; + + // + // Set the state to indicate we are waiting for data. + // + psInst->iCDCRequestState = eCDCStateWaitData; + + // + // Now read the payload of the request. We handle the actual + // operation in the data callback once this data is received. + // + USBDCDRequestDataEP0(0, (uint8_t *)&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->ui32USBBase, 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->ui32USBBase, USB_EP_0, false); + + // + // Ask the client for the current line coding. + // + psCDCDevice->pfnControlCallback(psCDCDevice->pvControlCBData, + USBD_CDC_EVENT_GET_LINE_CODING, 0, + &sLineCoding); + + // + // Send the line coding information back to the host. + // + USBDCDSendDataEP0(0, (uint8_t *)&sLineCoding, sizeof(tLineCoding)); + + break; + } + + case USB_CDC_SET_CONTROL_LINE_STATE: + { + // + // ACK what we have already received + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, USB_EP_0, false); + + // + // Set the handshake lines as required. + // + psInst->ui16ControlLineState = pUSBRequest->wValue; + + // + // Remember that we are due to notify the client of a line + // state change. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, + CDC_DO_LINE_STATE_CHANGE, true); + + // + // See if we can notify now. + // + bRetcode = CheckAndSendLineStateChange(psCDCDevice); + + // + // If we could not 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->ui32USBBase, USB_EP_0, false); + + // + // Keep a copy of the requested break duration. + // + psInst->ui16BreakDuration = pUSBRequest->wValue; + + // + // Remember that we need to send a break request. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, + CDC_DO_SEND_BREAK, true); + + // + // Send the break request if all outstanding receive data has been + // processed. + // + bRetcode = CheckAndSendBreak(psCDCDevice, pUSBRequest->wValue); + + // + // If we could not 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 *pvCDCDevice) +{ + tUSBDCDCDevice *psCDCDevice; + tCDCSerInstance *psInst; + + ASSERT(pvCDCDevice != 0); + + // + // The CDC device structure pointer. + // + psCDCDevice = (tUSBDCDCDevice *)pvCDCDevice; + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &psCDCDevice->sPrivateData; + + // + // 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 *pvCDCDevice) +{ + const tUSBDCDCDevice *psCDCDevice; + + ASSERT(pvCDCDevice != 0); + + // + // The CDC device structure pointer. + // + psCDCDevice = (const tUSBDCDCDevice *)pvCDCDevice; + + // + // 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 *pvCDCDevice) +{ + tUSBDCDCDevice *psCDCDevice; + + ASSERT(pvCDCDevice != 0); + + // + // The CDC device structure pointer. + // + psCDCDevice = (tUSBDCDCDevice *)pvCDCDevice; + + // + // 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 ui32Index is the index of the USB controller for which this tick +// is being generated. +// \param ui32TimemS is the elapsed time in milliseconds since the last call +// to this function. +// +// \return None. +// +//***************************************************************************** +static void +CDCTickHandler(void *pvCDCDevice, uint32_t ui32TimemS) +{ + bool bCanSend; + tUSBDCDCDevice *psCDCDevice; + tCDCSerInstance *psInst; + uint32_t ui32Size; + + ASSERT(pvCDCDevice != 0); + + // + // The CDC device structure pointer. + // + psCDCDevice = (tUSBDCDCDevice *)pvCDCDevice; + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &psCDCDevice->sPrivateData; + + // + // Is there any outstanding operation that we should try to perform? + // + if(psInst->ui16DeferredOpFlags) + { + // + // 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->ui16DeferredOpFlags & (1 << CDC_DO_CLEAR_BREAK)) + { + // + // Will our break timer expire this time? + // + if(psInst->ui16BreakDuration <= ui32TimemS) + { + // + // Yes - turn off the break condition. + // + SendBreak(psCDCDevice, false); + } + else + { + // + // We have not timed out yet. Decrement the break timer. + // + psInst->ui16BreakDuration -= (uint16_t)ui32TimemS; + } + } + + // 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(psCDCDevice); + + // + // 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->ui16DeferredOpFlags & (1 << CDC_DO_SEND_BREAK)) + { + SendBreak(psCDCDevice, true); + } + + // + // Do we need to set the RTS/DTR states? + // + if(psInst->ui16DeferredOpFlags & (1 << CDC_DO_LINE_STATE_CHANGE)) + { + SendLineStateChange(psCDCDevice); + } + + // + // Do we need to change the line coding parameters? + // + if(psInst->ui16DeferredOpFlags & (1 << CDC_DO_LINE_CODING_CHANGE)) + { + SendLineCodingChange(psCDCDevice); + } + + // + // 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->ui16DeferredOpFlags & 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->ui16DeferredOpFlags & (1 << CDC_DO_PACKET_RX)) + { + // + // Yes - how big is the waiting packet? + // + ui32Size = MAP_USBEndpointDataAvail(psInst->ui32USBBase, + psInst->ui8BulkOUTEndpoint); + + // Tell the client that there is a packet waiting for it. + // + psCDCDevice->pfnRxCallback(psCDCDevice->pvRxCBData, + USB_EVENT_RX_AVAILABLE, ui32Size, + (void *)0); + } + } + } + + return; +} + +//***************************************************************************** +// +//! Initializes CDC device operation when used with a composite device. +//! +//! \param ui32Index is the index of the USB controller in use. +//! \param psCDCDevice points to a structure containing parameters customizing +//! the operation of the CDC device. +//! \param psCompEntry is the composite device entry to initialize when +//! creating a composite 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. When this CDC serial device is part of a composite device, then +//! the \e psCompEntry should point to the composite device entry to +//! initialize. This is part of the array that is passed to the +//! USBDCompositeInit() function. +//! +//! \return Returns zero on failure or a non-zero instance value that should be +//! used with the remaining USB CDC APIs. +// +//***************************************************************************** +void * +USBDCDCCompositeInit(uint32_t ui32Index, tUSBDCDCDevice *psCDCDevice, + tCompositeEntry *psCompEntry) +{ + tCDCSerInstance *psInst; + + // + // Check parameter validity. + // + ASSERT(ui32Index == 0); + ASSERT(psCDCDevice); + ASSERT(psCDCDevice->pfnControlCallback); + ASSERT(psCDCDevice->pfnRxCallback); + ASSERT(psCDCDevice->pfnTxCallback); + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &psCDCDevice->sPrivateData; + + // + // Initialize the composite entry that is used by the composite device + // class. + // + if(psCompEntry != 0) + { + psCompEntry->psDevInfo = &psInst->sDevInfo; + psCompEntry->pvInstance = (void *)psCDCDevice; + } + + // + // Initialize the device information structure. + // + psInst->sDevInfo.psCallbacks = &g_sCDCHandlers; + psInst->sDevInfo.pui8DeviceDescriptor = g_pui8CDCSerDeviceDescriptor; + + // + // The CDC serial configuration is different for composite devices and + // stand alone devices. + // + if(psCompEntry == 0) + { + psInst->sDevInfo.ppsConfigDescriptors = g_ppCDCSerConfigDescriptors; + } + else + { + psInst->sDevInfo.ppsConfigDescriptors = g_pCDCCompSerConfigDescriptors; + } + psInst->sDevInfo.ppui8StringDescriptors = 0; + psInst->sDevInfo.ui32NumStringDescriptors = 0; + + // + // Set the default endpoint and interface assignments. + // + psInst->ui8BulkINEndpoint = DATA_IN_ENDPOINT; + psInst->ui8BulkOUTEndpoint = DATA_OUT_ENDPOINT; + psInst->ui8InterfaceControl = SERIAL_INTERFACE_CONTROL; + psInst->ui8InterfaceData = 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->ui8ControlEndpoint = CONTROL_ENDPOINT; + + // + // Initialize the workspace in the passed instance structure. + // + psInst->ui32USBBase = USB0_BASE; + psInst->iCDCRxState = eCDCStateUnconfigured; + psInst->iCDCTxState = eCDCStateUnconfigured; + psInst->iCDCInterruptState = eCDCStateUnconfigured; + psInst->iCDCRequestState = eCDCStateUnconfigured; + psInst->ui8PendingRequest = 0; + psInst->ui16BreakDuration = 0; + psInst->ui16SerialState = 0; + psInst->ui16DeferredOpFlags = 0; + psInst->ui16ControlLineState = 0; + psInst->bRxBlocked = false; + psInst->bControlBlocked = false; + psInst->bConnected = false; + + // + // Initialize the device info structure for the serial device. + // + USBDCDDeviceInfoInit(0, &psInst->sDevInfo); + + // + // Plug in the client's string stable to the device information + // structure. + // + psInst->sDevInfo.ppui8StringDescriptors = + psCDCDevice->ppui8StringDescriptors; + psInst->sDevInfo.ui32NumStringDescriptors = + psCDCDevice->ui32NumStringDescriptors; + + // + // 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 ui32Index 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(uint32_t ui32Index, tUSBDCDCDevice *psCDCDevice) +{ + void *pvRet; + tCDCSerInstance *psInst; + tDeviceDescriptor *psDevDesc; + tConfigDescriptor *psConfigDesc; + + // + // Initialize the internal state for this class. + // + pvRet = USBDCDCCompositeInit(ui32Index, psCDCDevice, 0); + + if(pvRet) + { + // + // Fix up the device descriptor with the client-supplied values. + // + psDevDesc = (tDeviceDescriptor *)g_pui8CDCSerDeviceDescriptor; + psDevDesc->idVendor = psCDCDevice->ui16VID; + psDevDesc->idProduct = psCDCDevice->ui16PID; + + // + // Fix up the configuration descriptor with client-supplied values. + // + psConfigDesc = (tConfigDescriptor *)g_pui8CDCSerDescriptor; + psConfigDesc->bmAttributes = psCDCDevice->ui8PwrAttributes; + psConfigDesc->bMaxPower = (uint8_t)(psCDCDevice->ui16MaxPowermA / 2); + + // + // Create an instance pointer to the private data area. + // + psInst = &psCDCDevice->sPrivateData; + + // + // Enable the default interrupt control endpoint if this class is not + // being used in a composite device. + // + psInst->ui8ControlEndpoint = CONTROL_ENDPOINT; + + // + // Use the configuration descriptor with the interrupt control + // endpoint. + // + psInst->sDevInfo.ppsConfigDescriptors = g_ppCDCSerConfigDescriptors; + + // + // All is well so now pass the descriptors to the lower layer and put + // the CDC device on the bus. + // + USBDCDInit(ui32Index, &psInst->sDevInfo, (void *)psCDCDevice); + } + + return(pvRet); +} + +//***************************************************************************** +// +//! Shuts down the CDC device instance. +//! +//! \param pvCDCDevice 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 pvCDCDevice instance should not me used in +//! any other calls. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDCTerm(void *pvCDCDevice) +{ + tCDCSerInstance *psInst; + + ASSERT(pvCDCDevice); + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &((tUSBDCDCDevice *)pvCDCDevice)->sPrivateData; + + // + // Terminate the requested instance. + // + USBDCDTerm(USBBaseToIndex(psInst->ui32USBBase)); + + psInst->ui32USBBase = 0; + + return; +} + +//***************************************************************************** +// +//! Sets the client-specific pointer for the control callback. +//! +//! \param pvCDCDevice 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 *pvCDCDevice, void *pvCBData) +{ + tUSBDCDCDevice *psBulkDevice; + void *pvOldValue; + + ASSERT(pvCDCDevice); + + // + // The CDC device structure pointer. + // + psBulkDevice = (tUSBDCDCDevice *)pvCDCDevice; + + // + // Set the callback pointer for the control channel after remembering the + // previous value. + // + pvOldValue = psBulkDevice->pvControlCBData; + psBulkDevice->pvControlCBData = pvCBData; + + // + // Return the previous callback data value. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Sets the client-specific data parameter for the receive channel callback. +//! +//! \param pvCDCDevice 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 *pvCDCDevice, void *pvCBData) +{ + tUSBDCDCDevice *psBulkDevice; + void *pvOldValue; + + ASSERT(pvCDCDevice); + + // + // The CDC device structure pointer. + // + psBulkDevice = (tUSBDCDCDevice *)pvCDCDevice; + + // + // Set the callback data for the receive channel after remembering the + // previous value. + // + pvOldValue = psBulkDevice->pvRxCBData; + psBulkDevice->pvRxCBData = pvCBData; + + // + // Return the previous callback pointer. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Sets the client-specific data parameter for the transmit callback. +//! +//! \param pvCDCDevice 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 *pvCDCDevice, void *pvCBData) +{ + tUSBDCDCDevice *psBulkDevice; + void *pvOldValue; + + ASSERT(pvCDCDevice); + + // + // The CDC device structure pointer. + // + psBulkDevice = (tUSBDCDCDevice *)pvCDCDevice; + + // + // Set the callback data for the transmit channel after remembering the + // previous value. + // + pvOldValue = psBulkDevice->pvTxCBData; + psBulkDevice->pvTxCBData = pvCBData; + + // + // Return the previous callback pointer. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Transmits a packet of data to the USB host via the CDC data interface. +//! +//! \param pvCDCDevice is the pointer to the device instance structure as +//! returned by USBDCDCInit(). +//! \param pi8Data points to the first byte of data which is to be transmitted. +//! \param ui32Length 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 +//! \b 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 \e ui32Length 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. +// +//***************************************************************************** +uint32_t +USBDCDCPacketWrite(void *pvCDCDevice, uint8_t *pi8Data, uint32_t ui32Length, + bool bLast) +{ + tCDCSerInstance *psInst; + int32_t i32Retcode; + + ASSERT(pvCDCDevice); + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &((tUSBDCDCDevice *)pvCDCDevice)->sPrivateData; + + // + // Can we send the data provided? + // + if((ui32Length > DATA_IN_EP_MAX_SIZE) || + (psInst->iCDCTxState != eCDCStateIdle)) + { + // + // 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. + // + i32Retcode = MAP_USBEndpointDataPut(psInst->ui32USBBase, + psInst->ui8BulkINEndpoint, pi8Data, + ui32Length); + + // + // Did we copy the data successfully? + // + if(i32Retcode != -1) + { + // + // Remember how many bytes we sent. + // + psInst->ui16LastTxSize += (uint16_t)ui32Length; + + // + // 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->iCDCTxState = eCDCStateWaitData; + i32Retcode = MAP_USBEndpointDataSend(psInst->ui32USBBase, + psInst->ui8BulkINEndpoint, + USB_TRANS_IN); + } + } + + // + // Did an error occur while trying to send the data? + // + if(i32Retcode != -1) + { + // + // No - tell the caller we sent all the bytes provided. + // + return(ui32Length); + } + else + { + // + // Yes - tell the caller we could not send the data. + // + return(0); + } +} + +//***************************************************************************** +// +//! Reads a packet of data received from the USB host via the CDC data +//! interface. +//! +//! \param pvCDCDevice is the pointer to the device instance structure as +//! returned by USBDCDCInit(). +//! \param pi8Data points to a buffer into which the received data will be +//! written. +//! \param ui32Length is the size of the buffer pointed to by \e pi8Data. +//! \param bLast indicates whether the client will make a further call to +//! read additional data from the packet. +//! +//! This function reads up to ui32Length 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. +// +//***************************************************************************** +uint32_t +USBDCDCPacketRead(void *pvCDCDevice, uint8_t *pi8Data, uint32_t ui32Length, + bool bLast) +{ + uint32_t ui32EPStatus, ui32Count, ui32Pkt; + tCDCSerInstance *psInst; + int32_t i32Retcode; + + ASSERT(pvCDCDevice); + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &((tUSBDCDCDevice *)pvCDCDevice)->sPrivateData; + + // + // Does the relevant endpoint FIFO have a packet waiting for us? + // + ui32EPStatus = MAP_USBEndpointStatus(psInst->ui32USBBase, + psInst->ui8BulkOUTEndpoint); + + if(ui32EPStatus & 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->ui16DeferredOpFlags, 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? + // + ui32Pkt = MAP_USBEndpointDataAvail(psInst->ui32USBBase, + psInst->ui8BulkOUTEndpoint); + + // + // Get as much data as we can. + // + ui32Count = ui32Length; + i32Retcode = MAP_USBEndpointDataGet(psInst->ui32USBBase, + psInst->ui8BulkOUTEndpoint, + pi8Data, &ui32Count); + + // + // Did we read the last of the packet data? + // + if(ui32Count == ui32Pkt) + { + // + // Clear the endpoint status so that we know no packet is + // waiting. + // + MAP_USBDevEndpointStatusClear(psInst->ui32USBBase, + psInst->ui8BulkOUTEndpoint, + ui32EPStatus); + + // + // Acknowledge the data, thus freeing the host to send the + // next packet. + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, + psInst->ui8BulkOUTEndpoint, + true); + + // + // Clear the flag we set to indicate that a packet read is + // pending. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, + CDC_DO_PACKET_RX, false); + + } + + // + // If all went well, tell the caller how many bytes they got. + // + if(i32Retcode != -1) + { + return(ui32Count); + } + } + } + + // + // 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 pvCDCDevice 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 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. +// +//***************************************************************************** +uint32_t +USBDCDCTxPacketAvailable(void *pvCDCDevice) +{ + tCDCSerInstance *psInst; + + ASSERT(pvCDCDevice); + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &((tUSBDCDCDevice *)pvCDCDevice)->sPrivateData; + + // + // Do we have a packet transmission currently ongoing? + // + if(psInst->iCDCTxState != eCDCStateIdle) + { + // + // 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 pvCDCDevice 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. +// +//***************************************************************************** +uint32_t +USBDCDCRxPacketAvailable(void *pvCDCDevice) +{ + uint32_t ui32EPStatus, ui32Size; + tCDCSerInstance *psInst; + + ASSERT(pvCDCDevice); + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &((tUSBDCDCDevice *)pvCDCDevice)->sPrivateData; + + // + // 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? + // + ui32EPStatus = MAP_USBEndpointStatus(psInst->ui32USBBase, + psInst->ui8BulkOUTEndpoint); + + if(ui32EPStatus & USB_DEV_RX_PKT_RDY) + { + // + // Yes - a packet is waiting. How big is it? + // + ui32Size = MAP_USBEndpointDataAvail(psInst->ui32USBBase, + psInst->ui8BulkOUTEndpoint); + + return(ui32Size); + } + 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 pvCDCDevice is the pointer to the device instance structure as +//! returned by USBDCDCInit(). +//! \param ui16State 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 \e ui16State 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 *pvCDCDevice, uint16_t ui16State) +{ + tCDCSerInstance *psInst; + + ASSERT(pvCDCDevice); + + // + // Get a pointer to the CDC device instance data pointer + // + psInst = &((tUSBDCDCDevice *)pvCDCDevice)->sPrivateData; + + // + // 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->ui16SerialState |= (ui16State & USB_CDC_SERIAL_ERRORS); + psInst->ui16SerialState &= ~USB_CDC_SERIAL_ERRORS; + psInst->ui16SerialState |= (ui16State & ~USB_CDC_SERIAL_ERRORS); + + // + // Set the flag indicating that a serial state change is to be sent. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, CDC_DO_SERIAL_STATE_CHANGE, + true); + + // + // Can we send the state change immediately? + // + if(psInst->iCDCInterruptState == eCDCStateIdle) + { + // + // 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->iCDCInterruptState = eCDCStateWaitData; + SendSerialState(pvCDCDevice); + } + + return; +} +#ifndef DEPRECATED + +//***************************************************************************** +// +//! Reports the device power status (bus- or self-powered) to the USB library. +//! +//! \param pvCDCDevice is the pointer to the CDC device instance structure. +//! \param ui8Power 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 *pvCDCDevice, uint8_t ui8Power) +{ + ASSERT(pvCDCDevice); + + // + // Pass the request through to the lower layer. + // + USBDCDPowerStatusSet(0, ui8Power); +} +#endif + +//***************************************************************************** +// +//! Requests a remote wakeup to resume communication when in suspended state. +//! +//! \param pvCDCDevice 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 configuration 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. +// +//***************************************************************************** +bool +USBDCDCRemoteWakeupRequest(void *pvCDCDevice) +{ + ASSERT(pvCDCDevice); + + // + // 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..dc96086 --- /dev/null +++ b/usblib/device/usbdcdc.h @@ -0,0 +1,447 @@ +//***************************************************************************** +// +// usbdcdc.h - USBLib support for generic CDC ACM (serial) device. +// +// Copyright (c) 2008-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#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. + // + eCDCStateUnconfigured, + + // + // No outstanding transaction remains to be completed. + // + eCDCStateIdle, + + // + // Waiting on completion of a send or receive transaction. + // + eCDCStateWaitData, + + // + // Waiting for client to process data. + // + eCDCStateWaitClient +} +tCDCState; + +//***************************************************************************** +// +// PRIVATE +// +// This structure defines the private instance data and state variables for the +// CDC Serial device. The memory for this structure is allocated in the +// tUSBDCDCDevice structure passed on USBDCDCInit(). +// +//***************************************************************************** +typedef struct +{ + // + // Base address for the USB controller. + // + uint32_t ui32USBBase; + + // + // The device info to interact with the lower level DCD code. + // + tDeviceInfo sDevInfo; + + // + // The state of the serial receive state. + // + volatile tCDCState iCDCRxState; + + // + // The state of the serial transmit state. + // + volatile tCDCState iCDCTxState; + + // + // The state of the serial request state. + // + volatile tCDCState iCDCRequestState; + + // + // The state of the serial interrupt state. + // + volatile tCDCState iCDCInterruptState; + + // + // The current pending request. + // + volatile uint8_t ui8PendingRequest; + + // + // The current break duration used during send break requests. + // + uint16_t ui16BreakDuration; + + // + // The current line control state for the serial port. + // + uint16_t ui16ControlLineState; + + // + // The general serial state. + // + uint16_t ui16SerialState; + + // + // State of any pending operations that could not be handled immediately + // upon receipt. + // + volatile uint16_t ui16DeferredOpFlags; + + // + // Size of the last transmit. + // + uint16_t ui16LastTxSize; + + // + // The current serial line coding. + // + tLineCoding sLineCoding; + + // + // Serial port receive is blocked. + // + volatile bool bRxBlocked; + + // + // Serial control port is blocked. + // + volatile bool bControlBlocked; + + // + // The connection status of the device. + // + volatile bool bConnected; + + // + // The control endpoint number, this is modified in composite devices. + // + uint8_t ui8ControlEndpoint; + + // + // The IN endpoint number, this is modified in composite devices. + // + uint8_t ui8BulkINEndpoint; + + // + // The OUT endpoint number, this is modified in composite devices. + // + uint8_t ui8BulkOUTEndpoint; + + // + // The interface number for the control interface, this is modified in + // composite devices. + // + uint8_t ui8InterfaceControl; + + // + // The interface number for the data interface, this is modified in + // composite devices. + // + uint8_t ui8InterfaceData; +} +tCDCSerInstance; + +//***************************************************************************** +// +// The following defines are used when working with composite devices. +// +//***************************************************************************** + +//***************************************************************************** +// +// This is the size of the g_pui8IADSerDescriptor array in bytes. +// +//***************************************************************************** +#define SERDESCRIPTOR_SIZE (8) + +//***************************************************************************** +// +// This is the size of the g_pui8CDCSerCommInterface array in bytes. +// +//***************************************************************************** +#define SERCOMMINTERFACE_SIZE (35) + +//***************************************************************************** +// +// This is the size of the g_pui8CDCSerDataInterface array in bytes. +// +//***************************************************************************** +#define SERDATAINTERFACE_SIZE (23) + +//***************************************************************************** +// +//! 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. +// +//***************************************************************************** +#define COMPOSITE_DCDC_SIZE (SERDESCRIPTOR_SIZE + SERCOMMINTERFACE_SIZE + \ + SERDATAINTERFACE_SIZE) + +//***************************************************************************** +// +// CDC-specific events These events are provided to the application in the +// \e ui32Msg 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 ui32MsgValue 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. + // + const uint16_t ui16VID; + + // + //! The product ID that this device is to present in the device descriptor. + // + const uint16_t ui16PID; + + // + //! The maximum power consumption of the device, expressed in milliamps. + // + const uint16_t ui16MaxPowermA; + + // + //! 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. + // + const uint8_t ui8PwrAttributes; + + // + //! 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. + // + const 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. + // + const 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. + // + const 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 uint8_t * const *ppui8StringDescriptors; + + // + //! The number of descriptors provided in the ppStringDescriptors + //! array. This must be 1 + (5 * number of supported languages). + // + const uint32_t ui32NumStringDescriptors; + + // + //! 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 sPrivateData; +} +tUSBDCDCDevice; + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void *USBDCDCCompositeInit(uint32_t ui32Index, + tUSBDCDCDevice *psCDCDevice, + tCompositeEntry *psCompEntry); +extern void *USBDCDCInit(uint32_t ui32Index, + tUSBDCDCDevice *psCDCDevice); +extern void USBDCDCTerm(void *pvCDCDevice); +extern void *USBDCDCSetControlCBData(void *pvCDCDevice, void *pvCBData); +extern void *USBDCDCSetRxCBData(void *pvCDCDevice, void *pvCBData); +extern void *USBDCDCSetTxCBData(void *pvCDCDevice, void *pvCBData); +extern uint32_t USBDCDCPacketWrite(void *pvCDCDevice, uint8_t *pi8Data, + uint32_t ui32Length, bool bLast); +extern uint32_t USBDCDCPacketRead(void *pvCDCDevice, uint8_t *pi8Data, + uint32_t ui32Length, bool bLast); +extern uint32_t USBDCDCTxPacketAvailable(void *pvCDCDevice); +extern uint32_t USBDCDCRxPacketAvailable(void *pvCDCDevice); +extern void USBDCDCSerialStateChange(void *pvCDCDevice, uint16_t ui16State); +extern bool USBDCDCRemoteWakeupRequest(void *pvCDCDevice); + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// The following APIs are deprecated. +// +//***************************************************************************** +#ifndef DEPRECATED + +// +// Use USBDCDFeatureSet() or USBHCDFeatureSet() with \b USBLIB_FEATURE_POWER +// configuration option. +// +extern void USBDCDCPowerStatusSet(void *pvCDCDevice, uint8_t ui8Power); +#endif + +//***************************************************************************** +// +// 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..6260d42 --- /dev/null +++ b/usblib/device/usbdcdesc.c @@ -0,0 +1,643 @@ +//***************************************************************************** +// +// usbcdesc.c - Config descriptor parsing functions. +// +// Copyright (c) 2007-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.h" +#include "usblib/device/usbdevice.h" + +//***************************************************************************** +// +// The functions in this file mirror the descriptor parsing APIs available +// in usblib.h but parse configuration 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 pi16Desc. +//! \param pui32Sec points to a variable containing the section within +//! \e psConfig which contains \e pi16Desc. +//! \param pi16Desc points to the descriptor that we want to step past. +//! +//! This function walks forward one descriptor within a configuration +//! descriptor. The value returned is a pointer to the header of the next +//! descriptor after the descriptor supplied in \e pi16Desc. If the next +//! descriptor is in the next section, \e *pui32Sec will be incremented +//! accordingly. +//! +//! \return Returns a pointer to the next descriptor in the configuration +//! descriptor. +// +//***************************************************************************** +static tDescriptorHeader * +NextConfigDescGet(const tConfigHeader *psConfig, uint32_t *pui32Sec, + 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((uint8_t *)psDesc >= (psConfig->psSections[*pui32Sec]->pui8Data + + psConfig->psSections[*pui32Sec]->ui16Size)) + { + // + // Yes - move to the next section. + // + (*pui32Sec)++; + + // + // Are we still within the configuration descriptor? + // + if(*pui32Sec < psConfig->ui8NumSections) + { + // + // Yes - the new descriptor is at the start of the new section. + // + psDesc = + (tDescriptorHeader *)psConfig->psSections[*pui32Sec]->pui8Data; + } + 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 configuration +//! descriptor with the supplied interface number. +//! +//! \param psConfig points to the header structure for the configuration +//! descriptor to search. +//! \param ui8InterfaceNumber is the interface number of the descriptor to +//! query. +//! \param ui32Index is the zero based index of the descriptor. +//! \param pui32Section points to storage which is 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, + uint8_t ui8InterfaceNumber, uint32_t ui32Index, + uint32_t *pui32Section) +{ + tDescriptorHeader *psDescCheck; + uint32_t ui32Count, ui32Sec; + + // + // Set up for our descriptor counting loop. + // + psDescCheck = (tDescriptorHeader *)psConfig->psSections[0]->pui8Data; + ui32Count = 0; + ui32Sec = 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 == + ui8InterfaceNumber)) + { + // + // This is an interface descriptor for interface + // ui8InterfaceNumber. Determine if this is the n-th one we have + // found and, if so, return its pointer. + // + if(ui32Count == ui32Index) + { + // + // Found it - return the pointer and section number. + // + *pui32Section = ui32Sec; + 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. + // + ui32Count++; + } + + // + // Move on to the next descriptor. + // + psDescCheck = NextConfigDescGet(psConfig, &ui32Sec, 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 configuration 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 configuration descriptor will +//! result from concatenating the required sections. +// +//***************************************************************************** +uint32_t +USBDCDConfigDescGetSize(const tConfigHeader *psConfig) +{ + uint32_t ui32Loop, ui32Len; + + ui32Len = 0; + + // + // Determine the size of the whole descriptor by adding the sizes of + // each section which will be concatenated to produce it. + // + for(ui32Loop = 0; ui32Loop < psConfig->ui8NumSections; ui32Loop++) + { + ui32Len += psConfig->psSections[ui32Loop]->ui16Size; + } + + return(ui32Len); +} + +//***************************************************************************** +// +//! \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 ui32Type 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. +// +//***************************************************************************** +uint32_t +USBDCDConfigDescGetNum(const tConfigHeader *psConfig, uint32_t ui32Type) +{ + uint32_t ui32Section, ui32NumDescs; + + // + // Initialize our counts. + // + ui32NumDescs = 0; + + // + // Determine the number of descriptors of the given type in each of the + // sections comprising the configuration descriptor. Note that this + // assumes each section contains only whole descriptors! + // + for(ui32Section = 0; ui32Section < (uint32_t)psConfig->ui8NumSections; + ui32Section++) + { + ui32NumDescs += USBDescGetNum( + (tDescriptorHeader *)psConfig->psSections[ui32Section]->pui8Data, + psConfig->psSections[ui32Section]->ui16Size, ui32Type); + } + + return(ui32NumDescs); +} + +//***************************************************************************** +// +//! \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 ui32Type 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 ui32Index is the zero based index of the descriptor whose pointer is +//! to be returned. For example, passing value 1 in \e ui32Index returns the +//! second matching descriptor. +//! \param pui32Section 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, uint32_t ui32Type, + uint32_t ui32Index, uint32_t *pui32Section) +{ + uint32_t ui32Section, ui32TotalDescs, ui32NumDescs; + + // + // Initialize our counts. + // + ui32TotalDescs = 0; + + // + // Determine the number of descriptors of the given type in each of the + // sections comprising the configuration descriptor. This allows us to + // determine which section contains the descriptor we are being asked for. + // + for(ui32Section = 0; ui32Section < (uint32_t)psConfig->ui8NumSections; + ui32Section++) + { + // + // How many descriptors of the requested type exist in this section? + // + ui32NumDescs = USBDescGetNum( + (tDescriptorHeader *)psConfig->psSections[ui32Section]->pui8Data, + psConfig->psSections[ui32Section]->ui16Size, ui32Type); + + // + // Does this section contain the descriptor whose index we are looking + // for? + // + if((ui32TotalDescs + ui32NumDescs) > ui32Index) + { + // + // We know the requested descriptor exists in the current + // block so write the section number to the caller's storage. + // + *pui32Section = ui32Section; + + // + // Now find the actual descriptor requested and return its pointer. + // + return(USBDescGet( + (tDescriptorHeader *)psConfig->psSections[ui32Section]->pui8Data, + psConfig->psSections[ui32Section]->ui16Size, + ui32Type, ui32Index - ui32TotalDescs)); + } + + // + // 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. + // + ui32TotalDescs += ui32NumDescs; + } + + // + // 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 configuration descriptor. +//! +//! \param psConfig points to the header structure for the configuration +//! descriptor that is to be searched. +//! \param ui8InterfaceNumber 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 configuration +//! descriptor. +// +//***************************************************************************** +uint32_t +USBDCDConfigGetNumAlternateInterfaces(const tConfigHeader *psConfig, + uint8_t ui8InterfaceNumber) +{ + tDescriptorHeader *psDescCheck; + uint32_t ui32Count, ui32Sec; + + // + // Set up for our descriptor counting loop. + // + psDescCheck = (tDescriptorHeader *)psConfig->psSections[0]->pui8Data; + ui32Sec = 0; + ui32Count = 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 == + ui8InterfaceNumber)) + { + // + // Yes - increment our count. + // + ui32Count++; + } + + // + // Move on to the next descriptor. + // + psDescCheck = NextConfigDescGet(psConfig, &ui32Sec, psDescCheck); + } + + // + // Return the descriptor count to the caller. + // + return(ui32Count); +} + +//***************************************************************************** +// +//! \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 ui32Index is the zero based index of the interface that is to be +//! found. If \e ui32Alt is set to a value other than \b USB_DESC_ANY, this +//! is equivalent to the interface number being searched for. +//! \param ui32Alt 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 pui32Section 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 ui32Alt 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 +//! ui32Alt if \e ui32Index is set to the required interface number and +//! \e ui32Alt 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, uint32_t ui32Index, + uint32_t ui32Alt, uint32_t *pui32Section) +{ + // + // If we are being told to ignore the alternate configuration, this boils + // down to a very simple query. + // + if(ui32Alt == USB_DESC_ANY) + { + // + // Return the ui32Index-th interface descriptor we find in the + // configuration descriptor. + // + return((tInterfaceDescriptor *)USBDCDConfigDescGet(psConfig, + USB_DTYPE_INTERFACE, + ui32Index, + pui32Section)); + } + else + { + // + // In this case, a specific alternate setting number is required. + // Given that interface numbers are zero based indices, we can + // pass the supplied ui32Index parameter directly as the interface + // number to USBDescGetAlternateInterface() to retrieve the requested + // interface descriptor pointer. + // + return(ConfigAlternateInterfaceGet(psConfig, ui32Index, ui32Alt, + pui32Section)); + } +} + +//***************************************************************************** +// +//! \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 ui32InterfaceNumber is the interface number whose endpoint is to be +//! found. +//! \param ui32AltCfg is the alternate setting number which is to be searched +//! for. This must be a valid alternate setting number for the requested +//! interface. +//! \param ui32Index 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 ui32Index 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, + uint32_t ui32InterfaceNumber, + uint32_t ui32AltCfg, uint32_t ui32Index) +{ + tInterfaceDescriptor *psInterface; + tDescriptorHeader *psEndpoint; + uint32_t ui32Section, ui32Count; + + // + // Find the requested interface descriptor. + // + psInterface = USBDCDConfigGetInterface(psConfig, ui32InterfaceNumber, + ui32AltCfg, &ui32Section); + + // + // Did we find the requested interface? + // + if(psInterface) + { + // + // Is the index passed valid? + // + if(ui32Index >= 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. + // + ui32Count = 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(ui32Count == ui32Index) + { + // + // Yes - return the descriptor pointer to the caller. + // + return((tEndpointDescriptor *)psEndpoint); + } + + // + // Move on to look for the next endpoint. + // + ui32Count++; + } + + // + // Move to the next descriptor. + // + psEndpoint = NextConfigDescGet(psConfig, &ui32Section, + psEndpoint); + } + } + } + + // + // We could not 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..727ed42 --- /dev/null +++ b/usblib/device/usbdcomp.c @@ -0,0 +1,1544 @@ +//**************************************************************************** +// +// usbdcomp.c - USB composite device class driver. +// +// Copyright (c) 2010-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva USB Library. +// +//**************************************************************************** + +#include +#include +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/sysctl.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.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 uint8_t g_pui8CompDeviceDescriptor[] = +{ + 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 uint8_t g_pui8CompConfigDescriptor[] = +{ + // + // 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.pui32DeviceWorkspace). 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 + +//**************************************************************************** +// +// Various internal handlers needed by this class. +// +//**************************************************************************** +static void HandleDisconnect(void *pvCompositeInstance); +static void InterfaceChange(void *pvCompositeInstance, uint8_t ui8InterfaceNum, + uint8_t ui8AlternateSetting); +static void ConfigChangeHandler(void *pvCompositeInstance, uint32_t ui32Value); +static void DataSent(void *pvCompositeInstance, uint32_t ui32Info); +static void DataReceived(void *pvCompositeInstance, uint32_t ui32Info); +static void HandleEndpoints(void *pvCompositeInstance, uint32_t ui32Status); +static void HandleRequests(void *pvCompositeInstance, tUSBRequest *psUSBRequest); +static void SuspendHandler(void *pvCompositeInstance); +static void ResumeHandler(void *pvCompositeInstance); +static void ResetHandler(void *pvCompositeInstance); +static void HandleDevice(void *pvCompositeInstance, uint32_t ui32Request, + void *pvRequestData); +static void GetDescriptor(void *pvCompositeInstance, tUSBRequest *psUSBRequest); + +//**************************************************************************** +// +// Configuration Descriptor. +// +//**************************************************************************** +tConfigHeader *g_ppCompConfigDescriptors[1]; + +//**************************************************************************** +// +// The device information structure for the USB Composite device. +// +//**************************************************************************** +const tCustomHandlers g_sCompHandlers = +{ + // + // GetDescriptor + // + GetDescriptor, + + // + // RequestHandler + // + HandleRequests, + + // + // InterfaceChange + // + InterfaceChange, + + // + // ConfigChange + // + ConfigChangeHandler, + + // + // DataReceived + // + DataReceived, + + // + // DataSentCallback + // + DataSent, + + // + // ResetHandler + // + ResetHandler, + + // + // SuspendHandler + // + SuspendHandler, + + // + // ResumeHandler + // + ResumeHandler, + + // + // DisconnectHandler + // + HandleDisconnect, + + // + // EndpointHandler + // + HandleEndpoints, + + // + // DeviceHandler + // + HandleDevice, +}; + +//**************************************************************************** +// +// Use the lookup table from the field pui32DeviceWorkspace 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 uint32_t +InterfaceToIndex(tUSBDCompositeDevice *psDevice, uint32_t ui32Interface) +{ + uint32_t ui32Loop; + uint32_t ui32Lookup; + + // + // Check each lookup entry in turn. + // + for(ui32Loop = 0; ui32Loop < psDevice->ui32NumDevices; ui32Loop++) + { + // + // Get the look up value from the device. + // + ui32Lookup = psDevice->psDevices[ui32Loop].ui32DeviceWorkspace; + ui32Lookup = (ui32Lookup >> (8 * LOOKUP_INTERFACE_BYTE)) & 0xff; + + // + // 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(ui32Interface < ui32Lookup) + { + return(ui32Loop); + } + } + + // + // 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 pui32DeviceWorkspace 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 uint32_t +EndpointToIndex(tUSBDCompositeDevice *psDevice, uint32_t ui32Endpoint, + bool bInEndpoint) +{ + uint32_t ui32Loop, ui32EndpointByte, ui32Lookup; + + // + // Are we considering an IN or OUT endpoint? + // + ui32EndpointByte = bInEndpoint ? LOOKUP_IN_END_BYTE : LOOKUP_OUT_END_BYTE; + + // + // Check each lookup entry in turn. + // + for(ui32Loop = 0; ui32Loop < psDevice->ui32NumDevices; ui32Loop++) + { + // + // Get the look up byte from the device. + // + ui32Lookup = psDevice->psDevices[ui32Loop].ui32DeviceWorkspace; + ui32Lookup = (ui32Lookup >> (ui32EndpointByte * 8)) & 0xff; + + // + // 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(ui32Endpoint < ui32Lookup) + { + return(ui32Loop); + } + } + + // + // 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 *pvCompositeInstance, tUSBRequest *psUSBRequest) +{ + uint32_t ui32Idx; + const tDeviceInfo *psDeviceInfo; + tUSBDCompositeDevice *psCompDevice; + + // + // Create the composite device pointer. + // + psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance; + + // + // 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(psUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M) + { + case USB_RTYPE_INTERFACE: + { + ui32Idx = InterfaceToIndex(psCompDevice, + (psUSBRequest->wIndex & 0xFF)); + break; + } + + case USB_RTYPE_ENDPOINT: + { + ui32Idx = EndpointToIndex(psCompDevice, + (psUSBRequest->wIndex & 0x0F), + (psUSBRequest->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: + { + ui32Idx = INVALID_DEVICE_INDEX; + break; + } + } + + // + // Did we find a device class to pass the request to? + // + if(ui32Idx != INVALID_DEVICE_INDEX) + { + // + // Get a pointer to the individual device instance. + // + psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo; + + // + // Does this device have a GetDescriptor callback? + // + if(psDeviceInfo->psCallbacks->pfnGetDescriptor) + { + // + // Remember this device index so that we can correctly route any + // data notification callbacks to it. + // + psCompDevice->sPrivateData.ui32EP0Owner = ui32Idx; + + // + // Call the device to retrieve the descriptor. + // + psDeviceInfo->psCallbacks->pfnGetDescriptor( + psCompDevice->psDevices[ui32Idx].pvInstance, psUSBRequest); + } + else + { + // + // Oops - we can't satisfy the request so stall EP0 to indicate + // an error. + // + USBDCDStallEP0(USBBaseToIndex( + psCompDevice->sPrivateData.ui32USBBase)); + } + } + else + { + // + // We are unable to satisfy the descriptor request so stall EP0 to + // indicate an error. + // + USBDCDStallEP0(USBBaseToIndex( + psCompDevice->sPrivateData.ui32USBBase)); + } +} + +//**************************************************************************** +// +// This function will check if any device classes need an suspend handler +// called. +// +//**************************************************************************** +static void +SuspendHandler(void *pvCompositeInstance) +{ + uint32_t ui32Idx; + tUSBDCompositeDevice *psCompDevice; + const tDeviceInfo *psDeviceInfo; + void *pvDeviceInst; + + ASSERT(pvCompositeInstance != 0); + + // + // Create the device instance pointer. + // + psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance; + + // + // Inform the application that the device has resumed. + // + if(psCompDevice->pfnCallback) + { + psCompDevice->pfnCallback(pvCompositeInstance, USB_EVENT_SUSPEND, + 0, 0); + } + + for(ui32Idx = 0; ui32Idx < psCompDevice->ui32NumDevices; ui32Idx++) + { + psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo; + pvDeviceInst = psCompDevice->psDevices[ui32Idx].pvInstance; + + if(psDeviceInfo->psCallbacks->pfnSuspendHandler) + { + psDeviceInfo->psCallbacks->pfnSuspendHandler(pvDeviceInst); + } + } +} + +//**************************************************************************** +// +// This function will check if any device classes need an resume handler +// called. +// +//**************************************************************************** +static void +ResumeHandler(void *pvCompositeInstance) +{ + uint32_t ui32Idx; + tUSBDCompositeDevice *psCompDevice; + const tDeviceInfo *psDeviceInfo; + void *pvDeviceInst; + + ASSERT(pvCompositeInstance != 0); + + // + // Create the device instance pointer. + // + psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance; + + // + // Inform the application that the device has resumed. + // + if(psCompDevice->pfnCallback) + { + psCompDevice->pfnCallback(pvCompositeInstance, USB_EVENT_RESUME, + 0, 0); + } + + for(ui32Idx = 0; ui32Idx < psCompDevice->ui32NumDevices; ui32Idx++) + { + psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo; + pvDeviceInst = psCompDevice->psDevices[ui32Idx].pvInstance; + + if(psDeviceInfo->psCallbacks->pfnResumeHandler) + { + psDeviceInfo->psCallbacks->pfnResumeHandler(pvDeviceInst); + } + } +} + +//**************************************************************************** +// +// This function will check if any device classes need an reset handler +// called. +// +//**************************************************************************** +static void +ResetHandler(void *pvCompositeInstance) +{ + uint32_t ui32Idx; + tUSBDCompositeDevice *psCompDevice; + const tDeviceInfo *psDeviceInfo; + void *pvDeviceInst; + + ASSERT(pvCompositeInstance != 0); + + // + // Create the device instance pointer. + // + psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance; + + // + // Inform the application that the device has been connected. + // + if(psCompDevice->pfnCallback) + { + psCompDevice->pfnCallback(pvCompositeInstance, + USB_EVENT_CONNECTED, 0, 0); + } + + for(ui32Idx = 0; ui32Idx < psCompDevice->ui32NumDevices; ui32Idx++) + { + psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo; + pvDeviceInst = psCompDevice->psDevices[ui32Idx].pvInstance; + + if(psDeviceInfo->psCallbacks->pfnResetHandler) + { + psDeviceInfo->psCallbacks->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 *pvCompositeInstance, uint32_t ui32Info) +{ + uint32_t ui32Idx; + const tDeviceInfo *psDeviceInfo; + tUSBDCompositeDevice *psCompDevice; + + // + // Create the device instance pointer. + // + psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance; + + // + // Pass this notification on to the device which last handled a + // transaction on endpoint 0 (assuming we know who that was). + // + ui32Idx = psCompDevice->sPrivateData.ui32EP0Owner; + + if(ui32Idx != INVALID_DEVICE_INDEX) + { + psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo; + + if(psDeviceInfo->psCallbacks->pfnDataSent) + { + psDeviceInfo->psCallbacks->pfnDataSent( + psCompDevice->psDevices[ui32Idx].pvInstance, ui32Info); + } + } +} + +//**************************************************************************** +// +// 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 *pvCompositeInstance, uint32_t ui32Info) +{ + uint32_t ui32Idx; + const tDeviceInfo *psDeviceInfo; + tUSBDCompositeDevice *psCompDevice; + + // + // Create the device instance pointer. + // + psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance; + + // + // Pass this notification on to the device which last handled a + // transaction on endpoint 0 (assuming we know who that was). + // + ui32Idx = psCompDevice->sPrivateData.ui32EP0Owner; + + if(ui32Idx != INVALID_DEVICE_INDEX) + { + psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo; + + if(psDeviceInfo->psCallbacks->pfnDataReceived) + { + psDeviceInfo->psCallbacks->pfnDataReceived( + psCompDevice->psDevices[ui32Idx].pvInstance, ui32Info); + } + } +} + +//**************************************************************************** +// +// This function will check if any device classes need an endpoint handler +// called. +// +//**************************************************************************** +static void +HandleEndpoints(void *pvCompositeInstance, uint32_t ui32Status) +{ + uint32_t ui32Idx; + const tDeviceInfo *psDeviceInfo; + tUSBDCompositeDevice *psCompDevice; + + ASSERT(pvCompositeInstance != 0); + + // + // Create the device instance pointer. + // + psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance; + + // + // 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 ui32Status will be 0). Since the handlers are + // set up to ignore any callback that is not for them, this is safe. + // + for(ui32Idx = 0; ui32Idx < psCompDevice->ui32NumDevices; ui32Idx++) + { + psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo; + + if(psDeviceInfo->psCallbacks->pfnEndpointHandler) + { + psDeviceInfo->psCallbacks->pfnEndpointHandler( + psCompDevice->psDevices[ui32Idx].pvInstance, ui32Status); + } + } +} + +//***************************************************************************** +// +// Device instance specific handler. +// +//***************************************************************************** +static void +HandleDevice(void *pvCompositeInstance, uint32_t ui32Request, + void *pvRequestData) +{ + uint32_t ui32Idx; + tUSBDCompositeDevice *psCompDevice; + const tDeviceInfo *psDeviceInfo; + + ASSERT(pvCompositeInstance != 0); + + // + // Create the device instance pointer. + // + psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance; + + for(ui32Idx = 0; ui32Idx < psCompDevice->ui32NumDevices; ui32Idx++) + { + psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo; + + if(psDeviceInfo->psCallbacks->pfnDeviceHandler) + { + psDeviceInfo->psCallbacks->pfnDeviceHandler( + psCompDevice->psDevices[ui32Idx].pvInstance, ui32Request, + pvRequestData); + } + } + + if(psCompDevice->pfnCallback) + { + switch(ui32Request) + { + case USB_EVENT_LPM_RESUME: + { + // + // Pass the LPM resume event to the client. + // + psCompDevice->pfnCallback(0, USB_EVENT_LPM_RESUME, 0, + (void *)0); + break; + } + case USB_EVENT_LPM_SLEEP: + { + // + // Pass the LPM sleep event to the client. + // + psCompDevice->pfnCallback(0, USB_EVENT_LPM_SLEEP, 0, + (void *)0); + break; + } + case USB_EVENT_LPM_ERROR: + { + // + // Pass the LPM error event to the client. + // + psCompDevice->pfnCallback(0, USB_EVENT_LPM_ERROR, 0, + (void *)0); + break; + } + default: + { + break; + } + } + } +} + +//**************************************************************************** +// +// This function is called by the USB device stack whenever the device is +// disconnected from the host. +// +//**************************************************************************** +static void +HandleDisconnect(void *pvCompositeInstance) +{ + uint32_t ui32Idx; + const tDeviceInfo *psDeviceInfo; + tUSBDCompositeDevice *psCompDevice; + + ASSERT(pvCompositeInstance != 0); + + // + // Create the device instance pointer. + // + psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance; + + // + // Inform the application that the device has been disconnected. + // + if(psCompDevice->pfnCallback) + { + psCompDevice->pfnCallback(pvCompositeInstance, + USB_EVENT_DISCONNECTED, 0, 0); + } + + for(ui32Idx = 0; ui32Idx < psCompDevice->ui32NumDevices; ui32Idx++) + { + psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo; + + if(psDeviceInfo->psCallbacks->pfnDisconnectHandler) + { + psDeviceInfo->psCallbacks->pfnDisconnectHandler( + psCompDevice->psDevices[ui32Idx].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 *pvCompositeInstance, uint8_t ui8InterfaceNum, + uint8_t ui8AlternateSetting) +{ + uint32_t ui32Idx; + const tDeviceInfo *psDeviceInfo; + tUSBDCompositeDevice *psCompDevice; + + ASSERT(pvCompositeInstance != 0); + + // + // Create the device instance pointer. + // + psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance; + for(ui32Idx = 0; ui32Idx < psCompDevice->ui32NumDevices; ui32Idx++) + { + psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo; + + if(psDeviceInfo->psCallbacks->pfnInterfaceChange) + { + psDeviceInfo->psCallbacks->pfnInterfaceChange( + psCompDevice->psDevices[ui32Idx].pvInstance, + ui8InterfaceNum, ui8AlternateSetting); + } + } +} + +//**************************************************************************** +// +// 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 *pvCompositeInstance, uint32_t ui32Value) +{ + uint32_t ui32Idx; + const tDeviceInfo *psDeviceInfo; + tUSBDCompositeDevice *psCompDevice; + + ASSERT(pvCompositeInstance != 0); + + // + // Create the device instance pointer. + // + psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance; + + for(ui32Idx = 0; ui32Idx < psCompDevice->ui32NumDevices; ui32Idx++) + { + psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo; + + if(psDeviceInfo->psCallbacks->pfnConfigChange) + { + psDeviceInfo->psCallbacks->pfnConfigChange( + psCompDevice->psDevices[ui32Idx].pvInstance, ui32Value); + } + } +} + +//**************************************************************************** +// +// This function is called by the USB device stack whenever a non-standard +// request is received. +// +// \param pvCompositeInstance +// \param psUSBRequest 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 *pvCompositeInstance, tUSBRequest *psUSBRequest) +{ + uint32_t ui32Idx; + const tDeviceInfo *psDeviceInfo; + tUSBDCompositeDevice *psCompDevice; + + // + // Create the device instance pointer. + // + psCompDevice = (tUSBDCompositeDevice *)pvCompositeInstance; + + // + // 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(psUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M) + { + case USB_RTYPE_INTERFACE: + { + ui32Idx = InterfaceToIndex(psCompDevice, + (psUSBRequest->wIndex & 0xFF)); + break; + } + + case USB_RTYPE_ENDPOINT: + { + ui32Idx = EndpointToIndex(psCompDevice, + (psUSBRequest->wIndex & 0x0F), + (psUSBRequest->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: + { + ui32Idx = INVALID_DEVICE_INDEX; + break; + } + } + + // + // Did we find a device class to pass the request to? + // + if(ui32Idx != INVALID_DEVICE_INDEX) + { + // + // Get a pointer to the individual device instance. + // + psDeviceInfo = psCompDevice->psDevices[ui32Idx].psDevInfo; + + // + // Does this device have a RequestHandler callback? + // + if(psDeviceInfo->psCallbacks->pfnRequestHandler) + { + // + // Remember this device index so that we can correctly route any + // data notification callbacks to it. + // + psCompDevice->sPrivateData.ui32EP0Owner = ui32Idx; + + // + // Yes - call the device to retrieve the descriptor. + // + psDeviceInfo->psCallbacks->pfnRequestHandler( + psCompDevice->psDevices[ui32Idx].pvInstance, + psUSBRequest); + } + else + { + // + // Oops - we can't satisfy the request so stall EP0 to indicate + // an error. + // + USBDCDStallEP0(USBBaseToIndex( + psCompDevice->sPrivateData.ui32USBBase)); + } + } + else + { + // + // We are unable to satisfy the descriptor request so stall EP0 to + // indicate an error. + // + USBDCDStallEP0(USBBaseToIndex( + psCompDevice->sPrivateData.ui32USBBase)); + } +} + +//**************************************************************************** +// +// This function handles sending interface number changes to device instances. +// +//**************************************************************************** +static void +CompositeIfaceChange(tCompositeEntry *psCompDevice, uint8_t ui8Old, + uint8_t ui8New) +{ + uint8_t pui8Interfaces[2]; + + if(psCompDevice->psDevInfo->psCallbacks->pfnDeviceHandler) + { + // + // Create the data to pass to the device handler. + // + pui8Interfaces[0] = ui8Old; + pui8Interfaces[1] = ui8New; + + // + // Call the device handler to inform the class of the interface number + // change. + // + psCompDevice->psDevInfo->psCallbacks->pfnDeviceHandler( + psCompDevice->pvInstance, USB_EVENT_COMP_IFACE_CHANGE, + (void *)pui8Interfaces); + } +} + +//**************************************************************************** +// +// This function handles sending endpoint number changes to device instances. +// +//**************************************************************************** +static void +CompositeEPChange(tCompositeEntry *psCompDevice, uint8_t ui8Old, + uint8_t ui8New) +{ + uint8_t pui8Interfaces[2]; + + if(psCompDevice->psDevInfo->psCallbacks->pfnDeviceHandler) + { + // + // Create the data to pass to the device handler. + // + pui8Interfaces[0] = ui8Old; + pui8Interfaces[1] = ui8New; + + ui8New--; + + // + // Call the device handler to inform the class of the interface number + // change. + // + psCompDevice->psDevInfo->psCallbacks->pfnDeviceHandler( + psCompDevice->pvInstance, USB_EVENT_COMP_EP_CHANGE, + (void *)pui8Interfaces); + } +} + +//**************************************************************************** +// +// This function merges the configuration descriptors into a single multiple +// instance device. +// +//**************************************************************************** +uint32_t +BuildCompositeDescriptor(tUSBDCompositeDevice *psCompDevice) +{ + uint32_t ui32Idx, ui32Offset, ui32CPIdx, ui32FixINT, ui32Dev; + uint16_t ui16TotalLength, ui16Bytes; + uint8_t ui8Interface, ui8INEndpoint, ui8OUTEndpoint; + uint8_t *pui8Data, *pui8Config; + const tConfigHeader *psConfigHeader; + tDescriptorHeader *psHeader; + const uint8_t *pui8Descriptor; + tInterfaceDescriptor *psInterface; + tEndpointDescriptor *psEndpoint; + const tDeviceInfo *psDevice; + + // + // Save the number of devices to look through. + // + ui32Dev = 0; + ui32Idx = 0; + ui8Interface = 0; + ui8INEndpoint = 1; + ui8OUTEndpoint = 1; + ui32Offset = 0; + ui32FixINT = 0; + + // + // This puts the first section pointer in the first entry in the list + // of sections. + // + psCompDevice->sPrivateData.ppsCompSections[0] = + &psCompDevice->sPrivateData.psCompSections[0]; + + // + // Put the pointer to this instances configuration descriptor into the + // front of the list. + // + psCompDevice->sPrivateData.ppsCompSections[0]->pui8Data = + (uint8_t *)&psCompDevice->sPrivateData.sConfigDescriptor; + + psCompDevice->sPrivateData.ppsCompSections[0]->ui16Size = + psCompDevice->sPrivateData.sConfigDescriptor.bLength; + + // + // The configuration descriptor is 9 bytes so initialize the total length + // to 9 bytes. + // + ui16TotalLength = 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->sPrivateData.ppsCompSections[1] = + &psCompDevice->sPrivateData.psCompSections[1]; + + // + // Copy the pointer to the application supplied space into the section + // list. + // + psCompDevice->sPrivateData.ppsCompSections[1]->ui16Size = 0; + psCompDevice->sPrivateData.ppsCompSections[1]->pui8Data = + psCompDevice->sPrivateData.pui8Data; + + // + // Create a local pointer to the data that is used to copy data from + // the other devices into the composite descriptor. + // + pui8Data = psCompDevice->sPrivateData.pui8Data; + + // + // Consider each device in turn. + // + while(ui32Dev < psCompDevice->ui32NumDevices) + { + // + // Save the current starting address of this descriptor. + // + pui8Config = pui8Data + ui32Offset; + + // + // Create a local pointer to the configuration header. + // + psDevice = psCompDevice->psDevices[ui32Dev].psDevInfo; + psConfigHeader = psDevice->ppsConfigDescriptors[0]; + + // + // Loop through each of the sections in this device's configuration + // descriptor. + // + for(ui32Idx = 0; ui32Idx < psConfigHeader->ui8NumSections; ui32Idx++) + { + // + // Initialize the local offset in this descriptor. We include + // a special case here to ignore the initial 9 byte configuration + // descriptor since this has already been handled. + // + if(ui32Idx) + { + // + // This is not the first section so we handle everything in + // it. + // + ui16Bytes = 0; + } + else + { + // + // This is the first section for this device so skip the 9 + // byte configuration descriptor since we've already handled + // this. + // + ui16Bytes = 9; + + // + // If this section includes only the configuration descriptor, + // skip it entirely. + // + if(psConfigHeader->psSections[ui32Idx]->ui16Size <= ui16Bytes) + { + continue; + } + } + + // + // Get a pointer to the configuration descriptor. + // + pui8Descriptor = psConfigHeader->psSections[ui32Idx]->pui8Data; + + // + // Bounds check the allocated space and return if there is not + // enough space. + // + if(ui32Offset > psCompDevice->sPrivateData.ui32DataSize) + { + return(1); + } + + // + // Copy the descriptor from the device into the descriptor list. + // + for(ui32CPIdx = 0; + ui32CPIdx < psConfigHeader->psSections[ui32Idx]->ui16Size; + ui32CPIdx++) + { + pui8Data[ui32CPIdx + ui32Offset] = pui8Descriptor[ui32CPIdx]; + } + + // + // Read out the descriptors in this section. + // + while(ui16Bytes < psConfigHeader->psSections[ui32Idx]->ui16Size) + { + // + // Create a descriptor header pointer. + // + psHeader = (tDescriptorHeader *)&pui8Data[ui32Offset + + ui16Bytes]; + + // + // 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 = ui8Interface - 1; + } + else + { + // + // Notify the class that it's interface number has + // changed. + // + CompositeIfaceChange( + &psCompDevice->psDevices[ui32Dev], + psInterface->bInterfaceNumber, + ui8Interface); + // + // This was the non-alternate setting so save the + // value and move to the next interface number. + // + psInterface->bInterfaceNumber = ui8Interface; + + // + // No strings allowed on interface descriptors for + // composite devices. + // + psInterface->iInterface = 0; + + ui8Interface++; + } + } + // + // 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->ui16PID == USB_PID_COMP_SERIAL)) + { + // + // Check if the Fixed Interrupt endpoint has been + // set yet. + // + if(ui32FixINT == 0) + { + // + // Allocate the fixed interrupt endpoint and + // save its number. + // + ui32FixINT = ui8INEndpoint++; + } + + CompositeEPChange( + &psCompDevice->psDevices[ui32Dev], + psEndpoint->bEndpointAddress, + ui32FixINT); + + psEndpoint->bEndpointAddress = ui32FixINT | + USB_RTYPE_DIR_IN; + } + else + { + // + // Notify the class that it's interface number has + // changed. + // + CompositeEPChange( + &psCompDevice->psDevices[ui32Dev], + psEndpoint->bEndpointAddress, + ui8INEndpoint); + + psEndpoint->bEndpointAddress = ui8INEndpoint++ | + USB_RTYPE_DIR_IN; + } + } + else + { + // + // Notify the class that it's interface number has + // changed. + // + CompositeEPChange(&psCompDevice->psDevices[ui32Dev], + psEndpoint->bEndpointAddress, + ui8OUTEndpoint); + psEndpoint->bEndpointAddress = ui8OUTEndpoint++; + } + } + + // + // Move on to the next descriptor. + // + ui16Bytes += psHeader->bLength; + } + + ui32Offset += psConfigHeader->psSections[ui32Idx]->ui16Size; + + ui16TotalLength += ui16Bytes; + } + + // + // Allow the device class to make adjustments to the configuration + // descriptor. + // + psCompDevice->psDevices[ui32Dev].psDevInfo->psCallbacks->pfnDeviceHandler( + psCompDevice->psDevices[ui32Dev].pvInstance, + USB_EVENT_COMP_CONFIG, (void *)pui8Config); + + // + // Add an entry into the device workspace array to allow us to quickly + // map interface and endpoint numbers to device instances later. + // + psCompDevice->psDevices[ui32Dev].ui32DeviceWorkspace = + (ui32Dev << (LOOKUP_INDEX_BYTE * 8)) | + (ui8Interface << (LOOKUP_INTERFACE_BYTE * 8)) | + (ui8OUTEndpoint << (LOOKUP_OUT_END_BYTE * 8)) | + (ui8INEndpoint << (LOOKUP_IN_END_BYTE * 8)); + + // + // Move on to the next device. + // + ui32Dev++; + } + + // + // Modify the configuration descriptor to match the number of interfaces + // and the new total size. + // + psCompDevice->sPrivateData.sCompConfigHeader.ui8NumSections = 2; + psCompDevice->sPrivateData.ppsCompSections[1]->ui16Size = ui32Offset; + psCompDevice->sPrivateData.sConfigDescriptor.bNumInterfaces = + ui8Interface; + psCompDevice->sPrivateData.sConfigDescriptor.wTotalLength = + ui16TotalLength; + + + return(0); +} + +//**************************************************************************** +// +//! This function should be called once for the composite class device to +//! initialize basic operation and prepare for enumeration. +//! +//! \param ui32Index 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 ui32Size is the size in bytes of the data pointed to by the +//! \e pui8Data parameter. +//! \param pui8Data 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 ui32Size and \e pui8Data +//! parameters should be large enough to hold all of the class instances +//! passed in via the \e 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(uint32_t ui32Index, tUSBDCompositeDevice *psDevice, + uint32_t ui32Size, uint8_t *pui8Data) +{ + tCompositeInstance *psInst; + int32_t i32Idx; + uint8_t *pui8Temp; + + // + // Check parameter validity. + // + ASSERT(ui32Index == 0); + ASSERT(psDevice); + ASSERT(psDevice->ppui8StringDescriptors); + + // + // Initialize the work space in the passed instance structure. + // + psInst = &psDevice->sPrivateData; + psInst->ui32DataSize = ui32Size; + psInst->pui8Data = pui8Data; + + // + // Save the base address of the USB controller. + // + psInst->ui32USBBase = USBIndexToBase(ui32Index); + + // + // No device is currently transferring data on EP0. + // + psInst->ui32EP0Owner = INVALID_DEVICE_INDEX; + + // + // Initialize the device information structure. + // + psInst->sDevInfo.psCallbacks = &g_sCompHandlers; + psInst->sDevInfo.pui8DeviceDescriptor = g_pui8CompDeviceDescriptor; + psInst->sDevInfo.ppsConfigDescriptors = + (const tConfigHeader * const *)g_ppCompConfigDescriptors; + psInst->sDevInfo.ppui8StringDescriptors = 0; + psInst->sDevInfo.ui32NumStringDescriptors = 0; + + // + // Initialize the device info structure for the composite device. + // + USBDCDDeviceInfoInit(0, &psInst->sDevInfo); + + g_ppCompConfigDescriptors[0] = &psInst->sCompConfigHeader; + g_ppCompConfigDescriptors[0]->ui8NumSections = 0; + g_ppCompConfigDescriptors[0]->psSections = + (const tConfigSection * const *)psDevice->sPrivateData.ppsCompSections; + + // + // Create a byte pointer to use with the copy. + // + pui8Temp = (uint8_t *)&psInst->sConfigDescriptor; + + // + // Copy the default configuration descriptor into the instance data. + // + for(i32Idx = 0; i32Idx < g_pui8CompConfigDescriptor[0]; i32Idx++) + { + pui8Temp[i32Idx] = g_pui8CompConfigDescriptor[i32Idx]; + } + + // + // Create a byte pointer to use with the copy. + // + pui8Temp = (uint8_t *)&psInst->sDeviceDescriptor; + + // + // Copy the default configuration descriptor into the instance data. + // + for(i32Idx = 0; i32Idx < g_pui8CompDeviceDescriptor[0]; i32Idx++) + { + pui8Temp[i32Idx] = g_pui8CompDeviceDescriptor[i32Idx]; + } + + // + // Fix up the device descriptor with the client-supplied values. + // + psInst->sDeviceDescriptor.idVendor = psDevice->ui16VID; + psInst->sDeviceDescriptor.idProduct = psDevice->ui16PID; + + // + // Fix up the configuration descriptor with client-supplied values. + // + psInst->sConfigDescriptor.bmAttributes = psDevice->ui8PwrAttributes; + psInst->sConfigDescriptor.bMaxPower = + (uint8_t)(psDevice->ui16MaxPowermA>>1); + + psInst->sDevInfo.pui8DeviceDescriptor = + (const uint8_t *)&psInst->sDeviceDescriptor; + + // + // Plug in the client's string table to the device information + // structure. + // + psInst->sDevInfo.ppui8StringDescriptors = + psDevice->ppui8StringDescriptors; + psInst->sDevInfo.ui32NumStringDescriptors = + psDevice->ui32NumStringDescriptors; + + // + // 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); + } + + // + // All is well so now pass the descriptors to the lower layer and put + // the bulk device on the bus. + // + USBDCDInit(ui32Index, &psInst->sDevInfo, (void *)psDevice); + + // + // Return the pointer to the instance indicating that everything went + // well. + // + return((void *)psDevice); +} + +//**************************************************************************** +// +//! Shuts down the composite device. +//! +//! \param pvCompositeInstance is the pointer to the device instance structure +//! as returned by USBDCompositeInit(). +//! +//! This function terminates composite device interface for the instance +//! not me supplied. Following this call, the \e pvCompositeInstance instance +//! should not be used in any other calls. +//! +//! \return None. +// +//**************************************************************************** +void +USBDCompositeTerm(void *pvCompositeInstance) +{ + ASSERT(pvCompositeInstance != 0); + +} + +//**************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//**************************************************************************** + diff --git a/usblib/device/usbdcomp.h b/usblib/device/usbdcomp.h new file mode 100644 index 0000000..e064193 --- /dev/null +++ b/usblib/device/usbdcomp.h @@ -0,0 +1,261 @@ +//***************************************************************************** +// +// usbdcomp.h - USB composite device class driver. +// +// Copyright (c) 2010-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#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 *psDeviceInfo; + + // + // 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. + // + uint8_t ui8IfaceOffset; +} +tUSBDCompositeEntry; + +//***************************************************************************** +// +// PRIVATE +// +// This structure defines the private instance data and state variables for the +// composite device class. The memory for this structure is included in +// the sPrivateData 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. + // + uint32_t ui32USBBase; + + // + // The device information pointer. + // + tDeviceInfo sDevInfo; + + // + // 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. + // + uint32_t ui32DataSize; + uint8_t *pui8Data; + + // + // The current "owner" of endpoint 0. This is used to track the device + // class which is currently transferring data on EP0. + // + uint32_t ui32EP0Owner; +} +tCompositeInstance; + +//***************************************************************************** +// +//! 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. + // + const uint16_t ui16VID; + + // + //! The product ID that this device is to present in the device descriptor. + // + const uint16_t ui16PID; + + // + //! The maximum power consumption of the device, expressed in mA. + // + const uint16_t ui16MaxPowermA; + + // + //! Indicates whether the device is self or bus-powered and whether or not + //! it supports remote wake up. Valid values are \b USB_CONF_ATTR_SELF_PWR + //! or \b USB_CONF_ATTR_BUS_PWR, optionally ORed with + //! \b USB_CONF_ATTR_RWAKE. + // + const uint8_t ui8PwrAttributes; + + // + //! A pointer to the callback function which will be called to notify + //! the application of events relating to the operation of the composite + //! device. + // + const 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 uint8_t * const *ppui8StringDescriptors; + + // + //! The number of descriptors provided in the ppStringDescriptors + //! array. This must be 1 + ((5 + (number of strings)) * + //! (number of languages)). + // + const uint32_t ui32NumStringDescriptors; + + // + //! The number of devices in the psDevices array. + // + const uint32_t ui32NumDevices; + + // + //! This application supplied array holds the the top level device class + //! information as well as the Instance data for that class. + // + tCompositeEntry * const psDevices; + + // + //! The private data for this device instance. 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 sPrivateData; +} +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(uint32_t ui32Index, + tUSBDCompositeDevice *psCompDevice, + uint32_t ui32Size, uint8_t *pui8Data); +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..0a100cc --- /dev/null +++ b/usblib/device/usbdconfig.c @@ -0,0 +1,555 @@ +//***************************************************************************** +// +// usbdconfig.c - High level USB device configuration function. +// +// Copyright (c) 2008-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbdevicepriv.h" + +//***************************************************************************** +// +//! \addtogroup device_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Structure used in compiling FIFO size and endpoint properties from a +// configuration descriptor. +// +//***************************************************************************** +typedef struct +{ + uint32_t pui32Size[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 uint32_t +GetEndpointFIFOSize(uint32_t ui32MaxPktSize, uint32_t *pupBytesUsed) +{ + uint32_t ui32Loop, ui32FIFOSize; + + // + // 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(ui32Loop = USB_FIFO_SZ_8; ui32Loop <= USB_FIFO_SZ_2048; ui32Loop++) + { + // + // How many bytes does this FIFO value represent? + // + ui32FIFOSize = USBFIFOSizeToBytes(ui32Loop); + + // + // Is this large enough to hold one packet. + // + if(ui32FIFOSize >= ui32MaxPktSize) + { + // + // Return the FIFO size setting and the USB_FIFO_SZ_ value. + // + *pupBytesUsed = ui32FIFOSize; + + return(ui32Loop); + } + } + + // + // 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, uint32_t *pui32EPIndex, + uint32_t *pui32MaxPktSize, uint32_t *pui32Flags) +{ + // + // Get the endpoint index. + // + *pui32EPIndex = psEndpoint->bEndpointAddress & USB_EP_DESC_NUM_M; + + // + // Extract the maximum packet size. + // + *pui32MaxPktSize = psEndpoint->wMaxPacketSize & USB_EP_MAX_PACKET_COUNT_M; + + // + // Is this an IN or an OUT endpoint? + // + *pui32Flags = (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: + { + *pui32Flags |= USB_EP_MODE_CTRL; + break; + } + case USB_EP_ATTR_BULK: + { + *pui32Flags |= USB_EP_MODE_BULK; + break; + } + case USB_EP_ATTR_INT: + { + *pui32Flags |= USB_EP_MODE_INT; + break; + } + case USB_EP_ATTR_ISOC: + { + *pui32Flags |= USB_EP_MODE_ISOC; + break; + } + } +} + +//***************************************************************************** +// +//! Configure the USB controller appropriately for the device whose +//! configuration 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. +//! +//! \return Returns \b true on success or \b false on failure. +// +//***************************************************************************** +bool +USBDeviceConfig(tDCDInstance *psDevInst, const tConfigHeader *psConfig) +{ + uint32_t ui32Loop, ui32Count, ui32NumInterfaces, ui32EpIndex, ui32EpType, + ui32MaxPkt, ui32NumEndpoints, ui32Flags, ui32BytesUsed, + ui32Section; + tInterfaceDescriptor *psInterface; + tEndpointDescriptor *psEndpoint; + tUSBEndpointInfo psEPInfo[NUM_USB_EP - 1]; + + // + // A valid device instance is required. + // + ASSERT(psDevInst != 0); + + // + // Catch bad pointers in a debug build. + // + ASSERT(psConfig); + + // + // Clear out our endpoint info. + // + for(ui32Loop = 0; ui32Loop < (NUM_USB_EP - 1); ui32Loop++) + { + psEPInfo[ui32Loop].pui32Size[EP_INFO_IN] = 0; + psEPInfo[ui32Loop].pui32Size[EP_INFO_OUT] = 0; + } + + // + // How many (total) endpoints does this configuration describe? + // + ui32NumEndpoints = USBDCDConfigDescGetNum(psConfig, + USB_DTYPE_ENDPOINT); + + // + // How many interfaces are included? + // + ui32NumInterfaces = 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(ui32Loop = 0; ui32Loop < ui32NumEndpoints; ui32Loop++) + { + // + // Get a pointer to the endpoint descriptor. + // + psEndpoint = (tEndpointDescriptor *)USBDCDConfigDescGet( + psConfig, USB_DTYPE_ENDPOINT, ui32Loop, + &ui32Section); + + // + // Extract the endpoint number and whether it is an IN or OUT + // endpoint. + // + ui32EpIndex = (uint32_t) + psEndpoint->bEndpointAddress & USB_EP_DESC_NUM_M; + ui32EpType = (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((ui32EpIndex >= NUM_USB_EP) || (ui32EpIndex == 0)) + { + return(false); + } + + // + // Does this endpoint have a max packet size requirement larger than + // any previous use we have seen? + // + if(psEndpoint->wMaxPacketSize > + psEPInfo[ui32EpIndex - 1].pui32Size[ui32EpType]) + { + // + // Yes - remember the new maximum packet size. + // + psEPInfo[ui32EpIndex - 1].pui32Size[ui32EpType] = + 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(ui32Loop = 0; ui32Loop < ui32NumInterfaces; ui32Loop++) + { + // + // Get the next interface descriptor in the configuration descriptor. + // + psInterface = USBDCDConfigGetInterface(psConfig, ui32Loop, + USB_DESC_ANY, &ui32Section); + + // + // 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. + // + ui32NumEndpoints = (uint32_t)psInterface->bNumEndpoints; + + // + // Walk through each endpoint in this interface and configure + // it appropriately. + // + for(ui32Count = 0; ui32Count < ui32NumEndpoints; ui32Count++) + { + // + // Get a pointer to the endpoint descriptor. + // + psEndpoint = USBDCDConfigGetInterfaceEndpoint(psConfig, + psInterface->bInterfaceNumber, + psInterface->bAlternateSetting, + ui32Count); + + // + // Make sure we got a good pointer. + // + if(psEndpoint) + { + // + // Determine maximum packet size and flags from the + // endpoint descriptor. + // + GetEPDescriptorType(psEndpoint, &ui32EpIndex, &ui32MaxPkt, + &ui32Flags); + + // + // Make sure no-one is trying to configure endpoint 0. + // + if(!ui32EpIndex) + { + return(false); + } + + // + // Set the endpoint configuration. + // + USBDevEndpointConfigSet(USB0_BASE, + IndexToUSBEP(ui32EpIndex), + ui32MaxPkt, ui32Flags); + } + } + } + } + + // + // 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. + // + ui32Count = MAX_PACKET_SIZE_EP0; + for(ui32Loop = 1; ui32Loop < NUM_USB_EP; ui32Loop++) + { + // + // Configure the IN endpoint at this index if it is referred to + // anywhere. + // + if(psEPInfo[ui32Loop - 1].pui32Size[EP_INFO_IN]) + { + // + // What FIFO size flag do we use for this endpoint? + // + ui32MaxPkt = GetEndpointFIFOSize( + psEPInfo[ui32Loop - 1].pui32Size[EP_INFO_IN], + &ui32BytesUsed); + + // + // The FIFO space could not be allocated. + // + if(ui32BytesUsed == 0) + { + return(false); + } + + // + // Now actually configure the FIFO for this endpoint. + // + USBFIFOConfigSet(USB0_BASE, IndexToUSBEP(ui32Loop), ui32Count, + ui32MaxPkt, USB_EP_DEV_IN); + ui32Count += ui32BytesUsed; + } + + // + // Configure the OUT endpoint at this index. + // + if(psEPInfo[ui32Loop - 1].pui32Size[EP_INFO_OUT]) + { + // + // What FIFO size flag do we use for this endpoint? + // + ui32MaxPkt = GetEndpointFIFOSize( + psEPInfo[ui32Loop - 1].pui32Size[EP_INFO_OUT], + &ui32BytesUsed); + + // + // The FIFO space could not be allocated. + // + if(ui32BytesUsed == 0) + { + return(false); + } + + // + // Now actually configure the FIFO for this endpoint. + // + USBFIFOConfigSet(USB0_BASE, IndexToUSBEP(ui32Loop), ui32Count, + ui32MaxPkt, USB_EP_DEV_OUT); + ui32Count += ui32BytesUsed; + } + + } + + // + // 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 ui8InterfaceNum 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 ui8AlternateSetting 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. +// +//***************************************************************************** +bool +USBDeviceConfigAlternate(tDCDInstance *psDevInst, + const tConfigHeader *psConfig, + uint8_t ui8InterfaceNum, + uint8_t ui8AlternateSetting) +{ + uint32_t ui32NumInterfaces, ui32NumEndpoints, ui32Loop, ui32Count, + ui32MaxPkt, ui32Flags, ui32Section, ui32EpIndex; + tInterfaceDescriptor *psInterface; + tEndpointDescriptor *psEndpoint; + + // + // How many interfaces are included in the descriptor? + // + ui32NumInterfaces = USBDCDConfigDescGetNum(psConfig, + USB_DTYPE_INTERFACE); + + // + // Find the interface descriptor for the supplied interface and alternate + // setting numbers. + // + + for(ui32Loop = 0; ui32Loop < ui32NumInterfaces; ui32Loop++) + { + // + // Get the next interface descriptor in the configuration descriptor. + // + psInterface = USBDCDConfigGetInterface(psConfig, ui32Loop, + USB_DESC_ANY, &ui32Section); + + // + // Is this the default interface (bAlternateSetting set to 0)? + // + if(psInterface && + (psInterface->bInterfaceNumber == ui8InterfaceNum) && + (psInterface->bAlternateSetting == ui8AlternateSetting)) + { + // + // 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? + // + ui32NumEndpoints = (uint32_t)psInterface->bNumEndpoints; + + // + // Walk through each endpoint in turn. + // + for(ui32Count = 0; ui32Count < ui32NumEndpoints; ui32Count++) + { + // + // Get a pointer to the endpoint descriptor. + // + psEndpoint = USBDCDConfigGetInterfaceEndpoint(psConfig, + psInterface->bInterfaceNumber, + psInterface->bAlternateSetting, + ui32Count); + + // + // Make sure we got a good pointer. + // + if(psEndpoint) + { + // + // Determine maximum packet size and flags from the + // endpoint descriptor. + // + GetEPDescriptorType(psEndpoint, &ui32EpIndex, &ui32MaxPkt, + &ui32Flags); + + // + // Make sure no-one is trying to configure endpoint 0. + // + if(!ui32EpIndex) + { + return(false); + } + + // + // Set the endpoint configuration. + // + USBDevEndpointConfigSet(USB0_BASE, + IndexToUSBEP(ui32EpIndex), + ui32MaxPkt, ui32Flags); + } + } + + // + // 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..24adb26 --- /dev/null +++ b/usblib/device/usbddfu-rt.c @@ -0,0 +1,661 @@ +//***************************************************************************** +// +// usbddfu-rt.c - USB Device Firmware Update runtime device class driver. +// +// Copyright (c) 2010-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "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.h" +#include "driverlib/rom_map.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.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 uint8_t g_pui8DFUDeviceDescriptor[] = +{ + 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 identifier. + 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. +// +//***************************************************************************** +uint8_t g_pui8DFUConfigDescriptor[] = +{ + // + // 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. +// +//***************************************************************************** +uint8_t g_pui8DFUInterface[DFUINTERFACE_SIZE] = +{ + // + // 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. +// +//***************************************************************************** +uint8_t g_pui8DFUFunctionalDesc[DFUFUNCTIONALDESC_SIZE] = +{ + // + // 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_pui8DFUConfigDescriptor), + g_pui8DFUConfigDescriptor +}; + +const tConfigSection g_sDFUInterfaceSection = +{ + sizeof(g_pui8DFUInterface), + g_pui8DFUInterface +}; + +const tConfigSection g_sDFUFunctionalDescSection = +{ + sizeof(g_pui8DFUFunctionalDesc), + g_pui8DFUFunctionalDesc +}; + +//***************************************************************************** +// +// 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(g_psDFUSections[0])) + +//***************************************************************************** +// +// The header for the single configuration we support. This is the root of +// the data structure that defines all the bits and pieces that are pulled +// together to generate the configuration descriptor. +// +//***************************************************************************** +tConfigHeader g_sDFUConfigHeader = +{ + NUM_DFU_SECTIONS, + g_psDFUSections +}; + +//***************************************************************************** +// +// Configuration Descriptor. +// +//***************************************************************************** +const tConfigHeader * const g_ppsDFUConfigDescriptors[] = +{ + &g_sDFUConfigHeader +}; + +//***************************************************************************** +// +// Forward references for device handler callbacks +// +//***************************************************************************** +static void HandleGetDescriptor(void *pvDFUInstance, tUSBRequest *psUSBRequest); +static void HandleRequest(void *pvDFUInstance, tUSBRequest *psUSBRequest); +static void HandleDevice(void *pvDFUInstance, uint32_t ui32Request, + void *pvRequestData); + +//***************************************************************************** +// +// The device information structure for the USB DFU devices. +// +//***************************************************************************** +static const tCustomHandlers g_sDFUHandlers = +{ + // + // GetDescriptor + // + HandleGetDescriptor, + + // + // RequestHandler + // + HandleRequest, + + // + // InterfaceChange + // + 0, + + // + // ConfigChange + // + 0, + + // + // DataReceived + // + 0, + + // + // DataSentCallback + // + 0, + + // + // ResetHandler + // + 0, + + // + // SuspendHandler + // + 0, + + // + // + + // + // ResumeHandler + // + 0, + + // + // DisconnectHandler + // + 0, + + // + // EndpointHandler + // + 0, + + // + // Device handler. + // + HandleDevice, +}; + +//***************************************************************************** +// +// 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 *pvDFUInstance, uint32_t ui32Request, void *pvRequestData) +{ + tDFUInstance *psInst; + uint8_t *pui8Data; + + // + // Get a pointer to the DFU device instance data pointer + // + psInst = &((tUSBDDFUDevice *)pvDFUInstance)->sPrivateData; + + // + // Get a byte pointer to the data. + // + pui8Data = (uint8_t *)pvRequestData; + + // + // Which request event have we been passed? + // + switch(ui32Request) + { + // + // This was an interface change event. + // + case USB_EVENT_COMP_IFACE_CHANGE: + { + // + // Save the change to the interface number. + // + psInst->ui8Interface = pui8Data[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 pvDFUInstance is the instance data for this request. +// \param psUSBRequest 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 *pvDFUInstance, tUSBRequest *psUSBRequest) +{ + uint32_t ui32Size; + + ASSERT(pvDFUInstance != 0); + + // + // Which type of class descriptor are we being asked for? We only support + // 1 type - the DFU functional descriptor. + // + if(((psUSBRequest->wValue >> 8) == USB_DFU_FUNC_DESCRIPTOR_TYPE) && + ((psUSBRequest->wValue & 0xFF) == 0)) + { + // + // If there is more data to send than the host requested then just + // send the requested amount of data. + // + if((uint16_t)g_pui8DFUFunctionalDesc[0] > psUSBRequest->wLength) + { + ui32Size = (uint32_t)psUSBRequest->wLength; + } + else + { + ui32Size = (uint32_t)g_pui8DFUFunctionalDesc[0]; + } + + // + // Send the data via endpoint 0. + // + USBDCDSendDataEP0(0, g_pui8DFUFunctionalDesc, ui32Size); + } + 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 pvDFUInstance is the instance data for this HID device. +// \param psUSBRequest 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 *pvDFUInstance, tUSBRequest *psUSBRequest) +{ + tDFUInstance *psInst; + tUSBDDFUDevice *psDevice; + + ASSERT(pvDFUInstance != 0); + + // + // Get a pointer to the DFU device structure + // + psDevice = pvDFUInstance; + + // + // Get a pointer to the DFU device instance data pointer + // + psInst = &psDevice->sPrivateData; + + // + // Make sure the request was for this interface. + // + if(psUSBRequest->wIndex != psInst->ui8Interface) + { + return; + } + + // + // Determine the type of request. + // + switch(psUSBRequest->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 ui32Index is the index of the USB controller which is to be +//! initialized for DFU runtime device operation. +//! \param psDFUDevice points to a structure containing parameters customizing +//! the operation of the DFU device. +//! \param psCompEntry is the composite device entry to initialize when +//! creating a composite device. +//! +//! The \e psCompEntry should point to the composite device entry to +//! initialize. This is part of the array that is passed to the +//! USBDCompositeInit() function. +//! +//! \return Returns zero on failure or a non-zero instance value that should be +//! used with the remaining USB DFU APIs. +// +//***************************************************************************** +void * +USBDDFUCompositeInit(uint32_t ui32Index, tUSBDDFUDevice *psDFUDevice, + tCompositeEntry *psCompEntry) +{ + tDFUInstance *psInst; + + // + // Check parameter validity. + // + ASSERT(ui32Index == 0); + ASSERT(psDFUDevice); + ASSERT(psCompEntry != 0); + + // + // Get a pointer to the DFU device instance data pointer + // + psInst = &psDFUDevice->sPrivateData; + + // + // Initialize the composite entry that is used by the composite device + // class. + // + if(psCompEntry != 0) + { + psCompEntry->psDevInfo = &psInst->sDevInfo; + psCompEntry->pvInstance = (void *)psDFUDevice; + } + + // + // Initialize the device information structure. + // + psInst->sDevInfo.psCallbacks = &g_sDFUHandlers; + psInst->sDevInfo.pui8DeviceDescriptor = g_pui8DFUDeviceDescriptor; + psInst->sDevInfo.ppsConfigDescriptors = g_ppsDFUConfigDescriptors; + psInst->sDevInfo.ppui8StringDescriptors = 0; + psInst->sDevInfo.ui32NumStringDescriptors = 0; + + psInst->ui32USBBase = USB0_BASE; + psInst->bConnected = false; + psInst->ui8Interface = 0; + + // + // Initialize the device info structure for the DFU device. + // + USBDCDDeviceInfoInit(0, &psInst->sDevInfo); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psDFUDevice); +} + +//***************************************************************************** +// +//! Shuts down the DFU device. +//! +//! \param pvDFUInstance 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 pvDFUInstance instance should not me used in +//! any other calls. +//! +//! \return None. +// +//***************************************************************************** +void +USBDDFUCompositeTerm(void *pvDFUInstance) +{ + tDFUInstance *psInst; + + ASSERT(pvDFUInstance); + + // + // Get a pointer to our instance data. + // + psInst = &((tUSBDDFUDevice *)pvDFUInstance)->sPrivateData; + + // + // Terminate the requested instance. + // + USBDCDTerm(0); + + psInst->ui32USBBase = 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))(*(uint32_t *)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..c344207 --- /dev/null +++ b/usblib/device/usbddfu-rt.h @@ -0,0 +1,184 @@ +//***************************************************************************** +// +// usbddfu-rt.h - Definitions used by runtime DFU class devices. +// +// Copyright (c) 2010-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#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 +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// This is the size of the g_pui8DFUInterface array in bytes. +// +//***************************************************************************** +#define DFUINTERFACE_SIZE (9) + +//***************************************************************************** +// +// This is the size of the g_pui8DFUFunctionalDesc array in bytes. +// +//***************************************************************************** +#define DFUFUNCTIONALDESC_SIZE (9) + +//***************************************************************************** +// +//! 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 ui32Size parameter. +// +//***************************************************************************** +#define COMPOSITE_DDFU_SIZE (DFUINTERFACE_SIZE + DFUFUNCTIONALDESC_SIZE) + +//***************************************************************************** +// +//! 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 included in the +// sPrivateData field in the tUSBDDFUDevice structure passed in the +// USBDDFUCompositeInit() function. +// +//***************************************************************************** +typedef struct +{ + // + // Base address for the USB controller. + // + uint32_t ui32USBBase; + + // + // The device info to interact with the lower level DCD code. + // + tDeviceInfo sDevInfo; + + // + // The DFU class interface number, this is modified in composite devices. + // + uint8_t ui8Interface; + + // + // The connection status of the device. + // + bool 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. + // + const tUSBCallback pfnCallback; + + // + //! A client-supplied pointer which will be sent as the first + //! parameter in all calls made to the pfnCallback function. + // + void * const pvCBData; + + // + //! The private instance data for this device class. 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 sPrivateData; +} +tUSBDDFUDevice; + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void *USBDDFUCompositeInit(uint32_t ui32Index, + tUSBDDFUDevice *psDFUDevice, + tCompositeEntry *psCompEntry); +extern void USBDDFUCompositeTerm(void *pvDFUInstance); +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..94e4cce --- /dev/null +++ b/usblib/device/usbdenum.c @@ -0,0 +1,3190 @@ +//***************************************************************************** +// +// usbenum.c - Enumeration code to handle all endpoint zero traffic. +// +// Copyright (c) 2007-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_ints.h" +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "inc/hw_sysctl.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/usblibpriv.h" +#include "usblib/usbulpi.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbdevicepriv.h" +#include "usblib/usblibpriv.h" + +//***************************************************************************** +// +// External prototypes. +// +//***************************************************************************** +extern tUSBMode g_iUSBMode; + +//***************************************************************************** +// +// Local functions prototypes. +// +//***************************************************************************** +static void USBDGetStatus(void *pvInstance, tUSBRequest *psUSBRequest); +static void USBDClearFeature(void *pvInstance, tUSBRequest *psUSBRequest); +static void USBDSetFeature(void *pvInstance, tUSBRequest *psUSBRequest); +static void USBDSetAddress(void *pvInstance, tUSBRequest *psUSBRequest); +static void USBDGetDescriptor(void *pvInstance, tUSBRequest *psUSBRequest); +static void USBDSetDescriptor(void *pvInstance, tUSBRequest *psUSBRequest); +static void USBDGetConfiguration(void *pvInstance, + tUSBRequest *psUSBRequest); +static void USBDSetConfiguration(void *pvInstance, + tUSBRequest *psUSBRequest); +static void USBDGetInterface(void *pvInstance, tUSBRequest *psUSBRequest); +static void USBDSetInterface(void *pvInstance, tUSBRequest *psUSBRequest); +static void USBDSyncFrame(void *pvInstance, tUSBRequest *psUSBRequest); +static void USBDEP0StateTx(uint32_t ui32Index); +static void USBDEP0StateTxConfig(uint32_t ui32Index); +static int32_t USBDStringIndexFromRequest(uint16_t ui16Lang, + uint16_t ui16Index); + +//***************************************************************************** +// +//! \addtogroup device_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// Indices into the ppui8Halt 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.ui32DevAddress 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 LPM states. +// +//***************************************************************************** +#define USBLIB_LPM_STATE_DISABLED 0x00000000 +#define USBLIB_LPM_STATE_AWAKE 0x00000001 +#define USBLIB_LPM_STATE_SLEEP 0x00000002 + +//***************************************************************************** +// +// The buffer for reading data coming into EP0 +// +//***************************************************************************** +static uint8_t g_pui8DataBufferIn[EP0_MAX_PACKET_SIZE]; + +//***************************************************************************** +// +// This is 480000000/60000000 or a PLL Divide of 8. +// +//***************************************************************************** +static uint32_t g_ui32PLLDiv = 8; + +//***************************************************************************** +// +// Holds the ULPI configuration. +// +//***************************************************************************** +static uint32_t g_ui32ULPISupport; + +//***************************************************************************** +// +// This is the instance data for the USB controller itself and not a USB +// device class. +// +//***************************************************************************** +tDCDInstance g_psDCDInst[1]; + +//***************************************************************************** +// +// This is the currently active class in use by USBLib. There is only one +// of these per USB controller and no device has more than one controller. +// +//***************************************************************************** +tDeviceInfo *g_ppsDevInfo[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 an instance of the tDeviceInfo structure. +//! +//! \param ui32Index is the index of the USB controller which is to be +//! initialized. +//! \param psDeviceInfo is a pointer to the tDeviceInfo structure that needs +//! to be initialized. +//! +//! This function must be called by a USB device class +//! instance to initialize the basic tDeviceInfo required for all USB device +//! class modules. This is typically called in the initialization routine for +//! USB device class. For example in usbdaudio.c that supports USB device +//! audio classes, this function is called in the USBDAudioCompositeInit() +//! function which is used for both composite and non-composites instances of +//! the USB audio class. +//! +//! \note This function should not be called directly by applications. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDDeviceInfoInit(uint32_t ui32Index, tDeviceInfo *psDeviceInfo) +{ + // + // Check the arguments. + // + ASSERT(ui32Index == 0); + ASSERT(psDeviceInfo != 0); + + // + // Save the USB interrupt number. + // + g_psDCDInst[0].ui32IntNum = INT_USB0_TM4C123; + + // + // These devices have a different USB interrupt number. + // + if(CLASS_IS_TM4C129) + { + g_psDCDInst[0].ui32IntNum = INT_USB0_TM4C129; + } + + // + // Disable LPM support by default. + // + g_psDCDInst[0].ui32LPMState = 0; + + // + // Initialize a couple of fields in the device state structure. + // + g_psDCDInst[0].ui32Configuration = DEFAULT_CONFIG_ID; + g_psDCDInst[0].ui32DefaultConfiguration = DEFAULT_CONFIG_ID; + + g_psDCDInst[0].iEP0State = eUSBStateIdle; + + // + // Default to the state where remote wake up is disabled. + // + g_psDCDInst[0].ui8Status = 0; + g_psDCDInst[0].bRemoteWakeup = false; + + // + // Determine the self- or bus-powered state based on the flags the + // user provided. + // + g_psDCDInst[0].bPwrSrcSet = false; +} + +//***************************************************************************** +// +//! Initialize the USB library device control driver for a given hardware +//! controller. +//! +//! \param ui32Index 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. +//! \param pvDCDCBData is the callback data for any device callbacks. +//! +//! This function must be called by a device class which wishes to operate +//! as a USB device and is not typically called by an application. This +//! function 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 \e pvDCDCBData contains a pointer to data that is returned +//! with the DCD calls back to the function in the psDevice->psCallbacks() +//! functions. +//! +//! The device information structure passed in \e psDevice must remain +//! unchanged between this call and any matching call to USBDCDTerm() because +//! it is not copied by the USB library. +//! +//! The USBStackModeSet() function can be called with eUSBModeForceDevice 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. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDInit(uint32_t ui32Index, tDeviceInfo *psDevice, void *pvDCDCBData) +{ + const tConfigHeader *psHdr; + const tConfigDescriptor *psDesc; + + // + // Check the arguments. + // + ASSERT(ui32Index == 0); + ASSERT(psDevice != 0); + + g_ppsDevInfo[0] = psDevice; + g_psDCDInst[0].pvCBData = pvDCDCBData; + + // + // Initialize the Device Info structure for a USB device instance. + // + USBDCDDeviceInfoInit(ui32Index, psDevice); + + // + // Should not call this if the stack is in host mode. + // + ASSERT(g_iUSBMode != eUSBModeHost); + ASSERT(g_iUSBMode != eUSBModeForceHost); + + // + // Default to device mode if no mode was set. + // + if(g_iUSBMode == eUSBModeNone) + { + g_iUSBMode = eUSBModeDevice; + } + + // + // Only do hardware update if the stack is in not in OTG mode. + // + if(g_iUSBMode != eUSBModeOTG) + { + // + // 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(); + + // + // Set the PLL to USB clock divider. + // + USBClockEnable(USB0_BASE, g_ui32PLLDiv, USB_CLOCK_INTERNAL); + + // + // Configure ULPI support. + // + if(g_ui32ULPISupport != USBLIB_FEATURE_ULPI_NONE) + { + USBULPIEnable(USB0_BASE); + + if(g_ui32ULPISupport & USBLIB_FEATURE_ULPI_HS) + { + ULPIConfigSet(USB0_BASE, ULPI_CFG_HS); + } + else + { + ULPIConfigSet(USB0_BASE, ULPI_CFG_FS); + } + } + else + { + USBULPIDisable(USB0_BASE); + } + + // + // Force device mode if requested. + // + if(g_iUSBMode == eUSBModeForceDevice) + { + MAP_USBDevMode(USB0_BASE); + } + else if(g_iUSBMode == eUSBModeDevice) + { + // + // To run in active device mode the OTG signals must be active. + // This allows disconnect to be detected by the controller. + // + MAP_USBOTGMode(USB0_BASE); + } + + // + // In all other cases, set the mode to device this function should not + // be called in OTG mode. + // + g_iUSBMode = eUSBModeDevice; + + // + // Enable or disable LPM functionality. + // + if(g_psDCDInst[0].ui32Features & USBLIB_FEATURE_LPM_EN) + { + // + // Enable full LPM support and all LPM related interrupts. + // USB_INTLPM_ERROR is not enabled since there is no response to + // this interrupt. + // + USBDevLPMConfig(USB0_BASE, USB_DEV_LPM_EN); + USBLPMIntEnable(USB0_BASE, USB_INTLPM_RESUME | USB_INTLPM_ERROR | + USB_INTLPM_ACK | USB_INTLPM_NYET); + USBDevLPMEnable(USB0_BASE); + + // + // Awake by default. + // + g_psDCDInst[0].ui32LPMState = USBLIB_LPM_STATE_AWAKE; + } + else + { + USBDevLPMDisable(USB0_BASE); + USBDevLPMConfig(USB0_BASE, USB_DEV_LPM_NONE); + g_psDCDInst[0].ui32LPMState = USBLIB_LPM_STATE_DISABLED; + } + } + + // + // Initialize the USB DMA interface. + // + g_psDCDInst[0].psDMAInstance = USBLibDMAInit(0); + + // + // Initialize the USB tick module. + // + InternalUSBTickInit(); + + // + // Get a pointer to the default configuration descriptor. + // + psHdr = psDevice->ppsConfigDescriptors[ + g_psDCDInst[0].ui32DefaultConfiguration - 1]; + psDesc = (const tConfigDescriptor *)(psHdr->psSections[0]->pui8Data); + + if((psDesc->bmAttributes & USB_CONF_ATTR_PWR_M) == USB_CONF_ATTR_SELF_PWR) + { + g_psDCDInst[0].ui8Status |= USB_STATUS_SELF_PWR; + } + else + { + g_psDCDInst[0].ui8Status &= ~USB_STATUS_SELF_PWR; + } + + // + // Only do hardware update if the stack is not in OTG mode. + // + if(g_iUSBMode != eUSBModeOTG) + { + // + // 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); + + // + // Attach the device using the soft connect. + // + MAP_USBDevConnect(USB0_BASE); + + // + // Enable the USB interrupt. + // + OS_INT_ENABLE(g_psDCDInst[0].ui32IntNum); + } +} + +//***************************************************************************** +// +//! Free the USB library device control driver for a given hardware controller. +//! +//! \param ui32Index 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(uint32_t ui32Index) +{ + // + // Check the arguments. + // + ASSERT(ui32Index == 0); + + // + // Disable the USB interrupts. + // + OS_INT_DISABLE(g_psDCDInst[0].ui32IntNum); + + // + // Reset the tick handlers so that they can be reconfigured when and if + // USBDCDInit() is called. + // + InternalUSBTickReset(); + + // + // No active device. + // + g_ppsDevInfo[0] = 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 ui32Index is the index of the USB controller from which the data +//! is being requested. +//! \param pui8Data is a pointer to the buffer to fill with data from the USB +//! host. +//! \param ui32Size 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, +//! psCallbacks->pfnDataReceived() in the tDeviceInfo structure +//! must 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(uint32_t ui32Index, uint8_t *pui8Data, uint32_t ui32Size) +{ + ASSERT(ui32Index == 0); + + // + // Enter the RX state on end point 0. + // + g_psDCDInst[0].iEP0State = eUSBStateRx; + + // + // Save the pointer to the data. + // + g_psDCDInst[0].pui8EP0Data = pui8Data; + + // + // Location to save the current number of bytes received. + // + g_psDCDInst[0].ui32OUTDataSize = ui32Size; + + // + // Bytes remaining to be received. + // + g_psDCDInst[0].ui32EP0DataRemain = ui32Size; +} + +//***************************************************************************** +// +//! This function requests transfer of data to the host on endpoint zero. +//! +//! \param ui32Index is the index of the USB controller which is to be used to +//! send the data. +//! \param pui8Data is a pointer to the buffer to send via endpoint zero. +//! \param ui32Size 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, +//! psCallbacks->pfnDataSent in the tDeviceInfo structure must +//! contain a valid function pointer. This callback could be used to free up +//! the buffer passed into this function in the \e pui8Data parameter. The +//! contents of the \e pui8Data buffer must remain unchanged until the +//! pfnDataSent callback is received. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDSendDataEP0(uint32_t ui32Index, uint8_t *pui8Data, uint32_t ui32Size) +{ + ASSERT(ui32Index == 0); + + // + // Return the externally provided device descriptor. + // + g_psDCDInst[0].pui8EP0Data = pui8Data; + + // + // The size of the device descriptor is in the first byte. + // + g_psDCDInst[0].ui32EP0DataRemain = ui32Size; + + // + // Save the total size of the data sent. + // + g_psDCDInst[0].ui32OUTDataSize = ui32Size; + + // + // Now in the transmit data state. + // + USBDEP0StateTx(0); +} + +//***************************************************************************** +// +//! This function sets the default configuration for the device. +//! +//! \param ui32Index is the index of the USB controller whose default +//! configuration is to be set. +//! \param ui32DefaultConfig 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 +//! ppsConfigDescriptors array for the device. +//! +//! If this function is not called, the USB library will return the first +//! configuration in the ppsConfigDescriptors 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, ppsConfigDescriptors, +//! 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(uint32_t ui32Index, uint32_t ui32DefaultConfig) +{ + ASSERT(ui32Index == 0); + + g_psDCDInst[0].ui32DefaultConfiguration = ui32DefaultConfig; +} + +//***************************************************************************** +// +//! This function generates a stall condition on endpoint zero. +//! +//! \param ui32Index 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(uint32_t ui32Index) +{ + ASSERT(ui32Index == 0); + + // + // Stall the endpoint in question. + // + MAP_USBDevEndpointStall(USB0_BASE, USB_EP_0, USB_EP_DEV_OUT); + + // + // Enter the stalled state. + // + g_psDCDInst[0].iEP0State = eUSBStateStall; +} +#ifndef DEPRECATED + +//***************************************************************************** +// +//! Reports the device power status (bus- or self-powered) to the library. +//! +//! \param ui32Index is the index of the USB controller whose device power +//! status is being reported. +//! \param ui8Power 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(uint32_t ui32Index, uint8_t ui8Power) +{ + // + // Check for valid parameters. + // + ASSERT((ui8Power == USB_STATUS_BUS_PWR) || + (ui8Power == USB_STATUS_SELF_PWR)); + ASSERT(ui32Index == 0); + + // + // Update the device status with the new power status flag. + // + g_psDCDInst[0].bPwrSrcSet = true; + g_psDCDInst[0].ui8Status &= ~USB_STATUS_PWR_M; + g_psDCDInst[0].ui8Status |= ui8Power; +} +#endif + +//***************************************************************************** +// +//! This function is used to enable/disable features of the USB library. +//! +//! \param ui32Index is the index of the USB controller whose device power +//! status is being reported. +//! \param ui32Feature indicates which feature is being changed. +//! \param pvFeature holds the data that controls the feature request. +//! +//! Applications can change the support levels of some USB library features by +//! calling this function to enable/disable certain features. This function +//! should normally be called before class initialization functions since the +//! settings need to be in place before enumeration starts. This allows the +//! USB library to properly respond to all enumeration requests. The +//! \e ui32Feature value is one of the \b USBLIB_FEATURE_* defines which +//! controls the type of request being made. The \e pvFeature is a feature +//! specific data structure that is determined by the value passed in the +//! \e ui32Feature parameter. +//! +//! \return Returns \b true if the feature was successfully changed and returns +//! \b false if the feature was not able to be changed or is not supported. +// +//***************************************************************************** +bool +USBDCDFeatureSet(uint32_t ui32Index, uint32_t ui32Feature, void *pvFeature) +{ + bool bRetCode; + tLPMFeature *psLPMFeature; + + bRetCode = true; + + switch(ui32Feature) + { + case USBLIB_FEATURE_LPM: + { + // + // Save the LPM setting. + // + psLPMFeature = (tLPMFeature *)pvFeature; + + if(psLPMFeature->ui32Features & USBLIB_FEATURE_LPM_EN) + { + g_psDCDInst[0].ui32Features |= USBLIB_FEATURE_LPM_EN; + } + else + { + g_psDCDInst[0].ui32Features &= ~USBLIB_FEATURE_LPM_EN; + } + + break; + } + case USBLIB_FEATURE_USBPLL: + { + // + // If the PLL rate is not evenly divisible by 60MHz then + // do not set it. + // + if((*(uint32_t *)pvFeature % 60000000) != 0) + { + bRetCode = false; + } + else + { + // + // Save the new PLL rate. + // + g_ui32PLLDiv = (*(uint32_t *)pvFeature / 60000000); + } + break; + } + case USBLIB_FEATURE_USBULPI: + { + // + // Save the ULPI support level. + // + g_ui32ULPISupport = *(uint32_t *)pvFeature; + + break; + } + case USBLIB_FEATURE_POWER: + { + // + // Update the device status with the new power status flag. + // + g_psDCDInst[0].bPwrSrcSet = true; + g_psDCDInst[0].ui8Status &= ~USBLIB_FEATURE_POWER_SELF; + g_psDCDInst[0].ui8Status |= (uint8_t)(*(uint32_t *)pvFeature); + + break; + } + default: + { + bRetCode = false; + break; + } + } + return(bRetCode); +} + +//***************************************************************************** +// +//! Requests an LPM remote wake up to resume communication when in an LPM sleep +//! state. +//! +//! \param ui32Index is the index of the USB controller that will request +//! a bus wake up. +//! +//! When the host controller puts the device into an LPM sleep state, the +//! device can call this function to initiate LPM remote wake up signaling to +//! the host. If the remote wake up feature has been enabled by the host, this +//! will cause the host to respond to the LPM remote wake request and resume +//! normal operation. If the host has disabled remote wake up, \b false is +//! returned to indicate that the wake up request was not successful. +//! +//! \return Returns \b true if the remote wake up request has been sent or +//!\b false if LPM remote wake up is disabled. +// +//***************************************************************************** +bool +USBDCDRemoteWakeLPM(uint32_t ui32Index) +{ + if(USBLPMRemoteWakeEnabled(USB0_BASE)) + { + USBDevLPMRemoteWake(USB0_BASE); + return(true); + } + return(false); +} + +//***************************************************************************** +// +//! Requests a remote wake up to resume communication when in suspended state. +//! +//! \param ui32Index 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. +// +//***************************************************************************** +bool +USBDCDRemoteWakeupRequest(uint32_t ui32Index) +{ + // + // Check for parameter validity. + // + ASSERT(ui32Index == 0); + + // + // Is remote wake up signaling currently enabled? + // + if(g_psDCDInst[0].ui8Status & 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_psDCDInst[0].bRemoteWakeup) + { + // + // No - we are not in the middle of a wake up sequence so start + // one here. + // + g_psDCDInst[0].ui8RemoteWakeupCount = 0; + g_psDCDInst[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(tDCDInstance *psDevInst) +{ + if(g_psDCDInst[0].bRemoteWakeup) + { + // + // Increment the millisecond counter we use to time the resume + // signaling. + // + g_psDCDInst[0].ui8RemoteWakeupCount++; + + // + // Have we reached the 10mS mark? If so, we need to turn the signaling + // off again. + // + if(g_psDCDInst[0].ui8RemoteWakeupCount == 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 does not 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_psDCDInst[0].ui8RemoteWakeupCount == REMOTE_WAKEUP_READY_MS) + { + // + // We are now finished with the remote wake up signaling. + // + g_psDCDInst[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_ppsDevInfo[0]->psCallbacks->pfnResumeHandler) + { + g_ppsDevInfo[0]->psCallbacks->pfnResumeHandler( + g_psDCDInst[0].pvCBData); + } + } + } +} + +//***************************************************************************** +// +// 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(uint32_t ui32Index) +{ + uint32_t ui32Size; + tUSBRequest *psRequest; + + // + // Cast the buffer to a request structure. + // + psRequest = (tUSBRequest *)g_pui8DataBufferIn; + + // + // Set the buffer size. + // + ui32Size = EP0_MAX_PACKET_SIZE; + + // + // Get the data from the USB controller end point 0. + // + MAP_USBEndpointDataGet(USB0_BASE, USB_EP_0, g_pui8DataBufferIn, + &ui32Size); + + // + // If there was a null setup packet then just return. + // + if(!ui32Size) + { + return; + } + + // + // See if this is a standard request or not. + // + if((psRequest->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_ppsDevInfo[0]->psCallbacks->pfnRequestHandler) + { + g_ppsDevInfo[0]->psCallbacks->pfnRequestHandler( + g_psDCDInst[0].pvCBData, + psRequest); + } + else + { + // + // If there is no handler then stall this request. + // + USBDCDStallEP0(0); + } + } + else + { + // + // Assure that the jump table is not out of bounds. + // + if((psRequest->bRequest < + (sizeof(g_psUSBDStdRequests) / sizeof(tStdRequest))) && + (g_psUSBDStdRequests[psRequest->bRequest] != 0)) + { + // + // Jump table to the appropriate handler. + // + g_psUSBDStdRequests[psRequest->bRequest](&g_psDCDInst[0], + psRequest); + } + 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 eUSBStateIdle, \b eUSBStateTx, \b eUSBStateRx, +// \b eUSBStateStall, and \b eUSBStateStatus. In the \b eUSBStateIdle +// 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 eUSBStateTx, \b eUSBStateRx, or \b eUSBStateStall depending on the +// command. If the controller enters the \b eUSBStateTx or \b eUSBStateRx +// then once all data has been sent or received, it must pass through the +// \b eUSBStateStatus state to allow the host to acknowledge completion of +// the request. The \b eUSBStateStall is entered from \b eUSBStateIdle in +// the event that the USB request was not valid. Both the \b eUSBStateStall +// and \b eUSBStateStatus are transitional states that return to the +// \b eUSBStateIdle state. +// +// \return None. +// +// eUSBStateIdle -*--> eUSBStateTx -*-> eUSBStateStatus -*->eUSBStateIdle +// | | | +// |--> eUSBStateRx | +// | | +// |--> eUSBStateStall ---------->-------- +// +// ---------------------------------------------------------------- +// | Current State | State 0 | State 1 | +// | --------------------|-------------------|---------------------- +// | eUSBStateIdle | eUSBStateTx/RX | eUSBStateStall | +// | eUSBStateTx | eUSBStateStatus | | +// | eUSBStateRx | eUSBStateStatus | | +// | eUSBStateStatus | eUSBStateIdle | | +// | eUSBStateStall | eUSBStateIdle | | +// ---------------------------------------------------------------- +// +//***************************************************************************** +void +USBDeviceEnumHandler(tDCDInstance *pDevInstance) +{ + uint32_t ui32EPStatus, ui32DataSize; + + // + // Get the end point 0 status. + // + ui32EPStatus = MAP_USBEndpointStatus(USB0_BASE, USB_EP_0); + + switch(pDevInstance->iEP0State) + { + // + // Handle the status state, this is a transitory state from + // eUSBStateTx or eUSBStateRx back to eUSBStateIdle. + // + case eUSBStateStatus: + { + // + // Just go back to the idle state. + // + pDevInstance->iEP0State = eUSBStateIdle; + + // + // If there is a pending address change then set the address. + // + if(pDevInstance->ui32DevAddress & DEV_ADDR_PENDING) + { + // + // Clear the pending address change and set the address. + // + pDevInstance->ui32DevAddress &= ~DEV_ADDR_PENDING; + MAP_USBDevAddrSet(USB0_BASE, pDevInstance->ui32DevAddress); + } + + // + // If a new packet is already pending, we need to read it + // and handle whatever request it contains. + // + if(ui32EPStatus & 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 eUSBStateIdle: + { + // + // Is there a packet waiting for us? + // + if(ui32EPStatus & 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 eUSBStateTx: + { + USBDEP0StateTx(0); + break; + } + + // + // We are still in the middle of sending the configuration descriptor + // so handle this in the EP0StateTxConfig() function. + // + case eUSBStateTxConfig: + { + USBDEP0StateTxConfig(0); + break; + } + + // + // Handle the receive state for commands that are receiving data on + // endpoint zero. + // + case eUSBStateRx: + { + // + // Set the number of bytes to get out of this next packet. + // + if(pDevInstance->ui32EP0DataRemain > EP0_MAX_PACKET_SIZE) + { + // + // Don't send more than EP0_MAX_PACKET_SIZE bytes. + // + ui32DataSize = EP0_MAX_PACKET_SIZE; + } + else + { + // + // There was space so send the remaining bytes. + // + ui32DataSize = pDevInstance->ui32EP0DataRemain; + } + + // + // Get the data from the USB controller end point 0. + // + MAP_USBEndpointDataGet(USB0_BASE, USB_EP_0, + pDevInstance->pui8EP0Data, &ui32DataSize); + + // + // 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->ui32EP0DataRemain < EP0_MAX_PACKET_SIZE) + { + // + // Return to the idle state. + // + pDevInstance->iEP0State = eUSBStateStatus; + + // + // If there is a receive callback then call it. + // + if((g_ppsDevInfo[0]->psCallbacks->pfnDataReceived) && + (pDevInstance->ui32OUTDataSize != 0)) + { + // + // Call the custom receive handler to handle the data + // that was received. + // + g_ppsDevInfo[0]->psCallbacks->pfnDataReceived( + g_psDCDInst[0].pvCBData, + pDevInstance->ui32OUTDataSize); + + // + // Indicate that there is no longer any data being waited + // on. + // + pDevInstance->ui32OUTDataSize = 0; + } + + // + // 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); + } + 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->pui8EP0Data += ui32DataSize; + + // + // Decrement the number of bytes that are being waited on. + // + pDevInstance->ui32EP0DataRemain -= ui32DataSize; + + break; + } + // + // The device stalled endpoint zero so check if the stall needs to be + // cleared once it has been successfully sent. + // + case eUSBStateStall: + { + // + // If we sent a stall then acknowledge this interrupt. + // + if(ui32EPStatus & 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->iEP0State = eUSBStateIdle; + + } + break; + } + // + // Halt on an unknown state, but only in DEBUG mode builds. + // + default: + { + ASSERT(0); + break; + } + } +} + +//***************************************************************************** +// +// 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(tDCDInstance *pDevInstance) +{ + uint32_t ui32Loop; + + // + // Disable remote wake up signaling (as per USB 2.0 spec 9.1.1.6). + // + pDevInstance->ui8Status &= ~USB_STATUS_REMOTE_WAKE; + pDevInstance->bRemoteWakeup = false; + + // + // Call the device dependent code to indicate a bus reset has occurred. + // + if(g_ppsDevInfo[0]->psCallbacks->pfnResetHandler) + { + g_ppsDevInfo[0]->psCallbacks->pfnResetHandler(g_psDCDInst[0].pvCBData); + } + + // + // Reset the default configuration identifier and alternate function + // selections. + // + pDevInstance->ui32Configuration = pDevInstance->ui32DefaultConfiguration; + + for(ui32Loop = 0; ui32Loop < USB_MAX_INTERFACES_PER_DEVICE; ui32Loop++) + { + pDevInstance->pui8AltSetting[ui32Loop] = (uint8_t)0; + } +} + +//***************************************************************************** +// +// This function handles the GET_STATUS standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param psUSBRequest 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 psUSBRequest 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 *psUSBRequest) +{ + uint16_t ui16Data, ui16Index; + uint32_t ui32Dir; + tDCDInstance *psUSBControl; + + ASSERT(psUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDCDInstance *)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(psUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M) + { + // + // This was a Device Status request. + // + case USB_RTYPE_DEVICE: + { + // + // Return the current status for the device. + // + ui16Data = (uint16_t)psUSBControl->ui8Status; + + break; + } + + // + // This was a Interface status request. + // + case USB_RTYPE_INTERFACE: + { + // + // Interface status always returns 0. + // + ui16Data = (uint16_t)0; + + break; + } + + // + // This was an endpoint status request. + // + case USB_RTYPE_ENDPOINT: + { + // + // Which endpoint are we dealing with? + // + ui16Index = psUSBRequest->wIndex & USB_REQ_EP_NUM_M; + + // + // Check if this was a valid endpoint request. + // + if((ui16Index == 0) || (ui16Index >= NUM_USB_EP)) + { + USBDCDStallEP0(0); + return; + } + else + { + // + // Are we dealing with an IN or OUT endpoint? + // + ui32Dir = ((psUSBRequest->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. + // + ui16Data = + (uint16_t)psUSBControl->ppui8Halt[ui32Dir][ui16Index - 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->ui32EP0DataRemain = 2; + psUSBControl->pui8EP0Data = (uint8_t *)&ui16Data; + + // + // Send the response. + // + USBDEP0StateTx(0); +} + +//***************************************************************************** +// +// This function handles the CLEAR_FEATURE standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param psUSBRequest holds the options for the Clear Feature USB request. +// +// This function handles device or endpoint clear feature requests. The +// \e psUSBRequest 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 *psUSBRequest) +{ + tDCDInstance *psUSBControl; + uint32_t ui32Dir; + uint16_t ui16Index; + + ASSERT(psUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDCDInstance *)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(psUSBRequest->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 & psUSBRequest->wValue) + { + // + // Clear the remote wake up state. + // + psUSBControl->ui8Status &= ~USB_STATUS_REMOTE_WAKE; + } + else + { + USBDCDStallEP0(0); + } + break; + } + + // + // This is a clear feature request at the endpoint level. + // + case USB_RTYPE_ENDPOINT: + { + // + // Which endpoint are we dealing with? + // + ui16Index = psUSBRequest->wIndex & USB_REQ_EP_NUM_M; + + // + // Not a valid endpoint. + // + if((ui16Index == 0) || (ui16Index > NUM_USB_EP)) + { + USBDCDStallEP0(0); + } + else + { + // + // Only the halt feature is supported. + // + if(USB_FEATURE_EP_HALT == psUSBRequest->wValue) + { + // + // Are we dealing with an IN or OUT endpoint? + // + ui32Dir = ((psUSBRequest->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->ppui8Halt[ui32Dir][ui16Index - 1] = 0; + + if(ui32Dir == HALT_EP_IN) + { + MAP_USBDevEndpointStallClear(USB0_BASE, + IndexToUSBEP(ui16Index), + USB_EP_DEV_IN); + } + else + { + MAP_USBDevEndpointStallClear(USB0_BASE, + IndexToUSBEP(ui16Index), + 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 psUSBRequest holds the feature in the wValue field of the USB +// request. +// +// This function handles device or endpoint set feature requests. The +// \e psUSBRequest 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 *psUSBRequest) +{ + tDCDInstance *psUSBControl; + uint16_t ui16Index; + uint32_t ui32Dir; + + ASSERT(psUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDCDInstance *)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(psUSBRequest->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 & psUSBRequest->wValue) + { + // + // Set the remote wake up state. + // + psUSBControl->ui8Status |= USB_STATUS_REMOTE_WAKE; + } + else + { + USBDCDStallEP0(0); + } + break; + } + + // + // This is a set feature request at the endpoint level. + // + case USB_RTYPE_ENDPOINT: + { + // + // Which endpoint are we dealing with? + // + ui16Index = psUSBRequest->wIndex & USB_REQ_EP_NUM_M; + + // + // Not a valid endpoint? + // + if((ui16Index == 0) || (ui16Index >= NUM_USB_EP)) + { + USBDCDStallEP0(0); + } + else + { + // + // Only the Halt feature can be set. + // + if(USB_FEATURE_EP_HALT == psUSBRequest->wValue) + { + // + // Are we dealing with an IN or OUT endpoint? + // + ui32Dir = ((psUSBRequest->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->ppui8Halt[ui32Dir][ui16Index - 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 psUSBRequest 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 *psUSBRequest) +{ + tDCDInstance *psUSBControl; + + ASSERT(psUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDCDInstance *)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->ui32DevAddress = psUSBRequest->wValue | DEV_ADDR_PENDING; + + // + // Transition directly to the status state since there is no data phase + // for this request. + // + psUSBControl->iEP0State = eUSBStateStatus; +} + +//***************************************************************************** +// +// This function handles the GET_DESCRIPTOR standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param psUSBRequest 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->pui8DeviceDescriptor 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->psCallbacks->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 *psUSBRequest) +{ + bool bConfig; + tDCDInstance *psUSBControl; + tDeviceInfo *psDevice; + const tConfigHeader *psConfig; + const tDeviceDescriptor *psDeviceDesc; + uint8_t ui8Index; + int32_t i32Index; + + ASSERT(psUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDCDInstance *)pvInstance; + psDevice = g_ppsDevInfo[0]; + + // + // 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(psUSBRequest->wValue >> 8) + { + // + // This request was for a device descriptor. + // + case USB_DTYPE_DEVICE: + { + // + // Return the externally provided device descriptor. + // + psUSBControl->pui8EP0Data = + (uint8_t *)psDevice->pui8DeviceDescriptor; + + // + // The size of the device descriptor is in the first byte. + // + psUSBControl->ui32EP0DataRemain = + psDevice->pui8DeviceDescriptor[0]; + + break; + } + + // + // This request was for a configuration descriptor. + // + case USB_DTYPE_CONFIGURATION: + { + // + // Which configuration are we being asked for? + // + ui8Index = (uint8_t)(psUSBRequest->wValue & 0xFF); + + // + // Is this valid? + // + psDeviceDesc = + (const tDeviceDescriptor *)psDevice->pui8DeviceDescriptor; + + if(ui8Index >= psDeviceDesc->bNumConfigurations) + { + // + // This is an invalid configuration index. Stall EP0 to + // indicate a request error. + // + USBDCDStallEP0(0); + psUSBControl->pui8EP0Data = 0; + psUSBControl->ui32EP0DataRemain = 0; + } + else + { + // + // Return the externally specified configuration descriptor. + // + psConfig = psDevice->ppsConfigDescriptors[ui8Index]; + + // + // Start by sending data from the beginning of the first + // descriptor. + // + psUSBControl->ui8ConfigSection = 0; + psUSBControl->ui16SectionOffset = 0; + psUSBControl->pui8EP0Data = + (uint8_t *)psConfig->psSections[0]->pui8Data; + + // + // Determine the total size of the configuration descriptor + // by counting the sizes of the sections comprising it. + // + psUSBControl->ui32EP0DataRemain = + USBDCDConfigDescGetSize(psConfig); + + // + // Remember that we need to send the configuration descriptor + // and which descriptor we need to send. + // + psUSBControl->ui8ConfigIndex = ui8Index; + + bConfig = true; + } + break; + } + + // + // This request was for a string descriptor. + // + case USB_DTYPE_STRING: + { + // + // Determine the correct descriptor index based on the requested + // language ID and index. + // + i32Index = USBDStringIndexFromRequest(psUSBRequest->wIndex, + psUSBRequest->wValue & 0xFF); + + // + // If the mapping function returned -1 then stall the request to + // indicate that the request was not valid. + // + if(i32Index == -1) + { + USBDCDStallEP0(0); + break; + } + + // + // Return the externally specified configuration descriptor. + // + psUSBControl->pui8EP0Data = + (uint8_t *)psDevice->ppui8StringDescriptors[i32Index]; + + // + // The total size of a string descriptor is in byte 0. + // + psUSBControl->ui32EP0DataRemain = + psDevice->ppui8StringDescriptors[i32Index][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->psCallbacks->pfnGetDescriptor) + { + psDevice->psCallbacks->pfnGetDescriptor(g_psDCDInst[0].pvCBData, + psUSBRequest); + } + 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->pui8EP0Data) + { + // + // If there is more data to send than is requested then just + // send the requested amount of data. + // + if(psUSBControl->ui32EP0DataRemain > psUSBRequest->wLength) + { + psUSBControl->ui32EP0DataRemain = psUSBRequest->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 ui16Lang is the requested string language ID. +// \param ui16Index 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 int32_t +USBDStringIndexFromRequest(uint16_t ui16Lang, uint16_t ui16Index) +{ + tString0Descriptor *pLang; + uint32_t ui32NumLangs, ui32NumStringi16PerLang, ui32Loop; + + // + // Make sure we have a string table at all. + // + if((g_ppsDevInfo[0] == 0) || + (g_ppsDevInfo[0]->ppui8StringDescriptors == 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(ui16Index == 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). + // + ui32NumLangs = + (g_ppsDevInfo[0]->ppui8StringDescriptors[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. + // + ui32NumStringi16PerLang = + ((g_ppsDevInfo[0]->ui32NumStringDescriptors - 1) /ui32NumLangs); + + // + // 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 + (ui32NumStringi16PerLang * ui32NumLangs)) != + g_ppsDevInfo[0]->ui32NumStringDescriptors) + { + 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_ppsDevInfo[0]->ppui8StringDescriptors[0]); + + // + // Look through the supported languages looking for the one we were asked + // for. + // + for(ui32Loop = 0; ui32Loop < ui32NumLangs; ui32Loop++) + { + // + // Have we found the requested language? + // + if(pLang->wLANGID[ui32Loop] == ui16Lang) + { + // + // Yes - calculate the index of the descriptor to send. + // + return((ui32NumStringi16PerLang * ui32Loop) + ui16Index); + } + } + + // + // 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 psUSBRequest 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 *psUSBRequest) +{ + // + // 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 psUSBRequest 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 *psUSBRequest) +{ + uint8_t ui8Value; + tDCDInstance *psUSBControl; + + ASSERT(psUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDCDInstance *)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->ui32DevAddress & DEV_ADDR_PENDING) + { + ui8Value = 0; + } + else + { + ui8Value = (uint8_t)psUSBControl->ui32Configuration; + } + + psUSBControl->ui32EP0DataRemain = 1; + psUSBControl->pui8EP0Data = &ui8Value; + + // + // 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 psUSBRequest 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 psUSBRequest. 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->psCallbacks-> +// it will be called so that the application can respond to a change in +// configuration. +// +// \return None. +// +//***************************************************************************** +static void +USBDSetConfiguration(void *pvInstance, tUSBRequest *psUSBRequest) +{ + tDCDInstance *psUSBControl; + tDeviceInfo *psDevice; + const tConfigHeader *psHdr; + const tConfigDescriptor *psDesc; + + + // + // Create the device information pointer. + // + psUSBControl = (tDCDInstance *)pvInstance; + psDevice = g_ppsDevInfo[0]; + + // + // 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(psUSBRequest->wValue > psDevice->pui8DeviceDescriptor[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->ui32Configuration = psUSBRequest->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->ui32Configuration) + { + // + // Get a pointer to the configuration descriptor. This will always + // be the first section in the current configuration. + // + psHdr = psDevice->ppsConfigDescriptors[psUSBRequest->wValue - 1]; + psDesc = + (const tConfigDescriptor *)(psHdr->psSections[0]->pui8Data); + + // + // 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->ui8Status |= USB_STATUS_SELF_PWR; + } + else + { + psUSBControl->ui8Status &= ~USB_STATUS_SELF_PWR; + } + } + + // + // Configure endpoints for the new configuration. + // + USBDeviceConfig(psUSBControl, + psDevice->ppsConfigDescriptors[psUSBRequest->wValue - 1]); + } + + // + // If there is a configuration change callback then call it. + // + if(psDevice->psCallbacks->pfnConfigChange) + { + psDevice->psCallbacks->pfnConfigChange(g_psDCDInst[0].pvCBData, + psUSBControl->ui32Configuration); + } + } +} + +//***************************************************************************** +// +// This function handles the GET_INTERFACE standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param psUSBRequest 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 *psUSBRequest) +{ + uint8_t ui8Value; + tDCDInstance *psUSBControl; + + ASSERT(psUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDCDInstance *)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->ui32DevAddress & DEV_ADDR_PENDING) + { + ui8Value = (uint8_t)0; + } + else + { + // + // Is the interface number valid? + // + if(psUSBRequest->wIndex < USB_MAX_INTERFACES_PER_DEVICE) + { + // + // Read the current alternate setting for the required interface. + // + ui8Value = psUSBControl->pui8AltSetting[psUSBRequest->wIndex]; + } + else + { + // + // An invalid interface number was specified. + // + USBDCDStallEP0(0); + return; + } + } + + // + // Send the single byte response. + // + psUSBControl->ui32EP0DataRemain = 1; + psUSBControl->pui8EP0Data = &ui8Value; + + // + // 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 psUSBRequest 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->psCallbacks->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 *psUSBRequest) +{ + const tConfigHeader *psConfig; + tInterfaceDescriptor *psInterface; + uint32_t ui32Loop, ui32Section, ui32NumInterfaces; + uint8_t ui8Interface; + bool bRetcode; + tDCDInstance *psUSBControl; + tDeviceInfo *psDevice; + + ASSERT(psUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDCDInstance *)pvInstance; + psDevice = g_ppsDevInfo[0]; + + // + // 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->ppsConfigDescriptors[psUSBControl->ui32Configuration - 1]; + + // + // How many interfaces are included in the descriptor? + // + ui32NumInterfaces = USBDCDConfigDescGetNum(psConfig, USB_DTYPE_INTERFACE); + + // + // Find the interface descriptor for the supplied interface and alternate + // setting numbers. + // + for(ui32Loop = 0; ui32Loop < ui32NumInterfaces; ui32Loop++) + { + // + // Get the next interface descriptor in the configuration descriptor. + // + psInterface = USBDCDConfigGetInterface(psConfig, ui32Loop, + USB_DESC_ANY, &ui32Section); + + // + // Is this the required interface with the correct alternate setting? + // + if(psInterface && + (psInterface->bInterfaceNumber == psUSBRequest->wIndex) && + (psInterface->bAlternateSetting == psUSBRequest->wValue)) + { + ui8Interface = psInterface->bInterfaceNumber; + + // + // Make sure we don't write outside the bounds of the + // pui8AltSetting array (in a debug build, anyway, since this + // indicates an error in the device descriptor). + // + ASSERT(ui8Interface < USB_MAX_INTERFACES_PER_DEVICE); + + // + // This is the correct interface descriptor so save the + // setting. + // + psUSBControl->pui8AltSetting[ui8Interface] = + psInterface->bAlternateSetting; + + // + // Reconfigure the endpoints to match the requirements of the + // new alternate setting for the interface. + // + bRetcode = USBDeviceConfigAlternate(psUSBControl, psConfig, + ui8Interface, + psInterface->bAlternateSetting); + + // + // If there is a callback then notify the application of the + // change to the alternate interface. + // + if(bRetcode && psDevice->psCallbacks->pfnInterfaceChange) + { + psDevice->psCallbacks->pfnInterfaceChange( + g_psDCDInst[0].pvCBData, + psUSBRequest->wIndex, + psUSBRequest->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 psUSBRequest 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 *psUSBRequest) +{ + // + // 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 ui32Index is the index of the USB controller which is to be +// initialized. +// +// \return None. +// +//***************************************************************************** +static void +USBDEP0StateTx(uint32_t ui32Index) +{ + uint32_t ui32NumBytes; + uint8_t *pui8Data; + + ASSERT(ui32Index == 0); + + // + // In the TX state on endpoint zero. + // + g_psDCDInst[0].iEP0State = eUSBStateTx; + + // + // Set the number of bytes to send this iteration. + // + ui32NumBytes = g_psDCDInst[0].ui32EP0DataRemain; + + // + // Limit individual transfers to 64 bytes. + // + if(ui32NumBytes > EP0_MAX_PACKET_SIZE) + { + ui32NumBytes = EP0_MAX_PACKET_SIZE; + } + + // + // Save the pointer so that it can be passed to the USBEndpointDataPut() + // function. + // + pui8Data = (uint8_t *)g_psDCDInst[0].pui8EP0Data; + + // + // Advance the data pointer and counter to the next data to be sent. + // + g_psDCDInst[0].ui32EP0DataRemain -= ui32NumBytes; + g_psDCDInst[0].pui8EP0Data += ui32NumBytes; + + // + // Put the data in the correct FIFO. + // + MAP_USBEndpointDataPut(USB0_BASE, USB_EP_0, pui8Data, ui32NumBytes); + + // + // If this is exactly 64 then don't set the last packet yet. + // + if(ui32NumBytes == 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_psDCDInst[0].iEP0State = eUSBStateStatus; + + // + // 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_ppsDevInfo[0]->psCallbacks->pfnDataSent) && + (g_psDCDInst[0].ui32OUTDataSize != 0)) + { + // + // Call the custom handler. + // + g_ppsDevInfo[0]->psCallbacks->pfnDataSent( + g_psDCDInst[0].pvCBData, + g_psDCDInst[0].ui32OUTDataSize); + + // + // There is no longer any data pending to be sent. + // + g_psDCDInst[0].ui32OUTDataSize = 0; + } + } +} + +//***************************************************************************** +// +// This internal function handles sending the configuration descriptor on +// endpoint zero. +// +// \param ui32Index is the index of the USB controller. +// +// \return None. +// +//***************************************************************************** +static void +USBDEP0StateTxConfig(uint32_t ui32Index) +{ + uint32_t ui32NumBytes, ui32SecBytes, ui32ToSend; + uint8_t *pui8Data; + tConfigDescriptor sConfDesc; + const tConfigHeader *psConfig; + const tConfigSection *psSection; + + ASSERT(ui32Index == 0); + + // + // In the TX state on endpoint zero. + // + g_psDCDInst[0].iEP0State = eUSBStateTxConfig; + + // + // Find the current configuration descriptor definition. + // + psConfig = g_ppsDevInfo[0]->ppsConfigDescriptors[ + g_psDCDInst[0].ui8ConfigIndex]; + + // + // Set the number of bytes to send this iteration. + // + ui32NumBytes = g_psDCDInst[0].ui32EP0DataRemain; + + // + // Limit individual transfers to 64 bytes. + // + if(ui32NumBytes > EP0_MAX_PACKET_SIZE) + { + ui32NumBytes = 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.ui32EP0DataRemain. + // + if((g_psDCDInst[0].ui16SectionOffset == 0) && + (g_psDCDInst[0].ui8ConfigSection == 0)) + { + // + // Copy the USB configuration descriptor from the beginning of the + // first section of the current configuration. + // + sConfDesc = *(tConfigDescriptor *)g_psDCDInst[0].pui8EP0Data; + + // + // Update the total size. + // + sConfDesc.wTotalLength = (uint16_t)USBDCDConfigDescGetSize(psConfig); + + // + // Write the descriptor to the USB FIFO. + // + ui32ToSend = (ui32NumBytes < sizeof(tConfigDescriptor)) ? ui32NumBytes: + sizeof(tConfigDescriptor); + MAP_USBEndpointDataPut(USB0_BASE, USB_EP_0, (uint8_t *)&sConfDesc, + ui32ToSend); + + // + // Did we reach the end of the first section? + // + if(psConfig->psSections[0]->ui16Size == ui32ToSend) + { + // + // Update our tracking indices to point to the start of the next + // section. + // + g_psDCDInst[0].ui16SectionOffset = 0; + g_psDCDInst[0].ui8ConfigSection = 1; + } + else + { + // + // Note that we have sent the first few bytes of the descriptor. + // + g_psDCDInst[0].ui16SectionOffset = (uint8_t)ui32ToSend; + } + + // + // How many bytes do we have remaining to send on this iteration? + // + ui32ToSend = ui32NumBytes - ui32ToSend; + } + else + { + // + // Set the number of bytes we still have to send on this call. + // + ui32ToSend = ui32NumBytes; + } + + // + // Add the relevant number of bytes to the USB FIFO + // + while(ui32ToSend) + { + // + // Get a pointer to the current configuration section. + // + psSection = psConfig->psSections[g_psDCDInst[0].ui8ConfigSection]; + + // + // Calculate bytes are available in the current configuration section. + // + ui32SecBytes = (uint32_t)(psSection->ui16Size - + g_psDCDInst[0].ui16SectionOffset); + + // + // Save the pointer so that it can be passed to the + // USBEndpointDataPut() function. + // + pui8Data = (uint8_t *)psSection->pui8Data + + g_psDCDInst[0].ui16SectionOffset; + + // + // Are there more bytes in this section that we still have to send? + // + if(ui32SecBytes > ui32ToSend) + { + // + // Yes - send only the remaining bytes in the transfer. + // + ui32SecBytes = ui32ToSend; + } + + // + // Put the data in the correct FIFO. + // + MAP_USBEndpointDataPut(USB0_BASE, USB_EP_0, pui8Data, ui32SecBytes); + + // + // Fix up our pointers for the next iteration. + // + ui32ToSend -= ui32SecBytes; + g_psDCDInst[0].ui16SectionOffset += (uint8_t)ui32SecBytes; + + // + // Have we reached the end of a section? + // + if(g_psDCDInst[0].ui16SectionOffset == psSection->ui16Size) + { + // + // Yes - move to the next one. + // + g_psDCDInst[0].ui8ConfigSection++; + g_psDCDInst[0].ui16SectionOffset = 0; + } + } + + // + // Fix up the number of bytes remaining to be sent and the start pointer. + // + g_psDCDInst[0].ui32EP0DataRemain -= ui32NumBytes; + + // + // If we ran out of bytes in the configuration section, bail and just + // send out what we have. + // + if(psConfig->ui8NumSections <= g_psDCDInst[0].ui8ConfigSection) + { + g_psDCDInst[0].ui32EP0DataRemain = 0; + } + + // + // If there is no more data don't keep looking or ui8ConfigSection might + // overrun the available space. + // + if(g_psDCDInst[0].ui32EP0DataRemain != 0) + { + pui8Data =(uint8_t *) + psConfig->psSections[g_psDCDInst[0].ui8ConfigSection]->pui8Data; + ui32ToSend = g_psDCDInst[0].ui16SectionOffset; + g_psDCDInst[0].pui8EP0Data = (pui8Data + ui32ToSend); + } + + // + // If this is exactly 64 then don't set the last packet yet. + // + if(ui32NumBytes == 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_ppsDevInfo[0]->psCallbacks->pfnDataSent) && + (g_psDCDInst[0].ui32OUTDataSize != 0)) + { + // + // Call the custom handler. + // + g_ppsDevInfo[0]->psCallbacks->pfnDataSent(g_psDCDInst[0].pvCBData, + g_psDCDInst[0].ui32OUTDataSize); + + // + // There is no longer any data pending to be sent. + // + g_psDCDInst[0].ui32OUTDataSize = 0; + } + + // + // Now go to the status state and wait for the transmit to complete. + // + g_psDCDInst[0].iEP0State = eUSBStateStatus; + } +} + +//***************************************************************************** +// +// The internal USB device interrupt handler. +// +// \param ui32Index is the USB controller associated with this interrupt. +// \param ui32Status 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(uint32_t ui32Index, uint32_t ui32Status) +{ + static uint32_t ui32SOFDivide = 0; + void *pvInstance; + uint32_t ui32DMAIntStatus; + uint32_t ui32LPMStatus; + + // + // If device initialization has not been performed then just disconnect + // from the USB bus and return from the handler. + // + if(g_ppsDevInfo[0] == 0) + { + MAP_USBDevDisconnect(USB0_BASE); + return; + } + + pvInstance = g_psDCDInst[0].pvCBData; + + // + // Received a reset from the host. + // + if(ui32Status & USB_INTCTRL_RESET) + { + USBDeviceEnumResetHandler(&g_psDCDInst[0]); + } + + // + // Suspend was signaled on the bus. + // + if(ui32Status & USB_INTCTRL_SUSPEND) + { + // + // Call the SuspendHandler() if it was specified. + // + if(g_ppsDevInfo[0]->psCallbacks->pfnSuspendHandler) + { + g_ppsDevInfo[0]->psCallbacks->pfnSuspendHandler(pvInstance); + } + } + + // + // Resume was signaled on the bus. + // + if(ui32Status & USB_INTCTRL_RESUME) + { + // + // Call the ResumeHandler() if it was specified. + // + if(g_ppsDevInfo[0]->psCallbacks->pfnResumeHandler) + { + g_ppsDevInfo[0]->psCallbacks->pfnResumeHandler(pvInstance); + } + } + + // + // USB device was disconnected. + // + if(ui32Status & USB_INTCTRL_DISCONNECT) + { + // + // Call the DisconnectHandler() if it was specified. + // + if(g_ppsDevInfo[0]->psCallbacks->pfnDisconnectHandler) + { + g_ppsDevInfo[0]->psCallbacks->pfnDisconnectHandler(pvInstance); + } + } + + // + // Start of Frame was received. + // + if(ui32Status & USB_INTCTRL_SOF) + { + // + // Increment the global Start of Frame counter. + // + g_ui32USBSOFCount++; + + // + // Increment our SOF divider. + // + ui32SOFDivide++; + + // + // Handle resume signaling if required. + // + USBDeviceResumeTickHandler(&g_psDCDInst[0]); + + // + // Have we counted enough SOFs to allow us to call the tick function? + // + if(ui32SOFDivide == USB_SOF_TICK_DIVIDE) + { + // + // Yes - reset the divider and call the SOF tick handler. + // + ui32SOFDivide = 0; + InternalUSBStartOfFrameTick(USB_SOF_TICK_DIVIDE); + } + } + + // + // Handle LPM interrupts. + // + ui32LPMStatus = USBLPMIntStatus(USB0_BASE); + + // + // The host LPM resume request has been acknowledged, allow the device + // class to handle the sleep state. + // + if((g_psDCDInst[0].ui32LPMState == USBLIB_LPM_STATE_SLEEP) && + ((ui32LPMStatus & (USB_INTLPM_ACK | USB_INTLPM_RESUME)) == + USB_INTLPM_RESUME)) + { + // + // Notify the class of the wake from LPM L1. + // + if(g_ppsDevInfo[0]->psCallbacks->pfnDeviceHandler) + { + g_ppsDevInfo[0]->psCallbacks->pfnDeviceHandler(pvInstance, + USB_EVENT_LPM_RESUME, + (void *)0); + } + + // + // Now back in the awake state. + // + g_psDCDInst[0].ui32LPMState = USBLIB_LPM_STATE_AWAKE; + + // + // Enable receiving of LPM packet. + // + USBDevLPMEnable(USB0_BASE); + } + // + // The host LPM sleep request has been acknowledged, allow the device + // class to handle the sleep state. + // + else if((g_psDCDInst[0].ui32LPMState == USBLIB_LPM_STATE_AWAKE) && + ((ui32LPMStatus & (USB_INTLPM_ACK | USB_INTLPM_RESUME)) == + USB_INTLPM_ACK)) + { + if(g_ppsDevInfo[0]->psCallbacks->pfnDeviceHandler) + { + g_ppsDevInfo[0]->psCallbacks->pfnDeviceHandler(pvInstance, + USB_EVENT_LPM_SLEEP, + (void *)0); + } + + // + // Now back in the sleep state. + // + g_psDCDInst[0].ui32LPMState = USBLIB_LPM_STATE_SLEEP; + } + else if(ui32LPMStatus & USB_INTLPM_NYET) + { + // + // The device has held off the sleep state because LPM + // responses are disabled. + // + if(g_ppsDevInfo[0]->psCallbacks->pfnDeviceHandler) + { + g_ppsDevInfo[0]->psCallbacks->pfnDeviceHandler(pvInstance, + USB_EVENT_LPM_ERROR, + (void *)0); + } + } + + // + // Get the controller interrupt status. + // + ui32Status = MAP_USBIntStatusEndpoint(USB0_BASE); + + // + // Handle end point 0 interrupts. + // + if(ui32Status & USB_INTEP_0) + { + USBDeviceEnumHandler(&g_psDCDInst[0]); + ui32Status &= ~USB_INTEP_0; + } + + // + // Check to see if any DMA transfers are pending + // + ui32DMAIntStatus = USBLibDMAIntStatus(g_psDCDInst[0].psDMAInstance); + + if(ui32DMAIntStatus) + { + // + // Handle any DMA interrupt processing. + // + USBLibDMAIntHandler(g_psDCDInst[0].psDMAInstance, ui32DMAIntStatus); + } + + // + // 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((g_ppsDevInfo[0]->psCallbacks->pfnEndpointHandler) && + ((ui32Status != 0) || (ui32DMAIntStatus != 0))) + { + g_ppsDevInfo[0]->psCallbacks->pfnEndpointHandler(pvInstance, ui32Status); + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/device/usbdevice.h b/usblib/device/usbdevice.h new file mode 100644 index 0000000..d4df6a6 --- /dev/null +++ b/usblib/device/usbdevice.h @@ -0,0 +1,232 @@ +//***************************************************************************** +// +// usbdevice.h - types and definitions used during USB enumeration. +// +// Copyright (c) 2008-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#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 \e 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" + +//***************************************************************************** +// +//! 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. + // + const tCustomHandlers * psCallbacks; + + // + //! A pointer to the device descriptor for this device. + // + const uint8_t *pui8DeviceDescriptor; + + // + //! 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, \e pui8DeviceDescriptor. + // + const tConfigHeader * const *ppsConfigDescriptors; + + // + //! 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 uint8_t * const *ppui8StringDescriptors; + + // + //! The total number of descriptors provided in the ppStringDescriptors + //! array. + // + uint32_t ui32NumStringDescriptors; +}; + +//***************************************************************************** +// +//! 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 *psDevInfo; + + // + //! This is the instance data for the device structure. + // + void *pvInstance; + + // + //! A per-device workspace used by the composite device. + // + uint32_t ui32DeviceWorkspace; +} +tCompositeEntry; + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Public APIs offered by the USB library device control driver. +// +//***************************************************************************** +extern void USBDCDInit(uint32_t ui32Index, tDeviceInfo *psDevice, + void *pvDCDCBData); +extern void USBDCDTerm(uint32_t ui32Index); +extern void USBDCDStallEP0(uint32_t ui32Index); +extern void USBDCDRequestDataEP0(uint32_t ui32Index, uint8_t *pui8Data, + uint32_t ui32Size); +extern void USBDCDSendDataEP0(uint32_t ui32Index, uint8_t *pui8Data, + uint32_t ui32Size); +extern void USBDCDSetDefaultConfiguration(uint32_t ui32Index, + uint32_t ui32DefaultConfig); +extern uint32_t USBDCDConfigDescGetSize(const tConfigHeader *psConfig); +extern uint32_t USBDCDConfigDescGetNum(const tConfigHeader *psConfig, + uint32_t ui32Type); +extern tDescriptorHeader *USBDCDConfigDescGet(const tConfigHeader *psConfig, + uint32_t ui32Type, + uint32_t ui32Index, + uint32_t *pui32Section); +extern uint32_t + USBDCDConfigGetNumAlternateInterfaces(const tConfigHeader *psConfig, + uint8_t ui8InterfaceNumber); +extern tInterfaceDescriptor * + USBDCDConfigGetInterface(const tConfigHeader *psConfig, + uint32_t ui32Index, uint32_t ui32AltCfg, + uint32_t *pui32Section); +extern tEndpointDescriptor * + USBDCDConfigGetInterfaceEndpoint(const tConfigHeader *psConfig, + uint32_t ui32InterfaceNumber, + uint32_t ui32AltCfg, + uint32_t ui32Index); +extern bool USBDCDRemoteWakeupRequest(uint32_t ui32Index); +extern bool USBDCDFeatureSet(uint32_t ui32Index, uint32_t ui32Feature, + void *pvFeature); +extern bool USBDCDRemoteWakeLPM(uint32_t ui32Index); + +//***************************************************************************** +// +// Device mode interrupt handler for controller index 0. +// +//***************************************************************************** +extern void USB0DeviceIntHandler(void); + +//***************************************************************************** +// +// The following APIs are deprecated. +// +//***************************************************************************** +#ifndef DEPRECATED + +// +// Use USBDCDFeatureSet() or USBHCDFeatureSet() with \b USBLIB_FEATURE_POWER +// configuration option. +// +extern void USBDCDPowerStatusSet(uint32_t ui32Index, uint8_t ui8Power); +#endif + +//***************************************************************************** +// +// 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..4eef230 --- /dev/null +++ b/usblib/device/usbdevicepriv.h @@ -0,0 +1,248 @@ +//***************************************************************************** +// +// 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-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#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. + // + eUSBStateIdle, + + // + // The USB device is sending data back to the host due to an IN request. + // + eUSBStateTx, + + // + // The USB device is sending the configuration descriptor back to the host + // due to an IN request. + // + eUSBStateTxConfig, + + // + // The USB device is receiving data from the host due to an OUT + // request from the host. + // + eUSBStateRx, + + // + // 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. + // + eUSBStateStatus, + + // + // This endpoint has signaled a stall condition and is waiting for the + // stall to be acknowledged by the host controller. + // + eUSBStateStall +} +tEP0State; + +typedef struct tDeviceInfo tDeviceInfo; + +//***************************************************************************** +// +// The USB controller device information. +// +//***************************************************************************** +typedef struct +{ + // + // The current state of endpoint zero. + // + volatile tEP0State iEP0State; + + // + // The devices current address, this also has a change pending bit in the + // MSB of this value specified by DEV_ADDR_PENDING. + // + volatile uint32_t ui32DevAddress; + + // + // This holds the current active configuration for this device. + // + uint32_t ui32Configuration; + + // + // This holds the configuration id that will take effect after a reset. + // + uint32_t ui32DefaultConfiguration; + + // + // This holds the current alternate interface for this device. + // + uint8_t pui8AltSetting[USB_MAX_INTERFACES_PER_DEVICE]; + + // + // This is the pointer to the current data being sent out or received + // on endpoint zero. + // + uint8_t *pui8EP0Data; + + // + // This is the number of bytes that remain to be sent from or received + // into the g_sUSBDeviceState.pui8EP0Data data buffer. + // + volatile uint32_t ui32EP0DataRemain; + + // + // The amount of data being sent/received due to a custom request. + // + uint32_t ui32OUTDataSize; + + // + // Holds the current device status. + // + uint8_t ui8Status; + + // + // Holds the endpoint status for the HALT condition. This array is sized + // to hold halt status for all IN and OUT endpoints. + // + uint8_t ppui8Halt[2][USBLIB_NUM_EP - 1]; + + // + // Holds the configuration descriptor section number currently being sent + // to the host. + // + uint8_t ui8ConfigSection; + + // + // Holds the offset within the configuration descriptor section currently + // being sent to the host. + // + uint16_t ui16SectionOffset; + + // + // Holds the index of the configuration that we are currently sending back + // to the host. + // + uint8_t ui8ConfigIndex; + + // + // 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. + // + bool bPwrSrcSet; + + // + // This flag indicates whether or not remote wake up signaling is in + // progress. + // + bool bRemoteWakeup; + + // + // During remote wake up signaling, this counter is used to track the + // number of milliseconds since the signaling was initiated. + // + uint8_t ui8RemoteWakeupCount; + + // + // The DMA instance information for this USB controller. + // + tUSBDMAInstance *psDMAInstance; + + // + // The interrupt number for this instance. + // + uint32_t ui32IntNum; + + // + // Pointer to the device supplied call back data. + // + void *pvCBData; + + // + // This holds the state of the LPM support for the device. + // + uint32_t ui32LPMState; + + // + // Device feature flags. + // + uint32_t ui32Features; +} +tDCDInstance; + +extern tDCDInstance g_psDCDInst[]; +extern tDeviceInfo *g_ppsDevInfo[]; + +//***************************************************************************** +// +// Device enumeration functions provided by device/usbenum.c and called from +// the interrupt handler in device/usbhandler.c +// +//***************************************************************************** +extern bool USBDeviceConfig(tDCDInstance *psDevInst, + const tConfigHeader *psConfig); +extern bool USBDeviceConfigAlternate(tDCDInstance *psDevInst, + const tConfigHeader *psConfig, + uint8_t ui8InterfaceNum, + uint8_t ui8AlternateSetting); + +extern void USBDCDDeviceInfoInit(uint32_t ui32Index, tDeviceInfo *psDevice); + +//***************************************************************************** +// +// Macro access function to device information. +// +//***************************************************************************** +#define DCDGetDMAInstance(psDevInfo) (&(psDevInfo->psDCDInst->sDMAInstance)) + +//***************************************************************************** +// +// 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..751cc44 --- /dev/null +++ b/usblib/device/usbdhandler.c @@ -0,0 +1,85 @@ +//***************************************************************************** +// +// usbhandler.c - General USB handling routines. +// +// Copyright (c) 2007-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.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) +{ + uint32_t ui32Status; + + // + // Get the controller interrupt status. + // + ui32Status = MAP_USBIntStatusControl(USB0_BASE); + + // + // Call the internal handler. + // + USBDeviceIntHandlerInternal(0, ui32Status); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/device/usbdhid.c b/usblib/device/usbdhid.c new file mode 100644 index 0000000..c708f62 --- /dev/null +++ b/usblib/device/usbdhid.c @@ -0,0 +1,2502 @@ +//***************************************************************************** +// +// usbdhid.c - USB HID device class driver. +// +// Copyright (c) 2008-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/usb.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.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 ui16DeferredOpFlags 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->ui16DeferredOpFlags to indicate that the operation is pending. +// +//***************************************************************************** +#define HID_DO_PACKET_RX 5 +#define HID_DO_SEND_IDLE_REPORT 6 + +//***************************************************************************** +// +// 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 + +//***************************************************************************** +// +// Device Descriptor. This is stored in RAM to allow several fields to be +// changed at runtime based on the client's requirements. +// +//***************************************************************************** +uint8_t g_pui8HIDDeviceDescriptor[] = +{ + 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 + USBDHID_MAX_PACKET, // 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. +}; + +//***************************************************************************** +// +// Forward references for device handler callbacks +// +//***************************************************************************** +static void HandleGetDescriptor(void *pvHIDInstance, tUSBRequest *psUSBRequest); +static void HandleRequest(void *pvHIDInstance, tUSBRequest *psUSBRequest); +static void HandleConfigChange(void *pvHIDInstance, uint32_t ui32Info); +static void HandleEP0DataReceived(void *pvHIDInstance, uint32_t ui32Info); +static void HandleEP0DataSent(void *pvHIDInstance, uint32_t ui32Info); +static void HandleReset(void *pvHIDInstance); +static void HandleSuspend(void *pvHIDInstance); +static void HandleResume(void *pvHIDInstance); +static void HandleDisconnect(void *pvHIDInstance); +static void HandleEndpoints(void *pvHIDInstance, uint32_t ui32Status); +static void HandleDevice(void *pvHIDInstance, uint32_t ui32Request, + void *pvRequestData); + +//***************************************************************************** +// +// The device information structure for the USB HID devices. +// +//***************************************************************************** +const tCustomHandlers g_sHIDHandlers = +{ + // + // GetDescriptor + // + HandleGetDescriptor, + + // + // RequestHandler + // + HandleRequest, + + // + // InterfaceChange + // + 0, + + // + // ConfigChange + // + HandleConfigChange, + + // + // DataReceived + // + HandleEP0DataReceived, + + // + // DataSentCallback + // + HandleEP0DataSent, + + // + // ResetHandler + // + HandleReset, + + // + // SuspendHandler + // + HandleSuspend, + + // + // ResumeHandler + // + HandleResume, + + // + // DisconnectHandler + // + HandleDisconnect, + + // + // EndpointHandler + // + HandleEndpoints, + + // + // Device handler. + // + HandleDevice +}; + +//***************************************************************************** +// +// Set or clear deferred operation flags in an "atomic" manner. +// +// \param pui16DeferredOp points to the flags variable which is to be modified. +// \param ui16Bit 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 uint16_t *pui16DeferredOp, uint16_t ui16Bit, + bool bSet) +{ + // + // Set the flag bit to 1 or 0 using a bitband access. + // + HWREGBITH(pui16DeferredOp, ui16Bit) = 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 psHIDDevice points to the HID device structure whose report timer is +// to be cleared. +// \param ui8ReportID 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 psHIDDevice structure. +// +// \return None. +// +//***************************************************************************** +static void +ClearReportTimer(const tUSBDHIDDevice *psHIDDevice, uint8_t ui8ReportID) +{ + uint32_t ui32Loop; + + if(psHIDDevice->ui8NumInputReports > 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(ui32Loop = 0; ui32Loop < psHIDDevice->ui8NumInputReports; + ui32Loop++) + { + if(psHIDDevice->psReportIdle[ui32Loop].ui8ReportID == ui8ReportID) + { + break; + } + } + } + else + { + ui32Loop = 0; + } + + // + // If we drop out of the loop with an index less than ui8NumInputReports, + // we found the relevant report so clear its timer. + // + if(ui32Loop < psHIDDevice->ui8NumInputReports) + { + psHIDDevice->psReportIdle[ui32Loop].ui32TimeSinceReportmS = 0; + } +} + +//***************************************************************************** +// +// This function is called to clear the idle period timers for each input +// report supported by the device. +// +// \param psHIDDevice points to the HID device structure whose timers are to be +// cleared. +// \param ui32TimemS is the elapsed time in milliseconds since the last call +// to this function. +// +// \return None. +// +//***************************************************************************** +static void +ClearIdleTimers(const tUSBDHIDDevice *psHIDDevice) +{ + uint32_t ui32Loop; + + // + // Clear the "time till next report" counters for each input report. + // + for(ui32Loop = 0; ui32Loop < psHIDDevice->ui8NumInputReports; ui32Loop++) + { + psHIDDevice->psReportIdle[ui32Loop].ui16TimeTillNextmS = + psHIDDevice->psReportIdle[ui32Loop].ui8Duration4mS * 4; + } +} + +//***************************************************************************** +// +// This function is called periodically to allow us to process the report idle +// timers. +// +// \param psHIDDevice points to the HID device structure whose timers are to be +// updated. +// \param ui32ElapsedmS indicates the number of milliseconds that have elapsed +// since the last call to this function. +// +// \return None. +// +//***************************************************************************** +static void +ProcessIdleTimers(const tUSBDHIDDevice *psHIDDevice, uint32_t ui32ElapsedmS) +{ + uint32_t ui32Loop, ui32SizeReport; + void *pvReport; + tHIDInstance *psInst; + bool bDeferred; + + // + // Get our instance data pointer + // + psInst = &((tUSBDHIDDevice *)psHIDDevice)->sPrivateData; + + // + // We have not had to defer any report transmissions yet. + // + bDeferred = false; + + // + // Look at each of the input report idle timers in turn. + // + for(ui32Loop = 0; ui32Loop < psHIDDevice->ui8NumInputReports; ui32Loop++) + { + // + // Update the time since the last report was sent. + // + psHIDDevice->psReportIdle[ui32Loop].ui32TimeSinceReportmS += + ui32ElapsedmS; + + // + // Is this timer running? + // + if(psHIDDevice->psReportIdle[ui32Loop].ui8Duration4mS) + { + // + // Yes - is it about to expire? + // + if(psHIDDevice->psReportIdle[ui32Loop].ui16TimeTillNextmS <= + ui32ElapsedmS) + { + // + // The timer is about to expire. Can we send a report right + // now? + // + if((psInst->iHIDTxState == eHIDStateIdle) && + (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. + // + ui32SizeReport = psHIDDevice->pfnRxCallback( + psHIDDevice->pvRxCBData, + USBD_HID_EVENT_IDLE_TIMEOUT, + psHIDDevice->psReportIdle[ui32Loop].ui8ReportID, + &pvReport); + + // + // Schedule the report for transmission. + // + USBDHIDReportWrite((void *)psHIDDevice, pvReport, + ui32SizeReport, true); + + // + // Reload the timer for the next period. + // + psHIDDevice->psReportIdle[ui32Loop].ui16TimeTillNextmS = + psHIDDevice->psReportIdle[ui32Loop].ui8Duration4mS * 4; + } + else + { + // + // We can't send the report straight away so flag it for + // transmission as soon as the previous transmission ends. + // + psHIDDevice->psReportIdle[ui32Loop].ui16TimeTillNextmS = 0; + bDeferred = true; + } + } + else + { + // + // The timer is not about to expire. Update the time till the + // next report transmission. + // + psHIDDevice->psReportIdle[ui32Loop].ui16TimeTillNextmS -= + ui32ElapsedmS; + } + } + } + + // + // If we had to defer transmission of any report, remember this so that we + // will process it as soon as possible. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, HID_DO_SEND_IDLE_REPORT, + bDeferred); +} + +static void +SetIdleTimeout(const tUSBDHIDDevice *psHIDDevice, uint8_t ui8ReportID, + uint8_t ui8Timeout4mS) +{ + uint32_t ui32Loop; + bool 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(ui32Loop = 0; ui32Loop < psHIDDevice->ui8NumInputReports; ui32Loop++) + { + psIdle = &psHIDDevice->psReportIdle[ui32Loop]; + + // + // 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(!ui8ReportID || (ui8ReportID == psIdle->ui8ReportID)) + { + // + // Save the new duration for the idle timer. + // + psIdle->ui8Duration4mS = ui8Timeout4mS; + + // + // Are we enabling the idle timer? If so, fix up the time until it + // needs to fire. + // + if(ui8Timeout4mS) + { + // + // Determine what the timeout is for this report given the time + // since the last report of this type was sent. + // + if(psIdle->ui32TimeSinceReportmS >= + ((uint32_t)ui8Timeout4mS * 4)) + { + psIdle->ui16TimeTillNextmS = 0; + bReportNeeded = true; + } + else + { + psIdle->ui16TimeTillNextmS = + (((uint16_t)ui8Timeout4mS * 4) - + psIdle->ui32TimeSinceReportmS); + } + } + } + } + + // + // 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(psHIDDevice, 0); + } +} + +//***************************************************************************** +// +// Find the idle timeout for a given HID input report. +// +// \param psHIDDevice points to the HID device whose report idle timeout is to +// be found. +// \param ui8ReportID 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 uint32_t +GetIdleTimeout(const tUSBDHIDDevice *psHIDDevice, uint8_t ui8ReportID) +{ + uint32_t ui32Loop; + tHIDReportIdle *psIdle; + + // + // Search through all the input reports looking for ones that fit the + // requirements. + // + for(ui32Loop = 0; ui32Loop < psHIDDevice->ui8NumInputReports; ui32Loop++) + { + psIdle = &psHIDDevice->psReportIdle[ui32Loop]; + + // + // 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(!ui8ReportID || (ui8ReportID == psIdle->ui8ReportID)) + { + // + // 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((uint32_t)psIdle->ui8Duration4mS); + } + } + + // + // If we drop out, the report could not 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 psHIDDevice points to the HID device which is to be searched for the +// required class descriptor. +// \param ui8Type is the type of class descriptor being requested. This will +// be either USB_HID_DTYPE_REPORT or USB_HID_DTYPE_PHYSICAL. +// \param ui32Index 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 uint32_t +FindHIDDescriptor(const tUSBDHIDDevice *psHIDDevice, uint8_t ui8Type, + uint32_t ui32Index, uint32_t *pui32Len) +{ + bool bFoundType; + uint32_t ui32Loop, ui32Count, ui32LastFound; + const tHIDClassDescriptorInfo *psDesc; + + // + // Remember that we have not found any descriptor with a matching type yet. + // + bFoundType = false; + ui32Count = 0; + ui32LastFound = 0; + + // + // Walk through all the class descriptors looking for the one which + // matches the requested index and type. + // + for(ui32Loop = 0; ui32Loop < psHIDDevice->psHIDDescriptor->bNumDescriptors; + ui32Loop++) + { + psDesc = &(psHIDDevice->psHIDDescriptor->sClassDescriptor[ui32Loop]); + if(psDesc->bDescriptorType == ui8Type) + { + // + // We found a descriptor of the correct type. Is this the + // correct index? + // + bFoundType = true; + + // + // Is this the descriptor we are looking for? + // + if(ui32Count == ui32Index) + { + // + // Yes - we found it so return the index and size to the + // caller. + // + *pui32Len = (uint32_t)psDesc->wDescriptorLength; + return(ui32Loop); + } + 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. + // + ui32Count++; + ui32LastFound = ui32Loop; + } + } + } + + // + // If we drop out, we did not find the requested descriptor. Now handle + // the special case of a physical descriptor - if we found any physical + // descriptors, return the last one. + // + if((ui8Type == USB_HID_DTYPE_PHYSICAL) && bFoundType) + { + // + // Get the length of the last descriptor we found. + // + psDesc = + &(psHIDDevice->psHIDDescriptor->sClassDescriptor[ui32LastFound]); + *pui32Len = (uint32_t)psDesc->wDescriptorLength; + + // + // Return the index to the caller. + // + return(ui32LastFound); + } + else + { + // + // We could not 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 psHIDInst points to the HID 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 int16_t packet or a +// packet with no data to indicate the end of the transaction. +// +//***************************************************************************** +static int32_t +ScheduleReportTransmission(tHIDInstance *psHIDInst) +{ + uint32_t ui32NumBytes; + uint8_t *pui8Data; + int32_t i32Retcode; + + // + // Set the number of bytes to send this iteration. + // + ui32NumBytes = (uint32_t)(psHIDInst->ui16InReportSize - + psHIDInst->ui16InReportIndex); + + // + // Limit individual transfers to the maximum packet size for the endpoint. + // + if(ui32NumBytes > USBDHID_MAX_PACKET) + { + ui32NumBytes = USBDHID_MAX_PACKET; + } + + // + // Where are we sending this data from? + // + pui8Data = psHIDInst->pui8InReportData + psHIDInst->ui16InReportIndex; + + // + // Put the data in the correct FIFO. + // + i32Retcode = MAP_USBEndpointDataPut(psHIDInst->ui32USBBase, + psHIDInst->ui8INEndpoint, + pui8Data, ui32NumBytes); + + if(i32Retcode != -1) + { + // + // Update the count and index ready for the next time round. + // + psHIDInst->ui16InReportIndex += ui32NumBytes; + + // + // Send out the current data. + // + i32Retcode = MAP_USBEndpointDataSend(psHIDInst->ui32USBBase, + psHIDInst->ui8INEndpoint, + USB_TRANS_IN); + } + + // + // Tell the caller how we got on. + // + return(i32Retcode); +} + +//***************************************************************************** +// +// Receives notifications related to data received from the host. +// +// \param psHIDDevice is the device instance whose endpoint is to be processed. +// \param ui32Status 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 bool +ProcessDataFromHost(tUSBDHIDDevice *psHIDDevice, uint32_t ui32Status) +{ + uint32_t ui32EPStatus, ui32Size; + tHIDInstance *psInst; + + // + // Get a pointer to our instance data. + // + psInst = &psHIDDevice->sPrivateData; + + // + // Get the endpoint status to see why we were called. + // + ui32EPStatus = MAP_USBEndpointStatus(USB0_BASE, psInst->ui8OUTEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(USB0_BASE, psInst->ui8OUTEndpoint, + ui32EPStatus); + + // + // Has a packet been received? + // + if(ui32EPStatus & 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 does not read + // the packet in the context of the USB_EVENT_RX_AVAILABLE callback, + // the event will be signaled later during tick processing. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, HID_DO_PACKET_RX, + true); + + // + // How big is the packet we have just been sent? + // + ui32Size = MAP_USBEndpointDataAvail(psInst->ui32USBBase, + psInst->ui8OUTEndpoint); + + // + // 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. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USB_EVENT_RX_AVAILABLE, ui32Size, + (void *)0); + } + else + { + // + // No packet was received. Some error must have been reported. Check + // and pass this on to the client if necessary. + // + if(ui32EPStatus & USB_RX_ERROR_FLAGS) + { + // + // This is an error we report to the client so... + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USB_EVENT_ERROR, + (ui32EPStatus & USB_RX_ERROR_FLAGS), + (void *)0); + } + return(false); + } + + return(true); +} + +//***************************************************************************** +// +// Receives notifications related to data sent to the host. +// +// \param psHIDDevice is the device instance whose endpoint is to be processed. +// \param ui32Status 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 bool +ProcessDataToHost(tUSBDHIDDevice *psHIDDevice, uint32_t ui32Status) +{ + tHIDInstance *psInst; + uint32_t ui32EPStatus; + + // + // Get a pointer to our instance data. + // + psInst = &psHIDDevice->sPrivateData; + + // + // Get the endpoint status to see why we were called. + // + ui32EPStatus = MAP_USBEndpointStatus(psInst->ui32USBBase, + psInst->ui8INEndpoint); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(psInst->ui32USBBase, psInst->ui8INEndpoint, + ui32EPStatus); + + // + // 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 ui16InReportIndex has reached the ui16InReportSize value. + // + if(psInst->ui16InReportSize == psInst->ui16InReportIndex) + { + // + // We finished sending the last report so are idle once again. + // + psInst->iHIDTxState = eHIDStateIdle; + + // + // Notify the client that the report transmission completed. + // + psHIDDevice->pfnTxCallback(psHIDDevice->pvTxCBData, + USB_EVENT_TX_COMPLETE, + psInst->ui16InReportSize, (void *)0); + + // + // Do we have any reports to send as a result of idle timer timeouts? + // + if(psInst->ui16DeferredOpFlags & (1 << HID_DO_SEND_IDLE_REPORT)) + { + // + // Yes - send reports for any timers that expired recently. + // + ProcessIdleTimers(psHIDDevice, 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 ui32Status. +// +//***************************************************************************** +static void +HandleEndpoints(void *pvHIDInstance, uint32_t ui32Status) +{ + tUSBDHIDDevice *psHIDInst; + tHIDInstance *psInst; + + ASSERT(pvHIDInstance != 0); + + // + // Determine if the serial device is in single or composite mode because + // the meaning of ui32Index is different in both cases. + // + psHIDInst = (tUSBDHIDDevice *)pvHIDInstance; + psInst = &psHIDInst->sPrivateData; + + // + // Handler for the interrupt OUT data endpoint. + // + if(ui32Status & (0x10000 << USBEPToIndex(psInst->ui8OUTEndpoint))) + { + // + // Data is being sent to us from the host. + // + ProcessDataFromHost(pvHIDInstance, ui32Status); + } + + // + // Handler for the interrupt IN data endpoint. + // + if(ui32Status & (1 << USBEPToIndex(psInst->ui8INEndpoint))) + { + ProcessDataToHost(pvHIDInstance, ui32Status); + } +} + +//***************************************************************************** +// +// Called by the USB stack whenever a configuration change occurs. +// +//***************************************************************************** +static void +HandleConfigChange(void *pvHIDInstance, uint32_t ui32Info) +{ + tHIDInstance *psInst; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvHIDInstance != 0); + + // + // Create the instance pointer. + // + psHIDDevice = pvHIDInstance; + + // + // Get a pointer to our instance data. + // + psInst = &psHIDDevice->sPrivateData; + + // + // Set all our endpoints to idle state. + // + psInst->iHIDRxState = eHIDStateIdle; + psInst->iHIDTxState = eHIDStateIdle; + + // + // 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. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USB_EVENT_CONNECTED, 0, (void *)0); + } + + // + // Clear the idle timers for each input report. + // + ClearIdleTimers(psHIDDevice); + + // + // Remember that we are connected. + // + psInst->bConnected = true; +} + +//***************************************************************************** +// +// Device instance specific handler. +// +//***************************************************************************** +static void +HandleDevice(void *pvHIDInstance, uint32_t ui32Request, void *pvRequestData) +{ + tHIDInstance *psInst; + uint8_t *pui8Data; + tUSBDHIDDevice *psHIDDevice; + + psHIDDevice = (tUSBDHIDDevice *)pvHIDInstance; + + // + // Create the serial instance data. + // + psInst = &((tUSBDHIDDevice *)pvHIDInstance)->sPrivateData; + + // + // Create the int8_t array used by the events supported by the USB CDC + // serial class. + // + pui8Data = (uint8_t *)pvRequestData; + + switch(ui32Request) + { + // + // This was an interface change event. + // + case USB_EVENT_COMP_IFACE_CHANGE: + { + psInst->ui8Interface = pui8Data[1]; + break; + } + + // + // This was an endpoint change event. + // + case USB_EVENT_COMP_EP_CHANGE: + { + // + // Determine if this is an IN or OUT endpoint that has changed. + // + if(pui8Data[0] & USB_EP_DESC_IN) + { + psInst->ui8INEndpoint = IndexToUSBEP((pui8Data[1] & 0x7f)); + } + else + { + // + // Extract the new endpoint number. + // + psInst->ui8OUTEndpoint = IndexToUSBEP(pui8Data[1] & 0x7f); + } + break; + } + case USB_EVENT_LPM_RESUME: + { + if(psHIDDevice->pfnRxCallback) + { + // + // Pass the LPM resume event to the client. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USB_EVENT_LPM_RESUME, 0, (void *)0); + } + break; + } + case USB_EVENT_LPM_SLEEP: + { + if(psHIDDevice->pfnRxCallback) + { + // + // Pass the LPM sleep event to the client. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USB_EVENT_LPM_SLEEP, 0, (void *)0); + } + break; + } + case USB_EVENT_LPM_ERROR: + { + if(psHIDDevice->pfnRxCallback) + { + // + // Pass the LPM error event to the client. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USB_EVENT_LPM_ERROR, 0, (void *)0); + } + break; + } + default: + { + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device is +// disconnected from the host. +// +//***************************************************************************** +static void +HandleDisconnect(void *pvHIDInstance) +{ + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvHIDInstance != 0); + + // + // Create the instance pointer. + // + psHIDDevice = (tUSBDHIDDevice *)pvHIDInstance; + + // + // If we are not currently connected so let the client know we are open + // for business. + // + if(psHIDDevice->sPrivateData.bConnected) + { + // + // Pass the disconnected event to the client. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USB_EVENT_DISCONNECTED, 0, (void *)0); + } + + // + // Remember that we are no longer connected. + // + psHIDDevice->sPrivateData.bConnected = false; +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever a request for a +// non-standard descriptor is received. +// +// \param pvHIDInstance is the instance data for this request. +// \param psUSBRequest 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 *pvHIDInstance, tUSBRequest *psUSBRequest) +{ + uint32_t ui32Size, ui32Desc; + const tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvHIDInstance != 0); + + // + // Which device are we dealing with? + // + psHIDDevice = pvHIDInstance; + + // + // Which type of class descriptor are we being asked for? + // + switch(psUSBRequest->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. + // + ui32Size = 0; + ui32Desc = FindHIDDescriptor(psHIDDevice, + psUSBRequest->wValue >> 8, + psUSBRequest->wValue & 0xFF, + &ui32Size); + + // + // Did we find the descriptor? + // + if(ui32Desc == 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(ui32Size > psUSBRequest->wLength) + { + ui32Size = psUSBRequest->wLength; + } + + // + // Send the data via endpoint 0. + // + USBDCDSendDataEP0(0, + (uint8_t *)psHIDDevice->ppui8ClassDescriptors[ui32Desc], + ui32Size); + + 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? + // + ui32Size = (uint32_t)psHIDDevice->psHIDDescriptor->bLength; + + // + // If there is more data to send than the host requested then just + // send the requested amount of data. + // + if(ui32Size > psUSBRequest->wLength) + { + ui32Size = psUSBRequest->wLength; + } + + // + // Send the data via endpoint 0. + // + USBDCDSendDataEP0(0, (uint8_t *)psHIDDevice->psHIDDescriptor, + ui32Size); + 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 pvHIDInstance is the instance data for this HID device. +// \param psUSBRequest 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 *pvHIDInstance, tUSBRequest *psUSBRequest) +{ + tHIDInstance *psInst; + uint8_t ui8Protocol; + uint32_t ui32Size, ui32Timeout; + uint8_t *pui8Report; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvHIDInstance != 0); + + // + // Which device are we dealing with? + // + psHIDDevice = pvHIDInstance; + + // + // Get a pointer to our instance data. + // + psInst = &psHIDDevice->sPrivateData; + + // + // Make sure the request was for this interface. + // + if(psUSBRequest->wIndex != psInst->ui8Interface) + { + return; + } + + // + // Determine the type of request. + // + switch(psUSBRequest->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->ui16OutReportSize = psUSBRequest->wLength; + psInst->pui8OutReportData = + (uint8_t *)psHIDDevice->pfnRxCallback( + psHIDDevice->pvRxCBData, + USBD_HID_EVENT_GET_REPORT_BUFFER, + psUSBRequest->wValue, + (void *)(uint32_t)(psUSBRequest->wLength)); + + // + // Did the client provide us a buffer? + // + if(!psInst->pui8OutReportData) + { + // + // The application could not 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->iHIDRxState = eHIDStateWaitData; + + // + // Now read the payload of the request. We handle the actual + // operation in the data callback once this data is received. + // + USBDCDRequestDataEP0(0, psInst->pui8OutReportData, + (uint32_t)psUSBRequest->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->ui32USBBase, 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: + { + // + // Get the latest report from the application. + // + ui32Size = psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USBD_HID_EVENT_GET_REPORT, + psUSBRequest->wValue, &pui8Report); + + // + // Need to ACK the data on end point 0 in this case. + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, USB_EP_0, true); + + // + // ..then send back the requested report. + // + psInst->bGetRequestPending = true; + USBDCDSendDataEP0(0, pui8Report, ui32Size); + + 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(psHIDDevice, psUSBRequest->wValue & 0xFF, + (psUSBRequest->wValue >> 8) & 0xFF); + + // + // Need to ACK the data on end point 0 in this case. + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, 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: + { + // + // Determine the timeout for the requested report. + // + ui32Timeout = GetIdleTimeout(psHIDDevice, psUSBRequest->wValue); + + if(ui32Timeout != HID_NOT_FOUND) + { + // + // Need to ACK the data on end point 0 in this case. + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, USB_EP_0, true); + + // + // Send our response to the host. + // + USBDCDSendDataEP0(0, (uint8_t *)&ui32Timeout, 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(psHIDDevice->ui8Subclass == USB_HID_SCLASS_BOOT) + { + // + // We need to ACK the data on end point 0 in this case. + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, USB_EP_0, true); + + // + // We are a boot subclass device so pass this on to the + // application. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USBD_HID_EVENT_SET_PROTOCOL, + psUSBRequest->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(psHIDDevice->ui8Subclass == USB_HID_SCLASS_BOOT) + { + // + // We need to ACK the data on end point 0 in this case. + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, USB_EP_0, true); + + // + // We are a boot subclass device so pass this on to the + // application callback to get the answer. + // + ui8Protocol = (uint8_t)psHIDDevice->pfnRxCallback( + psHIDDevice->pvRxCBData, USBD_HID_EVENT_GET_PROTOCOL, 0, + (void *)0); + + // + // Send our response to the host. + // + USBDCDSendDataEP0(0, (uint8_t *)&ui8Protocol, 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 *pvHIDInstance, uint32_t ui32DataSize) +{ + tHIDInstance *psInst; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvHIDInstance != 0); + + // + // Which device are we dealing with? + // + psHIDDevice = pvHIDInstance; + + // + // If we were not passed any data, just return. + // + if(ui32DataSize == 0) + { + return; + } + + // + // Get our instance data pointer. + // + psInst = &psHIDDevice->sPrivateData; + + // + // Make sure we are actually expecting something. + // + if(psInst->iHIDRxState != eHIDStateWaitData) + { + return; + } + + // + // Change the endpoint state back to idle now that we have been passed + // the data we were waiting for. + // + psInst->iHIDRxState = eHIDStateIdle; + + // + // 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. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USBD_HID_EVENT_SET_REPORT, + psInst->ui16OutReportSize, + psInst->pui8OutReportData); +} + +//***************************************************************************** +// +// 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 *pvHIDInstance, uint32_t ui32Info) +{ + tHIDInstance *psInst; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvHIDInstance != 0); + + // + // Which device are we dealing with? + // + psHIDDevice = pvHIDInstance; + + // + // Get our instance data pointer. + // + psInst = &psHIDDevice->sPrivateData; + + // + // 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; + + psHIDDevice->pfnRxCallback(psHIDDevice->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 *pvHIDInstance) +{ + ASSERT(pvHIDInstance != 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(pvHIDInstance); +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the bus is put into +// suspend state. +// +//***************************************************************************** +static void +HandleSuspend(void *pvHIDInstance) +{ + const tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvHIDInstance != 0); + + // + // Create the instance pointer. + // + psHIDDevice = (const tUSBDHIDDevice *)pvHIDInstance; + + // + // Pass the event on to the client. + // + psHIDDevice->pfnRxCallback(psHIDDevice->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 *pvHIDInstance) +{ + const tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvHIDInstance != 0); + + // + // Create the instance pointer. + // + psHIDDevice = (const tUSBDHIDDevice *)pvHIDInstance; + + // + // Pass the event on to the client. + // + psHIDDevice->pfnRxCallback(psHIDDevice->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 pvHIDInstance is the instance data for this request. +// \param ui32TimemS is the elapsed time in milliseconds since the last call +// to this function. +// +// \return None. +// +//***************************************************************************** +static void +HIDTickHandler(void *pvHIDInstance, uint32_t ui32TimemS) +{ + tHIDInstance *psInst; + uint32_t ui32Size; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvHIDInstance != 0); + + // + // Create the instance pointer. + // + psHIDDevice = (tUSBDHIDDevice *)pvHIDInstance; + + // + // Get our instance data pointer. + // + psInst = &psHIDDevice->sPrivateData; + + // + // If we are connected, process our idle timers. + // + if(psInst->bConnected) + { + ProcessIdleTimers(psHIDDevice, ui32TimemS); + } + + // + // Do we have a deferred receive waiting + // + if(psInst->ui16DeferredOpFlags & (1 << HID_DO_PACKET_RX)) + { + // + // Yes - how big is the waiting packet? + // + ui32Size = MAP_USBEndpointDataAvail(USB0_BASE, psInst->ui8OUTEndpoint); + + // + // Tell the client that there is a packet waiting for it. + // + psHIDDevice->pfnRxCallback(psHIDDevice->pvRxCBData, + USB_EVENT_RX_AVAILABLE, ui32Size, + (void *)0); + } + + return; +} + +//***************************************************************************** +// +//! Initializes HID device operation for a given USB controller. +//! +//! \param ui32Index is the index of the USB controller which is to be +//! initialized for HID device operation. +//! \param psHIDDevice 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 psHIDDevice +//! 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 \b USBDHID_MAX_PACKET +//! 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 +//! \b 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 \b 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, \b 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 \b 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 psHIDDevice pointer on success. +// +//***************************************************************************** +void * +USBDHIDInit(uint32_t ui32Index, tUSBDHIDDevice *psHIDDevice) +{ + tDeviceDescriptor *pi16DevDesc; + + // + // Check parameter validity. + // + ASSERT(ui32Index == 0); + ASSERT(psHIDDevice); + ASSERT(psHIDDevice->ppui8StringDescriptors); + ASSERT(psHIDDevice->pfnRxCallback); + ASSERT(psHIDDevice->pfnTxCallback); + ASSERT(psHIDDevice->ppui8ClassDescriptors); + ASSERT(psHIDDevice->psHIDDescriptor); + ASSERT((psHIDDevice->ui8NumInputReports == 0) || psHIDDevice->psReportIdle); + + USBDHIDCompositeInit(ui32Index, psHIDDevice, 0); + + // + // Fix up the device descriptor with the client-supplied values. + // + pi16DevDesc = (tDeviceDescriptor *)psHIDDevice->sPrivateData.sDevInfo.pui8DeviceDescriptor; + pi16DevDesc->idVendor = psHIDDevice->ui16VID; + pi16DevDesc->idProduct = psHIDDevice->ui16PID; + + // + // All is well so now pass the descriptors to the lower layer and put + // the HID device on the bus. + // + USBDCDInit(ui32Index, &psHIDDevice->sPrivateData.sDevInfo, + (void *)psHIDDevice); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psHIDDevice); +} + +//***************************************************************************** +// +//! Initializes HID device operation for a given USB controller. +//! +//! \param ui32Index is the index of the USB controller which is to be +//! initialized for HID device operation. +//! \param psHIDDevice points to a structure containing parameters customizing +//! the operation of the HID device. +//! \param psCompEntry is the composite device entry to initialize when +//! creating a composite device. +//! +//! USB HID device classes call this function to initialize the lower level +//! HID interface in the USB controller. If this HID device device is part of +//! a composite device, then the \e psCompEntry should point to the composite +//! device entry to initialize. This is part of the array that is passed to +//! the USBDCompositeInit() function. +//! +//! \return Returns zero on failure or a non-zero instance value that should be +//! used with the remaining USB HID APIs. +// +//***************************************************************************** +void * +USBDHIDCompositeInit(uint32_t ui32Index, tUSBDHIDDevice *psHIDDevice, + tCompositeEntry *psCompEntry) +{ + tHIDInstance *psInst; + tEndpointDescriptor *psEndpoint; + + // + // Check parameter validity. + // + ASSERT(ui32Index == 0); + ASSERT(psHIDDevice); + ASSERT(psHIDDevice->ppsConfigDescriptor); + ASSERT(psHIDDevice->ppui8StringDescriptors); + ASSERT(psHIDDevice->pfnRxCallback); + ASSERT(psHIDDevice->pfnTxCallback); + ASSERT(psHIDDevice->ppui8ClassDescriptors); + ASSERT(psHIDDevice->psHIDDescriptor); + ASSERT((psHIDDevice->ui8NumInputReports == 0) || psHIDDevice->psReportIdle); + + // + // Initialize the workspace in the passed instance structure. + // + psInst = &psHIDDevice->sPrivateData; + + // + // Initialize the device information structure. + // + psInst->sDevInfo.psCallbacks = &g_sHIDHandlers; + psInst->sDevInfo.pui8DeviceDescriptor = g_pui8HIDDeviceDescriptor; + psInst->sDevInfo.ppsConfigDescriptors = psHIDDevice->ppsConfigDescriptor; + psInst->sDevInfo.ppui8StringDescriptors = + psHIDDevice->ppui8StringDescriptors; + psInst->sDevInfo.ui32NumStringDescriptors = + psHIDDevice->ui32NumStringDescriptors; + + // + // Default the endpoints zero before looking for them in the configuration + // descriptor. + // + psInst->ui8Interface = 0; + psInst->ui8INEndpoint = 0; + psInst->ui8OUTEndpoint = 0; + + // + // Get the first endpoint descriptor on interface 0. + // + psEndpoint = + USBDCDConfigGetInterfaceEndpoint(psHIDDevice->ppsConfigDescriptor[0], + psInst->ui8Interface, 0, 0); + + if(psEndpoint) + { + if(psEndpoint->bEndpointAddress & 0x80) + { + psInst->ui8INEndpoint = IndexToUSBEP(psEndpoint->bEndpointAddress); + } + else + { + psInst->ui8OUTEndpoint = IndexToUSBEP(psEndpoint->bEndpointAddress); + } + } + + // + // Get the second endpoint descriptor on interface 0. + // + psEndpoint = + USBDCDConfigGetInterfaceEndpoint(psHIDDevice->ppsConfigDescriptor[0], + psInst->ui8Interface, 0, 1); + if(psEndpoint) + { + if(psEndpoint->bEndpointAddress & 0x80) + { + psInst->ui8INEndpoint = IndexToUSBEP(psEndpoint->bEndpointAddress); + } + else + { + psInst->ui8OUTEndpoint = IndexToUSBEP(psEndpoint->bEndpointAddress); + } + } + + // + // Must have at least an IN endpoint. + // + if(psInst->ui8INEndpoint == 0) + { + return((void *)0); + } + + // + // Initialize the composite entry that is used by the composite device + // class. + // + if(psCompEntry != 0) + { + psCompEntry->psDevInfo = &psInst->sDevInfo; + psCompEntry->pvInstance = (void *)psHIDDevice; + } + + psInst->ui32USBBase = USB0_BASE; + psInst->iHIDRxState = eHIDStateUnconfigured; + psInst->iHIDTxState = eHIDStateUnconfigured; + psInst->ui16DeferredOpFlags = 0; + psInst->bConnected = false; + psInst->bGetRequestPending = false; + psInst->bSendInProgress = false; + psInst->ui16InReportIndex = 0; + psInst->ui16InReportSize = 0; + psInst->pui8InReportData = (uint8_t *)0; + psInst->ui16OutReportSize = 0; + psInst->pui8OutReportData = (uint8_t *)0; + + // + // Initialize the device info structure for the HID device. + // + USBDCDDeviceInfoInit(0, &psInst->sDevInfo); + + // + // Initialize the input report idle timers if any input reports exist. + // + ClearIdleTimers(psHIDDevice); + + // + // 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 *)psHIDDevice); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psHIDDevice); +} + +//***************************************************************************** +// +//! Shuts down the HID device. +//! +//! \param pvHIDInstance 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 pvHIDInstance instance should not me used in +//! any other calls. +//! +//! \return None. +// +//***************************************************************************** +void +USBDHIDTerm(void *pvHIDInstance) +{ + tHIDInstance *psInst; + + ASSERT(pvHIDInstance); + + // + // Get a pointer to our instance data. + // + psInst = &((tUSBDHIDDevice *)pvHIDInstance)->sPrivateData; + + // + // Terminate the requested instance. + // + USBDCDTerm(USBBaseToIndex(psInst->ui32USBBase)); + + psInst->ui32USBBase = 0; +} + +//***************************************************************************** +// +//! Sets the client-specific pointer parameter for the receive channel +//! callback. +//! +//! \param pvHIDInstance 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 pvHIDInstance 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 *pvHIDInstance, void *pvCBData) +{ + void *pvOldValue; + + ASSERT(pvHIDInstance); + + // + // Set the callback data for the receive channel after remembering the + // previous value. + // + pvOldValue = ((tUSBDHIDDevice *)pvHIDInstance)->pvRxCBData; + ((tUSBDHIDDevice *)pvHIDInstance)->pvRxCBData = pvCBData; + + // + // Return the previous callback data value. + // + return(pvOldValue); +} + +//***************************************************************************** +// +//! Sets the client-specific data pointer for the transmit callback. +//! +//! \param pvHIDInstance 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 pvHIDInstance 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 *pvHIDInstance, void *pvCBData) +{ + void *pvOldValue; + + ASSERT(pvHIDInstance); + + // + // Set the callback data for the transmit channel after remembering the + // previous value. + // + pvOldValue = ((tUSBDHIDDevice *)pvHIDInstance)->pvTxCBData; + ((tUSBDHIDDevice *)pvHIDInstance)->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 pvHIDInstance is the pointer to the device instance structure as +//! returned by USBDHIDInit(). +//! \param pi8Data points to the first byte of data which is to be transmitted. +//! \param ui32Length 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 \e pui8Data 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. +// +//***************************************************************************** +uint32_t +USBDHIDReportWrite(void *pvHIDInstance, uint8_t *pi8Data, uint32_t ui32Length, + bool bLast) +{ + tHIDInstance *psInst; + int32_t i32Retcode; + + ASSERT(pvHIDInstance); + + // + // Get our instance data pointer + // + psInst = &((tUSBDHIDDevice *)pvHIDInstance)->sPrivateData; + + // + // 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->iHIDTxState != eHIDStateIdle) + { + // + // 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(ui32Length) + { + ClearReportTimer(pvHIDInstance, *pi8Data); + } + + // + // Keep track of the whereabouts of the report so that we can send it in + // multiple packets if necessary. + // + psInst->pui8InReportData = pi8Data; + psInst->ui16InReportIndex = 0; + psInst->ui16InReportSize = ui32Length; + + // + // Schedule transmission of the first packet of the report. + // + psInst->iHIDTxState = eHIDStateWaitData; + i32Retcode = 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(i32Retcode != -1) + { + // + // No - tell the caller we sent all the bytes provided. + // + return(ui32Length); + } + else + { + // + // Yes - tell the caller we could not send the data. + // + return(0); + } +} + +//***************************************************************************** +// +//! Reads a packet of data received from the USB host via the interrupt OUT +//! endpoint (if in use). +//! +//! \param pvHIDInstance is the pointer to the device instance structure as +//! returned by USBDHIDInit(). +//! \param pi8Data points to a buffer into which the received data will be +//! written. +//! \param ui32Length is the size of the buffer pointed to by pi8Data. +//! \param bLast indicates whether the client will make a further call to +//! read additional data from the packet. +//! +//! This function reads up to \e ui32Length 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. +// +//***************************************************************************** +uint32_t +USBDHIDPacketRead(void *pvHIDInstance, uint8_t *pi8Data, uint32_t ui32Length, + bool bLast) +{ + uint32_t ui32EPStatus, ui32Count, ui32Pkt; + tHIDInstance *psInst; + int32_t i32Retcode; + + ASSERT(pvHIDInstance); + + // + // Get our instance data pointer + // + psInst = &((tUSBDHIDDevice *)pvHIDInstance)->sPrivateData; + + // + // Does the relevant endpoint FIFO have a packet waiting for us? + // + ui32EPStatus = MAP_USBEndpointStatus(psInst->ui32USBBase, + psInst->ui8OUTEndpoint); + if(ui32EPStatus & USB_DEV_RX_PKT_RDY) + { + // + // How many bytes are available for us to receive? + // + ui32Pkt = MAP_USBEndpointDataAvail(psInst->ui32USBBase, + psInst->ui8OUTEndpoint); + + // + // Get as much data as we can. + // + ui32Count = ui32Length; + i32Retcode = MAP_USBEndpointDataGet(psInst->ui32USBBase, + psInst->ui8OUTEndpoint, + pi8Data, &ui32Count); + + // + // Did we read the last of the packet data? + // + if(ui32Count == ui32Pkt) + { + // + // Clear the endpoint status so that we know no packet is + // waiting. + // + MAP_USBDevEndpointStatusClear(psInst->ui32USBBase, + psInst->ui8OUTEndpoint, + ui32EPStatus); + + // + // Acknowledge the data, thus freeing the host to send the + // next packet. + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, + psInst->ui8OUTEndpoint, true); + + // + // Clear the flag we set to indicate that a packet read is + // pending. + // + SetDeferredOpFlag(&psInst->ui16DeferredOpFlags, + HID_DO_PACKET_RX, false); + } + + // + // If all went well, tell the caller how many bytes they got. + // + if(i32Retcode != -1) + { + return(ui32Count); + } + } + + // + // 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 pvHIDInstance 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 (\b USBDHID_MAX_PACKET) 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 +//! \b USBDHID_MAX_PACKET if no transmission is currently in progress. +// +//***************************************************************************** +uint32_t +USBDHIDTxPacketAvailable(void *pvHIDInstance) +{ + tHIDInstance *psInst; + + ASSERT(pvHIDInstance); + + // + // Get our instance data pointer. + // + psInst = &((tUSBDHIDDevice *)pvHIDInstance)->sPrivateData; + + // + // Do we have a packet transmission currently ongoing? + // + if(psInst->iHIDTxState != eHIDStateIdle) + { + // + // 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(USBDHID_MAX_PACKET); + } +} + +//***************************************************************************** +// +//! Determines whether a packet is available and, if so, the size of the +//! buffer required to read it. +//! +//! \param pvHIDInstance 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. +// +//***************************************************************************** +uint32_t +USBDHIDRxPacketAvailable(void *pvHIDInstance) +{ + uint32_t ui32EPStatus, ui32Size; + tHIDInstance *psInst; + + ASSERT(pvHIDInstance); + + // + // Get our instance data pointer + // + psInst = &((tUSBDHIDDevice *)pvHIDInstance)->sPrivateData; + + // + // Does the relevant endpoint FIFO have a packet waiting for us? + // + ui32EPStatus = MAP_USBEndpointStatus(psInst->ui32USBBase, + psInst->ui8OUTEndpoint); + if(ui32EPStatus & USB_DEV_RX_PKT_RDY) + { + // + // Yes - a packet is waiting. How big is it? + // + ui32Size = MAP_USBEndpointDataAvail(psInst->ui32USBBase, + psInst->ui8OUTEndpoint); + + return(ui32Size); + } + else + { + // + // There is no packet waiting to be received. + // + return(0); + } +} +#ifndef DEPRECATED + +//***************************************************************************** +// +//! Reports the device power status (bus- or self-powered) to the USB library. +//! +//! \param pvHIDInstance is the pointer to the HID device instance structure. +//! \param ui8Power 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 *pvHIDInstance, uint8_t ui8Power) +{ + ASSERT(pvHIDInstance); + + // + // Pass the request through to the lower layer. + // + USBDCDPowerStatusSet(0, ui8Power); +} +#endif + +//***************************************************************************** +// +//! Requests a remote wake up to resume communication when in suspended state. +//! +//! \param pvHIDInstance 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. +// +//***************************************************************************** +bool +USBDHIDRemoteWakeupRequest(void *pvHIDInstance) +{ + ASSERT(pvHIDInstance); + + // + // 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..14830fa --- /dev/null +++ b/usblib/device/usbdhid.h @@ -0,0 +1,1102 @@ +//***************************************************************************** +// +// usbdhid.h - Definitions used by HID class devices. +// +// Copyright (c) 2008-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#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 +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// This is the size of the g_pui8HIDInterface array in bytes. +// +//***************************************************************************** +#define HIDINTERFACE_SIZE (9) + +//***************************************************************************** +// +// This is the size of the g_pui8HIDInEndpoint array in bytes. +// +//***************************************************************************** +#define HIDINENDPOINT_SIZE (7) + +//***************************************************************************** +// +// This is the size of the g_pui8HIDOutEndpoint array in bytes. +// +//***************************************************************************** +#define HIDOUTENDPOINT_SIZE (7) + +//***************************************************************************** +// +// This is the size of the tHIDDescriptor in bytes. +// +//***************************************************************************** +#define HIDDESCRIPTOR_SIZE (9) + +//***************************************************************************** +// +//! 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. +// +//***************************************************************************** +#define COMPOSITE_DHID_SIZE (HIDINTERFACE_SIZE + HIDINENDPOINT_SIZE + \ + HIDOUTENDPOINT_SIZE + HIDDESCRIPTOR_SIZE) + +//***************************************************************************** +// +// Macros used to create the static Report Descriptors. +// +//***************************************************************************** + +//***************************************************************************** +// +//! This is a macro to assist adding Usage Page entries in HID report +//! descriptors. +//! +//! \param ui8Value 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(ui8Value) 0x05, ((ui8Value) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Usage Page entries in HID report +//! descriptors when a vendor-specific value is to be used. +//! +//! \param ui16Value 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. Vendor-specific values must lie in the range 0xFF00 to +//! 0xFFFF inclusive. +//! +//! \return Not a function. +// +//***************************************************************************** +#define UsagePageVendor(ui16Value) 0x06, ((ui16Value) & 0xFF), \ + (((ui16Value) >> 8) & 0xFF) + +//***************************************************************************** +// +//! This is a macro to assist adding Usage entries in HID report descriptors. +//! +//! \param ui8Value 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(ui8Value) 0x09, ((ui8Value) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding vendor-specific Usage entries in HID +//! report descriptors. +//! +//! \param ui16Value is the vendor-specific Usage value in the range 0xFF00 to +//! 0xFFFF. +//! +//! 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 UsageVendor(ui16Value) 0x0A, ((ui16Value) & 0xFF), \ + (((ui16Value) >> 8) & 0xFF) + +//***************************************************************************** +// +//! This is a macro to assist adding Usage Minimum entries in HID report +//! descriptors. +//! +//! \param ui8Value 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(ui8Value) 0x19, ((ui8Value) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Usage Maximum entries in HID report +//! descriptors. +//! +//! \param ui8Value 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(ui8Value) 0x29, ((ui8Value) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Logical Minimum entries in HID report +//! descriptors. +//! +//! \param i8Value 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(i8Value) 0x15, ((i8Value) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Logical Maximum entries in HID report +//! descriptors. +//! +//! \param i8Value 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(i8Value) 0x25, ((i8Value) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Physical Minimum entries in HID report +//! descriptors. +//! +//! \param i16Value 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(i16Value) \ + 0x36, ((i16Value) & 0xFF), \ + (((i16Value) >> 8) & 0xFF) + +//***************************************************************************** +// +//! This is a macro to assist adding Physical Maximum entries in HID report +//! descriptors. +//! +//! \param i16Value 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(i16Value) \ + 0x46, ((i16Value) & 0xFF), \ + (((i16Value) >> 8) & 0xFF) + +//***************************************************************************** +// +//! This is a macro to assist adding Collection entries in HID report +//! descriptors. +//! +//! \param ui8Value 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(ui8Value) 0xa1, ((ui8Value) & 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 ui8Value 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(ui8Value) 0x95, ((ui8Value) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Report ID entries in HID report +//! descriptors. +//! +//! \param ui8Value 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(ui8Value) 0x85, ((ui8Value) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Report Size entries in HID report +//! descriptors. +//! +//! \param ui8Value 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(ui8Value) 0x75, ((ui8Value) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Input entries in HID report descriptors. +//! +//! \param ui8Value 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(ui8Value) 0x81, ((ui8Value) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Input entries in HID report descriptors. +//! +//! \param ui16Value 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(ui16Value) 0x82, ((ui16Value) & 0xff), \ + (((ui16Value) >> 8) & 0xFF) + +//***************************************************************************** +// +//! This is a macro to assist adding Feature entries in HID report descriptors. +//! +//! \param ui8Value is bit mask to specify the type of a set of feature report +//! items. Note that if the \b 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(ui8Value) 0xB1, ((ui8Value) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Feature entries in HID report descriptors. +//! +//! \param ui16Value 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 \b 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(ui16Value) 0xB2, ((ui16Value) & 0xff), \ + (((ui16Value) >> 8) & 0xFF) + +//***************************************************************************** +// +//! This is a macro to assist adding Output entries in HID report descriptors. +//! +//! \param ui8Value is bit mask to specify the type of a set of output report +//! items. Note that if the \b 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(ui8Value) 0x91, ((ui8Value) & 0xff) + +//***************************************************************************** +// +//! This is a macro to assist adding Output entries in HID report descriptors. +//! +//! \param ui16Value 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 \b USB_HID_OUTPUT_BITF 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(ui16Value) 0x92, ((ui16Value) & 0xff), \ + (((ui16Value) >> 8) & 0xFF) + +//***************************************************************************** +// +//! This is a macro to assist adding Unit Exponent entries in HID report +//! descriptors. +//! +//! \param i8Value 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(i8Value) 0x55, ((i8Value) & 0x0f) + +//***************************************************************************** +// +//! This is a macro to assist adding Unit entries for uncommon units in HID +//! report descriptors. +//! +//! \param ui32Value 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(ui32Value) 0x67, (ui32Value) & 0x0f), \ + (((ui32Value) >> 8) & 0xFF), \ + (((ui32Value) >> 16) & 0xFF), \ + (((ui32Value) >> 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. +// +//***************************************************************************** +#define USBDHID_MAX_PACKET 64 + +//***************************************************************************** +// +// PRIVATE +// +// This enumeration holds the various states that the device can be in during +// normal operation. +// +//***************************************************************************** +typedef enum +{ + // + // Unconfigured. + // + eHIDStateUnconfigured, + + // + // No outstanding transaction remains to be completed. + // + eHIDStateIdle, + + // + // Waiting on completion of a send or receive transaction. + // + eHIDStateWaitData +} +tHIDState; + +//***************************************************************************** +// +// PRIVATE +// +// This structure defines the private instance data and state variables for +// HID devices. The memory for this structure is included in the +// sPrivateData field in the tUSBDHIDDevice structure passed in the +// USBDHIDInit() function. +// +//***************************************************************************** +typedef struct +{ + // + // Base address for the USB controller. + // + uint32_t ui32USBBase; + + // + // The device info to interact with the lower level DCD code. + // + tDeviceInfo sDevInfo; + + // + // The state of the HID receive channel. + // + volatile tHIDState iHIDRxState; + + // + // The state of the HID transmit channel. + // + volatile tHIDState iHIDTxState; + + // + // State of any pending operations that could not be handled immediately + // upon receipt. + // + volatile uint16_t ui16DeferredOpFlags; + + // + // Size of the HID IN report. + // + uint16_t ui16InReportSize; + + // + // . + // + uint16_t ui16InReportIndex; + + // + // Size of the HID OUT report. + // + uint16_t ui16OutReportSize; + + // + // Pointer to the current HID IN report data. + // + uint8_t *pui8InReportData; + + // + // Pointer to the current HID OUT report data. + // + uint8_t *pui8OutReportData; + + // + // The connection status of the device. + // + volatile bool bConnected; + + // + // Whether an IN transaction is in process. + // + volatile bool bSendInProgress; + + // + // An HID request transaction is in process(Endpoint 0). + // + bool bGetRequestPending; + + // + // The IN endpoint number, this is modified in composite devices. + // + uint8_t ui8INEndpoint; + + // + // The OUT endpoint number, this is modified in composite devices. + // + uint8_t ui8OUTEndpoint; + + // + // The bulk class interface number, this is modified in composite devices. + // + uint8_t ui8Interface; +} +tHIDInstance; + +//***************************************************************************** +// +//! 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. + // + uint8_t ui8Duration4mS; + + // + //! 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. + // + uint8_t ui8ReportID; + + // + //! 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 \b USBD_HID_EVENT_IDLE_TIMEOUT. + // + uint16_t ui16TimeTillNextmS; + + // + //! 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. + // + uint32_t ui32TimeSinceReportmS; +} +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. + // + uint16_t ui16VID; + + // + //! The product ID that this device is to present in the device descriptor. + // + uint16_t ui16PID; + + // + //! The maximum power consumption of the device, expressed in milliamps. + // + uint16_t ui16MaxPowermA; + + // + //! Indicates whether the device is self- or bus-powered and whether or not + //! it supports remote wakeup. Valid values are \b USB_CONF_ATTR_SELF_PWR + //! or \b USB_CONF_ATTR_BUS_PWR, optionally ORed with + //! \b USB_CONF_ATTR_RWAKE. + // + uint8_t ui8PwrAttributes; + + // + //! The interface subclass to publish to the server for this HID device. + // + uint8_t ui8Subclass; + + // + //! The interface protocol to publish to the server for this HID device. + // + uint8_t ui8Protocol; + + // + //! 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 \e pi16ReportIdle below. + // + uint8_t ui8NumInputReports; + + // + //! 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 + //! ui8Duration4mS and ui8ReportID 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 updates 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 ui8NumInputReports field above. + // + tHIDReportIdle *psReportIdle; + + //! A pointer to the callback function which is 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 is sent as the first + //! parameter in all calls made to the receive channel callback, + //! pfnRxCallback. + // + void *pvRxCBData; + + // + //! A pointer to the callback function which is 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 is 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 \b USB_EVENT_RX_AVAILABLE events. If false, + //! reports from the host are received via endpoint zero and passed to the + //! application via \b USBD_HID_EVENT_REPORT_SENT events. + // + bool 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 + //! \e pi16HIDDescriptor. + // + const uint8_t * const *ppui8ClassDescriptors; + + // + //! 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 uint8_t * const *ppui8StringDescriptors; + + // + //! The number of descriptors provided in the \e ppStringDescriptors + //! array. This must be 1 + ((5 + (num HID strings)) * (num languages)). + // + uint32_t ui32NumStringDescriptors; + + // + // ! The configuration descriptor for this HID device. + // + const tConfigHeader * const *ppsConfigDescriptor; + + // + //! The 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 sPrivateData; +} +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 ui32MsgValue 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 +//! \b 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 +//! \b 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 ui32MsgValue 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 "uint32_t" 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 ui32MsgValue field indicates the +//! size of the report and pvMsgData points to the first byte of the report. +//! The report buffer was previously returned in response to an +//! earlier \b USBD_HID_EVENT_GET_REPORT_BUFFER callback. The HID device class +//! driver does 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, +//! \b USB_HID_PROTOCOL_BOOT or \b 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 ui32MsgData value contains the requested protocol, +//! \b USB_HID_PROTOCOL_BOOT or \b 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 ui32MsgData value contains 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) + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void *USBDHIDInit(uint32_t ui32Index, tUSBDHIDDevice *psHIDDevice); +extern void *USBDHIDCompositeInit(uint32_t ui32Index, + tUSBDHIDDevice *psDevice, + tCompositeEntry *psCompEntry); +extern void USBDHIDTerm(void *pvHIDInstance); +extern void *USBDHIDSetRxCBData(void *pvHIDInstance, void *pvCBData); +extern void *USBDHIDSetTxCBData(void *pvHIDInstance, void *pvCBData); +extern uint32_t USBDHIDReportWrite(void *pvHIDInstance, uint8_t *pi8Data, + uint32_t ui32Length, bool bLast); +extern uint32_t USBDHIDPacketRead(void *pvHIDInstance, uint8_t *pi8Data, + uint32_t ui32Length, bool bLast); +extern uint32_t USBDHIDTxPacketAvailable(void *pvHIDInstance); +extern uint32_t USBDHIDRxPacketAvailable(void *pvHIDInstance); +extern bool USBDHIDRemoteWakeupRequest(void *pvHIDInstance); + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// The following APIs are deprecated. +// +//***************************************************************************** +#ifndef DEPRECATED + +// +// Use USBDCDFeatureSet() or USBHCDFeatureSet() with \b USBLIB_FEATURE_POWER +// configuration option. +// +extern void USBDHIDPowerStatusSet(void *pvHIDInstance, uint8_t ui8Power); +#endif + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif // __USBDHID_H__ diff --git a/usblib/device/usbdhidgamepad.c b/usblib/device/usbdhidgamepad.c new file mode 100644 index 0000000..a0945cc --- /dev/null +++ b/usblib/device/usbdhidgamepad.c @@ -0,0 +1,845 @@ +//***************************************************************************** +// +// usbdhidgame.c - USB HID Gamepad device class driver +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.h" +#include "usblib/device/usbdevice.h" +#include "usblib/usbhid.h" +#include "usblib/device/usbdhid.h" +#include "usblib/device/usbdhidgamepad.h" + +//***************************************************************************** +// +//! \addtogroup hid_gamepad_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// 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. +// +//***************************************************************************** +static uint8_t g_pui8GameDescriptor[] = +{ + // + // Configuration descriptor header. + // + 9, // Size of the configuration descriptor. + USB_DTYPE_CONFIGURATION, // Type of this descriptor. + USBShort(24), // 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, // Self Powered. + 0, // The maximum power in 2mA increments. +}; + +//***************************************************************************** +// +// This is the HID interface descriptor for the gamepad device. +// +//***************************************************************************** +static uint8_t g_pui8HIDInterface[HIDINTERFACE_SIZE] = +{ + // + // 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. + 1, // 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. +}; + +//***************************************************************************** +// +// This is the HID IN endpoint descriptor for the gamepad device. +// +//***************************************************************************** +static const uint8_t g_pui8HIDInEndpoint[HIDINENDPOINT_SIZE] = +{ + // + // Interrupt IN endpoint descriptor + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_IN | USBEPToIndex(USB_EP_1), + USB_EP_ATTR_INT, // Endpoint is an interrupt endpoint. + USBShort(USBFIFOSizeToBytes(USB_FIFO_SZ_64)), + // The maximum packet size. + 1, // The polling interval for this endpoint. +}; + +//***************************************************************************** +// +// The following is the HID report structure definition that is passed back +// to the host. +// +//***************************************************************************** +static const uint8_t g_pui8GameReportDescriptor[] = +{ + UsagePage(USB_HID_GENERIC_DESKTOP), + Usage(USB_HID_JOYSTICK), + Collection(USB_HID_APPLICATION), + // + // The axis for the controller. + // + UsagePage(USB_HID_GENERIC_DESKTOP), + Usage (USB_HID_POINTER), + Collection (USB_HID_PHYSICAL), + + // + // The X, Y and Z values which are specified as 8-bit absolute + // position values. + // + Usage (USB_HID_X), + Usage (USB_HID_Y), + Usage (USB_HID_Z), + + // + // 3 8-bit absolute values. + // + ReportSize(8), + ReportCount(3), + Input(USB_HID_INPUT_DATA | USB_HID_INPUT_VARIABLE | + USB_HID_INPUT_ABS), + + // + // The 8 buttons. + // + UsagePage(USB_HID_BUTTONS), + UsageMinimum(1), + UsageMaximum(8), + LogicalMinimum(0), + LogicalMaximum(1), + PhysicalMinimum(0), + PhysicalMaximum(1), + + // + // 8 - 1 bit values for the buttons. + // + ReportSize(1), + ReportCount(8), + Input(USB_HID_INPUT_DATA | USB_HID_INPUT_VARIABLE | + USB_HID_INPUT_ABS), + + EndCollection, + EndCollection +}; + +//***************************************************************************** +// +// The HID descriptor for the gamepad device. +// +//***************************************************************************** +static tHIDDescriptor g_sGameHIDDescriptor = +{ + 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_pui8GameReportDescriptor) + // Size of report descriptor + } + } +}; + +//***************************************************************************** +// +// The HID configuration descriptor is defined as four sections. +// These sections are: +// +// 1. The 9 byte configuration descriptor. +// 2. The interface descriptor. +// 3. The HID report and physical descriptors, provided by the application +// or the default can be used. +// 4. The mandatory interrupt IN endpoint descriptor. +// +//***************************************************************************** +static const tConfigSection g_sHIDConfigSection = +{ + sizeof(g_pui8GameDescriptor), + g_pui8GameDescriptor +}; + +static const tConfigSection g_sHIDInterfaceSection = +{ + sizeof(g_pui8HIDInterface), + g_pui8HIDInterface +}; + +static const tConfigSection g_sHIDInEndpointSection = +{ + sizeof(g_pui8HIDInEndpoint), + g_pui8HIDInEndpoint +}; + +//***************************************************************************** +// +// Place holder for the user's HID descriptor block. +// +//***************************************************************************** +static tConfigSection g_sHIDDescriptorSection = +{ + sizeof(g_sGameHIDDescriptor), + (const uint8_t *)&g_sGameHIDDescriptor +}; + +//***************************************************************************** +// +// This array lists all the sections that must be concatenated to make a +// single, complete HID configuration descriptor. +// +//***************************************************************************** +static const tConfigSection *g_psHIDSections[] = +{ + &g_sHIDConfigSection, + &g_sHIDInterfaceSection, + &g_sHIDDescriptorSection, + &g_sHIDInEndpointSection, +}; + +#define NUM_HID_SECTIONS ((sizeof(g_psHIDSections) / \ + sizeof(tConfigSection *))) + +//***************************************************************************** +// +// The header for the single configuration supported. 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. +// +//***************************************************************************** +static tConfigHeader g_sHIDConfigHeader = +{ + NUM_HID_SECTIONS, + g_psHIDSections +}; + +//***************************************************************************** +// +// Configuration Descriptor. +// +//***************************************************************************** +static const tConfigHeader * const g_ppsHIDConfigDescriptors[] = +{ + &g_sHIDConfigHeader +}; + +//***************************************************************************** +// +// The HID class descriptor table. For the gamepad class there is only a +// single report descriptor. +// +//***************************************************************************** +static const uint8_t *g_ppui8GameClassDescriptors[] = +{ + g_pui8GameReportDescriptor +}; + +//***************************************************************************** +// +// HID gamepad transmit channel event handler function. +// +// \param pvGameDevice is the event callback pointer provided during +// USBDHIDInit(). This is a pointer to the HID gamepad device structure +// of the type tUSBDHIDGamepadDevice. +// \param ui32Event identifies the event we are being called back for. +// \param ui32MsgData is an event-specific value. +// \param pvMsgData is an event-specific pointer. +// +// This function is called by the lower level HID device class driver to inform +// the application of particular asynchronous events related to report events +// related to using the interrupt IN endpoint. +// +// \return Returns a value which is event-specific. +// +//***************************************************************************** +static uint32_t +HIDGamepadTxHandler(void *pvGameDevice, uint32_t ui32Event, + uint32_t ui32MsgData, void *pvMsgData) +{ + tUSBDGamepadInstance *psInst; + tUSBDHIDGamepadDevice *psGamepad; + + // + // Make sure we did not get a NULL pointer. + // + ASSERT(pvGameDevice); + + // + // Get a pointer to our instance data + // + psGamepad = (tUSBDHIDGamepadDevice *)pvGameDevice; + psInst = &psGamepad->sPrivateData; + + // + // Which event were we sent? + // + switch (ui32Event) + { + // + // A report transmitted via the interrupt IN endpoint was acknowledged + // by the host. + // + case USB_EVENT_TX_COMPLETE: + { + // + // The last transmission is complete so return to the idle state. + // + psInst->iState = eHIDGamepadStateIdle; + + // + // Pass the event on to the application. + // + psGamepad->pfnCallback(psGamepad->pvCBData, USB_EVENT_TX_COMPLETE, + ui32MsgData, (void *)0); + + break; + } + + // + // Ignore all other events related to transmission of reports via + // the interrupt IN endpoint. + // + default: + { + break; + } + } + + return(0); +} + +//***************************************************************************** +// +// Main HID device class event receive handler function. +// +// \param pvGameDevice is the event callback pointer provided during +// USBDHIDInit(). This is a pointer to the HID gamepad device structure +// of the type tUSBDHIDGamepadDevice. +// \param ui32Event identifies the event we are being called back for. +// \param ui32MsgData is an event-specific value. +// \param pvMsgData is an event-specific pointer. +// +// This function is called by the lower level HID device class driver to inform +// the application of particular asynchronous events related to operation of +// the gamepad HID device. +// +// \note This function also receive all generic events as well such as +// \b USB_EVENT_CONNECTED and USB_EVENT_DISCONNECTED. +// +// \return Returns a value which is event-specific. +// +//***************************************************************************** +static uint32_t +HIDGamepadRxHandler(void *pvGamepad, uint32_t ui32Event, uint32_t ui32MsgData, + void *pvMsgData) +{ + tUSBDGamepadInstance *psInst; + tUSBDHIDGamepadDevice *psGamepad; + uint32_t ui32Ret; + + // + // Make sure we did not get a NULL pointer. + // + ASSERT(pvGamepad); + + // + // Return zero by default. + // + ui32Ret = 0; + + // + // Get a pointer to our instance data + // + psGamepad = (tUSBDHIDGamepadDevice *)pvGamepad; + psInst = &psGamepad->sPrivateData; + + // + // Which event were we sent? + // + switch(ui32Event) + { + // + // The host has connected to us and configured the device. + // + case USB_EVENT_CONNECTED: + { + // + // Now in the idle state. + // + psInst->iState = eHIDGamepadStateIdle; + + // + // Pass the information on to the application. + // + psGamepad->pfnCallback(psGamepad->pvCBData, USB_EVENT_CONNECTED, 0, + (void *)0); + + break; + } + + // + // The host has disconnected from us. + // + case USB_EVENT_DISCONNECTED: + { + psInst->iState = eHIDGamepadStateNotConnected; + + // + // Pass the information on to the application. + // + ui32Ret = psGamepad->pfnCallback(psGamepad->pvCBData, + USB_EVENT_DISCONNECTED, 0, + (void *)0); + + break; + } + + // + // This handles the Set Idle command. + // + case USBD_HID_EVENT_IDLE_TIMEOUT: + { + // + // Give the pointer to the idle report structure. + // + *(void **)pvMsgData = (void *)&psInst->sReportIdle; + + ui32Ret = sizeof(psInst->sReportIdle); + + break; + } + + // + // The host is polling for a particular report and the HID driver + // is asking for the latest version to transmit. + // + case USBD_HID_EVENT_GET_REPORT: + { + // + // If this is an IN request then pass the request on to the + // application. All other requests are ignored. + // + if(ui32MsgData == USB_HID_REPORT_IN) + { + ui32Ret = psGamepad->pfnCallback(psGamepad->pvCBData, + USBD_HID_EVENT_GET_REPORT, 0, + pvMsgData); + } + + break; + } + + // + // 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; + } + + // + // Pass these events to the client unchanged. + // + case USB_EVENT_ERROR: + case USB_EVENT_SUSPEND: + case USB_EVENT_RESUME: + case USB_EVENT_LPM_RESUME: + case USB_EVENT_LPM_SLEEP: + case USB_EVENT_LPM_ERROR: + { + ui32Ret = psGamepad->pfnCallback(psGamepad->pvCBData, ui32Event, + ui32MsgData, pvMsgData); + + break; + } + + // + // This event is sent in response to a host Set_Report request which + // is not supported for gamepads. + // + case USBD_HID_EVENT_GET_REPORT_BUFFER: + + // + // We ignore all other events. + // + default: + { + break; + } + } + return(ui32Ret); +} + +//***************************************************************************** +// +//! Initializes HID gamepad device operation for a given USB controller. +//! +//! \param ui32Index is the index of the USB controller that is to be +//! initialized for HID gamepad device operation. +//! \param psGamepad points to a structure containing parameters +//! customizing the operation of the HID gamepad device. +//! +//! An application that enables a USB HID gamepad interface to a USB host +//! must call this function to initialize the USB controller and attach the +//! gamepad device to the USB bus. This function performs all required USB +//! initialization, and the device is ready for operation on the function +//! return. +//! +//! On successful completion, this function returns the modified \e psGamepad +//! pointer passed to it or returns a NULL pointer if there was a problem. +//! This pointer must be passed on all future calls to the HID gamepad device +//! driver. +//! +//! When a host connects and configures the device, the application callback +//! receives \b USB_EVENT_CONNECTED, after which calls can be made to +//! USBDHIDGamepadSendReport() to report changes to the gamepad interface to +//! the USB host when it requests them. +//! +//! \note The application must not make any calls to the lower level USB device +//! interfaces if interacting with USB via the USB HID gamepad device class +//! API. +//! +//! \return Returns NULL on failure or the \e psGamepad pointer on success. +// +//***************************************************************************** +tUSBDHIDGamepadDevice * +USBDHIDGamepadInit(uint32_t ui32Index, tUSBDHIDGamepadDevice *psGamepad) +{ + void *pvRetcode; + tUSBDHIDDevice *psHIDDevice; + tConfigDescriptor *pConfigDesc; + + // + // Check basic parameter validity. + // + ASSERT(psGamepad); + ASSERT(psGamepad->ppui8StringDescriptors); + ASSERT(psGamepad->pfnCallback); + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psGamepad->sPrivateData.sHIDDevice; + + // + // Call the common initialization routine. + // + pvRetcode = USBDHIDGamepadCompositeInit(ui32Index, psGamepad, 0); + + pConfigDesc = (tConfigDescriptor *)g_pui8GameDescriptor; + pConfigDesc->bmAttributes = psGamepad->ui8PwrAttributes; + pConfigDesc->bMaxPower = (uint8_t)(psGamepad->ui16MaxPowermA / 2); + + // + // 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 + // gamepad. + // + pvRetcode = USBDHIDInit(ui32Index, psHIDDevice); + + return(psGamepad); + } + else + { + return((tUSBDHIDGamepadDevice *)0); + } +} + +//***************************************************************************** +// +//! Initializes HID gamepad device operation for a given USB controller. +//! +//! \param ui32Index is the index of the USB controller that is to be +//! initialized for HID gamepad device operation. +//! \param psGamepad points to a structure containing parameters +//! customizing the operation of the HID gamepad device. +//! \param psCompEntry is the composite device entry to initialize when +//! creating a composite device. +//! +//! This call is very similar to USBDHIDGamepadInit() except that it is used +//! for initializing an instance of the HID gamepad device for use in a +//! composite device. If this HID gamepad is part of a composite device, then +//! the \e psCompEntry should point to the composite device entry to +//! initialize. This entry is part of the array that is passed to the +//! USBDCompositeInit() function to start up and complete configuration of a +//! composite USB device. +//! +//! \return Returns NULL on failure or the \e psGamepad value that should be +//! used with the remaining USB HID gamepad APIs. +// +//***************************************************************************** +tUSBDHIDGamepadDevice * +USBDHIDGamepadCompositeInit(uint32_t ui32Index, + tUSBDHIDGamepadDevice *psGamepad, + tCompositeEntry *psCompEntry) +{ + tUSBDGamepadInstance *psInst; + tUSBDHIDDevice *psHIDDevice; + + // + // Check parameter validity. + // + ASSERT(psGamepad); + ASSERT(psGamepad->ppui8StringDescriptors); + ASSERT(psGamepad->pfnCallback); + + // + // Get a pointer to our instance data + // + psInst = &psGamepad->sPrivateData; + + // + // Initialize the various fields in our instance structure. + // + psInst->iState = eHIDGamepadStateNotConnected; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psInst->sHIDDevice; + + // + // Initialize the HID device class instance structure based on input from + // the caller. + // + psHIDDevice->ui16PID = psGamepad->ui16PID; + psHIDDevice->ui16VID = psGamepad->ui16VID; + psHIDDevice->ui16MaxPowermA = psGamepad->ui16MaxPowermA; + psHIDDevice->ui8PwrAttributes = psGamepad->ui8PwrAttributes; + psHIDDevice->ui8Subclass = 0; + psHIDDevice->ui8Protocol = 0; + psHIDDevice->ui8NumInputReports = 1; + psHIDDevice->psReportIdle = &psInst->sReportIdle; + psInst->sReportIdle.ui8Duration4mS = 125; + psInst->sReportIdle.ui8ReportID = 0; + psInst->sReportIdle.ui32TimeSinceReportmS = 0; + psInst->sReportIdle.ui16TimeTillNextmS = 0; + psHIDDevice->pfnTxCallback = HIDGamepadTxHandler; + psHIDDevice->pvRxCBData = (void *)psGamepad; + psHIDDevice->pfnRxCallback = HIDGamepadRxHandler; + psHIDDevice->pvTxCBData = (void *)psGamepad; + psHIDDevice->bUseOutEndpoint = false, + psHIDDevice->psHIDDescriptor = &g_sGameHIDDescriptor; + psHIDDevice->ppui8ClassDescriptors = g_ppui8GameClassDescriptors; + psHIDDevice->ppui8StringDescriptors = psGamepad->ppui8StringDescriptors; + psHIDDevice->ui32NumStringDescriptors = + psGamepad->ui32NumStringDescriptors; + psHIDDevice->ppsConfigDescriptor = g_ppsHIDConfigDescriptors; + + // + // If there was an override for the report descriptor then use it. + // + if(psGamepad->pui8ReportDescriptor) + { + // + // Save the report descriptor in the list of report descriptors. + // + g_ppui8GameClassDescriptors[0] = psGamepad->pui8ReportDescriptor; + + // + // Override the report descriptor size. + // + g_sGameHIDDescriptor.sClassDescriptor[0].wDescriptorLength = + psGamepad->ui32ReportSize; + } + + // + // Initialize the lower layer HID driver and pass it the various structures + // and descriptors necessary to declare that we are a gamepad. + // + return(USBDHIDCompositeInit(ui32Index, psHIDDevice, psCompEntry)); +} + +//***************************************************************************** +// +//! Schedules a report to be sent once the host requests more data. +//! +//! \param psHIDGamepad is the structure pointer that is returned from the +//! USBDHIDGamepadCompositeInit() or USBDHIDGamepadInit() functions. +//! \param pvReport is the data to send to the host. +//! \param ui32Size is the number of bytes in the \e pvReport buffer. +//! +//! This call is made by an application to schedule data to be sent to the +//! host when the host requests an update from the device. The application +//! must then wait for a \b USB_EVENT_TX_COMPLETE event in the function +//! provided in the \e pfnCallback pointer in the tUSBDHIDGamepadDevice +//! structure before being able to send more data with this function. The +//! pointer passed in the \e pvReport can be updated once this call returns as +//! the data has been copied from the buffer. The function returns +//! \b USBDGAMEPAD_SUCCESS if the transmission was successfully scheduled or +//! \b USBDGAMEPAD_TX_ERROR if the report could not be sent at this time. +//! If the call is made before the device is connected or ready to communicate +//! with the host, then the function can return \b USBDGAMEPAD_NOT_CONFIGURED. +//! +//! \return The function returns one of the \b USBDGAMEPAD_* values. +// +//***************************************************************************** +uint32_t +USBDHIDGamepadSendReport(tUSBDHIDGamepadDevice *psHIDGamepad, void *pvReport, + uint32_t ui32Size) +{ + uint32_t ui32Retcode, ui32Count; + tUSBDGamepadInstance *psInst; + tUSBDHIDDevice *psHIDDevice; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psHIDGamepad->sPrivateData.sHIDDevice; + + // + // Get a pointer to our instance data + // + psInst = &psHIDGamepad->sPrivateData; + + // + // If we are not configured, return an error here before trying to send + // anything. + // + if(psInst->iState == eHIDGamepadStateNotConnected) + { + return(USBDGAMEPAD_NOT_CONFIGURED); + } + + // + // Only send a report if the transmitter is currently free. + // + if(USBDHIDTxPacketAvailable((void *)psHIDDevice)) + { + // + // Send the report to the host. + // + psInst->iState = eHIDGamepadStateSending; + ui32Count = USBDHIDReportWrite((void *)psHIDDevice, pvReport, ui32Size, + true); + + // + // Did we schedule a packet for transmission correctly? + // + if(ui32Count == 0) + { + // + // No - report the error to the caller. + // + ui32Retcode = USBDGAMEPAD_TX_ERROR; + } + else + { + ui32Retcode = USBDGAMEPAD_SUCCESS; + } + } + else + { + ui32Retcode = USBDGAMEPAD_TX_ERROR; + } + + // + // Return the relevant error code to the caller. + // + return(ui32Retcode); +} + +//***************************************************************************** +// +//! Shuts down the HID gamepad device. +//! +//! \param psGamepad is the pointer to the device instance structure +//! as returned by USBDHIDGamepadInit() or USBDHIDGamepadCompositeInit(). +//! +//! This function terminates HID gamepad operation for the instance supplied +//! and removes the device from the USB bus. Following this call, the +//! \e psGamepad instance may not me used in any other call to the HID +//! gamepad device other than to reinitialize by calling USBDHIDGamepadInit() +//! or USBDHIDGamepadCompositeInit(). +//! +//! \return None. +// +//***************************************************************************** +void +USBDHIDGamepadTerm(tUSBDHIDGamepadDevice *psGamepad) +{ + tUSBDHIDDevice *psHIDDevice; + + ASSERT(psGamepad); + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psGamepad->sPrivateData.sHIDDevice; + + // + // Mark the device as no longer connected. + // + psGamepad->sPrivateData.iState = eHIDGamepadStateNotConnected; + + // + // Terminate the low level HID driver. + // + USBDHIDTerm(psHIDDevice); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/device/usbdhidgamepad.h b/usblib/device/usbdhidgamepad.h new file mode 100644 index 0000000..3610680 --- /dev/null +++ b/usblib/device/usbdhidgamepad.h @@ -0,0 +1,274 @@ +//***************************************************************************** +// +// usbdhidgame.h - The header information for using the USB libraries game pad +// device class. +// +// Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#ifndef __USBDHIDGAME_H__ +#define __USBDHIDGAME_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_gamepad_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// PRIVATE +// +// This enumeration holds the various states that the game pad can be in during +// normal operation. This should not be used by applications and is only +// here for memory allocation purposes. +// +//***************************************************************************** +typedef enum +{ + // + // Not yet configured. + // + eHIDGamepadStateNotConnected, + + // + // Nothing to transmit and not waiting on data to be sent. + // + eHIDGamepadStateIdle, + + // + // Waiting on data to be sent. + // + eHIDGamepadStateSending +} +tGamepadState; + +//***************************************************************************** +// +// PRIVATE +// +// This is the structure for an instance of a USB game pad device. This should +// not be used by applications and is only here for memory allocation purposes. +// +//***************************************************************************** +typedef struct +{ + // + // This is needed for the lower level HID driver. + // + tUSBDHIDDevice sHIDDevice; + + // + // The current state of the game pad device. + // + tGamepadState iState; + + // + // The idle timeout control structure for our input report. This is + // required by the lower level HID driver. + // + tHIDReportIdle sReportIdle; +} tUSBDGamepadInstance; + +//***************************************************************************** +// +//! This structure is used by the application to define operating parameters +//! for the HID game device. +// +//***************************************************************************** +typedef struct +{ + // + //! The vendor ID that this device is to present in the device descriptor. + // + const uint16_t ui16VID; + + // + //! The product ID that this device is to present in the device descriptor. + // + const uint16_t ui16PID; + + // + //! The maximum power consumption of the device, expressed in milliamps. + // + const uint16_t ui16MaxPowermA; + + // + //! Indicates whether the device is self- or bus-powered and whether or not + //! it supports remote wake up. Valid values are \b USB_CONF_ATTR_SELF_PWR + //! or \b USB_CONF_ATTR_BUS_PWR, optionally ORed with + //! \b USB_CONF_ATTR_RWAKE. + // + const uint8_t ui8PwrAttributes; + + // + //! A pointer to the callback function that is called to notify + //! the application of general events. This pointer must point to a valid + //! function. + // + const tUSBCallback pfnCallback; + + // + //! A client-supplied pointer that is sent as the first parameter in all + //! calls made to the pfnCallback gamedevice callback function. + // + 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 uint8_t * const *ppui8StringDescriptors; + + // + //! The number of descriptors provided in the \e ppStringDescriptors + //! array, which must be (1 + (5 * (number of languages))). + // + const uint32_t ui32NumStringDescriptors; + + // + //! Optional report descriptor if the application wants to use a custom + //! descriptor. + // + const uint8_t *pui8ReportDescriptor; + + // + //! The size of the optional report descriptor define in + //! pui8ReportDescriptor. + // + const uint32_t ui32ReportSize; + + // + //! The private instance data for this device. This memory must + //! remain accessible for as long as the game device is in use and + //! must not be modified by any code outside the HID game device driver. + // + tUSBDGamepadInstance sPrivateData; +} +tUSBDHIDGamepadDevice; + +//***************************************************************************** +// +//! The USBDHIDGamepadSendReport() call successfully scheduled the report. +// +//***************************************************************************** +#define USBDGAMEPAD_SUCCESS 0 + +//***************************************************************************** +// +//! The USBDHIDGamepadSendReport() function could not send the report at this +//! time. +// +//***************************************************************************** +#define USBDGAMEPAD_TX_ERROR 1 + +//***************************************************************************** +// +//! The device is not currently configured and cannot perform any operations. +// +//***************************************************************************** +#define USBDGAMEPAD_NOT_CONFIGURED \ + 2 + +//***************************************************************************** +// +//! This structure is the default packed report structure that is sent to the +//! host. The application can provide its own structure if the default report +//! descriptor is overridden by the application. This structure or an +//! application-defined structure is passed to the USBDHIDGamepadSendReport +//! function to send gamepad updates to the host. +// +//***************************************************************************** +typedef struct +{ + // + //! Signed 8-bit value (-128 to 127). + // + int8_t i8XPos; + + // + //! Signed 8-bit value (-128 to 127). + // + int8_t i8YPos; + + // + //! Signed 8-bit value (-128 to 127). + // + int8_t i8ZPos; + + // + //! 8-bit button mapping with button 1 in the LSB. + // + uint8_t ui8Buttons; +} +PACKED tGamepadReport; + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern tUSBDHIDGamepadDevice *USBDHIDGamepadInit(uint32_t ui32Index, + tUSBDHIDGamepadDevice *psHIDGamepad); +extern tUSBDHIDGamepadDevice *USBDHIDGamepadCompositeInit(uint32_t ui32Index, + tUSBDHIDGamepadDevice *psHIDGamepad, + tCompositeEntry *psCompEntry); +extern void USBDHIDGamepadTerm(tUSBDHIDGamepadDevice *psCompEntry); + +extern uint32_t USBDHIDGamepadSendReport(tUSBDHIDGamepadDevice *psHIDGamepad, + void *pvReport, uint32_t ui32Size); + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + +//***************************************************************************** +// +// Mark the end of the C bindings section for C++ compilers. +// +//***************************************************************************** +#ifdef __cplusplus +} +#endif + +#endif diff --git a/usblib/device/usbdhidkeyb.c b/usblib/device/usbdhidkeyb.c new file mode 100644 index 0000000..619714c --- /dev/null +++ b/usblib/device/usbdhidkeyb.c @@ -0,0 +1,1321 @@ +//***************************************************************************** +// +// usbdhidkeyb.c - USB HID Keyboard device class driver. +// +// Copyright (c) 2008-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.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 +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// 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. +// +//***************************************************************************** +static uint8_t g_pui8KeybDescriptor[] = +{ + // + // 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. +// +//***************************************************************************** +static uint8_t g_pui8HIDInterface[HIDINTERFACE_SIZE] = +{ + // + // 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. + 1, // The number of endpoints used by this + // interface. + USB_CLASS_HID, // The interface class + USB_HID_SCLASS_BOOT, // The interface sub-class. + USB_HID_PROTOCOL_KEYB, // The interface protocol for the sub-class + // specified above. + 4, // The string index for this interface. +}; + +static const uint8_t g_pui8HIDInEndpoint[HIDINENDPOINT_SIZE] = +{ + // + // Interrupt IN endpoint descriptor + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_IN | USBEPToIndex(USB_EP_1), + USB_EP_ATTR_INT, // Endpoint is an interrupt endpoint. + USBShort(USBFIFOSizeToBytes(USB_FIFO_SZ_64)), + // The maximum packet size. + 16, // The polling interval for this endpoint. +}; + +static const uint8_t g_pui8HIDOutEndpoint[HIDOUTENDPOINT_SIZE] = +{ + // + // Interrupt OUT endpoint descriptor + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_OUT | USBEPToIndex(USB_EP_2), + USB_EP_ATTR_INT, // Endpoint is an interrupt endpoint. + USBShort(USBFIFOSizeToBytes(USB_FIFO_SZ_64)), + // The maximum packet size. + 16, // The polling interval for this endpoint. +}; + +//***************************************************************************** +// +// The following is the HID report structure definition that is passed back +// to the host. +// +//***************************************************************************** +static const uint8_t g_pui8KeybReportDescriptor[] = +{ + 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 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_pui8KeybReportDescriptor) + // Size of report descriptor + } + } +}; + +//***************************************************************************** +// +// 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). +// +//***************************************************************************** +static const tConfigSection g_sHIDConfigSection = +{ + sizeof(g_pui8KeybDescriptor), + g_pui8KeybDescriptor +}; + +static const tConfigSection g_sHIDInterfaceSection = +{ + sizeof(g_pui8HIDInterface), + g_pui8HIDInterface +}; + +static const tConfigSection g_sHIDInEndpointSection = +{ + sizeof(g_pui8HIDInEndpoint), + g_pui8HIDInEndpoint +}; + +static const tConfigSection g_sHIDOutEndpointSection = +{ + sizeof(g_pui8HIDOutEndpoint), + g_pui8HIDOutEndpoint +}; + +//***************************************************************************** +// +// Place holder for the user's HID descriptor block. +// +//***************************************************************************** +static tConfigSection g_sHIDDescriptorSection = +{ + sizeof(g_sKeybHIDDescriptor), + (const uint8_t *)&g_sKeybHIDDescriptor +}; + +//***************************************************************************** +// +// This array lists all the sections that must be concatenated to make a +// single, complete HID configuration descriptor. +// +//***************************************************************************** +static const tConfigSection *g_psHIDSections[] = +{ + &g_sHIDConfigSection, + &g_sHIDInterfaceSection, + &g_sHIDDescriptorSection, + &g_sHIDInEndpointSection, + &g_sHIDOutEndpointSection +}; + +#define NUM_HID_SECTIONS ((sizeof(g_psHIDSections) / \ + sizeof(g_psHIDSections[0])) - 1) + +//***************************************************************************** +// +// 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. +// +//***************************************************************************** +static tConfigHeader g_sHIDConfigHeader = +{ + NUM_HID_SECTIONS, + g_psHIDSections +}; + +//***************************************************************************** +// +// Configuration Descriptor. +// +//***************************************************************************** +static const tConfigHeader * const g_ppsHIDConfigDescriptors[] = +{ + &g_sHIDConfigHeader +}; + +//***************************************************************************** +// +// The HID class descriptor table. For the keyboard class, we have only a +// single report descriptor. +// +//***************************************************************************** +static const uint8_t * const g_pui8KeybClassDescriptors[] = +{ + g_pui8KeybReportDescriptor +}; + +//***************************************************************************** +// +// Forward references for keyboard device callback functions. +// +//***************************************************************************** +static uint32_t HIDKeyboardRxHandler(void *pvKeyboardDevice, uint32_t ui32Event, + uint32_t ui32MsgData, void *pvMsgData); +static uint32_t HIDKeyboardTxHandler(void *pvKeyboardDevice, uint32_t ui32Event, + uint32_t ui32MsgData, void *pvMsgData); + +//***************************************************************************** +// +// Main HID device class event handler function. +// +// \param pvKeyboardDevice is the event callback pointer provided during +// USBDHIDInit().This is a pointer to our HID device structure +// (&g_sHIDKeybDevice). +// \param ui32Event identifies the event we are being called back for. +// \param ui32MsgData 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 uint32_t +HIDKeyboardRxHandler(void *pvKeyboardDevice, uint32_t ui32Event, + uint32_t ui32MsgData, void *pvMsgData) +{ + tHIDKeyboardInstance *psInst; + tUSBDHIDKeyboardDevice *psKeyboardDevice; + + // + // Make sure we did not get a NULL pointer. + // + ASSERT(pvKeyboardDevice); + + // + // Get a pointer to our instance data + // + psKeyboardDevice = (tUSBDHIDKeyboardDevice *)pvKeyboardDevice; + psInst = &psKeyboardDevice->sPrivateData; + + // + // Which event were we sent? + // + switch (ui32Event) + { + // + // The host has connected to us and configured the device. + // + case USB_EVENT_CONNECTED: + { + psInst->ui8USBConfigured = true; + + // + // Pass the information on to the client. + // + psKeyboardDevice->pfnCallback(psKeyboardDevice->pvCBData, + USB_EVENT_CONNECTED, 0, (void *)0); + + break; + } + + // + // The host has disconnected from us. + // + case USB_EVENT_DISCONNECTED: + { + psInst->ui8USBConfigured = false; + + // + // Pass the information on to the client. + // + psKeyboardDevice->pfnCallback(psKeyboardDevice->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 ui32MsgValue parameter in this case. Set the report pointer + // in *pvMsgData and return the length of the report in bytes. + // + *(uint8_t **)pvMsgData = psInst->pui8Report; + 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((uint32_t)pvMsgData == KEYB_OUT_REPORT_SIZE ) + { + // + // Yes - return our pointer. + // + return((uint32_t)psInst->pui8DataBuffer); + } + 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->ui8LEDStates != psInst->pui8DataBuffer[0]) + { + // + // Note the new LED states. + // + psInst->ui8LEDStates = psInst->pui8DataBuffer[0]; + + // + // Pass the information on to the client. + // + psKeyboardDevice->pfnCallback( + psKeyboardDevice->pvCBData, + USBD_HID_KEYB_EVENT_SET_LEDS, + psInst->pui8DataBuffer[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->ui8Protocol = ui32MsgData; + 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->ui8Protocol); + } + + // + // Pass ERROR, SUSPEND and RESUME to the client unchanged. + // + case USB_EVENT_ERROR: + case USB_EVENT_SUSPEND: + case USB_EVENT_RESUME: + case USB_EVENT_LPM_RESUME: + case USB_EVENT_LPM_SLEEP: + case USB_EVENT_LPM_ERROR: + { + return(psKeyboardDevice->pfnCallback( + psKeyboardDevice->pvCBData, + ui32Event, ui32MsgData, pvMsgData)); + } + + // + // We ignore all other events. + // + default: + { + break; + } + } + return(0); +} + +//***************************************************************************** +// +// HID device class transmit channel event handler function. +// +// \param pvKeyboardDevice is the event callback pointer provided during +// USBDHIDInit(). This is a pointer to our HID device structure +// (&g_sHIDKeybDevice). +// \param ui32Event identifies the event we are being called back for. +// \param ui32MsgData 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 uint32_t +HIDKeyboardTxHandler(void *pvKeyboardDevice, uint32_t ui32Event, + uint32_t ui32MsgData, void *pvMsgData) +{ + tHIDKeyboardInstance *psInst; + tUSBDHIDKeyboardDevice *psHIDKbDevice; + tUSBDHIDDevice *psHIDDevice; + uint32_t ui32Count; + + // + // Make sure we did not get a NULL pointer. + // + ASSERT(pvKeyboardDevice); + + // + // Get a pointer to our instance data + // + psHIDKbDevice = (tUSBDHIDKeyboardDevice *)pvKeyboardDevice; + psInst = &psHIDKbDevice->sPrivateData; + psHIDDevice = &psInst->sHIDDevice; + + // + // Which event were we sent? + // + switch (ui32Event) + { + // + // 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. + // + ui32Count = USBDHIDReportWrite((void *)psHIDDevice, + psInst->pui8Report, + KEYB_IN_REPORT_SIZE, true); + + // + // If we scheduled the report for transmission, clear the + // change flag. + // + if(ui32Count != 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. + // + psHIDKbDevice->pfnCallback(psHIDKbDevice->pvCBData, + USB_EVENT_TX_COMPLETE, ui32MsgData, + (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 ui8UsageCode 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 bool +AddKeyToPressedList(tHIDKeyboardInstance *psInst, uint8_t ui8UsageCode) +{ + uint32_t ui32Loop; + bool 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(ui32Loop = 0; ui32Loop < (uint32_t)psInst->ui8KeyCount; ui32Loop++) + { + // + // Is this key already included in the list of keys in the pressed + // state? + // + if(ui8UsageCode == psInst->pui8KeysPressed[ui32Loop]) + { + // + // 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(ui32Loop >= psInst->ui8KeyCount) + { + if(psInst->ui8KeyCount < KEYB_MAX_CHARS_PER_REPORT) + { + // + // We have room so store the new key press in the list. + // + psInst->pui8KeysPressed[psInst->ui8KeyCount] = ui8UsageCode; + psInst->ui8KeyCount++; + 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 ui8UsageCode 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 has 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 has not +// changed. +// +//***************************************************************************** +static bool +RemoveKeyFromPressedList(tHIDKeyboardInstance *psInst, + uint8_t ui8UsageCode) +{ + uint32_t ui32Loop; + uint32_t ui32Pos; + + // + // Keep the compiler happy by setting ui32Pos to something. + // + ui32Pos = 0; + + // + // Find the usage code in the current list. + // + for(ui32Loop = 0; ui32Loop < KEYB_MAX_CHARS_PER_REPORT; ui32Loop++) + { + if(psInst->pui8KeysPressed[ui32Loop] == ui8UsageCode) + { + ui32Pos = ui32Loop; + break; + } + } + + // + // If we dropped out at the end of the loop, we could not find the code so + // just return false. + // + if(ui32Loop == 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(ui32Loop = (ui32Pos + 1); ui32Loop < KEYB_MAX_CHARS_PER_REPORT; + ui32Loop++) + { + psInst->pui8KeysPressed[ui32Loop - 1] = + psInst->pui8KeysPressed[ui32Loop]; + } + + // + // Clear the last entry in the array and adjust the number of keys in the + // array. + // + psInst->pui8KeysPressed[KEYB_MAX_CHARS_PER_REPORT - 1] = + HID_KEYB_USAGE_RESERVED; + psInst->ui8KeyCount--; + + // + // Tell the caller we were successful. + // + return(true); +} + +//***************************************************************************** +// +//! Initializes HID keyboard device operation for a given USB controller. +//! +//! \param ui32Index is the index of the USB controller which is to be +//! initialized for HID keyboard device operation. +//! \param psHIDKbDevice 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 returns the \e psHIDKbDevice +//! 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 +//! receives \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 causes unpredictable (though almost certainly +//! unpleasant) behavior. +//! +//! \return Returns NULL on failure or the \e psHIDKbDevice pointer on success. +// +//***************************************************************************** +void * +USBDHIDKeyboardInit(uint32_t ui32Index, tUSBDHIDKeyboardDevice *psHIDKbDevice) +{ + void *pvRetcode; + tUSBDHIDDevice *psHIDDevice; + tConfigDescriptor *pConfigDesc; + + // + // Check parameter validity. + // + ASSERT(psHIDKbDevice); + ASSERT(psHIDKbDevice->ppui8StringDescriptors); + ASSERT(psHIDKbDevice->pfnCallback); + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psHIDKbDevice->sPrivateData.sHIDDevice; + + // + // Call the common initialization routine. + // + pvRetcode = USBDHIDKeyboardCompositeInit(ui32Index, psHIDKbDevice, 0); + + pConfigDesc = (tConfigDescriptor *)g_pui8KeybDescriptor; + pConfigDesc->bmAttributes = psHIDKbDevice->ui8PwrAttributes; + pConfigDesc->bMaxPower = (uint8_t)(psHIDKbDevice->ui16MaxPowermA / 2); + + // + // 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(ui32Index, psHIDDevice); + + return((void *)psHIDKbDevice); + } + else + { + return((void *)0); + } +} + +//***************************************************************************** +// +//! Initializes HID keyboard device operation for a given USB controller. +//! +//! \param ui32Index is the index of the USB controller which is to be +//! initialized for HID keyboard device operation. +//! \param psHIDKbDevice points to a structure containing parameters +//! customizing the operation of the HID keyboard device. +//! \param psCompEntry is the composite device entry to initialize when +//! creating a composite device. +//! +//! This call is very similar to USBDHIDKeyboardInit() except that it is used +//! for initializing an instance of the HID keyboard device for use in a +//! composite device. If this HID keyboard is part of a composite device, then +//! the \e psCompEntry should point to the composite device entry to +//! initialize. This is part of the array that is passed to the +//! USBDCompositeInit() function. +//! +//! \return Returns zero on failure or a non-zero instance value that should be +//! used with the remaining USB HID Keyboard APIs. +// +//***************************************************************************** +void * +USBDHIDKeyboardCompositeInit(uint32_t ui32Index, + tUSBDHIDKeyboardDevice *psHIDKbDevice, + tCompositeEntry *psCompEntry) +{ + tHIDKeyboardInstance *psInst; + uint32_t ui32Loop; + tUSBDHIDDevice *psHIDDevice; + + // + // Check parameter validity. + // + ASSERT(psHIDKbDevice); + ASSERT(psHIDKbDevice->ppui8StringDescriptors); + ASSERT(psHIDKbDevice->pfnCallback); + + // + // Get a pointer to our instance data + // + psInst = &psHIDKbDevice->sPrivateData; + + // + // Initialize the various fields in our instance structure. + // + psInst->ui8USBConfigured = 0; + psInst->ui8Protocol = USB_HID_PROTOCOL_REPORT; + psInst->sReportIdle.ui8Duration4mS = 125; + psInst->sReportIdle.ui8ReportID = 0; + psInst->sReportIdle.ui32TimeSinceReportmS = 0; + psInst->sReportIdle.ui16TimeTillNextmS = 0; + psInst->ui8LEDStates = 0; + psInst->ui8KeyCount = 0; + for(ui32Loop = 0; ui32Loop < KEYB_MAX_CHARS_PER_REPORT; ui32Loop++) + { + psInst->pui8KeysPressed[ui32Loop] = HID_KEYB_USAGE_RESERVED; + } + + psInst->eKeyboardState = HID_KEYBOARD_STATE_UNCONFIGURED; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psInst->sHIDDevice; + + // + // Initialize the HID device class instance structure based on input from + // the caller. + // + psHIDDevice->ui16PID = psHIDKbDevice->ui16PID; + psHIDDevice->ui16VID = psHIDKbDevice->ui16VID; + psHIDDevice->ui16MaxPowermA = psHIDKbDevice->ui16MaxPowermA; + psHIDDevice->ui8PwrAttributes = psHIDKbDevice->ui8PwrAttributes; + psHIDDevice->ui8Subclass = USB_HID_SCLASS_BOOT; + psHIDDevice->ui8Protocol = USB_HID_PROTOCOL_KEYB; + psHIDDevice->ui8NumInputReports = 1; + psHIDDevice->psReportIdle = 0; + psHIDDevice->pfnRxCallback = HIDKeyboardRxHandler; + psHIDDevice->pvRxCBData = (void *)psHIDKbDevice; + psHIDDevice->pfnTxCallback = HIDKeyboardTxHandler; + psHIDDevice->pvTxCBData = (void *)psHIDKbDevice; + psHIDDevice->bUseOutEndpoint = false, + + psHIDDevice->psHIDDescriptor = &g_sKeybHIDDescriptor; + psHIDDevice->ppui8ClassDescriptors = g_pui8KeybClassDescriptors; + psHIDDevice->ppui8StringDescriptors = + psHIDKbDevice->ppui8StringDescriptors; + psHIDDevice->ui32NumStringDescriptors = + psHIDKbDevice->ui32NumStringDescriptors; + psHIDDevice->ppsConfigDescriptor = g_ppsHIDConfigDescriptors; + + 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(ui32Index, psHIDDevice, psCompEntry)); +} + +//***************************************************************************** +// +//! Shuts down the HID keyboard device. +//! +//! \param pvKeyboardDevice 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 pvKeyboardDevice instance may not me used in any other call to the HID +//! keyboard device other than USBDHIDKeyboardInit(). +//! +//! \return None. +// +//***************************************************************************** +void +USBDHIDKeyboardTerm(void *pvKeyboardDevice) +{ + tUSBDHIDKeyboardDevice *psHIDKbDevice; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvKeyboardDevice); + + // + // Get a pointer to the device. + // + psHIDKbDevice = (tUSBDHIDKeyboardDevice *)pvKeyboardDevice; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psHIDKbDevice->sPrivateData.sHIDDevice; + + // + // Mark the device as no longer configured. + // + psHIDKbDevice->sPrivateData.ui8USBConfigured = 0; + + // + // Terminate the low level HID driver. + // + USBDHIDTerm(psHIDDevice); +} + +//***************************************************************************** +// +//! Sets the client-specific pointer parameter for the keyboard callback. +//! +//! \param pvKeyboardDevice 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 \e pvKeyboardDevice structure passed to +//! USBDHIDKeyboardInit() resides in RAM. If this structure is in flash, +//! callback data changes is not possible. +//! +//! \return Returns the previous callback pointer that was set for this +//! instance. +// +//***************************************************************************** +void * +USBDHIDKeyboardSetCBData(void *pvKeyboardDevice, void *pvCBData) +{ + void *pvOldCBData; + tUSBDHIDKeyboardDevice *psKeyboard; + + // + // Check for a NULL pointer in the device parameter. + // + ASSERT(pvKeyboardDevice); + + // + // Get a pointer to our keyboard device. + // + psKeyboard = (tUSBDHIDKeyboardDevice *)pvKeyboardDevice; + + // + // 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 pvKeyboardDevice is the pointer to the device instance structure +//! as returned by USBDHIDKeyboardInit(). +//! \param ui8Modifiers 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 ui8UsageCode 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 contains the +//! rollover error code, \b HID_KEYB_USAGE_ROLLOVER instead of key usage codes +//! and the caller receives return code \b 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. +// +//***************************************************************************** +uint32_t +USBDHIDKeyboardKeyStateChange(void *pvKeyboardDevice, uint8_t ui8Modifiers, + uint8_t ui8UsageCode, bool bPress) +{ + bool bRetcode; + uint32_t ui32Loop, ui32Count; + tHIDKeyboardInstance *psInst; + tUSBDHIDKeyboardDevice *psHIDKbDevice; + tUSBDHIDDevice *psHIDDevice; + + psHIDKbDevice = (tUSBDHIDKeyboardDevice *)pvKeyboardDevice; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psHIDKbDevice->sPrivateData.sHIDDevice; + + // + // Assume all is well until we determine otherwise. + // + bRetcode = true; + + // + // Get a pointer to our instance data + // + psInst = &psHIDKbDevice->sPrivateData; + + // + // Update the global keyboard report with the information passed. + // + psInst->pui8Report[0] = ui8Modifiers; + psInst->pui8Report[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(ui8UsageCode != 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, ui8UsageCode); + } + else + { + // + // A key has been released - remove it from the list. + // + bRetcode = RemoveKeyFromPressedList(psInst, ui8UsageCode); + + // + // 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(ui32Loop = 0; ui32Loop < KEYB_MAX_CHARS_PER_REPORT; ui32Loop++) + { + psInst->pui8Report[2 + ui32Loop] = (bRetcode ? + psInst->pui8KeysPressed[ui32Loop] : HID_KEYB_USAGE_ROLLOVER); + } + } + + // + // If we are not configured, return an error here before trying to send + // anything. + // + if(!psInst->ui8USBConfigured) + { + 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; + ui32Count = USBDHIDReportWrite((void *)psHIDDevice, + psInst->pui8Report, KEYB_IN_REPORT_SIZE, + true); + + // + // Did we schedule a packet for transmission correctly? + // + if(!ui32Count) + { + // + // 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); +} +#ifndef DEPRECATED + +//***************************************************************************** +// +//! Reports the device power status (bus or self powered) to the USB library. +//! +//! \param pvKeyboardDevice is the pointer to the keyboard device instance +//! structure. +//! \param ui8Power 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 *pvKeyboardDevice, uint8_t ui8Power) +{ + tUSBDHIDKeyboardDevice *psHIDKbDevice; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvKeyboardDevice); + + // + // Get the keyboard device pointer. + // + psHIDKbDevice = (tUSBDHIDKeyboardDevice *)pvKeyboardDevice; + + // + // Get a pointer to the HID device data. + + psHIDDevice = &psHIDKbDevice->sPrivateData.sHIDDevice; + + // + // Pass the request through to the lower layer. + // + USBDHIDPowerStatusSet((void *)psHIDDevice, ui8Power); +} +#endif + +//***************************************************************************** +// +//! Requests a remote wake up to resume communication when in suspended state. +//! +//! \param pvKeyboardDevice 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 causes the bus +//! to resume operation within 20mS. If the host has disabled remote wake up, +//! \b false is 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. +// +//***************************************************************************** +bool +USBDHIDKeyboardRemoteWakeupRequest(void *pvKeyboardDevice) +{ + tUSBDHIDKeyboardDevice *psHIDKbDevice; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvKeyboardDevice); + + // + // Get the keyboard device pointer. + // + psHIDKbDevice = (tUSBDHIDKeyboardDevice *)pvKeyboardDevice; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psHIDKbDevice->sPrivateData.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..c509b9f --- /dev/null +++ b/usblib/device/usbdhidkeyb.h @@ -0,0 +1,365 @@ +//***************************************************************************** +// +// usbdhidkeyb.h - Definitions used by HID keyboard class devices. +// +// Copyright (c) 2008-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#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 results 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. + // + uint8_t ui8USBConfigured; + + // + // The protocol requested by the host, USB_HID_PROTOCOL_BOOT or + // USB_HID_PROTOCOL_REPORT. + // + uint8_t ui8Protocol; + + // + // The current states that the keyboard LEDs are to be set to. + // + volatile uint8_t ui8LEDStates; + + // + // The total number of keys currently pressed. This indicates the number + // of key press entries in the pui8KeysPressed array. + // + uint8_t ui8KeyCount; + + // + // 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 bool bChangeMade; + + // + // A buffer used to receive output reports from the host. + // + uint8_t pui8DataBuffer[KEYB_OUT_REPORT_SIZE]; + + // + // A buffer used to hold the last input report sent to the host. + // + uint8_t pui8Report[KEYB_IN_REPORT_SIZE]; + + // + // A buffer containing the usage codes of all non-modifier keys currently + // in the pressed state. + // + uint8_t pui8KeysPressed[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; + + // + // This is needed for the lower level HID driver. + // + tUSBDHIDDevice sHIDDevice; +} +tHIDKeyboardInstance; + +//***************************************************************************** +// +//! 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. + // + const uint16_t ui16VID; + + // + //! The product ID that this device is to present in the device descriptor. + // + const uint16_t ui16PID; + + // + //! The maximum power consumption of the device, expressed in milliamps. + // + const uint16_t ui16MaxPowermA; + + // + //! Indicates whether the device is self- or bus-powered and whether or not + //! it supports remote wakeup. Valid values are \b USB_CONF_ATTR_SELF_PWR + //! or \b USB_CONF_ATTR_BUS_PWR, optionally ORed with + //! \b USB_CONF_ATTR_RWAKE. + // + const uint8_t ui8PwrAttributes; + + //! A pointer to the callback function which is called to notify + //! the application of general events and those related to reception of + //! Output and Feature reports via the (optional) interrupt OUT endpoint. + // + const tUSBCallback pfnCallback; + + // + //! A client-supplied pointer which is 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 uint8_t * const *ppui8StringDescriptors; + + // + //! The number of descriptors provided in the ppStringDescriptors + //! array. This must be (1 + (5 * (num languages))). + // + const uint32_t ui32NumStringDescriptors; + + // + //! The 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 sPrivateData; +} +tUSBDHIDKeyboardDevice; + +//***************************************************************************** +// +// Keyboard-specific device class driver events +// +//***************************************************************************** + +//***************************************************************************** +// +//! This event indicates that the keyboard LED states are to be set. The +//! ui32MsgValue 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 \b HID_KEYB_NUM_LOCK, +//! \b HID_KEYB_CAPS_LOCK, \b HID_KEYB_SCROLL_LOCK, \b HID_KEYB_COMPOSE and +//! \b 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 reports 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 ui8UsageCode 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 is stored and passed to the host once configuration completes. +// +//***************************************************************************** +#define KEYB_ERR_NOT_CONFIGURED 4 + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void *USBDHIDKeyboardInit(uint32_t ui32Index, + tUSBDHIDKeyboardDevice *psHIDKbDevice); +extern void *USBDHIDKeyboardCompositeInit(uint32_t ui32Index, + tUSBDHIDKeyboardDevice *psHIDKbDevice, + tCompositeEntry *psCompEntry); +extern void USBDHIDKeyboardTerm(void *pvKeyboardInstance); +extern void *USBDHIDKeyboardSetCBData(void *pvKeyboardInstance, + void *pvCBData); +extern uint32_t USBDHIDKeyboardKeyStateChange(void *pvKeyboardInstance, + uint8_t ui8Modifiers, + uint8_t ui8UsageCode, + bool bPressed); +extern void USBDHIDKeyboardPowerStatusSet(void *pvKeyboardInstance, + uint8_t ui8Power); +extern bool USBDHIDKeyboardRemoteWakeupRequest(void *pvKeyboardInstance); + +//***************************************************************************** +// +// 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..8b8a95a --- /dev/null +++ b/usblib/device/usbdhidmouse.c @@ -0,0 +1,1008 @@ +//***************************************************************************** +// +// usbdhidmouse.c - USB HID Mouse device class driver. +// +// Copyright (c) 2008-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.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 +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// 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. +// +//***************************************************************************** +uint8_t g_pui8MouseDescriptor[] = +{ + // + // 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. +// +//***************************************************************************** +uint8_t g_pui8HIDInterface[HIDINTERFACE_SIZE] = +{ + // + // 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. + 1, // The number of endpoints used by this + // interface. + USB_CLASS_HID, // The interface class + USB_HID_SCLASS_BOOT, // The interface sub-class. + USB_HID_PROTOCOL_MOUSE, // The interface protocol for the sub-class + // specified above. + 4, // The string index for this interface. +}; + +const uint8_t g_pui8HIDInEndpoint[HIDINENDPOINT_SIZE] = +{ + // + // Interrupt IN endpoint descriptor + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_IN | USBEPToIndex(USB_EP_1), + USB_EP_ATTR_INT, // Endpoint is an interrupt endpoint. + USBShort(USBFIFOSizeToBytes(USB_FIFO_SZ_64)), + // The maximum packet size. + 16, // The polling interval for this endpoint. +}; + +//***************************************************************************** +// +// The report descriptor for the mouse class device. +// +//***************************************************************************** +static const uint8_t g_pui8MouseReportDescriptor[] = +{ + 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), + + EndCollection, + EndCollection, +}; + +//***************************************************************************** +// +// 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_pui8MouseReportDescriptor) + // Size of report descriptor + } + } +}; + +//***************************************************************************** +// +// 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_pui8MouseDescriptor), + g_pui8MouseDescriptor +}; + +const tConfigSection g_sHIDInterfaceSection = +{ + sizeof(g_pui8HIDInterface), + g_pui8HIDInterface +}; + +const tConfigSection g_sHIDInEndpointSection = +{ + sizeof(g_pui8HIDInEndpoint), + g_pui8HIDInEndpoint +}; + +//***************************************************************************** +// +// Place holder for the user's HID descriptor block. +// +//***************************************************************************** +tConfigSection g_sHIDDescriptorSection = +{ + sizeof(g_sMouseHIDDescriptor), + (const uint8_t *)&g_sMouseHIDDescriptor +}; + +//***************************************************************************** +// +// 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, +}; + +#define NUM_HID_SECTIONS (sizeof(g_psHIDSections) / \ + sizeof(g_psHIDSections[0])) + +//***************************************************************************** +// +// The header for the single configuration we support. This is the root of +// the data structure that defines all the bits and pieces that are pulled +// together to generate the configuration descriptor. 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_ppsHIDConfigDescriptors[] = +{ + &g_sHIDConfigHeader +}; + +//***************************************************************************** +// +// The HID class descriptor table. For the mouse class, we have only a single +// report descriptor. +// +//***************************************************************************** +static const uint8_t * const g_pui8MouseClassDescriptors[] = +{ + g_pui8MouseReportDescriptor +}; + +//***************************************************************************** +// +// Forward references for mouse device callback functions. +// +//***************************************************************************** +static uint32_t HIDMouseRxHandler(void *pvMouseDevice, uint32_t ui32Event, + uint32_t ui32MsgData, void *pvMsgData); +static uint32_t HIDMouseTxHandler(void *pvMouseDevice, uint32_t ui32Event, + uint32_t ui32MsgData, 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 pvMouseDevice is the event callback pointer provided during +// USBDHIDInit(). This is a pointer to our HID device structure +// (&g_sHIDMouseDevice). +// \param ui32Event identifies the event we are being called back for. +// \param ui32MsgData 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 uint32_t +HIDMouseRxHandler(void *pvMouseDevice, uint32_t ui32Event, + uint32_t ui32MsgData, void *pvMsgData) +{ + tHIDMouseInstance *psInst; + tUSBDHIDMouseDevice *psMouseDevice; + + // + // Make sure we did not get a NULL pointer. + // + ASSERT(pvMouseDevice); + + // + // Get a pointer to our instance data + // + psMouseDevice = (tUSBDHIDMouseDevice *)pvMouseDevice; + psInst = &psMouseDevice->sPrivateData; + + // + // Which event were we sent? + // + switch(ui32Event) + { + // + // The host has connected to us and configured the device. + // + case USB_EVENT_CONNECTED: + { + psInst->ui8USBConfigured = true; + + // + // Pass the information on to the client. + // + psMouseDevice->pfnCallback(psMouseDevice->pvCBData, + USB_EVENT_CONNECTED, 0, (void *)0); + + break; + } + + // + // The host has disconnected from us. + // + case USB_EVENT_DISCONNECTED: + { + psInst->ui8USBConfigured = false; + + // + // Pass the information on to the client. + // + psMouseDevice->pfnCallback(psMouseDevice->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 ui32MsgValue parameter in this case. Set the report pointer + // in *pvMsgData and return the length of the report in bytes. + // + *(uint8_t **)pvMsgData = psInst->pui8Report; + 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->ui8Protocol = ui32MsgData; + 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->ui8Protocol); + } + + // + // Pass ERROR, SUSPEND and RESUME to the client unchanged. + // + case USB_EVENT_ERROR: + case USB_EVENT_SUSPEND: + case USB_EVENT_RESUME: + case USB_EVENT_LPM_RESUME: + case USB_EVENT_LPM_SLEEP: + case USB_EVENT_LPM_ERROR: + { + return(psMouseDevice->pfnCallback(psMouseDevice->pvCBData, + ui32Event, ui32MsgData, + pvMsgData)); + } + + // + // We ignore all other events. + // + default: + { + break; + } + } + return(0); +} + +//***************************************************************************** +// +// HID device class transmit channel event handler function. +// +// \param pvMouseDevice is the event callback pointer provided during +// USBDHIDInit(). This is a pointer to our HID device structure +// (&g_sHIDMouseDevice). +// \param ui32Event identifies the event we are being called back for. +// \param ui32MsgData 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 uint32_t +HIDMouseTxHandler(void *pvMouseDevice, uint32_t ui32Event, + uint32_t ui32MsgData, void *pvMsgData) +{ + tHIDMouseInstance *psInst; + tUSBDHIDMouseDevice *psMouseDevice; + + // + // Make sure we did not get a NULL pointer. + // + ASSERT(pvMouseDevice); + + // + // Get a pointer to our instance data + // + psMouseDevice = (tUSBDHIDMouseDevice *)pvMouseDevice; + psInst = &psMouseDevice->sPrivateData; + + // + // Which event were we sent? + // + switch (ui32Event) + { + // + // A report transmitted via the interrupt IN endpoint was acknowledged + // by the host. + // + case USB_EVENT_TX_COMPLETE: + { + // + // Our last transmission is complete. + // + psInst->iMouseState = eHIDMouseStateIdle; + + // + // Pass the event on to the client. + // + psMouseDevice->pfnCallback(psMouseDevice->pvCBData, + USB_EVENT_TX_COMPLETE, ui32MsgData, + (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 ui32Index is the index of the USB controller which is to be +//! initialized for HID mouse device operation. +//! \param psMouseDevice 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 returns the \e psMouseDevice +//! 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 +//! receives \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 causes unpredictable (though almost certainly unpleasant) +//! behavior. +//! +//! \return Returns NULL on failure or the psMouseDevice pointer on success. +// +//***************************************************************************** +void * +USBDHIDMouseInit(uint32_t ui32Index, tUSBDHIDMouseDevice *psMouseDevice) +{ + void *pvRetcode; + tUSBDHIDDevice *psHIDDevice; + tConfigDescriptor *pConfigDesc; + + // + // Check parameter validity. + // + ASSERT(psMouseDevice); + ASSERT(psMouseDevice->ppui8StringDescriptors); + ASSERT(psMouseDevice->pfnCallback); + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psMouseDevice->sPrivateData.sHIDDevice; + + pConfigDesc = (tConfigDescriptor *)g_pui8MouseDescriptor; + pConfigDesc->bmAttributes = psMouseDevice->ui8PwrAttributes; + pConfigDesc->bMaxPower = (uint8_t)(psMouseDevice->ui16MaxPowermA / 2); + + // + // Call the common initialization routine. + // + pvRetcode = USBDHIDMouseCompositeInit(ui32Index, psMouseDevice, 0); + + // + // 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(ui32Index, psHIDDevice); + + return((void *)psMouseDevice); + } + else + { + return((void *)0); + } +} + +//***************************************************************************** +// +//! Initializes HID mouse device operation for a given USB controller. +//! +//! \param ui32Index is the index of the USB controller which is to be +//! initialized for HID mouse device operation. +//! \param psMouseDevice points to a structure containing parameters +//! customizing the operation of the HID mouse device. +//! \param psCompEntry is the composite device entry to initialize when +//! creating a composite 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. If this HID mouse is part of a composite device, then the +//! \e psCompEntry should point to the composite device entry to initialize. +//! This is part of the array that is passed to the USBDCompositeInit() +//! function. +//! +//! \return Returns zero on failure or a non-zero instance value that should be +//! used with the remaining USB HID Mouse APIs. +// +//***************************************************************************** +void * +USBDHIDMouseCompositeInit(uint32_t ui32Index, + tUSBDHIDMouseDevice *psMouseDevice, + tCompositeEntry *psCompEntry) +{ + tHIDMouseInstance *psInst; + tUSBDHIDDevice *psHIDDevice; + + + // + // Check parameter validity. + // + ASSERT(psMouseDevice); + ASSERT(psMouseDevice->ppui8StringDescriptors); + ASSERT(psMouseDevice->pfnCallback); + + // + // Get a pointer to our instance data + // + psInst = &psMouseDevice->sPrivateData; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psMouseDevice->sPrivateData.sHIDDevice; + + // + // Initialize the various fields in our instance structure. + // + psInst->ui8USBConfigured = 0; + psInst->ui8Protocol = USB_HID_PROTOCOL_REPORT; + psInst->sReportIdle.ui8Duration4mS = 0; + psInst->sReportIdle.ui8ReportID = 0; + psInst->sReportIdle.ui32TimeSinceReportmS = 0; + psInst->sReportIdle.ui16TimeTillNextmS = 0; + psInst->iMouseState = eHIDMouseStateUnconfigured; + + // + // Initialize the HID device class instance structure based on input from + // the caller. + // + psHIDDevice->ui16PID = psMouseDevice->ui16PID; + psHIDDevice->ui16VID = psMouseDevice->ui16VID; + psHIDDevice->ui16MaxPowermA = psMouseDevice->ui16MaxPowermA; + psHIDDevice->ui8PwrAttributes = psMouseDevice->ui8PwrAttributes; + psHIDDevice->ui8Subclass = USB_HID_SCLASS_BOOT; + psHIDDevice->ui8Protocol = USB_HID_PROTOCOL_MOUSE; + psHIDDevice->ui8NumInputReports = 1; + psHIDDevice->psReportIdle = &psInst->sReportIdle; + psHIDDevice->pfnRxCallback = HIDMouseRxHandler; + psHIDDevice->pvRxCBData = (void *)psMouseDevice; + psHIDDevice->pfnTxCallback = HIDMouseTxHandler; + psHIDDevice->pvTxCBData = (void *)psMouseDevice; + psHIDDevice->bUseOutEndpoint = false; + psHIDDevice->psHIDDescriptor = &g_sMouseHIDDescriptor; + psHIDDevice->ppui8ClassDescriptors = g_pui8MouseClassDescriptors; + psHIDDevice->ppui8StringDescriptors = + psMouseDevice->ppui8StringDescriptors; + psHIDDevice->ui32NumStringDescriptors = + psMouseDevice->ui32NumStringDescriptors; + psHIDDevice->ppsConfigDescriptor = g_ppsHIDConfigDescriptors; + + // + // Initialize the lower layer HID driver and pass it the various structures + // and descriptors necessary to declare that we are a keyboard. + // + return(USBDHIDCompositeInit(ui32Index, psHIDDevice, psCompEntry)); +} + +//***************************************************************************** +// +//! Shuts down the HID mouse device. +//! +//! \param pvMouseDevice 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 pvMouseDevice instance may not me used in any other call to the HID +//! mouse device other than USBDHIDMouseInit(). +//! +//! \return None. +// +//***************************************************************************** +void +USBDHIDMouseTerm(void *pvMouseDevice) +{ + tUSBDHIDMouseDevice *psMouseDevice; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvMouseDevice); + + // + // Get a pointer to the device. + // + psMouseDevice = (tUSBDHIDMouseDevice *)pvMouseDevice; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psMouseDevice->sPrivateData.sHIDDevice; + + // + // Mark our device as no longer configured. + // + psMouseDevice->sPrivateData.ui8USBConfigured = 0; + + // + // Terminate the low level HID driver. + // + USBDHIDTerm(psHIDDevice); +} + +//***************************************************************************** +// +//! Sets the client-specific pointer parameter for the mouse callback. +//! +//! \param pvMouseDevice 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 pvMouseDevice structure passed to USBDHIDMouseInit() +//! resides in RAM. If this structure is in flash, callback data changes are +//! not possible. +//! +//! \return Returns the previous callback pointer that was set for this +//! instance. +// +//***************************************************************************** +void * +USBDHIDMouseSetCBData(void *pvMouseDevice, void *pvCBData) +{ + void *pvOldCBData; + tUSBDHIDMouseDevice *psMouse; + + // + // Check for a NULL pointer in the device parameter. + // + ASSERT(pvMouseDevice); + + // + // Get a pointer to our mouse device. + // + psMouse = (tUSBDHIDMouseDevice *)pvMouseDevice; + + // + // 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 pvMouseDevice is the pointer to the mouse device instance structure. +//! \param i8DeltaX 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 i8DeltaY 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 ui8Buttons is a bit mask indicating which (if any) of the three +//! mouse buttons is pressed. Valid values are logical OR combinations of +//! \b MOUSE_REPORT_BUTTON_1, \b MOUSE_REPORT_BUTTON_2 and +//! \b 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 is returned +//! and the state change is 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. +// +//***************************************************************************** +uint32_t +USBDHIDMouseStateChange(void *pvMouseDevice, int8_t i8DeltaX, int8_t i8DeltaY, + uint8_t ui8Buttons) +{ + uint32_t ui32Retcode, ui32Count; + tHIDMouseInstance *psInst; + tUSBDHIDMouseDevice *psMouseDevice; + tUSBDHIDDevice *psHIDDevice; + + // + // Get a pointer to the device. + // + psMouseDevice = (tUSBDHIDMouseDevice *)pvMouseDevice; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psMouseDevice->sPrivateData.sHIDDevice; + + // + // Get a pointer to our instance data + // + psInst = &psMouseDevice->sPrivateData; + + // + // Update the global mouse report with the information passed. + // + psInst->pui8Report[HID_REPORT_BUTTONS] = ui8Buttons; + psInst->pui8Report[HID_REPORT_X] = (uint8_t)i8DeltaX; + psInst->pui8Report[HID_REPORT_Y] = (uint8_t)i8DeltaY; + + // + // If we are not configured, return an error here before trying to send + // anything. + // + if(!psInst->ui8USBConfigured) + { + 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->iMouseState = eHIDMouseStateSend; + ui32Count = USBDHIDReportWrite((void *)psHIDDevice, + psInst->pui8Report, MOUSE_REPORT_SIZE, + true); + + // + // Did we schedule a packet for transmission correctly? + // + if(!ui32Count) + { + // + // No - report the error to the caller. + // + ui32Retcode = MOUSE_ERR_TX_ERROR; + } + else + { + ui32Retcode = MOUSE_SUCCESS; + } + } + else + { + ui32Retcode = MOUSE_ERR_TX_ERROR; + } + // + // Return the relevant error code to the caller. + // + return(ui32Retcode); +} +#ifndef DEPRECATED + +//***************************************************************************** +// +//! Reports the device power status (bus- or self-powered) to the USB library. +//! +//! \param pvMouseDevice is the pointer to the mouse device instance structure. +//! \param ui8Power 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 *pvMouseDevice, uint8_t ui8Power) +{ + tUSBDHIDMouseDevice *psMouseDevice; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvMouseDevice); + + // + // Get the keyboard device pointer. + // + psMouseDevice = (tUSBDHIDMouseDevice *)pvMouseDevice; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psMouseDevice->sPrivateData.sHIDDevice; + + // + // Pass the request through to the lower layer. + // + USBDHIDPowerStatusSet((void *)psHIDDevice, ui8Power); +} +#endif + +//***************************************************************************** +// +//! Requests a remote wake up to resume communication when in suspended state. +//! +//! \param pvMouseDevice 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 causes the bus +//! to resume operation within 20mS. If the host has disabled remote wake up, +//! \b false is 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. +// +//***************************************************************************** +bool +USBDHIDMouseRemoteWakeupRequest(void *pvMouseDevice) +{ + tUSBDHIDMouseDevice *psMouseDevice; + tUSBDHIDDevice *psHIDDevice; + + ASSERT(pvMouseDevice); + + // + // Get the keyboard device pointer. + // + psMouseDevice = (tUSBDHIDMouseDevice *)pvMouseDevice; + + // + // Get a pointer to the HID device data. + // + psHIDDevice = &psMouseDevice->sPrivateData.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..6978ca7 --- /dev/null +++ b/usblib/device/usbdhidmouse.h @@ -0,0 +1,299 @@ +//***************************************************************************** +// +// usbdhidmouse.h - Public header file for the USB HID Mouse device class +// driver +// +// Copyright (c) 2008-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#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. + // + eHIDMouseStateUnconfigured, + + // + // No keys to send and not waiting on data. + // + eHIDMouseStateIdle, + + // + // Waiting on report data from the host. + // + eHIDMouseStateWaitData, + + // + // Waiting on data to be sent out. + // + eHIDMouseStateSend +} +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. + // + uint8_t ui8USBConfigured; + + // + // The protocol requested by the host, USB_HID_PROTOCOL_BOOT or + // USB_HID_PROTOCOL_REPORT. + // + uint8_t ui8Protocol; + + // + // A buffer used to hold the last input report sent to the host. + // + uint8_t pui8Report[MOUSE_REPORT_SIZE]; + + // + // The current state of the mouse interrupt IN endpoint. + // + volatile tMouseState iMouseState; + + // + // The idle timeout control structure for our input report. This is + // required by the lower level HID driver. + // + tHIDReportIdle sReportIdle; + + // + // This is needed for the lower level HID driver. + // + tUSBDHIDDevice sHIDDevice; +} +tHIDMouseInstance; + +//***************************************************************************** +// +//! 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. + // + const uint16_t ui16VID; + + // + //! The product ID that this device is to present in the device descriptor. + // + const uint16_t ui16PID; + + // + //! The maximum power consumption of the device, expressed in milliamps. + // + const uint16_t ui16MaxPowermA; + + // + //! 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. + // + const uint8_t ui8PwrAttributes; + + // + //! A pointer to the callback function which is called to notify + //! the application of events relating to the operation of the mouse. + // + const tUSBCallback pfnCallback; + + // + //! A client-supplied pointer which is 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 uint8_t * const *ppui8StringDescriptors; + + // + //! The number of descriptors provided in the ppStringDescriptors + //! array. This must be (1 + (5 * (num languages))). + // + const uint32_t ui32NumStringDescriptors; + + // + //! The 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 sPrivateData; +} +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 is been ignored. +// +//***************************************************************************** +#define MOUSE_ERR_NOT_CONFIGURED \ + 4 + +//***************************************************************************** +// +//! Setting this bit in the ui8Buttons 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 ui8Buttons 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 ui8Buttons 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(uint32_t ui32Index, + tUSBDHIDMouseDevice *psMouseDevice); +extern void *USBDHIDMouseCompositeInit(uint32_t ui32Index, + tUSBDHIDMouseDevice *psMouseDevice, + tCompositeEntry *psCompEntry); +extern void USBDHIDMouseTerm(void *pvMouseDevice); +extern void *USBDHIDMouseSetCBData(void *pvMouseDevice, void *pvCBData); +extern uint32_t USBDHIDMouseStateChange(void *pvMouseDevice, int8_t i8DeltaX, + int8_t i8DeltaY, uint8_t ui8Buttons); +extern void USBDHIDMousePowerStatusSet(void *pvMouseDevice, + uint8_t ui8Power); +extern bool USBDHIDMouseRemoteWakeupRequest(void *pvMouseDevice); + +//***************************************************************************** +// +// 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..daea1f0 --- /dev/null +++ b/usblib/device/usbdmsc.c @@ -0,0 +1,2441 @@ +//***************************************************************************** +// +// usbdmsc.c - USB mass storage device class driver. +// +// Copyright (c) 2009-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/sysctl.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.h" +#include "usblib/usbmsc.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbdmsc.h" + +//***************************************************************************** +// +//! \addtogroup msc_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// These are the internal flags used with the ui32Flags member variable. +// +//***************************************************************************** +#define USBD_FLAG_DMA_IN 0x00000001 +#define USBD_FLAG_DMA_OUT 0x00000002 +#define USBD_FLAG_ALLOW_REMOVAL 0x00000004 + +//***************************************************************************** +// +// The subset of endpoint status flags that we consider to be reception +// errors. These are passed to the client via USB_EVENT_ERROR if seen. +// +//***************************************************************************** +#define USB_RX_ERROR_FLAGS (USBERR_DEV_RX_DATA_ERROR | \ + USBERR_DEV_RX_OVERRUN | \ + USBERR_DEV_RX_FIFO_FULL) + +//***************************************************************************** +// +// These are fields that are used by the USB descriptors for the Mass Storage +// Class. +// +//***************************************************************************** +#define USB_MSC_SUBCLASS_SCSI 0x6 +#define USB_MSC_PROTO_BULKONLY 0x50 + +//***************************************************************************** +// +// Endpoints to use for each of the required endpoints in the driver. +// +//***************************************************************************** +#define DATA_IN_ENDPOINT USB_EP_1 +#define DATA_OUT_ENDPOINT USB_EP_1 + +//***************************************************************************** +// +// Maximum packet size for the bulk endpoints is 64 bytes. +// +//***************************************************************************** +#define DATA_IN_EP_MAX_SIZE 64 +#define DATA_OUT_EP_MAX_SIZE 64 + +//***************************************************************************** +// +// These defines control the size of USB transfers for commands. +// +//***************************************************************************** +#define COMMAND_BUFFER_SIZE 64 + +//***************************************************************************** +// +// The block size of a device. It defaults to DEVICE_BLOCK_SIZE +// +//***************************************************************************** +static uint32_t g_pui32BlockSize = DEVICE_BLOCK_SIZE; + +//***************************************************************************** +// +// The local buffer used to read in commands and process them. +// +//***************************************************************************** +static uint8_t g_pui8Command[COMMAND_BUFFER_SIZE]; + +//***************************************************************************** +// +// The current transfer state is held in these variables. +// +//***************************************************************************** +static tMSCCSW g_sSCSICSW; + +//***************************************************************************** +// +// The current state for the SCSI commands that are being handled and are +// stored in the tMSCInstance.ui8SCSIState structure member. +// +//***************************************************************************** + +// +// No command in process. +// +#define STATE_SCSI_IDLE 0x00 + +// +// Sending and reading logical blocks. +// +#define STATE_SCSI_SEND_BLOCKS 0x01 + +// +// Receiving and writing logical blocks. +// +#define STATE_SCSI_RECEIVE_BLOCKS 0x02 + +// +// Send the status once the previous transfer is complete. +// +#define STATE_SCSI_SEND_STATUS 0x03 + +// +// Status was prepared to be sent and now waiting for it to have gone out. +// +#define STATE_SCSI_SENT_STATUS 0x04 + +//***************************************************************************** +// +// Device Descriptor. This is stored in RAM to allow several fields to be +// changed at runtime based on the client's requirements. +// +//***************************************************************************** +static uint8_t g_pui8MSCDeviceDescriptor[] = +{ + 18, // Size of this structure. + USB_DTYPE_DEVICE, // Type of this structure. + USBShort(0x110), // USB version 1.1 (if we say 2.0, hosts + // assume + // high-speed - see USB 2.0 spec 9.2.6.6) + 0, // USB Device Class (spec 5.1.1) + 0, // USB Device Sub-class (spec 5.1.1) + 0, // USB Device protocol (spec 5.1.1) + 64, // Maximum packet size for default pipe. + USBShort(0), // Vendor ID (filled in during + // USBDCDCInit). + USBShort(0), // Product ID (filled in during + // USBDCDCInit). + USBShort(0x100), // Device Version BCD. + 1, // Manufacturer string identifier. + 2, // Product string identifier. + 3, // Product serial number. + 1 // Number of configurations. +}; + +//***************************************************************************** +// +// Mass storage device configuration descriptor. +// +// It is vital that the configuration descriptor bConfigurationValue field +// (byte 6) is 1 for the first configuration and increments by 1 for each +// additional configuration defined here. This relationship is assumed in the +// device stack for simplicity even though the USB 2.0 specification imposes +// no such restriction on the bConfigurationValue values. +// +// Note that this structure is deliberately located in RAM since we need to +// be able to patch some values in it based on client requirements. +// +//***************************************************************************** +static uint8_t g_pui8MSCDescriptor[] = +{ + // + // Configuration descriptor header. + // + 9, // Size of the configuration descriptor. + USB_DTYPE_CONFIGURATION, // Type of this descriptor. + USBShort(32), // The total size of this full structure. + 1, // The number of interfaces in this + // configuration. + 1, // The unique value for this configuration. + 0, // The string identifier that describes + // this configuration. + USB_CONF_ATTR_SELF_PWR, // Bus Powered, Self Powered, remote wake + // up. + 250, // The maximum power in 2mA increments. +}; + +//***************************************************************************** +// +// The remainder of the configuration descriptor is stored in flash since we +// don't need to modify anything in it at runtime. +// +//***************************************************************************** +const uint8_t g_pui8MSCInterface[MSCINTERFACE_SIZE] = +{ + // + // Vendor-specific Interface Descriptor. + // + 9, // Size of the interface descriptor. + USB_DTYPE_INTERFACE, // Type of this descriptor. + 0, // The index for this interface. + 0, // The alternate setting for this + // interface. + 2, // The number of endpoints used by this + // interface. + USB_CLASS_MASS_STORAGE, // The interface class + USB_MSC_SUBCLASS_SCSI, // The interface sub-class. + USB_MSC_PROTO_BULKONLY, // The interface protocol for the sub-class + // specified above. + 0, // The string index for this interface. + + // + // Endpoint Descriptor + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_IN | USBEPToIndex(DATA_IN_ENDPOINT), + USB_EP_ATTR_BULK, // Endpoint is a bulk endpoint. + USBShort(DATA_IN_EP_MAX_SIZE), // The maximum packet size. + 0, // The polling interval for this endpoint. + + // + // Endpoint Descriptor + // + 7, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + USB_EP_DESC_OUT | USBEPToIndex(DATA_OUT_ENDPOINT), + USB_EP_ATTR_BULK, // Endpoint is a bulk endpoint. + USBShort(DATA_OUT_EP_MAX_SIZE), // The maximum packet size. + 0, // The polling interval for this endpoint. +}; + +//***************************************************************************** +// +// The mass storage configuration descriptor is defined as two sections, +// one containing just the 9 byte USB configuration descriptor and the other +// containing everything else that is sent to the host along with it. +// +//***************************************************************************** +const tConfigSection g_sMSCConfigSection = +{ + sizeof(g_pui8MSCDescriptor), + g_pui8MSCDescriptor +}; + +const tConfigSection g_sMSCInterfaceSection = +{ + sizeof(g_pui8MSCInterface), + g_pui8MSCInterface +}; + +//***************************************************************************** +// +// This array lists all the sections that must be concatenated to make a +// single, complete bulk device configuration descriptor. +// +//***************************************************************************** +const tConfigSection *g_psMSCSections[] = +{ + &g_sMSCConfigSection, + &g_sMSCInterfaceSection +}; + +#define NUM_MSC_SECTIONS (sizeof(g_psMSCSections) / \ + sizeof(g_psMSCSections[0])) + +//***************************************************************************** +// +// The header for the single configuration we support. This is the root of +// the data structure that defines all the bits and pieces that are pulled +// together to generate the configuration descriptor. +// +//***************************************************************************** +const tConfigHeader g_sMSCConfigHeader = +{ + NUM_MSC_SECTIONS, + g_psMSCSections +}; + +//***************************************************************************** +// +// Configuration Descriptor. +// +//***************************************************************************** +const tConfigHeader * const g_ppsMSCConfigDescriptors[] = +{ + &g_sMSCConfigHeader +}; + +//***************************************************************************** +// +// Various internal handlers needed by this class. +// +//***************************************************************************** +static void HandleDisconnect(void *pvMSCDevice); +static void ConfigChangeHandler(void *pvMSCDevice, uint32_t ui32Value); +static void HandleEndpoints(void *pvMSCDevice, uint32_t ui32Status); +static void HandleRequests(void *pvMSCDevice, tUSBRequest *psUSBRequest); +static void USBDSCSISendStatus(tUSBDMSCDevice *psMSCDevice); +uint32_t USBDSCSICommand(tUSBDMSCDevice *psMSCDevice, tMSCCBW *psSCSICBW); +static void HandleDevice(void *pvMSCDevice, uint32_t ui32Request, + void *pvRequestData); + +//***************************************************************************** +// +// The device information structure for the USB MSC device. +// +//***************************************************************************** +const tCustomHandlers g_sMSCHandlers = +{ + // + // GetDescriptor + // + 0, + + // + // RequestHandler + // + HandleRequests, + + // + // InterfaceChange + // + 0, + + // + // ConfigChange + // + ConfigChangeHandler, + + // + // DataReceived + // + 0, + + // + // DataSentCallback + // + 0, + + // + // ResetHandler + // + 0, + + // + // SuspendHandler + // + 0, + + // + // ResumeHandler + // + 0, + + // + // DisconnectHandler + // + HandleDisconnect, + + // + // EndpointHandler + // + HandleEndpoints, + + // + // Device handler + // + HandleDevice +}; + +//***************************************************************************** +// +//! This function is used by an application if it can detect insertion or +//! removal of the media. +//! +//! \param pvMSCDevice is the mass storage device instance that had a media +//! change. +//! \param iMediaStatus is the updated status for the media. +//! +//! This function should be called by an application when it detects a change +//! in the status of the media in use by the USB mass storage class. The +//! \e iMediaStatus parameter will indicate the new status of the media and +//! can also indicate that the application has no knowledge of the media state. +//! +//! There are currently the three following values for the \e iMediaStatus +//! parameter: +//! - \b eUSBDMSCMediaPresent indicates that the media is present or has been +//! added. +//! - \b eUSBDMSCMediaNotPresent indicates that the media is not present or was +//! removed. +//! - \b eUSBDMSCMediaUnknown indicates that the application has no knowledge +//! of the media state and the USB mass storage class. +//! +//! It will be left up to the application to call this function whenever it +//! detects a change or simply call it once with \b eUSBDMSCMediaUnknown and +//! allow the mass storage class to infer the state from the remaining device +//! APIs. +//! +//! \note It is recommended that the application use this function to inform +//! the mass storage class of media state changes as it will lead to a more +//! responsive system. +//! +//! \return None. +// +//***************************************************************************** +void +USBDMSCMediaChange(void *pvMSCDevice, tUSBDMSCMediaStatus iMediaStatus) +{ + tUSBDMSCDevice *psMSCDevice; + + // + // Create a device instance pointer. + // + psMSCDevice = pvMSCDevice; + + // + // Save the current media status. + // + psMSCDevice->sPrivateData.iMediaStatus = iMediaStatus; +} + +//***************************************************************************** +// +// This function is called to handle the interrupts on the Bulk endpoints for +// the mass storage class. +// +//***************************************************************************** +static void +HandleEndpoints(void *pvMSCDevice, uint32_t ui32Status) +{ + tUSBDMSCDevice *psMSCDevice; + tMSCInstance *psInst; + tMSCCBW *psSCSICBW; + uint32_t ui32EPStatus, ui32Size; + + ASSERT(pvMSCDevice != 0); + + // + // Determine if the serial device is in single or composite mode because + // the meaning of ui32Index is different in both cases. + // + psMSCDevice = pvMSCDevice; + + // + // Initialize the workspace in the passed instance structure. + // + psInst = &psMSCDevice->sPrivateData; + + // + // Get the endpoints status. + // + ui32EPStatus = MAP_USBEndpointStatus(USB0_BASE, psInst->ui8OUTEndpoint); + + // + // Handler for the bulk IN data endpoint. + // + if((ui32Status & (1 << USBEPToIndex(psInst->ui8INEndpoint))) || + ((psInst->ui32Flags & USBD_FLAG_DMA_IN) && + (USBLibDMAChannelStatus(psInst->psDMAInstance, psInst->ui8INDMA) & + USBLIBSTATUS_DMA_COMPLETE))) + { + switch(psInst->ui8SCSIState) + { + // + // Handle the case where we are sending out data due to a read + // command. + // + case STATE_SCSI_SEND_BLOCKS: + { + // + // Decrement the number of bytes left to send. + // + psInst->ui32BytesToTransfer -= g_pui32BlockSize; + + // + // If we are done then move on to the status phase. + // + if(psInst->ui32BytesToTransfer == 0) + { + // + // Set the status so that it can be sent when this + // response has has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 0; + g_sSCSICSW.dCSWDataResidue = 0; + + // + // DMA has completed for the IN endpoint. + // + psInst->ui32Flags &= ~USBD_FLAG_DMA_IN; + + // + // Disable uDMA on the endpoint + // + MAP_USBEndpointDMADisable(USB0_BASE, psInst->ui8INEndpoint, + USB_EP_DEV_IN); + + if(psMSCDevice->pfnEventCallback) + { + psMSCDevice->pfnEventCallback(0, USBD_MSC_EVENT_IDLE, + 0, 0); + } + + // + // Make sure that the transfer has actually finished. If + // it has not there will be another interrupt to send + // out the status. + // + if(USBEndpointStatus(USB0_BASE,psInst->ui8INEndpoint) & + USB_DEV_TX_TXPKTRDY) + { + // + // Send back the status once this transfer is complete. + // + psInst->ui8SCSIState = STATE_SCSI_SEND_STATUS; + } + else + { + // + // Indicate success and no extra data coming. + // + USBDSCSISendStatus(psMSCDevice); + } + + // + // The transfer is complete so don't read anymore data. + // + break; + } + + // + // Move on to the next Logical Block. + // + psInst->ui32CurrentLBA++; + + // + // Read the new data and send it out. + // + if(psMSCDevice->sMediaFunctions.pfnBlockRead(psInst->pvMedia, + (uint8_t *)psInst->pui32Buffer, + psInst->ui32CurrentLBA, 1) == 0) + { + } + + // + // Configure and enable DMA for the IN transfer. + // + USBLibDMATransfer(psInst->psDMAInstance, + psInst->ui8INDMA, psInst->pui32Buffer, + g_pui32BlockSize); + + // + // Start the DMA transfer. + // + USBLibDMAChannelEnable(psInst->psDMAInstance, + psInst->ui8INDMA); + + break; + } + + // + // Handle sending status. + // + case STATE_SCSI_SEND_STATUS: + { + // + // Indicate success and no extra data coming. + // + USBDSCSISendStatus(psMSCDevice); + + break; + } + + // + // Handle completing sending status. + // + case STATE_SCSI_SENT_STATUS: + { + psInst->ui8SCSIState = STATE_SCSI_IDLE; + + break; + } + + // + // These cases should not occur as the being in the IDLE state due + // to an IN interrupt is invalid. + // + case STATE_SCSI_IDLE: + default: + { + break; + } + } + } + + // + // Handler for the bulk OUT data endpoint. + // + if((ui32Status & (0x10000 << USBEPToIndex(psInst->ui8OUTEndpoint))) || + ((psInst->ui32Flags & USBD_FLAG_DMA_OUT) && + (USBLibDMAChannelStatus(psInst->psDMAInstance, psInst->ui8OUTDMA) & + USBLIBSTATUS_DMA_COMPLETE))) + { + // + // Get the endpoint status to see why we were called. + // + ui32EPStatus = MAP_USBEndpointStatus(USB0_BASE, + psInst->ui8OUTEndpoint); + + switch(psInst->ui8SCSIState) + { + // + // Receiving and writing bytes to the storage device. + // + case STATE_SCSI_RECEIVE_BLOCKS: + { + // + // Update the current status for the buffer. + // + psInst->ui32BytesToTransfer -= g_pui32BlockSize; + + // + // Write the new data. + // + psMSCDevice->sMediaFunctions.pfnBlockWrite(psInst->pvMedia, + (uint8_t *)psInst->pui32Buffer, + psInst->ui32CurrentLBA, 1); + + // + // Move on to the next Logical Block. + // + psInst->ui32CurrentLBA++; + + // + // Check if all bytes have been received. + // + if(psInst->ui32BytesToTransfer == 0) + { + // + // Set the status so that it can be sent when this response + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 0; + g_sSCSICSW.dCSWDataResidue = 0; + + // + // DMA has completed for the OUT endpoint. + // + psInst->ui32Flags &= ~USBD_FLAG_DMA_OUT; + + // + // Indicate success and no extra data coming. + // + USBDSCSISendStatus(psMSCDevice); + + // + // Disable uDMA on the endpoint + // + MAP_USBEndpointDMADisable(USB0_BASE, + psInst->ui8OUTEndpoint, + USB_EP_DEV_OUT); + + // + // If there is an event callback then call it to notify + // that last operation has completed. + // + if(psMSCDevice->pfnEventCallback) + { + psMSCDevice->pfnEventCallback(0, USBD_MSC_EVENT_IDLE, + 0, 0); + } + } + else + { + // + // Configure and enable DMA for the OUT transfer. + // + USBLibDMATransfer(psInst->psDMAInstance, + psInst->ui8OUTDMA, psInst->pui32Buffer, + g_pui32BlockSize); + } + + break; + } + + // + // If there is an OUT transfer in idle state then it was a new + // command. + // + case STATE_SCSI_IDLE: + { + // + // Attempt to handle the new command. + // + + // + // Receive the command. + // + ui32Size = COMMAND_BUFFER_SIZE; + MAP_USBEndpointDataGet(psInst->ui32USBBase, + psInst->ui8OUTEndpoint, + g_pui8Command, &ui32Size); + psSCSICBW = (tMSCCBW *)g_pui8Command; + + // + // Acknowledge the OUT data packet. + // + MAP_USBDevEndpointDataAck(psInst->ui32USBBase, + psInst->ui8OUTEndpoint, false); + + // + // If this is a valid CBW then handle it. + // + if(psSCSICBW->dCBWSignature == CBW_SIGNATURE) + { + g_sSCSICSW.dCSWSignature = CSW_SIGNATURE; + g_sSCSICSW.dCSWTag = psSCSICBW->dCBWTag; + g_sSCSICSW.dCSWDataResidue = 0; + g_sSCSICSW.bCSWStatus = 0; + + USBDSCSICommand(psMSCDevice, psSCSICBW); + } + else + { + // + // Just return to the idle state since we are now out of + // sync with the host. This should not happen, but this + // should allow the device to synchronize with the host + // controller. + // + psInst->ui8SCSIState = STATE_SCSI_IDLE; + } + + break; + } + default: + { + break; + } + } + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(USB0_BASE, psInst->ui8OUTEndpoint, + ui32EPStatus); + } +} + +//***************************************************************************** +// +// Device instance specific handler. +// +//***************************************************************************** +static void +HandleDevice(void *pvMSCDevice, uint32_t ui32Request, void *pvRequestData) +{ + tMSCInstance *psInst; + uint8_t *pui8Data; + tUSBDMSCDevice *psMSCDevice; + + psMSCDevice = (tUSBDMSCDevice *)pvMSCDevice; + + // + // Get the instance data pointers. + // + psInst = &((tUSBDMSCDevice *)pvMSCDevice)->sPrivateData; + + // + // Create the 8-bit array used by the events supported by the USB MSC + // class. + // + pui8Data = (uint8_t *)pvRequestData; + + switch(ui32Request) + { + // + // This was an interface change event. + // + case USB_EVENT_COMP_IFACE_CHANGE: + { + psInst->ui8Interface = pui8Data[1]; + break; + } + + // + // This was an endpoint change event. + // + case USB_EVENT_COMP_EP_CHANGE: + { + // + // Determine if this is an IN or OUT endpoint that has changed. + // + if(pui8Data[0] & USB_EP_DESC_IN) + { + psInst->ui8INEndpoint = IndexToUSBEP((pui8Data[1] & 0x7f)); + + // + // If the DMA channel has already been allocated then clear + // that channel and prepare to possibly use a new one. + // + if(psInst->ui8INDMA != 0) + { + USBLibDMAChannelRelease(psInst->psDMAInstance, + psInst->ui8INDMA); + } + + // + // Allocate a DMA channel to the endpoint. + // + psInst->ui8INDMA = + USBLibDMAChannelAllocate(psInst->psDMAInstance, + psInst->ui8INEndpoint, 0, + USB_DMA_EP_TX | + USB_DMA_EP_DEVICE); + + // + // Set the DMA individual transfer size. + // + USBLibDMAUnitSizeSet(psInst->psDMAInstance, psInst->ui8INDMA, + 32); + + // + // Set the DMA arbitration size. + // + USBLibDMAArbSizeSet(psInst->psDMAInstance, psInst->ui8INDMA, + 16); + } + else + { + // + // If the DMA channel has already been allocated then clear + // that channel and prepare to possibly use a new one. + // + if(psInst->ui8OUTDMA != 0) + { + USBLibDMAChannelRelease(psInst->psDMAInstance, + psInst->ui8OUTDMA); + } + + // + // Allocate a DMA channel to the endpoint. + // + psInst->ui8OUTDMA = + USBLibDMAChannelAllocate(psInst->psDMAInstance, + psInst->ui8OUTEndpoint, 0, + USB_DMA_EP_RX | + USB_DMA_EP_DEVICE); + + // + // Set the DMA individual transfer size. + // + USBLibDMAUnitSizeSet(psInst->psDMAInstance, psInst->ui8OUTDMA, + 32); + + // + // Set the DMA arbitration size. + // + USBLibDMAArbSizeSet(psInst->psDMAInstance, psInst->ui8OUTDMA, + 16); + } + break; + } + case USB_EVENT_LPM_RESUME: + { + if(psMSCDevice->pfnEventCallback) + { + // + // Pass the LPM resume event to the client. + // + psMSCDevice->pfnEventCallback(0, USB_EVENT_LPM_RESUME, 0, + (void *)0); + } + break; + } + case USB_EVENT_LPM_SLEEP: + { + if(psMSCDevice->pfnEventCallback) + { + // + // Pass the LPM sleep event to the client. + // + psMSCDevice->pfnEventCallback(0, USB_EVENT_LPM_RESUME, 0, + (void *)0); + } + break; + } + case USB_EVENT_LPM_ERROR: + { + if(psMSCDevice->pfnEventCallback) + { + // + // Pass the LPM error event to the client. + // + psMSCDevice->pfnEventCallback(0, USB_EVENT_LPM_RESUME, 0, + (void *)0); + } + break; + } + default: + { + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device is +// disconnected from the host. +// +//***************************************************************************** +static void +HandleDisconnect(void *pvMSCDevice) +{ + tUSBDMSCDevice *psMSCDevice; + + ASSERT(pvMSCDevice != 0); + + // + // Create the instance pointer. + // + psMSCDevice = (tUSBDMSCDevice *)pvMSCDevice; + + // + // Close the drive requested. + // + if(psMSCDevice->sPrivateData.pvMedia != 0) + { + psMSCDevice->sPrivateData.pvMedia = 0; + psMSCDevice->sMediaFunctions.pfnClose(0); + } + + // + // If we have a control callback, let the client know we are open for + // business. + // + if(psMSCDevice->pfnEventCallback) + { + // + // Pass the connected event to the client. + // + psMSCDevice->pfnEventCallback(pvMSCDevice, USB_EVENT_DISCONNECTED, 0, + 0); + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device +// configuration changes. +// +//***************************************************************************** +static void +ConfigChangeHandler(void *pvMSCDevice, uint32_t ui32Value) +{ + tUSBDMSCDevice *psMSCDevice; + + ASSERT(pvMSCDevice != 0); + + // + // Create the instance pointer. + // + psMSCDevice = (tUSBDMSCDevice *)pvMSCDevice; + + // + // If the DMA channel has already been allocated then clear + // that channel and prepare to possibly use a new one. + // + if(psMSCDevice->sPrivateData.ui8OUTDMA != 0) + { + USBLibDMAChannelRelease(psMSCDevice->sPrivateData.psDMAInstance, + psMSCDevice->sPrivateData.ui8OUTDMA); + } + + // + // Configure the DMA for the OUT endpoint. + // + psMSCDevice->sPrivateData.ui8OUTDMA = + USBLibDMAChannelAllocate(psMSCDevice->sPrivateData.psDMAInstance, + psMSCDevice->sPrivateData.ui8OUTEndpoint, 64, + USB_DMA_EP_RX | USB_DMA_EP_DEVICE); + + USBLibDMAUnitSizeSet(psMSCDevice->sPrivateData.psDMAInstance, + psMSCDevice->sPrivateData.ui8OUTDMA, 32); + + USBLibDMAArbSizeSet(psMSCDevice->sPrivateData.psDMAInstance, + psMSCDevice->sPrivateData.ui8OUTDMA, 16); + + // + // If the DMA channel has already been allocated then clear + // that channel and prepare to possibly use a new one. + // + if(psMSCDevice->sPrivateData.ui8INDMA != 0) + { + USBLibDMAChannelRelease(psMSCDevice->sPrivateData.psDMAInstance, + psMSCDevice->sPrivateData.ui8INDMA); + } + + // + // Configure the DMA for the IN endpoint. + // + psMSCDevice->sPrivateData.ui8INDMA = + USBLibDMAChannelAllocate(psMSCDevice->sPrivateData.psDMAInstance, + psMSCDevice->sPrivateData.ui8INEndpoint, 64, + USB_DMA_EP_TX | USB_DMA_EP_DEVICE); + + USBLibDMAUnitSizeSet(psMSCDevice->sPrivateData.psDMAInstance, + psMSCDevice->sPrivateData.ui8INDMA, 32); + + USBLibDMAArbSizeSet(psMSCDevice->sPrivateData.psDMAInstance, + psMSCDevice->sPrivateData.ui8INDMA, 16); + + // + // If we have a control callback, let the client know we are open for + // business. + // + if(psMSCDevice->pfnEventCallback) + { + // + // Pass the connected event to the client. + // + psMSCDevice->pfnEventCallback(pvMSCDevice, USB_EVENT_CONNECTED, 0, 0); + } +} + +//***************************************************************************** +// +//! This function should be called once for the mass storage class device to +//! initialized basic operation and prepare for enumeration. +//! +//! \param ui32Index is the index of the USB controller to initialize for +//! mass storage class device operation. +//! \param psMSCDevice points to a structure containing parameters customizing +//! the operation of the mass storage device. +//! +//! In order for an application to initialize the USB device mass storage +//! class, it must first call this function with the a valid mass storage +//! device class structure in the \e psMSCDevice parameter. This allows this +//! function to initialize the USB controller and device code to be prepared to +//! enumerate and function as a USB mass storage device. +//! +//! This function returns a void pointer that must be passed in to all other +//! APIs used by the mass storage class. +//! +//! See the documentation on the tUSBDMSCDevice structure for more information +//! on how to properly fill the structure members. +//! +//! \return Returns 0 on failure or a non-zero void pointer on success. +// +//***************************************************************************** +void * +USBDMSCInit(uint32_t ui32Index, tUSBDMSCDevice *psMSCDevice) +{ + tDeviceDescriptor *psDevDesc; + tConfigDescriptor *pConfDesc; + + // + // Check parameter validity. + // + ASSERT(ui32Index == 0); + ASSERT(psMSCDevice); + ASSERT(psMSCDevice->ppui8StringDescriptors); + + USBDMSCCompositeInit(ui32Index, psMSCDevice, 0); + + // + // Fix up the device descriptor with the client-supplied values. + // + psDevDesc = (tDeviceDescriptor *)g_pui8MSCDeviceDescriptor; + psDevDesc->idVendor = psMSCDevice->ui16VID; + psDevDesc->idProduct = psMSCDevice->ui16PID; + + // + // Fix up the configuration descriptor with client-supplied values. + // + pConfDesc = (tConfigDescriptor *)g_pui8MSCDescriptor; + pConfDesc->bmAttributes = psMSCDevice->ui8PwrAttributes; + pConfDesc->bMaxPower = (uint8_t)(psMSCDevice->ui16MaxPowermA / 2); + + // + // All is well so now pass the descriptors to the lower layer and put + // the bulk device on the bus. + // + USBDCDInit(ui32Index, &psMSCDevice->sPrivateData.sDevInfo, + (void *)psMSCDevice); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psMSCDevice); +} + +//***************************************************************************** +// +//! This function should be called once for the mass storage class device to +//! initialized basic operation and prepare for enumeration. +//! +//! \param ui32Index is the index of the USB controller to initialize for +//! mass storage class device operation. +//! \param psMSCDevice points to a structure containing parameters customizing +//! the operation of the mass storage device. +//! \param psCompEntry is the composite device entry to initialize when +//! creating a composite device. +//! +//! In order for an application to initialize the USB device mass storage +//! class, it must first call this function with the a valid mass storage +//! device class structure in the \e psMSCDevice parameter. This allows this +//! function to initialize the USB controller and device code to be prepared to +//! enumerate and function as a USB mass storage device. If this mass storage +//! device is part of a composite device, then the \e psCompEntry should +//! point to the composite device entry to initialize. This is part of the +//! array that is passed to the USBDCompositeInit() function. +//! +//! This function returns a void pointer that must be passed in to all other +//! APIs used by the mass storage class. +//! +//! See the documentation on the tUSBDMSCDevice structure for more information +//! on how to properly fill the structure members. +//! +//! \return Returns zero on failure or a non-zero instance value that should be +//! used with the remaining USB mass storage APIs. +// +//***************************************************************************** +void * +USBDMSCCompositeInit(uint32_t ui32Index, tUSBDMSCDevice *psMSCDevice, + tCompositeEntry *psCompEntry) +{ + tMSCInstance *psInst; + + // + // Check parameter validity. + // + ASSERT(ui32Index == 0); + ASSERT(psMSCDevice); + ASSERT(psMSCDevice->ppui8StringDescriptors); + ASSERT(psCompEntry != 0); + + // + // Initialize the workspace in the passed instance structure. + // + psInst = &psMSCDevice->sPrivateData; + psInst->ui32USBBase = USB0_BASE; + psInst->bConnected = false; + psInst->iMediaStatus = eUSBDMSCMediaUnknown; + + // + // Initialize the composite entry that is used by the composite device + // class. + // + if(psCompEntry != 0) + { + psCompEntry->psDevInfo = &psInst->sDevInfo; + psCompEntry->pvInstance = (void *)psMSCDevice; + } + + // + // Initialize the device information structure. + // + psInst->sDevInfo.psCallbacks = &g_sMSCHandlers; + psInst->sDevInfo.pui8DeviceDescriptor = g_pui8MSCDeviceDescriptor; + psInst->sDevInfo.ppsConfigDescriptors = g_ppsMSCConfigDescriptors; + psInst->sDevInfo.ppui8StringDescriptors = 0; + psInst->sDevInfo.ui32NumStringDescriptors = 0; + + // + // Initialize the device info structure for the mass storage device. + // + USBDCDDeviceInfoInit(0, &psInst->sDevInfo); + + // + // Set the initial interface and endpoints. + // + psInst->ui8Interface = 0; + psInst->ui8OUTEndpoint = DATA_OUT_ENDPOINT; + psInst->ui8INEndpoint = DATA_IN_ENDPOINT; + + // + // Set the initial SCSI state to idle. + // + psInst->ui8SCSIState = STATE_SCSI_IDLE; + + // + // Plug in the client's string stable to the device information + // structure. + // + psInst->sDevInfo.ppui8StringDescriptors = + psMSCDevice->ppui8StringDescriptors; + psInst->sDevInfo.ui32NumStringDescriptors = + psMSCDevice->ui32NumStringDescriptors; + + // + // Open the drive requested. + // + psInst->pvMedia = psMSCDevice->sMediaFunctions.pfnOpen(0); + + if(psInst->pvMedia == 0) + { + // + // There is no media currently present. + // + psInst->ui8SenseKey = SCSI_RS_KEY_NOT_READY; + psInst->ui16AddSenseCode = SCSI_RS_MED_NOT_PRSNT; + } + else + { + // + // Media is now ready for use. + // + psInst->ui8SenseKey = SCSI_RS_KEY_UNIT_ATTN; + psInst->ui16AddSenseCode = SCSI_RS_MED_NOTRDY2RDY; + } + + // + // Enable Clocking to the USB controller. + // + MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_USB0); + + // + // Turn on USB Phy clock. + // + MAP_SysCtlUSBPLLEnable(); + + // + // Get the DMA instance pointer. + // + psInst->psDMAInstance = USBLibDMAInit(0); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psMSCDevice); +} + +//***************************************************************************** +// +//! Shuts down the mass storage device. +//! +//! \param pvMSCDevice is the pointer to the device instance structure as +//! returned by USBDMSCInit() or USBDMSCCompositeInit(). +//! +//! This function terminates mass storage operation for the instance supplied +//! and removes the device from the USB bus. Following this call, the +//! \e pvMSCDevice instance may not me used in any other call to the mass +//! storage device other than USBDMSCInit() or USBDMSCCompositeInit(). +//! +//! \return None. +// +//***************************************************************************** +void +USBDMSCTerm(void *pvMSCDevice) +{ + tUSBDMSCDevice *psMSCDevice; + + ASSERT(pvMSCDevice != 0); + + // + // Cleanly exit device mode. + // + USBDCDTerm(0); + + // + // Create a device instance pointer. + // + psMSCDevice = pvMSCDevice; + + // + // If the media was opened the close it out. + // + if(psMSCDevice->sPrivateData.pvMedia != 0) + { + psMSCDevice->sPrivateData.pvMedia = 0; + psMSCDevice->sMediaFunctions.pfnClose(0); + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever a non-standard +// request is received. +// +// \param pvMSCDevice is instance data for this request. +// \param pUSBRequest points to the request received. +// +// This call parses the provided request structure to determine the command. +// The only mass storage command supported over endpoint 0 is the Get Max LUN +// command. +// +// \return None. +// +//***************************************************************************** +static void +HandleRequests(void *pvMSCDevice, tUSBRequest *pUSBRequest) +{ + // + // This class only support a single LUN. + // + static const uint8_t ui8MaxLun = 0; + + ASSERT(pvMSCDevice != 0); + + // + // Determine the type of request. + // + switch(pUSBRequest->bRequest) + { + // + // A Set Report request is received from the host when it sends an + // Output report via endpoint 0. + // + case USBREQ_GET_MAX_LUN: + { + // + // Need to ACK the data on end point 0 with last data since there + // is no more data expected. + // + USBDevEndpointDataAck(USB0_BASE, USB_EP_0, true); + + // + // Send our response to the host. + // + USBDCDSendDataEP0(0, (uint8_t *)&ui8MaxLun, 1); + + break; + } + case USBREQ_BULK_ONLY_RESET: + { + // + // Need to ACK the data on end point 0 with last data since there + // is no more data expected. + // + USBDevEndpointDataAck(USB0_BASE, USB_EP_0, true); + + // + // Send a null packet to the host. + // + USBDCDSendDataEP0(0, (uint8_t *)&ui8MaxLun, 0); + + break; + } + + // + // This request was not recognized so stall. + // + default: + { + USBDCDStallEP0(0); + break; + } + } +} + +//***************************************************************************** +// +// This function is used to handle the SCSI Inquiry command when it is received +// from the host. +// +//***************************************************************************** +static void +USBDSCSIInquiry(tUSBDMSCDevice *psMSCDevice) +{ + int32_t i32Idx; + tMSCInstance *psInst; + uint32_t *pui32Data; + + // + // Create a local 32-bit pointer to the command. + // + pui32Data = (uint32_t *)g_pui8Command; + + // + // Create the serial instance data. + // + psInst = &psMSCDevice->sPrivateData; + + // + // Direct Access device, Removable storage and SCSI 1 responses. + // + pui32Data[0] = SCSI_INQ_PDT_SBC | (SCSI_INQ_RMB << 8); + + // + // Additional Length is fixed at 31 bytes. + // + pui32Data[1] = 31; + + // + // Copy the Vendor string. + // + for(i32Idx = 0; i32Idx < 8; i32Idx++) + { + g_pui8Command[i32Idx + 8] = psMSCDevice->pui8Vendor[i32Idx]; + } + + // + // Copy the Product string. + // + for(i32Idx = 0; i32Idx < 16; i32Idx++) + { + g_pui8Command[i32Idx + 16] = psMSCDevice->pui8Product[i32Idx]; + } + + // + // Copy the Version string. + // + for(i32Idx = 0; i32Idx < 4; i32Idx++) + { + g_pui8Command[i32Idx + 32] = psMSCDevice->pui8Version[i32Idx]; + } + + // + // Send the SCSI Inquiry Response. + // + MAP_USBEndpointDataPut(USB0_BASE, psInst->ui8INEndpoint, g_pui8Command, + 36); + + // + // Send the data to the host. + // + MAP_USBEndpointDataSend(USB0_BASE, psInst->ui8INEndpoint, USB_TRANS_IN); + + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 0; + g_sSCSICSW.dCSWDataResidue = 0; + + psInst->ui8SCSIState = STATE_SCSI_SEND_STATUS; +} + +//***************************************************************************** +// +// This function is used to handle the SCSI Read Capacities command when it is +// received from the host. +// +//***************************************************************************** +static void +USBDSCSIReadCapacities(tUSBDMSCDevice *psMSCDevice) +{ + uint32_t ui32Blocks; + tMSCInstance *psInst; + uint32_t *pui32Data; + + // + // Create a local 32-bit pointer to the command. + // + pui32Data = (uint32_t *)g_pui8Command; + + // + // Get our instance data pointer. + // + psInst = &psMSCDevice->sPrivateData; + + if(psInst->pvMedia != 0) + { + if(psMSCDevice->sMediaFunctions.pfnBlockSize) + { + // + // Query the block size for the device + // + g_pui32BlockSize = + psMSCDevice->sMediaFunctions.pfnBlockSize(psInst->pvMedia); + } + ui32Blocks = + psMSCDevice->sMediaFunctions.pfnNumBlocks(psInst->pvMedia); + + pui32Data[0] = 0x08000000; + + // + // Fill in the number of blocks, the bytes endianness must be changed. + // + g_pui8Command[4] = ui32Blocks >> 24; + g_pui8Command[5] = 0xff & (ui32Blocks >> 16); + g_pui8Command[6] = 0xff & (ui32Blocks >> 8); + g_pui8Command[7] = 0xff & (ui32Blocks); + + // + // Current media capacity + // + g_pui8Command[8] = 0x2; + + // + // Fill in the block size, which is g_pui32BlockSize. + // + g_pui8Command[9] = 0xff & (g_pui32BlockSize >> 16); + g_pui8Command[10] = 0xff & (g_pui32BlockSize >> 8); + g_pui8Command[11] = 0xff & g_pui32BlockSize; + + // + // Send out the 12 bytes that are in this response. + // + MAP_USBEndpointDataPut(USB0_BASE, psInst->ui8INEndpoint, g_pui8Command, + 12); + MAP_USBEndpointDataSend(USB0_BASE, psInst->ui8INEndpoint, + USB_TRANS_IN); + + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 0; + g_sSCSICSW.dCSWDataResidue = 0; + } + else + { + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 1; + g_sSCSICSW.dCSWDataResidue = 0; + + // + // Stall the IN endpoint + // + MAP_USBDevEndpointStall(USB0_BASE, psInst->ui8INEndpoint, + USB_EP_DEV_IN); + + // + // Mark the sense code as valid and indicate that these is no media + // present. + // + psInst->ui8ErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS; + psInst->ui8SenseKey = SCSI_RS_KEY_NOT_READY; + psInst->ui16AddSenseCode = SCSI_RS_MED_NOT_PRSNT; + } + + psInst->ui8SCSIState = STATE_SCSI_SEND_STATUS; +} + +//***************************************************************************** +// +// This function is used to handle the SCSI Read Capacity command when it is +// received from the host. +// +//***************************************************************************** +static void +USBDSCSIReadCapacity(tUSBDMSCDevice *psMSCDevice) +{ + uint32_t ui32Blocks; + tMSCInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = &psMSCDevice->sPrivateData; + + if(psMSCDevice->sMediaFunctions.pfnBlockSize) + { + // + // Query the block size for the device + // + g_pui32BlockSize = + psMSCDevice->sMediaFunctions.pfnBlockSize(psInst->pvMedia); + } + + ui32Blocks = psMSCDevice->sMediaFunctions.pfnNumBlocks(psInst->pvMedia); + + // + // Only decrement if any blocks were found. + // + if(ui32Blocks != 0) + { + // + // One less than the maximum number is the last addressable + // block. + // + ui32Blocks--; + } + + if(psInst->pvMedia != 0) + { + // + // Fill in the number of blocks, the bytes endianness must be changed. + // + g_pui8Command[0] = 0xff & (ui32Blocks >> 24); + g_pui8Command[1] = 0xff & (ui32Blocks >> 16); + g_pui8Command[2] = 0xff & (ui32Blocks >> 8); + g_pui8Command[3] = 0xff & (ui32Blocks); + + g_pui8Command[4] = 0; + + // + // Fill in the block size, which is g_pui32BlockSize. + // + g_pui8Command[5] = 0xff & (g_pui32BlockSize >> 16); + g_pui8Command[6] = 0xff & (g_pui32BlockSize >> 8); + g_pui8Command[7] = 0xff & g_pui32BlockSize; + + // + // Send the SCSI Inquiry Response. + // + MAP_USBEndpointDataPut(USB0_BASE, psInst->ui8INEndpoint, g_pui8Command, + 8); + MAP_USBEndpointDataSend(USB0_BASE, psInst->ui8INEndpoint, + USB_TRANS_IN); + + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 0; + g_sSCSICSW.dCSWDataResidue = 0; + } + else + { + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 1; + g_sSCSICSW.dCSWDataResidue = 0; + + // + // Stall the IN endpoint + // + MAP_USBDevEndpointStall(USB0_BASE, psInst->ui8INEndpoint, + USB_EP_DEV_IN); + + // + // Mark the sense code as valid and indicate that these is no media + // present. + // + psInst->ui8ErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS; + psInst->ui8SenseKey = SCSI_RS_KEY_NOT_READY; + psInst->ui16AddSenseCode = SCSI_RS_MED_NOT_PRSNT; + } + + psInst->ui8SCSIState = STATE_SCSI_SEND_STATUS; +} + +//***************************************************************************** +// +// This function is used to handle the SCSI Request Sense command when it is +// received from the host. +// +//***************************************************************************** +static void +USBDSCSIRequestSense(tUSBDMSCDevice *psMSCDevice) +{ + tMSCInstance *psInst; + int32_t i32Idx; + + // + // Zero out the response data. + // + for(i32Idx = 0; i32Idx < 18; i32Idx++) + { + g_pui8Command[i32Idx] = 0; + } + + // + // Get our instance data pointer. + // + psInst = &psMSCDevice->sPrivateData; + + // + // The request sense response. + // + g_pui8Command[0] = psInst->ui8ErrorCode; + g_pui8Command[2] = psInst->ui8SenseKey; + + // + // There are 10 more bytes of data. + // + g_pui8Command[7] = 10; + + // + // Transition from not ready to ready. + // + g_pui8Command[12] = (uint8_t)psInst->ui16AddSenseCode; + g_pui8Command[13] = (uint8_t)(psInst->ui16AddSenseCode >> 8); + + // + // Send the SCSI Inquiry Response. + // + MAP_USBEndpointDataPut(USB0_BASE, psInst->ui8INEndpoint, g_pui8Command, + 18); + MAP_USBEndpointDataSend(USB0_BASE, psInst->ui8INEndpoint, USB_TRANS_IN); + + // + // Reset the valid flag on errors. + // + psInst->ui8ErrorCode = SCSI_RS_CUR_ERRORS; + + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 0; + g_sSCSICSW.dCSWDataResidue = 0; + + // + // Move on to the status phase. + // + psInst->ui8SCSIState = STATE_SCSI_SEND_STATUS; +} + +//***************************************************************************** +// +// This function is used to handle the SCSI Read 10 command when it is +// received from the host. +// +//***************************************************************************** +static void +USBDSCSIRead10(tUSBDMSCDevice *psMSCDevice, tMSCCBW *psSCSICBW) +{ + uint16_t ui16NumBlocks; + tMSCInstance *psInst; + + // + // Default the number of blocks. + // + ui16NumBlocks = 0; + + // + // Get our instance data pointer. + // + psInst = &psMSCDevice->sPrivateData; + + if(psInst->pvMedia != 0) + { + // + // Get the logical block from the CBW structure. This switching + // is required to convert from big to little endian. + // + psInst->ui32CurrentLBA = (psSCSICBW->CBWCB[2] << 24) | + (psSCSICBW->CBWCB[3] << 16) | + (psSCSICBW->CBWCB[4] << 8) | + (psSCSICBW->CBWCB[5] << 0); + + // + // More bytes to read. + // + ui16NumBlocks = (psSCSICBW->CBWCB[7] << 8) | psSCSICBW->CBWCB[8]; + + // + // Read the next logical block from the storage device. + // + if(psMSCDevice->sMediaFunctions.pfnBlockRead(psInst->pvMedia, + (uint8_t *)psInst->pui32Buffer, psInst->ui32CurrentLBA, 1) == 0) + { + psInst->pvMedia = 0; + psMSCDevice->sMediaFunctions.pfnClose(0); + } + } + + // + // If there is media present then start transferring the data. + // + if(psInst->pvMedia != 0) + { + // + // Configure and DMA for the IN transfer. + // + USBLibDMATransfer(psInst->psDMAInstance, psInst->ui8INDMA, + psInst->pui32Buffer, g_pui32BlockSize); + + // + // Remember that a DMA is in progress. + // + psInst->ui32Flags |= USBD_FLAG_DMA_IN; + + // + // Schedule the remaining bytes to send. + // + psInst->ui32BytesToTransfer = (g_pui32BlockSize * ui16NumBlocks); + + // + // Move on and start sending blocks. + // + psInst->ui8SCSIState = STATE_SCSI_SEND_BLOCKS; + + if(psMSCDevice->pfnEventCallback) + { + psMSCDevice->pfnEventCallback(0, USBD_MSC_EVENT_READING, 0, 0); + } + } + else + { + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 1; + g_sSCSICSW.dCSWDataResidue = 0; + + // + // Stall the IN endpoint + // + MAP_USBDevEndpointStall(USB0_BASE, psInst->ui8INEndpoint, + USB_EP_DEV_IN); + + // + // Mark the sense code as valid and indicate that these is no media + // present. + // + psInst->ui8ErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS; + psInst->ui8SenseKey = SCSI_RS_KEY_NOT_READY; + psInst->ui16AddSenseCode = SCSI_RS_MED_NOT_PRSNT; + + psInst->ui8SCSIState = STATE_SCSI_SEND_STATUS; + } +} + +//***************************************************************************** +// +// This function is used to handle the SCSI Read 10 command when it is +// received from the host. +// +//***************************************************************************** +static void +USBDSCSIWrite10(tUSBDMSCDevice *psMSCDevice, tMSCCBW *psSCSICBW) +{ + uint16_t ui16NumBlocks; + tMSCInstance *psInst; + + // + // Get instance data pointers. + // + psInst = &psMSCDevice->sPrivateData; + + // + // If there is media present then start transferring the data. + // + if(psInst->pvMedia != 0) + { + // + // Get the logical block from the CBW structure. This switching + // is required to convert from big to little endian. + // + psInst->ui32CurrentLBA = (psSCSICBW->CBWCB[2] << 24) | + (psSCSICBW->CBWCB[3] << 16) | + (psSCSICBW->CBWCB[4] << 8) | + (psSCSICBW->CBWCB[5] << 0); + + // + // More bytes to read. + // + ui16NumBlocks = (psSCSICBW->CBWCB[7] << 8) | psSCSICBW->CBWCB[8]; + + psInst->ui32BytesToTransfer = g_pui32BlockSize * ui16NumBlocks; + + // + // Start sending logical blocks, these are always multiples of + // g_pui32BlockSize bytes. + // + psInst->ui8SCSIState = STATE_SCSI_RECEIVE_BLOCKS; + + // + // Configure and enable DMA for the OUT transfer. + // + USBLibDMATransfer(psInst->psDMAInstance, psInst->ui8OUTDMA, + psInst->pui32Buffer, g_pui32BlockSize); + + // + // Remember that a DMA is in progress. + // + psInst->ui32Flags |= USBD_FLAG_DMA_OUT; + + // + // Notify the application of the write event. + // + if(psMSCDevice->pfnEventCallback) + { + psMSCDevice->pfnEventCallback(0, USBD_MSC_EVENT_WRITING, 0, 0); + } + } + else + { + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 1; + g_sSCSICSW.dCSWDataResidue = 0; + + // + // Stall the IN endpoint + // + MAP_USBDevEndpointStall(USB0_BASE, psInst->ui8OUTEndpoint, + USB_EP_DEV_OUT); + + // + // Mark the sense code as valid and indicate that these is no media + // present. + // + psInst->ui8ErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS; + psInst->ui8SenseKey = SCSI_RS_KEY_NOT_READY; + psInst->ui16AddSenseCode = SCSI_RS_MED_NOT_PRSNT; + + psInst->ui8SCSIState = STATE_SCSI_SEND_STATUS; + } +} + +//***************************************************************************** +// +// This function is used to handle the SCSI Mode Sense 6 command when it is +// received from the host. +// +//***************************************************************************** +static void +USBDSCSIModeSense6(tUSBDMSCDevice *psMSCDevice, tMSCCBW *psSCSICBW) +{ + tMSCInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = &psMSCDevice->sPrivateData; + + // + // If there is media present send the response. + // + if(psInst->pvMedia != 0) + { + // + // Three extra bytes in this response. + // + g_pui8Command[0] = 3; + g_pui8Command[1] = 0; + g_pui8Command[2] = 0; + g_pui8Command[3] = 0; + + // + // Manually send the response back to the host. + // + MAP_USBEndpointDataPut(USB0_BASE, psInst->ui8INEndpoint, g_pui8Command, + 4); + MAP_USBEndpointDataSend(USB0_BASE, psInst->ui8INEndpoint, + USB_TRANS_IN); + + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 0; + g_sSCSICSW.dCSWDataResidue = psSCSICBW->dCBWDataTransferLength - 4; + } + else + { + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 1; + g_sSCSICSW.dCSWDataResidue = 0; + + // + // Stall the IN endpoint + // + MAP_USBDevEndpointStall(USB0_BASE, psInst->ui8INEndpoint, + USB_EP_DEV_IN); + + // + // Mark the sense code as valid and indicate that these is no media + // present. + // + psInst->ui8ErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS; + psInst->ui8SenseKey = SCSI_RS_KEY_NOT_READY; + psInst->ui16AddSenseCode = SCSI_RS_MED_NOT_PRSNT; + } + + psInst->ui8SCSIState = STATE_SCSI_SEND_STATUS; +} + +//***************************************************************************** +// +// This function is used to send out the response data based on the current +// status of the mass storage class. +// +//***************************************************************************** +static void +USBDSCSISendStatus(tUSBDMSCDevice *psMSCDevice) +{ + tMSCInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = &psMSCDevice->sPrivateData; + + // + // Respond with the requested status. + // + MAP_USBEndpointDataPut(USB0_BASE, psInst->ui8INEndpoint, + (uint8_t *)&g_sSCSICSW, 13); + MAP_USBEndpointDataSend(USB0_BASE, psInst->ui8INEndpoint, USB_TRANS_IN); + + // + // Move the state to status sent so that the next interrupt will move the + // statue to idle. + // + psInst->ui8SCSIState = STATE_SCSI_SENT_STATUS; +} + +//***************************************************************************** +// +// This function is used to handle the Prevent/Allow Medium Removal command when it is +// received from the host. +// +//***************************************************************************** +static void +USBDSCSIPreventAllowMediumRemoval(tUSBDMSCDevice *psMSCDevice, tMSCCBW *psSCSICBW) +{ + tMSCInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = &psMSCDevice->sPrivateData; + + // + // If there is media present send the response. + // + if(psInst->pvMedia != 0) + { + // + // See if this was an allow or prevent removal request. + // + if((psSCSICBW->CBWCB[4] & SCSI_PE_MEDRMV_M) == SCSI_PE_MEDRMV_ALLOW) + { + psInst->ui32Flags |= USBD_FLAG_ALLOW_REMOVAL; + } + else + { + psInst->ui32Flags &= ~USBD_FLAG_ALLOW_REMOVAL; + } + + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 0; + g_sSCSICSW.dCSWDataResidue = 0; + } + else + { + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 1; + g_sSCSICSW.dCSWDataResidue = 0; + + // + // Stall the IN endpoint + // + MAP_USBDevEndpointStall(USB0_BASE, psInst->ui8INEndpoint, + USB_EP_DEV_IN); + + // + // Mark the sense code as valid and indicate that these is no media + // present. + // + psInst->ui8ErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS; + psInst->ui8SenseKey = SCSI_RS_KEY_NOT_READY; + psInst->ui16AddSenseCode = SCSI_RS_MED_NOT_PRSNT; + } + + psInst->ui8SCSIState = STATE_SCSI_SEND_STATUS; +} + +//***************************************************************************** +// +// This function is used to handle the SCSI Start/Stop Unit command when it is +// received from the host. +// +//***************************************************************************** +static void +USBDSCSIStartStopUnit(tUSBDMSCDevice *psMSCDevice, tMSCCBW *psSCSICBW) +{ + tMSCInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = &psMSCDevice->sPrivateData; + + // + // If there is media present send the response. + // + if(psInst->pvMedia != 0) + { + switch(psSCSICBW->CBWCB[4] & (SCSI_SS_UNIT_START | SCSI_SS_UNIT_LOEJ)) + { + case 0: + { + // + // Media state is now stopped but not ejected. + // + psInst->iMediaStatus = eUSBDMSCMediaStopped; + + g_sSCSICSW.bCSWStatus = 0; + + break; + } + case SCSI_SS_UNIT_START: + { + // + // Return to Media present. + // + psInst->iMediaStatus = eUSBDMSCMediaPresent; + + g_sSCSICSW.bCSWStatus = 0; + + break; + } + case SCSI_SS_UNIT_LOEJ: + { + // + // Only allow eject if the Prevent/Allow Medium Removal has + // been sent and enabled medium removal. + // + if(psInst->ui32Flags & USBD_FLAG_ALLOW_REMOVAL) + { + psInst->iMediaStatus = eUSBDMSCMediaNotPresent; + psMSCDevice->sMediaFunctions.pfnClose(0); + psMSCDevice->sPrivateData.pvMedia = 0; + g_sSCSICSW.bCSWStatus = 0; + } + else + { + g_sSCSICSW.bCSWStatus = 1; + } + + break; + } + case SCSI_SS_UNIT_START | SCSI_SS_UNIT_LOEJ: + { + // + // Since there was no media, check for media here. + // + psInst->pvMedia = psMSCDevice->sMediaFunctions.pfnOpen(0); + + // + // If it is still not present then fail this command. + // + if(psInst->pvMedia != 0) + { + g_sSCSICSW.bCSWStatus = 0; + } + else + { + g_sSCSICSW.bCSWStatus = 1; + } + break; + } + default: + { + break; + } + } + + // + // There is no further data to send. + // + g_sSCSICSW.dCSWDataResidue = 0; + } + else + { + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 1; + g_sSCSICSW.dCSWDataResidue = 0; + + // + // Stall the IN endpoint + // + MAP_USBDevEndpointStall(USB0_BASE, psInst->ui8INEndpoint, + USB_EP_DEV_IN); + + // + // Mark the sense code as valid and indicate that these is no media + // present. + // + psInst->ui8ErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS; + psInst->ui8SenseKey = SCSI_RS_KEY_NOT_READY; + psInst->ui16AddSenseCode = SCSI_RS_MED_NOT_PRSNT; + } + + psInst->ui8SCSIState = STATE_SCSI_SEND_STATUS; +} + +//***************************************************************************** +// +// This function is used to handle all SCSI commands. +// +//***************************************************************************** +uint32_t +USBDSCSICommand(tUSBDMSCDevice *psMSCDevice, tMSCCBW *psSCSICBW) +{ + uint32_t ui32RetCode, ui32TransferLength; + tMSCInstance *psInst; + + // + // Get our instance data pointer. + // + psInst = &psMSCDevice->sPrivateData; + + // + // Initialize the return code. + // + ui32RetCode = 1; + + // + // Save the transfer length because it may be overwritten by some calls. + // + ui32TransferLength = psSCSICBW->dCBWDataTransferLength; + + switch(psSCSICBW->CBWCB[0]) + { + // + // Respond to the SCSI Inquiry command. + // + case SCSI_INQUIRY_CMD: + { + USBDSCSIInquiry(psMSCDevice); + + break; + } + + // + // Respond to the test unit ready command. + // + case SCSI_TEST_UNIT_READY: + { + g_sSCSICSW.dCSWDataResidue = 0; + + if(psInst->pvMedia != 0) + { + // + // Set the status to success for now, this could be different + // if there is no media present. + // + g_sSCSICSW.bCSWStatus = 0; + } + else if(psInst->iMediaStatus == eUSBDMSCMediaNotPresent) + { + // + // Set the status to success for now, this could be different + // if there is no media present. + // + g_sSCSICSW.bCSWStatus = 1; + psInst->ui8ErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS; + psInst->ui8SenseKey = SCSI_RS_KEY_NOT_READY; + psInst->ui16AddSenseCode = SCSI_RS_MED_NOT_PRSNT; + } + else + { + // + // Since there was no media, check for media here. + // + psInst->pvMedia = psMSCDevice->sMediaFunctions.pfnOpen(0); + + // + // If it is still not present then fail this command. + // + if(psInst->pvMedia != 0) + { + g_sSCSICSW.bCSWStatus = 0; + } + else + { + g_sSCSICSW.bCSWStatus = 1; + } + } + break; + } + + // + // Handle the Read Capacities command. + // + case SCSI_READ_CAPACITIES: + { + USBDSCSIReadCapacities(psMSCDevice); + + break; + } + + // + // Handle the Read Capacity command. + // + case SCSI_READ_CAPACITY: + { + USBDSCSIReadCapacity(psMSCDevice); + + break; + } + + // + // Handle the Request Sense command. + // + case SCSI_REQUEST_SENSE: + { + USBDSCSIRequestSense(psMSCDevice); + + break; + } + + // + // Handle the Read 10 command. + // + case SCSI_READ_10: + { + USBDSCSIRead10(psMSCDevice, psSCSICBW); + + break; + } + + // + // Handle the Write 10 command. + // + case SCSI_WRITE_10: + { + USBDSCSIWrite10(psMSCDevice, psSCSICBW); + + break; + } + + // + // Handle the Mode Sense 6 command. + // + case SCSI_MODE_SENSE_6: + { + USBDSCSIModeSense6(psMSCDevice, psSCSICBW); + + break; + } + + // + // Handle the Prevent/Allow Medium Removal command. + // + case SCSI_MEDIUM_REMOVAL: + { + USBDSCSIPreventAllowMediumRemoval(psMSCDevice, psSCSICBW); + + break; + } + + // + // Handle the Prevent/Allow Medium Removal command. + // + case SCSI_START_STOP_UNIT: + { + USBDSCSIStartStopUnit(psMSCDevice, psSCSICBW); + break; + } + + default: + { + // + // Set the status so that it can be sent when this response has + // has be successfully sent. + // + g_sSCSICSW.bCSWStatus = 1; + g_sSCSICSW.dCSWDataResidue = psSCSICBW->dCBWDataTransferLength; + + // + // If there is data then there is more work to do. + // + if(psSCSICBW->dCBWDataTransferLength != 0) + { + if(psSCSICBW->bmCBWFlags & CBWFLAGS_DIR_IN) + { + // + // Stall the IN endpoint + // + MAP_USBDevEndpointStall(USB0_BASE, psInst->ui8INEndpoint, + USB_EP_DEV_IN); + } + else + { + // + // Stall the OUT endpoint + // + MAP_USBDevEndpointStall(USB0_BASE, psInst->ui8OUTEndpoint, + USB_EP_DEV_OUT); + + } + + // + // Go back to the idle state and wait for the host to clear + // the stall later. + // + psInst->ui8SCSIState = STATE_SCSI_IDLE; + } + + // + // Set the sense codes. + // + psInst->ui8ErrorCode = SCSI_RS_VALID | SCSI_RS_CUR_ERRORS; + psInst->ui8SenseKey = SCSI_RS_KEY_ILGL_RQST; + psInst->ui16AddSenseCode = SCSI_RS_PV_INVALID; + + break; + } + } + + // + // If there is no data then send out the current status. + // + if(ui32TransferLength == 0) + { + USBDSCSISendStatus(psMSCDevice); + } + return(ui32RetCode); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** diff --git a/usblib/device/usbdmsc.h b/usblib/device/usbdmsc.h new file mode 100644 index 0000000..9f33e4c --- /dev/null +++ b/usblib/device/usbdmsc.h @@ -0,0 +1,420 @@ +//***************************************************************************** +// +// usbdmsc.h - USB mass storage device class driver. +// +// Copyright (c) 2009-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva USB Library. +// +//***************************************************************************** + +#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 +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! Media Access functions. +// +//***************************************************************************** +typedef struct +{ + // + //! This function is used to initialize and open the physical drive number + //! associated with the parameter \e ui32Drive. The function returns + //! zero if the drive could not be opened for some reason. In the case of + //! removable device like an SD card this function must return zero if + //! the SD card is not present. + //! The function returns a pointer to data that should be passed to other + //! APIs or returns 0 if no drive was found. + // + void *(*pfnOpen)(uint32_t ui32Drive); + + //************************************************************************* + // + //! This function closes the drive number in use by the mass storage class + //! device. The \e pvDrive is the pointer that was returned from a call to + //! \e pfnOpen. This function is used to close the physical drive + //! number associated with the parameter \e pvDrive. This function + //! returns 0 if the drive was closed successfully and any other value + //! indicates a failure. + // + //************************************************************************* + void (*pfnClose)(void *pvDrive); + + //************************************************************************* + // + //! This function reads a block of data from a device opened by the + //! \e pfnOpen call. The \e pvDrive parameter is the pointer that was + //! returned from the original call to \e pfnOpen. The \e pui8Data + //! parameter is the buffer that data will be written into. The data area + //! pointed to by \e pui8Data must be at least \e ui32NumBlocks * Block + //! Size bytes to prevent overwriting data. The \e ui32Sector is the block + //! address to read and \e ui32NumBlocks is the number of blocks to read. + //! This function returns the number of bytes that were read from the + //! and placed into the \e pui8Data buffer.. + // + //************************************************************************* + uint32_t (*pfnBlockRead)(void *pvDrive, uint8_t *pui8Data, + uint32_t ui32Sector, uint32_t ui32NumBlocks); + + //************************************************************************* + // + //! This function is use to write blocks to a physical device from the + //! buffer pointed to by the \e pui8Data buffer. The \e pvDrive parameter + //! is the pointer that was returned from the original call to \e pfnOpen. + //! The \e pui8Data is the pointer to the data to write to the storage + //! device and \e ui32NumBlocks is the number of blocks to write. The + //! \e ui32Sector parameter is the sector number used to write the block. + //! If the number of blocks is greater than one then the block address + //! increments and writes to the next block until + //! \e ui32NumBlocks * Block Size bytes are written. This function returns + //! the number of bytes that were written to the device. + // + //************************************************************************* + uint32_t (*pfnBlockWrite)(void *pvDrive, uint8_t *pui8Data, + uint32_t ui32Sector, uint32_t ui32NumBlocks); + + //************************************************************************* + // + //! This function returns the total number of blocks on a physical device + //! based on the \e pvDrive parameter. The \e pvDrive parameter + //! is the pointer that was returned from the original call to \e pfnOpen. + // + //************************************************************************* + uint32_t (*pfnNumBlocks)(void *pvDrive); + + //************************************************************************* + // + //! This function returns the block size for a physical device based on the + //! \e pvDrive parameter. The \e pvDrive parameter is the pointer + //! that was returned from the original call to \e pfnOpen. + // + //************************************************************************* + uint32_t (*pfnBlockSize)(void *pvDrive); + +} +tMSCDMedia; + +//***************************************************************************** +// +// These defines control the default sizes of USB transfers for data and +// commands. +// +//***************************************************************************** +#define DEVICE_BLOCK_SIZE 512 + +//***************************************************************************** +// +// USBDMSCMediaChange() tUSBDMSCMediaStatus values. +// +//***************************************************************************** +typedef enum +{ + eUSBDMSCMediaPresent, + eUSBDMSCMediaNotPresent, + eUSBDMSCMediaStopped, + eUSBDMSCMediaUnknown +} +tUSBDMSCMediaStatus; + +//***************************************************************************** +// +// PRIVATE +// +// This structure defines the private instance data and state variables for the +// mass storage class. The memory for this structure is in the the +// sPrivateData 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 +{ + // + // Base address for the USB controller. + // + uint32_t ui32USBBase; + + // + // The device info to interact with the lower level DCD code. + // + tDeviceInfo sDevInfo; + + // + // These three values are used to return the current sense data for an + // instance of the mass storage class. + // + uint8_t ui8ErrorCode; + uint8_t ui8SenseKey; + uint16_t ui16AddSenseCode; + + // + // The pointer to the instance returned from the Open call to the media. + // + void *pvMedia; + + // + // The connection status of the device. + // + volatile bool bConnected; + + // + // Holds the flag settings for this instance. + // + uint32_t ui32Flags; + + // + // Holds the current media status. + // + tUSBDMSCMediaStatus iMediaStatus; + + // + // MSC block buffer. + // + uint32_t pui32Buffer[0x1000>>2]; + + // + // Current number of bytes to transfer. + // + uint32_t ui32BytesToTransfer; + + // + // The LBA for the current transfer. + // + uint32_t ui32CurrentLBA; + + // + // The IN endpoint number, this is modified in composite devices. + // + uint8_t ui8INEndpoint; + + // + // The IN DMA channel. + // + uint8_t ui8INDMA; + + // + // The OUT endpoint number, this is modified in composite devices. + // + uint8_t ui8OUTEndpoint; + + // + // The OUT DMA channel. + // + uint8_t ui8OUTDMA; + + // + // The bulk class interface number, this is modified in composite devices. + // + uint8_t ui8Interface; + + // + // Active SCSI state. + // + uint8_t ui8SCSIState; + + // + // A copy of the DMA instance data used with calls to USBLibDMA functions. + // + tUSBDMAInstance *psDMAInstance; +} +tMSCInstance; + +//***************************************************************************** +// +// This is the size of the g_pui8MSCInterface array in bytes. +// +//***************************************************************************** +#define MSCINTERFACE_SIZE (23) + +//***************************************************************************** +// +//! 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. +// +// +//***************************************************************************** +#define COMPOSITE_DMSC_SIZE (MSCINTERFACE_SIZE) + +//***************************************************************************** +// +//! 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. + // + const uint16_t ui16VID; + + // + //! The product ID that this device is to present in the device descriptor. + // + const uint16_t ui16PID; + + // + //! 8 byte vendor string. + // + const uint8_t pui8Vendor[8]; + + // + //! 16 byte vendor string. + // + const uint8_t pui8Product[16]; + + // + //! 4 byte vendor string. + // + const uint8_t pui8Version[4]; + + // + //! The maximum power consumption of the device, expressed in milliamps. + // + const uint16_t ui16MaxPowermA; + + // + //! Indicates whether the device is self or bus-powered and whether or not + //! it supports remote wakeup. Valid values are \b USB_CONF_ATTR_SELF_PWR + //! or \b USB_CONF_ATTR_BUS_PWR, optionally ORed with + //! \b USB_CONF_ATTR_RWAKE. + // + const uint8_t ui8PwrAttributes; + + // + //! 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 uint8_t * const *ppui8StringDescriptors; + + // + //! The number of descriptors provided in the \e ppStringDescriptors + //! array. This must be 1 + ((5 + (num HID strings)) * (num languages)). + // + const uint32_t ui32NumStringDescriptors; + + // + //! 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. + // + const tMSCDMedia sMediaFunctions; + + // + //! This is the callback function for various events that occur during + //! mass storage class operation. + // + const tUSBCallback pfnEventCallback; + + // + //! 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 sPrivateData; +} +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) + +//***************************************************************************** +// +// API Function Prototypes +// +//***************************************************************************** +extern void *USBDMSCInit(uint32_t ui32Index, + tUSBDMSCDevice *psMSCDevice); +extern void *USBDMSCCompositeInit(uint32_t ui32Index, + tUSBDMSCDevice *psMSCDevice, + tCompositeEntry *psCompEntry); +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