/*
 * Copyright (c) 2015 - 2016, Freescale Semiconductor, Inc.
 * Copyright 2016,2019 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "usb_host_config.h"
#if ((defined USB_HOST_CONFIG_HUB) && (USB_HOST_CONFIG_HUB))
#include "usb_host.h"
#include "usb_host_hub.h"

/*******************************************************************************
 * Definitions
 ******************************************************************************/

/*******************************************************************************
 * Prototypes
 ******************************************************************************/
/*!
 * @brief hub control transfer callback.
 *
 * @param param       callback parameter.
 * @param transfer    callback transfer.
 * @param status      transfer status.
 */
static void USB_HostHubControlCallback(void *param, usb_host_transfer_t *transfer, usb_status_t status);

/*!
 * @brief hub interrupt in transfer callback.
 *
 * @param param       callback parameter.
 * @param transfer    callback transfer.
 * @param status      transfer status.
 */
static void USB_HostHubInPipeCallback(void *param, usb_host_transfer_t *transfer, usb_status_t status);

/*!
 * @brief USB_HostHubSendPortReset's transfer callback.
 *
 * @param param       callback parameter.
 * @param transfer    callback transfer.
 * @param status      transfer status.
 */
static void USB_HostHubResetCallback(void *param, usb_host_transfer_t *transfer, usb_status_t status);

/*!
 * @brief hub control transfer common code.
 *
 * @param classHandle   the class handle.
 * @param requestType   request type.
 * @param request        setup packet request field.
 * @param wvalue         setup packet wValue field.
 * @param windex         setup packet wIndex field.
 * @param buffer         the buffer pointer.
 * @param bufferLength     the buffer length.
 * @param callbackFn    this callback is called after this function completes.
 * @param callbackParam the first parameter in the callback function.
 *
 * @return kStatus_USB_Success or error codes.
 */
static usb_status_t USB_HostHubClassRequestCommon(usb_host_class_handle classHandle,
                                                  uint8_t requestType,
                                                  uint8_t request,
                                                  uint16_t wvalue,
                                                  uint16_t windex,
                                                  uint8_t *buffer,
                                                  uint16_t bufferLength,
                                                  transfer_callback_t callbackFn,
                                                  void *callbackParam);

/*******************************************************************************
 * Variables
 ******************************************************************************/

/*******************************************************************************
 * Code
 ******************************************************************************/

static void USB_HostHubControlCallback(void *param, usb_host_transfer_t *transfer, usb_status_t status)
{
    usb_host_hub_instance_t *hubInstance = (usb_host_hub_instance_t *)param;

    hubInstance->controlTransfer = NULL;
    if (NULL != hubInstance->controlCallbackFn)
    {
        /* callback to application, callback function is initialized in the USB_HostPrinterControl,
        USB_HostPrinterSetInterface
        or USB_HostHubClassRequestCommon, but is the same function */
        hubInstance->controlCallbackFn(hubInstance->controlCallbackParam, transfer->transferBuffer,
                                       transfer->transferSofar, status); /* callback to application */
    }
    (void)USB_HostFreeTransfer(hubInstance->hostHandle, transfer);
}

static void USB_HostHubInPipeCallback(void *param, usb_host_transfer_t *transfer, usb_status_t status)
{
    usb_host_hub_instance_t *hubInstance = (usb_host_hub_instance_t *)param;

    if (NULL != hubInstance->inCallbackFn)
    {
        /* callback to application, callback function is initialized in the USB_HostHubInterruptRecv */
        hubInstance->inCallbackFn(hubInstance->inCallbackParam, transfer->transferBuffer, transfer->transferSofar,
                                  status); /* callback to application */
    }
    (void)USB_HostFreeTransfer(hubInstance->hostHandle, transfer);
}

static void USB_HostHubResetCallback(void *param, usb_host_transfer_t *transfer, usb_status_t status)
{
    usb_host_hub_instance_t *hubInstance = (usb_host_hub_instance_t *)param;

    /* note: there is not callback to application, the re-enumeration will start automatically after reset. */
    (void)USB_HostFreeTransfer(hubInstance->hostHandle, transfer);
}

static usb_status_t USB_HostHubClassRequestCommon(usb_host_class_handle classHandle,
                                                  uint8_t requestType,
                                                  uint8_t request,
                                                  uint16_t wvalue,
                                                  uint16_t windex,
                                                  uint8_t *buffer,
                                                  uint16_t bufferLength,
                                                  transfer_callback_t callbackFn,
                                                  void *callbackParam)
{
    usb_host_hub_instance_t *hubInstance = (usb_host_hub_instance_t *)classHandle;
    usb_host_transfer_t *transfer;

    if (hubInstance->controlTransfer != NULL)
    {
        return kStatus_USB_Busy;
    }

    /* get transfer */
    if (USB_HostMallocTransfer(hubInstance->hostHandle, &transfer) != kStatus_USB_Success)
    {
#ifdef HOST_ECHO
        usb_echo("error to get transfer\r\n");
#endif
        return kStatus_USB_Error;
    }

    /* save hub application callback */
    hubInstance->controlCallbackFn    = callbackFn;
    hubInstance->controlCallbackParam = callbackParam;

    /* initialize transfer */
    transfer->transferBuffer             = buffer;
    transfer->transferLength             = bufferLength;
    transfer->callbackFn                 = USB_HostHubControlCallback;
    transfer->callbackParam              = hubInstance;
    transfer->setupPacket->bmRequestType = requestType;
    transfer->setupPacket->bRequest      = request;
    transfer->setupPacket->wValue        = USB_SHORT_TO_LITTLE_ENDIAN(wvalue);
    transfer->setupPacket->wIndex        = USB_SHORT_TO_LITTLE_ENDIAN(windex);
    transfer->setupPacket->wLength       = USB_SHORT_TO_LITTLE_ENDIAN(bufferLength);

    /* send transfer */
    if (USB_HostSendSetup(hubInstance->hostHandle, hubInstance->controlPipe, transfer) != kStatus_USB_Success)
    {
#ifdef HOST_ECHO
        usb_echo("Error in hid get report descriptor\r\n");
#endif
        (void)USB_HostFreeTransfer(hubInstance->hostHandle, transfer);
        return kStatus_USB_Error;
    }
    hubInstance->controlTransfer = transfer; /* record the on-going setup transfer */
    return kStatus_USB_Success;
}

usb_status_t USB_HostHubInit(usb_device_handle deviceHandle, usb_host_class_handle *classHandle)
{
    /* malloc the hub instance */
    usb_host_hub_instance_t *hubInstance =
        (usb_host_hub_instance_t *)OSA_MemoryAllocate(sizeof(usb_host_hub_instance_t));
    uint32_t *temp;
    uint32_t infoValue;

    if (hubInstance == NULL)
    {
        return kStatus_USB_AllocFail;
    }

#if ((defined(USB_HOST_CONFIG_BUFFER_PROPERTY_CACHEABLE)) && (USB_HOST_CONFIG_BUFFER_PROPERTY_CACHEABLE > 0U))
    hubInstance->hubDescriptor    = (uint8_t *)SDK_Malloc(7 + (USB_HOST_HUB_MAX_PORT >> 3) + 1, USB_CACHE_LINESIZE);
    hubInstance->portStatusBuffer = (uint8_t *)SDK_Malloc(4, USB_CACHE_LINESIZE);
    hubInstance->hubStatusBuffer  = (uint8_t *)SDK_Malloc(4, USB_CACHE_LINESIZE);
    hubInstance->hubBitmapBuffer  = (uint8_t *)SDK_Malloc((USB_HOST_HUB_MAX_PORT >> 3) + 1, USB_CACHE_LINESIZE);
#endif

    /* initialize hub instance structure */
    hubInstance->deviceHandle    = deviceHandle;
    hubInstance->interfaceHandle = NULL;
    (void)USB_HostHelperGetPeripheralInformation(deviceHandle, (uint32_t)kUSB_HostGetHostHandle, &infoValue);
    temp                    = (uint32_t *)infoValue;
    hubInstance->hostHandle = (usb_host_handle)temp;
    (void)USB_HostHelperGetPeripheralInformation(deviceHandle, (uint32_t)kUSB_HostGetDeviceControlPipe, &infoValue);
    temp                     = (uint32_t *)infoValue;
    hubInstance->controlPipe = (usb_host_pipe_handle)temp;
    (void)USB_HostHelperGetPeripheralInformation(deviceHandle, (uint32_t)kUSB_HostGetDeviceLevel, &infoValue);
    hubInstance->hubLevel = (uint8_t)infoValue;

    *classHandle = hubInstance; /* return the hub class handle */
    return kStatus_USB_Success;
}

usb_status_t USB_HostHubSetInterface(usb_host_class_handle classHandle,
                                     usb_host_interface_handle interfaceHandle,
                                     uint8_t alternateSetting,
                                     transfer_callback_t callbackFn,
                                     void *callbackParam)
{
    usb_status_t status;
    usb_host_hub_instance_t *hubInstance = (usb_host_hub_instance_t *)classHandle;
    usb_host_interface_t *interface      = (usb_host_interface_t *)interfaceHandle;
    usb_descriptor_endpoint_t *epDesc    = NULL;
    usb_host_pipe_init_t pipeInit;
    uint8_t epIndex;

    if (classHandle == NULL)
    {
        return kStatus_USB_InvalidHandle;
    }

    hubInstance->interfaceHandle = interfaceHandle; /* save the interface handle */

    /* notify the host driver that the interface is used by class */
    status = USB_HostOpenDeviceInterface(hubInstance->deviceHandle, interfaceHandle);
    if (status != kStatus_USB_Success)
    {
        return status;
    }

    /* close opened hub interrupt pipe */
    if (hubInstance->interruptPipe != NULL)
    {
        status = USB_HostCancelTransfer(hubInstance->hostHandle, hubInstance->interruptPipe, NULL);
        if (status != kStatus_USB_Success)
        {
#ifdef HOST_ECHO
            usb_echo("error when Cancel pipe\r\n");
#endif
        }
        status = USB_HostClosePipe(hubInstance->hostHandle, hubInstance->interruptPipe);
        if (status != kStatus_USB_Success)
        {
#ifdef HOST_ECHO
            usb_echo("error when close pipe\r\n");
#endif
        }
        hubInstance->interruptPipe = NULL;
    }

    /* open hub interrupt pipe */
    for (epIndex = 0; epIndex < interface->epCount; ++epIndex)
    {
        epDesc = interface->epList[epIndex].epDesc;
        if (((epDesc->bEndpointAddress & USB_DESCRIPTOR_ENDPOINT_ADDRESS_DIRECTION_MASK) ==
             USB_DESCRIPTOR_ENDPOINT_ADDRESS_DIRECTION_IN) &&
            ((epDesc->bmAttributes & USB_DESCRIPTOR_ENDPOINT_ATTRIBUTE_TYPE_MASK) == USB_ENDPOINT_INTERRUPT))
        {
            /* get pipe information from endpoint descriptor */
            pipeInit.devInstance     = hubInstance->deviceHandle;
            pipeInit.pipeType        = USB_ENDPOINT_INTERRUPT;
            pipeInit.direction       = USB_IN;
            pipeInit.endpointAddress = (epDesc->bEndpointAddress & USB_DESCRIPTOR_ENDPOINT_ADDRESS_NUMBER_MASK);
            pipeInit.interval        = epDesc->bInterval;
            pipeInit.maxPacketSize   = (uint16_t)((USB_SHORT_FROM_LITTLE_ENDIAN_ADDRESS(epDesc->wMaxPacketSize) &
                                                 USB_DESCRIPTOR_ENDPOINT_MAXPACKETSIZE_SIZE_MASK));
            pipeInit.numberPerUframe = (uint8_t)((USB_SHORT_FROM_LITTLE_ENDIAN_ADDRESS(epDesc->wMaxPacketSize) &
                                                  USB_DESCRIPTOR_ENDPOINT_MAXPACKETSIZE_MULT_TRANSACTIONS_MASK));
            pipeInit.nakCount        = USB_HOST_CONFIG_MAX_NAK;

            /* open hub interrupt in pipe */
            status = USB_HostOpenPipe(hubInstance->hostHandle, &hubInstance->interruptPipe, &pipeInit);
            if (status != kStatus_USB_Success)
            {
#ifdef HOST_ECHO
                usb_echo("usb_host_hid_set_interface fail to open pipe\r\n");
#endif
                return kStatus_USB_Error;
            }
            break;
        }
    }

    /* hub don't support alternatesetting that is not 0 */
    if (alternateSetting == 0U)
    {
        if (callbackFn != NULL)
        {
            callbackFn(callbackParam, NULL, 0, kStatus_USB_Success);
        }
    }
    else
    {
#ifdef HOST_ECHO
        usb_echo("host don't support alternate setting\r\n");
#endif
        return kStatus_USB_Error;
    }

    return status;
}

usb_status_t USB_HostHubDeinit(usb_device_handle deviceHandle, usb_host_class_handle classHandle)
{
    usb_host_hub_instance_t *hubInstance = (usb_host_hub_instance_t *)classHandle;
    usb_status_t status                  = kStatus_USB_Success;
    if (deviceHandle == NULL)
    {
        return kStatus_USB_InvalidHandle;
    }

    if (classHandle != NULL)
    {
        /* close opened hub interrupt pipe */
        if (hubInstance->interruptPipe != NULL)
        {
            status = USB_HostCancelTransfer(hubInstance->hostHandle, hubInstance->interruptPipe, NULL);
            if (status != kStatus_USB_Success)
            {
#ifdef HOST_ECHO
                usb_echo("error when Cancel pipe\r\n");
#endif
            }
            status = USB_HostClosePipe(hubInstance->hostHandle, hubInstance->interruptPipe);
            if (status != kStatus_USB_Success)
            {
#ifdef HOST_ECHO
                usb_echo("hub close interrupt pipe error\r\n");
#endif
            }
            hubInstance->interruptPipe = NULL;
        }

        /* cancel control transfer if exist */
        if ((hubInstance->controlPipe != NULL) && (hubInstance->controlTransfer != NULL))
        {
            status =
                USB_HostCancelTransfer(hubInstance->hostHandle, hubInstance->controlPipe, hubInstance->controlTransfer);
        }

        /* notify host driver that the interface will not be used */
        (void)USB_HostCloseDeviceInterface(deviceHandle, hubInstance->interfaceHandle);
#if ((defined(USB_HOST_CONFIG_BUFFER_PROPERTY_CACHEABLE)) && (USB_HOST_CONFIG_BUFFER_PROPERTY_CACHEABLE > 0U))
        SDK_Free(hubInstance->hubDescriptor);
        SDK_Free(hubInstance->portStatusBuffer);
        SDK_Free(hubInstance->hubStatusBuffer);
        SDK_Free(hubInstance->hubBitmapBuffer);
#endif
        OSA_MemoryFree(hubInstance);
    }
    else
    {
        /* notify host driver that the interface will not be used */
        (void)USB_HostCloseDeviceInterface(deviceHandle, NULL);
    }

    return status;
}

usb_status_t USB_HostHubInterruptRecv(usb_host_class_handle classHandle,
                                      uint8_t *buffer,
                                      uint16_t bufferLength,
                                      transfer_callback_t callbackFn,
                                      void *callbackParam)
{
    usb_host_hub_instance_t *hubInstance = (usb_host_hub_instance_t *)classHandle;
    usb_host_transfer_t *transfer;

    if (classHandle == NULL)
    {
        return kStatus_USB_InvalidHandle;
    }

    /* get transfer */
    if (USB_HostMallocTransfer(hubInstance->hostHandle, &transfer) != kStatus_USB_Success)
    {
#ifdef HOST_ECHO
        usb_echo("error to get transfer\r\n");
#endif
        return kStatus_USB_Error;
    }

    /* save hub application callback */
    hubInstance->inCallbackFn    = callbackFn;
    hubInstance->inCallbackParam = callbackParam;

    /* initialize transfer */
    transfer->transferBuffer = buffer;
    transfer->transferLength = bufferLength;
    transfer->callbackFn     = USB_HostHubInPipeCallback;
    transfer->callbackParam  = hubInstance;

    /* call host driver API to receive data */
    if (USB_HostRecv(hubInstance->hostHandle, hubInstance->interruptPipe, transfer) != kStatus_USB_Success)
    {
#ifdef HOST_ECHO
        usb_echo("failed to USB_HostRecv\r\n");
#endif
        (void)USB_HostFreeTransfer(hubInstance->hostHandle, transfer);
        return kStatus_USB_Error;
    }

    return kStatus_USB_Success;
}

usb_status_t USB_HostHubSendPortReset(usb_host_class_handle classHandle, uint8_t portNumber)
{
    usb_host_hub_instance_t *hubInstance = (usb_host_hub_instance_t *)classHandle;
    usb_host_transfer_t *transfer;

    if (classHandle == NULL)
    {
        return kStatus_USB_InvalidHandle;
    }

    /* get transfer */
    if (USB_HostMallocTransfer(hubInstance->hostHandle, &transfer) != kStatus_USB_Success)
    {
#ifdef HOST_ECHO
        usb_echo("error to get transfer\r\n");
#endif
        return kStatus_USB_Busy;
    }

    /* initialize transfer */
    transfer->transferBuffer = NULL;
    transfer->transferLength = 0;
    transfer->callbackFn     = USB_HostHubResetCallback;
    transfer->callbackParam  = hubInstance;
    transfer->setupPacket->bmRequestType =
        USB_REQUEST_TYPE_DIR_OUT | USB_REQUEST_TYPE_TYPE_CLASS | USB_REQUEST_TYPE_RECIPIENT_OTHER;
    transfer->setupPacket->bRequest = USB_REQUEST_STANDARD_SET_FEATURE;
    transfer->setupPacket->wValue   = USB_SHORT_TO_LITTLE_ENDIAN(PORT_RESET);
    transfer->setupPacket->wIndex   = USB_SHORT_TO_LITTLE_ENDIAN(portNumber);
    transfer->setupPacket->wLength  = 0;

    /* send the transfer */
    if (USB_HostSendSetup(hubInstance->hostHandle, hubInstance->controlPipe, transfer) != kStatus_USB_Success)
    {
#ifdef HOST_ECHO
        usb_echo("Error in hid get report descriptor\r\n");
#endif
        (void)USB_HostFreeTransfer(hubInstance->hostHandle, transfer);
        return kStatus_USB_Error;
    }
    return kStatus_USB_Success;
}

/*!
 * @brief hub get descriptor.
 *
 * This function implements get hub descriptor specific request.
 *
 * @param classHandle   the class handle.
 * @param buffer         the buffer pointer.
 * @param bufferLength     the buffer length.
 * @param callbackFn    this callback is called after this function completes.
 * @param callbackParam the first parameter in the callback function.
 *
 * @retval kStatus_USB_Success        send successfully.
 * @retval kStatus_USB_InvalidHandle  The classHandle is NULL pointer.
 * @retval kStatus_USB_Busy           There is no idle transfer.
 * @retval kStatus_USB_Error          pipe is not initialized.
 *                                    Or, send transfer fail, please reference to USB_HostSendSetup.
 */
usb_status_t USB_HostHubGetDescriptor(usb_host_class_handle classHandle,
                                      uint8_t *buffer,
                                      uint16_t bufferLength,
                                      transfer_callback_t callbackFn,
                                      void *callbackParam)
{
    return USB_HostHubClassRequestCommon(
        classHandle, USB_REQUEST_TYPE_DIR_IN | USB_REQUEST_TYPE_TYPE_CLASS | USB_REQUEST_TYPE_RECIPIENT_DEVICE,
        USB_REQUEST_STANDARD_GET_DESCRIPTOR, 0x00, 0, buffer, bufferLength, callbackFn, callbackParam);
}

/*!
 * @brief hub clear feature.
 *
 * This function implements clear hub feature specific request.
 *
 * @param classHandle   the class handle.
 * @param buffer         the buffer pointer.
 * @param bufferLength     the buffer length.
 * @param callbackFn    this callback is called after this function completes.
 * @param callbackParam the first parameter in the callback function.
 *
 * @retval kStatus_USB_Success        send successfully.
 * @retval kStatus_USB_InvalidHandle  The classHandle is NULL pointer.
 * @retval kStatus_USB_Busy           There is no idle transfer.
 * @retval kStatus_USB_Error          pipe is not initialized.
 *                                    Or, send transfer fail, please reference to USB_HostSendSetup.
 */
usb_status_t USB_HostHubClearFeature(usb_host_class_handle classHandle,
                                     uint8_t feature,
                                     transfer_callback_t callbackFn,
                                     void *callbackParam)
{
    return USB_HostHubClassRequestCommon(classHandle, USB_REQUEST_TYPE_DIR_OUT | USB_REQUEST_TYPE_TYPE_CLASS,
                                         USB_REQUEST_STANDARD_CLEAR_FEATURE, feature, 0, NULL, 0, callbackFn,
                                         callbackParam);
}

/*!
 * @brief hub get status.
 *
 * This function implements get hub status specific request.
 *
 * @param classHandle   the class handle.
 * @param buffer         the buffer pointer.
 * @param bufferLength     the buffer length.
 * @param callbackFn    this callback is called after this function completes.
 * @param callbackParam the first parameter in the callback function.
 *
 * @retval kStatus_USB_Success        send successfully.
 * @retval kStatus_USB_InvalidHandle  The classHandle is NULL pointer.
 * @retval kStatus_USB_Busy           There is no idle transfer.
 * @retval kStatus_USB_Error          pipe is not initialized.
 *                                    Or, send transfer fail, please reference to USB_HostSendSetup.
 */
usb_status_t USB_HostHubGetStatus(usb_host_class_handle classHandle,
                                  uint8_t *buffer,
                                  uint16_t bufferLength,
                                  transfer_callback_t callbackFn,
                                  void *callbackParam)
{
    return USB_HostHubClassRequestCommon(classHandle, USB_REQUEST_TYPE_DIR_IN | USB_REQUEST_TYPE_TYPE_CLASS,
                                         USB_REQUEST_STANDARD_GET_STATUS, 0, 0, buffer, bufferLength, callbackFn,
                                         callbackParam);
}

/*!
 * @brief hub set feature.
 *
 * This function implements set hub feature specific request.
 *
 * @param classHandle   the class handle.
 * @param buffer         the buffer pointer.
 * @param bufferLength     the buffer length.
 * @param callbackFn    this callback is called after this function completes.
 * @param callbackParam the first parameter in the callback function.
 *
 * @retval kStatus_USB_Success        send successfully.
 * @retval kStatus_USB_InvalidHandle  The classHandle is NULL pointer.
 * @retval kStatus_USB_Busy           There is no idle transfer.
 * @retval kStatus_USB_Error          pipe is not initialized.
 *                                    Or, send transfer fail, please reference to USB_HostSendSetup.
 */
usb_status_t USB_HostHubSetPortFeature(usb_host_class_handle classHandle,
                                       uint8_t portNumber,
                                       uint8_t feature,
                                       transfer_callback_t callbackFn,
                                       void *callbackParam)
{
    return USB_HostHubClassRequestCommon(
        classHandle, USB_REQUEST_TYPE_DIR_OUT | USB_REQUEST_TYPE_TYPE_CLASS | USB_REQUEST_TYPE_RECIPIENT_OTHER,
        USB_REQUEST_STANDARD_SET_FEATURE, feature, portNumber, NULL, 0, callbackFn, callbackParam);
}

/*!
 * @brief hub clear port feature.
 *
 * This function implements clear hub port feature specific request.
 *
 * @param classHandle   the class handle.
 * @param buffer         the buffer pointer.
 * @param bufferLength     the buffer length.
 * @param callbackFn    this callback is called after this function completes.
 * @param callbackParam the first parameter in the callback function.
 *
 * @retval kStatus_USB_Success        send successfully.
 * @retval kStatus_USB_InvalidHandle  The classHandle is NULL pointer.
 * @retval kStatus_USB_Busy           There is no idle transfer.
 * @retval kStatus_USB_Error          pipe is not initialized.
 *                                    Or, send transfer fail, please reference to USB_HostSendSetup.
 */
usb_status_t USB_HostHubClearPortFeature(usb_host_class_handle classHandle,
                                         uint8_t portNumber,
                                         uint8_t feature,
                                         transfer_callback_t callbackFn,
                                         void *callbackParam)
{
    return USB_HostHubClassRequestCommon(
        classHandle, USB_REQUEST_TYPE_DIR_OUT | USB_REQUEST_TYPE_TYPE_CLASS | USB_REQUEST_TYPE_RECIPIENT_OTHER,
        USB_REQUEST_STANDARD_CLEAR_FEATURE, feature, portNumber, NULL, 0, callbackFn, callbackParam);
}

/*!
 * @brief hub port get status.
 *
 * This function implements get hub port status specific request.
 *
 * @param classHandle   the class handle.
 * @param buffer         the buffer pointer.
 * @param bufferLength     the buffer length.
 * @param callbackFn    this callback is called after this function completes.
 * @param callbackParam the first parameter in the callback function.
 *
 * @retval kStatus_USB_Success        send successfully.
 * @retval kStatus_USB_InvalidHandle  The classHandle is NULL pointer.
 * @retval kStatus_USB_Busy           There is no idle transfer.
 * @retval kStatus_USB_Error          pipe is not initialized.
 *                                    Or, send transfer fail, please reference to USB_HostSendSetup.
 */
usb_status_t USB_HostHubGetPortStatus(usb_host_class_handle classHandle,
                                      uint8_t portNumber,
                                      uint8_t *buffer,
                                      uint16_t bufferLength,
                                      transfer_callback_t callbackFn,
                                      void *callbackParam)
{
    return USB_HostHubClassRequestCommon(
        classHandle, USB_REQUEST_TYPE_DIR_IN | USB_REQUEST_TYPE_TYPE_CLASS | USB_REQUEST_TYPE_RECIPIENT_OTHER,
        USB_REQUEST_STANDARD_GET_STATUS, 0, portNumber, buffer, bufferLength, callbackFn, callbackParam);
}

#endif /* USB_HOST_CONFIG_HUB */
