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


/* Include necessary system files.  */

#define UX_SOURCE_CODE

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


#if UX_MAX_DEVICES > 1

#if defined(UX_HOST_STANDALONE)
UINT _ux_host_class_hub_descriptor_parse(UX_HOST_CLASS_HUB *hub, UCHAR *descriptor);
UINT
#else
static inline UINT
#endif
_ux_host_class_hub_descriptor_parse(UX_HOST_CLASS_HUB *hub, UCHAR *descriptor)
{
UINT            status = UX_SUCCESS;
ULONG           tt_protocols; /* (bDeviceProtocol << 8) | bInterfaceProtocol  */
ULONG           port_index;

    /* Parse the device descriptor and create the local descriptor.  */
    _ux_utility_descriptor_parse(descriptor, _ux_system_hub_descriptor_structure, UX_HUB_DESCRIPTOR_ENTRIES,
                                                            (UCHAR *) &hub -> ux_host_class_hub_descriptor);

    /* Check the protocol used by the HUB. This will indicate if the HUB supports multiple TTs in high speed mode.  */
    tt_protocols = ((ULONG)hub -> ux_host_class_hub_device -> ux_device_descriptor.bDeviceProtocol << 8) |
                    hub -> ux_host_class_hub_interface -> ux_interface_descriptor.bInterfaceProtocol;
    switch(tt_protocols)
    {

    /* 1. A hub operating at full-/low-speed has a device descriptor with a bDeviceProtocol field set to zero(0)
     *    and an interface descriptor with a bInterfaceProtocol field set to zero(0).
     */
    case (UX_HOST_CLASS_HUB_PROTOCOL_FS << 8):

        /* In the case of full speed hub, there are no TTs to declare */
        break;

    /* 2. A hub that has a single TT must set the bDeviceProtocol field of the device descriptor to one(1) and
     *    the interface descriptor bInterfaceProtocol field set to 0.
     * 3. A multiple TT hub must set the bDeviceProtocol field of the device descriptor to two (2).
     *    The first interface descriptor has the bInterfaceProtocol field set to one(1).
     */
    case (UX_HOST_CLASS_HUB_PROTOCOL_SINGLE_TT << 8): /* Fall through.  */
    case (UX_HOST_CLASS_HUB_PROTOCOL_MULTIPLE_TT << 8) | UX_HOST_CLASS_HUB_PROTOCOL_SINGLE_TT:

        /* Single TT hub or single TT interface.  */
        /* Check if current setting supports this Hub.  */
        if (hub -> ux_host_class_hub_descriptor.bNbPorts > UX_MAX_TT)
        {

            status = UX_TOO_MANY_HUB_PORTS;
            break;
        }

        /* In a single TT state, all the downstream ports report to the same
           TT and share the 1.1 USB segment bandwidth. This is a very crude
           but working method, we simply set all the ports bits to the first
           TT.  */
        hub -> ux_host_class_hub_device -> ux_device_hub_tt[0].ux_hub_tt_port_mapping =   UX_TT_MASK;
        hub -> ux_host_class_hub_device -> ux_device_hub_tt[0].ux_hub_tt_max_bandwidth =  UX_TT_BANDWIDTH;
        break;

    /* 3. A multiple TT hub must set the bDeviceProtocol field of the device descriptor to two (2).
        *    The first interface descriptor has the bInterfaceProtocol field set to one(1).
        *    Such a hub also has a second interface descriptor where the bInterfaceProtocol is set to two(2).
        */
    case (UX_HOST_CLASS_HUB_PROTOCOL_MULTIPLE_TT << 8) | UX_HOST_CLASS_HUB_PROTOCOL_MULTIPLE_TT:

        /* Multiple TTs.  */
        /* Check if current setting supports this Hub.  */
        if (hub -> ux_host_class_hub_descriptor.bNbPorts > UX_MAX_TT)
        {

            status = UX_TOO_MANY_HUB_PORTS;
            break;
        }

        /* In the case of multiple TTs, each downstream port can sustain the USB 1.1
            max bandwidth and therefore we allocate one TT per port with that bandwidth.  */
        for (port_index = 0; port_index < hub -> ux_host_class_hub_descriptor.bNbPorts; port_index++)
        {

            hub -> ux_host_class_hub_device -> ux_device_hub_tt[port_index].ux_hub_tt_port_mapping =   (ULONG)(1 << port_index);
            hub -> ux_host_class_hub_device -> ux_device_hub_tt[port_index].ux_hub_tt_max_bandwidth =  UX_TT_BANDWIDTH;
        }
        break;

    default: /* Invalid bDeviceProtocol and bInterfaceProtocol pair.  */

        /* We should never get here. In this case the protocol value of the HUB is illegal.  */
        status =  UX_DESCRIPTOR_CORRUPTED;
    }

    /* Return status.  */
    return(status);
}
#endif


/**************************************************************************/
/*                                                                        */
/*  FUNCTION                                               RELEASE        */
/*                                                                        */
/*    _ux_host_class_hub_descriptor_get                   PORTABLE C      */
/*                                                           6.3.0        */
/*  AUTHOR                                                                */
/*                                                                        */
/*    Chaoqiong Xiao, Microsoft Corporation                               */
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*    This function obtains the HUB descriptor. This descriptor contains  */
/*    the number of downstream ports and the power characteristics of the */
/*    HUB (self powered or bus powered).                                  */
/*                                                                        */
/*  INPUT                                                                 */
/*                                                                        */
/*    hub                                   Pointer to HUB                */
/*                                                                        */
/*  OUTPUT                                                                */
/*                                                                        */
/*    Completion Status                                                   */
/*                                                                        */
/*  CALLS                                                                 */
/*                                                                        */
/*    _ux_host_stack_transfer_request       Process transfer request      */
/*    _ux_utility_memory_allocate           Allocate memory block         */
/*    _ux_utility_memory_free               Release memory block          */
/*    _ux_utility_descriptor_parse          Parse descriptor              */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    HUB Class                                                           */
/*                                                                        */
/**************************************************************************/
UINT  _ux_host_class_hub_descriptor_get(UX_HOST_CLASS_HUB *hub)
{

UCHAR           *descriptor;
UX_ENDPOINT     *control_endpoint;
UX_TRANSFER     *transfer_request;
UINT            status;


    /* We need to get the default control endpoint transfer request pointer.  */
    control_endpoint =  &hub -> ux_host_class_hub_device -> ux_device_control_endpoint;
    transfer_request =  &control_endpoint -> ux_endpoint_transfer_request;

    /* Need to allocate memory for the descriptor.  */
    descriptor =  _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HUB_DESCRIPTOR_LENGTH);
    if (descriptor == UX_NULL)
        return(UX_MEMORY_INSUFFICIENT);

    /* 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_HUB_DESCRIPTOR_LENGTH;
    transfer_request -> ux_transfer_request_function =          UX_HOST_CLASS_HUB_GET_DESCRIPTOR;
    transfer_request -> ux_transfer_request_type =              UX_REQUEST_IN | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_DEVICE;
    transfer_request -> ux_transfer_request_value =             (UX_HUB_DESCRIPTOR_ITEM << 8);
    transfer_request -> ux_transfer_request_index =             0;

#if defined(UX_HOST_STANDALONE)

    /* Save allocated buffer pointer.  */
    hub -> ux_host_class_hub_allocated = descriptor;

    /* Link to running transfer.  */
    UX_TRANSFER_STATE_RESET(transfer_request);
    hub -> ux_host_class_hub_transfer = transfer_request;
    status = UX_SUCCESS;
#else

    /* Send request to HCD layer.  */
    status =  _ux_host_stack_transfer_request(transfer_request);

    /* Did the transfer succeed?  */
    if (status == UX_SUCCESS)
    {

        /* Is the length valid?  */
        if (transfer_request -> ux_transfer_request_actual_length == UX_HUB_DESCRIPTOR_LENGTH)
        {

#if UX_MAX_DEVICES > 1
            status = _ux_host_class_hub_descriptor_parse(hub, descriptor);
            if (status != UX_SUCCESS)
            {

                /* Error trap. */
                _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HUB, status);

                /* If trace is enabled, insert this event into the trace buffer.  */
                UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, status, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0)
            }
#endif
        }
        else
        {

            /* Error trap. */
            _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HUB, 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)

            /* The descriptor must be corrupted if we got an invalid length.  */
            status =  UX_DESCRIPTOR_CORRUPTED;
        }
    }

    /* Free the memory for the descriptor.  */
    _ux_utility_memory_free(descriptor);
#endif

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