/***************************************************************************
 * 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 Stack                                                        */
/**                                                                       */
/**************************************************************************/
/**************************************************************************/

#define UX_SOURCE_CODE

/* Include necessary system files.  */

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

#if defined(UX_HOST_STANDALONE)


#define UX_HOST_STACK_ENUM_TRANS_ERROR(t)       (                               \
    (t)->ux_transfer_request_completion_code != UX_SUCCESS ||                   \
    (t)->ux_transfer_request_actual_length !=                                   \
        (t)->ux_transfer_request_requested_length)

static inline VOID _ux_host_stack_port_check_run(VOID);
static inline VOID _ux_host_stack_enum_run(VOID);
static inline VOID _ux_host_stack_pending_transfers_run(VOID);


/**************************************************************************/
/*                                                                        */
/*  FUNCTION                                                 RELEASE      */
/*                                                                        */
/*    _ux_host_stack_tasks_run                              PORTABLE C    */
/*                                                             6.3.0      */
/*  AUTHOR                                                                */
/*                                                                        */
/*    Chaoqiong Xiao, Microsoft Corporation                               */
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*    This function runs host stack and registered classes tasks.         */
/*                                                                        */
/*    It's valid only in standalone mode.                                 */
/*    It's non-blocking.                                                  */
/*                                                                        */
/*  INPUT                                                                 */
/*                                                                        */
/*                                                                        */
/*  OUTPUT                                                                */
/*                                                                        */
/*    Completion State Status                                             */
/*                                                                        */
/*  CALLS                                                                 */
/*                                                                        */
/*    (ux_hcd_entry_function)               HCD function                  */
/*    (ux_host_class_task_function)         Host class task function      */
/*    (ux_system_host_change_function)      Host change callback function */
/*    (ux_system_host_enum_hub_function)    Host hub enumeration function */
/*    _ux_host_stack_rh_change_process      Host Root Hub process         */
/*    _ux_host_stack_device_address_set     Start process to set address  */
/*    _ux_host_stack_configuration_set      Start process to set config   */
/*    _ux_host_stack_device_descriptor_read Start process to read device  */
/*                                          descriptor                    */
/*    _ux_host_stack_new_configuration_create                             */
/*                                          Link a new configuration to   */
/*                                          a device                      */
/*    _ux_host_stack_configuration_instance_create                        */
/*                                          Create configuration instance */
/*    _ux_host_stack_transfer_run           Runs transfer state machine   */
/*    _ux_host_stack_class_device_scan      Query device class driver     */
/*    _ux_host_stack_class_interface_scan   Query interface class driver  */
/*    _ux_host_stack_interfaces_scan        Parse interfaces in a         */
/*                                          configuration                 */
/*    _ux_host_stack_device_remove          Remove a device               */
/*    _ux_utility_descriptor_parse          Parse a descriptor            */
/*    _ux_utility_memory_allocate           Allocate memory               */
/*    _ux_utility_memory_free               Free memory                   */
/*    _ux_utility_time_get                  Get current time tick         */
/*    _ux_system_error_handler              Error trap                    */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    Application                                                         */
/*                                                                        */
/**************************************************************************/
UINT _ux_host_stack_tasks_run(VOID)
{

UX_HCD *hcd_inst;
ULONG hcd_index;
UX_HOST_CLASS *class_inst;
ULONG class_index;

    /* =========== Run all HCD tasks.  */
    for (hcd_index = 0; hcd_index < UX_SYSTEM_HOST_MAX_HCD_GET(); hcd_index++)
    {
        hcd_inst = &_ux_system_host->ux_system_host_hcd_array[hcd_index];
        if (hcd_inst -> ux_hcd_status != UX_HCD_STATUS_OPERATIONAL ||
            hcd_inst -> ux_hcd_entry_function == UX_NULL)
            continue;
        hcd_inst -> ux_hcd_entry_function(hcd_inst, UX_HCD_TASKS_RUN, UX_NULL);
    }

    /* =========== Run port check process.  */
    _ux_host_stack_port_check_run();

    /* =========== Run enumeration process.  */
    _ux_host_stack_enum_run();

    /* =========== Run classes tasks.  */
    for (class_index = 0; class_index < UX_SYSTEM_HOST_MAX_CLASS_GET(); class_index++)
    {
        class_inst = &_ux_system_host->ux_system_host_class_array[class_index];
        if ((class_inst -> ux_host_class_status == UX_UNUSED) ||
            (class_inst -> ux_host_class_task_function == UX_NULL))
            continue;
        class_inst -> ux_host_class_task_function(class_inst);
    }

    /* =========== Run pending transfer tasks.  */
    _ux_host_stack_pending_transfers_run();

    /* =========== Run background tasks.  */
    if (_ux_system_host -> ux_system_host_change_function)
    {
        _ux_system_host -> ux_system_host_change_function(
                        UX_STANDALONE_WAIT_BACKGROUND_TASK, UX_NULL, UX_NULL);
    }

    /* No idle report support now.  */
    return (UX_STATE_WAIT);
}

static inline VOID _ux_host_stack_port_check_run(VOID)
{
#if UX_MAX_DEVICES > 1

    /* We try the hub first. For this we look into the USBX project
        structure to see if there is at least one hub.  */
    if (_ux_system_host->ux_system_host_enum_hub_function != UX_NULL)
    {

        /* Yes, there is a HUB function, call it!  */
        _ux_system_host->ux_system_host_enum_hub_function();
    }
#endif

    /* Check root hub changes.  */
    _ux_host_stack_rh_change_process();
}

static inline UINT _ux_host_stack_rh_port_enable(UX_DEVICE *device)
{
UX_HCD          *hcd = UX_DEVICE_HCD_GET(device);
ALIGN_TYPE      port_index = (ALIGN_TYPE)UX_DEVICE_PORT_LOCATION_GET(device);
    return hcd -> ux_hcd_entry_function(hcd, UX_HCD_ENABLE_PORT, (VOID *)port_index);
}
static inline UINT _ux_host_stack_rh_port_reset(UX_DEVICE *device)
{
UX_HCD          *hcd = UX_DEVICE_HCD_GET(device);
ALIGN_TYPE      port_index = (ALIGN_TYPE)UX_DEVICE_PORT_LOCATION_GET(device);
    return hcd -> ux_hcd_entry_function(hcd, UX_HCD_RESET_PORT, (VOID *)port_index);
}
static inline UINT _ux_host_stack_rh_port_status_get(UX_DEVICE *device)
{
UX_HCD          *hcd = UX_DEVICE_HCD_GET(device);
ALIGN_TYPE      port_index = (ALIGN_TYPE)UX_DEVICE_PORT_LOCATION_GET(device);
    return hcd -> ux_hcd_entry_function(hcd, UX_HCD_GET_PORT_STATUS, (VOID *)port_index);
}
static inline VOID _ux_host_stack_enum_address_sent(UX_DEVICE *device)
{

    /* Address (request wValue) applied to device.  */
    device -> ux_device_address = device -> ux_device_control_endpoint.
                ux_endpoint_transfer_request.ux_transfer_request_value & 0x7F;
    device -> ux_device_state = UX_DEVICE_ADDRESSED;

    /* Unlock enumeration.  */
    _ux_system_host -> ux_system_host_enum_lock = UX_NULL;
}
static inline VOID _ux_host_stack_enum_device_descriptor_parse0(
    UX_DEVICE *device, UCHAR *descriptor)
{

    /* Endpoint descriptor updated.  */
    device -> ux_device_descriptor.bMaxPacketSize0 = descriptor[7];

    /* Update EP0 wMaxPacketSize.  */
    device -> ux_device_control_endpoint.
                ux_endpoint_descriptor.wMaxPacketSize = descriptor[7];
}
static inline VOID _ux_host_stack_enum_configuration_descriptor_parse0(
    UX_DEVICE *device, UX_CONFIGURATION *configuration, UCHAR *descriptor)
{
    /* This configuration must be linked to the device.  */
    _ux_host_stack_new_configuration_create(device, configuration);

    /* The descriptor is in a packed format, parse it locally.  */
    _ux_utility_descriptor_parse(descriptor,
        _ux_system_configuration_descriptor_structure,
        UX_CONFIGURATION_DESCRIPTOR_ENTRIES,
        (UCHAR *) &configuration -> ux_configuration_descriptor);
}
static inline UINT _ux_host_stack_enum_configuration_read(UX_DEVICE *device)
{

UX_CONFIGURATION    *configuration;
UCHAR               *descriptor;
UX_ENDPOINT         *control_endpoint;
UX_TRANSFER         *transfer_request;

    /* Allocate configuration container for the descriptor.  */
    configuration = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY,
                                                    sizeof(UX_CONFIGURATION));
    if (configuration == UX_NULL)
        return(UX_MEMORY_INSUFFICIENT);

    /* Need to allocate memory for the configuration descriptor the first time we read
       only the configuration descriptor when we have the configuration descriptor, we have
       the length of the entire configuration\interface\endpoint descriptors.  */
    descriptor =  _ux_utility_memory_allocate(UX_SAFE_ALIGN,
                    UX_CACHE_SAFE_MEMORY, UX_CONFIGURATION_DESCRIPTOR_LENGTH);
    if (descriptor == UX_NULL)
    {
        _ux_utility_memory_free(configuration);
        return(UX_MEMORY_INSUFFICIENT);
    }

    /* Save allocated configuration container.  */
    device -> ux_device_enum_inst.configuration = configuration;

    /* Retrieve the pointer to the control endpoint and its transfer_request.  */
    control_endpoint =  &device -> ux_device_control_endpoint;
    transfer_request =  &control_endpoint -> ux_endpoint_transfer_request;

    /* Create a transfer_request for the GET_DESCRIPTOR request.  */
    transfer_request -> ux_transfer_request_data_pointer =      descriptor;
    transfer_request -> ux_transfer_request_requested_length =  UX_CONFIGURATION_DESCRIPTOR_LENGTH;
    transfer_request -> ux_transfer_request_function =          UX_GET_DESCRIPTOR;
    transfer_request -> ux_transfer_request_type =              UX_REQUEST_IN |
                                                                UX_REQUEST_TYPE_STANDARD |
                                                                UX_REQUEST_TARGET_DEVICE;
    transfer_request -> ux_transfer_request_value =             (UINT)device -> ux_device_enum_index |
                                                                (UINT)(UX_CONFIGURATION_DESCRIPTOR_ITEM << 8);
    transfer_request -> ux_transfer_request_index =             0;

    /* Transfer for wait.  */
    UX_TRANSFER_STATE_RESET(transfer_request);
    device -> ux_device_enum_trans = transfer_request;

    return(UX_SUCCESS);
}
static inline VOID _ux_host_stack_configuration_parsed(UX_DEVICE *device)
{
UINT            status;

    /* By default (error) configuration not activated.  */
    device -> ux_device_enum_state = UX_HOST_STACK_ENUM_FAIL;

    /* Query device class driver.  */
    status =  _ux_host_stack_class_device_scan(device);
    if (status == UX_SUCCESS)
    {

        /* Prepare device activation.  */
        device -> ux_device_enum_index = 0xFF;
        device -> ux_device_enum_state = UX_HOST_STACK_ENUM_ACTIVATE;
        device -> ux_device_enum_inst.device = device;
    }
    else if (status == UX_NO_CLASS_MATCH)
    {

        /* Query interface class driver.  */
        status =  _ux_host_stack_class_interface_scan(device);
        if (status == UX_SUCCESS)
        {

            /* Prepare interfaces activation.  */
            device -> ux_device_enum_index = 0;
            device -> ux_device_enum_state = UX_HOST_STACK_ENUM_CONFIG_SET;
            device -> ux_device_enum_inst.configuration =
                                        device -> ux_device_first_configuration;
        }
    }
}

static inline VOID _ux_host_stack_device_enumerated(UX_DEVICE *device)
{

    /* If trace is enabled, register this object.  */
    UX_TRACE_OBJECT_REGISTER(UX_TRACE_HOST_OBJECT_TYPE_DEVICE,
            UX_DEVICE_HCD_GET(device), UX_DEVICE_PARENT_GET(device),
            UX_DEVICE_PORT_LOCATION_GET(device), 0);

    /* Check if there is unnecessary resource to free.  */
    if (device -> ux_device_packed_configuration &&
        device -> ux_device_packed_configuration_keep_count == 0)
    {
        _ux_utility_memory_free(device -> ux_device_packed_configuration);
        device -> ux_device_packed_configuration = UX_NULL;
    }

    /* Reset enumeration state.  */
    device -> ux_device_enum_state = UX_STATE_IDLE;

    /* Unlock device EP0.  */
    device -> ux_device_control_endpoint.ux_endpoint_transfer_request.
                        ux_transfer_request_flags &= ~UX_TRANSFER_FLAG_LOCK;

    /* Disconnect from enumeration list.  */

    /* Clear enumeration flag to stop enumeration sequence.  */
    device -> ux_device_flags &= ~UX_DEVICE_FLAG_ENUM;

    /* If HCD is dead or device disconnected, free device.  */
    if (UX_DEVICE_HCD_GET(device) -> ux_hcd_status != UX_HCD_STATUS_OPERATIONAL ||
        (device -> ux_device_enum_port_status & UX_PS_CCS) == 0)
    {
        _ux_host_stack_device_remove(UX_DEVICE_HCD_GET(device),
                                     UX_DEVICE_PARENT_GET(device),
                                     UX_DEVICE_PORT_LOCATION_GET(device));

        /* Done without notification.  */
        return;
    }

    /* Device connected and enumerated success/fail.  */

    /* For root hub, set root hub connection status.  */
    if (UX_DEVICE_PARENT_IS_ROOTHUB(device))
    {
        UX_DEVICE_HCD_GET(device) -> ux_hcd_rh_device_connection |=
                    (ULONG)(1 << UX_DEVICE_PORT_LOCATION_GET(device));
    }

    /* If change function available, notify device connection.  */
    if (_ux_system_host -> ux_system_host_change_function)
    {
        _ux_system_host -> ux_system_host_change_function(
                        UX_DEVICE_CONNECTION, UX_NULL, (VOID*)device);
    }
}

static inline UINT _ux_host_stack_interface_activate_wait(UX_DEVICE *device,
    UX_HOST_CLASS_COMMAND *command)
{
UINT            status;
UX_INTERFACE    *interface_inst;

    command -> ux_host_class_command_container = device -> ux_device_enum_inst.ptr;
    command -> ux_host_class_command_class_ptr = (device -> ux_device_enum_index == 0xFF) ?
                device -> ux_device_class :
                device -> ux_device_enum_inst.interface -> ux_interface_class;
    command -> ux_host_class_command_request = UX_HOST_CLASS_COMMAND_ACTIVATE_WAIT;

    /* If there is no class linked, just next state.  */
    if (command -> ux_host_class_command_class_ptr != UX_NULL)
        status = command -> ux_host_class_command_class_ptr -> ux_host_class_entry_function(command);
    else
        status = UX_STATE_NEXT;

    /* No wait command or ready for next state.  */
    if (status == UX_FUNCTION_NOT_SUPPORTED ||
        status == UX_STATE_NEXT)
    {

        /* Activate for device class driver.  */
        if (device -> ux_device_enum_index == 0xFF)
        {

            /* Enumeration done (class driver controls device configuration).  */
            device -> ux_device_enum_state = UX_HOST_STACK_ENUM_DONE;
            return(UX_STATE_NEXT);
        }
        else
        {

            /* Activate next interface (default setting).  */
            interface_inst = device -> ux_device_enum_inst.interface -> ux_interface_next_interface;
            while(interface_inst != UX_NULL &&
                interface_inst -> ux_interface_descriptor.bAlternateSetting != 0)
            {
                interface_inst = interface_inst -> ux_interface_next_interface;
            }
            if (interface_inst != UX_NULL)
            {
                device -> ux_device_enum_inst.interface = interface_inst;
                device -> ux_device_enum_state = UX_HOST_STACK_ENUM_ACTIVATE;
                return(UX_STATE_NEXT);
            }

            /* Enumeration is done.  */
            device -> ux_device_enum_state = UX_HOST_STACK_ENUM_DONE;
            return(UX_STATE_NEXT);
        }
    }

    /* Error cases.  */
    if (status < UX_STATE_NEXT)
    {
        device -> ux_device_enum_state = UX_HOST_STACK_ENUM_RETRY;
        return(UX_STATE_NEXT);
    }

    /* Keep waiting.  */
    return(UX_STATE_WAIT);
}


static inline VOID _ux_host_stack_device_enum_run(UX_DEVICE *device)
{
UX_INTERRUPT_SAVE_AREA
UINT                    status;
ULONG                   current_ms, elapsed_ms;
UX_TRANSFER             *trans;
UX_CONFIGURATION        *configuration;
UX_HOST_CLASS_COMMAND   class_command;
UCHAR                   *buffer;
INT                     immediate_state = UX_TRUE;

    /* Check if the device enumeration should be processed.  */
    if ((device -> ux_device_flags & UX_DEVICE_FLAG_ENUM) == 0)
        return;

    /* Enumeration lock check, devices not addressed should wait.  */
    UX_DISABLE
    if (_ux_system_host -> ux_system_host_enum_lock &&
        device -> ux_device_address == 0)
    {

        /* Check if the device is the one locks, others should wait.  */
        if (device != _ux_system_host -> ux_system_host_enum_lock)
        {

            /* Wait, return.  */
            UX_RESTORE
            return;
        }
    }
    UX_RESTORE

    while (immediate_state)
    {
        switch (device -> ux_device_enum_state)
        {
        case UX_STATE_RESET:

            /* Reset retry counts.  */
            device -> ux_device_enum_retry = UX_RH_ENUMERATION_RETRY;

            device -> ux_device_enum_state = UX_HOST_STACK_ENUM_PORT_ENABLE;
            device -> ux_device_enum_next_state = UX_HOST_STACK_ENUM_PORT_ENABLE;
            device -> ux_device_enum_port_status = UX_PS_CCS;

            /* Fall through.  */
        case UX_HOST_STACK_ENUM_PORT_ENABLE:

            /* Lock enumeration any way.  */
            _ux_system_host -> ux_system_host_enum_lock = device;

#if UX_MAX_DEVICES > 1
            if (UX_DEVICE_PARENT_IS_HUB(device))
            {

                /* Issue a port reset on hub side.  */
                device -> ux_device_flags |= UX_DEVICE_FLAG_RESET;
                device -> ux_device_enum_state = UX_HOST_STACK_ENUM_HUB_OPERATION_WAIT;
                return;
            }
#endif

            /* For device connected to roohub, we may need port enable (OHCI).  */
            status = _ux_host_stack_rh_port_enable(device);
            if (status == UX_PORT_INDEX_UNKNOWN)
            {

                /* No retry, enumeration fail.  */
                device -> ux_device_enum_state = UX_HOST_STACK_ENUM_FAIL;
                continue;
            }

            /* Wait a while after port connection.  */
            device -> ux_device_enum_next_state = UX_HOST_STACK_ENUM_PORT_RESET;
            device -> ux_device_enum_state = UX_HOST_STACK_ENUM_WAIT;
            device -> ux_device_enum_wait_start = _ux_utility_time_get();
            device -> ux_device_enum_wait_ms =
                        UX_MS_TO_TICK_NON_ZERO(UX_RH_ENUMERATION_RETRY_DELAY);
            continue;

#if UX_MAX_DEVICES > 1
        case UX_HOST_STACK_ENUM_HUB_OPERATION_WAIT:

            /* Keep waiting, state is changed in hub tasks.  */
            return;
#endif

        case UX_HOST_STACK_ENUM_PORT_RESET:

            /* Reset may blocking, wait the reset done.  */
            /* Fall through.  */
        case UX_HOST_STACK_ENUM_PORT_RESET_WAIT:

            /* Run states for reset.  */
            status = _ux_host_stack_rh_port_reset(device);

            /* Check fatal error (exit enumeration).  */
            if (status < UX_STATE_ERROR)
            {

                /* Fail no retry.  */
                device -> ux_device_enum_state = UX_HOST_STACK_ENUM_FAIL;
                continue;
            }

            /* Check error.  */
            if (status == UX_STATE_ERROR)
            {

                /* Fail retry.  */
                device -> ux_device_enum_state = UX_HOST_STACK_ENUM_RETRY;
                continue;
            }

            /* Ready for next state.  */
            if (status == UX_STATE_NEXT)
            {

                /* Return device address to 0.  */
#if UX_MAX_DEVICES > 1
                if (device -> ux_device_address)
                {

                    /* Free the address.  */
                    UX_DEVICE_HCD_GET(device) ->
                    ux_hcd_address[(device -> ux_device_address-1) >> 3] &=
                        (UCHAR)(1u << ((device -> ux_device_address-1) & 7u));
                }
#endif

                /* Get port status.  */
                status = _ux_host_stack_rh_port_status_get(device);
                if (status == UX_PORT_INDEX_UNKNOWN)
                {

                    /* Fail no retry: No connection.  */
                    device -> ux_device_enum_port_status = 0;
                    device -> ux_device_enum_state = UX_HOST_STACK_ENUM_FAIL;
                    continue;
                }

                /* Save port status, next : SetAddress.  */
                device -> ux_device_enum_port_status = (UCHAR)status;
                device -> ux_device_enum_trans = &device ->
                        ux_device_control_endpoint.ux_endpoint_transfer_request;
                device -> ux_device_enum_state = UX_HOST_STACK_ENUM_TRANS_LOCK_WAIT;
                device -> ux_device_enum_next_state = UX_HOST_STACK_ENUM_DEVICE_ADDR_SET;
                continue;
            }

            /* Keep waiting.  */
            return;

        case UX_HOST_STACK_ENUM_DEVICE_ADDR_SET:

            /* Check port connection status.  */
            if ((device -> ux_device_enum_port_status & UX_PS_CCS) == 0)
            {

                /* Port disconnected, no retry, just fail.  */
                device -> ux_device_enum_state = UX_HOST_STACK_ENUM_FAIL;
                continue;
            }

            /* Set device speed.  */
            device -> ux_device_speed = device -> ux_device_enum_port_status >> UX_PS_DS;

            /* Reset bMaxPacketSize0.  */
            device -> ux_device_descriptor.bMaxPacketSize0 = 0;

            /* Start SetAddress().  */
            status = _ux_host_stack_device_address_set(device);
            if (status != UX_SUCCESS)
            {

                /* Request fail, retry.  */
                device -> ux_device_enum_state = UX_HOST_STACK_ENUM_RETRY;
                continue;
            }

            /* Wait request progress done.  */
            UX_TRANSFER_STATE_RESET(device -> ux_device_enum_trans);
            device -> ux_device_enum_state = UX_HOST_STACK_ENUM_TRANS_WAIT;
            device -> ux_device_enum_next_state = UX_HOST_STACK_ENUM_DEVICE_ADDR_SENT;
            continue;

        case UX_HOST_STACK_ENUM_DEVICE_ADDR_SENT:

            /* Transfer error check.  */
            trans = &device -> ux_device_control_endpoint.ux_endpoint_transfer_request;
            if (trans -> ux_transfer_request_completion_code != UX_SUCCESS)
            {
                device -> ux_device_enum_state = UX_HOST_STACK_ENUM_RETRY;
                continue;
            }

            /* Address is already sent to device.  */
            _ux_host_stack_enum_address_sent(device);

            /* Wait at least 2ms after address is sent.  */
            device -> ux_device_enum_next_state = UX_HOST_STACK_ENUM_DEVICE_DESCR_READ;
            device -> ux_device_enum_state = UX_HOST_STACK_ENUM_WAIT;
            device -> ux_device_enum_wait_start = _ux_utility_time_get();
            device -> ux_device_enum_wait_ms = UX_MS_TO_TICK_NON_ZERO(2);
            continue;

        case UX_HOST_STACK_ENUM_DEVICE_DESCR_READ:

            /* Start GetDescriptor(Device).  */
            status = _ux_host_stack_device_descriptor_read(device);
            if (status != UX_SUCCESS)
            {
                device -> ux_device_enum_state = UX_HOST_STACK_ENUM_RETRY;
                continue;
            }
            UX_TRANSFER_STATE_RESET(device -> ux_device_enum_trans);
            device -> ux_device_enum_state = UX_HOST_STACK_ENUM_TRANS_WAIT;
            device -> ux_device_enum_next_state = UX_HOST_STACK_ENUM_DEVICE_DESCR_PARSE;
            continue;

        case UX_HOST_STACK_ENUM_DEVICE_DESCR_PARSE:

            /* Transfer error check.  */
            trans = &device -> ux_device_control_endpoint.ux_endpoint_transfer_request;
            buffer = trans -> ux_transfer_request_data_pointer;
            if (UX_HOST_STACK_ENUM_TRANS_ERROR(trans))
            {
                _ux_utility_memory_free(buffer);
                device -> ux_device_enum_state = UX_HOST_STACK_ENUM_RETRY;
                continue;
            }

            /* Device bMaxPacketSize0 not available, update it.  */
            if(device -> ux_device_descriptor.bMaxPacketSize0 == 0)
            {
                /* Validate the bMaxPacketSize0.  */
                if (buffer[7] != 8 && buffer[7] != 16 && buffer[7] != 32 && buffer[7] != 64)
                {

                    _ux_utility_memory_free(buffer);
                    device -> ux_device_enum_state = UX_HOST_STACK_ENUM_RETRY;
                    continue;
                }

                /* Validate the USB-IF bDeviceClass class code.  */
#if defined(UX_HOST_DEVICE_CLASS_CODE_VALIDATION_ENABLE)
                switch(buffer[4])
                {
                case 0x00: /* Fall through.  */
                case 0x02: /* Fall through.  */
                case 0x09: /* Fall through.  */
                case 0x11: /* Fall through.  */
                case 0xDC: /* Fall through.  */
                case 0xEF: /* Fall through.  */
                case 0xFF:
                    break;
                default:

                    /* Invalid device class code.  */
                    _ux_utility_memory_free(buffer);
                    device -> ux_device_enum_state = UX_HOST_STACK_ENUM_RETRY;
                    continue;
                }
#endif

                _ux_host_stack_enum_device_descriptor_parse0(device, buffer);

                /* Update SETUP wLength.  */
                trans -> ux_transfer_request_requested_length = UX_DEVICE_DESCRIPTOR_LENGTH;

                /* Issue GetDescriptor(Device) again and parse it.  */
                device -> ux_device_enum_trans = trans;
                UX_TRANSFER_STATE_RESET(trans);
                device -> ux_device_enum_state = UX_HOST_STACK_ENUM_TRANS_WAIT;
                device -> ux_device_enum_next_state = UX_HOST_STACK_ENUM_DEVICE_DESCR_PARSE;
                continue;
            }

            /* Parse the device descriptor.  */
            _ux_utility_descriptor_parse(buffer,
                _ux_system_device_descriptor_structure, UX_DEVICE_DESCRIPTOR_ENTRIES,
                (UCHAR *) &device -> ux_device_descriptor);
            _ux_utility_memory_free(buffer);
            trans -> ux_transfer_request_data_pointer = UX_NULL;

            /* Start configuration enumeration, from index 0.  */
            device -> ux_device_enum_index = 0;

            /* Fall through.  */
        case UX_HOST_STACK_ENUM_CONFIG_DESCR_READ:

            /* Start GetDescriptor(Configuration, 9).  */
            status = _ux_host_stack_enum_configuration_read(device);
            if (status != UX_SUCCESS)
            {
                device -> ux_device_enum_state = UX_HOST_STACK_ENUM_RETRY;
                continue;
            }
            device -> ux_device_enum_state = UX_HOST_STACK_ENUM_TRANS_WAIT;
            device -> ux_device_enum_next_state = UX_HOST_STACK_ENUM_CONFIG_DESCR_PARSE;

            /* In this case, there are two memory blocks allocated floating:
               - UX_CONFIGURATION
               - configuration descriptor RX buffer.  */
            continue;

        case UX_HOST_STACK_ENUM_CONFIG_DESCR_PARSE:

            /* Transfer error check.  */
            trans = &device -> ux_device_control_endpoint.ux_endpoint_transfer_request;
            buffer = trans -> ux_transfer_request_data_pointer;
            if (UX_HOST_STACK_ENUM_TRANS_ERROR(trans))
            {
                _ux_utility_memory_free(buffer);
                _ux_utility_memory_free(device -> ux_device_enum_inst.ptr);
                device -> ux_device_enum_state = UX_HOST_STACK_ENUM_RETRY;
                continue;
            }

            /* Load current parsing configuration.  */
            configuration = device -> ux_device_enum_inst.configuration;

            /* Get configuration descriptor only.  */
            if (trans -> ux_transfer_request_actual_length == UX_CONFIGURATION_DESCRIPTOR_LENGTH)
            {

                /* Parse the configuration descriptor and free descriptor buffer.  */
                _ux_host_stack_enum_configuration_descriptor_parse0(device, configuration, buffer);

                /* Release descriptor buffer.  */
                _ux_utility_memory_free(buffer);
                trans -> ux_transfer_request_data_pointer = UX_NULL;

                /* If there is no interface for the configuration, check next.  */
                if (configuration -> ux_configuration_descriptor.wTotalLength ==
                    UX_CONFIGURATION_DESCRIPTOR_LENGTH)
                {
                    device -> ux_device_enum_state = UX_HOST_STACK_ENUM_CONFIG_DESCR_NEXT;
                    continue;
                }

                /* Allocate new buffer for total configuration.  */
                buffer = _ux_utility_memory_allocate(UX_SAFE_ALIGN,
                    UX_CACHE_SAFE_MEMORY,
                    configuration -> ux_configuration_descriptor.wTotalLength);
                if (buffer == UX_NULL)
                {
                    device -> ux_device_enum_state = UX_HOST_STACK_ENUM_RETRY;
                    continue;
                }
                trans -> ux_transfer_request_data_pointer = buffer;

                /* Modify transfer length for total configuration  */
                trans -> ux_transfer_request_requested_length =
                    configuration -> ux_configuration_descriptor.wTotalLength;

                /* Start transfer again.  */
                device -> ux_device_enum_trans = trans;
                UX_TRANSFER_STATE_RESET(device -> ux_device_enum_trans);
                device -> ux_device_enum_state = UX_HOST_STACK_ENUM_TRANS_WAIT;
                device -> ux_device_enum_next_state = UX_HOST_STACK_ENUM_CONFIG_DESCR_PARSE;

                /* In this case, there is one memory blocks allocated floating:
                    - configuration descriptor RX buffer.  */
                continue;
            }

            /* Parse configuration and it's interfaces.  */
            status = _ux_host_stack_interfaces_scan(configuration, buffer);

            /* Release descriptor memory.  */
            _ux_utility_memory_free(buffer);
            trans -> ux_transfer_request_data_pointer = UX_NULL;

            /* Check operation status.  */
            if (status != UX_SUCCESS)
            {
                device -> ux_device_enum_state = UX_HOST_STACK_ENUM_RETRY;
                continue;
            }

            /* Enumerate next configuration.  */
            /* Fall through.  */
        case UX_HOST_STACK_ENUM_CONFIG_DESCR_NEXT:
            device -> ux_device_enum_index ++;
            if (device -> ux_device_enum_index <
                device -> ux_device_descriptor.bNumConfigurations)
            {
                device -> ux_device_enum_state = UX_HOST_STACK_ENUM_CONFIG_DESCR_READ;
                continue;
            }

            /* All configurations are enumerated.  */
            _ux_host_stack_configuration_parsed(device);

            /* Roll back to next state.  */
            continue;

        case UX_HOST_STACK_ENUM_CONFIG_SET:

            /* The device is now in the unconfigured state. We need to deal
               with the amount of power the device is consuming before allowing
               it to be configured. Otherwise we may run the risk of an over
               current fault. */
            configuration = device -> ux_device_enum_inst.configuration;
            if (((configuration -> ux_configuration_descriptor.bmAttributes &
                    UX_CONFIGURATION_DEVICE_SELF_POWERED) == 0) &&
                 (configuration -> ux_configuration_descriptor.MaxPower >
                    UX_DEVICE_MAX_POWER_GET(device)))
            {

                /* Error trap. */
                _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, UX_OVER_CURRENT_CONDITION);

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

                /* Enumeration fail without retry.  */
                device -> ux_device_enum_state = UX_HOST_STACK_ENUM_FAIL;
                continue;
            }

            /* Prepare transfer request SetConfiguration().  */
            _ux_host_stack_configuration_set(configuration);

            /* Roll back to start transfer wait.  */
            UX_TRANSFER_STATE_RESET(device -> ux_device_enum_trans);
            device -> ux_device_enum_next_state = UX_HOST_STACK_ENUM_CONFIG_ACTIVATE;
            device -> ux_device_enum_state = UX_HOST_STACK_ENUM_TRANS_WAIT;
            continue;

        case UX_HOST_STACK_ENUM_CONFIG_ACTIVATE:
            trans = &device -> ux_device_control_endpoint.ux_endpoint_transfer_request;
            if (UX_HOST_STACK_ENUM_TRANS_ERROR(trans))
            {
                device -> ux_device_enum_state = UX_HOST_STACK_ENUM_RETRY;
                continue;
            }

            /* Set device state to configured.  */
            configuration = device -> ux_device_enum_inst.configuration;
            device -> ux_device_state = UX_DEVICE_CONFIGURED;
            device -> ux_device_current_configuration = configuration;

            /* Create the configuration instance.  */
            status =  _ux_host_stack_configuration_instance_create(configuration);
            if (status != UX_SUCCESS)
            {
                device -> ux_device_enum_state = UX_HOST_STACK_ENUM_RETRY;
                continue;
            }

            if (configuration -> ux_configuration_first_interface)
            {

                /* Activate classes.  */
                device -> ux_device_enum_index = 0;
                device -> ux_device_enum_inst.interface =
                            configuration -> ux_configuration_first_interface;
            }
            else
            {

                /* No activation, done  */
                device -> ux_device_enum_state = UX_HOST_STACK_ENUM_DONE;
                continue;
            }

            /* Fall through.  */
        case UX_HOST_STACK_ENUM_ACTIVATE:

            /* Class activate.  */
            class_command.ux_host_class_command_request = UX_HOST_CLASS_COMMAND_ACTIVATE_START;
            if (device -> ux_device_enum_index == 0xFF)
            {

                /* Device class driver.  */
                class_command.ux_host_class_command_container = device;
                class_command.ux_host_class_command_class_ptr = device -> ux_device_class;
            }
            else
            {

                /* Interface class driver.  */
                class_command.ux_host_class_command_container = device -> ux_device_enum_inst.ptr;
                class_command.ux_host_class_command_class_ptr =
                                    device -> ux_device_enum_inst.interface -> ux_interface_class;
            }

            /* If there class linked, start activation.  */
            if (class_command.ux_host_class_command_class_ptr != UX_NULL)
            {
                status = class_command.ux_host_class_command_class_ptr ->
                                    ux_host_class_entry_function(&class_command);
                if (status != UX_SUCCESS)
                {
                    device -> ux_device_enum_state = UX_HOST_STACK_ENUM_RETRY;
                    continue;
                }
            }

            /* Class activate execute wait.  */
            device -> ux_device_enum_state = UX_HOST_STACK_ENUM_ACTIVATE_WAIT;

            /* Fall through.  */
        case UX_HOST_STACK_ENUM_ACTIVATE_WAIT:

            /* Class activate execution wait.  */
            status = _ux_host_stack_interface_activate_wait(device, &class_command);

            /* Next state.  */
            if (status != UX_STATE_WAIT)
                continue;

            /* Keep waiting.  */
            return;

        case UX_HOST_STACK_ENUM_TRANS_WAIT:

            /* Poll transfer task.  */
            trans = device -> ux_device_enum_trans;
            status = _ux_host_stack_transfer_run(trans);

            /* Transfer done - next state.  */
            if (status == UX_STATE_NEXT || status == UX_STATE_IDLE)
            {
                device -> ux_device_enum_trans = UX_NULL;
                device -> ux_device_enum_state = device -> ux_device_enum_next_state;
                return;
            }

            /* Check error.  */
            if (status < UX_STATE_ERROR)
            {

                /* No retry, fail.  */
                if (trans -> ux_transfer_request_data_pointer)
                {
                    _ux_utility_memory_free(trans -> ux_transfer_request_data_pointer);
                    trans -> ux_transfer_request_data_pointer = UX_NULL;
                }
                if (device -> ux_device_enum_next_state == UX_HOST_STACK_ENUM_CONFIG_DESCR_PARSE)
                {
                    _ux_utility_memory_free(device -> ux_device_enum_inst.ptr);
                    device -> ux_device_enum_inst.ptr = UX_NULL;
                }
                device -> ux_device_enum_trans = UX_NULL;
                device -> ux_device_enum_state = UX_HOST_STACK_ENUM_FAIL;
                continue;
            }
            if (status < UX_STATE_NEXT)
            {

                /* Error, retry.  */
                if (trans -> ux_transfer_request_data_pointer)
                {
                    _ux_utility_memory_free(trans -> ux_transfer_request_data_pointer);
                    trans -> ux_transfer_request_data_pointer = UX_NULL;
                }
                if (device -> ux_device_enum_next_state == UX_HOST_STACK_ENUM_CONFIG_DESCR_PARSE)
                {
                    _ux_utility_memory_free(device -> ux_device_enum_inst.ptr);
                    device -> ux_device_enum_inst.ptr = UX_NULL;
                }
                device -> ux_device_enum_trans = UX_NULL;
                device -> ux_device_enum_state = UX_HOST_STACK_ENUM_RETRY;
                continue;
            }

            /* Transfer in progress, wait.  */
            return;

        case UX_HOST_STACK_ENUM_TRANS_LOCK_WAIT:
            trans = device -> ux_device_enum_trans;
            UX_DISABLE
            if (trans -> ux_transfer_request_flags & UX_TRANSFER_FLAG_LOCK)
            {

                /* Keep waiting.  */
                UX_RESTORE;
                return;
            }

            /* Lock it.  */
            trans -> ux_transfer_request_flags |= UX_TRANSFER_FLAG_LOCK;
            UX_RESTORE

            /* Apply next expected state.  */
            device -> ux_device_enum_state = device -> ux_device_enum_next_state;
            continue;

        case UX_HOST_STACK_ENUM_WAIT:
            current_ms = _ux_utility_time_get();
            elapsed_ms = _ux_utility_time_elapsed(current_ms,
                                                  device -> ux_device_enum_wait_start);
            if (elapsed_ms < device -> ux_device_enum_wait_ms)

                /* Keep waiting.  */
                return;

            /* Next pre-defined state.  */
            device -> ux_device_enum_state = device -> ux_device_enum_next_state;
            continue;

        case UX_HOST_STACK_ENUM_RETRY:

            /* Check remaining retry count. */
            if (device -> ux_device_enum_retry > 0)
            {
                device -> ux_device_enum_retry --;

                /* Check if there is unnecessary resource to free.  */
                if (device -> ux_device_packed_configuration &&
                    device -> ux_device_packed_configuration_keep_count == 0)
                {
                    _ux_utility_memory_free(device -> ux_device_packed_configuration);
                    device -> ux_device_packed_configuration = UX_NULL;
                }

                /* Start from port enable delay.  */
                device -> ux_device_enum_next_state = UX_HOST_STACK_ENUM_PORT_RESET;
                device -> ux_device_enum_state = UX_HOST_STACK_ENUM_WAIT;
                device -> ux_device_enum_wait_start = _ux_utility_time_get();
                device -> ux_device_enum_wait_ms =
                        UX_MS_TO_TICK_NON_ZERO(UX_RH_ENUMERATION_RETRY_DELAY);
                continue;
            }

            /* Tried several times, fail case.  */
            /* Fall through.  */
        case UX_HOST_STACK_ENUM_FAIL:

            /* Clear lock anyway.  */
            if (device == _ux_system_host -> ux_system_host_enum_lock)
                _ux_system_host -> ux_system_host_enum_lock = UX_NULL;

            /* Error trap. */
            _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD,
                UX_DEVICE_PARENT_IS_HUB(device) ? UX_SYSTEM_CONTEXT_HUB :
                                                  UX_SYSTEM_CONTEXT_ROOT_HUB,
                UX_DEVICE_ENUMERATION_FAILURE);

            /* If trace is enabled, insert this event into the trace buffer.  */
            UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DEVICE_ENUMERATION_FAILURE,
                UX_DEVICE_PORT_LOCATION_GET(device), 0, 0, UX_TRACE_ERRORS, 0, 0);

            /* Fall through.  */
        case UX_HOST_STACK_ENUM_DONE:
            _ux_host_stack_device_enumerated(device);

            /* We are done now.  */
            /* Fall through.  */
        case UX_HOST_STACK_ENUM_IDLE:

            /* Nothing to run.  */
            return;

        default:

            /* Invalid state, reset.  */
            device -> ux_device_enum_state = UX_STATE_RESET;
        }

        /* Invalid unhandled state.  */
        _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HOST_STACK, UX_INVALID_STATE);

        /* Break the immediate state loop.  */
        immediate_state = UX_FALSE;
    }
}

static inline VOID _ux_host_stack_enum_run(VOID)
{

UX_DEVICE           *enum_device;

    /* Check if there is device pending enumeration.  */
    enum_device = _ux_system_host -> ux_system_host_enum_device;

    /* Run enumeration task for each device.  */
    while(enum_device != UX_NULL)
    {

        /* Run enumeration task on the device.  */
        if ((enum_device -> ux_device_flags & UX_DEVICE_FLAG_PROTECT) == 0)
        {
            enum_device -> ux_device_flags |= UX_DEVICE_FLAG_PROTECT;
            _ux_host_stack_device_enum_run(enum_device);
            enum_device -> ux_device_flags &= ~UX_DEVICE_FLAG_PROTECT;
        }

        /* Check device lock.  */
        if (enum_device -> ux_device_flags & UX_DEVICE_FLAG_LOCK)
        {
            break;
        }

        /* Check next enumerating device.  */
        enum_device = enum_device -> ux_device_enum_next;
    }
}

static inline VOID _ux_host_stack_pending_transfers_run(VOID)
{
UX_TRANSFER         *transfer, *next;
    transfer = _ux_system_host -> ux_system_host_pending_transfers;
    while(transfer)
    {
        next = transfer -> ux_transfer_request_next_pending;
        _ux_host_stack_transfer_run(transfer);
        if (transfer == next || _ux_system_host -> ux_system_host_pending_transfers == next)
            break;
        transfer = next;
    }
}
#endif
