/*
 * Copyright (c) 2015, Freescale Semiconductor, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * o Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * o Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * o Neither the name of Freescale Semiconductor, Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "usb_host_config.h"
#include "usb_host.h"
#include "usb_host_hci.h"
#include "usb_host_devices.h"
#include "fsl_device_registers.h"

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

/*******************************************************************************
 * Prototypes
 ******************************************************************************/
extern uint32_t USB_HostHubGetTotalThinkTime(uint8_t parentHubNo);

/*!
 * @brief get the idle host instance.
 *
 * @return  host instance pointer.
 */
static usb_host_instance_t *USB_HostGetInstance(void);

/*!
 * @brief release host instance.
 *
 * @param hostInstance  host instance pointer.
 */
static void USB_HostReleaseInstance(usb_host_instance_t *hostInstance);

/*!
 * @brief get the khci/ehci interface.
 *
 * @param controllerId    controller id.
 * @param controllerTable return controller interface structure.
 */
static void USB_HostGetControllerInterface(uint8_t controllerId,
                                           const usb_host_controller_interface_t **controllerTable);

#if ((defined USB_HOST_CONFIG_EHCI) && (USB_HOST_CONFIG_EHCI))
#if ((defined USB_HOST_CONFIG_COMPLIANCE_TEST) && (USB_HOST_CONFIG_COMPLIANCE_TEST))
extern void USB_HostEhciTestModeInit(usb_device_handle devHandle);
#endif /* USB_HOST_CONFIG_COMPLIANCE_TEST */
#endif /* USB_HOST_CONFIG_EHCI */

/*******************************************************************************
 * Variables
 ******************************************************************************/
/*! @brief USB host instance resource */
usb_host_instance_t g_UsbHostInstance[USB_HOST_CONFIG_MAX_HOST];

#if ((defined USB_HOST_CONFIG_EHCI) && (USB_HOST_CONFIG_EHCI))
#include "usb_host_ehci.h"
static const usb_host_controller_interface_t s_EhciInterface = \
{
    USB_HostEhciCreate,    USB_HostEhciDestory,  USB_HostEhciOpenPipe, USB_HostEhciClosePipe,
    USB_HostEhciWritePipe, USB_HostEhciReadpipe, USB_HostEhciIoctl,
};
#endif /* USB_HOST_CONFIG_EHCI */

#if ((defined USB_HOST_CONFIG_KHCI) && (USB_HOST_CONFIG_KHCI))
#include "usb_host_khci.h"
static const usb_host_controller_interface_t s_KhciInterface = \
{
    USB_HostKhciCreate,    USB_HostKhciDestory,  USB_HostKhciOpenPipe, USB_HostKhciClosePipe,
    USB_HostKhciWritePipe, USB_HostKhciReadpipe, USB_HostKciIoctl,
};
#endif /* USB_HOST_CONFIG_KHCI */

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

#if ((defined USB_HOST_CONFIG_COMPLIANCE_TEST) && (USB_HOST_CONFIG_COMPLIANCE_TEST))
/*FUNCTION*----------------------------------------------------------------
*
* Function Name  : usb_test_mode_init
* Returned Value : None
* Comments       :
*     This function is called by common class to initialize the class driver. It
*     is called in response to a select interface call by application
*
*END*--------------------------------------------------------------------*/
usb_status_t USB_HostTestModeInit(usb_device_handle deviceHandle)
{
    usb_host_device_instance_t *deviceInstance = (usb_host_device_instance_t *)deviceHandle;
    usb_host_instance_t *hostInstance = (usb_host_instance_t *)deviceInstance->hostHandle;
    uint32_t productId;
    uint32_t vendorId;

    usb_echo("usb host test init\r\n");
    USB_HostHelperGetPeripheralInformation(deviceHandle, kUSB_HostGetDevicePID, &productId);
    USB_HostHelperGetPeripheralInformation(deviceHandle, kUSB_HostGetDeviceVID, &vendorId);
    usb_echo(" vendor id :0x%x product id:0x%x \r\n", vendorId, productId);

    if ((productId != 0x0200U) && (productId != 0x0101) && (productId != 0x0102) && (productId != 0x0103) &&
        (productId != 0x0104) && (productId != 0x0105) && (productId != 0x0106) && (productId != 0x0107) &&
        (productId != 0x0108))
    {
        usb_echo("device not supported\r\n");
    }

    if (productId == 0x0200U)
    {
        usb_echo("PET test device attached\r\n");
    }
    else
    {
#if ((defined USB_HOST_CONFIG_EHCI) && (USB_HOST_CONFIG_EHCI))
        if (hostInstance->controllerTable == &s_EhciInterface)
        {
            USB_HostEhciTestModeInit(deviceHandle);
        }
#endif
    }

    return kStatus_USB_Success;
}
#endif

static usb_host_instance_t *USB_HostGetInstance(void)
{
    uint8_t i = 0;
    USB_OSA_SR_ALLOC();
    USB_OSA_ENTER_CRITICAL();
    for (; i < USB_HOST_CONFIG_MAX_HOST; i++)
    {
        if (g_UsbHostInstance[i].occupied != 1)
        {
            uint8_t *buffer = (uint8_t *)&g_UsbHostInstance[i];
            for (uint32_t j = 0U; j < sizeof(usb_host_instance_t); j++)
            {
                buffer[j] = 0x00U;
            }
            g_UsbHostInstance[i].occupied = 1;
            USB_OSA_EXIT_CRITICAL();
            return &g_UsbHostInstance[i];
        }
    }
    USB_OSA_EXIT_CRITICAL();
    return NULL;
}

static void USB_HostReleaseInstance(usb_host_instance_t *hostInstance)
{
    USB_OSA_SR_ALLOC();
    USB_OSA_ENTER_CRITICAL();
    hostInstance->occupied = 0;
    USB_OSA_EXIT_CRITICAL();
}

static void USB_HostGetControllerInterface(uint8_t controllerId,
                                           const usb_host_controller_interface_t **controllerTable)
{
#if ((defined USB_HOST_CONFIG_KHCI) && (USB_HOST_CONFIG_KHCI))
    if (controllerId == kUSB_ControllerKhci0)
    {
        *controllerTable = &s_KhciInterface;
    }
#endif /* USB_HOST_CONFIG_KHCI */

#if ((defined USB_HOST_CONFIG_EHCI) && (USB_HOST_CONFIG_EHCI))
    if (controllerId == kUSB_ControllerEhci0)
    {
        *controllerTable = &s_EhciInterface;
    }
#endif /* USB_HOST_CONFIG_EHCI */
}

usb_status_t USB_HostInit(uint8_t controllerId, usb_host_handle *hostHandle, host_callback_t callbackFn)
{
    usb_status_t status = kStatus_USB_Success;
    usb_host_instance_t *hostInstance = NULL;
    usb_host_transfer_t *transferPrev = NULL;
    uint8_t i = 0;

    hostInstance = USB_HostGetInstance(); /* get one host instance */
    if (hostInstance == NULL)
    {
        return kStatus_USB_InvalidHandle;
    }

    /* get khci/ehci API table */
    USB_HostGetControllerInterface(controllerId, &hostInstance->controllerTable);
    if (hostInstance->controllerTable == NULL)
    {
        USB_HostReleaseInstance(hostInstance);
        return kStatus_USB_ControllerNotFound;
    }

    /* judge the controller interface one time at here */
    if ((hostInstance->controllerTable->controllerCreate == NULL) ||
        (hostInstance->controllerTable->controllerDestory == NULL) ||
        (hostInstance->controllerTable->controllerOpenPipe == NULL) ||
        (hostInstance->controllerTable->controllerClosePipe == NULL) ||
        (hostInstance->controllerTable->controllerWritePipe == NULL) ||
        (hostInstance->controllerTable->controllerReadPipe == NULL) ||
        (hostInstance->controllerTable->controllerIoctl == NULL))
    {
        return kStatus_USB_Error;
    }

    /* HOST instance init*/
    hostInstance->controllerId = controllerId;
    hostInstance->deviceCallback = callbackFn;
    hostInstance->deviceList = NULL;
    if (kStatus_USB_OSA_Success != USB_OsaMutexCreate(&hostInstance->hostMutex))
    {
        USB_HostReleaseInstance(hostInstance);
#ifdef HOST_ECHO
        usb_echo("host init: create host mutex fail\r\n");
#endif
        return kStatus_USB_Error;
    }

    /* initialize transfer list */

    hostInstance->transferHead = &hostInstance->transferList[0];
    transferPrev = hostInstance->transferHead;
    for (i = 1; i < USB_HOST_CONFIG_MAX_TRANSFERS; ++i)
    {
        transferPrev->next = &hostInstance->transferList[i];
        transferPrev = transferPrev->next;
    }

    /* controller create */
    status =
        hostInstance->controllerTable->controllerCreate(controllerId, hostInstance, &(hostInstance->controllerHandle));
    if ((status != kStatus_USB_Success) || (hostInstance->controllerHandle == NULL))
    {
        USB_OsaMutexDestroy(hostInstance->hostMutex);
        USB_HostReleaseInstance(hostInstance);
#ifdef HOST_ECHO
        usb_echo("host init: controller init fail\r\n");
#endif
        return kStatus_USB_Error;
    }

    *hostHandle = hostInstance;
    return kStatus_USB_Success;
}

usb_status_t USB_HostDeinit(usb_host_handle hostHandle)
{
    usb_status_t status = kStatus_USB_Success;
    usb_host_instance_t *hostInstance = (usb_host_instance_t *)hostHandle;
    usb_host_device_instance_t *deviceInstance = NULL;

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

    /* device list detach */
    deviceInstance = (usb_host_device_instance_t *)hostInstance->deviceList;
    while (deviceInstance != NULL)
    {
        deviceInstance = (usb_host_device_instance_t *)hostInstance->deviceList;
        USB_HostDetachDeviceInternal(hostHandle, deviceInstance);
    }

    /* controller instance destory */
    status = hostInstance->controllerTable->controllerDestory(hostInstance->controllerHandle);
    hostInstance->controllerHandle = NULL;
    if (status != kStatus_USB_Success)
    {
#ifdef HOST_ECHO
        usb_echo("host controller destory fail\r\n");
#endif
    }

    /* resource release */
    if (hostInstance->hostMutex)
    {
        USB_OsaMutexDestroy(hostInstance->hostMutex);
        hostInstance->hostMutex = NULL;
    }
    USB_HostReleaseInstance(hostInstance);

    return status;
}

usb_status_t USB_HostOpenPipe(usb_host_handle hostHandle,
                              usb_host_pipe_handle *pipeHandle,
                              usb_host_pipe_init_t *pipeInit)
{
    usb_status_t status = kStatus_USB_Success;
    usb_host_instance_t *hostInstance = (usb_host_instance_t *)hostHandle;

    if ((hostHandle == NULL) || (pipeInit == NULL))
    {
        return kStatus_USB_InvalidHandle;
    }

    /* call controller open pipe interface */
    status = hostInstance->controllerTable->controllerOpenPipe(hostInstance->controllerHandle, pipeHandle, pipeInit);

    return status;
}

usb_status_t USB_HostClosePipe(usb_host_handle hostHandle, usb_host_pipe_handle pipeHandle)
{
    usb_status_t status = kStatus_USB_Success;
    usb_host_instance_t *hostInstance = (usb_host_instance_t *)hostHandle;

    if ((hostHandle == NULL) || (pipeHandle == NULL))
    {
        return kStatus_USB_InvalidHandle;
    }

    /* call controller close pipe interface */
    status = hostInstance->controllerTable->controllerClosePipe(hostInstance->controllerHandle, pipeHandle);

    return status;
}

usb_status_t USB_HostSend(usb_host_handle hostHandle, usb_host_pipe_handle pipeHandle, usb_host_transfer_t *transfer)
{
    usb_status_t status = kStatus_USB_Success;
    usb_host_instance_t *hostInstance = (usb_host_instance_t *)hostHandle;

    if ((hostHandle == NULL) || (pipeHandle == NULL) || (transfer == NULL))
    {
        return kStatus_USB_InvalidHandle;
    }

    /* initialize transfer */
    transfer->transferSofar = 0;
    transfer->direction = USB_OUT;

    USB_HostLock(); /* This api can be called by host task and app task */
/* keep this code: in normal situation application will guarantee the device is attached when call send/receive function
 */
#if 0
    if ((USB_HostValidateDevice(pipe_ptr->deviceHandle) != kStatus_USB_Success) || (!(USB_HostGetDeviceAttachState(pipe_ptr->deviceHandle))))
    {
        USB_HostUnlock();
        return status;
    }
#endif
/* call controller write pipe interface */
#if ((defined USB_HOST_CONFIG_BUFFER_PROPERTY_CACHEABLE) && (USB_HOST_CONFIG_BUFFER_PROPERTY_CACHEABLE))
    if (transfer->transferLength > 0)
    {
        USB_CacheFlushLines((void *)transfer->transferBuffer, transfer->transferLength);
    }
#endif
    status = hostInstance->controllerTable->controllerWritePipe(hostInstance->controllerHandle, pipeHandle, transfer);

    USB_HostUnlock();
    return status;
}

usb_status_t USB_HostSendSetup(usb_host_handle hostHandle,
                               usb_host_pipe_handle pipeHandle,
                               usb_host_transfer_t *transfer)
{
    usb_status_t status = kStatus_USB_Success;
    usb_host_instance_t *hostInstance = (usb_host_instance_t *)hostHandle;

    if ((hostHandle == NULL) || (pipeHandle == NULL) || (transfer == NULL))
    {
        return kStatus_USB_InvalidHandle;
    }

    /* initialize transfer */
    transfer->transferSofar = 0;
    transfer->next = NULL;
    transfer->setupStatus = 0;
    if ((transfer->setupPacket.bmRequestType & USB_REQUSET_TYPE_DIR_MASK) == USB_REQUEST_TYPE_DIR_IN)
    {
        transfer->direction = USB_IN;
    }
    else
    {
        transfer->direction = USB_OUT;
    }

    USB_HostLock(); /* This API can be called by host task and application task */
/* keep this code: in normal situation application will guarantee the device is attached when call send/receive function
 */
#if 0
    if ((USB_HostValidateDevice(pipe_ptr->deviceHandle) != kStatus_USB_Success) || (!(USB_HostGetDeviceAttachState(pipe_ptr->deviceHandle))))
    {
        USB_HostUnlock();
        return status;
    }
#endif
/* call controller write pipe interface */
#if ((defined USB_HOST_CONFIG_BUFFER_PROPERTY_CACHEABLE) && (USB_HOST_CONFIG_BUFFER_PROPERTY_CACHEABLE))
    if (transfer->transferLength > 0)
    {
        USB_CacheFlushLines((void *)transfer->transferBuffer, transfer->transferLength);
    }
#endif
    status = hostInstance->controllerTable->controllerWritePipe(hostInstance->controllerHandle, pipeHandle, transfer);

    USB_HostUnlock();
    return status;
}

usb_status_t USB_HostRecv(usb_host_handle hostHandle, usb_host_pipe_handle pipeHandle, usb_host_transfer_t *transfer)
{
    usb_status_t status = kStatus_USB_Success;
    usb_host_instance_t *hostInstance = (usb_host_instance_t *)hostHandle;

    if ((hostHandle == NULL) || (pipeHandle == NULL) || (transfer == NULL))
    {
        return kStatus_USB_InvalidHandle;
    }

    /* initialize transfer */
    transfer->transferSofar = 0;
    transfer->direction = USB_IN;

    USB_HostLock(); /* This API can be called by host task and application task */
/* keep this code: in normal situation application will guarantee the device is attached when call send/receive function
 */
#if 0
    if ((USB_HostValidateDevice(pipe_ptr->deviceHandle) != kStatus_USB_Success) || (!(USB_HostGetDeviceAttachState(pipe_ptr->deviceHandle))))
    {
        USB_HostUnlock();
        return status;
    }
#endif

#if ((defined USB_HOST_CONFIG_BUFFER_PROPERTY_CACHEABLE) && (USB_HOST_CONFIG_BUFFER_PROPERTY_CACHEABLE))
    if (transfer->transferLength > 0)
    {
        USB_CacheInvalidateLines((void *)transfer->transferBuffer, transfer->transferLength);
    }
#endif
    status = hostInstance->controllerTable->controllerReadPipe(hostInstance->controllerHandle, pipeHandle, transfer);

    USB_HostUnlock();
    return status;
}

usb_status_t USB_HostCancelTransfer(usb_host_handle hostHandle,
                                    usb_host_pipe_handle pipeHandle,
                                    usb_host_transfer_t *transfer)
{
    usb_status_t status = kStatus_USB_Success;
    usb_host_instance_t *hostInstance = (usb_host_instance_t *)hostHandle;
    usb_host_cancel_param_t cancelParam;

    if ((hostHandle == NULL) || (pipeHandle == NULL))
    {
        return kStatus_USB_InvalidHandle;
    }

    /* initialize cancel parameter */
    cancelParam.pipeHandle = pipeHandle;
    cancelParam.transfer = transfer;

    /* USB_HostLock(); This api can be called by host task and app task */
    status = hostInstance->controllerTable->controllerIoctl(hostInstance->controllerHandle, kUSB_HostCancelTransfer,
                                                            &cancelParam);
    /* USB_HostUnlock(); */

    return status;
}

usb_status_t USB_HostMallocTransfer(usb_host_handle hostHandle, usb_host_transfer_t **transfer)
{
    usb_host_instance_t *hostInstance = (usb_host_instance_t *)hostHandle;

    if ((hostHandle == NULL) || (transfer == NULL))
    {
        return kStatus_USB_InvalidHandle;
    }

    /* get one from the transfer_head */
    USB_HostLock();
    if (hostInstance->transferHead != NULL)
    {
        *transfer = hostInstance->transferHead;
        hostInstance->transferHead = hostInstance->transferHead->next;
        USB_HostUnlock();
        return kStatus_USB_Success;
    }
    else
    {
        *transfer = NULL;
        USB_HostUnlock();
        return kStatus_USB_Error;
    }
}

usb_status_t USB_HostFreeTransfer(usb_host_handle hostHandle, usb_host_transfer_t *transfer)
{
    usb_host_instance_t *hostInstance = (usb_host_instance_t *)hostHandle;

    if (hostHandle == NULL)
    {
        return kStatus_USB_InvalidHandle;
    }
    if (transfer == NULL)
    {
        return kStatus_USB_Success;
    }

    /* release one to the transfer_head */
    USB_HostLock();
    transfer->next = hostInstance->transferHead;
    hostInstance->transferHead = transfer;
    USB_HostUnlock();
    return kStatus_USB_Success;
}

usb_status_t USB_HostHelperGetPeripheralInformation(usb_device_handle deviceHandle,
                                                    uint32_t infoCode,
                                                    uint32_t *infoValue)
{
    usb_host_device_instance_t *deviceInstance = (usb_host_device_instance_t *)deviceHandle;
    if ((deviceHandle == NULL) || (infoValue == NULL))
    {
        return kStatus_USB_InvalidParameter;
    }

    switch (infoCode)
    {
        case kUSB_HostGetDeviceAddress: /* device address */
            *infoValue = (uint32_t)deviceInstance->setAddress;
            break;

        case kUSB_HostGetDeviceControlPipe: /* device control pipe */
            *infoValue = (uint32_t)deviceInstance->controlPipe;
            break;

        case kUSB_HostGetHostHandle: /* device host handle */
            *infoValue = (uint32_t)deviceInstance->hostHandle;
            break;

#if ((defined USB_HOST_CONFIG_HUB) && (USB_HOST_CONFIG_HUB))
        case kUSB_HostGetDeviceHubNumber: /* device hub address */
            *infoValue = (uint32_t)deviceInstance->hubNumber;
            break;

        case kUSB_HostGetDevicePortNumber: /* device port no */
            *infoValue = (uint32_t)deviceInstance->portNumber;
            break;

        case kUSB_HostGetDeviceLevel: /* device level */
            *infoValue = (uint32_t)deviceInstance->level;
            break;

        case kUSB_HostGetDeviceHSHubNumber: /* device high-speed hub address */
            *infoValue = (uint32_t)deviceInstance->hsHubNumber;
            break;

        case kUSB_HostGetDeviceHSHubPort: /* device high-speed hub port no */
            *infoValue = (uint32_t)deviceInstance->hsHubPort;
            break;

        case kUSB_HostGetHubThinkTime: /* device hub think time */
            *infoValue = USB_HostHubGetTotalThinkTime(deviceInstance->hubNumber);
            break;
#else
        case kUSB_HostGetDeviceHubNumber:   /* device hub address */
        case kUSB_HostGetDevicePortNumber:  /* device port no */
        case kUSB_HostGetDeviceHSHubNumber: /* device high-speed hub address */
        case kUSB_HostGetDeviceHSHubPort:   /* device high-speed hub port no */
        case kUSB_HostGetHubThinkTime:      /* device hub think time */
            *infoValue = 0;
            break;
        case kUSB_HostGetDeviceLevel: /* device level */
            *infoValue = 1;
            break;
#endif /* USB_HOST_CONFIG_HUB */

        case kUSB_HostGetDeviceSpeed: /* device speed */
            *infoValue = (uint32_t)deviceInstance->speed;
            break;

        case kUSB_HostGetDevicePID: /* device pid */
            *infoValue = (uint32_t)USB_SHORT_FROM_LITTLE_ENDIAN_ADDRESS(deviceInstance->deviceDescriptor.idProduct);
            break;

        case kUSB_HostGetDeviceVID: /* device vid */
            *infoValue = (uint32_t)USB_SHORT_FROM_LITTLE_ENDIAN_ADDRESS(deviceInstance->deviceDescriptor.idVendor);
            break;

        default:
            return kStatus_USB_Error;
    }

    return kStatus_USB_Success;
}

usb_status_t USB_HostHelperParseAlternateSetting(usb_host_interface_handle interfaceHandle,
                                                 uint8_t alternateSetting,
                                                 usb_host_interface_t *interface)
{
    uint32_t endPosition;
    usb_descriptor_union_t *unionDes;
    usb_host_ep_t *epParse;

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

    if (alternateSetting == 0)
    {
        return kStatus_USB_InvalidParameter;
    }

    /* parse configuration descriptor */
    unionDes = (usb_descriptor_union_t *)((usb_host_interface_t *)interfaceHandle)
                   ->interfaceDesc; /* interface extend descriptor start */
    endPosition =
        (uint32_t)unionDes +
        ((usb_host_interface_t *)interfaceHandle)->interfaceExtensionLength; /* interface extend descriptor end */
    unionDes = (usb_descriptor_union_t *)((uint32_t)unionDes + unionDes->common.bLength);

    /* search for the alternate setting interface descritpor */
    while ((uint32_t)unionDes < endPosition)
    {
        if (unionDes->interface.bDescriptorType == USB_DESCRIPTOR_TYPE_INTERFACE)
        {
            if (unionDes->interface.bAlternateSetting == alternateSetting)
            {
                break;
            }
            else
            {
                unionDes = (usb_descriptor_union_t *)((uint32_t)unionDes + unionDes->common.bLength);
            }
        }
        else
        {
            unionDes = (usb_descriptor_union_t *)((uint32_t)unionDes + unionDes->common.bLength);
        }
    }
    if ((uint32_t)unionDes >= endPosition)
    {
        return kStatus_USB_Error;
    }

    /* initialize interface handle structure instance */
    interface->interfaceDesc = &unionDes->interface;
    interface->alternateSettingNumber = 0;
    interface->epCount = 0;
    interface->interfaceExtension = NULL;
    interface->interfaceExtensionLength = 0;
    interface->interfaceIndex = unionDes->interface.bInterfaceNumber;

    /* search for endpoint descriptor start position */
    unionDes = (usb_descriptor_union_t *)((uint32_t)unionDes + unionDes->common.bLength);
    while ((uint32_t)unionDes < endPosition)
    {
        if ((unionDes->common.bDescriptorType != USB_DESCRIPTOR_TYPE_INTERFACE) &&
            (unionDes->common.bDescriptorType != USB_DESCRIPTOR_TYPE_ENDPOINT))
        {
            if (interface->interfaceExtension == NULL)
            {
                interface->interfaceExtension = (uint8_t *)unionDes;
            }
            interface->interfaceExtensionLength += unionDes->common.bLength;
            unionDes = (usb_descriptor_union_t *)((uint32_t)unionDes + unionDes->common.bLength);
        }
        else
        {
            break;
        }
    }

    /* parse endpoint descriptor */
    if (interface->interfaceDesc->bNumEndpoints != 0)
    {
        if ((unionDes->common.bDescriptorType != USB_DESCRIPTOR_TYPE_ENDPOINT) ||
            (interface->interfaceDesc->bNumEndpoints > USB_HOST_CONFIG_INTERFACE_MAX_EP))
        {
#ifdef HOST_ECHO
            usb_echo("interface descriptor error\n");
#endif
            return kStatus_USB_Error;
        }
        for (; interface->epCount < interface->interfaceDesc->bNumEndpoints; (interface->epCount)++)
        {
            if (((uint32_t)unionDes >= endPosition) ||
                (unionDes->common.bDescriptorType != USB_DESCRIPTOR_TYPE_ENDPOINT))
            {
#ifdef HOST_ECHO
                usb_echo("endpoint descriptor error\n");
#endif
                return kStatus_USB_Error;
            }
            epParse = (usb_host_ep_t *)&interface->epList[interface->epCount];
            epParse->epDesc = (usb_descriptor_endpoint_t *)unionDes;
            epParse->epExtensionLength = 0;
            epParse->epExtension = NULL;
            unionDes = (usb_descriptor_union_t *)((uint32_t)unionDes + unionDes->common.bLength);
            while ((uint32_t)unionDes < endPosition)
            {
                if ((unionDes->common.bDescriptorType != USB_DESCRIPTOR_TYPE_ENDPOINT) &&
                    (unionDes->common.bDescriptorType != USB_DESCRIPTOR_TYPE_INTERFACE))
                {
                    if (epParse->epExtension == NULL)
                    {
                        epParse->epExtension = (uint8_t *)unionDes;
                    }
                    epParse->epExtensionLength += unionDes->common.bLength;
                    unionDes = (usb_descriptor_union_t *)((uint32_t)unionDes + unionDes->common.bLength);
                }
                else
                {
                    break;
                }
            }
        }
    }

    return kStatus_USB_Success;
}

void USB_HostGetVersion(uint32_t *version)
{
    if (version)
    {
        *version =
            (uint32_t)USB_MAKE_VERSION(USB_STACK_VERSION_MAJOR, USB_STACK_VERSION_MINOR, USB_STACK_VERSION_BUGFIX);
    }
}
