From 044ad7c3987460ede48ff27afd6bdb0ca05a0432 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Mon, 4 Jul 2011 20:52:54 +0200 Subject: import at91lib from at91lib_20100901_softpack_1_9_v_1_0_svn_v15011 it's sad to see that atmel doesn't publish their svn repo or has a centralized location or even puts proper version/release info into the library itself --- usb/device/audio-looprec/AUDDLoopRecDriver.c | 493 +++++++++++++++++++++++++++ 1 file changed, 493 insertions(+) create mode 100644 usb/device/audio-looprec/AUDDLoopRecDriver.c (limited to 'usb/device/audio-looprec/AUDDLoopRecDriver.c') diff --git a/usb/device/audio-looprec/AUDDLoopRecDriver.c b/usb/device/audio-looprec/AUDDLoopRecDriver.c new file mode 100644 index 0000000..45534b5 --- /dev/null +++ b/usb/device/audio-looprec/AUDDLoopRecDriver.c @@ -0,0 +1,493 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "AUDDLoopRecDriver.h" +#include "AUDDLoopRecDriverDescriptors.h" +#include "AUDDLoopRecChannel.h" +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// Internal types +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Audio LoopRec driver internal state. +//------------------------------------------------------------------------------ +typedef struct { + + /// USB device driver instance. + USBDDriver usbdDriver; + #if defined(CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) + /// Multi-Buffer-Transfer List + USBDTransferBuffer * pMbl; + /// List Size + unsigned short listSize; + /// Current buffer + unsigned short currBuffer; + #endif + /// Callback for ISO IN + AUDDFrameTransferCallback fWrCallback; + /// List of AUDDLoopRecChannel instances for playback. + AUDDLoopRecChannel channels[AUDDLoopRecDriver_NUMCHANNELS+1]; + /// List of AUDDLoopRecChannel instances for record. + AUDDLoopRecChannel recChannels[1]; + +} AUDDLoopRecDriver; + +//------------------------------------------------------------------------------ +// Internal variables +//------------------------------------------------------------------------------ + +/// Global USB audio LoopRec driver instance. +static AUDDLoopRecDriver auddLoopRecDriver; +/// Array for storing the current setting of each interface. +static unsigned char auddLoopRecDriverInterfaces[3]; +/// Intermediate storage variable for the mute status of a channel. +static unsigned char muted; + +//------------------------------------------------------------------------------ +// Internal functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Callback triggered after the new mute status of a channel has been read +/// by AUDDLoopRecDriver_SetFeatureCurrentValue. Changes the mute status +/// of the given channel accordingly. +/// \param channel Number of the channel whose mute status has changed. +//------------------------------------------------------------------------------ +static void AUDDLoopRecDriver_MuteReceived(unsigned int channel) +{ + AUDDLoopRecChannel * pChannel; + if ((unsigned char)(channel >> 8) == + AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL_REC) { + pChannel = &auddLoopRecDriver.recChannels[0]; + } + else { + pChannel = &auddLoopRecDriver.channels[channel]; + } + if (muted) { + + AUDDLoopRecChannel_Mute(pChannel); + } + else { + + AUDDLoopRecChannel_Unmute(pChannel); + } + + USBD_Write(0, 0, 0, 0, 0); +} + +//------------------------------------------------------------------------------ +/// Sets the current value of a particular Feature control of a channel. +/// \param entity Entity number. +/// \param channel Channel number. +/// \param control Control selector value (see TODO). +/// \param length Length of the data containing the new value. +//------------------------------------------------------------------------------ +static void AUDDLoopRecDriver_SetFeatureCurrentValue(unsigned char entity, + unsigned char channel, + unsigned char control, + unsigned short length) +{ + TRACE_INFO_WP("sFeature "); + TRACE_DEBUG("\b(E%d, CS%d, CN%d, L%d) ", + entity, control, channel, length); + + // Check the the requested control is supported + // Control/channel combination not supported + if ((control != AUDFeatureUnitRequest_MUTE) || + (length != 1) || + ((entity == AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL) && + (channel > AUDDLoopRecDriver_NUMCHANNELS)) || + ((entity == AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL_REC) && + (channel > 0))) { + + USBD_Stall(0); + } + // Mute control on master channel / speakerphone + else { + + unsigned int argument = channel | (entity << 8); + USBD_Read(0, // Endpoint #0 + &muted, + sizeof(muted), + (TransferCallback) AUDDLoopRecDriver_MuteReceived, + (void *) argument); + } +} + +//------------------------------------------------------------------------------ +/// Sends the current value of a particular channel Feature to the USB host. +/// \param entity Entity number. +/// \param channel Channel number. +/// \param control Control selector value (see TODO). +/// \param length Length of the data to return. +//------------------------------------------------------------------------------ +static void AUDDLoopRecDriver_GetFeatureCurrentValue(unsigned char entity, + unsigned char channel, + unsigned char control, + unsigned char length) +{ + TRACE_INFO_WP("gFeature "); + TRACE_DEBUG("\b(CS%d, CN%d, L%d) ", control, channel, length); + + // Check that the requested control is supported + // Master channel mute control + if ((control == AUDFeatureUnitRequest_MUTE) + && (channel < (AUDDLoopRecDriver_NUMCHANNELS+1)) + && (length == 1)) { + + if (entity == AUDDLoopRecDriverDescriptors_FEATUREUNIT) { + muted = auddLoopRecDriver.channels[channel].muted; + } + else { + muted = auddLoopRecDriver.recChannels[0].muted; + } + USBD_Write(0, &muted, sizeof(muted), 0, 0); + } + else { + + USBD_Stall(0); + } +} + +//------------------------------------------------------------------------------ +/// Callback used by MBL transfer functions (USBD_Read & USBD_Write) to notify +/// that a transaction is complete. +/// \param pArg Pointer to callback arguments. +/// \param status USBD status. +/// \param nbBuffer Number of buffer that is processed. +//------------------------------------------------------------------------------ +void AUDDLoopRecDriver_MblWriteCallback(void *pArg, + unsigned char status, + unsigned int nbBuffer) +{ + char rc = 1; + if (auddLoopRecDriver.fWrCallback) { + + rc = auddLoopRecDriver.fWrCallback(pArg, status); + } + if (rc && status == USBD_STATUS_PARTIAL_DONE) { + + while(nbBuffer --) { + USBD_MblReuse(AUDDLoopRecDriverDescriptors_DATAIN, + 0, 0); + } + } +} + +//------------------------------------------------------------------------------ +// Callback re-implementation +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Triggered when the USB host emits a new SETUP request. The request is +/// forward to . +/// \param request Pointer to a USBGenericRequest instance. +//------------------------------------------------------------------------------ +#if !defined(NOAUTOCALLBACK) +void USBDCallbacks_RequestReceived(const USBGenericRequest *request) +{ + AUDDLoopRecDriver_RequestHandler(request); +} +#endif + +//------------------------------------------------------------------------------ +/// Invoked whenever the active setting of an interface is changed by the +/// host. Changes the status of the third LED accordingly. +/// \param interface Interface number. +/// \param setting Newly active setting. +//------------------------------------------------------------------------------ +void USBDDriverCallbacks_InterfaceSettingChanged(unsigned char interface, + unsigned char setting) +{ + if ((interface == AUDDLoopRecDriverDescriptors_STREAMING) + && (setting == 0)) { + + LED_Clear(USBD_LEDOTHER); + } + else { + + LED_Set(USBD_LEDOTHER); + } +} + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes an USB audio LoopRec device driver, as well as the underlying +/// USB controller. +//------------------------------------------------------------------------------ +void AUDDLoopRecDriver_Initialize() +{ + // Reset callback function + auddLoopRecDriver.fWrCallback = 0; + + // Initialize LoopRec channels + AUDDLoopRecChannel_Initialize(&(auddLoopRecDriver.channels[0]), + AUDDLoopRecDriver_MASTERCHANNEL, + 0); + AUDDLoopRecChannel_Initialize(&(auddLoopRecDriver.channels[1]), + AUDDLoopRecDriver_LEFTCHANNEL, + 0); + AUDDLoopRecChannel_Initialize(&(auddLoopRecDriver.channels[2]), + AUDDLoopRecDriver_RIGHTCHANNEL, + 0); + AUDDLoopRecChannel_Initialize(&(auddLoopRecDriver.recChannels[0]), + AUDDLoopRecDriver_RECCHANNEL, + 0); + + // Initialize the USB driver + USBDDriver_Initialize(&(auddLoopRecDriver.usbdDriver), + &auddLoopRecDriverDescriptors, + auddLoopRecDriverInterfaces); + USBD_Init(); + + // Initialize the third LED to indicate when the audio interface is active + LED_Configure(USBD_LEDOTHER); +} + +//------------------------------------------------------------------------------ +/// Handles audio-specific USB requests sent by the host, and forwards +/// standard ones to the USB device driver. +/// \param request Pointer to a USBGenericRequest instance. +//------------------------------------------------------------------------------ +void AUDDLoopRecDriver_RequestHandler(const USBGenericRequest *request) +{ + unsigned char entity; + unsigned char interface; + + TRACE_INFO_WP("NewReq "); + + // Check if this is a class request + if (USBGenericRequest_GetType(request) == USBGenericRequest_CLASS) { + + // Check if the request is supported + switch (USBGenericRequest_GetRequest(request)) { + + case AUDGenericRequest_SETCUR: + TRACE_INFO_WP( + "sCur(0x%04X) ", + USBGenericRequest_GetIndex(request)); + + // Check the target interface and entity + entity = AUDGenericRequest_GetEntity(request); + interface = AUDGenericRequest_GetInterface(request); + if (((entity == AUDDLoopRecDriverDescriptors_FEATUREUNIT) + ||(entity == AUDDLoopRecDriverDescriptors_FEATUREUNIT_REC)) + && (interface == AUDDLoopRecDriverDescriptors_CONTROL)) { + + AUDDLoopRecDriver_SetFeatureCurrentValue( + entity, + AUDFeatureUnitRequest_GetChannel(request), + AUDFeatureUnitRequest_GetControl(request), + USBGenericRequest_GetLength(request)); + } + else { + + TRACE_WARNING( + "AUDDLoopRecDriver_RequestHandler: Unsupported entity/interface combination (0x%04X)\n\r", + USBGenericRequest_GetIndex(request)); + USBD_Stall(0); + } + break; + + case AUDGenericRequest_GETCUR: + TRACE_INFO_WP( + "gCur(0x%04X) ", + USBGenericRequest_GetIndex(request)); + + // Check the target interface and entity + entity = AUDGenericRequest_GetEntity(request); + interface = AUDGenericRequest_GetInterface(request); + if (((entity == AUDDLoopRecDriverDescriptors_FEATUREUNIT) + ||(entity == AUDDLoopRecDriverDescriptors_FEATUREUNIT_REC)) + && (interface == AUDDLoopRecDriverDescriptors_CONTROL)) { + + AUDDLoopRecDriver_GetFeatureCurrentValue( + entity, + AUDFeatureUnitRequest_GetChannel(request), + AUDFeatureUnitRequest_GetControl(request), + USBGenericRequest_GetLength(request)); + } + else { + + TRACE_WARNING( + "AUDDLoopRecDriver_RequestHandler: Unsupported entity/interface combination (0x%04X)\n\r", + USBGenericRequest_GetIndex(request)); + USBD_Stall(0); + } + break; + + default: + + TRACE_WARNING( + "AUDDLoopRecDriver_RequestHandler: Unsupported request (%d)\n\r", + USBGenericRequest_GetRequest(request)); + USBD_Stall(0); + } + } + // Check if this is a standard request + else if (USBGenericRequest_GetType(request) == USBGenericRequest_STANDARD) { + + // Forward request to the standard handler + USBDDriver_RequestHandler(&(auddLoopRecDriver.usbdDriver), request); + } + // Unsupported request type + else { + + TRACE_WARNING( + "AUDDLoopRecDriver_RequestHandler: Unsupported request type (%d)\n\r", + USBGenericRequest_GetType(request)); + USBD_Stall(0); + } +} + +//------------------------------------------------------------------------------ +/// Reads incoming audio data sent by the USB host into the provided +/// buffer. When the transfer is complete, an optional callback function is +/// invoked. +/// \param buffer Pointer to the data storage buffer. +/// \param length Size of the buffer in bytes. +/// \param callback Optional callback function. +/// \param argument Optional argument to the callback function. +/// \return USBD_STATUS_SUCCESS if the transfer is started successfully; +/// otherwise an error code. +//------------------------------------------------------------------------------ +unsigned char AUDDLoopRecDriver_Read(void *buffer, + unsigned int length, + TransferCallback callback, + void *argument) +{ + return USBD_Read(AUDDLoopRecDriverDescriptors_DATAOUT, + buffer, + length, + callback, + argument); +} + +//------------------------------------------------------------------------------ +/// Initialize Frame List for sending audio data. +/// \param pMultiBufferList Pointer to the transfer list to initialize. +/// \param pBufferList Pointer list to the buffers used for frames sending. +/// \param listSize Number of frames. +/// \param frameSize Size in number of bytes of each frame. +//------------------------------------------------------------------------------ +void AUDDLoopRecDriver_SetupWriteMbl(void * pMultiBufferList, + unsigned char **pBufferList, + unsigned short listSize, + unsigned short frameSize) +{ + int i; + + SANITY_CHECK(pMultiBufferList && pBufferList && listSize && frameSize); + +#if (defined(CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS)) && defined(DMA) + // Initialize for circled DMA LLI + { + USBDDmaDescriptor * pLl = (USBDDmaDescriptor*)pMultiBufferList; + for (i = 0; ; i ++) { + pLl[i].pDataAddr = pBufferList[i]; + pLl[i].bufferLength = frameSize; + if (i < listSize - 1) { + pLl[i].pNxtDesc = &pLl[i + 1]; + } + else { + pLl[i].pNxtDesc = &pLl[0]; + break; + } + } + } +#else + // Initialize for circled multi-buffer-list + { + USBDTransferBuffer * pMbl = (USBDTransferBuffer*)pMultiBufferList; + for (i = 0; i < listSize; i ++) { + pMbl[i].pBuffer = pBufferList[i]; + pMbl[i].size = frameSize; + pMbl[i].transferred = 0; + pMbl[i].buffered = 0; + pMbl[i].remaining = frameSize; + } + } +#endif +} + +//------------------------------------------------------------------------------ +/// Start sending audio data, through MBL window. +/// When any frame is complete, an optional callback function is invoked, return +/// 1 of the callback cause the frame buffer re-used. +/// If callback not assigned, frames always reused. +/// \param buffer Pointer to the data storage buffer. +/// \param length Size of the buffer in bytes. +/// \param callback Optional callback function. +/// \param argument Optional argument to the callback function. +/// \return USBD_STATUS_SUCCESS if the transfer is started successfully; +/// otherwise an error code. +//------------------------------------------------------------------------------ +unsigned char AUDDLoopRecDriver_Write(void * pMbl, + unsigned short listSize, + AUDDFrameTransferCallback callback, + void * argument) +{ + auddLoopRecDriver.fWrCallback = callback; + return USBD_MblWrite(AUDDLoopRecDriverDescriptors_DATAIN, + pMbl, + listSize, + 1, + 0, + AUDDLoopRecDriver_MblWriteCallback, + argument); +} + +//----------------------------------------------------------------------------- +/// Starts a remote wake-up sequence if the host has explicitely enabled it +/// by sending the appropriate SET_FEATURE request. +//----------------------------------------------------------------------------- +void AUDDLoopRecDriver_RemoteWakeUp(void) +{ + // Remote wake-up has been enabled + if (USBDDriver_IsRemoteWakeUpEnabled(&auddLoopRecDriver.usbdDriver)) { + + USBD_RemoteWakeUp(); + } +} + + -- cgit v1.2.3