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/host/usbhaudio.c | 1557 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1557 insertions(+) create mode 100644 usblib/host/usbhaudio.c (limited to 'usblib/host/usbhaudio.c') diff --git a/usblib/host/usbhaudio.c b/usblib/host/usbhaudio.c new file mode 100644 index 0000000..129130c --- /dev/null +++ b/usblib/host/usbhaudio.c @@ -0,0 +1,1557 @@ +//***************************************************************************** +// +// usbhaudio.c - USB host audio 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_types.h" +#include "driverlib/usb.h" +#include "usblib/usblib.h" +#include "usblib/usblibpriv.h" +#include "usblib/usbaudio.h" +#include "usblib/host/usbhost.h" +#include "usblib/host/usbhostpriv.h" +#include "usblib/host/usbhaudio.h" + +//***************************************************************************** +// +//! \addtogroup usblib_host_class +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// These defines are used with the USBHostAudioFormatSet() +// USBHostAudioFormatGet() to parse out interface number and alternate +// setting number for an interface. +// +//***************************************************************************** +#define INTERFACE_NUM_M 0x000000FF +#define INTERFACE_ALTSETTING_M 0x0000FF00 +#define INTERFACE_ALTSETTING_S 8 + +//***************************************************************************** +// +// Used to indicate an invalid interface descriptor number. +// +//***************************************************************************** +#define INVALID_INTERFACE 0xffffffff + +//***************************************************************************** +// +// Forward declarations for the driver open and close calls. +// +//***************************************************************************** +static void *USBAudioOpen(tUSBHostDevice *psDevice); +static void USBAudioClose(void *pvInstance); + +//***************************************************************************** +// +// This is the structure for an instance of a USB host audio driver. +// +//***************************************************************************** +struct tUSBHostAudioInstance +{ + // + // Save the device instance. + // + tUSBHostDevice *psDevice; + + // + // Used to save the call back. + // + tUSBHostAudioCallback pfnCallback; + + // + // This is the control interface. + // + uint8_t ui8IControl; + + // + // This is the output streaming interface. + // + uint8_t ui8OutInterface; + + // + // This is the currently selected active output interface used with + // ui8OutInterface interface. + // + uint8_t ui8OutAltSetting; + + // + // This is the streaming interface. + // + uint8_t ui8InInterface; + + // + // This is the currently selected active input interface used with + // ui8InInterface interface. + // + uint8_t ui8InAltSetting; + + // + // The Isochronous endpoint addresses. + // + uint8_t ui8IsochInAddress; + uint8_t ui8IsochOutAddress; + + tACInputTerminal *psInTerminal; + tACOutputTerminal *psOutTerminal; + + // + // Holds the identifier for the Feature Unit for controlling volume. + // + uint8_t ui8VolumeID; + + tACFeatureUnit *psFeatureUnit; + + // + // Holds what types of controls are enabled on the device. + // + uint16_t pui16Controls[3]; + + // + // Isochronous IN pipe. + // + uint32_t ui32IsochInPipe; + uint16_t ui16PipeSizeIn; + tUSBHostAudioCallback pfnInCallback; + void *pvInBuffer; + + // + // Isochronous OUT pipe. + // + uint32_t ui32IsochOutPipe; + uint16_t ui16PipeSizeOut; + tUSBHostAudioCallback pfnOutCallback; + void *pvOutBuffer; + + // + // State flags for this audio instance. + // + uint32_t ui32Flags; +}; + +//***************************************************************************** +// +// The internal flags for an audio interface. +// +//***************************************************************************** +#define AUDIO_FLAG_OUT_ACTIVE 1 // Audio output is active. +#define AUDIO_FLAG_IN_ACTIVE 2 // Audio input is active. + +//***************************************************************************** +// +// The USB Host audio instance. +// +//***************************************************************************** +static tUSBHostAudioInstance g_sAudioDevice = +{ + 0 +}; + +//***************************************************************************** +// +//! This constant global structure defines the Audio Class Driver that is +//! provided with the USB library. +// +//***************************************************************************** +const tUSBHostClassDriver g_sUSBHostAudioClassDriver = +{ + USB_CLASS_AUDIO, + USBAudioOpen, + USBAudioClose, + 0 +}; + +//***************************************************************************** +// +// This is the internal function that handles callbacks from the USB IN pipe. +// +//***************************************************************************** +static void +PipeCallbackIN(uint32_t ui32Pipe, uint32_t ui32Event) +{ + // + // Only handle the data available callback and pass it on to the + // application. + // + if(ui32Event == USB_EVENT_RX_AVAILABLE) + { + if(g_sAudioDevice.pfnInCallback) + { + g_sAudioDevice.pfnInCallback(&g_sAudioDevice, + USB_EVENT_RX_AVAILABLE, + USBHCDPipeTransferSizeGet(ui32Pipe), + g_sAudioDevice.pvInBuffer); + } + } +} + +//***************************************************************************** +// +// This is the internal function that handles callbacks from the USB OUT pipe. +// +//***************************************************************************** +static void +PipeCallbackOUT(uint32_t ui32Pipe, uint32_t ui32Event) +{ + // + // Only handle the transmit complete callback and pass it on to the + // application. + // + if(ui32Event == USB_EVENT_TX_COMPLETE) + { + if(g_sAudioDevice.pfnOutCallback) + { + g_sAudioDevice.pfnOutCallback(&g_sAudioDevice, + USB_EVENT_TX_COMPLETE, 0, + g_sAudioDevice.pvOutBuffer); + } + } +} + +//***************************************************************************** +// +// Finds a given terminal and type in an audio configuration descriptor. +// +//***************************************************************************** +static tDescriptorHeader * +AudioTerminalGet(tConfigDescriptor *psConfigDesc, uint32_t ui32Terminal, + uint32_t ui32TerminalType) +{ + tACOutputTerminal *psOutput; + tDescriptorHeader *psHeader; + int32_t i32BytesRemaining; + + psHeader = (tDescriptorHeader *)psConfigDesc; + i32BytesRemaining = psConfigDesc->wTotalLength; + + while(i32BytesRemaining > 0) + { + // + // Output and input terminals are the same past the bDescriptorSubtype + // and wTerminalType that are being searched for. + // + psOutput = (tACOutputTerminal *)psHeader; + + // + // Only CS_INTERFACE descriptors can be a terminal. + // + if((psHeader->bDescriptorType == USB_DTYPE_CS_INTERFACE) && + (ui32Terminal == psOutput->bDescriptorSubtype)) + { + if((psOutput->bDescriptorSubtype == USB_AI_OUTPUT_TERMINAL) || + (psOutput->bDescriptorSubtype == USB_AI_INPUT_TERMINAL)) + + { + // + // If this was the terminal type that was requested, the + // return it. + // + if(psOutput->wTerminalType == ui32TerminalType) + { + return(psHeader); + } + } + else if(psOutput->bDescriptorSubtype == USB_AI_FEATURE_UNIT) + { + return(psHeader); + } + } + + // + // Decrease the bytes remaining by the size of this descriptor. + // + i32BytesRemaining -= psHeader->bLength; + + // + // Move the pointer to the next header. + // + psHeader = (tDescriptorHeader *)((uint32_t)psHeader + + psHeader->bLength); + } + return((tDescriptorHeader *)0); +} + +//***************************************************************************** +// +// This function returns the interface number for the control interface +// in the structure passed in the psConfigDesc. +// +// \param psConfigDescriptor is a pointer to the memory containing a valid +// configuration descriptor for a device. +// +// This function searches a configuration descriptor for a control interface +// descriptor. The function only search for the first descriptor and then +// returns when it finds one. +// +// \return The first control interface descriptor number for an audio device +// or INVALID_INTERFACE if no control interface descriptor was found. +// +//***************************************************************************** +static uint32_t +AudioControlGet(tConfigDescriptor *psConfigDesc) +{ + tDescriptorHeader *psHeader; + tInterfaceDescriptor *psInterface; + uint32_t ui32Interface; + int32_t i32Bytes; + + psHeader = (tDescriptorHeader *)psConfigDesc; + i32Bytes = psConfigDesc->wTotalLength; + + // + // Initialize the interface number to an invalid value. + // + ui32Interface = INVALID_INTERFACE; + + // + // Search the whole configuration descriptor. + // + while(i32Bytes > 0) + { + // + // Find an interface descriptor and see if it is a control interface. + // + if(psHeader->bDescriptorType == USB_DTYPE_INTERFACE) + { + psInterface = (tInterfaceDescriptor *)psHeader; + + // + // If this is the control interface then return the value to the + // caller. + // + if(psInterface->bInterfaceSubClass == USB_ASC_AUDIO_CONTROL) + { + ui32Interface = psInterface->bInterfaceNumber; + + break; + } + } + + // + // Decrease the bytes remaining by the size of this descriptor. + // + i32Bytes -= psHeader->bLength; + + // + // Move the pointer to the next header. + // + psHeader = (tDescriptorHeader*)((uint32_t)psHeader + + psHeader->bLength); + } + return(ui32Interface); +} + +//***************************************************************************** +// +// If it exists, finds the correct audio interface for a given audio format. +// +//***************************************************************************** +static uint32_t +AudioGetInterface(tUSBHostAudioInstance *psAudioDevice, uint16_t ui16Format, + uint32_t ui32SampleRate, uint32_t ui32Bytes, + uint32_t ui32Channels, uint32_t ui32Flags) +{ + tDescriptorHeader *psHeader; + tInterfaceDescriptor *psInterface; + tEndpointDescriptor *pINEndpoint, *pOUTEndpoint; + tACHeader *pACHeader; + tACGeneral *pGeneral; + tASFormat *pFormat; + tEndpointDescriptor *pEndpoint; + uint8_t *pui8Value; + uint32_t ui32Value; + int32_t i32Bytes, i32Idx; + + // + // Initialize the Interface pointer to null. + // + psInterface = 0; + pINEndpoint = 0; + pOUTEndpoint = 0; + + // + // Start at the top of the configuration descriptor. + // + psHeader = (tDescriptorHeader *)psAudioDevice->psDevice->psConfigDescriptor; + + i32Bytes = psAudioDevice->psDevice->psConfigDescriptor->wTotalLength; + + while(i32Bytes > 0) + { + if(psHeader->bDescriptorType == USB_DTYPE_INTERFACE) + { + // + // If a new interface was found and the last one satisfied all + // requirements then a valid interface was found so break out. + // + if(psInterface) + { + break; + } + + // + // Get the new interface pointer. + // + psInterface = (tInterfaceDescriptor *)psHeader; + + // + // Reset the endpoints on finding a new interface descriptor. + // + pINEndpoint = 0; + pOUTEndpoint = 0; + + // + // If this is not a valid audio streaming interface then reset + // the interface pointer to null. + // + if((psInterface->bNumEndpoints == 0) || + (psInterface->bInterfaceClass != USB_CLASS_AUDIO) || + (psInterface->bInterfaceSubClass != USB_ASC_AUDIO_STREAMING)) + { + psInterface = 0; + } + } + if((psInterface) && + (psHeader->bDescriptorType == USB_DTYPE_CS_INTERFACE)) + { + pACHeader = (tACHeader *)psHeader; + + // + // If this is a General descriptor the check if the format matches. + // + if(pACHeader->bDescriptorSubtype == USB_AS_GENERAL) + { + // + // Just save the pointer to the format descriptor. + // + pGeneral = (tACGeneral *)psHeader; + + // + // If this interface has the wrong format then set it to null + // so that the rest of this interface is ignored. + // + if(pGeneral->wFormatTag != ui16Format) + { + psInterface = 0; + } + } + else if(pACHeader->bDescriptorSubtype == USB_AS_FORMAT_TYPE) + { + pFormat = (tASFormat *)psHeader; + + // + // If the number of bytes per sample and number of channels do + // not match then reset the interface pointer so that the rest + // of this interface is ignored. + // + if((pFormat->bNrChannels != ui32Channels) || + (pFormat->bSubFrameSize != ui32Bytes)) + { + psInterface = 0; + } + else + { + pui8Value = &pFormat->tSamFreq; + + // + // Attempt to find the sample rate in the sample rate + // table for this interface. + // + for(i32Idx = 0; i32Idx < pFormat->bSamFreqType; i32Idx++) + { + ui32Value = (*((uint32_t *)&pui8Value[i32Idx * 3]) & + 0xffffff); + + if(ui32Value == ui32SampleRate) + { + break; + } + } + + // + // If the sample rate was not found then set the interface + // pointer to null so that the rest of this interface is + // ignored. + // + if(i32Idx == pFormat->bSamFreqType) + { + psInterface = 0; + } + } + } + } + else if((psInterface) && + (psHeader->bDescriptorType == USB_DTYPE_ENDPOINT)) + { + pEndpoint = (tEndpointDescriptor *)psHeader; + + // + // See what direction is being requested. + // + if(ui32Flags & USBH_AUDIO_FORMAT_IN) + { + // + // If this is an input endpoint and is just a feed back input + // then ignore it. + // + if(pEndpoint->bEndpointAddress & USB_EP_DESC_IN) + { + if((pEndpoint->bmAttributes & USB_EP_ATTR_USAGE_M) + == USB_EP_ATTR_USAGE_FEEDBACK) + { + psInterface = 0; + } + else + { + // + // Save this endpoint as a possible valid endpoint + // + pINEndpoint = pEndpoint; + } + } + } + else + { + // + // If this is an output endpoint and is just a feed back input + // then ignore it. + // + if((pEndpoint->bEndpointAddress & USB_EP_DESC_IN) == 0) + { + if((pEndpoint->bmAttributes & USB_EP_ATTR_USAGE_M) + == USB_EP_ATTR_USAGE_FEEDBACK) + { + psInterface = 0; + } + else + { + // + // Save this endpoint as a possible valid endpoint; + // + pOUTEndpoint = pEndpoint; + } + } + } + } + + // + // Decrease the bytes remaining by the size of this descriptor. + // + i32Bytes -= psHeader->bLength; + + // + // Move the pointer to the next header. + // + psHeader = (tDescriptorHeader*)((uint32_t)psHeader + + psHeader->bLength); + } + + // + // If there is still a valid interface then return the values. + // + if(psInterface) + { + // + // Check a valid IN endpoint descriptor. + // + if(pINEndpoint) + { + // + // Save the endpoint address. + // + g_sAudioDevice.ui8IsochInAddress = pINEndpoint->bEndpointAddress & + USB_EP_DESC_NUM_M; + + // + // If there is no current pipe then just allocate a new one with + // the settings for this interface. + // + if(g_sAudioDevice.ui32IsochInPipe == 0) + { + // + // Allocate the USB Pipe for this Isochronous IN end point. + // + g_sAudioDevice.ui32IsochInPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_IN_DMA, + g_sAudioDevice.psDevice, + pINEndpoint->wMaxPacketSize, + PipeCallbackIN); + } + else if(g_sAudioDevice.ui16PipeSizeIn < pINEndpoint->wMaxPacketSize) + { + // + // Free the old endpoint and allocate a new one. + // + USBHCDPipeFree(g_sAudioDevice.ui32IsochInPipe); + + // + // Allocate the USB Pipe for this Isochronous IN end point. + // + g_sAudioDevice.ui32IsochInPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_IN_DMA, + g_sAudioDevice.psDevice, + pINEndpoint->wMaxPacketSize, + PipeCallbackIN); + + // + // Save the new size of the maximum packet size for this + // USB pipe. + // + g_sAudioDevice.ui16PipeSizeIn = pINEndpoint->wMaxPacketSize; + } + + // + // Configure the USB pipe as a Isochronous IN end point. + // + USBHCDPipeConfig(g_sAudioDevice.ui32IsochInPipe, + pINEndpoint->wMaxPacketSize, + 0, + g_sAudioDevice.ui8IsochInAddress); + } + + // + // Check a valid OUT endpoint descriptor. + // + if(pOUTEndpoint) + { + // + // Save the endpoint address. + // + g_sAudioDevice.ui8IsochOutAddress = + pOUTEndpoint->bEndpointAddress & USB_EP_DESC_NUM_M; + + // + // If there is no current pipe then just allocate a new one with + // the settings for this interface. + // + if(g_sAudioDevice.ui32IsochOutPipe == 0) + { + // + // Allocate the USB Pipe for this Isochronous OUT end point. + // + g_sAudioDevice.ui32IsochOutPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_OUT_DMA, + g_sAudioDevice.psDevice, + pOUTEndpoint->wMaxPacketSize, + PipeCallbackOUT); + } + else if(g_sAudioDevice.ui16PipeSizeOut < + pOUTEndpoint->wMaxPacketSize) + { + // + // Free the old endpoint and allocate a new one. + // + USBHCDPipeFree(g_sAudioDevice.ui32IsochOutPipe); + + // + // Allocate the USB Pipe for this Isochronous OUT end point. + // + g_sAudioDevice.ui32IsochOutPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_OUT_DMA, + g_sAudioDevice.psDevice, + pOUTEndpoint->wMaxPacketSize, + PipeCallbackOUT); + + // + // Save the new size of the maximum packet size for this + // USB pipe. + // + g_sAudioDevice.ui16PipeSizeOut = pOUTEndpoint->wMaxPacketSize; + } + + // + // Configure the USB pipe as a Isochronous OUT end point. + // + USBHCDPipeConfig(g_sAudioDevice.ui32IsochOutPipe, + pOUTEndpoint->wMaxPacketSize, 0, + g_sAudioDevice.ui8IsochOutAddress); + } + + return(psInterface->bInterfaceNumber | + (psInterface->bAlternateSetting << INTERFACE_ALTSETTING_S)); + } + return(INVALID_INTERFACE); +} + +//***************************************************************************** +// +// This function is used to open an instance of the USB host audio driver. +// +// \param psDevice is a pointer to the device information structure. +// +// This function attempts to open an instance of the USB host audio driver +// based on the information contained in the psDevice structure. This call +// fails if there are not sufficient resources to open the device. The +// function returns a value that should be passed back into USBHostAudioClose() +// when the driver is no longer needed. +// +// \return The function returns a pointer to a USB host audio driver +// instance. +// +//***************************************************************************** +static void * +USBAudioOpen(tUSBHostDevice *psDevice) +{ + uint32_t ui32Temp; + tConfigDescriptor *psConfigDesc; + + // + // Don't allow the device to be opened without closing first. + // + if(g_sAudioDevice.psDevice) + { + return(0); + } + + // + // Save the Host device pointer. + // + g_sAudioDevice.psDevice = psDevice; + + // + // Save a shorter name for the configuration descriptor. + // + psConfigDesc = psDevice->psConfigDescriptor; + + // + // Find the input terminal. + // + g_sAudioDevice.psInTerminal = + (tACInputTerminal *)AudioTerminalGet(psConfigDesc, + USB_AI_INPUT_TERMINAL, + USB_TTYPE_STREAMING); + + // + // Find the output terminal. + // + g_sAudioDevice.psOutTerminal = + (tACOutputTerminal *)AudioTerminalGet(psConfigDesc, + USB_AI_OUTPUT_TERMINAL, + USB_TTYPE_STREAMING); + + // + // Find the feature unit. + g_sAudioDevice.psFeatureUnit = + (tACFeatureUnit *)AudioTerminalGet(psConfigDesc, + USB_AI_FEATURE_UNIT, 0); + + // + // Need some kind of terminal to send or receive audio from. + // + if((g_sAudioDevice.psOutTerminal == 0) && + (g_sAudioDevice.psInTerminal == 0)) + { + return(0); + } + + // + // Find the Audio control interface. + // + ui32Temp = AudioControlGet(psConfigDesc); + + if(ui32Temp == INVALID_INTERFACE) + { + return(0); + } + + // + // Save the control interface index and increment the number + // of interfaces that have been found. + // + g_sAudioDevice.ui8IControl = (uint8_t)ui32Temp; + + // + // If the call back exists, call it with an Open event. + // + if(g_sAudioDevice.pfnCallback != 0) + { + g_sAudioDevice.pfnCallback(&g_sAudioDevice, + USBH_AUDIO_EVENT_OPEN, 0, 0); + } + + // + // If a feature unit was found, save the ID + // + if(g_sAudioDevice.psFeatureUnit != 0) + { + g_sAudioDevice.ui8VolumeID = g_sAudioDevice.psFeatureUnit->bUnitID; + } + + // + // Allocate the USB Pipe for this Isochronous IN end point. + // + g_sAudioDevice.ui32IsochInPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_IN_DMA, + g_sAudioDevice.psDevice, 256, PipeCallbackIN); + g_sAudioDevice.ui16PipeSizeIn = 256; + + // + // Allocate the USB Pipe for this Isochronous OUT end point. + // + g_sAudioDevice.ui32IsochOutPipe = + USBHCDPipeAllocSize(0, USBHCD_PIPE_ISOC_OUT_DMA, + g_sAudioDevice.psDevice, 256, PipeCallbackOUT); + g_sAudioDevice.ui16PipeSizeOut = 256; + + // + // Clear the flags. + // + g_sAudioDevice.ui32Flags = 0; + + // + // Return the only instance of this device. + // + return(&g_sAudioDevice); +} + +//***************************************************************************** +// +// This function is used to release an instance of the USB host audio driver. +// +// \param pvAudioDevice is an instance pointer that needs to be released. +// +// This function frees up any resources in use by the USB host audio +// driver instance that is passed in. The \e pvAudioDevice pointer should be a +// valid value that was returned from a call to USBHostAudioOpen(). +// +// \return None. +// +//***************************************************************************** +static void +USBAudioClose(void *pvAudioDevice) +{ + tUSBHostAudioInstance *psAudioDevice; + + psAudioDevice = (tUSBHostAudioInstance *)pvAudioDevice; + + // + // Do nothing if there is not a driver open. + // + if(psAudioDevice->psDevice == 0) + { + return; + } + + // + // Reset the device pointer. + // + psAudioDevice->psDevice = 0; + + // + // Free the Isochronous IN pipe. + // + if(psAudioDevice->ui32IsochInPipe != 0) + { + USBHCDPipeFree(psAudioDevice->ui32IsochInPipe); + } + + // + // Free the Isochronous OUT pipe. + // + if(psAudioDevice->ui32IsochOutPipe != 0) + { + USBHCDPipeFree(psAudioDevice->ui32IsochOutPipe); + } + + // + // If the call back exists then call it. + // + if(psAudioDevice->pfnCallback != 0) + { + psAudioDevice->pfnCallback(psAudioDevice, USBH_AUDIO_EVENT_CLOSE, 0, + 0); + } +} + +//***************************************************************************** +// +//! This function should be called before any devices are present to enable +//! the host audio class driver. +//! +//! \param ui32Index is the audio device to open (currently only 0 is +//! supported). +//! \param pfnCallback is the driver call back for host audio events. +//! +//! This function is called to open an instance of a host audio device and +//! should provide a valid callback function for host audio events in the +//! \e pfnCallback parameter. This function must be called before the USB +//! host code can successfully enumerate an audio device. +//! +//! \return This function returns the driver instance to use for the other +//! host audio functions. If there is no instance available at the time of +//! this call, this function returns zero. +// +//***************************************************************************** +tUSBHostAudioInstance * +USBHostAudioOpen(uint32_t ui32Index, tUSBHostAudioCallback pfnCallback) +{ + // + // Only one audio device is supported at this time and on one instance + // is supported so if there is already a call back then fail. + // + if((ui32Index != 0) || (g_sAudioDevice.pfnCallback)) + { + return(0); + } + + // + // Save the call back. + // + g_sAudioDevice.pfnCallback = pfnCallback; + + // + // Return the requested device instance. + // + return(&g_sAudioDevice); +} + +//***************************************************************************** +// +//! This function should be called to release an audio device instance. +//! +//! \param psAudioInstance is the device instance that is to be released. +//! +//! This function is called when a host audio device needs to be released. +//! This could be in preparation for shutdown or a switch to USB device mode, +//! for example. Following this call, the audio device is available and can +//! be opened again using a call to USBHostAudioOpen(). After calling this +//! function, the host audio driver will no longer provide any callbacks or +//! accept calls to other audio driver APIs. +//! +//! \return None. +// +//***************************************************************************** +void +USBHostAudioClose(tUSBHostAudioInstance *psAudioInstance) +{ + // + // Close the audio device. + // + USBAudioClose(psAudioInstance); + + // + // Clear the call back indicating that the device is now closed. + // + psAudioInstance->pfnCallback = 0; +} + +//***************************************************************************** +// +// This function is used to request settings from a given audio interface. +// +// \param psAudioDevice is the audio device instance to access. +// \param ui32Interface is the interface to access. +// \param ui32Channel is the channel number to access. +// \param ui32Request is the audio device request. +// +// This function is used to get volume control parameters from a given +// interface and on a given channel. The \e ui32Interface is the interface to +// make the request specified by \e ui32Channel and \e ui32Request. The +// \e ui32Request parameter must be one of the USB_AC_GET_* values. +// +// \return This function returns the requested value. +// +//***************************************************************************** +static uint32_t +VolumeSettingGet(tUSBHostAudioInstance *psAudioDevice, uint32_t ui32Interface, + uint32_t ui32Channel, uint32_t ui32Request) +{ + uint32_t ui32Value; + tUSBRequest sSetupPacket; + + ui32Value = 0; + + // + // This is a Class specific Interface IN request. + // + sSetupPacket.bmRequestType = USB_RTYPE_DIR_IN | USB_RTYPE_CLASS | + USB_RTYPE_INTERFACE; + + // + // Request a Device Descriptor. + // + sSetupPacket.bRequest = (ui32Request & 0xff); + + // + // Request for a string descriptor. + // + sSetupPacket.wValue = VOLUME_CONTROL | (ui32Channel & 0xff); + + // + // Set the language ID. + // + sSetupPacket.wIndex = (psAudioDevice->ui8VolumeID << 8) | + (ui32Interface & 0xff); + + // + // Only request the space available. + // + sSetupPacket.wLength = 2; + + // + // Put the setup packet in the buffer. + // + USBHCDControlTransfer(0, &sSetupPacket, psAudioDevice->psDevice, + (uint8_t *)&ui32Value, 4, + psAudioDevice->psDevice->sDeviceDescriptor.bMaxPacketSize0); + + return(ui32Value); +} + +//***************************************************************************** +// +//! This function is used to get the current volume setting for a given +//! audio device. +//! +//! \param psAudioInstance is an instance of the USB audio device. +//! \param ui32Interface is the interface number to use to query the current +//! volume setting. +//! \param ui32Channel is the 0 based channel number to query. +//! +//! The function is used to retrieve the current volume setting for an audio +//! device on the channel specified by \e ui32Channel. The \e ui32Interface is +//! ignored for now and should be set to 0 to access the default audio control +//! interface. The \e ui32Channel value starts with 0 which is the master +//! audio volume control interface. The remaining \e ui32Channel values +//! provide access to various other audio channels, with 1 and 2 being left and +//! right audio channels. +//! +//! \note On devices that do not support volume control interfaces, this +//! call returns 0, indicating a 0db setting. +//! +//! \return Returns the current volume setting for the requested interface. +// +//***************************************************************************** +uint32_t +USBHostAudioVolumeGet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32Interface, uint32_t ui32Channel) +{ + return(VolumeSettingGet(psAudioInstance, ui32Interface, ui32Channel, + USB_AC_GET_CUR)); +} + +//***************************************************************************** +// +//! This function is used to get the maximum volume setting for a given +//! audio device. +//! +//! \param psAudioInstance is an instance of the USB audio device. +//! \param ui32Interface is the interface number to use to query the maximum +//! volume control value. +//! \param ui32Channel is the 0 based channel number to query. +//! +//! The function is used to retrieve the maximum volume setting for an audio +//! device on the channel specified by \e ui32Channel. The \e ui32Interface is +//! ignored for now and should be set to 0 to access the default audio control +//! interface. The \e ui32Channel value starts with 0 which is the master +//! audio volume control interface. The remaining \e ui32Channel values +//! provide access to various other audio channels, with 1 and 2 being left and +//! right audio channels. +//! +//! \note On devices that do not support volume control interfaces, this +//! call returns 0, indicating a 0db setting. +//! +//! \return Returns the maximum volume setting for the requested interface. +// +//***************************************************************************** +uint32_t +USBHostAudioVolumeMaxGet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32Interface, uint32_t ui32Channel) +{ + return(VolumeSettingGet(psAudioInstance, ui32Interface, ui32Channel, + USB_AC_GET_MAX)); +} + +//***************************************************************************** +// +//! This function is used to get the minimum volume setting for a given +//! audio device. +//! +//! \param psAudioInstance is an instance of the USB audio device. +//! \param ui32Interface is the interface number to use to query the minimum +//! volume control value. +//! \param ui32Channel is the 0 based channel number to query. +//! +//! The function is used to retrieve the minimum volume setting for an audio +//! device on the channel specified by \e ui32Channel. The \e ui32Interface is +//! ignored for now and should be set to 0 to access the default audio control +//! interface. The \e ui32Channel value starts with 0 which is the master +//! audio volume control interface. The remaining \e ui32Channel values +//! provide access to various other audio channels, with 1 and 2 being left and +//! right audio channels. +//! +//! \note On devices that do not support volume control interfaces, this +//! call returns 0, indicating a 0db setting. +//! +//! \return Returns the minimum volume setting for the requested interface. +// +//***************************************************************************** +uint32_t +USBHostAudioVolumeMinGet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32Interface, uint32_t ui32Channel) +{ + return(VolumeSettingGet(psAudioInstance, ui32Interface, ui32Channel, + USB_AC_GET_MIN)); +} + +//***************************************************************************** +// +//! This function is used to get the volume control resolution for a given +//! audio device. +//! +//! \param psAudioInstance is an instance of the USB audio device. +//! \param ui32Interface is the interface number to use to query the resolution +//! for the volume control. +//! \param ui32Channel is the 0 based channel number to query. +//! +//! The function is used to retrieve the volume control resolution for an audio +//! device on the channel specified by \e ui32Channel. The \e ui32Interface is +//! ignored for now and should be set to 0 to access the default audio control +//! interface. The \e ui32Channel value starts with 0 which is the master +//! audio volume control interface. The remaining \e ui32Channel values +//! provide access to various other audio channels, with 1 and 2 being left and +//! right audio channels. +//! +//! \note On devices that do not support volume control interfaces, this +//! call returns 0, indicating a 0db setting. +//! +//! \return Returns the volume control resolution for the requested interface. +// +//***************************************************************************** +uint32_t +USBHostAudioVolumeResGet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32Interface, uint32_t ui32Channel) +{ + return(VolumeSettingGet(psAudioInstance, ui32Interface, ui32Channel, + USB_AC_GET_RES)); +} + +//***************************************************************************** +// +//! This function is used to set the current volume setting for a given +//! audio device. +//! +//! \param psAudioInstance is an instance of the USB audio device. +//! \param ui32Interface is the interface number to use to set the current +//! volume setting. +//! \param ui32Channel is the 0 based channel number to query. +//! \param ui32Value is the value to write to the USB audio device. +//! +//! The function is used to set the current volume setting for an audio +//! device on the channel specified by \e ui32Channel. The \e ui32Interface is +//! ignored for now and should be set to 0 to access the default audio control +//! interface. The \e ui32Channel value starts with 0 which is the master +//! audio volume control interface. The remaining \e ui32Channel values +//! provide access to various other audio channels, with 1 and 2 being left and +//! right audio channels. +//! +//! \note On devices that do not support volume control interfaces, this +//! call returns 0, indicating a 0db setting. +//! +//! \return None. +// +//***************************************************************************** +void +USBHostAudioVolumeSet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32Interface, uint32_t ui32Channel, + uint32_t ui32Value) +{ + tUSBRequest sSetupPacket; + + // + // This is a Class specific Interface OUT request. + // + sSetupPacket.bmRequestType = USB_RTYPE_DIR_OUT | USB_RTYPE_CLASS | + USB_RTYPE_INTERFACE; + + // + // Request is to set the current value. + // + sSetupPacket.bRequest = USB_AC_SET_CUR; + + // + // Request the volume control. + // + sSetupPacket.wValue = VOLUME_CONTROL | (ui32Channel & 0xff); + + // + // Set Volume control ID and interface to 0. + // + sSetupPacket.wIndex = psAudioInstance->ui8VolumeID << 8; + + // + // Only request the space available. + // + sSetupPacket.wLength = 2; + + // + // Put the setup packet in the buffer. + // + USBHCDControlTransfer(0, &sSetupPacket, psAudioInstance->psDevice, + (uint8_t *)&ui32Value, 2, + psAudioInstance->psDevice->sDeviceDescriptor.bMaxPacketSize0); +} + +//***************************************************************************** +// +//! This function is called to determine if an audio format is supported by the +//! connected USB Audio device. +//! +//! \param psAudioInstance is the device instance for this call. +//! \param ui32SampleRate is the sample rate of the audio stream. +//! \param ui32Bits is the number of bits per sample in the audio stream. +//! \param ui32Channels is the number of channels in the audio stream. +//! \param ui32Flags is a set of flags to determine what type of interface to +//! retrieve. +//! +//! This function is called when an application needs to determine which audio +//! formats are supported by a USB audio device that has been connected. The +//! \e psAudioInstance value that is used with this call is the value that was +//! returned from the USBHostAudioOpen() function. This call checks the +//! USB audio device to determine if it can support the values provided in the +//! \e ui32SampleRate, \e ui32Bits, and \e ui32Channels values. The +//! \e ui32Flags currently only supports either the \b USBH_AUDIO_FORMAT_IN or +//! \b USBH_AUDIO_FORMAT_OUT values that indicates if a request is for an +//! audio input and an audio output. If the format is supported this +//! function returns zero, and this function returns a non-zero value if the +//! format is not supported. This function does not set the current output or +//! input format. +//! +//! \return A value of zero indicates the supplied format is supported and +//! a non-zero value indicates that the format is not supported. +// +//***************************************************************************** +uint32_t +USBHostAudioFormatGet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32SampleRate, uint32_t ui32Bits, + uint32_t ui32Channels, uint32_t ui32Flags) +{ + // + // Look for the requested format. + // + if(AudioGetInterface(psAudioInstance, USB_ADF_PCM, ui32SampleRate, + ui32Bits >> 3, ui32Channels, ui32Flags) != + INVALID_INTERFACE) + { + return(0); + } + return(1); +} + +//***************************************************************************** +// +//! This function is called to set the current sample rate on an audio +//! interface. +//! +//! \param psAudioInstance specifies the device instance for this call. +//! \param ui32SampleRate is the sample rate in Hz. +//! \param ui32Bits is the number of bits per sample. +//! \param ui32Channels is then number of audio channels. +//! \param ui32Flags is a set of flags that determine the access type. +//! +//! This function is called when to set the current audio output or input +//! format for a USB audio device. The \e psAudioInstance value that is used +//! with this call is the value that was returned from the USBHostAudioOpen() +//! function. The application can use this call to insure that the audio +//! format is supported and set the format at the same time. If the +//! application is just checking for supported rates, then it should call the +//! USBHostAudioFormatGet(). +//! +//! \note This function must be called before attempting to send or receive +//! audio with the USBHostAudioPlay() or USBHostAudioRecord() functions. +//! +//! \return A non-zero value indicates the supplied format is not supported and +//! a zero value indicates that the format was supported and has been +//! configured. +// +//***************************************************************************** +uint32_t +USBHostAudioFormatSet(tUSBHostAudioInstance *psAudioInstance, + uint32_t ui32SampleRate, uint32_t ui32Bits, + uint32_t ui32Channels, uint32_t ui32Flags) +{ + uint32_t ui32Interface; + + // + // Look for the requested format. + // + ui32Interface = AudioGetInterface(psAudioInstance, USB_ADF_PCM, + ui32SampleRate, ui32Bits >> 3, + ui32Channels, ui32Flags); + + if(ui32Interface == INVALID_INTERFACE) + { + return(1); + } + + // + // Determine if this is an input or output request. + // + if(ui32Flags & USBH_AUDIO_FORMAT_IN) + { + // + // Get the active interface number and alternate setting for this + // format. + // + psAudioInstance->ui8InInterface = + (uint8_t)(ui32Interface & INTERFACE_NUM_M); + psAudioInstance->ui8InAltSetting = + (uint8_t)((ui32Interface & INTERFACE_ALTSETTING_M) >> + INTERFACE_ALTSETTING_S); + } + else + { + // + // Get the active interface number and alternate setting for this + // format. + // + psAudioInstance->ui8OutInterface = + (uint8_t)(ui32Interface & INTERFACE_NUM_M); + psAudioInstance->ui8OutAltSetting = + (uint8_t)((ui32Interface & INTERFACE_ALTSETTING_M) >> + INTERFACE_ALTSETTING_S); + } + return(0); +} + +//***************************************************************************** +// +//! This function is called to send an audio buffer to the USB audio device. +//! +//! \param psAudioInstance specifies the device instance for this call. +//! \param pvBuffer is the audio buffer to send. +//! \param ui32Size is the size of the buffer in bytes. +//! \param pfnCallback is a pointer to a callback function that is called +//! when the buffer can be used again. +//! +//! This function is called when an application needs to schedule a new buffer +//! for output to the USB audio device. Since this call schedules the transfer +//! and returns immediately, the application should provide a \e pfnCallback +//! function to be notified when the buffer can be used again by the +//! application. The \e pfnCallback function provided is called with the +//! \e pvBuffer parameter set to the \e pvBuffer provided by this call, the +//! \e ui32Param can be ignored and the \e ui32Event parameter is +//! \b USB_EVENT_TX_COMPLETE. +//! +//! \return This function returns the number of bytes that were scheduled +//! to be sent. If this function returns zero then there was no USB audio +//! device present or the request could not be satisfied at this time. +// +//***************************************************************************** +int32_t +USBHostAudioPlay(tUSBHostAudioInstance *psAudioInstance, void *pvBuffer, + uint32_t ui32Size, tUSBHostAudioCallback pfnCallback) +{ + uint32_t ui32Bytes; + + // + // Make sure that there is a device present. + // + if(psAudioInstance->psDevice == 0) + { + return(0); + } + + // + // If the audio output interface is not active then select the current + // active audio interface. + // + if(HWREGBITW(&psAudioInstance->ui32Flags, AUDIO_FLAG_OUT_ACTIVE) == 0) + { + // + // Indicate the active audio interface has been selected. + // + HWREGBITW(&psAudioInstance->ui32Flags, AUDIO_FLAG_OUT_ACTIVE) = 1; + + // + // Configure the USB audio device to use the selected audio interface. + // + USBHCDSetInterface(0, (uint32_t)psAudioInstance->psDevice, + psAudioInstance->ui8OutInterface, + psAudioInstance->ui8OutAltSetting); + } + + // + // Save the callback function and the buffer pointer. + // + psAudioInstance->pfnOutCallback = pfnCallback; + psAudioInstance->pvOutBuffer = (void *)pvBuffer; + + // + // Schedule the data to be written out to the FIFO. + // + ui32Bytes = USBHCDPipeSchedule(psAudioInstance->ui32IsochOutPipe, pvBuffer, + ui32Size); + + // + // Return the number of bytes scheduled to be sent. + // + return(ui32Bytes); +} + +//***************************************************************************** +// +//! This function is called to provide an audio buffer to the USB audio device +//! for audio input. +//! +//! \param psAudioInstance specifies the device instance for this call. +//! \param pvBuffer is the audio buffer to send. +//! \param ui32Size is the size of the buffer in bytes. +//! \param pfnCallback is a pointer to a callback function that is called +//! when the buffer has been filled. +//! +//! This function is called when an application needs to schedule a new buffer +//! for input from the USB audio device. Since this call schedules the +//! transfer and returns immediately, the application should provide a +//! \e pfnCallback function to be notified when the buffer has been filled with +//! audio data. When the \e pfnCallback function is called, the \e pvBuffer +//! parameter is set to \e pvBuffer provided in this call, the \e ui32Param is +//! the number of valid bytes in the pvBuffer and the \e ui32Event is set to +//! \b USB_EVENT_RX_AVAILABLE. +//! +//! \return This function returns the number of bytes that were scheduled +//! to be sent. If this function returns zero then there was no USB audio +//! device present or the device does not support audio input. +// +//***************************************************************************** +int32_t +USBHostAudioRecord(tUSBHostAudioInstance *psAudioInstance, void *pvBuffer, + uint32_t ui32Size, tUSBHostAudioCallback pfnCallback) +{ + uint32_t ui32Bytes; + + // + // Make sure that there is a device present. + // + if(psAudioInstance->psDevice == 0) + { + return(0); + } + + // + // If the audio input interface is not active then select the current + // active audio interface. + // + if(HWREGBITW(&psAudioInstance->ui32Flags, AUDIO_FLAG_IN_ACTIVE) == 0) + { + // + // Indicate the active audio interface has been selected. + // + HWREGBITW(&psAudioInstance->ui32Flags, AUDIO_FLAG_IN_ACTIVE) = 1; + + // + // Configure the USB audio device to use the selected audio interface. + // + USBHCDSetInterface(0, (uint32_t)psAudioInstance->psDevice, + psAudioInstance->ui8InInterface, + psAudioInstance->ui8InAltSetting); + } + + // + // Save the callback function and the buffer pointer. + // + psAudioInstance->pfnInCallback = pfnCallback; + psAudioInstance->pvInBuffer = (void *)pvBuffer; + + // + // Schedule the data to be read from the FIFO. + // + ui32Bytes = USBHCDPipeSchedule(psAudioInstance->ui32IsochInPipe, pvBuffer, + ui32Size); + + // + // Return the number of bytes scheduled to be sent. + // + return(ui32Bytes); +} + +//***************************************************************************** +// +//! This function forwards an LPM request for a device to enter L1 sleep state. +//! +//! \param psAudioInstance is the audio device instance that was returned +//! from the call to USBHostAudioOpen(). +//! +//! This function forwards a request from an application to the audio device +//! class to request that a device enter the LPM L1 sleep state. The +//! caller must check the return value to see if the request can be +//! attempted at this time. If another LPM transaction is busy on this or +//! another device, then this function returns \b USBHCD_LPM_PENDING. If +//! the LPM request was scheduled to be sent the function returns +//! \b USBHCD_LPM_AVAIL. The caller should check the USBHCDLPMStatus() +//! function to determine if the request completed successfully or if there +//! was an error. +//! +//! \return This function returns the following values: +//! - \b USBHCD_LPM_AVAIL - The transition to L1 state is scheduled to be sent. +//! - \b USBHCD_LPM_PENDING - There is already an LPM request pending. +// +//***************************************************************************** +uint32_t +USBHostAudioLPMSleep(tUSBHostAudioInstance *psAudioInstance) +{ + // + // Call the host controller function to send the sleep command. + // + return(USBHCDLPMSleep(psAudioInstance->psDevice)); +} + +//***************************************************************************** +// +//! This function returns the current status of an LPM request. +//! +//! \param psAudioInstance is the audio device instance that was returned +//! from the call to USBHostAudioOpen(). +//! +//! This function returns the current status of LPM requests for a given +//! device. This is called to determine if a previous request completed +//! successfully or if there was an error. +//! +//! \return This function returns the following values: +//! - \b USBHCD_LPM_AVAIL - There are no pending LPM requests on this specific +//! device or the last request completed successfully. +//! - \b USBHCD_LPM_ERROR - The last LPM request for this device did not +//! complete successfully. +//! - \b USBHCD_LPM_PENDING - The last LPM request has not completed. +// +//***************************************************************************** +uint32_t +USBHostAudioLPMStatus(tUSBHostAudioInstance *psAudioInstance) +{ + // + // Call the host controller function to get the current LPM status. + // + return(USBHCDLPMStatus(psAudioInstance->psDevice)); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** + -- cgit v1.3.1