/*
 * 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"
#if ((defined USB_HOST_CONFIG_PHDC) && (USB_HOST_CONFIG_PHDC))
#include "usb_host.h"
#include "usb_host_phdc.h"

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

/*******************************************************************************
 * Prototypes
 ******************************************************************************/

/*!
 * @brief phdc control pipe transfer callback.
 *
 * @param param       callback parameter.
 * @param transfer    callback transfer.
 * @param status      transfer status.
 */
static void USB_HostPhdcControlPipeCallback(void *param, usb_host_transfer_t *transfer, usb_status_t status);

/*!
 * @brief phdc set and clear feature endpoint halt callback.
 *
 * @param param       callback parameter.
 * @param transfer    callback transfer.
 * @param status      transfer status.
 */
static void USB_HostPhdcSetClearFeatureEndpointHaltCallback(void *param,
                                                            usb_host_transfer_t *transfer,
                                                            usb_status_t status);

/*!
 * @brief phdc interrupt pipe transfer callback.
 *
 * @param param       callback parameter.
 * @param transfer    callback transfer.
 * @param status      transfer status.
 */
static void USB_HostPhdcInterruptPipeCallback(void *param, usb_host_transfer_t *transfer, usb_status_t status);

/*!
 * @brief phdc bulk in pipe transfer callback.
 *
 * @param param       callback parameter.
 * @param transfer    callback transfer.
 * @param status      transfer status.
 */
static void USB_HostPhdcBulkInPipeCallback(void *param, usb_host_transfer_t *transfer, usb_status_t status);

/*!
 * @brief phdc bulk out pipe transfer callback.
 *
 * @param param       callback parameter.
 * @param transfer    callback transfer.
 * @param status      transfer status.
 */
static void USB_HostPhdcBulkOutPipeCallback(void *param, usb_host_transfer_t *transfer, usb_status_t status);

/*!
 * @brief phdc set interface callback, open pipes.
 *
 * @param param       callback parameter.
 * @param transfer    callback transfer.
 * @param status      transfer status.
 */
static void USB_HostPhdcSetInterfaceCallback(void *param, usb_host_transfer_t *transfer, usb_status_t status);

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

/*! @brief meta-data message preamble signature string */
static char const metaDataMsgPreambleSignature[] = "PhdcQoSSignature";

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

/*!
 * @brief phdc control pipe transfer callback.
 *
 * @param param       callback parameter.
 * @param transfer    callback transfer.
 * @param status      transfer status.
 */
static void USB_HostPhdcControlPipeCallback(void *param, usb_host_transfer_t *transfer, usb_status_t status)
{
    usb_host_phdc_instance_t *phdcInstance = (usb_host_phdc_instance_t *)param;
    phdcInstance->controlTransfer = NULL;
    if (kStatus_USB_Success == status)
    {
        if (USB_HOST_PHDC_SET_FEATURE_REQUEST == transfer->setupPacket.bRequest)
        {
            /* Meta-data message preamble feature is enabled */
            phdcInstance->isMessagePreambleEnabled = 1U;
        }
        else if (USB_HOST_PHDC_CLEAR_FEATURE_REQUEST == transfer->setupPacket.bRequest)
        {
            /* Meta-data message preamble feature is disable */
            phdcInstance->isMessagePreambleEnabled = 0U;
        }
        else
        {
        }
    }
    if (NULL != phdcInstance->controlCallbackFn)
    {
        phdcInstance->controlCallbackFn(phdcInstance->controlCallbackParam, transfer->transferBuffer,
                                        transfer->transferSofar, status);
    }
    USB_HostFreeTransfer(phdcInstance->hostHandle, transfer);
}

/*!
 * @brief phdc set and clear feature endpoint halt callback.
 *
 * @param param       callback parameter.
 * @param transfer    callback transfer.
 * @param status      transfer status.
 */
static void USB_HostPhdcSetClearFeatureEndpointHaltCallback(void *param,
                                                            usb_host_transfer_t *transfer,
                                                            usb_status_t status)
{
    usb_host_phdc_instance_t *phdcInstance = (usb_host_phdc_instance_t *)param;
    phdcInstance->controlTransfer = NULL;
    if (kStatus_USB_Success == status)
    {
        if ((transfer->setupPacket.bRequest == USB_REQUSET_STANDARD_SET_FEATURE) &&
            (transfer->setupPacket.bmRequestType == USB_REQUEST_TYPE_RECIPIENT_ENDPOINT) &&
            (transfer->setupPacket.wValue ==
             USB_SHORT_TO_LITTLE_ENDIAN(USB_REQUSET_STANDARD_FEATURE_SELECTOR_ENDPOINT_HALT)) &&
            (transfer->setupPacket.wIndex ==
             USB_SHORT_TO_LITTLE_ENDIAN(phdcInstance->bulkInEndpointInformation.epDesc->bEndpointAddress)))
        {
            /* The host shall issue CLEAR_FEATURE ENDPOINT_HALT request to the device */
            usb_host_process_feature_param_t featureParam;
            featureParam.requestType = kRequestEndpoint;
            featureParam.featureSelector = USB_REQUSET_STANDARD_FEATURE_SELECTOR_ENDPOINT_HALT;
            featureParam.interfaceOrEndpoint = phdcInstance->bulkInEndpointInformation.epDesc->bEndpointAddress;
            if (kStatus_USB_Success != USB_HostPhdcSetClearFeatureEndpointHalt(phdcInstance->hostHandle,
                                                                               USB_REQUSET_STANDARD_CLEAR_FEATURE,
                                                                               &featureParam, NULL, NULL))
            {
#ifdef HOST_ECHO
                usb_echo("Error for USB_HostPhdcSetClearFeatureEndpointHalt\r\n");
#endif
            }
        }
        if ((transfer->setupPacket.bRequest == USB_REQUSET_STANDARD_CLEAR_FEATURE) &&
            (transfer->setupPacket.bmRequestType == USB_REQUEST_TYPE_RECIPIENT_ENDPOINT) &&
            (transfer->setupPacket.wValue ==
             USB_SHORT_TO_LITTLE_ENDIAN(USB_REQUSET_STANDARD_FEATURE_SELECTOR_ENDPOINT_HALT)) &&
            (transfer->setupPacket.wIndex ==
             USB_SHORT_TO_LITTLE_ENDIAN(phdcInstance->bulkInEndpointInformation.epDesc->bEndpointAddress)))
        {
            /* Reduces the number of bulk transfer to 0 to expect new message preamble transfer */
            phdcInstance->numberTransferBulkIn = 0U;
        }
    }
    if (NULL != phdcInstance->controlCallbackFn)
    {
        /* Notify to application the status of request */
        phdcInstance->controlCallbackFn(phdcInstance->controlCallbackParam, transfer->transferBuffer,
                                        transfer->transferSofar, status);
    }
    USB_HostFreeTransfer(phdcInstance->hostHandle, transfer);
}

/*!
 * @brief phdc interrupt pipe transfer callback.
 *
 * @param param       callback parameter.
 * @param transfer    callback transfer.
 * @param status      transfer status.
 */
static void USB_HostPhdcInterruptPipeCallback(void *param, usb_host_transfer_t *transfer, usb_status_t status)
{
    usb_host_phdc_instance_t *phdcInstance = (usb_host_phdc_instance_t *)param;
    if (NULL != phdcInstance->inCallbackFn)
    {
        phdcInstance->inCallbackFn(phdcInstance->inCallbackParam, transfer->transferBuffer, transfer->transferSofar,
                                   status);
    }
    USB_HostFreeTransfer(phdcInstance->hostHandle, transfer);
}

/*!
 * @brief phdc bulk in pipe transfer callback.
 *
 * @param param       callback parameter.
 * @param transfer    callback transfer.
 * @param status      transfer status.
 */
static void USB_HostPhdcBulkInPipeCallback(void *param, usb_host_transfer_t *transfer, usb_status_t status)
{
    usb_host_phdc_instance_t *phdcInstance = (usb_host_phdc_instance_t *)param;
    if (status == kStatus_USB_Success)
    {
        /* The meta-data message preamble is implemented and enabled */
        if (phdcInstance->isMessagePreambleEnabled == 1U)
        {
            /* The meta-data message preamble feature is enabled, then all data transfers or sets
            of data transfers shall be preceded by a meta-data message preamble transfer. The
            numberTransferBulkIn is initialized as zero for receiving this message preamble data,
            then it is updated to the value of bNumTransfers field of message preamble data */
            if (phdcInstance->numberTransferBulkIn)
            {
                /* When numberTransferBulkIn reduces to 0, a new meta-data message preamble shall
                be transferred */
                phdcInstance->numberTransferBulkIn--;
            }
            else
            {
                uint8_t preambleSignatureChecking = 1U;
                /* The received packet is meta-data message preamble */
                usb_host_phdc_metadata_preamble_t *metaDataMsgPreamble =
                    (usb_host_phdc_metadata_preamble_t *)transfer->transferBuffer;
                /* Meta-data message preamble signature checking */
                for (uint8_t i = 0U; i < USB_HOST_PHDC_MESSAGE_PREAMBLE_SIGNATURE_SIZE; i++)
                {
                    if (*(transfer->transferBuffer + i) != metaDataMsgPreambleSignature[i])
                    {
                        preambleSignatureChecking = 0U;
                        break;
                    }
                }
                if (preambleSignatureChecking)
                {
                    /* Checks if the meta-data message preamble contains an invalid bmLatencyReliability value
                    or bNumTransfers value */
                    if ((!(metaDataMsgPreamble->bNumberTransfers)) || /* bNumTransfers shall never equal zero */
                        (metaDataMsgPreamble->bQosEncodingVersion != 0x01U) || /* Encoding version should be 0x01 */
                        ((metaDataMsgPreamble->bmLatencyReliability !=
                          0x02U) && /* Medium.Good latency, reliability bin */
                         (metaDataMsgPreamble->bmLatencyReliability !=
                          0x04U) && /* Medium.Better latency, reliability bin */
                         (metaDataMsgPreamble->bmLatencyReliability !=
                          0x08U) && /* Medium.Best latency, reliability bin */
                         (metaDataMsgPreamble->bmLatencyReliability !=
                          0x10U) && /* High.Best latency, reliability bin */
                         (metaDataMsgPreamble->bmLatencyReliability !=
                          0x20U) /* VeryHigh.Best latency, reliability bin */))
                    {
                        /* The host shall issue SET_FEATURE ENDPOINT_HALT request to the device */
                        usb_host_process_feature_param_t featureParam;
                        featureParam.requestType = kRequestEndpoint;
                        featureParam.featureSelector = USB_REQUSET_STANDARD_FEATURE_SELECTOR_ENDPOINT_HALT;
                        featureParam.interfaceOrEndpoint =
                            phdcInstance->bulkInEndpointInformation.epDesc->bEndpointAddress;
                        if (kStatus_USB_Success !=
                            USB_HostPhdcSetClearFeatureEndpointHalt(
                                phdcInstance->hostHandle, USB_REQUSET_STANDARD_SET_FEATURE, &featureParam, NULL, NULL))
                        {
#ifdef HOST_ECHO
                            usb_echo(
                                "USB_HostPhdcBulkInPipeCallback: Error for "
                                "USB_HostPhdcSetClearFeatureEndpointHalt\r\n");
#endif
                        }
                    }
                    else
                    {
                        /* The meta-data message preamble data is correct, update the phdc status and
                         * numberTransferBulkIn value */
                        phdcInstance->numberTransferBulkIn = metaDataMsgPreamble->bNumberTransfers;
                    }
                }
            }
        }
    }
    if (NULL != phdcInstance->inCallbackFn)
    {
        phdcInstance->inCallbackFn(phdcInstance->inCallbackParam, transfer->transferBuffer, transfer->transferSofar,
                                   status);
    }
    USB_HostFreeTransfer(phdcInstance->hostHandle, transfer);
}

/*!
 * @brief phdc bulk out pipe transfer callback.
 *
 * @param param       callback parameter.
 * @param transfer    callback transfer.
 * @param status      transfer status.
 */
static void USB_HostPhdcBulkOutPipeCallback(void *param, usb_host_transfer_t *transfer, usb_status_t status)
{
    usb_host_phdc_instance_t *phdcInstance = (usb_host_phdc_instance_t *)param;
    if (NULL != phdcInstance->outCallbackFn)
    {
        phdcInstance->outCallbackFn(phdcInstance->outCallbackParam, transfer->transferBuffer, transfer->transferSofar,
                                    status);
    }
    USB_HostFreeTransfer(phdcInstance->hostHandle, transfer);
}

/*!
 * @brief phdc open interface.
 *
 * @param phdcInstance     phdc instance pointer.
 *
 * @return kStatus_USB_Success or error codes.
 */
static usb_status_t USB_HostPhdcOpenInterface(usb_host_phdc_instance_t *phdcInstance)
{
    usb_status_t status;
    uint8_t epIndex = 0U;
    usb_host_pipe_init_t pipeInit;
    usb_descriptor_endpoint_t *epDesc = NULL;
    usb_host_interface_t *interface;
    if (NULL != phdcInstance->interruptPipe)
    {
        /* Close the PHDC interrupt pipe if it is opening */
        status = USB_HostClosePipe(phdcInstance->hostHandle, phdcInstance->interruptPipe);

        if (status != kStatus_USB_Success)
        {
#ifdef HOST_ECHO
            usb_echo("USB_HostPhdcOpenInterface: Error when close pipe\r\n");
#endif
        }
        phdcInstance->interruptPipe = NULL;
    }

    if (NULL != phdcInstance->bulkInPipe)
    {
        /* Close the PHDC bulk in pipe if it is opening */
        status = USB_HostClosePipe(phdcInstance->hostHandle, phdcInstance->bulkInPipe);

        if (status != kStatus_USB_Success)
        {
#ifdef HOST_ECHO
            usb_echo("USB_HostPhdcOpenInterface: Error when close pipe\r\n");
#endif
        }
        phdcInstance->bulkInPipe = NULL;
    }

    if (NULL != phdcInstance->bulkOutPipe)
    {
        /* Close the PHDC bulk out pipe if it is opening */
        status = USB_HostClosePipe(phdcInstance->hostHandle, phdcInstance->bulkOutPipe);

        if (status != kStatus_USB_Success)
        {
#ifdef HOST_ECHO
            usb_echo("USB_HostPhdcOpenInterface: Error when close pipe\r\n");
#endif
        }
        phdcInstance->bulkOutPipe = NULL;
    }

    /* open interface pipes */
    interface = (usb_host_interface_t *)phdcInstance->interfaceHandle;
    for (epIndex = 0U; 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))
        {
            /* Initialize the interrupt pipe */
            pipeInit.devInstance = phdcInstance->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 = (USB_SHORT_FROM_LITTLE_ENDIAN_ADDRESS(epDesc->wMaxPacketSize) &
                                        USB_DESCRIPTOR_ENDPOINT_MAXPACKETSIZE_MULT_TRANSACTIONS_MASK);
            pipeInit.nakCount = USB_HOST_CONFIG_MAX_NAK;
            status = USB_HostOpenPipe(phdcInstance->hostHandle, &phdcInstance->interruptPipe, &pipeInit);
            if (status != kStatus_USB_Success)
            {
#ifdef HOST_ECHO
                usb_echo("USB_HostPhdcOpenInterface: Error when open pipe\r\n");
#endif
                return kStatus_USB_Error;
            }
            /* save interrupt in endpoint information */
            phdcInstance->interruptInEndpointInformation = interface->epList[epIndex];
        }
        else 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_BULK))
        {
            /* Initialize bulk in pipe */
            pipeInit.devInstance = phdcInstance->deviceHandle;
            pipeInit.pipeType = USB_ENDPOINT_BULK;
            pipeInit.direction = USB_IN;
            pipeInit.endpointAddress = (epDesc->bEndpointAddress & USB_DESCRIPTOR_ENDPOINT_ADDRESS_NUMBER_MASK);
            pipeInit.maxPacketSize = (uint16_t)(USB_SHORT_FROM_LITTLE_ENDIAN_ADDRESS(epDesc->wMaxPacketSize) &
                                                USB_DESCRIPTOR_ENDPOINT_MAXPACKETSIZE_SIZE_MASK);
            pipeInit.numberPerUframe = (USB_SHORT_FROM_LITTLE_ENDIAN_ADDRESS(epDesc->wMaxPacketSize) &
                                        USB_DESCRIPTOR_ENDPOINT_MAXPACKETSIZE_MULT_TRANSACTIONS_MASK);
            pipeInit.nakCount = USB_HOST_CONFIG_MAX_NAK;
            status = USB_HostOpenPipe(phdcInstance->hostHandle, &phdcInstance->bulkInPipe, &pipeInit);
            if (status != kStatus_USB_Success)
            {
#ifdef HOST_ECHO
                usb_echo("USB_HostPhdcOpenInterface: Error when open pipe\r\n");
#endif
                return kStatus_USB_Error;
            }
            /* save bulk in endpoint information */
            phdcInstance->bulkInEndpointInformation = interface->epList[epIndex];
        }
        else if (((epDesc->bEndpointAddress & USB_DESCRIPTOR_ENDPOINT_ADDRESS_DIRECTION_MASK) ==
                  USB_DESCRIPTOR_ENDPOINT_ADDRESS_DIRECTION_OUT) &&
                 ((epDesc->bmAttributes & USB_DESCRIPTOR_ENDPOINT_ATTRIBUTE_TYPE_MASK) == USB_ENDPOINT_BULK))
        {
            /* Initialize bulk out pipe */
            pipeInit.devInstance = phdcInstance->deviceHandle;
            pipeInit.pipeType = USB_ENDPOINT_BULK;
            pipeInit.direction = USB_OUT;
            pipeInit.endpointAddress = (epDesc->bEndpointAddress & USB_DESCRIPTOR_ENDPOINT_ADDRESS_NUMBER_MASK);
            pipeInit.maxPacketSize = (uint16_t)(USB_SHORT_FROM_LITTLE_ENDIAN_ADDRESS(epDesc->wMaxPacketSize) &
                                                USB_DESCRIPTOR_ENDPOINT_MAXPACKETSIZE_SIZE_MASK);
            pipeInit.numberPerUframe = (USB_SHORT_FROM_LITTLE_ENDIAN_ADDRESS(epDesc->wMaxPacketSize) &
                                        USB_DESCRIPTOR_ENDPOINT_MAXPACKETSIZE_MULT_TRANSACTIONS_MASK);
            pipeInit.nakCount = USB_HOST_CONFIG_MAX_NAK;
            status = USB_HostOpenPipe(phdcInstance->hostHandle, &phdcInstance->bulkOutPipe, &pipeInit);
            if (status != kStatus_USB_Success)
            {
#ifdef HOST_ECHO
                usb_echo("USB_HostPhdcOpenInterface: Error when open pipe\r\n");
#endif
                return kStatus_USB_Error;
            }
            /* save bulk out endpoint information */
            phdcInstance->bulkOutEndpointInformation = interface->epList[epIndex];
        }
        else
        {
        }
    }
    return kStatus_USB_Success;
}

/*!
 * @brief phdc set interface callback, open pipes.
 *
 * @param param       callback parameter.
 * @param transfer    callback transfer.
 * @param status      transfer status.
 */
static void USB_HostPhdcSetInterfaceCallback(void *param, usb_host_transfer_t *transfer, usb_status_t status)
{
    usb_host_phdc_instance_t *phdcInstance = (usb_host_phdc_instance_t *)param;

    phdcInstance->controlTransfer = NULL;
    if (status == kStatus_USB_Success)
    {
        /* set interface is done, open the interface */
        status = USB_HostPhdcOpenInterface(phdcInstance);
    }

    if (NULL != phdcInstance->controlCallbackFn)
    {
        /* Notify to application the status of set interface request */
        phdcInstance->controlCallbackFn(phdcInstance->controlCallbackParam, NULL, 0U, status);
    }
    USB_HostFreeTransfer(phdcInstance->hostHandle, transfer);
}

/*!
 * @brief set interface.
 *
 * This function binds the interface with the phdc instance.
 *
 * @param classHandle      the class handle.
 * @param interfaceHandle  the interface handle.
 * @param alternateSetting the alternate setting value.
 * @param callbackFn       this callback is called after this function completes.
 * @param callbackParam    the first parameter in the callback function.
 *
 * @retval kStatus_USB_Success        The device is initialized successfully.
 * @retval kStatus_USB_InvalidHandle  The classHandle is NULL pointer.
 * @retval kStatus_USB_Busy           There is no idle transfer.
 * @retval kStatus_USB_Error          send transfer fail, please reference to USB_HostSendSetup.
 * @retval kStatus_USB_Busy           callback return status, there is no idle pipe.
 * @retval kStatus_USB_TransferStall  callback return status, the transfer is stall by device.
 * @retval kStatus_USB_Error          callback return status, open pipe fail, please reference to USB_HostOpenPipe.
 */
usb_status_t USB_HostPhdcSetInterface(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_phdc_instance_t *phdcInstance = (usb_host_phdc_instance_t *)classHandle;
    usb_host_transfer_t *transfer;

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

    phdcInstance->interfaceHandle = interfaceHandle;

    status = USB_HostOpenDeviceInterface(phdcInstance->deviceHandle, interfaceHandle);

    /* Cancel interrupt transfers */
    if (NULL != phdcInstance->interruptPipe)
    {
        status = USB_HostCancelTransfer(phdcInstance->hostHandle, phdcInstance->interruptPipe, NULL);
        if (status != kStatus_USB_Success)
        {
#ifdef HOST_ECHO
            usb_echo("USB_HostPhdcSetInterface: Error when cancel pipe\r\n");
#endif
        }
    }
    /* Cancel bulk in transfers */
    if (NULL != phdcInstance->bulkInPipe)
    {
        status = USB_HostCancelTransfer(phdcInstance->hostHandle, phdcInstance->bulkInPipe, NULL);
        if (status != kStatus_USB_Success)
        {
#ifdef HOST_ECHO
            usb_echo("USB_HostPhdcSetInterface: Error when cancel pipe\r\n");
#endif
        }
    }
    /* Cancel bulk out transfers */
    if (NULL != phdcInstance->bulkOutPipe)
    {
        status = USB_HostCancelTransfer(phdcInstance->hostHandle, phdcInstance->bulkOutPipe, NULL);
        if (status != kStatus_USB_Success)
        {
#ifdef HOST_ECHO
            usb_echo("USB_HostPhdcSetInterface: Error when cancel pipe\r\n");
#endif
        }
        phdcInstance->bulkOutPipe = NULL;
    }
    if (0U == alternateSetting)
    {
        if (NULL != callbackFn)
        {
            status = USB_HostPhdcOpenInterface(phdcInstance);
            callbackFn(callbackParam, NULL, 0U, status);
        }
    }
    else
    {
        /* Create transfer buffer */
        if (USB_HostMallocTransfer(phdcInstance->hostHandle, &transfer) != kStatus_USB_Success)
        {
#ifdef HOST_ECHO
            usb_echo("USB_HostPhdcSetInterface: Error to get transfer\r\n");
#endif
            return kStatus_USB_Error;
        }
        /* Save application callback function and parameter */
        phdcInstance->controlCallbackFn = callbackFn;
        phdcInstance->controlCallbackParam = callbackParam;
        /* Initialize transfer */
        transfer->callbackFn = USB_HostPhdcSetInterfaceCallback;
        transfer->callbackParam = phdcInstance;
        transfer->setupPacket.bRequest = USB_REQUSET_STANDARD_SET_INTERFACE;
        transfer->setupPacket.bmRequestType = USB_REQUEST_TYPE_RECIPIENT_INTERFACE;
        transfer->setupPacket.wIndex = USB_SHORT_TO_LITTLE_ENDIAN(
            ((usb_host_interface_t *)phdcInstance->interfaceHandle)->interfaceDesc->bInterfaceNumber);
        transfer->setupPacket.wValue = USB_SHORT_TO_LITTLE_ENDIAN(alternateSetting);
        transfer->setupPacket.wLength = 0;
        transfer->transferBuffer = NULL;
        transfer->transferLength = 0;
        /* Send set interface request to device */
        status = USB_HostSendSetup(phdcInstance->hostHandle, phdcInstance->controlPipe, transfer);
        if (status == kStatus_USB_Success)
        {
            phdcInstance->controlTransfer = transfer;
        }
        else
        {
            USB_HostFreeTransfer(phdcInstance->hostHandle, transfer);
        }
    }
    return status;
}

/*!
 * @brief initialize the phdc instance.
 *
 * This function allocates the resource for phdc instance.
 *
 * @param deviceHandle       the device handle.
 * @param classHandle        return class handle.
 *
 * @retval kStatus_USB_Success        The device is initialized successfully.
 * @retval kStatus_USB_AllocFail      Allocate memory fail.
 */
usb_status_t USB_HostPhdcInit(usb_host_handle deviceHandle, usb_host_class_handle *classHandle)
{
    usb_host_phdc_instance_t *phdcInstance =
        (usb_host_phdc_instance_t *)USB_OsaMemoryAllocate(sizeof(usb_host_phdc_instance_t));
    uint32_t infoValue;

    if (NULL == phdcInstance)
    {
        return kStatus_USB_AllocFail;
    }
    /* Initialize PHDC instance */
    phdcInstance->deviceHandle = deviceHandle;
    USB_HostHelperGetPeripheralInformation(deviceHandle, kUSB_HostGetHostHandle, &infoValue);
    phdcInstance->hostHandle = (usb_host_handle)infoValue;
    USB_HostHelperGetPeripheralInformation(deviceHandle, kUSB_HostGetDeviceControlPipe, &infoValue);
    phdcInstance->controlPipe = (usb_host_pipe_handle)infoValue;

    *classHandle = phdcInstance;
    return kStatus_USB_Success;
}

/*!
 * @brief de-initialize the phdc instance.
 *
 * This function release the resource for phdc instance.
 *
 * @param deviceHandle   the device handle.
 * @param classHandle the class handle.
 *
 * @retval kStatus_USB_Success        The device is de-initialized successfully.
 */
usb_status_t USB_HostPhdcDeinit(usb_host_handle deviceHandle, usb_host_class_handle classHandle)
{
    usb_status_t status;
    usb_host_phdc_instance_t *phdcInstance = (usb_host_phdc_instance_t *)classHandle;

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

    if (NULL != classHandle)
    {
        if (NULL != phdcInstance->interruptPipe)
        {
            /* Cancel/close interrupt transfers/pipe */
            status = USB_HostCancelTransfer(phdcInstance->hostHandle, phdcInstance->interruptPipe, NULL);
            status = USB_HostClosePipe(phdcInstance->hostHandle, phdcInstance->interruptPipe);

            if (status != kStatus_USB_Success)
            {
#ifdef HOST_ECHO
                usb_echo("USB_HostPhdcDeinit: Error when close pipe\r\n");
#endif
            }
            phdcInstance->interruptPipe = NULL;
        }
        if (NULL != phdcInstance->bulkInPipe)
        {
            /* Cancel/close bulk in transfers/pipe */
            status = USB_HostCancelTransfer(phdcInstance->hostHandle, phdcInstance->bulkInPipe, NULL);
            status = USB_HostClosePipe(phdcInstance->hostHandle, phdcInstance->bulkInPipe);

            if (status != kStatus_USB_Success)
            {
#ifdef HOST_ECHO
                usb_echo("USB_HostPhdcDeinit: Error when close pipe\r\n");
#endif
            }
            phdcInstance->bulkInPipe = NULL;
        }
        if (NULL != phdcInstance->bulkOutPipe)
        {
            /* Cancel/close bulk out transfers/pipe */
            status = USB_HostCancelTransfer(phdcInstance->hostHandle, phdcInstance->bulkOutPipe, NULL);
            status = USB_HostClosePipe(phdcInstance->hostHandle, phdcInstance->bulkOutPipe);

            if (status != kStatus_USB_Success)
            {
#ifdef HOST_ECHO
                usb_echo("USB_HostPhdcDeinit: Error when close pipe\r\n");
#endif
            }
            phdcInstance->bulkOutPipe = NULL;
        }
        if ((NULL != phdcInstance->controlPipe) && (NULL != phdcInstance->controlTransfer))
        {
            /* Cancel control transfers */
            status = USB_HostCancelTransfer(phdcInstance->hostHandle, phdcInstance->controlPipe,
                                            phdcInstance->controlTransfer);
        }
        /* Close device interface */
        USB_HostCloseDeviceInterface(deviceHandle, phdcInstance->interfaceHandle);
        /* Release PHDC instance */
        USB_OsaMemoryFree(phdcInstance);
    }
    else
    {
        USB_HostCloseDeviceInterface(deviceHandle, NULL);
    }
    return kStatus_USB_Success;
}

/*!
 * @brief receive data.
 *
 * This function implements phdc receiving data.
 *
 * @param classHandle   the class handle.
 * @param qos           QoS of the data being received.
 * @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        receive request 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_HostRecv.
 */
usb_status_t USB_HostPhdcRecv(usb_host_class_handle classHandle,
                              uint8_t qos,
                              uint8_t *buffer,
                              uint32_t bufferLength,
                              transfer_callback_t callbackFn,
                              void *callbackParam)
{
    usb_host_phdc_instance_t *phdcInstance = (usb_host_phdc_instance_t *)classHandle;
    usb_host_transfer_t *transfer;
    usb_host_pipe_handle pipe;
    usb_host_phdc_qos_descriptor_t *qosDesc = NULL;

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

    if ((NULL == phdcInstance->interruptPipe) && (NULL == phdcInstance->bulkInPipe))
    {
        return kStatus_USB_Error;
    }
    /* Allocate the transfer buffer */
    if (USB_HostMallocTransfer(phdcInstance->hostHandle, &transfer) != kStatus_USB_Success)
    {
#ifdef HOST_ECHO
        usb_echo("error to get transfer\r\n");
#endif
        return kStatus_USB_Error;
    }
    /* Save application callback function and parameter */
    phdcInstance->inCallbackFn = callbackFn;
    phdcInstance->inCallbackParam = callbackParam;
    /* Initialize the transfer pointer */
    transfer->transferBuffer = buffer;
    transfer->transferLength = bufferLength;
    transfer->callbackParam = phdcInstance;
    /* The on can receive the data on interrupt pipe or bulk in pipe depends on the QoS value */
    pipe = (qos & 0x01U) ? (phdcInstance->interruptPipe) : (phdcInstance->bulkInPipe);
    if (pipe == phdcInstance->bulkInPipe)
    {
        /* get bulk in QoS descriptor */
        qosDesc = (usb_host_phdc_qos_descriptor_t *)phdcInstance->bulkInEndpointInformation.epExtension;
        transfer->callbackFn = USB_HostPhdcBulkInPipeCallback;
    }
    else
    {
        /* get interrupt in QoS descriptor */
        qosDesc = (usb_host_phdc_qos_descriptor_t *)phdcInstance->interruptInEndpointInformation.epExtension;
        transfer->callbackFn = USB_HostPhdcInterruptPipeCallback;
    }
    /* Latency and reliability checking */
    if (!(qos & qosDesc->bmLatencyReliability))
    {
#ifdef HOST_ECHO
        usb_echo("USB_HostPhdcRecv, ERROR: invalid QoS bin");
#endif
        return kStatus_USB_Error;
    }
    /* The previous control transfer is pending */
    if (NULL != phdcInstance->controlTransfer)
    {
#ifdef HOST_ECHO
        usb_echo("USB_HostPhdcRecv, ERROR: Control transfer is in progress");
#endif
        return kStatus_USB_Busy;
    }
    if (USB_HostRecv(phdcInstance->hostHandle, pipe, transfer) != kStatus_USB_Success)
    {
#ifdef HOST_ECHO
        usb_echo("fail to USB_HostRecv\r\n");
#endif
        USB_HostFreeTransfer(phdcInstance->hostHandle, transfer);
        return kStatus_USB_Error;
    }

    return kStatus_USB_Success;
}

/*!
 * @brief send data.
 *
 * This function implements phdc sending data.
 *
 * @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 request 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_HostSend.
 */
usb_status_t USB_HostPhdcSend(usb_host_class_handle classHandle,
                              uint8_t *buffer,
                              uint32_t bufferLength,
                              transfer_callback_t callbackFn,
                              void *callbackParam)
{
    usb_host_phdc_instance_t *phdcInstance = (usb_host_phdc_instance_t *)classHandle;
    usb_host_transfer_t *transfer;
    usb_status_t status;
    if (classHandle == NULL)
    {
        return kStatus_USB_InvalidHandle;
    }

    if (NULL == phdcInstance->bulkOutPipe)
    {
        return kStatus_USB_Error;
    }
    if (NULL != phdcInstance->controlTransfer)
    {
        status = kStatus_USB_Busy;
#ifdef HOST_ECHO
        usb_echo("USB_HostPhdcSend, Error: control transfer is in progress");
#endif
        return status;
    }
    /* The meta-data message preamble is implemented and enabled */
    if (phdcInstance->isMessagePreambleEnabled == 1U)
    {
        /* The meta-data message preamble feature is enabled, then all data transfers or sets
        of data transfers shall be preceded by a meta-data message preamble transfer. The
        numberTransferBulkOut is initialized as zero for sending this message preamble data,
        then it is updated to the value of bNumTransfers field of message preamble data */
        if (phdcInstance->numberTransferBulkOut)
        {
            /* When numberTransferBulkOut reduces to 0, a new meta-data message preamble shall
            be transferred */
            phdcInstance->numberTransferBulkOut--;
        }
        else
        {
            usb_host_phdc_qos_descriptor_t *qosDesc =
                (usb_host_phdc_qos_descriptor_t *)phdcInstance->bulkOutEndpointInformation.epExtension;
            ;
            uint8_t latencyReliability = ((usb_host_phdc_metadata_preamble_t *)buffer)->bmLatencyReliability;
            /* Latency reliability validity checking */
            if ((latencyReliability != 0x02U) && /* Medium.Good latency, reliability bin */
                (latencyReliability != 0x04U) && /* Medium.Better latency, reliability bin */
                (latencyReliability != 0x08U) && /* Medium.Best latency, reliability bin */
                (latencyReliability != 0x10U) && /* High.Best latency, reliability bin */
                (latencyReliability != 0x20U) /* VeryHigh.Best latency, reliability bin */)
            {
                status = kStatus_USB_InvalidRequest;
#ifdef HOST_ECHO
                usb_echo("USB_HostPhdcSend, Error: invalid LatencyReliability");
#endif
                return status;
            }
            /* LatencyReliablity checking */
            if (0U == (qosDesc->bmLatencyReliability & latencyReliability))
            {
                status = kStatus_USB_Error;
#ifdef HOST_ECHO
                usb_echo("USB_HostPhdcSend, Error: the latency reliability is not supported by Bulk OUT endpoint");
#endif
                return status;
            }
            if (0U == ((usb_host_phdc_metadata_preamble_t *)buffer)->bNumberTransfers)
            {
                status = kStatus_USB_Error;
#ifdef HOST_ECHO
                usb_echo("USB_HostPhdcSend, Error: the numTransfer should never zero");
#endif
                return status;
            }
            /* Update the number of bulk out transfer */
            phdcInstance->numberTransferBulkOut = ((usb_host_phdc_metadata_preamble_t *)buffer)->bNumberTransfers;
        }
    }
    /* Allocate the transfer pointer */
    if (USB_HostMallocTransfer(phdcInstance->hostHandle, &transfer) != kStatus_USB_Success)
    {
#ifdef HOST_ECHO
        usb_echo("error to get transfer\r\n");
#endif
        return kStatus_USB_Error;
    }
    /* Save the application callback function and parameter */
    phdcInstance->outCallbackFn = callbackFn;
    phdcInstance->outCallbackParam = callbackParam;
    /* Initialize the transfer pointer */
    transfer->transferBuffer = buffer;
    transfer->transferLength = bufferLength;
    transfer->callbackFn = USB_HostPhdcBulkOutPipeCallback;
    transfer->callbackParam = phdcInstance;
    if (USB_HostSend(phdcInstance->hostHandle, phdcInstance->bulkOutPipe, transfer) != kStatus_USB_Success)
    {
#ifdef HOST_ECHO
        usb_echo("fail to USB_HostSend\r\n");
#endif
        USB_HostFreeTransfer(phdcInstance->hostHandle, transfer);
        return kStatus_USB_Error;
    }
    return kStatus_USB_Success;
}

/*!
 * @brief phdc sends control request.
 *
 * @param classHandle   the class handle.
 * @param request_type  setup packet request.
 * @param callbackFn    this callback is called after this function completes.
 * @param callbackParam the first parameter in the callback function.
 *
 * @retval kStatus_USB_Success        send request 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_HostSend.
 */
usb_status_t USB_HostPhdcSendControlRequest(usb_host_class_handle classHandle,
                                            uint8_t request,
                                            transfer_callback_t callbackFn,
                                            void *callbackParam)
{
    usb_host_phdc_instance_t *phdcInstance = (usb_host_phdc_instance_t *)classHandle;
    usb_status_t status = kStatus_USB_Success;
    usb_host_transfer_t *transfer;

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

    if (NULL == phdcInstance->controlPipe)
    {
        return kStatus_USB_Error;
    }

    if (NULL != phdcInstance->controlTransfer)
    {
        return kStatus_USB_Busy;
    }
    /* Allocate the transfer pointer */
    if (USB_HostMallocTransfer(phdcInstance->hostHandle, &transfer) != kStatus_USB_Success)
    {
#ifdef HOST_ECHO
        usb_echo("error to get transfer\r\n");
#endif
        return kStatus_USB_Error;
    }
    /* Save the callback function and parameter */
    phdcInstance->controlCallbackFn = callbackFn;
    phdcInstance->controlCallbackParam = callbackParam;
    /* Initialize the transfer pointer */
    transfer->callbackFn = USB_HostPhdcControlPipeCallback;
    transfer->callbackParam = phdcInstance;
    transfer->setupPacket.bRequest = request;
    transfer->setupPacket.wIndex = USB_SHORT_TO_LITTLE_ENDIAN(
        ((usb_host_interface_t *)phdcInstance->interfaceHandle)->interfaceDesc->bInterfaceNumber);
    switch (request)
    {
        case USB_HOST_PHDC_GET_STATUS_REQUEST:
            /* Initialize the PHDC get status request */
            transfer->setupPacket.wValue = 0U;
            transfer->setupPacket.bmRequestType =
                USB_REQUEST_TYPE_DIR_IN | USB_REQUEST_TYPE_TYPE_CLASS | USB_REQUEST_TYPE_RECIPIENT_INTERFACE;
            transfer->setupPacket.wLength = 2U;
            break;
        case USB_HOST_PHDC_SET_FEATURE_REQUEST:
        case USB_HOST_PHDC_CLEAR_FEATURE_REQUEST:
            /* Initialize the PHDC set/clear feature request */
            transfer->setupPacket.bmRequestType =
                USB_REQUEST_TYPE_DIR_OUT | USB_REQUEST_TYPE_TYPE_CLASS | USB_REQUEST_TYPE_RECIPIENT_INTERFACE;
            transfer->setupPacket.wValue =
                (USB_HOST_PHDC_FEATURE_METADATA | (uint16_t)((uint16_t)USB_HOST_PHDC_QOS_ENCODING_VERSION << 8U));
            transfer->setupPacket.wLength = 0U;
            break;
        default:
            status = kStatus_USB_InvalidRequest;
            break;
    }
    if (USB_HostSendSetup(phdcInstance->hostHandle, phdcInstance->controlPipe, transfer) != kStatus_USB_Success)
    {
#ifdef HOST_ECHO
        usb_echo("fail for USB_HostSendSetup\r\n");
#endif
        USB_HostFreeTransfer(phdcInstance->hostHandle, transfer);
        return kStatus_USB_Error;
    }
    phdcInstance->controlTransfer = transfer;

    return status;
}

/*!
 * @brief phdc set and clear feature endpoint halt request.
 *
 * @param classHandle   the class handle.
 * @param request       setup packet request.
 * @param param         request parameter
 * @param callbackFn    this callback is called after this function completes.
 * @param callbackParam the first parameter in the callback function.
 *
 * @retval kStatus_USB_Success        send request 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_HostSend.
 */
usb_status_t USB_HostPhdcSetClearFeatureEndpointHalt(usb_host_class_handle classHandle,
                                                     uint8_t request,
                                                     void *param,
                                                     transfer_callback_t callbackFn,
                                                     void *callbackParam)
{
    usb_host_phdc_instance_t *phdcInstance = (usb_host_phdc_instance_t *)classHandle;
    usb_host_transfer_t *transfer;
    if (NULL == classHandle)
    {
        return kStatus_USB_InvalidHandle;
    }

    if (NULL == phdcInstance->controlPipe)
    {
        return kStatus_USB_Error;
    }

    if (NULL != phdcInstance->controlTransfer)
    {
        return kStatus_USB_Busy;
    }
    /* Allocate the transfer pointer */
    if (USB_HostMallocTransfer(phdcInstance->hostHandle, &transfer) != kStatus_USB_Success)
    {
#ifdef HOST_ECHO
        usb_echo("error to get transfer\r\n");
#endif
        return kStatus_USB_Error;
    }
    /* Save application callback function and parameter */
    phdcInstance->controlCallbackFn = callbackFn;
    phdcInstance->controlCallbackParam = callbackParam;
    /* Initialize the transfer request */
    transfer->callbackFn = USB_HostPhdcSetClearFeatureEndpointHaltCallback;
    transfer->callbackParam = phdcInstance;
    if (USB_HostRequestControl(phdcInstance->hostHandle, request, transfer, param))
    {
#ifdef HOST_ECHO
        usb_echo("fail for USB_HostRequestControl\r\n");
#endif
        USB_HostFreeTransfer(phdcInstance->hostHandle, transfer);
        return kStatus_USB_Error;
    }
    phdcInstance->controlTransfer = transfer;

    return kStatus_USB_Success;
}

/*!
 * @brief USB_HostPhdcGetEndpointInformation.
 * This function returns the PHDC endpoint information structure contains endpoint
 * descriptor and endpoint extended descriptor.
 *
 * @param classHandle   the class handle.
 * @param pipeType      pipe type.
 * @param direction     pipe direction.
 *
 * @retval endpointReturn   All input parameters are valid.
 * @retval NULL             One or more input parameters are invalid.
 */
usb_host_ep_t *USB_HostPhdcGetEndpointInformation(usb_host_class_handle classHandle,
                                                  uint8_t pipeType,
                                                  uint8_t direction)
{
    usb_host_phdc_instance_t *phdcInstance = (usb_host_phdc_instance_t *)classHandle;
    usb_host_ep_t *endpointReturn = NULL;
    if (NULL != classHandle)
    {
        if (pipeType == USB_ENDPOINT_BULK)
        {
            if (direction == USB_IN)
            {
                /* bulk in endpoint information */
                endpointReturn = (usb_host_ep_t *)&phdcInstance->bulkInEndpointInformation;
            }
            else
            {
                /* bulk out endpoint information */
                endpointReturn = (usb_host_ep_t *)&phdcInstance->bulkOutEndpointInformation;
            }
        }
        else if (pipeType == USB_ENDPOINT_INTERRUPT)
        {
            /* interrupt in endpoint information */
            endpointReturn = (usb_host_ep_t *)&phdcInstance->interruptInEndpointInformation;
        }
        else
        {
        }
    }
    return endpointReturn;
}
#endif
