/***************************************************************************
 * Copyright (c) 2024 Microsoft Corporation
 * Copyright (c) 2026-present Eclipse ThreadX contributors
 *
 * This program and the accompanying materials are made available under the
 * terms of the MIT License which is available at
 * https://opensource.org/licenses/MIT.
 *
 * SPDX-License-Identifier: MIT
 **************************************************************************/

/**************************************************************************/
/**                                                                       */
/** USBX Component                                                        */
/**                                                                       */
/**   Device Audio Class                                                  */
/**                                                                       */
/**************************************************************************/
/**************************************************************************/

#define UX_SOURCE_CODE


/* Include necessary system files.  */

#include "ux_api.h"
#include "ux_device_class_audio.h"
#include "ux_device_stack.h"


/**************************************************************************/
/*                                                                        */
/*  FUNCTION                                               RELEASE        */
/*                                                                        */
/*    _ux_device_class_audio_initialize                   PORTABLE C      */
/*                                                           6.3.0        */
/*  AUTHOR                                                                */
/*                                                                        */
/*    Chaoqiong Xiao, Microsoft Corporation                               */
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*    This function initializes the USB Audio device.                     */
/*                                                                        */
/*  INPUT                                                                 */
/*                                                                        */
/*    command                               Pointer to audio command      */
/*                                                                        */
/*  OUTPUT                                                                */
/*                                                                        */
/*    Completion Status                                                   */
/*                                                                        */
/*  CALLS                                                                 */
/*                                                                        */
/*    _ux_utility_memory_allocate           Allocate memory               */
/*    _ux_utility_memory_copy               Copy memory                   */
/*    _ux_utility_memory_free               Free memory                   */
/*    _ux_device_thread_create              Create thread to use          */
/*    _ux_device_thread_delete              Delete thread                 */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    Device Audio Class                                                  */
/*                                                                        */
/**************************************************************************/
UINT  _ux_device_class_audio_initialize(UX_SLAVE_CLASS_COMMAND *command)
{

UINT                                    status = UX_SUCCESS;
UX_DEVICE_CLASS_AUDIO                   *audio;
UX_DEVICE_CLASS_AUDIO_PARAMETER         *audio_parameter;
UX_DEVICE_CLASS_AUDIO_STREAM            *stream;
UX_DEVICE_CLASS_AUDIO_STREAM_PARAMETER  *stream_parameter;
UX_SLAVE_CLASS                          *audio_class;
ULONG                                   memory_size;
ULONG                                   streams_size;
ULONG                                   i;


    /* Get the class container.  */
    audio_class =  command -> ux_slave_class_command_class_ptr;

    /* Get the pointer to the application parameters for the audio class.  */
    audio_parameter = (UX_DEVICE_CLASS_AUDIO_PARAMETER *)command -> ux_slave_class_command_parameter;

    /* Create an instance of the device audio class, with additional streams and controls instances.  */
    memory_size  = sizeof(UX_DEVICE_CLASS_AUDIO);

    /* Put 0 to default result.  */
    streams_size = 0;

    /* Confirm there is no overflow on multiply.  */
    UX_UTILITY_MULC_SAFE(audio_parameter -> ux_device_class_audio_parameter_streams_nb, (ULONG)sizeof(UX_DEVICE_CLASS_AUDIO_STREAM), streams_size, status);
    if (status != UX_SUCCESS)
        return(status);

    /* Confirm there is no overflow on add.  */
    UX_UTILITY_ADD_SAFE(memory_size, streams_size, memory_size, status);
    if (status != UX_SUCCESS)
        return(status);

    /* Create buffer for audio and controls.  */
    audio = (UX_DEVICE_CLASS_AUDIO *)_ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, memory_size);

    /* Check for successful allocation.  */
    if (audio == UX_NULL)
        return(UX_MEMORY_INSUFFICIENT);

#if defined(UX_DEVICE_CLASS_AUDIO_INTERRUPT_SUPPORT)

    /* Create resources for interrupt endpoint support.  */

    /* Get status data size.  */
    audio -> ux_device_class_audio_status_size =
                audio_parameter -> ux_device_class_audio_parameter_status_size;

    /* Calculate queue size in bytes.  */
    if (UX_OVERFLOW_CHECK_MULV_ULONG(
        audio_parameter -> ux_device_class_audio_parameter_status_queue_size,
        audio_parameter -> ux_device_class_audio_parameter_status_size))
    {
        _ux_utility_memory_free(audio);
        return(UX_MATH_OVERFLOW);
    }
    memory_size = audio_parameter -> ux_device_class_audio_parameter_status_queue_size *
                audio_parameter -> ux_device_class_audio_parameter_status_size;
    audio -> ux_device_class_audio_status_queue_bytes = memory_size;

#if !defined(UX_DEVICE_STANDALONE)
    if (UX_OVERFLOW_CHECK_ADD_ULONG(memory_size, UX_DEVICE_CLASS_AUDIO_INTERRUPT_THREAD_STACK_SIZE))
    {
        _ux_utility_memory_free(audio);
        return(UX_MATH_OVERFLOW);
    }
    memory_size += UX_DEVICE_CLASS_AUDIO_INTERRUPT_THREAD_STACK_SIZE;

    audio_class -> ux_slave_class_thread_stack = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, memory_size);
    if (audio_class -> ux_slave_class_thread_stack == UX_NULL)
        status = UX_MEMORY_INSUFFICIENT;

    if (status == UX_SUCCESS)
    {
        status = _ux_device_thread_create(&audio_class -> ux_slave_class_thread,
                        "ux_device_class_audio_status_thread",
                        _ux_device_class_audio_interrupt_thread_entry, (ULONG)(ALIGN_TYPE)audio,
                        audio_class -> ux_slave_class_thread_stack,
                        UX_DEVICE_CLASS_AUDIO_INTERRUPT_THREAD_STACK_SIZE,
                        UX_THREAD_PRIORITY_CLASS, UX_THREAD_PRIORITY_CLASS,
                        UX_NO_TIME_SLICE, UX_DONT_START);
        if (status == UX_SUCCESS)
        {
            UX_THREAD_EXTENSION_PTR_SET(&(audio_class -> ux_slave_class_thread), audio)

            status = _ux_device_semaphore_create(&audio -> ux_device_class_audio_status_semaphore,
                                "ux_device_class_audio_status_semaphore", 0);
            if (status == UX_SUCCESS)
            {
                status = _ux_device_mutex_create(&audio -> ux_device_class_audio_status_mutex,
                                "ux_device_class_audio_status_mutex");
                if (status != UX_SUCCESS)
                    status = UX_MUTEX_ERROR;

                if (status != UX_SUCCESS)
                    _ux_device_semaphore_delete(&audio -> ux_device_class_audio_status_semaphore);
            }
            else
                status = UX_SEMAPHORE_ERROR;

            if (status != UX_SUCCESS)
                _ux_device_thread_delete(&audio_class -> ux_slave_class_thread);
        }
        else
            status = UX_THREAD_ERROR;

        if (status != UX_SUCCESS)
        {
            _ux_utility_memory_free(audio_class -> ux_slave_class_thread_stack);
            audio_class -> ux_slave_class_thread_stack = UX_NULL;
        }
    }

    if (status != UX_SUCCESS)
    {
        _ux_utility_memory_free(audio);
        return(status);
    }

    /* Status queue locates right after status stack.  */
    audio -> ux_device_class_audio_status_queue =
                        (UCHAR *)audio_class -> ux_slave_class_thread_stack +
                        UX_DEVICE_CLASS_AUDIO_INTERRUPT_THREAD_STACK_SIZE;
#else
    audio -> ux_device_class_audio_status_queue = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, memory_size);

    /* Check for successful allocation.  */
    if (audio -> ux_device_class_audio_status_queue == UX_NULL)
    {
        _ux_utility_memory_free(audio);
        return(UX_MEMORY_INSUFFICIENT);
    }
#endif

#if UX_DEVICE_ENDPOINT_BUFFER_OWNER == 1

    /* Allocate memory for interrupt endpoint buffer.  */
    audio -> ux_device_class_audio_interrupt_buffer = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_CACHE_SAFE_MEMORY,
                    audio_parameter -> ux_device_class_audio_parameter_status_size);
    if (audio -> ux_device_class_audio_interrupt_buffer == UX_NULL)
    {
        _ux_utility_memory_free(audio -> ux_device_class_audio_status_queue);
        _ux_utility_memory_free(audio);
        return(UX_MEMORY_INSUFFICIENT);
    }
#endif

#endif

    /* Save streams.  */
    if (streams_size)
    {
        audio -> ux_device_class_audio_streams = (UX_DEVICE_CLASS_AUDIO_STREAM *)((UCHAR *)audio + sizeof(UX_DEVICE_CLASS_AUDIO));
        audio -> ux_device_class_audio_streams_nb = audio_parameter -> ux_device_class_audio_parameter_streams_nb;
    }

    /* Allocate resources for streams.  */
    stream = audio -> ux_device_class_audio_streams;
    stream_parameter = audio_parameter -> ux_device_class_audio_parameter_streams;
    for (i = 0; i < audio -> ux_device_class_audio_streams_nb; i ++)
    {

#if UX_DEVICE_ENDPOINT_BUFFER_OWNER == 1
#if defined(UX_DEVICE_CLASS_AUDIO_FEEDBACK_SUPPORT)

        /* Allocate memory for feedback endpoint buffer.  */
        stream -> ux_device_class_audio_stream_feedback_buffer = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_CACHE_SAFE_MEMORY,
                        UX_DEVICE_CLASS_AUDIO_FEEDBACK_BUFFER_SIZE);
        if (stream -> ux_device_class_audio_stream_feedback_buffer == UX_NULL)
        {
            status = UX_MEMORY_INSUFFICIENT;
            break;
        }
#endif
#endif
        /* Get stream direction */
        stream -> ux_device_class_audio_stream_direction = stream_parameter -> ux_device_class_audio_stream_parameter_endpoint_direction;

        /* Create memory block based on max frame buffer size and max number of frames buffered.
           Each frame require some additional header memory (8 bytes).  */
        stream -> ux_device_class_audio_stream_frame_buffer_size = stream_parameter -> ux_device_class_audio_stream_parameter_max_frame_buffer_size;

        if (UX_OVERFLOW_CHECK_ADD_USHORT(stream -> ux_device_class_audio_stream_frame_buffer_size, 8))
        {
            status = UX_ERROR;
            break;
        }
        stream -> ux_device_class_audio_stream_frame_buffer_size += 8;

        if (UX_OVERFLOW_CHECK_MULV_ULONG(stream -> ux_device_class_audio_stream_frame_buffer_size,
            stream_parameter -> ux_device_class_audio_stream_parameter_max_frame_buffer_nb))
        {
            status = UX_ERROR;
            break;
        }
        memory_size = stream -> ux_device_class_audio_stream_frame_buffer_size *
                            stream_parameter -> ux_device_class_audio_stream_parameter_max_frame_buffer_nb;

        /* Create block of buffer buffer is cache safe for USB transfer.  */
#if UX_DEVICE_ENDPOINT_BUFFER_OWNER == 1
        stream -> ux_device_class_audio_stream_buffer = (UCHAR *)_ux_utility_memory_allocate(UX_NO_ALIGN, UX_CACHE_SAFE_MEMORY, memory_size);
#else
        stream -> ux_device_class_audio_stream_buffer = (UCHAR *)_ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, memory_size);
#endif

        /* Check for successful allocation.  */
        if (stream -> ux_device_class_audio_stream_buffer == UX_NULL)
        {
            status = UX_MEMORY_INSUFFICIENT;
            break;
        }

        stream -> ux_device_class_audio_stream_buffer_size = memory_size;
        stream -> ux_device_class_audio_stream_transfer_pos = (UX_DEVICE_CLASS_AUDIO_FRAME *)stream -> ux_device_class_audio_stream_buffer;
        stream -> ux_device_class_audio_stream_access_pos = stream -> ux_device_class_audio_stream_transfer_pos;

#if !defined(UX_DEVICE_STANDALONE)

        /* Create memory block for streaming thread stack in addition.  */
        if (stream_parameter -> ux_device_class_audio_stream_parameter_thread_stack_size == 0)
            memory_size = UX_DEVICE_CLASS_AUDIO_FEEDBACK_THREAD_STACK_SIZE;
        else
            memory_size = stream_parameter -> ux_device_class_audio_stream_parameter_thread_stack_size;
        stream -> ux_device_class_audio_stream_thread_stack = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, memory_size);

        /* Check for successful allocation.  */
        if (stream -> ux_device_class_audio_stream_thread_stack == UX_NULL)
        {
            status = UX_MEMORY_INSUFFICIENT;
            break;
        }

        /* Create streaming thread.  */
        status =  _ux_device_thread_create(&stream -> ux_device_class_audio_stream_thread , "ux_device_class_audio_stream_thread",
                    stream_parameter -> ux_device_class_audio_stream_parameter_thread_entry,
                    (ULONG)(ALIGN_TYPE)stream, (VOID *) stream -> ux_device_class_audio_stream_thread_stack,
                    memory_size, UX_THREAD_PRIORITY_CLASS,
                    UX_THREAD_PRIORITY_CLASS, UX_NO_TIME_SLICE, UX_DONT_START);

        /* Check for successful allocation.  */
        if (status != UX_SUCCESS)
        {
            _ux_utility_memory_free(stream -> ux_device_class_audio_stream_thread_stack);
            stream -> ux_device_class_audio_stream_thread_stack = UX_NULL;
            break;
        }

        UX_THREAD_EXTENSION_PTR_SET(&(stream -> ux_device_class_audio_stream_thread), stream)
#else

        /* Save task function for streaming.  */
        stream -> ux_device_class_audio_stream_task_function = stream_parameter -> ux_device_class_audio_stream_parameter_task_function;
#endif

#if defined(UX_DEVICE_CLASS_AUDIO_FEEDBACK_SUPPORT)

#if !defined(UX_DEVICE_STANDALONE)

        /* Check entry to confirm feedback is supported.  */
        if (stream_parameter -> ux_device_class_audio_stream_parameter_feedback_thread_entry)
        {
            /* Create memory block for streaming thread stack in addition.  */
            if (stream_parameter -> ux_device_class_audio_stream_parameter_feedback_thread_stack_size == 0)
                memory_size = UX_THREAD_STACK_SIZE;
            else
                memory_size = stream_parameter -> ux_device_class_audio_stream_parameter_feedback_thread_stack_size;
            stream -> ux_device_class_audio_stream_feedback_thread_stack = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, memory_size);

            /* Check for successful allocation.  */
            if (stream -> ux_device_class_audio_stream_feedback_thread_stack == UX_NULL)
            {
                status = UX_MEMORY_INSUFFICIENT;
                break;
            }

            /* Create streaming thread.  */
            status =  _ux_utility_thread_create(&stream -> ux_device_class_audio_stream_feedback_thread , "ux_device_class_audio_stream_feedback_thread",
                        stream_parameter -> ux_device_class_audio_stream_parameter_feedback_thread_entry,
                        (ULONG)(ALIGN_TYPE)stream, (VOID *) stream -> ux_device_class_audio_stream_feedback_thread_stack,
                        memory_size, UX_THREAD_PRIORITY_CLASS,
                        UX_THREAD_PRIORITY_CLASS, UX_NO_TIME_SLICE, UX_DONT_START);

            /* Check for successful allocation.  */
            if (status != UX_SUCCESS)
            {
                _ux_utility_memory_free(stream -> ux_device_class_audio_stream_feedback_thread_stack);
                stream -> ux_device_class_audio_stream_feedback_thread_stack = UX_NULL;
                break;
            }

            UX_THREAD_EXTENSION_PTR_SET(&(stream -> ux_device_class_audio_stream_feedback_thread), stream)
        }
#else
        if (stream_parameter -> ux_device_class_audio_stream_parameter_feedback_task_function)
        {

            /* Save task function for streaming.  */
            stream -> ux_device_class_audio_stream_feedback_task_function = stream_parameter -> ux_device_class_audio_stream_parameter_feedback_task_function;
        }
#endif
#endif

        /* Save callbacks.  */
        _ux_utility_memory_copy(&stream -> ux_device_class_audio_stream_callbacks,
                                &stream_parameter -> ux_device_class_audio_stream_parameter_callbacks,
                                sizeof(UX_DEVICE_CLASS_AUDIO_STREAM_CALLBACKS)); /* Use case of memcpy is verified. */

        /* Save audio instance.  */
        stream -> ux_device_class_audio_stream_audio = audio;

        stream ++;
        stream_parameter ++;
    }

    /* Check for successful creation.  */
    if (status == UX_SUCCESS)
    {

        /* Save the address of the Audio instance inside the Audio container.  */
        audio_class -> ux_slave_class_instance = (VOID *) audio;

        /* Link to class instance.  */
        audio -> ux_device_class_audio_class = audio_class;

        /* Save callbacks.  */
        _ux_utility_memory_copy(&audio -> ux_device_class_audio_callbacks,
            &audio_parameter -> ux_device_class_audio_parameter_callbacks,
            sizeof(UX_DEVICE_CLASS_AUDIO_CALLBACKS)); /* Use case of memcpy is verified. */

#if defined(UX_DEVICE_STANDALONE)

        /* Link task function.  */
        audio_class -> ux_slave_class_task_function = _ux_device_class_audio_tasks_run;
#endif

        /* Return completion status.  */
        return(UX_SUCCESS);
    }

    /* Error trap!  */
    _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, status);

    /* Free allocated resources.  */
    stream = audio -> ux_device_class_audio_streams;
    for (i = 0; i < audio -> ux_device_class_audio_streams_nb; i ++)
    {
#if defined(UX_DEVICE_CLASS_AUDIO_FEEDBACK_SUPPORT)
#if UX_DEVICE_ENDPOINT_BUFFER_OWNER == 1
        if (stream -> ux_device_class_audio_stream_feedback_buffer)
            _ux_utility_memory_free(stream -> ux_device_class_audio_stream_feedback_buffer);
#endif
#if !defined(UX_DEVICE_STANDALONE)
        if (stream -> ux_device_class_audio_stream_feedback_thread_stack)
        {
            _ux_device_thread_delete(&stream -> ux_device_class_audio_stream_feedback_thread);
            _ux_utility_memory_free(stream -> ux_device_class_audio_stream_feedback_thread_stack);
        }
#endif
#endif
#if !defined(UX_DEVICE_STANDALONE)
        if (stream -> ux_device_class_audio_stream_thread_stack)
        {
            _ux_device_thread_delete(&stream -> ux_device_class_audio_stream_thread);
            _ux_utility_memory_free(stream -> ux_device_class_audio_stream_thread_stack);
        }
#endif
        if (stream -> ux_device_class_audio_stream_buffer)
            _ux_utility_memory_free(stream -> ux_device_class_audio_stream_buffer);
        stream ++;
    }
#if defined(UX_DEVICE_CLASS_AUDIO_INTERRUPT_SUPPORT)
#if UX_DEVICE_ENDPOINT_BUFFER_OWNER == 1
    if (audio -> ux_device_class_audio_interrupt_buffer)
        _ux_utility_memory_free(audio -> ux_device_class_audio_interrupt_buffer);
#endif
#if !defined(UX_DEVICE_STANDALONE)
    if (audio_class -> ux_slave_class_thread_stack)
    {
        _ux_device_thread_delete(&audio_class -> ux_slave_class_thread);
        _ux_utility_memory_free(audio_class -> ux_slave_class_thread_stack);

        _ux_device_semaphore_delete(&audio -> ux_device_class_audio_status_semaphore);
        _ux_device_mutex_delete(&audio -> ux_device_class_audio_status_mutex);
    }
#else
    if (audio -> ux_device_class_audio_status_queue)
    {
        _ux_utility_memory_free(audio -> ux_device_class_audio_status_queue);
    }
#endif
#endif
    _ux_utility_memory_free(audio);

    return(status);
}

/**************************************************************************/
/*                                                                        */
/*  FUNCTION                                               RELEASE        */
/*                                                                        */
/*    _uxe_device_class_audio_initialize                  PORTABLE C      */
/*                                                           6.3.0        */
/*  AUTHOR                                                                */
/*                                                                        */
/*    Chaoqiong Xiao, Microsoft Corporation                               */
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*    This function checks errors in audio initialization function call.  */
/*                                                                        */
/*  INPUT                                                                 */
/*                                                                        */
/*    command                               Pointer to audio command      */
/*                                                                        */
/*  OUTPUT                                                                */
/*                                                                        */
/*    Completion Status                                                   */
/*                                                                        */
/*  CALLS                                                                 */
/*                                                                        */
/*    _ux_device_class_audio_initialize     Initialize audio instance     */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    Device Audio Class                                                  */
/*                                                                        */
/**************************************************************************/
UINT  _uxe_device_class_audio_initialize(UX_SLAVE_CLASS_COMMAND *command)
{

UX_DEVICE_CLASS_AUDIO_PARAMETER         *audio_parameter;
ULONG                                   i;

    /* Get the pointer to the application parameters for the audio class.  */
    audio_parameter = (UX_DEVICE_CLASS_AUDIO_PARAMETER *)command -> ux_slave_class_command_parameter;

    /* Sanity checks.  */
    if (audio_parameter == UX_NULL)
        return(UX_INVALID_PARAMETER);

    /* There must be at least one stream.  */
    if (audio_parameter -> ux_device_class_audio_parameter_streams == UX_NULL ||
        audio_parameter -> ux_device_class_audio_parameter_streams_nb < 1)
        return(UX_INVALID_PARAMETER);

    for (i = 0; i < audio_parameter -> ux_device_class_audio_parameter_streams_nb; i ++)
    {
        if ((audio_parameter -> ux_device_class_audio_parameter_streams[i].ux_device_class_audio_stream_parameter_max_frame_buffer_size == 0) ||
            (audio_parameter -> ux_device_class_audio_parameter_streams[i].ux_device_class_audio_stream_parameter_max_frame_buffer_nb == 0))
            return(UX_INVALID_PARAMETER);
    }

#if defined(UX_DEVICE_CLASS_AUDIO_INTERRUPT_SUPPORT)

    /* There must be status setting for event queue.  */
    if (audio_parameter -> ux_device_class_audio_parameter_status_queue_size == 0 ||
        audio_parameter -> ux_device_class_audio_parameter_status_size == 0)
        return(UX_INVALID_PARAMETER);
#endif

    /* Do initialize.  */
    return(_ux_device_class_audio_initialize(command));
}
