/*
 * Copyright 2015-2016 Freescale
 * Copyright 2016,2019-2020 NXP
 * This software is owned or controlled by NXP and may only be used strictly
 * in accordance with the applicable license terms.  By expressly accepting
 * such terms or by downloading, installing, activating and/or otherwise using
 * the software, you are agreeing that you have read, and that you agree to
 * comply with and are bound by, such license terms.  If you do not agree to
 * be bound by the applicable license terms, then you may not retain, install,
 * activate or otherwise use the software.
 */

#include <stdio.h>
#include <stdlib.h>
#include "usb_device_config.h"
#if ((defined(USB_DEVICE_CONFIG_AUDIO)) && (USB_DEVICE_CONFIG_AUDIO > 0U))

//#include "app_defines.h"
#include "board.h"
#include "usb.h"
#include "usb_spec.h"
#include "usb_device.h"
#include "usb_device_class.h"
#include "usb_device_audio.h"
#include "usb_device_ch9.h"
//#include "usb_app_device_descriptor.h"
#include "usb_device_descriptor.h"
#include "audio.h"
#include "audio_mixer.h"
#include "audio_rx.h"

#include "usb_internal.h"
#include "composite.h"
#include "usb_volume_conversion.h"
#include "audiocodec_volume_wm8904.h"
#include "usb_latency_debug.h"
//#include "usb_ctrl.h"
#include "audio_unified.h"
#include "audio.h"
#include "usb_feedback.h"

/*******************************************************************************
 * Definitions
 ******************************************************************************/

/*******************************************************************************
 * Prototypes
 ******************************************************************************/
 extern void USB_FeedbackCalculate(UsbFeedback_t *BufferSettings, uint32_t BufferSpaceInUse);

/*******************************************************************************
 * Variables
 ******************************************************************************/
USB_DMA_NONINIT_DATA_ALIGN(USB_DATA_ALIGN_SIZE)
#if USB_CHAT_SPEAKER_ENABLE
uint8_t audioChatPlayPacket[294];
#endif
#if USB_GAME_SPEAKER_ENABLE
uint8_t audioGamePlayPacket[(FS_ISO_OUT_ENDP_PACKET_SIZE(AUDIO_TX_FORMAT_MAX) + AUDIO_OUT_FORMAT_CHANNELS * AUDIO_TX_FORMAT_MAX)];
#endif
#if USB_RECORDER_ENABLE
USB_DMA_NONINIT_DATA_ALIGN(USB_DATA_ALIGN_SIZE)
uint8_t audioRecPacket[(FS_ISO_IN_ENDP_PACKET_SIZE + AUDIO_IN_FORMAT_CHANNELS * AUDIO_RX_FORMAT_SIZE)];
#endif


usb_device_composite_struct_t *g_deviceAudioComposite;

/*******************************************************************************
* Code
******************************************************************************/

#if USB_CTRL_SERVICE_RX_ENABLE
//static void RecLatencyDebug(usbConfig_t *conf, bool receiving)
//{
//    if (conf->enableLatencyDebug) {
//        USB_LatencyDebug_SendingAudioToHost(receiving);
//    }
//}

//static void RecordingPipeChangedCallback(bool pipeState)
//{
//    if (g_UsbServiceCtxt.userConfig.on_recording_pipe_change) {
//        g_UsbServiceCtxt.userConfig.on_recording_pipe_change(pipeState);
//    }
//}
#endif

#if USB_CTRL_SERVICE_TX_ENABLE
//static void SpeakerLatencyDebug(usbConfig_t *conf, bool receiving)
//{
//    if (conf->enableLatencyDebug) {
//        USB_LatencyDebug_ReceivingAudioFromHost(receiving);
//    }
//}
#endif

#if USB_CHAT_SPEAKER_ENABLE
static void ChatSpeakerPipeChangedCallback(bool pipeState)
{
//    if (g_UsbServiceCtxt.userConfig.on_chat_playback_pipe_change) {
//        g_UsbServiceCtxt.userConfig.on_chat_playback_pipe_change(pipeState);
//    }
}
#endif

#if USB_GAME_SPEAKER_ENABLE
static void GameSpeakerPipeChangedCallback(bool pipeState)
{
    if (g_UsbServiceCtxt.userConfig.on_game_playback_pipe_change) {
        g_UsbServiceCtxt.userConfig.on_game_playback_pipe_change(pipeState);
    }
}
#endif

typedef void (*usb_volume_changed_cb)(int16_t current, int16_t min, int16_t max);
typedef void (*usb_state_changed_cb)(bool new_state);

/*!
 * @brief Audio class specific request function.
 *
 * This function handles the Audio class specific requests.
 *
 * @param handle           The USB device handle.
 * @param event            The USB device event type.
 * @param param            The parameter of the device specific request.
 *
 * @return A USB error code or kStatus_USB_Success.
 */
usb_status_t USB_DeviceAudioRequest(class_handle_t handle, uint32_t event, void *param)
{
    usb_device_control_request_struct_t *request = (usb_device_control_request_struct_t *)param;
    usb_status_t error = kStatus_USB_Success;
    usb_audio_settings_t *audioSettings = NULL;
    usb_volume_changed_cb volumeChangedCb = NULL;
    usb_state_changed_cb muteChangedCb = NULL;

#if USB_RECORDER_ENABLE
    if (handle == g_deviceAudioComposite->audioUnified.audioRecorderHandle) {
        audioSettings = &g_deviceAudioComposite->audioUnified.recSettings;
        //volumeChangedCb = g_UsbServiceCtxt.userConfig.on_recording_volume_change;
        //muteChangedCb = g_UsbServiceCtxt.userConfig.on_recording_mute_change;
    } else
#endif
#if USB_CHAT_SPEAKER_ENABLE
    if (handle == g_deviceAudioComposite->audioUnified.audioChatSpeakerHandle) {
        audioSettings = &g_deviceAudioComposite->audioUnified.chatSpeakerSettings;
        //volumeChangedCb = g_UsbServiceCtxt.userConfig.on_chat_playback_volume_change;
        //muteChangedCb = g_UsbServiceCtxt.userConfig.on_chat_playback_mute_change;
    } else
#endif
#if USB_GAME_SPEAKER_ENABLE
    if (handle == g_deviceAudioComposite->audioUnified.audioGameSpeakerHandle) {
        audioSettings = &g_deviceAudioComposite->audioUnified.gameSpeakerSettings;
        volumeChangedCb = g_UsbServiceCtxt.userConfig.on_game_playback_volume_change;
        muteChangedCb = g_UsbServiceCtxt.userConfig.on_game_playback_mute_change;
    } else
#endif
    {
        PERR("USB Audio request on unregistered handle!");
        return kStatus_USB_InvalidRequest;
    }

    switch (event)
    {
        case USB_DEVICE_AUDIO_GET_CUR_MUTE_CONTROL:
            request->buffer = &audioSettings->curMute;
            request->length = sizeof(audioSettings->curMute);
            break;
        case USB_DEVICE_AUDIO_GET_CUR_VOLUME_CONTROL:
            request->buffer = audioSettings->curVolume;
            request->length = sizeof(audioSettings->curVolume);
            break;
        case USB_DEVICE_AUDIO_GET_CUR_BASS_CONTROL:
            request->buffer = &audioSettings->curBass;
            request->length = sizeof(audioSettings->curBass);
            break;
        case USB_DEVICE_AUDIO_GET_CUR_MID_CONTROL:
            request->buffer = &audioSettings->curMid;
            request->length = sizeof(audioSettings->curMid);
            break;
        case USB_DEVICE_AUDIO_GET_CUR_TREBLE_CONTROL:
            request->buffer = &audioSettings->curTreble;
            request->length = sizeof(audioSettings->curTreble);
            break;
        case USB_DEVICE_AUDIO_GET_CUR_AUTOMATIC_GAIN_CONTROL:
            request->buffer = &audioSettings->curAutomaticGain;
            request->length = sizeof(audioSettings->curAutomaticGain);
            break;
        case USB_DEVICE_AUDIO_GET_CUR_DELAY_CONTROL:
            request->buffer = audioSettings->curDelay;
            request->length = sizeof(audioSettings->curDelay);
            break;
        case USB_DEVICE_AUDIO_GET_CUR_SAMPLING_FREQ_CONTROL:
            request->buffer = audioSettings->curSamplingFrequency;
            request->length = sizeof(audioSettings->curSamplingFrequency);
            break;
        case USB_DEVICE_AUDIO_GET_MIN_VOLUME_CONTROL:
            request->buffer = audioSettings->minVolume;
            request->length = sizeof(audioSettings->minVolume);
            break;
        case USB_DEVICE_AUDIO_GET_MIN_BASS_CONTROL:
            request->buffer = &audioSettings->minBass;
            request->length = sizeof(audioSettings->minBass);
            break;
        case USB_DEVICE_AUDIO_GET_MIN_MID_CONTROL:
            request->buffer = &audioSettings->minMid;
            request->length = sizeof(audioSettings->minMid);
            break;
        case USB_DEVICE_AUDIO_GET_MIN_TREBLE_CONTROL:
            request->buffer = &audioSettings->minTreble;
            request->length = sizeof(audioSettings->minTreble);
            break;
        case USB_DEVICE_AUDIO_GET_MIN_DELAY_CONTROL:
            request->buffer = audioSettings->minDelay;
            request->length = sizeof(audioSettings->minDelay);
            break;
        case USB_DEVICE_AUDIO_GET_MIN_SAMPLING_FREQ_CONTROL:
            request->buffer = audioSettings->minSamplingFrequency;
            request->length = sizeof(audioSettings->minSamplingFrequency);
            break;
        case USB_DEVICE_AUDIO_GET_MAX_VOLUME_CONTROL:
            request->buffer = audioSettings->maxVolume;
            request->length = sizeof(audioSettings->maxVolume);
            break;
        case USB_DEVICE_AUDIO_GET_MAX_BASS_CONTROL:
            request->buffer = &audioSettings->maxBass;
            request->length = sizeof(audioSettings->maxBass);
            break;
        case USB_DEVICE_AUDIO_GET_MAX_MID_CONTROL:
            request->buffer = &audioSettings->maxMid;
            request->length = sizeof(audioSettings->maxMid);
            break;
        case USB_DEVICE_AUDIO_GET_MAX_TREBLE_CONTROL:
            request->buffer = &audioSettings->maxTreble;
            request->length = sizeof(audioSettings->maxTreble);
            break;
        case USB_DEVICE_AUDIO_GET_MAX_DELAY_CONTROL:
            request->buffer = audioSettings->maxDelay;
            request->length = sizeof(audioSettings->maxDelay);
            break;
        case USB_DEVICE_AUDIO_GET_MAX_SAMPLING_FREQ_CONTROL:
            request->buffer = audioSettings->maxSamplingFrequency;
            request->length = sizeof(audioSettings->maxSamplingFrequency);
            break;
        case USB_DEVICE_AUDIO_GET_RES_VOLUME_CONTROL:
            request->buffer = audioSettings->resVolume;
            request->length = sizeof(audioSettings->resVolume);
            break;
        case USB_DEVICE_AUDIO_GET_RES_BASS_CONTROL:
            request->buffer = &audioSettings->resBass;
            request->length = sizeof(audioSettings->resBass);
            break;
        case USB_DEVICE_AUDIO_GET_RES_MID_CONTROL:
            request->buffer = &audioSettings->resMid;
            request->length = sizeof(audioSettings->resMid);
            break;
        case USB_DEVICE_AUDIO_GET_RES_TREBLE_CONTROL:
            request->buffer = &audioSettings->resTreble;
            request->length = sizeof(audioSettings->resTreble);
            break;
        case USB_DEVICE_AUDIO_GET_RES_DELAY_CONTROL:
            request->buffer = audioSettings->resDelay;
            request->length = sizeof(audioSettings->resDelay);
            break;
        case USB_DEVICE_AUDIO_GET_RES_SAMPLING_FREQ_CONTROL:
            request->buffer = audioSettings->resSamplingFrequency;
            request->length = sizeof(audioSettings->resSamplingFrequency);
            break;

        case USB_DEVICE_AUDIO_SET_CUR_VOLUME_CONTROL:
            if (request->isSetup == 1U)
            {
                request->buffer = audioSettings->curVolume;
            }
            else
            {
                if (volumeChangedCb) {
#define ATOS(__a)  ((int16_t)(__a[0] | (__a[1] << 8)))
                    volumeChangedCb(ATOS(audioSettings->curVolume),
                                    ATOS(audioSettings->minVolume),
                                    ATOS(audioSettings->maxVolume));
                }
            }
            break;
        case USB_DEVICE_AUDIO_SET_CUR_MUTE_CONTROL:
            if (request->isSetup == 1U)
            {
                request->buffer = &audioSettings->curMute;
            }
            else
            {
                if (muteChangedCb) {
                    muteChangedCb(audioSettings->curMute);
                }
            }
            break;
        case USB_DEVICE_AUDIO_SET_CUR_BASS_CONTROL:
            if (request->isSetup == 1U)
            {
                request->buffer = &audioSettings->curBass;
            }
            break;
        case USB_DEVICE_AUDIO_SET_CUR_MID_CONTROL:
            if (request->isSetup == 1U)
            {
                request->buffer = &audioSettings->curMid;
            }
            break;
        case USB_DEVICE_AUDIO_SET_CUR_TREBLE_CONTROL:
            if (request->isSetup == 1U)
            {
                request->buffer = &audioSettings->curTreble;
            }
            break;
        case USB_DEVICE_AUDIO_SET_CUR_AUTOMATIC_GAIN_CONTROL:
            if (request->isSetup == 1U)
            {
                request->buffer = &audioSettings->curAutomaticGain;
            }
            break;
        case USB_DEVICE_AUDIO_SET_CUR_DELAY_CONTROL:
            if (request->isSetup == 1U)
            {
                request->buffer = audioSettings->curDelay;
            }
            break;
        case USB_DEVICE_AUDIO_SET_CUR_SAMPLING_FREQ_CONTROL:
            if (request->isSetup == 1U)
            {
                request->buffer = audioSettings->curSamplingFrequency;
            }
            break;
        case USB_DEVICE_AUDIO_SET_MIN_BASS_CONTROL:
            if (request->isSetup == 1U)
            {
                request->buffer = &audioSettings->minBass;
            }
            break;
        case USB_DEVICE_AUDIO_SET_MIN_MID_CONTROL:
            if (request->isSetup == 1U)
            {
                request->buffer = &audioSettings->minMid;
            }
            break;
        case USB_DEVICE_AUDIO_SET_MIN_TREBLE_CONTROL:
            if (request->isSetup == 1U)
            {
                request->buffer = &audioSettings->minTreble;
            }
            break;
        case USB_DEVICE_AUDIO_SET_MIN_DELAY_CONTROL:
            if (request->isSetup == 1U)
            {
                request->buffer = audioSettings->minDelay;
            }
            break;
        case USB_DEVICE_AUDIO_SET_MIN_SAMPLING_FREQ_CONTROL:
            if (request->isSetup == 1U)
            {
                request->buffer = audioSettings->minSamplingFrequency;
            }
            break;
        case USB_DEVICE_AUDIO_SET_MAX_BASS_CONTROL:
            if (request->isSetup == 1U)
            {
                request->buffer = &audioSettings->maxBass;
            }
            break;
        case USB_DEVICE_AUDIO_SET_MAX_MID_CONTROL:
            if (request->isSetup == 1U)
            {
                request->buffer = &audioSettings->maxMid;
            }
            break;
        case USB_DEVICE_AUDIO_SET_MAX_TREBLE_CONTROL:
            if (request->isSetup == 1U)
            {
                request->buffer = &audioSettings->maxTreble;
            }
            break;
        case USB_DEVICE_AUDIO_SET_MAX_DELAY_CONTROL:
            if (request->isSetup == 1U)
            {
                request->buffer = audioSettings->maxDelay;
            }
            break;
        case USB_DEVICE_AUDIO_SET_MAX_SAMPLING_FREQ_CONTROL:
            if (request->isSetup == 1U)
            {
                request->buffer = audioSettings->maxSamplingFrequency;
            }
            break;
        case USB_DEVICE_AUDIO_SET_RES_BASS_CONTROL:
            if (request->isSetup == 1U)
            {
                request->buffer = &audioSettings->resBass;
            }
            break;
        case USB_DEVICE_AUDIO_SET_RES_MID_CONTROL:
            if (request->isSetup == 1U)
            {
                request->buffer = &audioSettings->resMid;
            }
            break;
        case USB_DEVICE_AUDIO_SET_RES_TREBLE_CONTROL:
            if (request->isSetup == 1U)
            {
                request->buffer = &audioSettings->resTreble;
            }
            break;
        case USB_DEVICE_AUDIO_SET_RES_DELAY_CONTROL:
            if (request->isSetup == 1U)
            {
                request->buffer = audioSettings->resDelay;
            }
            break;
        case USB_DEVICE_AUDIO_SET_RES_SAMPLING_FREQ_CONTROL:
            if (request->isSetup == 1U)
            {
                request->buffer = audioSettings->resSamplingFrequency;
            }
            break;
        case USB_DEVICE_AUDIO_SET_MIN_VOLUME_CONTROL:
            /* Fallthrough */
        case USB_DEVICE_AUDIO_SET_MAX_VOLUME_CONTROL:
            /* Fallthrough */
        case USB_DEVICE_AUDIO_SET_RES_VOLUME_CONTROL:
            /*
             * NOTE:
             * Don't set min-,max-volume and resolution
             * We want to force our values since
             * these are in-line with the codec used on headset side
             */
            error = kStatus_USB_InvalidRequest;
            break;
        default:
            error = kStatus_USB_InvalidRequest;
            break;
    }
    return error;
}

#if USB_CTRL_SERVICE_RX_ENABLE
/* The USB_RecorderDataMatch() function increase/decrease the adjusted packet interval according to the reserved
 * ringbuffer size */
static uint32_t USB_RecorderDataMatch(uint32_t reservedspace)
{
    uint32_t epPacketSize = 0;
    if (reservedspace >= AUDIO_BUFFER_UPPER_LIMIT(audio_GetRxBufferSize()))
    {
        epPacketSize = FS_ISO_IN_ENDP_PACKET_SIZE + AUDIO_RX_FORMAT_SIZE * AUDIO_IN_FORMAT_CHANNELS;
    }
    else if ((reservedspace >=
              AUDIO_BUFFER_LOWER_LIMIT(audio_GetRxBufferSize())) &&
             (reservedspace <
              AUDIO_BUFFER_UPPER_LIMIT(audio_GetRxBufferSize())))
    {
        epPacketSize = FS_ISO_IN_ENDP_PACKET_SIZE;
    }
    else if (reservedspace <
             AUDIO_BUFFER_LOWER_LIMIT(audio_GetRxBufferSize()))
    {
        epPacketSize = FS_ISO_IN_ENDP_PACKET_SIZE - AUDIO_RX_FORMAT_SIZE * AUDIO_IN_FORMAT_CHANNELS;
    }
    else
    {
    }
    return epPacketSize;
}
#endif

/*!
 * @brief device Audio callback function.
 *
 * This function handle the Audio class specified event.
 * @param handle          The USB class  handle.
 * @param event           The USB device event type.
 * @param param           The parameter of the class specific event.
 * @return kStatus_USB_Success or error.
 */
usb_status_t USB_DeviceAudioCompositeCallback(class_handle_t handle, uint32_t event, void *param)
{
    usb_status_t error = kStatus_USB_Error;
    usb_device_endpoint_callback_message_struct_t *ep_cb_param;
    ep_cb_param = (usb_device_endpoint_callback_message_struct_t *)param;
    uint32_t fbPacketSize = (USB_SPEED_HIGH == g_composite.speed) ? HS_ISO_FEEDBACK_ENDP_PACKET_SIZE : FS_ISO_FEEDBACK_ENDP_PACKET_SIZE;


    usb_speaker_instance_t speakerInstance = 0;
    UsbFeedback_t *usbFeedback = NULL;
    uint8_t *audioPlayPacket = NULL;
    uint8_t streamEp = 0;
    uint8_t feedbackEp = 0;
    uint32_t bufferInUsed;

    if (handle == g_deviceAudioComposite->audioUnified.audioChatSpeakerHandle) {
        speakerInstance = kUSB_SPEAKER_INSTANCE_Chat;
        usbFeedback = &g_UsbChatSpeaker_FeedbackSettings;
        audioPlayPacket = (uint8_t *)audioChatPlayPacket;
        streamEp = USB_AUDIO_SPEAKER_STREAM_ENDPOINT;
        feedbackEp = USB_AUDIO_SPEAKER_FEEDBACK_ENDPOINT;
    }

#if USB_GAME_SPEAKER_ENABLE
    if (handle == g_deviceAudioComposite->audioUnified.audioGameSpeakerHandle) {
        speakerInstance = kUSB_SPEAKER_INSTANCE_Game;
        usbFeedback = &g_UsbGameSpeaker_FeedbackSettings;
        audioPlayPacket = (uint8_t *)audioGamePlayPacket;
        streamEp = USB_AUDIO_GAME_SPEAKER_STREAM_ENDPOINT;
        feedbackEp = USB_AUDIO_GAME_SPEAKER_FEEDBACK_ENDPOINT;
    }
#endif


    switch (event)
    {
        case kUSB_DeviceAudioEventStreamSendResponse:  /* a packet was sent to the host -> prepare the next one and send it */
            /* the sent data was either feedback-data from the speakers or a sample from the recorder */
            if ((g_deviceAudioComposite->audioUnified.attach) && (ep_cb_param->length != (USB_UNINITIALIZED_VAL_32)))
            {
                bool isSpeakerFeedback = (ep_cb_param->length == fbPacketSize);
#if USB_CTRL_SERVICE_TX_ENABLE
                if (isSpeakerFeedback)
                {
                    error = USB_DeviceAudioSend(handle, feedbackEp, (uint8_t *)&usbFeedback->Feedback, fbPacketSize);
                }
#endif
#if USB_CTRL_SERVICE_RX_ENABLE
                if (!isSpeakerFeedback)
                {
                    //RecLatencyDebug(&g_UsbServiceCtxt.userConfig, true);

                    uint32_t epPacketSize = FS_ISO_IN_ENDP_PACKET_SIZE;

                    OSA_SR_ALLOC();     
                    OSA_ENTER_CRITICAL();   
                    epPacketSize = USB_RecorderDataMatch(audio_RxAvailableSpace());
                    OSA_EXIT_CRITICAL();  

                    /* read the next packet from the recorder ringbuffer */
                    if (g_deviceAudioComposite->audioUnified.enableStreaming) {
                       // audio_ReadBuffer(audioRecPacket, epPacketSize / AUDIO_RX_FORMAT_SIZE);
                    } else {
                        /**
                         * PHCARE-342: send an empty audio recorder packet to the host. This avoids sending the last (non-empty)
                         * packet repeatedly to the host until the USB recorder pipe is closed.
                         */
                        memset(audioRecPacket, 0, sizeof(audioRecPacket) / sizeof(audioRecPacket[0]));
                    }

                    /* request to send that packet to the host */
                    error = USB_DeviceAudioSend(g_deviceAudioComposite->audioUnified.audioRecorderHandle,
                                                USB_AUDIO_RECORDER_STREAM_ENDPOINT,
                                                &audioRecPacket[0],
                                                epPacketSize);

                    //RecLatencyDebug(&g_UsbServiceCtxt.userConfig, false);
                }
#endif
            }
            break;

        case kUSB_DeviceAudioEventStreamRecvResponse:  /* a packet was received from the host -> process it and receive the next one */

            if ((g_deviceAudioComposite->audioUnified.attach) && (ep_cb_param->length != (USB_UNINITIALIZED_VAL_32)))
            {


                /* write the received packet to the speaker ringbuffer */
                if (g_deviceAudioComposite->audioUnified.enableStreaming) {
                                    //SpeakerLatencyDebug(&g_UsbServiceCtxt.userConfig, true);
                bufferInUsed = AUDIO_MIXER_GetBufferFilling(kUSB_SPEAKER_INSTANCE_Chat);
                USB_FeedbackCalculate(&g_UsbChatSpeaker_FeedbackSettings, bufferInUsed);

                    AUDIO_MIXER_WriteSamples(speakerInstance, audioPlayPacket,
                                             (ep_cb_param->length / AUDIO_OUT_FORMAT_SIZE),
                                             AUDIO_OUT_FORMAT_SIZE);
                }

                /* request to receive the next packet from the host */
                error = USB_DeviceAudioRecv(handle, streamEp, &audioPlayPacket[0],
                                            (FS_ISO_OUT_ENDP_PACKET_SIZE(AUDIO_OUT_FORMAT_SIZE) \
                                            + AUDIO_OUT_FORMAT_CHANNELS * AUDIO_OUT_FORMAT_SIZE));

                //SpeakerLatencyDebug(&g_UsbServiceCtxt.userConfig, false);
            }
            break;

        default:
            if (param && (event > 0xFF))
            {
                error = USB_DeviceAudioRequest(handle, event, param);
            }
            break;
    }

    return error;
}

/*!
 * @brief Audio set configuration function.
 *
 * This function sets configuration for msc class.
 *
 * @param handle The Audio class handle.
 * @param configure The Audio class configure index.
 *
 * @return A USB error code or kStatus_USB_Success.
 */
usb_status_t USB_DeviceAudioCompositeSetConfigure(class_handle_t handle, uint8_t configure)
{
    (void)handle;

    if (1 == configure)
    {
        g_deviceAudioComposite->audioUnified.attach = 1U;
    }
    return kStatus_USB_Success;
}

#if USB_CTRL_SERVICE_RX_ENABLE
usb_status_t USB_DeviceAudioRecorderSetInterface(class_handle_t handle, uint8_t interface, uint8_t alternateSetting)
{
    (void)handle;
    (void)interface;

    if (alternateSetting == 1U) {
        //RecLatencyDebug(&g_UsbServiceCtxt.userConfig, true);

        //USB_PipeCtrlPipeChanged(&g_UsbServiceCtxt.recPipeCtrl, true);

        memset(audioRecPacket, 0, sizeof(audioRecPacket)/sizeof(audioRecPacket[0]));

        USB_DeviceAudioSend(
            g_deviceAudioComposite->audioUnified.audioRecorderHandle,
            USB_AUDIO_RECORDER_STREAM_ENDPOINT,
            &audioRecPacket[0],
            FS_ISO_IN_ENDP_PACKET_SIZE);

        //RecLatencyDebug(&g_UsbServiceCtxt.userConfig, false);
    } else if (alternateSetting == 0U) {
        //audio_StopRx();
        /* TODO: Cancel pending requests (USB_DeviceCancel)? */
        //USB_PipeCtrlPipeChanged(&g_UsbServiceCtxt.recPipeCtrl, false);
    }

    return kStatus_USB_Success;
}
#endif


usb_status_t USB_DeviceAudioSpeakerSetInterface(class_handle_t handle, uint8_t interface, uint8_t alternateSetting)
{
    (void)interface;

    usb_speaker_instance_t speakerInstance = 0;
    //usb_pipe_ctrl_t *speakerPipeCtrl = NULL;
    UsbFeedback_t *usbFeedback = NULL;
    uint8_t *audioPlayPacket = NULL;
    uint8_t streamEp = 0;
    uint8_t feedbackEp = 0;

#if USB_CHAT_SPEAKER_ENABLE
    if (handle == g_deviceAudioComposite->audioUnified.audioChatSpeakerHandle) {
        speakerInstance = kUSB_SPEAKER_INSTANCE_Chat;
        //speakerPipeCtrl = &g_UsbServiceCtxt.chatSpeakerPipeCtrl;
        usbFeedback = &g_UsbChatSpeaker_FeedbackSettings;
        audioPlayPacket = (uint8_t *)audioChatPlayPacket;
        streamEp = USB_AUDIO_SPEAKER_STREAM_ENDPOINT;
        feedbackEp = USB_AUDIO_SPEAKER_FEEDBACK_ENDPOINT;
    }
#endif
#if USB_GAME_SPEAKER_ENABLE
    if (handle == g_deviceAudioComposite->audioUnified.audioGameSpeakerHandle) {
        speakerInstance = kUSB_SPEAKER_INSTANCE_Game;
        speakerPipeCtrl = &g_UsbServiceCtxt.gameSpeakerPipeCtrl;
        usbFeedback = &g_UsbGameSpeaker_FeedbackSettings;
        audioPlayPacket = (uint8_t *)audioGamePlayPacket;
        streamEp = USB_AUDIO_GAME_SPEAKER_STREAM_ENDPOINT;
        feedbackEp = USB_AUDIO_GAME_SPEAKER_FEEDBACK_ENDPOINT;
    }
#endif

    if (alternateSetting == 1U) /* enable interface -> start receiving audio and start sending feedback */
    {
        //SpeakerLatencyDebug(&g_UsbServiceCtxt.userConfig, true);

        //USB_PipeCtrlPipeChanged(speakerPipeCtrl, true);
        USB_FeedbackStart(usbFeedback, AUDIO_APP_FEEDBACK_UPDATE_US);

        /* request to receive (the first) audio packet from the host */
        memset(audioPlayPacket, 0, (FS_ISO_OUT_ENDP_PACKET_SIZE(AUDIO_OUT_FORMAT_SIZE) \
               + AUDIO_OUT_FORMAT_CHANNELS * AUDIO_OUT_FORMAT_SIZE));
        USB_DeviceAudioRecv(handle, streamEp, &audioPlayPacket[0], \
                            (FS_ISO_OUT_ENDP_PACKET_SIZE(AUDIO_OUT_FORMAT_SIZE) \
                            + AUDIO_OUT_FORMAT_CHANNELS * AUDIO_OUT_FORMAT_SIZE));

        /* request to send (the first) feedback packet to the host */
        USB_DeviceAudioSend(handle, feedbackEp, (uint8_t *)&usbFeedback->Feedback,
            (USB_SPEED_HIGH == g_composite.speed) ? HS_ISO_FEEDBACK_ENDP_PACKET_SIZE : FS_ISO_FEEDBACK_ENDP_PACKET_SIZE);

        //SpeakerLatencyDebug(&g_UsbServiceCtxt.userConfig, false);
    } else if (alternateSetting == 0U) { /* disable interface -> stop receiving audio and stop sending feedback */
        /* TODO: move cancel to separate function */
        if (!handle)
        {
            return kStatus_USB_InvalidHandle;
        }
        usb_device_audio_struct_t *audioHandle = (usb_device_audio_struct_t *)handle;

        USB_DeviceCancel(audioHandle->handle, feedbackEp | (USB_IN << USB_DESCRIPTOR_ENDPOINT_ADDRESS_DIRECTION_SHIFT));
        USB_DeviceCancel(audioHandle->handle, streamEp | (USB_OUT << USB_DESCRIPTOR_ENDPOINT_ADDRESS_DIRECTION_SHIFT));

        /* TODO: move stop to DPC? */
        AUDIO_MIXER_StopInterface(speakerInstance);

        USB_FeedbackStop(usbFeedback);
        USB_FeedbackReset(usbFeedback);
        //USB_PipeCtrlPipeChanged(speakerPipeCtrl, false);
    }

    return kStatus_USB_Success;
}


#if USB_CTRL_SERVICE_RX_ENABLE
static void InitRecSettings(usb_audio_settings_t *recSettings)
{
    /*
     * @anchor current_vol_max_setting
     *
     * Current volume we initially report to USB host is set to max value.
     * A PC host will overwrite this value after we report it with his actual current volume and set it if it changes (using the USB_DEVICE_AUDIO_SET_CUR_VOLUME_CONTROL audio request).
     * A cellphone host however, will scale the volume himself and not use audio requests to set the volume.
     *
     * We saw however, that the cellphone host will send an audio request one-time at boot/enumeration to set the current volume.
     * The value it uses is the current volume we report initially (so it basically echo's back the current value).
     * So to ensure a good user experience, we set the current volume to max and the cellphone host will then scale the volume relative to our max volume setting.
     */
    recSettings->curMute = 0x00U;
    recSettings->curVolume[0] =
        USB_VOLUME_CONVERSION_LOW_BYTES(USB_VolumeConversion_ConvertVolumeToUsb(AUDIOCODEC_MAX_INPUT_VOLUME_DB));
    recSettings->curVolume[1] =
        USB_VOLUME_CONVERSION_HIGH_BYTES(USB_VolumeConversion_ConvertVolumeToUsb(AUDIOCODEC_MAX_INPUT_VOLUME_DB));
    recSettings->minVolume[0] =
        USB_VOLUME_CONVERSION_LOW_BYTES(USB_VolumeConversion_ConvertVolumeToUsb(AUDIOCODEC_MIN_INPUT_VOLUME_DB));
    recSettings->minVolume[1] =
        USB_VOLUME_CONVERSION_HIGH_BYTES(USB_VolumeConversion_ConvertVolumeToUsb(AUDIOCODEC_MIN_INPUT_VOLUME_DB));
    recSettings->maxVolume[0] =
        USB_VOLUME_CONVERSION_LOW_BYTES(USB_VolumeConversion_ConvertVolumeToUsb(AUDIOCODEC_MAX_INPUT_VOLUME_DB));
    recSettings->maxVolume[1] =
        USB_VOLUME_CONVERSION_HIGH_BYTES(USB_VolumeConversion_ConvertVolumeToUsb(AUDIOCODEC_MAX_INPUT_VOLUME_DB));
    recSettings->resVolume[0] =
        USB_VOLUME_CONVERSION_LOW_BYTES(USB_VolumeConversion_ConvertResolution(AUDIOCODEC_INPUT_VOLUME_RES_DB));
    recSettings->resVolume[1] =
        USB_VOLUME_CONVERSION_HIGH_BYTES(USB_VolumeConversion_ConvertResolution(AUDIOCODEC_INPUT_VOLUME_RES_DB));
    recSettings->curBass = 0x00U;
    recSettings->curBass = 0x00U;
    recSettings->minBass = 0x80U;
    recSettings->maxBass = 0x7FU;
    recSettings->resBass = 0x01U;
    recSettings->curMid = 0x00U;
    recSettings->minMid = 0x80U;
    recSettings->maxMid = 0x7FU;
    recSettings->resMid = 0x01U;
    recSettings->curTreble = 0x01U;
    recSettings->minTreble = 0x80U;
    recSettings->maxTreble = 0x7FU;
    recSettings->resTreble = 0x01U;
    recSettings->curAutomaticGain = 0x01U;
    recSettings->curDelay[0] = 0x00U;
    recSettings->curDelay[1] = 0x40U;
    recSettings->minDelay[0] = 0x00U;
    recSettings->minDelay[1] = 0x00U;
    recSettings->maxDelay[0] = 0xFFU;
    recSettings->maxDelay[1] = 0xFFU;
    recSettings->resDelay[0] = 0x00U;
    recSettings->resDelay[1] = 0x01U;
    recSettings->curLoudness = 0x01U;
    recSettings->curSamplingFrequency[0] = 0x00U;
    recSettings->curSamplingFrequency[1] = 0x00U;
    recSettings->curSamplingFrequency[2] = 0x01U;
    recSettings->minSamplingFrequency[0] = 0x00U;
    recSettings->minSamplingFrequency[1] = 0x00U;
    recSettings->minSamplingFrequency[2] = 0x01U;
    recSettings->maxSamplingFrequency[0] = 0x00U;
    recSettings->maxSamplingFrequency[1] = 0x00U;
    recSettings->maxSamplingFrequency[2] = 0x01U;
    recSettings->resSamplingFrequency[0] = 0x00U;
    recSettings->resSamplingFrequency[1] = 0x00U;
    recSettings->resSamplingFrequency[2] = 0x01U;
}
#endif


static void InitSpeakerSettings(usb_audio_settings_t *speakerSettings)
{
    /*
     * For explanation on why current volume is set to max here, see above in @ref current_vol_max_setting
     */
    speakerSettings->curMute = 0x00U;
    speakerSettings->curVolume[0] =
        USB_VOLUME_CONVERSION_LOW_BYTES(USB_VolumeConversion_ConvertVolumeToUsb(AUDIOCODEC_MAX_OUTPUT_VOLUME_DB));
    speakerSettings->curVolume[1] =
        USB_VOLUME_CONVERSION_HIGH_BYTES(USB_VolumeConversion_ConvertVolumeToUsb(AUDIOCODEC_MAX_OUTPUT_VOLUME_DB));
    speakerSettings->minVolume[0] =
        USB_VOLUME_CONVERSION_LOW_BYTES(USB_VolumeConversion_ConvertVolumeToUsb(AUDIOCODEC_MIN_OUTPUT_VOLUME_DB));
    speakerSettings->minVolume[1] =
        USB_VOLUME_CONVERSION_HIGH_BYTES(USB_VolumeConversion_ConvertVolumeToUsb(AUDIOCODEC_MIN_OUTPUT_VOLUME_DB));
    speakerSettings->maxVolume[0] =
        USB_VOLUME_CONVERSION_LOW_BYTES(USB_VolumeConversion_ConvertVolumeToUsb(AUDIOCODEC_MAX_OUTPUT_VOLUME_DB));

    speakerSettings->maxVolume[1] =
        USB_VOLUME_CONVERSION_HIGH_BYTES(USB_VolumeConversion_ConvertVolumeToUsb(AUDIOCODEC_MAX_OUTPUT_VOLUME_DB));
    speakerSettings->resVolume[0] =
        USB_VOLUME_CONVERSION_LOW_BYTES(USB_VolumeConversion_ConvertResolution(AUDIOCODEC_OUTPUT_VOLUME_RES_DB));
    speakerSettings->resVolume[1] =
        USB_VOLUME_CONVERSION_HIGH_BYTES(USB_VolumeConversion_ConvertResolution(AUDIOCODEC_OUTPUT_VOLUME_RES_DB));
    speakerSettings->curBass = 0x00U;
    speakerSettings->curBass = 0x00U;
    speakerSettings->minBass = 0x80U;
    speakerSettings->maxBass = 0x7FU;
    speakerSettings->resBass = 0x01U;
    speakerSettings->curMid = 0x00U;
    speakerSettings->minMid = 0x80U;
    speakerSettings->maxMid = 0x7FU;
    speakerSettings->resMid = 0x01U;
    speakerSettings->curTreble = 0x01U;
    speakerSettings->minTreble = 0x80U;
    speakerSettings->maxTreble = 0x7FU;
    speakerSettings->resTreble = 0x01U;
    speakerSettings->curAutomaticGain = 0x01U;
    speakerSettings->curDelay[0] = 0x00U;
    speakerSettings->curDelay[1] = 0x40U;
    speakerSettings->minDelay[0] = 0x00U;
    speakerSettings->minDelay[1] = 0x00U;
    speakerSettings->maxDelay[0] = 0xFFU;
    speakerSettings->maxDelay[1] = 0xFFU;
    speakerSettings->resDelay[0] = 0x00U;
    speakerSettings->resDelay[1] = 0x01U;
    speakerSettings->curLoudness = 0x01U;
    speakerSettings->curSamplingFrequency[0] = 0x00U;
    speakerSettings->curSamplingFrequency[1] = 0x00U;
    speakerSettings->curSamplingFrequency[2] = 0x01U;
    speakerSettings->minSamplingFrequency[0] = 0x00U;
    speakerSettings->minSamplingFrequency[1] = 0x00U;
    speakerSettings->minSamplingFrequency[2] = 0x01U;
    speakerSettings->maxSamplingFrequency[0] = 0x00U;
    speakerSettings->maxSamplingFrequency[1] = 0x00U;
    speakerSettings->maxSamplingFrequency[2] = 0x01U;
    speakerSettings->resSamplingFrequency[0] = 0x00U;
    speakerSettings->resSamplingFrequency[1] = 0x00U;
    speakerSettings->resSamplingFrequency[2] = 0x01U;
}


/*!
 * @brief Audio init function.
 *
 * This function initializes the device with the composite device class information.
 *
 * @param device_composite          The pointer to the composite device structure.
 * @return kStatus_USB_Success .
 */
usb_status_t USB_DeviceAudioCompositeInit(usb_device_composite_struct_t *device_composite)
{
    uint32_t i;
    g_deviceAudioComposite = device_composite;
    g_deviceAudioComposite->audioUnified.copyProtect = 0x01U;
    g_deviceAudioComposite->audioUnified.enableStreaming = true;

#if USB_CHAT_SPEAKER_ENABLE
    InitSpeakerSettings(&g_deviceAudioComposite->audioUnified.chatSpeakerSettings);
    //USB_PipeCtrlInit(&g_UsbServiceCtxt.chatSpeakerPipeCtrl, ChatSpeakerPipeChangedCallback);
#endif
#if USB_GAME_SPEAKER_ENABLE
    InitSpeakerSettings(&g_deviceAudioComposite->audioUnified.gameSpeakerSettings);
    USB_PipeCtrlInit(&g_UsbServiceCtxt.gameSpeakerPipeCtrl, GameSpeakerPipeChangedCallback);
#endif
#if USB_RECORDER_ENABLE
    InitRecSettings(&g_deviceAudioComposite->audioUnified.recSettings);
    //USB_PipeCtrlInit(&g_UsbServiceCtxt.recPipeCtrl, RecordingPipeChangedCallback);
#endif

    return kStatus_USB_Success;
}

#if USB_CTRL_SERVICE_TX_ENABLE
void USB_AudioUnifiedStopTx(void *cookie)
{
    (void)cookie;

    PDBG("Stop tx Audio");

    AUDIO_MIXER_StopAllInterfaces();
#if USB_CHAT_SPEAKER_ENABLE
    USB_FeedbackStop(&g_UsbChatSpeaker_FeedbackSettings);
    USB_FeedbackReset(&g_UsbChatSpeaker_FeedbackSettings);
#endif
#if USB_GAME_SPEAKER_ENABLE
    USB_FeedbackStop(&g_UsbGameSpeaker_FeedbackSettings);
    USB_FeedbackReset(&g_UsbGameSpeaker_FeedbackSettings);
#endif
}
#endif

#if USB_CTRL_SERVICE_RX_ENABLE
void USB_AudioUnifiedStopRx(void *cookie)
{
    (void)cookie;

    PDBG("Stop rx Audio");

    audio_StopRx();
}
#endif

void USB_AudioEnableStreaming(bool enable)
{
    g_deviceAudioComposite->audioUnified.enableStreaming = enable;
}

#endif
