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

#define UX_SOURCE_CODE


/* Include necessary system files.  */

#include "ux_api.h"
#include "ux_device_class_hid.h"
#include "ux_device_stack.h"


/**************************************************************************/ 
/*                                                                        */ 
/*  FUNCTION                                               RELEASE        */ 
/*                                                                        */ 
/*    _ux_device_class_hid_initialize                     PORTABLE C      */ 
/*                                                           6.3.0        */
/*  AUTHOR                                                                */
/*                                                                        */
/*    Chaoqiong Xiao, Microsoft Corporation                               */
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */ 
/*    This function initializes the USB HID device.                       */ 
/*    This function is called by the class register function. It is only  */ 
/*    done once.                                                          */ 
/*                                                                        */ 
/*  INPUT                                                                 */ 
/*                                                                        */ 
/*    command                              Pointer to hid command         */ 
/*                                                                        */ 
/*  OUTPUT                                                                */ 
/*                                                                        */ 
/*    Completion Status                                                   */ 
/*                                                                        */ 
/*  CALLS                                                                 */ 
/*                                                                        */ 
/*    _ux_utility_memory_allocate           Allocate memory               */
/*    _ux_utility_memory_free               Free memory                   */
/*    _ux_device_thread_create              Create thread                 */
/*    _ux_device_thread_delete              Delete thread                 */
/*    _ux_utility_event_flags_create        Create event flags group      */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    USBX Source Code                                                    */
/*                                                                        */
/**************************************************************************/
UINT  _ux_device_class_hid_initialize(UX_SLAVE_CLASS_COMMAND *command)
{
                                          
UX_SLAVE_CLASS_HID                      *hid;
UX_SLAVE_CLASS_HID_PARAMETER            *hid_parameter;
UX_SLAVE_CLASS                          *class_ptr;
UINT                                    status = UX_SUCCESS;
ULONG                                   array_memory_size;
#if (UX_DEVICE_ENDPOINT_BUFFER_OWNER == 1) && defined(UX_DEVICE_CLASS_HID_ZERO_COPY)
UINT                                    i;
UCHAR                                   *buffer;
#endif


    /* Compile option checks.  */
    UX_ASSERT(UX_DEVICE_CLASS_HID_EVENT_BUFFER_LENGTH <= UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH);
#if UX_DEVICE_ENDPOINT_BUFFER_OWNER == 0
    UX_ASSERT(UX_DEVICE_CLASS_HID_EVENT_BUFFER_LENGTH <= UX_SLAVE_REQUEST_DATA_MAX_LENGTH);
#endif


    /* Get the pointer to the application parameters for the hid class.  */
    hid_parameter =  command -> ux_slave_class_command_parameter;

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

    /* Create an instance of the device hid class.  */
    hid =  _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_SLAVE_CLASS_HID));

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

    /* Save the address of the HID instance inside the HID container.  */
    class_ptr -> ux_slave_class_instance = (VOID *) hid;

#if defined(UX_DEVICE_CLASS_HID_OWN_ENDPOINT_BUFFER)

    /* Allocate buffer(s) for endpoint(s).  */
    UX_ASSERT(!UX_DEVICE_CLASS_HID_ENDPOINT_BUFFER_SIZE_CALC_OVERFLOW);
    hid -> ux_device_class_hid_endpoint_buffer = _ux_utility_memory_allocate(
                            UX_NO_ALIGN, UX_CACHE_SAFE_MEMORY,
                            UX_DEVICE_CLASS_HID_ENDPOINT_BUFFER_SIZE);
    if (hid -> ux_device_class_hid_endpoint_buffer == UX_NULL)
    {
        _ux_utility_memory_free(hid);
        return(UX_MEMORY_INSUFFICIENT);
    }
#endif

#if !defined(UX_DEVICE_STANDALONE)

    /* Allocate some memory for the thread stack. */
    class_ptr -> ux_slave_class_thread_stack =  
            _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, UX_DEVICE_CLASS_HID_THREAD_STACK_SIZE);
    
    /* Check for successful allocation.  */
    if (class_ptr -> ux_slave_class_thread_stack == UX_NULL)
        status = UX_MEMORY_INSUFFICIENT;

    /* This instance needs to be running in a different thread. So start
       a new thread. We pass a pointer to the class to the new thread.  This thread
       does not start until we have a instance of the class. */
    if (status == UX_SUCCESS)
        status =  _ux_device_thread_create(&class_ptr -> ux_slave_class_thread, "ux_slave_hid_thread", 
                    _ux_device_class_hid_interrupt_thread,
                    (ULONG) (ALIGN_TYPE) class_ptr, (VOID *) class_ptr -> ux_slave_class_thread_stack,
                    UX_DEVICE_CLASS_HID_THREAD_STACK_SIZE, UX_THREAD_PRIORITY_CLASS,
                    UX_THREAD_PRIORITY_CLASS, UX_NO_TIME_SLICE, UX_DONT_START);
#else

#if defined(UX_DEVICE_CLASS_HID_FLEXIBLE_EVENTS_QUEUE)

    /* Set event buffer.  */
    hid -> ux_device_class_hid_event.ux_device_class_hid_event_buffer =
                                    UX_DEVICE_CLASS_HID_INTERRUPTIN_BUFFER(hid);
#endif

    /* Set task function.  */
    class_ptr -> ux_slave_class_task_function = _ux_device_class_hid_tasks_run;
#endif

    /* Check the creation of this thread.  */
    if (status == UX_SUCCESS)
    {

#if !defined(UX_DEVICE_STANDALONE)
        UX_THREAD_EXTENSION_PTR_SET(&(class_ptr -> ux_slave_class_thread), class_ptr)
#endif


        /* Store all the application parameter information about the report.  */
        hid -> ux_device_class_hid_report_address             = hid_parameter -> ux_device_class_hid_parameter_report_address;
        hid -> ux_device_class_hid_report_length              = hid_parameter -> ux_device_class_hid_parameter_report_length;
        hid -> ux_device_class_hid_report_id                  = hid_parameter -> ux_device_class_hid_parameter_report_id;

        /* Store the callback function.  */
        hid -> ux_device_class_hid_callback                   = hid_parameter -> ux_device_class_hid_parameter_callback;
        hid -> ux_device_class_hid_get_callback               = hid_parameter -> ux_device_class_hid_parameter_get_callback;

#if defined(UX_DEVICE_CLASS_HID_FLEXIBLE_EVENTS_QUEUE)

        /* If event length is invalid, UX_DEVICE_CLASS_HID_EVENT_BUFFER_LENGTH is used.  */
        if (UX_DEVICE_CLASS_HID_PARAM_EVENT_MAX_LENGTH(hid_parameter) == 0 ||
            UX_DEVICE_CLASS_HID_PARAM_EVENT_MAX_LENGTH(hid_parameter) > UX_DEVICE_CLASS_HID_EVENT_BUFFER_LENGTH)
            UX_DEVICE_CLASS_HID_PARAM_EVENT_MAX_LENGTH(hid_parameter) = UX_DEVICE_CLASS_HID_EVENT_BUFFER_LENGTH;

        /* If event queue size is invalid, UX_DEVICE_CLASS_HID_MAX_EVENTS_QUEUE is used.  */
        if (UX_DEVICE_CLASS_HID_PARAM_EVENT_QUEUE_SIZE(hid_parameter) < 2 ||
            UX_DEVICE_CLASS_HID_PARAM_EVENT_QUEUE_SIZE(hid_parameter) > UX_DEVICE_CLASS_HID_MAX_EVENTS_QUEUE)
            UX_DEVICE_CLASS_HID_PARAM_EVENT_QUEUE_SIZE(hid_parameter) = UX_DEVICE_CLASS_HID_MAX_EVENTS_QUEUE;

        /* Save event size.  */
        UX_DEVICE_CLASS_HID_EVENT_MAX_LENGTH(hid) = UX_DEVICE_CLASS_HID_PARAM_EVENT_MAX_LENGTH(hid_parameter);
#endif

        /* Create the event array.  */
        UX_ASSERT(!UX_OVERFLOW_CHECK_MULC_ULONG(
                    UX_DEVICE_CLASS_HID_EVENT_QUEUE_ITEM_SIZE(hid),
                    UX_DEVICE_CLASS_HID_PARAM_EVENT_QUEUE_SIZE(hid_parameter)));
        array_memory_size = UX_DEVICE_CLASS_HID_EVENT_QUEUE_ITEM_SIZE(hid) * UX_DEVICE_CLASS_HID_PARAM_EVENT_QUEUE_SIZE(hid_parameter);
        hid -> ux_device_class_hid_event_array =  _ux_utility_memory_allocate(UX_NO_ALIGN,
                                        UX_REGULAR_MEMORY, array_memory_size);

        /* Do we need event buffer?
         * 1. Even zero copy, report copy is kept to avoid keep buffers in application.
         * 2. Other cases, buffer must be allocated.
         */
        /* Allocate buffer if needed.  */
        {

#if (UX_DEVICE_ENDPOINT_BUFFER_OWNER == 1) && defined(UX_DEVICE_CLASS_HID_ZERO_COPY)

            /* Allocate cache safe event buffers.  */
            buffer = _ux_utility_memory_allocate_mulv_safe(UX_NO_ALIGN, UX_CACHE_SAFE_MEMORY,
                        UX_DEVICE_CLASS_HID_PARAM_EVENT_MAX_LENGTH(hid_parameter),
                        UX_DEVICE_CLASS_HID_PARAM_EVENT_QUEUE_SIZE(hid_parameter));

            /* Allocation error check.  */
            if (buffer == UX_NULL)
            {
                if (hid -> ux_device_class_hid_event_array != UX_NULL)
                {
                    _ux_utility_memory_free(hid -> ux_device_class_hid_event_array);
                    hid -> ux_device_class_hid_event_array = UX_NULL;
                }
            }
            else
            {

                /* Assign event buffers.  */
                for (i = 0; i < UX_DEVICE_CLASS_HID_PARAM_EVENT_QUEUE_SIZE(hid_parameter); i ++)
                {
                    hid -> ux_device_class_hid_event_array[i].ux_device_class_hid_event_buffer = buffer;
                    buffer += UX_DEVICE_CLASS_HID_PARAM_EVENT_MAX_LENGTH(hid_parameter);
                }
            }
#else

            /* Regular event place data following id,type and length.  */
#endif
        }

        /* Check for successful allocation.  */
        if (hid -> ux_device_class_hid_event_array != UX_NULL)
        {

            /* Initialize the head and tail of the notification round robin buffers. 
               At first, the head and tail are pointing to the beginning of the array.  */
            hid -> ux_device_class_hid_event_array_head =  hid -> ux_device_class_hid_event_array;
            hid -> ux_device_class_hid_event_array_tail =  hid -> ux_device_class_hid_event_array;
            hid -> ux_device_class_hid_event_array_end  =  (UX_DEVICE_CLASS_HID_EVENT*)((UCHAR*)hid -> ux_device_class_hid_event_array + array_memory_size);

            /* Store the start and stop signals if needed by the application.  */
            hid -> ux_slave_class_hid_instance_activate = hid_parameter -> ux_slave_class_hid_instance_activate;
            hid -> ux_slave_class_hid_instance_deactivate = hid_parameter -> ux_slave_class_hid_instance_deactivate;

            /* By default no event wait timeout.  */
            hid -> ux_device_class_hid_event_wait_timeout = UX_WAIT_FOREVER;

#if !defined(UX_DEVICE_STANDALONE)

            /* Create a event flag group for the hid class to synchronize with the event interrupt thread.  */
            status =  _ux_utility_event_flags_create(&hid -> ux_device_class_hid_event_flags_group, "ux_device_class_hid_event_flag");

            /* Check status.  */
            if (status != UX_SUCCESS)
                status = UX_EVENT_ERROR;
            else
#endif
            {
#if defined(UX_DEVICE_CLASS_HID_INTERRUPT_OUT_SUPPORT)

#if !defined(UX_DEVICE_STANDALONE)

                /* Create a mutex for reading reentry check.  */
                status = _ux_utility_mutex_create(&hid -> ux_device_class_hid_read_mutex,
                                                  "ux_device_class_hid_read_mutex");
                if (status == UX_SUCCESS)
                {
#endif

                    /* If receiver is enabled by parameter, initialize it.  */
                    if (hid_parameter -> ux_device_class_hid_parameter_receiver_initialize)
                    {

                        /* Allocate buffer for receiver and receiver events.  */
                        status = hid_parameter ->
                                ux_device_class_hid_parameter_receiver_initialize(hid,
                                                hid_parameter,
                                                &hid -> ux_device_class_hid_receiver);
                    }

                    /* Done success, return.  */
                    if (status == UX_SUCCESS)
                        return(status);

#if !defined(UX_DEVICE_STANDALONE)

                    /* There is error, delete mutex.  */
                    _ux_device_mutex_delete(&hid -> ux_device_class_hid_read_mutex);
                }
                else
                    status = UX_MUTEX_ERROR;

                /* There is error, delete event flags.  */
                _ux_utility_event_flags_delete(&hid -> ux_device_class_hid_event_flags_group);
#endif
#else
                return(status);
#endif

            }

#if !defined(UX_DEVICE_STANDALONE) || defined(UX_DEVICE_CLASS_HID_INTERRUPT_OUT_SUPPORT)

            /* There is still initialization activities after array creation,
             * and some error occurs in this stage.  */
            /* Free allocated event array memory.  */
#if (UX_DEVICE_ENDPOINT_BUFFER_OWNER == 1) && defined(UX_DEVICE_CLASS_HID_ZERO_COPY)
            _ux_utility_memory_free(hid -> ux_device_class_hid_event_array -> ux_device_class_hid_event_buffer);
#endif
            _ux_utility_memory_free(hid -> ux_device_class_hid_event_array);
#endif

        }
        else
            status =  UX_MEMORY_INSUFFICIENT;

#if !defined(UX_DEVICE_STANDALONE)

        /* Delete thread.  */
        _ux_device_thread_delete(&class_ptr -> ux_slave_class_thread);
#endif
    }
    else
        status = (UX_THREAD_ERROR);

#if !defined(UX_DEVICE_STANDALONE)

    /* Free stack. */
    if (class_ptr -> ux_slave_class_thread_stack)
        _ux_utility_memory_free(class_ptr -> ux_slave_class_thread_stack);
#endif

#if defined(UX_DEVICE_CLASS_HID_OWN_ENDPOINT_BUFFER)
    _ux_utility_memory_free(hid -> ux_device_class_hid_endpoint_buffer);
#endif

    /* Unmount instance. */
    class_ptr -> ux_slave_class_instance =  UX_NULL;

    /* Free HID instance. */
    _ux_utility_memory_free(hid);

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


/**************************************************************************/
/*                                                                        */
/*  FUNCTION                                               RELEASE        */
/*                                                                        */
/*    _uxe_device_class_hid_initialize                    PORTABLE C      */
/*                                                           6.3.0        */
/*  AUTHOR                                                                */
/*                                                                        */
/*    Chaoqiong Xiao, Microsoft Corporation                               */
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*    This function checks errors in HID initialize function call.        */
/*                                                                        */
/*  INPUT                                                                 */
/*                                                                        */
/*    command                               Pointer to hid command        */ 
/*                                                                        */
/*  OUTPUT                                                                */
/*                                                                        */
/*    None                                                                */
/*                                                                        */
/*  CALLS                                                                 */
/*                                                                        */
/*    _ux_device_class_hid_initialize       Initialize HID instance       */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    Device Stack                                                        */
/*                                                                        */
/**************************************************************************/
UINT  _uxe_device_class_hid_initialize(UX_SLAVE_CLASS_COMMAND *command)
{

UX_SLAVE_CLASS_HID_PARAMETER            *hid_parameter;

    /* Get the pointer to the application parameters for the hid class.  */
    hid_parameter =  command -> ux_slave_class_command_parameter;

    /* Check input parameters.  */
    if ((hid_parameter -> ux_device_class_hid_parameter_report_address == UX_NULL) ||
        (hid_parameter -> ux_device_class_hid_parameter_report_length == 0) ||
        (hid_parameter -> ux_device_class_hid_parameter_report_length > UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH))
    {
        return(UX_INVALID_PARAMETER);
    }

    /* Invoke initialize function.  */
    return(_ux_device_class_hid_initialize(command));
}
