/***************************************************************************
 * 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                                                        */
/**                                                                       */
/**   Audio Class                                                         */
/**                                                                       */
/**************************************************************************/
/**************************************************************************/


/* Include necessary system files.  */

#define UX_SOURCE_CODE

#include "ux_api.h"
#include "ux_host_class_audio.h"
#include "ux_host_stack.h"


#if defined(UX_HOST_CLASS_AUDIO_2_SUPPORT)
static inline UINT _ux_host_class_audio_alternate_setting_locate_2(
    UX_HOST_CLASS_AUDIO *audio,
    UX_HOST_CLASS_AUDIO_SAMPLING *audio_sampling,
    UINT *alternate_setting);
#endif


/**************************************************************************/
/*                                                                        */
/*  FUNCTION                                               RELEASE        */
/*                                                                        */
/*    _ux_host_class_audio_alternate_setting_locate       PORTABLE C      */
/*                                                           6.1.12       */
/*  AUTHOR                                                                */
/*                                                                        */
/*    Chaoqiong Xiao, Microsoft Corporation                               */
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*     This function finds the right alternate setting according to the   */
/*     sampling desired.                                                  */
/*                                                                        */
/*  INPUT                                                                 */
/*                                                                        */
/*    audio                                 Pointer to audio class        */
/*    audio_sampling                        Pointer to audio sampling     */
/*    alternate_setting                     Pointer to located alternate  */
/*                                            setting                     */
/*                                                                        */
/*  OUTPUT                                                                */
/*                                                                        */
/*    Completion Status                                                   */
/*                                                                        */
/*  CALLS                                                                 */
/*                                                                        */
/*    _ux_utility_descriptor_parse          Parse descriptor              */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    Audio Class                                                         */
/*                                                                        */
/**************************************************************************/
UINT  _ux_host_class_audio_alternate_setting_locate(UX_HOST_CLASS_AUDIO *audio, UX_HOST_CLASS_AUDIO_SAMPLING *audio_sampling,
                                                    UINT *alternate_setting)
{

#if defined(UX_HOST_CLASS_AUDIO_2_SUPPORT)
    return(_ux_host_class_audio_alternate_setting_locate_2(audio, audio_sampling, alternate_setting));
#else

UCHAR *                                  descriptor;
UX_INTERFACE_DESCRIPTOR                  interface_descriptor;
UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR audio_interface_descriptor;
ULONG                                    total_descriptor_length;
UINT                                     descriptor_length;
UINT                                     descriptor_type;
UINT                                     descriptor_subtype;
UINT                                     interface_found;
ULONG                                    lower_frequency;
ULONG                                    higher_frequency;
UINT                                     specific_frequency_count;


    /* Get the descriptor to the entire configuration.  */
    descriptor =  audio -> ux_host_class_audio_configuration_descriptor;
    total_descriptor_length =  audio -> ux_host_class_audio_configuration_descriptor_length;

    /* Default is Interface descriptor not yet found.  */
    interface_found =  UX_FALSE;

    /* Scan the descriptor for the Audio Streaming interface.  */
    while (total_descriptor_length)
    {

        /* Gather the length, type and subtype of the descriptor.  */
        descriptor_length =   *descriptor;

        /* Make sure this descriptor has at least the minimum length.  */
        if (descriptor_length < 3)
        {

            /* Error trap. */
            _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED);

            /* If trace is enabled, insert this event into the trace buffer.  */
            UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0)

            return(UX_DESCRIPTOR_CORRUPTED);
        }

        descriptor_type =     *(descriptor + 1);
        descriptor_subtype =  *(descriptor + 2);

        /* Process relative to descriptor type.  */
        switch (descriptor_type)
        {


        case UX_INTERFACE_DESCRIPTOR_ITEM:

            /* Parse the interface descriptor and make it machine independent.  */
            _ux_utility_descriptor_parse(descriptor, _ux_system_interface_descriptor_structure,
                                            UX_INTERFACE_DESCRIPTOR_ENTRIES, (UCHAR *) &interface_descriptor);

            /* Ensure we have the correct interface for Audio streaming.  */
            if ((interface_descriptor.bInterfaceClass == UX_HOST_CLASS_AUDIO_CLASS) &&
                (interface_descriptor.bInterfaceSubClass == UX_HOST_CLASS_AUDIO_SUBCLASS_STREAMING))
            {

                /* Mark we have found it.  */
                interface_found =  UX_TRUE;

                /* And memorize the alternate setting.  */
                *alternate_setting =  interface_descriptor.bAlternateSetting;
            }
            else
            {

                /* Haven't found it.  */
                interface_found =  UX_FALSE;
            }
            break;


        case UX_HOST_CLASS_AUDIO_CS_INTERFACE:

            /* First make sure we have found the correct generic interface descriptor.  */
            if ((interface_found == UX_TRUE) && (descriptor_subtype == UX_HOST_CLASS_AUDIO_CS_FORMAT_TYPE))
            {

                /* Parse the FORMAT_TYPE descriptor and make it machine independent.  */
                _ux_utility_descriptor_parse(descriptor, _ux_system_class_audio_interface_descriptor_structure,
                                                UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_ENTRIES, (UCHAR *) &audio_interface_descriptor);

                /* This descriptor must refer to a PCM audio type.  */
                if (audio_interface_descriptor.bFormatType != UX_HOST_CLASS_AUDIO_FORMAT_TYPE_I)
                    break;

                /* The number of channels demanded by the application must match.  */
                if (audio_sampling -> ux_host_class_audio_sampling_channels != audio_interface_descriptor.bNrChannels)
                    break;

                /* The resolution demanded by the application must match.  */
                if (audio_sampling -> ux_host_class_audio_sampling_resolution != audio_interface_descriptor.bBitResolution)
                    break;

                /* Check the frequency demanded. The descriptor frequency is either defined
                   as a min and max frequency or an array of specified values.  */
                if (audio_interface_descriptor.bSamFreqType == 0)
                {

                    if (descriptor_length < (UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 6))
                    {

                      /* Error trap. */
                      _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED);

                      /* If trace is enabled, insert this event into the trace buffer.  */
                      UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0)

                      return(UX_DESCRIPTOR_CORRUPTED);
                    }

                    /* The declaration of frequency is contiguous, so get the minimum and maximum */
                    lower_frequency =  (ULONG)  *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH) |
                                       ((ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 1)) << 8 |
                                       ((ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 2)) << 16;

                    higher_frequency =  (ULONG)  *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 3) |
                                        ((ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 4)) << 8 |
                                        ((ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 5)) << 16;

                    /* Now compare with what is required.  */
                    if ((audio_sampling -> ux_host_class_audio_sampling_frequency >= lower_frequency) &&
                        (audio_sampling -> ux_host_class_audio_sampling_frequency <= higher_frequency))
                    {

                        /* We have found the right alternate setting.  */
                        audio -> ux_host_class_audio_sampling_descriptor = descriptor;
                        return(UX_SUCCESS);
                    }
                }
                else
                {

                    if (descriptor_length < (UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + (UINT)(3 * audio_interface_descriptor.bSamFreqType)))
                    {
                      return(UX_DESCRIPTOR_CORRUPTED);
                    }

                    /* The declaration of the frequency is declared as an array of specific values.  */
                    for (specific_frequency_count = 0; specific_frequency_count < audio_interface_descriptor.bSamFreqType;
                            specific_frequency_count++)
                    {

                        lower_frequency =  (ULONG)  *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + (specific_frequency_count * 3)) |
                                           ((ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 1 + (specific_frequency_count * 3))) << 8 |
                                           ((ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 2 + (specific_frequency_count * 3))) << 16;

                        /* Now compare with what is required.  */
                        if (audio_sampling -> ux_host_class_audio_sampling_frequency == lower_frequency)
                        {

                            /* We have found the right alternate setting.  */
                            audio -> ux_host_class_audio_sampling_descriptor = descriptor;
                            return(UX_SUCCESS);
                        }
                    }
                }
            }
            break;
        }

        /* Verify if the descriptor is still valid.  */
        if (descriptor_length > total_descriptor_length)
        {

            /* If trace is enabled, insert this event into the trace buffer.  */
            UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0)

            return(UX_DESCRIPTOR_CORRUPTED);
        }

        /* Jump to the next descriptor if we have not reached the end.  */
        descriptor +=  descriptor_length;

        /* And adjust the length left to parse in the descriptor.  */
        total_descriptor_length -=  descriptor_length;
    }

    /* If trace is enabled, insert this event into the trace buffer.  */
    UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_NO_ALTERNATE_SETTING, audio, 0, 0, UX_TRACE_ERRORS, 0, 0)

    /* We get here when either the report descriptor has a problem or we could
       not find the right audio device.  */
    return(UX_NO_ALTERNATE_SETTING);
#endif
}

#if defined(UX_HOST_CLASS_AUDIO_2_SUPPORT)
struct UX_HOST_CLASS_AUDIO_ALT_LOCATE_PARSER {
    UX_HOST_CLASS_AUDIO_SAMPLING    *sampling;
    UCHAR                           *clock_descriptor;
    ULONG                           alt;
    UINT                            status;
};
static UINT _ux_host_class_audio_alt_locate_parse(VOID *arg,
                        UCHAR *packed_interface_descriptor,
                        UX_HOST_CLASS_AUDIO_SAMPLING_CHARACTERISTICS *sam_attr)
{
struct UX_HOST_CLASS_AUDIO_ALT_LOCATE_PARSER    *parser = (struct UX_HOST_CLASS_AUDIO_ALT_LOCATE_PARSER *)arg;
UX_HOST_CLASS_AUDIO_SAMPLING                    *sampling = parser -> sampling;
ULONG                                           frequency_low, frequency_high;

    /* Check bNrChannels.  */
    if (sampling -> ux_host_class_audio_sampling_channels !=
        sam_attr -> ux_host_class_audio_sampling_characteristics_channels)
        return(0);

    /* Check bBitResolution.  */
    if (sampling -> ux_host_class_audio_sampling_resolution !=
        sam_attr -> ux_host_class_audio_sampling_characteristics_resolution)
        return(0);

    /* Calculate frequency low.  */
    frequency_low = sam_attr -> ux_host_class_audio_sampling_characteristics_frequency_low;
    if (UX_OVERFLOW_CHECK_MULV_ULONG(frequency_low, sam_attr -> ux_host_class_audio_sampling_characteristics_clock_mul))
    {

        /* Math error.  */
        parser -> status = UX_MATH_OVERFLOW;
        return(1);
    }
    frequency_low *= sam_attr -> ux_host_class_audio_sampling_characteristics_clock_mul;
    frequency_low /= sam_attr -> ux_host_class_audio_sampling_characteristics_clock_div;

    /* Calculate frequency high.  */
    frequency_high = sam_attr -> ux_host_class_audio_sampling_characteristics_frequency_high;
    if (UX_OVERFLOW_CHECK_MULV_ULONG(frequency_high, sam_attr -> ux_host_class_audio_sampling_characteristics_clock_mul))
    {

        /* Math error.  */
        parser -> status = UX_MATH_OVERFLOW;
        return(1);
    }
    frequency_high *= sam_attr -> ux_host_class_audio_sampling_characteristics_clock_mul;
    frequency_high /= sam_attr -> ux_host_class_audio_sampling_characteristics_clock_div;

    /* Check frequency in [low, high].  */
    if ((frequency_low <= sampling -> ux_host_class_audio_sampling_frequency) &&
        (frequency_high >= sampling -> ux_host_class_audio_sampling_frequency))
    {

        /* Save bAlternateSetting @ 3.  */
        parser -> alt = (ULONG)packed_interface_descriptor[3];

        /* Save UAC 1.0 FormatTypeI or UAC 2.0 CSD.  */
        parser -> clock_descriptor =
            sam_attr -> ux_host_class_audio_sampling_characteristics_descriptor;
        return(1);
    }

    /* Continue parsing.  */
    return(0);
}
static inline UINT _ux_host_class_audio_alternate_setting_locate_2(
    UX_HOST_CLASS_AUDIO *audio,
    UX_HOST_CLASS_AUDIO_SAMPLING *audio_sampling,
    UINT *alternate_setting)
{
struct UX_HOST_CLASS_AUDIO_ALT_LOCATE_PARSER    parser;
UINT                                            status;

    /* Parse specific sampling setting to get alt setting.  */
    parser.sampling = audio_sampling;
    parser.clock_descriptor = UX_NULL;
    parser.alt = 0xFF;
    parser.status = UX_SUCCESS;
    status = _ux_host_class_audio_raw_sampling_parse(audio, _ux_host_class_audio_alt_locate_parse, (VOID*)&parser);

    /* Check descriptor error.  */
    if (status != UX_SUCCESS)
        return(status);

    /* Check if valid alternate setting is found.  */
    if (parser.alt == 0xFF)
    {

        /* If trace is enabled, insert this event into the trace buffer.  */
        UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_NO_ALTERNATE_SETTING, audio, 0, 0, UX_TRACE_ERRORS, 0, 0)
        return(UX_NO_ALTERNATE_SETTING);
    }

    /* Save alternate setting.  */
    *alternate_setting = parser.alt;
    audio -> ux_host_class_audio_sampling_descriptor = parser.clock_descriptor;
    return(parser.status);
}
#endif
