/* ----------------------------------------------------------------------------
 *         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.
 * ----------------------------------------------------------------------------
 */
/** \file */

/** \addtogroup usbd_aud_fun
 *@{
 */

/*------------------------------------------------------------------------------
 *         Headers
 *------------------------------------------------------------------------------*/
     
#include "USBLib_Trace.h"

#include "AUDDFunction.h"
#include "AUDDSpeakerPhone.h"

#include "AUDRequests.h"
     
#include "USBD.h"
#include "USBD_HAL.h"
#include "USBDDriver.h"

/*----------------------------------------------------------------------------
 *         Internal types
 *----------------------------------------------------------------------------*/

/**
 * \brief Audio speaker driver struct.
 */
typedef struct _AUDDFunction {
    /** Speaker & Phone function */
    AUDDSpeakerPhone drv;
    /** Stream instance for speaker */
    AUDDStream  speaker;
    /** Stream instance for microphone */
    AUDDStream  mic;
} AUDDFunction;

/*----------------------------------------------------------------------------
 *         Internal variables
 *----------------------------------------------------------------------------*/

/** Global USB audio function driver instance. */
static AUDDFunction auddFunction;

/*----------------------------------------------------------------------------
 *         Internal functions
 *----------------------------------------------------------------------------*/

/**
 * Callback triggerred after the mute or volume status of the channel has been
 * changed.
 * \param ec        Event code.
 * \param channel   Channel number.
 * \param pArg      Pointer to AUDDStream instance.
 */
static void AUDDFunction_EventCallback(uint32_t ec,
                                      uint8_t channel,
                                      AUDDStream *pArg)
{
    AUDDFunction *pAudf = &auddFunction;
    uint8_t mic = ((uint32_t)pArg == (uint32_t)(&pAudf->mic));
    if (ec == AUDD_EC_MuteChanged) {
        if (AUDDFunction_MuteChanged)
            AUDDFunction_MuteChanged(mic, channel, pArg->bmMute);
    }
    else if (ec == AUDD_EC_VolumeChanged) {
        /* Not supported now */
    }
}

/*---------------------------------------------------------------------------
 *         Exported functions
 *---------------------------------------------------------------------------*/

/**
 * Initializes an USB audio speaker device driver, as well as the underlying
 * USB controller.
 */
void AUDDFunction_Initialize(USBDDriver *pUsbd, uint8_t bInterface)
{
    AUDDFunction *pAudf = &auddFunction;
    AUDDSpeakerPhone *pDrv = &pAudf->drv;
    AUDDStream *pSpk = &pAudf->speaker;
    AUDDStream *pMic = &pAudf->mic;

    /* Stop warning */
    bInterface = bInterface;

    /* 0: Speaker */
    AUDDSpeakerPhone_InitializeStream(
        pSpk, AUDDFunction_MaxNumSpeakerChannels, 0,
        (AUDDStreamEventCallback)AUDDFunction_EventCallback,
        (void*)pSpk);
    /* 1: Mic */
    AUDDSpeakerPhone_InitializeStream(
        pMic, AUDDFunction_MaxNumMicrophoneChannels, 0,
        (AUDDStreamEventCallback)AUDDFunction_EventCallback,
        (void*)pMic);
    /* Audio Driver initialize */
    AUDDSpeakerPhone_Initialize(pDrv, pUsbd, pSpk, pMic);

}

/**
 * Configure function with expected descriptors and start functionality.
 * Usually invoked when device is configured.
 * \pDescriptors Pointer to the descriptors for function configure.
 * \wLength      Length of descriptors in number of bytes.
 */
void AUDDFunction_Configure(USBGenericDescriptor *pDescriptors,
                            uint16_t wLength)
{
    AUDDFunction *pAudf = &auddFunction;
    AUDDSpeakerPhone *pDrv = &pAudf->drv;
    AUDDSpeakerPhone_ParseInterfaces(pDrv, pDescriptors, wLength);
}

/**
 * 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 AUDDFunction_InterfaceSettingChangedHandler(uint8_t interface,
                                                 uint8_t setting)
{
    AUDDFunction *pAudf = &auddFunction;
    AUDDSpeakerPhone *pDrv = &pAudf->drv;
    if (setting == 0) AUDDSpeakerPhone_CloseStream(pDrv, interface);
    if (AUDDFunction_StreamSettingChanged) {
        uint8_t mic = (interface == pDrv->pMicrophone->bAsInterface);
        AUDDFunction_StreamSettingChanged(mic, setting);
    }
}

/**
 * Handles AUDIO-specific USB requests sent by the host
 * \param request Pointer to a USBGenericRequest instance.
 * \return USBRC_SUCCESS if request is handled.
 */
uint32_t AUDDFunction_RequestHandler(
    const USBGenericRequest *request)
{
    AUDDFunction *pAudf = &auddFunction;
    AUDDSpeakerPhone *pDrv = &pAudf->drv;
    return AUDDSpeakerPhone_RequestHandler(pDrv, request);
}

/**
 * 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.
 */
uint8_t AUDDFunction_Read(void *buffer,
                          uint32_t length,
                          TransferCallback callback,
                          void *argument)
{
    AUDDFunction *pAudf = &auddFunction;
    AUDDSpeakerPhone *pDrv = &pAudf->drv;
    return AUDDSpeakerPhone_Read(pDrv, buffer, length, callback, argument);
}

/**
 * Initialize Frame List for sending audio data.
 *
 * \param pListInit Pointer to the allocated list for audio write.
 * \param pDmaInit  Pointer to the allocated DMA descriptors for autio write
 *                  (if DMA supported).
 * \param listSize  Circular list size.
 * \param delaySize Start transfer after delaySize frames filled in.
 * \param callback  Optional callback function for transfer.
 * \param argument  Optional callback argument.
 * \return USBD_STATUS_SUCCESS if setup successfully; otherwise an error code.
 */
uint8_t AUDDFunction_SetupWrite(void * pListInit,
                                void * pDmaInit,
                                uint16_t listSize,
                                uint16_t delaySize,
                                TransferCallback callback,
                                void * argument)
{
    AUDDFunction *pAudf = &auddFunction;
    AUDDSpeakerPhone *pDrv = &pAudf->drv;
    return AUDDSpeakerPhone_SetupWrite(pDrv,
                                       pListInit, pDmaInit, listSize, delaySize,
                                       callback, argument);
}

/**
 *  Add frame buffer to audio sending list.
 *  \buffer Pointer to data frame to send.
 *  \length Frame size in bytes.
 *  \return USBD_STATUS_SUCCESS if the transfer is started successfully;
 *          otherwise an error code.
 */
uint8_t AUDDFunction_Write(void* buffer, uint16_t length)
{
    AUDDFunction *pAudf = &auddFunction;
    AUDDSpeakerPhone *pDrv = &pAudf->drv;
    return AUDDSpeakerPhone_Write(pDrv, buffer, length);
}

/**@}*/

