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


/* Include necessary system files.  */

#define UX_SOURCE_CODE

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


/**************************************************************************/
/*                                                                        */
/*  FUNCTION                                               RELEASE        */
/*                                                                        */
/*    _ux_host_stack_new_device_create                    PORTABLE C      */
/*                                                           6.1.12       */
/*  AUTHOR                                                                */
/*                                                                        */
/*    Chaoqiong Xiao, Microsoft Corporation                               */
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*    This function creates a new device on the USB. It may be called     */
/*    either by a hub or by the root hub.                                 */
/*                                                                        */
/*  INPUT                                                                 */
/*                                                                        */
/*    HCD                                   HCD that owns that device     */
/*    device_owner                          Either a root hub instance    */
/*                                            or a hub instance           */
/*    port_index                            Port the new device is mounted*/
/*    device_speed                          Speed at which the device is  */
/*                                            running (low, full, high)   */
/*    port_power_available                  Power available on the root   */
/*                                            or hub port. This value is  */
/*                                            used to ensure that the     */
/*                                            device can be configured    */
/*                                            without creating an         */
/*                                            OVER_CURRENT condition on   */
/*                                            the bus                     */
/*    created_device                        Destination to fill created   */
/*                                            device instance             */
/*                                                                        */
/*  OUTPUT                                                                */
/*                                                                        */
/*    Completion Status                                                   */
/*                                                                        */
/*    An Error code could be an indication that the device could not be   */
/*    created in which case the error is fatal to the enumeration and     */
/*    will not be retried. The error code can also indicate a failure     */
/*    for the device to respond to the GET_DEVICE_DESCRIPTOR. This error  */
/*    is not fatal and the caller should retry the enumeration process    */
/*    after the port to which the device is attached has been reset.      */
/*                                                                        */
/*  CALLS                                                                 */
/*                                                                        */
/*    _ux_host_stack_class_device_scan      Scan class devices            */
/*    _ux_host_stack_class_interface_scan   Scan class interfaces         */
/*    _ux_host_stack_device_address_set     Set device address            */
/*    _ux_host_stack_device_descriptor_read Read device descriptor        */
/*    _ux_host_stack_configuration_enumerate                              */
/*                                          Enumerate device config       */
/*    _ux_host_stack_new_device_get         Get new device                */
/*    _ux_utility_semaphore_create          Create a semaphore            */
/*    (ux_hcd_entry_function)               HCD entry function            */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    USBX Components                                                     */
/*                                                                        */
/**************************************************************************/
UINT  _ux_host_stack_new_device_create(UX_HCD *hcd, UX_DEVICE *device_owner,
                                UINT port_index, UINT device_speed,
                                UINT port_max_power,
                                UX_DEVICE **created_device)
{

UX_DEVICE           *device;
UINT                status;
UX_ENDPOINT         *control_endpoint;


#if UX_MAX_DEVICES > 1
    /* Verify the number of devices attached to the HCD already. Normally a HCD
       can have up to 127 devices but that can be tailored.  */
    if (hcd -> ux_hcd_nb_devices > UX_MAX_USB_DEVICES)
    {

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

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

        return(UX_TOO_MANY_DEVICES);
    }
#endif

    /* Get a new device container to store this new device.  */
    device =  _ux_host_stack_new_device_get();
    if (device == UX_NULL)
    {

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

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

        return(UX_TOO_MANY_DEVICES);
    }

    /* Store the device instance.  */
    *created_device = device;

    /* Increment the number of devices on this bus.  */
    hcd -> ux_hcd_nb_devices++;

    /* If trace is enabled, insert this event into the trace buffer.  */
    UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_NEW_DEVICE_CREATE, hcd, device_owner, port_index, device, UX_TRACE_HOST_STACK_EVENTS, 0, 0)

    /* At this stage the device is attached but not configured.
       we don't have to worry about power consumption yet.
       Initialize the device structure.  */
    device -> ux_device_handle =         (ULONG) (ALIGN_TYPE) device;
    device -> ux_device_state =          UX_DEVICE_ATTACHED;
    device -> ux_device_address =        0;
    device -> ux_device_speed =          device_speed;
    UX_DEVICE_MAX_POWER_SET(device, port_max_power);
    UX_DEVICE_PARENT_SET(device, device_owner);
    UX_DEVICE_HCD_SET(device, hcd);
    UX_DEVICE_PORT_LOCATION_SET(device, port_index);
    device -> ux_device_power_source =   UX_DEVICE_BUS_POWERED;

    /* Create a semaphore for the device. This is to protect endpoint 0 mostly for OTG HNP polling. The initial count is 1 as
       a mutex mechanism.  */
    status =  _ux_host_semaphore_create(&device -> ux_device_protection_semaphore, "ux_host_endpoint0_semaphore", 1);

    /* Check semaphore creation.  */
    if (status != UX_SUCCESS)
    {

        /* Return error. Device resources that have been allocated until this
           point should be freed by the caller via _ux_host_stack_device_resources_free.  */
        return(UX_SEMAPHORE_ERROR);
    }

    /* Initialize the default control endpoint permanently attached
       to the device.  */
    control_endpoint =                               &device -> ux_device_control_endpoint;
    control_endpoint -> ux_endpoint =                (ULONG) (ALIGN_TYPE) control_endpoint;
    control_endpoint -> ux_endpoint_next_endpoint =  UX_NULL;
    control_endpoint -> ux_endpoint_interface =      UX_NULL;
    control_endpoint -> ux_endpoint_device =         device;
    control_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_endpoint = control_endpoint;

    /* Create a semaphore for this endpoint to be attached to its transfer request.  */
    status =  _ux_host_semaphore_create(&control_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_semaphore, "ux_host_transfer_request_semaphore", 0);

    /* Check semaphore creation.  */
    if (status != UX_SUCCESS)
    {

        /* Return error. Device resources that have been allocated until this
           point should be freed by the caller via _ux_host_stack_device_resources_free.  */
        return(UX_SEMAPHORE_ERROR);
    }

    /* If the device is running in high speed the default max packet size for the control endpoint is 64.
       All other speeds the size is 8.  */
    if (device_speed == UX_HIGH_SPEED_DEVICE)
        control_endpoint -> ux_endpoint_descriptor.wMaxPacketSize =  UX_DEFAULT_HS_MPS;
    else
        control_endpoint -> ux_endpoint_descriptor.wMaxPacketSize =  UX_DEFAULT_MPS;

    /* Create the default control endpoint at the HCD level.  */
    status =  hcd -> ux_hcd_entry_function(hcd, UX_HCD_CREATE_ENDPOINT, (VOID *) control_endpoint);

#if defined(UX_HOST_STANDALONE)
    if (status == UX_SUCCESS)
    {

        /* Now control endpoint is ready, set state to running. */
        control_endpoint -> ux_endpoint_state =          UX_ENDPOINT_RUNNING;

        /* Setup default control request timeout.  */
        control_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_timeout_value =
                            UX_MS_TO_TICK_NON_ZERO(UX_CONTROL_TRANSFER_TIMEOUT);

        /* Set default control request mode to wait.  */
        control_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_flags |=
                                                    UX_TRANSFER_FLAG_AUTO_WAIT;
    }

    /* Enumeration steps will be done in task state machine.  */

#else

    /* Going on to do enumeration (requests).  */
    if (status == UX_SUCCESS)
    {

        /* Now control endpoint is ready, set state to running. */
        control_endpoint -> ux_endpoint_state =          UX_ENDPOINT_RUNNING;

        /* Set the address of the device. The first time a USB device is
           accessed, it responds to the address 0. We need to change the address
           to a free device address between 1 and 127 ASAP.  */
        status =  _ux_host_stack_device_address_set(device);
        if (status == UX_SUCCESS)
        {

            /* Get the device descriptor.  */
            status =  _ux_host_stack_device_descriptor_read(device);
            if (status == UX_SUCCESS)
            {

                /* Get the configuration descriptor(s) for the device
                   and parse all the configuration, interface, endpoints...  */
                status =  _ux_host_stack_configuration_enumerate(device);
            }
        }
    }

    /* Check the status of the previous operations. If there was an
       error during any of the phases, the device resources must be
       freed based on if we want to retry.  */
    if (status == UX_SUCCESS)
    {

        /* The device, configuration(s), interface(s), endpoint(s) are
           now in order for this device to work. No configuration is set
           yet. First we need to find a class driver that wants to own
           it. There is no need to have an orphan device in a configured state.   */
        status =  _ux_host_stack_class_device_scan(device);
        if (status == UX_NO_CLASS_MATCH)
        {

            status =  _ux_host_stack_class_interface_scan(device);

        }

        /* 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;
        }

        /* If trace is enabled, register this object.  */
        UX_TRACE_OBJECT_REGISTER(UX_TRACE_HOST_OBJECT_TYPE_DEVICE, hcd, device_owner, port_index, 0);
    }
#endif

    /* Return status. If there's an error, device resources that have been
       allocated until this point should be freed by the caller via _ux_host_stack_device_resources_free.  */
    return(status);
}
