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 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1510 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..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. +//! @} +// +//***************************************************************************** + -- cgit v1.3.1