/***********************************************************************
*
*           Copyright 2003 Mentor Graphics Corporation
*                         All Rights Reserved.
*
*           THIS WORK CONTAINS TRADE SECRET AND PROPRIETARY
*             INFORMATION WHICH IS THE PROPERTY OF MENTOR
*             GRAPHICS CORPORATION OR ITS LICENSORS AND IS
*                      SUBJECT TO LICENSE TERMS.
*
************************************************************************

*************************************************************************
*
* FILE NAME
*     nu_usbh_hw_ext.c
*
* COMPONENT
*     USB Host Stack
*
* DESCRIPTION
*     This file contains the implementation of the services of NU_USBH_HW.
*
* DATA STRUCTURES
*     None
*
* FUNCTIONS
*     _NU_USBH_HW_Create        initializes NU_USBH_HW control block.
*     _NU_USBH_HW_Delete        deletes an Host Hardware Driver.
*     _NU_USBH_HW_Initialize    installs USBH LISR for host h/w vector(s).
*     _NU_USBH_HW_Uninitialize  uninstalls USBH LISR for host h/w vector(s).
*
* DEPENDENCIES
*     nu_usb.h                  All USB definitions
*
************************************************************************/
#ifndef USBH_HW_EXT_C
#define USBH_HW_EXT_C

/* ==============  Standard Include Files ============================  */
#include "connectivity/nu_usb.h"

/*************************************************************************
* FUNCTION
*     _NU_USBH_HW_Create
*
* DESCRIPTION
*     This function initializes the NU_USBH_HW control block. NU_USBH_HW is
*     a derivative of NU_USB_HW. This function invokes the create function of
*     NU_USB_HW to initialize the NU_USB_HW portion of the control block.
*
* INPUTS
*     cb                 Pointer to the NU_USBH_HW control block.
*     name               7 character NU_NULL terminated string for the h/w.
*     pool               Pool pointer from which the h/w should meet all its
*                        memory requirements.
*     num_companion_hw   Number of companion controllers the h/w has. USB2.0
*                        controllers, typically those based on EHCI would have
*                        companion controllers to deal with 1.1 speeds. A USB1.1
*                        controller wouldn't generally have companion
*                        controllers and so while creating those h/w, this
*                        parameter can be 0.
*     speed              Speed of the host controllers root hub-
*                        USB_SPEED_HIGH, USB_SPEED_LOW, USB_SPEED_FULL.
*     base_address       The base address of the register set of the controller.
*     vector_number      The vector number for the interrupt generated by the h/w
*     dispatch           Pointer to the dispatch table containing function
*                        pointers of the derivative of NU_USBH_HW that
*                        actually implements some of the NU_USBH_HW services.
*
* OUTPUTS
*     NU_SUCCESS          Indicates successful completion of the service.
*     NU_USB_NOT_PRESENT  Indicates that the maximum number of control blocks
*                         that could be created in the sub system has
*                         exceeded.
*     NU_USB_INVLD_ARG    Invalid arguments
*************************************************************************/
STATUS _NU_USBH_HW_Create (NU_USBH_HW     *cb,
                           CHAR           *name,
                           NU_MEMORY_POOL *pool,
                           UINT8          num_companion_hw,
                           UINT8          speed,
                           VOID           *base_address,
                           INT            vector_number,
                           const VOID     *dispatch)
{
    STATUS         status = NU_SUCCESS;
    NU_USBH_SUBSYS *controller_subsys = &nu_usbh->controller_subsys;
    INT            irq = vector_number;

    NU_USB_PTRCHK(cb);
    NU_USB_MEMPOOLCHK(pool);
    NU_USB_PTRCHK(base_address);
    NU_USB_PTRCHK(dispatch);

    /* Call parent to initialize the base portions of the NU_USBH_HW */
    status = _NU_USB_HW_Create ((NU_USB_HW *) cb,
                           (NU_USB_SUBSYS *) controller_subsys, name,
                           speed, base_address, 1, &irq, dispatch);

    if (NU_SUCCESS != status)
    {
        return (status);
    }

    cb->pool = pool;
    cb->number_companion_controllers = num_companion_hw;
    cb->pending = NU_FALSE;

    return (NU_SUCCESS);
}

/*************************************************************************
* FUNCTION
*     _NU_USBH_HW_Delete
*
* DESCRIPTION
*     This function deletes a specified host hardware driver.
*
* INPUTS
*     cb               pointer to host hardware driver control block.
*
* OUTPUTS
*     NU_SUCCESS       host hardware driver deleted successfully
*
*************************************************************************/
STATUS _NU_USBH_HW_Delete (VOID *cb)
{
    NU_USB_PTRCHK(cb);

    return (_NU_USB_HW_Delete (cb));
}

/*************************************************************************
* FUNCTION
*     _NU_USBH_HW_Initialize
*
* DESCRIPTION
*     This function installs the USBH LISR for each vector of the USB h/w.
*
* INPUTS
*     cb               Pointer to the NU_USBH_HW control block.
*     stack            Pointer to the control block of the host stack.
*
* OUTPUTS
*     NU_SUCCESS        Indicates successful completion of the service.
*     NU_USB_INVLD_ARG  Invalid arguments
*************************************************************************/
STATUS _NU_USBH_HW_Initialize (NU_USB_HW * cb,
                               NU_USB_STACK * stack)
{
    UNSIGNED i;
    STATUS status = NU_SUCCESS;
    NU_USBH_HW *hw;

    NU_USB_PTRCHK(cb);
    NU_USB_PTRCHK(stack);

    hw = (NU_USBH_HW *) cb;

    /* Call base class's Initialize */
    status = _NU_USB_HW_Initialize (cb, stack);

    if (status == NU_SUCCESS)
    {
        for (i = 0; i < hw->controller.num_irq; i++)
        {
            status = USBH_HW_Register_LISR (hw, hw->controller.irq[i]);
            if (status != NU_SUCCESS)
            {
                break;
            }
        }
    }

    return (status);
}

/*************************************************************************
* FUNCTION
*     _NU_USBH_HW_Uninitialize
*
* DESCRIPTION
*     This function deinstalls the USBH LISR for each vector of the USB h/w.
*
* INPUTS
*     cb               Pointer to the NU_USBH_HW control block.
* OUTPUTS
*     NU_SUCCESS          Indicates successful completion of the service.
*     NU_USB_INVLD_ARG    Invalid arguments
*************************************************************************/
STATUS _NU_USBH_HW_Uninitialize (NU_USB_HW * hw)
{
    STATUS status = NU_SUCCESS;
    UNSIGNED i;
    NU_USBH_HW *cb;

    NU_USB_PTRCHK(hw);

    cb = (NU_USBH_HW *) hw;

    for (i = 0; i < cb->controller.num_irq; i++)
    {
        status = USBH_HW_Deregister_LISR (cb, cb->controller.irq[i]);

        if (status != NU_SUCCESS)
        {
            break;
        }
    }

    return (status);
}

#if ( CFG_NU_OS_CONN_USB_COM_STACK_SS_ENABLE == NU_TRUE )

/*************************************************************************
* FUNCTION
*
*       NU_USBH_HW_Update_Device
*
* DESCRIPTION
*
*       This function is used to update the parameters of device as they 
*       are retrieved from the device descriptors. The parameters include
*       System Exit Latency (SEL), Maximum Packet Size, TT time, number of
*       ports in hub device.
*
* INPUTS
*
*       cb                                  Pointer to the NU_USBH_HW
*                                           control block.
*       usb_device                          Pointer to USB device control
*                                           block.
*       packet_size                         Packet size.
*       sel                                 Exit latency value.
*       is_hub                              If device is hub or not.
*       tt_time                             Think time value.
*       num_ports                           Number of ports in case of hub.
*
* OUTPUTS
*
*       NU_SUCCESS                          The service executed
*                                           successfully.
*       NU_USB_NOT_SUPPORTED                This service is not supported
*                                           by the HC.
*       NU_USB_INVLD_ARG                    Indicates the one of the input
*                                           parameters has been incorrect
*                                           AND/OR the event could not be
*                                           processed without an error.
*
*************************************************************************/
STATUS NU_USBH_HW_Update_Device(NU_USBH_HW    *cb,
                                NU_USB_DEVICE *usb_device,
                                UINT8          packet_size,
                                UINT32         sel,
                                BOOLEAN        is_hub,
                                UINT8          tt_time,
                                UINT8          num_ports)
{
    STATUS  status;
    USB_DEV_SS_PARAMS device_params;

    /* Error checking.   */
    NU_USB_PTRCHK(cb);
    NU_USB_PTRCHK(usb_device);

    device_params.device      = usb_device;
    device_params.packet_size = packet_size;
    device_params.sel         = sel;
    device_params.is_hub      = is_hub;
    device_params.tt_time     = tt_time;
    device_params.num_ports   = num_ports;

    /* Invoke the controller function through IOCTL. */
    status = DVC_Dev_Ioctl(((NU_USB_HW *)cb)->dv_handle,
                           (((NU_USB_HW *)cb)->ioctl_base_addr + NU_USBH_IOCTL_UPDATE_DEVICE),
                           (VOID*)&device_params,
                           sizeof(USB_DEV_SS_PARAMS));

    return ( status );
}

/*************************************************************************
* FUNCTION
*
*       NU_USBH_HW_Init_Device
*
* DESCRIPTION
*
*      This function is used to create and initialize the data structures that
*      are required by the Host controller driver when device is connected.
*
* INPUTS
*
*       cb                                  Pointer to the NU_USBH_HW
*                                           control block.
*
* OUTPUTS
*
*       NU_SUCCESS                          The service executed
*                                           successfully.
*       NU_USB_NOT_SUPPORTED                This service is not supported
*                                           by the HC.
*       NU_USB_INVLD_ARG                    Indicates the one of the input
*                                           parameters has been incorrect
*                                           AND/OR the event could not be
*                                           processed without an error.
*
*************************************************************************/
STATUS NU_USBH_HW_Init_Device( NU_USBH_HW  *cb )
{
    STATUS  status;

    /* Error checking.   */
    NU_USB_PTRCHK(cb);

    /* Invoke the controller function through IOCTL. */
    status = DVC_Dev_Ioctl(((NU_USB_HW *)cb)->dv_handle,
                           (((NU_USB_HW *)cb)->ioctl_base_addr + NU_USBH_IOCTL_INIT_DEVICE),
                           NU_NULL,
                           0);

    return ( status );
}

/*************************************************************************
* FUNCTION
*
*       NU_USBH_HW_Dinit_Device
*
* DESCRIPTION
*
*       This IOCTL releases the resources acquired by the device once it
*       is disconnected.
*
* INPUTS
*
*       cb                                  Pointer to the NU_USBH_HW
*                                           control block.
*       function_address                    USB device address.
*
* OUTPUTS
*
*       NU_SUCCESS                          The service executed
*                                           successfully.
*       NU_USB_NOT_SUPPORTED                This service is not supported
*                                           by the HC.
*       NU_USB_INVLD_ARG                    Indicates the one of the input
*                                           parameters has been incorrect
*                                           AND/OR the event could not be
*                                           processed without an error.
*
*************************************************************************/
STATUS NU_USBH_HW_Dinit_Device(NU_USBH_HW  *cb,
                               UINT8        function_address )
{
    STATUS  status;

    /* Error checking.   */
    NU_USB_PTRCHK(cb);

    /* Invoke the controller function through IOCTL. */
    status = DVC_Dev_Ioctl(((NU_USB_HW *)cb)->dv_handle,
                           (((NU_USB_HW *)cb)->ioctl_base_addr + NU_USBH_IOCTL_DEINIT_DEVICE),
                           (VOID*)&function_address,
                           sizeof(UINT8));

    return ( status );
}

/*************************************************************************
* FUNCTION
*
*       NU_USBH_HW_Unstall_Pipe
*
* DESCRIPTION
*
*       This function unstalls endpoints.
*
* INPUTS
*       cb                                  Pointer to the NU_USBH_HW
*                                           control block.
*       bEndpointAddress                    Endpoints address.
*       function_address                    USB device address.
*
* OUTPUTS
*
*       NU_SUCCESS                          The service executed
*                                           successfully.
*       NU_USB_NOT_SUPPORTED                This service is not supported
*                                           by the HC.
*       NU_USB_INVLD_ARG                    Indicates the one of the input
*                                           parameters has been incorrect
*                                           AND/OR the event could not be
*                                           processed without an error.
*
*************************************************************************/
STATUS NU_USBH_HW_Unstall_Pipe(NU_USBH_HW  *cb,
                               UINT8        bEndpointAddress,
                               UINT8        function_address )
{
    STATUS  status;
    USB_EP_INFO ep_info;

    /* Error checking.   */
    NU_USB_PTRCHK(cb);

    ep_info.function_addr   = function_address;
    ep_info.endp_addr       = bEndpointAddress;

    /* Invoke the controller function through IOCTL. */
    status = DVC_Dev_Ioctl(((NU_USB_HW *)cb)->dv_handle,
                           (((NU_USB_HW *)cb)->ioctl_base_addr + NU_USBH_IOCTL_UNSTALL_PIPE),
                           (VOID*)&ep_info,
                           sizeof(USB_EP_INFO));

    return ( status );
}

/*************************************************************************
* FUNCTION
*
*       NU_USBH_HW_Disable_Pipe
*
* DESCRIPTION
*
*       This function stops the endpoint from processing the IRP. After 
*       completion of this fucntion endpoint is in stopped state
*
* INPUTS
*       cb                                  Pointer to the NU_USBH_HW
*                                           control block.
*       bEndpointAddress                    Endpoints address.
*       function_address                    USB device address.
*
* OUTPUTS
*
*       NU_SUCCESS                          The service executed
*                                           successfully.
*       NU_USB_NOT_SUPPORTED                This service is not supported
*                                           by the HC.
*       NU_USB_INVLD_ARG                    Indicates the one of the input
*                                           parameters has been incorrect
*                                           AND/OR the event could not be
*                                           processed without an error.
*
*************************************************************************/
STATUS NU_USBH_HW_Disable_Pipe(NU_USBH_HW  *cb,
                               UINT8        bEndpointAddress,
                               UINT8        function_address )
{
    STATUS  status;
    USB_EP_INFO ep_info;

    /* Error checking.   */
    NU_USB_PTRCHK(cb);

    ep_info.function_addr = function_address;
    ep_info.endp_addr     = bEndpointAddress;

    /* Invoke the controller function through IOCTL. */
    status = DVC_Dev_Ioctl(((NU_USB_HW *)cb)->dv_handle,
                           (((NU_USB_HW *)cb)->ioctl_base_addr + NU_USBH_IOCTL_DISABLE_PIPE),
                           (VOID*)&ep_info,
                           sizeof(USB_EP_INFO));

    return ( status );
}

/*************************************************************************
* FUNCTION
*
*       NU_USBH_HW_Reset_Bandwidth
*
* DESCRIPTION
*
*       This function is used to release the BW acquired by the device in
*       case of error.
*
* INPUTS
*
*       cb                                  Pointer to the NU_USBH_HW
*                                           control block.
*       function_address                    USB device address.
*
* OUTPUTS
*
*       NU_SUCCESS                          The service executed
*                                           successfully.
*       NU_USB_NOT_SUPPORTED                This service is not supported
*                                           by the HC.
*       NU_USB_INVLD_ARG                    Indicates the one of the input
*                                           parameters has been incorrect
*                                           AND/OR the event could not be
*                                           processed without an error.
*
*************************************************************************/
STATUS NU_USBH_HW_Reset_Bandwidth(NU_USBH_HW  *cb,
                                  UINT8        function_address)
{
    STATUS  status;

    /* Error checking.   */
    NU_USB_PTRCHK(cb);

    /* Invoke the controller function through IOCTL. */
    status = DVC_Dev_Ioctl(((NU_USB_HW *)cb)->dv_handle,
                           (((NU_USB_HW *)cb)->ioctl_base_addr + NU_USBH_IOCTL_RESET_BANDWIDTH),
                           (VOID*)&function_address,
                           sizeof(UINT8));

    return ( status );
}

/*************************************************************************
* FUNCTION
*     NU_USBH_HW_Allocate_Bandwidth
*
* DESCRIPTION
*     This function queries the HW for BW as per requirements of active 
*     configuration or alternate settings.
*
* INPUTS
*     cb             Pointer to the NU_USBH_HW control block.
*     usb_device     Pointer to USB device.
*     cnfg           New configuration.
*     curr_alt_set   Current alternate setting.
*     new_alt_set    New alternate setting.
* 
* OUTPUTS
*     NU_SUCCESS            Indicates successful completion of the service.
*     NU_USB_NOT_SUPPORTED  Invalid arguments
*************************************************************************/
STATUS NU_USBH_HW_Allocate_Bandwidth(NU_USBH_STACK    *cb,
                                  NU_USB_DEVICE    *usb_device,
                                  NU_USB_CFG       *cnfg,
                                  NU_USB_ALT_SETTG *curr_alt_set,
                                  NU_USB_ALT_SETTG *new_alt_set)
{
    STATUS status = NU_SUCCESS;
    NU_USB_ALT_SETTG *alt_setting;
    UINT8 intf_index;
    UINT8 i;
    NU_USB_ENDP_DESC  *ep_curr;
    NU_USB_ENDP_DESC  *ep_new;
    UINT8 burst_size = 0;
    NU_USB_SSEPCOMPANION_DESC   *ss_epc_desc;
    UINT16 bytes_per_interval = 0;
    UINT8  SSEndpCompAttrib = 0;
    UINT16 max_pkt_size = 0;

    if (usb_device->function_address == USB_ROOT_HUB)
    {
        return ( NU_SUCCESS );
    }

    /* If bandwidth is required for new configuration.*/
    if ( cnfg )
    {
        for (intf_index = 0;
             intf_index < cnfg->desc->bNumInterfaces; intf_index++)
        {
            alt_setting = &(cnfg->intf[intf_index].alt_settg[0]);

            if ( alt_setting )
            {
                /* Open the endpoints in alternate setting 0. */
                for (i = 0; i < NU_USB_MAX_ENDPOINTS; i++)
                {
                    ep_new = alt_setting->endp[i].desc;
                    ss_epc_desc = alt_setting->endp[i].epcompanion_desc;
                    if (ep_new)
                    {
                        max_pkt_size = ep_new->wMaxPacketSize0;
                        max_pkt_size |= (ep_new->wMaxPacketSize1 << 8);

                        if (ss_epc_desc)
                        {
                            burst_size = ss_epc_desc->bMaxBurst;
                            bytes_per_interval =ss_epc_desc->wBytesPerInterval;
                            SSEndpCompAttrib = ss_epc_desc->bmAttributes;

                            status = NU_USB_HW_Open_SS_Pipe((NU_USB_HW *) usb_device->hw,
                                                             usb_device->function_address,
                                                             ep_new->bEndpointAddress,
                                                             ep_new->bmAttributes,
                                                             usb_device->speed,
                                                             max_pkt_size,
                                                             ep_new->bInterval,
                                                             0,
                                                             burst_size,
                                                             SSEndpCompAttrib,
                                                             bytes_per_interval);
                       }
                       else
                       {
                            status = NU_USB_HW_Open_Pipe((NU_USB_HW *) usb_device->hw,
                                                         usb_device->function_address,
                                                         ep_new->bEndpointAddress,
                                                         ep_new->bmAttributes,
                                                         usb_device->speed,
                                                         max_pkt_size,
                                                         ep_new->bInterval,
                                                         0);
                        }
                        if ( status != NU_SUCCESS )
                        {
                            break;
                        }
                    }
                }
            }
        }
    }

    /* If bandwidth is required for new alternate setting. */
    else if ( new_alt_set)
    {
        if ( curr_alt_set )
        {
            /* Close the endpoints in old alternate setting. */
            for (i = 0; i < curr_alt_set->desc->bNumEndpoints; i++)
            {
               ep_curr = curr_alt_set->endp[i].desc;
               
               if (ep_curr)
               {
                   status = NU_USB_HW_Close_Pipe((NU_USB_HW *) usb_device->hw,
                                                  usb_device->function_address,
                                                  ep_curr->bEndpointAddress );
               }
            }
        }

        /* Open the endpoints in new alternate setting. */
        for (i = 0; i < new_alt_set->desc->bNumEndpoints; i++)
        {
            ep_new = new_alt_set->endp[i].desc;
            ss_epc_desc = new_alt_set->endp[i].epcompanion_desc;

            if ( ep_new )
            {
                max_pkt_size = ep_new->wMaxPacketSize0;
                max_pkt_size |= (ep_new->wMaxPacketSize1 << 8);

                if (ss_epc_desc)
                {
                    /* Open endpoint call need to add here. */
                    burst_size = ss_epc_desc->bMaxBurst;
                    bytes_per_interval =ss_epc_desc->wBytesPerInterval;
                    SSEndpCompAttrib =ss_epc_desc->bmAttributes;

                    status = NU_USB_HW_Open_SS_Pipe((NU_USB_HW *) usb_device->hw,
                                                    usb_device->function_address,
                                                    ep_new->bEndpointAddress,
                                                    ep_new->bmAttributes,
                                                    usb_device->speed,
                                                    max_pkt_size,
                                                    ep_new->bInterval,
                                                    0,
                                                    burst_size,
                                                    SSEndpCompAttrib,
                                                    bytes_per_interval);
               }
               else
               {
                    status = NU_USB_HW_Open_Pipe((NU_USB_HW *) usb_device->hw,
                                                 usb_device->function_address,
                                                 ep_new->bEndpointAddress,
                                                 ep_new->bmAttributes,
                                                 usb_device->speed,
                                                 max_pkt_size,
                                                 ep_new->bInterval,
                                                 0);
               }
               if ( status != NU_SUCCESS )
               {
                    break;
               }
            }
        }
    }

	if ( status != NU_SUCCESS )
	{
	    (VOID)NU_USBH_HW_Reset_Bandwidth( (NU_USBH_HW *) usb_device->hw,
                                           usb_device->function_address );
	}

    return ( status );
}

/*************************************************************************
* FUNCTION
*     NU_USBH_HW_Update_Exit_Latency
*
* DESCRIPTION
*     This function calculates U1 and U2 exit latencies for host initiated
*     link transitions.
*
* INPUTS
*     cb               Pointer to the NU_USBH_HW control block.
*     usb_device       Pointer to USB.
*     u1_exit_latency  U1 exit latency for host intiated transfer.
*     u2_exit_latency  U2 exit latency for host intiated transfer.
* 
* OUTPUTS
*     NU_SUCCESS            Indicates successful completion of the service.
*     NU_USB_NOT_SUPPORTED  Invalid arguments
*************************************************************************/
STATUS NU_USBH_HW_Update_Exit_Latency(NU_USBH_HW    *hw,
                                   NU_USBH_STACK *cb,
                                   NU_USB_DEVICE *dev,
                                   UINT8 *latency )
{
    STATUS              status        = NU_SUCCESS;
    NU_USB_DEVICE       *device       = NU_NULL;
    NU_USB_BOS          *bos          = NU_NULL;
    NU_USBH_DEVICE_HUB  *hub          = NU_NULL; 
    UINT8               u1_device_lat = 0;
    UINT8               u1_parent_lat = 0;
    UINT16              u2_device_lat = 0;
    UINT16              u2_parent_lat = 0;
    UINT16              u1_latency    = 0;
    UINT16              u2_latency    = 0;

    NU_USB_PTRCHK(cb);
    NU_USB_PTRCHK(dev);

    device = dev;

    do
    {
        /* Get the BOS descriptor handle. */
        status = NU_USB_DEVICE_Get_BOS(device, &bos);

        /* Get the U1 and U2 exit latencies of device from the SuperSpeed
         * device capability descriptor.
         */
        if (status == NU_SUCCESS)
        {
            status = NU_USB_DEVCAP_SuprSpd_Get_U1ExitLat(bos, &u1_device_lat);

            if ( status == NU_SUCCESS )
            {
                status = NU_USB_DEVCAP_SuprSpd_Get_U2ExitLat(bos, &u2_device_lat);

                if ( status == NU_SUCCESS )
                {
                    /* Get the parent device BOS descriptor . */
                    status = NU_USB_DEVICE_Get_BOS(device->parent, &bos);

                    if ( status == NU_SUCCESS )
                    {
                        status = NU_USB_DEVCAP_SuprSpd_Get_U1ExitLat(bos, &u1_parent_lat);

                        if ( status == NU_SUCCESS )
                        {
                            status = NU_USB_DEVCAP_SuprSpd_Get_U2ExitLat(bos, &u2_parent_lat);

                            if ( status == NU_SUCCESS )
                            {
                                if ( u1_parent_lat > u1_device_lat )
                                {
                                    u1_latency += u1_parent_lat;
                                }
                                else
                                {
                                    u1_latency += u1_device_lat;
                                }

                                if ( u2_parent_lat > u2_device_lat )
                                {
                                    u2_latency += u2_parent_lat;
                                }
                                else
                                {
                                    u2_latency += u2_device_lat;
                                }

                                hub = usb_find_hub( &cb->hub_driver, device->parent);

                                if ( hub )
                                {
                                    u1_latency += hub->hub30_desc->bHubHdrDecLat;
                                    u2_latency += hub->hub30_desc->bHubHdrDecLat;
                                }
                                else
                                {
                                    status = NU_USB_INVLD_HUB;
                                    break;
                                }

                                device = device->parent;
                            }
                        }
                    }
                }
            }
        }

    }   while ( !USBH_IS_ROOT_HUB(device) && ( status == NU_SUCCESS) );

    if ( status == NU_SUCCESS )
    {

#if USB_PMG_U2_TRANSITION_ENABLE
        status = NU_USBH_HW_Update_SEL( hw, dev, u2_latency );
        *latency = u2_latency;
#else
        status = NU_USBH_HW_Update_SEL( hw, dev, u1_latency );
        *latency = u1_latency;
#endif
    }

    return ( status );
}

#endif /* CFG_NU_OS_CONN_USB_COM_STACK_SS_ENABLE == NU_TRUE */
/************************************************************************/

#endif /* USBH_HW_EXT_C */
/* ======================  End Of File  =============================== */
