From c241dbd7e78c50327781a35d88d9f2db7ff2b271 Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Mon, 29 Oct 2012 23:08:53 +0200 Subject: Added usblib --- usblib/device/usbdaudio.c | 1485 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1485 insertions(+) create mode 100644 usblib/device/usbdaudio.c (limited to 'usblib/device/usbdaudio.c') diff --git a/usblib/device/usbdaudio.c b/usblib/device/usbdaudio.c new file mode 100644 index 0000000..a1ef199 --- /dev/null +++ b/usblib/device/usbdaudio.c @@ -0,0 +1,1485 @@ +//***************************************************************************** +// +// usbdaudio.c - USB audio device class driver. +// +// Copyright (c) 2009-2012 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/usb.h" +#include "driverlib/udma.h" +#include "usblib/usblib.h" +#include "usblib/usbaudio.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbdaudio.h" + +//***************************************************************************** +// +//! \addtogroup audio_device_class_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The following are the USB audio descriptor identifiers. +// +//***************************************************************************** +#define AUDIO_IN_TERMINAL_ID 1 +#define AUDIO_OUT_TERMINAL_ID 2 +#define AUDIO_CONTROL_ID 3 + +//***************************************************************************** +// +// The following are the USB interface numbers for this audio device. +// +//***************************************************************************** +#define AUDIO_INTERFACE_CONTROL 0 +#define AUDIO_INTERFACE_OUTPUT 1 + +//***************************************************************************** +// +// Endpoints to use for each of the required endpoints in the driver. +// +//***************************************************************************** +#define ISOC_OUT_ENDPOINT USB_EP_1 +#define ISOC_OUT_DMA_CHANNEL UDMA_CHANNEL_USBEP1RX + +//***************************************************************************** +// +// Max size is (48000 samples/sec * 4 bytes/sample) * 0.001 seconds/frame. +// +//***************************************************************************** +#define ISOC_OUT_EP_MAX_SIZE ((48000*4)/1000) + +//***************************************************************************** +// +// Device Descriptor. This is stored in RAM to allow several fields to be +// changed at runtime based on the client's requirements. +// +//***************************************************************************** +static unsigned char g_pAudioDeviceDescriptor[] = +{ + 18, // Size of this structure. + USB_DTYPE_DEVICE, // Type of this structure. + USBShort(0x110), // USB version 1.1 (if we say 2.0, hosts assume + // high-speed - see USB 2.0 spec 9.2.6.6) + 0, // USB Device Class (spec 5.1.1) + 0, // USB Device Sub-class (spec 5.1.1) + 0, // USB Device protocol (spec 5.1.1) + 64, // Maximum packet size for default pipe. + USBShort(0), // Vendor ID (filled in during USBDAudioInit). + USBShort(0), // Product ID (filled in during USBDAudioInit). + USBShort(0x100), // Device Version BCD. + 1, // Manufacturer string identifier. + 2, // Product string identifier. + 3, // Product serial number. + 1 // Number of configurations. +}; + +//***************************************************************************** +// +// Audio class device configuration descriptor. +// +// It is vital that the configuration descriptor bConfigurationValue field +// (byte 6) is 1 for the first configuration and increments by 1 for each +// additional configuration defined here. This relationship is assumed in the +// device stack for simplicity even though the USB 2.0 specification imposes +// no such restriction on the bConfigurationValue values. +// +// Note that this structure is deliberately located in RAM since we need to +// be able to patch some values in it based on client requirements. +// +//***************************************************************************** +static unsigned char g_pAudioDescriptor[] = +{ + // + // Configuration descriptor header. + // + 9, // Size of the configuration descriptor. + USB_DTYPE_CONFIGURATION, // Type of this descriptor. + USBShort(32), // The total size of this full structure. + 2, // The number of interfaces in this + // configuration. + 1, // The unique value for this configuration. + 0, // The string identifier that describes this + // configuration. + USB_CONF_ATTR_BUS_PWR, // Bus Powered, Self Powered, remote wake up. + 250, // The maximum power in 2mA increments. +}; + +//***************************************************************************** +// +// This is the Interface Association Descriptor for the serial device used in +// composite devices. +// +//***************************************************************************** +unsigned char g_pIADAudioDescriptor[] = +{ + + 8, // Size of the interface descriptor. + USB_DTYPE_INTERFACE_ASC, // Interface Association Type. + 0x0, // Default starting interface is 0. + 0x2, // Number of interfaces in this association. + USB_CLASS_AUDIO, // The device class for this association. + USB_SUBCLASS_UNDEFINED, // The device subclass for this association. + USB_PROTOCOL_UNDEFINED, // The protocol for this association. + 0 // The string index for this association. +}; + +const tConfigSection g_sIADAudioConfigSection = +{ + sizeof(g_pIADAudioDescriptor), + g_pIADAudioDescriptor +}; + +//***************************************************************************** +// +// The remainder of the configuration descriptor is stored in flash since we +// don't need to modify anything in it at runtime. +// +//***************************************************************************** +const unsigned char g_pAudioControlInterface[] = +{ + // + // Vendor-specific Interface Descriptor. + // + 9, // Size of the interface descriptor. + USB_DTYPE_INTERFACE, // Type of this descriptor. + AUDIO_INTERFACE_CONTROL, // The index for this interface. + 0, // The alternate setting for this interface. + 0, // The number of endpoints used by this + // interface. + USB_CLASS_AUDIO, // The interface class + USB_ASC_AUDIO_CONTROL, // The interface sub-class. + 0, // The interface protocol for the sub-class + // specified above. + 0, // The string index for this interface. + + // + // Audio Header Descriptor. + // + 9, // The size of this descriptor. + USB_DTYPE_CS_INTERFACE, // Interface descriptor is class specific. + USB_ACDSTYPE_HEADER, // Descriptor sub-type is HEADER. + USBShort(0x0100), // Audio Device Class Specification Release + // Number in Binary-Coded Decimal. + // Total number of bytes in + // g_pAudioControlInterface + USBShort((9 + 9 + 12 + 13 + 9)), + 1, // Number of streaming interfaces. + 1, // Index of the first and only streaming + // interface. + + // + // Audio Input Terminal Descriptor. + // + 12, // The size of this descriptor. + USB_DTYPE_CS_INTERFACE, // Interface descriptor is class specific. + USB_ACDSTYPE_IN_TERMINAL, // Descriptor sub-type is INPUT_TERMINAL. + AUDIO_IN_TERMINAL_ID, // Terminal ID for this interface. + // USB streaming interface. + USBShort(USB_TTYPE_STREAMING), + 0, // ID of the Output Terminal to which this + // Input Terminal is associated. + 2, // Number of logical output channels in the + // Terminal's output audio channel cluster. + USBShort((USB_CHANNEL_L | // Describes the spatial location of the + USB_CHANNEL_R)), // logical channels. + 0, // Channel Name string index. + 0, // Terminal Name string index. + + // + // Audio Feature Unit Descriptor + // + 13, // The size of this descriptor. + USB_DTYPE_CS_INTERFACE, // Interface descriptor is class specific. + USB_ACDSTYPE_FEATURE_UNIT, // Descriptor sub-type is FEATURE_UNIT. + AUDIO_CONTROL_ID, // Unit ID for this interface. + AUDIO_IN_TERMINAL_ID, // ID of the Unit or Terminal to which this + // Feature Unit is connected. + 2, // Size in bytes of an element of the + // bmaControls() array that follows. + // Master Mute control. + USBShort(USB_ACONTROL_MUTE), + // Left channel volume control. + USBShort(USB_ACONTROL_VOLUME), + // Right channel volume control. + USBShort(USB_ACONTROL_VOLUME), + 0, // Feature unit string index. + + // + // Audio Output Terminal Descriptor. + // + 9, // The size of this descriptor. + USB_DTYPE_CS_INTERFACE, // Interface descriptor is class specific. + USB_ACDSTYPE_OUT_TERMINAL, // Descriptor sub-type is INPUT_TERMINAL. + AUDIO_OUT_TERMINAL_ID, // Terminal ID for this interface. + // Output type is a generic speaker. + USBShort(USB_ATTYPE_SPEAKER), + AUDIO_IN_TERMINAL_ID, // ID of the input terminal to which this + // output terminal is connected. + AUDIO_CONTROL_ID, // ID of the feature unit that this output + // terminal is connected to. + 0, // Output terminal string index. + +}; + +//***************************************************************************** +// +// The audio streaming interface descriptor. This describes the two valid +// interfaces for this class. The first interface has no endpoints and is used +// by host operating systems to put the device in idle mode, while the second +// is used when the audio device is active. +// +//***************************************************************************** +const unsigned char g_pAudioStreamInterface[] = +{ + // + // Vendor-specific Interface Descriptor. + // + 9, // Size of the interface descriptor. + USB_DTYPE_INTERFACE, // Type of this descriptor. + AUDIO_INTERFACE_OUTPUT, // The index for this interface. + 0, // The alternate setting for this interface. + 0, // The number of endpoints used by this + // interface. + USB_CLASS_AUDIO, // The interface class + USB_ASC_AUDIO_STREAMING, // The interface sub-class. + 0, // Unused must be 0. + 0, // The string index for this interface. + + // + // Vendor-specific Interface Descriptor. + // + 9, // Size of the interface descriptor. + USB_DTYPE_INTERFACE, // Type of this descriptor. + 1, // The index for this interface. + 1, // The alternate setting for this interface. + 1, // The number of endpoints used by this + // interface. + USB_CLASS_AUDIO, // The interface class + USB_ASC_AUDIO_STREAMING, // The interface sub-class. + 0, // Unused must be 0. + 0, // The string index for this interface. + + // + // Class specific Audio Streaming Interface descriptor. + // + 7, // Size of the interface descriptor. + USB_DTYPE_CS_INTERFACE, // Interface descriptor is class specific. + USB_ASDSTYPE_GENERAL, // General information. + AUDIO_IN_TERMINAL_ID, // ID of the terminal to which this streaming + // interface is connected. + 1, // One frame delay. + USBShort(USB_ADF_PCM), // + + // + // Format type Audio Streaming descriptor. + // + 11, // Size of the interface descriptor. + USB_DTYPE_CS_INTERFACE, // Interface descriptor is class specific. + USB_ASDSTYPE_FORMAT_TYPE, // Audio Streaming format type. + USB_AF_TYPE_TYPE_I, // Type I audio format type. + 2, // Two audio channels. + 2, // Two bytes per audio sub-frame. + 16, // 16 bits per sample. + 1, // One sample rate provided. + USB3Byte(48000), // Only 48000 sample rate supported. + + // + // Endpoint Descriptor + // + 9, // The size of the endpoint descriptor. + USB_DTYPE_ENDPOINT, // Descriptor type is an endpoint. + // OUT endpoint with address + // ISOC_OUT_ENDPOINT. + USB_EP_DESC_OUT | USB_EP_TO_INDEX(ISOC_OUT_ENDPOINT), + USB_EP_ATTR_ISOC | // Endpoint is an adaptive isochronous data + USB_EP_ATTR_ISOC_ADAPT | // endpoint. + USB_EP_ATTR_USAGE_DATA, + USBShort(ISOC_OUT_EP_MAX_SIZE), // The maximum packet size. + 1, // The polling interval for this endpoint. + 0, // Refresh is unused. + 0, // Synch endpoint address. + + // + // Audio Streaming Isochronous Audio Data Endpoint Descriptor + // + 7, // The size of the descriptor. + USB_ACSDT_ENDPOINT, // Audio Class Specific Endpoint Descriptor. + USB_ASDSTYPE_GENERAL, // This is a general descriptor. + USB_EP_ATTR_ACG_SAMPLING, // Sampling frequency is supported. + USB_EP_LOCKDELAY_UNDEF, // Undefined lock delay units. + USBShort(0), // No lock delay. +}; + +//***************************************************************************** +// +// The audio device configuration descriptor is defined as three sections, +// one containing just the 9 byte USB configuration descriptor. The second +// holds the audio streaming interface and the third holds the audio control +// interface. +// +//***************************************************************************** +const tConfigSection g_sAudioConfigSection = +{ + sizeof(g_pAudioDescriptor), + g_pAudioDescriptor +}; + +const tConfigSection g_sAudioStreamInterfaceSection = +{ + sizeof(g_pAudioStreamInterface), + g_pAudioStreamInterface +}; + +const tConfigSection g_sAudioControlInterfaceSection = +{ + sizeof(g_pAudioControlInterface), + g_pAudioControlInterface +}; + +//***************************************************************************** +// +// This array lists all the sections that must be concatenated to make a +// single, complete audio device configuration descriptor. +// +//***************************************************************************** +const tConfigSection *g_psAudioSections[] = +{ + &g_sAudioConfigSection, + &g_sIADAudioConfigSection, + &g_sAudioControlInterfaceSection, + &g_sAudioStreamInterfaceSection +}; + +#define NUM_AUDIO_SECTIONS (sizeof(g_psAudioSections) / \ + sizeof(tConfigSection *)) + +//***************************************************************************** +// +// The header for the single configuration we support. This is the root of +// the data structure that defines all the bits and pieces that are pulled +// together to generate the configuration descriptor. +// +//***************************************************************************** +const tConfigHeader g_sAudioConfigHeader = +{ + NUM_AUDIO_SECTIONS, + g_psAudioSections +}; + +//***************************************************************************** +// +// Configuration Descriptor. +// +//***************************************************************************** +const tConfigHeader * const g_pAudioConfigDescriptors[] = +{ + &g_sAudioConfigHeader +}; + +//***************************************************************************** +// +// Various internal handlers needed by this class. +// +//***************************************************************************** +static void HandleDisconnect(void *pvInstance); +static void InterfaceChange(void *pvInstance, unsigned char ucInterface, + unsigned char ucAlternateSetting); +static void ConfigChangeHandler(void *pvInstance, unsigned long ulValue); +static void DataReceived(void *pvInstance, unsigned long ulInfo); +static void HandleEndpoints(void *pvInstance, unsigned long ulStatus); +static void HandleRequests(void *pvInstance, tUSBRequest *pUSBRequest); +static void HandleDevice(void *pvInstance, unsigned long ulRequest, + void *pvRequestData); + +//***************************************************************************** +// +// The FIFO configuration for USB audio device class. +// +//***************************************************************************** +const tFIFOConfig g_sUSBAudioFIFOConfig = +{ + // + // IN endpoints. + // + { + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN }, + { false, USB_EP_DEV_IN } + }, + + // + // OUT endpoints. + // + { + { false, USB_EP_DEV_OUT | USB_EP_DMA_MODE_1 | USB_EP_AUTO_CLEAR }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT }, + { false, USB_EP_DEV_OUT } + }, +}; + +//***************************************************************************** +// +// The device information structure for the USB Audio device. +// +//***************************************************************************** +tDeviceInfo g_sAudioDeviceInfo = +{ + // + // Device event handler callbacks. + // + { + // + // GetDescriptor + // + 0, + + // + // RequestHandler + // + HandleRequests, + + // + // InterfaceChange + // + InterfaceChange, + + // + // ConfigChange + // + ConfigChangeHandler, + + // + // DataReceived + // + DataReceived, + + // + // DataSentCallback + // + 0, + + // + // ResetHandler + // + 0, + + // + // SuspendHandler + // + 0, + + // + // ResumeHandler + // + 0, + + // + // DisconnectHandler + // + HandleDisconnect, + + // + // EndpointHandler + // + HandleEndpoints, + + // + // Device handler + // + HandleDevice + }, + g_pAudioDeviceDescriptor, + g_pAudioConfigDescriptors, + 0, + 0, + &g_sUSBAudioFIFOConfig +}; + +//***************************************************************************** +// +// This function is called to handle data being received back from the host so +// that the application callback can be called when the new data is ready. +// +//***************************************************************************** +static void +DataReceived(void *pvInstance, unsigned long ulInfo) +{ + tAudioInstance *psInst; + const tUSBDAudioDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDAudioDevice *)pvInstance; + + // + // Make a copy of this pointer for ease of use in this function. + // + psInst = psDevice->psPrivateData; + + // + // If there is an update pending and the request was to set a current + // value then check which value was set. + // + if(psInst->usUpdate && (psInst->ucRequest == USB_AC_SET_CUR)) + { + // + // Only handling interface requests. + // + if((psInst->usRequestType & USB_RTYPE_RECIPIENT_M) == + USB_RTYPE_INTERFACE) + { + if(psInst->usUpdate == VOLUME_CONTROL) + { + // + // Inform the callback of the new volume. + // + psDevice->pfnCallback(0, USBD_AUDIO_EVENT_VOLUME, + psInst->sVolume, 0); + } + else if(psDevice->psPrivateData->usUpdate == MUTE_CONTROL) + { + // + // Inform the callback of the new data. + // + psDevice->pfnCallback(0, USBD_AUDIO_EVENT_MUTE, psInst->ucMute, + 0); + } + } + psInst->usUpdate = 0; + } +} + +//***************************************************************************** +// +// This function is called to handle the interrupts on the isochronous endpoint +// for the audio device class. +// +//***************************************************************************** +static void +HandleEndpoints(void *pvInstance, unsigned long ulStatus) +{ + unsigned long ulEPStatus; + tAudioInstance *psInst; + unsigned char *pucData; + const tUSBDAudioDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDAudioDevice *)pvInstance; + + // + // Make a copy of this pointer for ease of use later in this function. + // + psInst = psDevice->psPrivateData; + + // + // Make sure this was for the isochronous out endpoint. + // + if((psInst->sBuffer.pvData != 0) && + (MAP_uDMAChannelModeGet(psInst->ucOUTDMA) == UDMA_MODE_STOP)) + { + // + // Save the pointer to the data buffer. + // + pucData = psInst->sBuffer.pvData; + + // + // Clear out the buffer pointer to indicate it is no longer in use. + // + psInst->sBuffer.pvData = 0; + + // + // Inform the callback of the new data. + // + psInst->sBuffer.pfnCallback(pucData, psInst->sBuffer.ulSize, + USBD_AUDIO_EVENT_DATAOUT); + + // + // Read out the current endpoint status. + // + ulEPStatus = MAP_USBEndpointStatus(USB0_BASE, psInst->ucOUTEndpoint); + + // + // Acknowledge that the data was read, this will not cause a bus + // acknowledgment. + // + MAP_USBDevEndpointDataAck(USB0_BASE, psInst->ucOUTEndpoint, 0); + + // + // Clear the status bits. + // + MAP_USBDevEndpointStatusClear(USB0_BASE, psInst->ucOUTEndpoint, + ulEPStatus); + } +} + +//***************************************************************************** +// +// Device instance specific handler. +// +//***************************************************************************** +static void +HandleDevice(void *pvInstance, unsigned long ulRequest, void *pvRequestData) +{ + tAudioInstance *psInst; + unsigned char *pucData; + + // + // Create the serial instance data. + // + psInst = ((tUSBDAudioDevice *)pvInstance)->psPrivateData; + + // + // Create the char array used by the events supported by the USB CDC + // serial class. + // + pucData = (unsigned char *)pvRequestData; + + switch(ulRequest) + { + // + // This was an interface change event. + // + case USB_EVENT_COMP_IFACE_CHANGE: + { + // + // Save the change to the appropriate interface number. + // + if(pucData[0] == AUDIO_INTERFACE_CONTROL) + { + psInst->ucInterfaceControl = pucData[1]; + } + else if(pucData[0] == AUDIO_INTERFACE_OUTPUT) + { + psInst->ucInterfaceAudio = pucData[1]; + } + break; + } + + // + // This was an endpoint change event. + // + case USB_EVENT_COMP_EP_CHANGE: + { + // + // Determine if this is an IN or OUT endpoint that has changed. + // + if((pucData[0] & USB_EP_DESC_IN) == 0) + { + // + // Extract the new endpoint number without the DIR bit. + // + psInst->ucOUTEndpoint = INDEX_TO_USB_EP(pucData[1] & 0x7f); + + // + // Extract the new DMA channel. + // + psInst->ucOUTDMA = UDMA_CHANNEL_USBEP1RX + + (((pucData[1] & 0x7f) - 1) * 2); + + // + // Basic configuration for DMA on the OUT endpoint. + // + MAP_uDMAChannelControlSet(psInst->ucOUTDMA, + (UDMA_SIZE_32 | UDMA_SRC_INC_NONE| + UDMA_DST_INC_32 | UDMA_ARB_16)); + + // + // Select this channel for this endpoint, this only affects + // devices that have this feature. + // + MAP_USBEndpointDMAChannel(USB0_BASE, psInst->ucOUTEndpoint, + psInst->ucOUTDMA); + } + break; + } + + // + // Handle class specific reconfiguring of the configuration descriptor + // once the composite class has built the full descriptor. + // + case USB_EVENT_COMP_CONFIG: + { + // + // This sets the bFirstInterface of the Interface Association + // descriptor to the first interface which is the control + // interface used by this instance. + // + pucData[2] = psInst->ucInterfaceControl; + + break; + } + + default: + { + break; + } + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device is +// disconnected from the host. +// +//***************************************************************************** +static void +HandleDisconnect(void *pvInstance) +{ + const tUSBDAudioDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDAudioDevice *)pvInstance; + + // + // Inform the application that the device has been disconnected. + // + psDevice->pfnCallback(0, USB_EVENT_DISCONNECTED, 0, 0); +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device +// interface changes. This occurs when the audio device transitions between +// being active and inactive. Interface AUDIO_INTERFACE_CONTROL is the +// inactive interface that has no endpoints, while interface +// AUDIO_INTERFACE_AUDIO has the single Isochronous OUT endpoint. +// +//***************************************************************************** +static void +InterfaceChange(void *pvInstance, unsigned char ucInterface, + unsigned char ucAlternateSetting) +{ + const tUSBDAudioDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDAudioDevice *)pvInstance; + + // + // Check which interface to change into. + // + if(ucAlternateSetting == 0) + { + // + // Alternate setting 0 is an inactive state. + // + if(psDevice->pfnCallback) + { + psDevice->pfnCallback(0, USBD_AUDIO_EVENT_IDLE, 0, 0); + } + } + else + { + // + // Alternate setting 1 is the active state. + // + if(psDevice->pfnCallback) + { + psDevice->pfnCallback(0, USBD_AUDIO_EVENT_ACTIVE, 0, 0); + } + + // + // Enable uDMA on the endpoint now that the active configuration + // has been selected. + // + MAP_USBEndpointDMAEnable(USB0_BASE, + psDevice->psPrivateData->ucOUTEndpoint, + USB_EP_DEV_OUT); + } +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever the device +// configuration changes. +// +//***************************************************************************** +static void +ConfigChangeHandler(void *pvInstance, unsigned long ulValue) +{ + const tUSBDAudioDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDAudioDevice *)pvInstance; + + // + // If we have a control callback, let the client know we are open for + // business. + // + if(psDevice->pfnCallback) + { + // + // Pass the connected event to the client. + // + psDevice->pfnCallback(pvInstance, USB_EVENT_CONNECTED, 0, 0); + } +} + +//***************************************************************************** +// +//! This function should be called once for the audio class device to +//! initialized basic operation and prepare for enumeration. +//! +//! \param ulIndex is the index of the USB controller to initialize for +//! audio class device operation. +//! \param psDevice points to a structure containing parameters customizing +//! the operation of the audio device. +//! +//! In order for an application to initialize the USB audio device class, it +//! must first call this function with the a valid audio device class structure +//! in the \e psDevice parameter. This allows this function to initialize the +//! USB controller and device code to be prepared to enumerate and function as +//! a USB audio device. +//! +//! This function returns a void pointer that must be passed in to all other +//! APIs used by the audio class. +//! +//! See the documentation on the tUSBDAudioDevice structure for more +//! information on how to properly fill the structure members. +//! +//! \return Returns 0 on failure or a non-zero void pointer on success. +// +//***************************************************************************** +void * +USBDAudioInit(unsigned long ulIndex, const tUSBDAudioDevice *psDevice) +{ + // + // Check parameter validity. + // + ASSERT(ulIndex == 0); + ASSERT(psDevice); + ASSERT(psDevice->ppStringDescriptors); + ASSERT(psDevice->psPrivateData); + + USBDAudioCompositeInit(ulIndex, psDevice); + + // + // All is well so now pass the descriptors to the lower layer and put + // the bulk device on the bus. + // + USBDCDInit(ulIndex, psDevice->psPrivateData->psDevInfo); + + // + // Basic configuration for DMA on the OUT endpoint. + // + MAP_uDMAChannelControlSet(psDevice->psPrivateData->ucOUTDMA, + (UDMA_SIZE_32 | UDMA_SRC_INC_NONE| + UDMA_DST_INC_32 | UDMA_ARB_16)); + + // + // Select this channel for this endpoint, this only affects devices that + // have this feature. + // + MAP_USBEndpointDMAChannel(USB0_BASE, psDevice->psPrivateData->ucOUTEndpoint, + psDevice->psPrivateData->ucOUTDMA); + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psDevice); +} + +//***************************************************************************** +// +//! This function should be called once for the audio class device to +//! initialized basic operation and prepare for enumeration. +//! +//! \param ulIndex is the index of the USB controller to initialize for +//! audio class device operation. +//! \param psDevice points to a structure containing parameters customizing +//! the operation of the audio device. +//! +//! In order for an application to initialize the USB audio device class, it +//! must first call this function with the a valid audio device class structure +//! in the \e psDevice parameter. This allows this function to initialize the +//! USB controller and device code to be prepared to enumerate and function as +//! a USB audio device. +//! +//! This function returns a void pointer that must be passed in to all other +//! APIs used by the audio class. +//! +//! See the documentation on the tUSBDAudioDevice structure for more +//! information on how to properly fill the structure members. +//! +//! \return Returns 0 on failure or a non-zero void pointer on success. +// +//***************************************************************************** +void * +USBDAudioCompositeInit(unsigned long ulIndex, const tUSBDAudioDevice *psDevice) +{ + tAudioInstance *psInst; + tDeviceDescriptor *psDevDesc; + + // + // Check parameter validity. + // + ASSERT(ulIndex == 0); + ASSERT(psDevice); + ASSERT(psDevice->ppStringDescriptors); + ASSERT(psDevice->psPrivateData); + + // + // Initialize the workspace in the passed instance structure. + // + psInst = psDevice->psPrivateData; + psInst->psConfDescriptor = (tConfigDescriptor *)g_pAudioDescriptor; + psInst->psDevInfo = &g_sAudioDeviceInfo; + psInst->ulUSBBase = USB0_BASE; + + // + // The Control interface is at index 0. + // + psInst->ucInterfaceControl = AUDIO_INTERFACE_CONTROL; + + // + // The Audio interface is at index 1. + // + psInst->ucInterfaceAudio = AUDIO_INTERFACE_OUTPUT; + + // + // Set the default Isochronous OUT endpoint. + // + psInst->ucOUTEndpoint = ISOC_OUT_ENDPOINT; + psInst->ucOUTDMA = ISOC_OUT_DMA_CHANNEL; + + // + // Set the initial buffer to null. + // + psInst->sBuffer.pvData = 0; + + // + // Save the volume settings. + // + psInst->sVolumeMax = psDevice->sVolumeMax; + psInst->sVolumeMin = psDevice->sVolumeMin; + psInst->sVolumeStep = psDevice->sVolumeStep; + + // + // No update pending to any command. + // + psInst->usUpdate = 0; + + // + // Fix up the device descriptor with the client-supplied values. + // + psDevDesc = (tDeviceDescriptor *)psInst->psDevInfo->pDeviceDescriptor; + psDevDesc->idVendor = psDevice->usVID; + psDevDesc->idProduct = psDevice->usPID; + // + // Fix up the configuration descriptor with client-supplied values. + // + psInst->psConfDescriptor->bmAttributes = psDevice->ucPwrAttributes; + psInst->psConfDescriptor->bMaxPower = + (unsigned char)(psDevice->usMaxPowermA / 2); + + // + // Plug in the client's string stable to the device information + // structure. + // + psInst->psDevInfo->ppStringDescriptors = psDevice->ppStringDescriptors; + psInst->psDevInfo->ulNumStringDescriptors = + psDevice->ulNumStringDescriptors; + psInst->psDevInfo->pvInstance = (void *)psDevice; + + // + // Return the pointer to the instance indicating that everything went well. + // + return((void *)psDevice); +} + +//***************************************************************************** +// +//! Shuts down the audio device. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDAudioInit(). +//! +//! This function terminates audio interface for the instance supplied. This +//! function should not be called if the audio device is part of a composite +//! device and instead the USBDCompositeTerm() function should be called for +//! the full composite device. +//! Following this call, the \e pvInstance instance should not me used in any +//! other calls. +//! +//! \return None. +// +//***************************************************************************** +void +USBDAudioTerm(void *pvInstance) +{ + ASSERT(pvInstance != 0); + + // + // Cleanly exit device mode. + // + USBDCDTerm(0); +} + +//***************************************************************************** +// +// This function is called by the USB device stack whenever a non-standard +// request is received. +// +// \param pvInstance is the instance data for this request. +// \param pUSBRequest points to the request received. +// +// This call parses the provided request structure to the type of request and +// will respond to all commands that are understood by the class. +// +// \return None. +// +//***************************************************************************** +static void +HandleRequests(void *pvInstance, tUSBRequest *pUSBRequest) +{ + unsigned long ulControl; + unsigned long ulRecipient; + unsigned long ulStall; + tAudioInstance *psInst; + const tUSBDAudioDevice *psDevice; + + ASSERT(pvInstance != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDAudioDevice *)pvInstance; + + // + // Make a copy of this pointer for ease of use in this function. + // + psInst = psDevice->psPrivateData; + + // + // Make sure to acknowledge that the data was read, this will not send and + // ACK that has already been done at this point. This just tells the + // hardware that the data was read. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false); + + // + // Don't stall by default. + // + ulStall = 0; + + // + // Get the request type. + // + ulRecipient = pUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M; + + // + // Save the request type and request value. + // + psInst->usRequestType = pUSBRequest->bmRequestType; + psInst->ucRequest = pUSBRequest->bRequest; + + // + // Check if this is an endpoint request to the audio streaming endpoint. + // + if((ulRecipient == USB_RTYPE_ENDPOINT) && + (pUSBRequest->wIndex == USB_EP_TO_INDEX(psInst->ucOUTEndpoint))) + { + // + // Determine the type of request. + // + switch(psInst->ucRequest) + { + case USB_AC_SET_CUR: + { + // + // Handle retrieving the sample rate. + // + if(pUSBRequest->wValue == SAMPLING_FREQ_CONTROL) + { + // + // Retrieve the requested sample rate. + // + USBDCDRequestDataEP0(0, + (unsigned char *)&psInst->ulSampleRate, + 3); + + // + // Save what we are updating. + // + psInst->usUpdate = SAMPLING_FREQ_CONTROL; + } + break; + } + case USB_AC_GET_CUR: + { + // + // Handle retrieving the sample rate. + // + if(pUSBRequest->wValue == SAMPLING_FREQ_CONTROL) + { + // + // Send back the current sample rate. + // + USBDCDSendDataEP0(0, + (unsigned char *)&psInst->ulSampleRate, + 3); + } + break; + } + default: + { + // + // Stall on unknown commands. + // + ulStall = 1; + break; + } + } + } + else if(ulRecipient == USB_RTYPE_INTERFACE) + { + // + // Make sure the request was for the control interface. + // + if((unsigned char)pUSBRequest->wIndex != psInst->ucInterfaceControl) + { + return; + } + + // + // Extract the control value from the message. + // + ulControl = pUSBRequest->wValue & USB_CS_CONTROL_M; + + // + // Handle an audio control request to the feature control unit. + // + if((AUDIO_CONTROL_ID << 8) == (pUSBRequest->wIndex & USB_CS_CONTROL_M)) + { + // + // Determine the type of request. + // + switch(psInst->ucRequest) + { + case USB_AC_GET_MAX: + { + if(ulControl == VOLUME_CONTROL) + { + // + // Return the maximum volume setting. + // + USBDCDSendDataEP0(0, + (unsigned char *)&psInst->sVolumeMax, + 2); + } + else + { + // + // Stall on unknown commands. + // + ulStall = 1; + } + break; + } + case USB_AC_GET_MIN: + { + if(ulControl == VOLUME_CONTROL) + { + // + // Return the minimum volume setting. + // + USBDCDSendDataEP0(0, + (unsigned char *)&psInst->sVolumeMin, + 2); + } + else + { + // + // Stall on unknown commands. + // + ulStall = 1; + } + break; + } + case USB_AC_GET_RES: + { + if(ulControl == VOLUME_CONTROL) + { + // + // Return the volume step setting. + // + USBDCDSendDataEP0(0, + (unsigned char *)&psInst->sVolumeStep, + 2); + } + else + { + // + // Stall on unknown commands. + // + ulStall = 1; + } + break; + } + case USB_AC_GET_CUR: + { + if(ulControl == VOLUME_CONTROL) + { + // + // Send back the current volume level. + // + USBDCDSendDataEP0(0, + (unsigned char *)&psInst->sVolume, + 2); + } + else if(ulControl == MUTE_CONTROL) + { + // + // Send back the current mute value. + // + USBDCDSendDataEP0(0, + (unsigned char *)&psInst->ucMute, 1); + } + else + { + // + // Stall on unknown commands. + // + ulStall = 1; + } + break; + } + case USB_AC_SET_CUR: + { + if(ulControl == VOLUME_CONTROL) + { + // + // Read the new volume level. + // + USBDCDRequestDataEP0(0, + (unsigned char *)&psInst->sVolume, + 2); + + // + // Save what we are updating. + // + psInst->usUpdate = VOLUME_CONTROL; + } + else if(ulControl == MUTE_CONTROL) + { + // + // Read the new mute setting. + // + USBDCDRequestDataEP0(0, + (unsigned char *)&psInst->ucMute, + 1); + + // + // Save what we are updating. + // + psInst->usUpdate = MUTE_CONTROL; + } + else + { + // + // Stall on unknown commands. + // + ulStall = 1; + } + break; + } + case USB_AC_SET_RES: + { + if(ulControl == VOLUME_CONTROL) + { + // + // Read the new volume step setting. + // + USBDCDRequestDataEP0(0, + (unsigned char *)&psInst->sVolumeStep, 2); + + // + // Save what we are updating. + // + psInst->usUpdate = VOLUME_CONTROL; + } + else + { + // + // Stall on unknown commands. + // + ulStall = 1; + } + break; + } + default: + { + // + // Stall on unknown commands. + // + ulStall = 1; + break; + } + } + } + } + + // + // Stall on all unknown commands. + // + if(ulStall) + { + USBDCDStallEP0(0); + } +} + +//***************************************************************************** +// +//! This function is used to supply buffers to the audio class to be filled +//! from the USB host device. +//! +//! \param pvInstance is the pointer to the device instance structure as +//! returned by USBDAudioInit() or USBDAudioInitComposite(). +//! \param pvBuffer is a pointer to the buffer to fill with audio data. +//! \param ulSize is the size in bytes of the buffer pointed to by the pvBuffer +//! parameter. +//! \param pfnCallback is a callback that will provide notification when this +//! buffer has valid data. +//! +//! This function fills the buffer pointed to by the \e pvBuffer parameter with +//! at most \e ulSize one packet of data from the host controller. The ulSize +//! has a minimum value of \b ISOC_OUT_EP_MAX_SIZE since each USB packet can be +//! at most \b ISOC_OUT_EP_MAX_SIZE bytes in size. Since the audio data may +//! not be received in amounts that evenly fit in the buffer provided, the +//! buffer may not be completely filled. The \e pfnCallback function will +//! provide the amount of valid data that was actually stored in the buffer +//! provided. The function will return zero if the buffer could be scheduled +//! to be filled, otherwise the function will return a non-zero value if there +//! was some reason that the buffer could not be added. +//! +//! \return Returns 0 to indicate success any other value indicates that the +//! buffer will not be filled. +// +//***************************************************************************** +long +USBAudioBufferOut(void *pvInstance, void *pvBuffer, unsigned long ulSize, + tUSBAudioBufferCallback pfnCallback) +{ + tAudioInstance *psInst; + const tUSBDAudioDevice *psDevice; + + // + // Make sure we were not passed NULL pointers. + // + ASSERT(pvInstance != 0); + ASSERT(pvBuffer != 0); + + // + // Create the instance pointer. + // + psDevice = (const tUSBDAudioDevice *)pvInstance; + + // + // Buffer must be at least one packet in size. + // + ASSERT(ulSize >= ISOC_OUT_EP_MAX_SIZE); + ASSERT(pfnCallback); + + // + // Create a pointer of the correct type from the private pointer. + // + psInst = psDevice->psPrivateData; + + // + // Initialize the buffer instance. + // + psInst->sBuffer.pvData = pvBuffer; + psInst->sBuffer.ulSize = ulSize; + psInst->sBuffer.ulNumBytes = 0; + psInst->sBuffer.pfnCallback = pfnCallback; + + // + // Configure and enable DMA for the OUT transfer. + // + MAP_uDMAChannelTransferSet(psInst->ucOUTDMA, UDMA_MODE_BASIC, + (void *)USBFIFOAddrGet(USB0_BASE, + psInst->ucOUTEndpoint), + psInst->sBuffer.pvData, ulSize >> 2); + + // + // Start the DMA transfer. + // + MAP_uDMAChannelEnable(psInst->ucOUTDMA); + + return(0); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + -- cgit v1.3.1