//
// Copyright 2011-2012 Cypress Semiconductor Corporation
// Copyright 2019 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: GPL-3.0-or-later
//

#include "cyfx3usb.h"
#include "cyfx3device.h"
#include "cyfx3utils.h"
#include "cyfx3i2c.h"

#include "common_helpers.h"
#include "common_descriptors.h"
#include "usb_descriptors.h"

typedef enum
{
    eStall = 0,     /* Send STALL */
    eDataIn,        /* Data IN Stage */
    eDataOut,       /* Data Out Stage */
    eStatus         /* Status Stage */
} eUsbStage;

typedef int (*PFI)();

#define USB_SETUP_DIR               (0x80) /* 0x80 = To Host */
#define USB_SETUP_MASK              (0x60) /* Used to mask off request type */
#define USB_STANDARD_REQUEST        (0x00) /* Standard Request */
#define USB_VENDOR_REQUEST          (0x40) /* Vendor Request */
#define USB_REQ_MASK                (0x3F) /* USB Request mask */
#define USB_REQ_DEV                 (0)    /* Device Request */
#define USB_REQ_INTERFACE           (1)    /* Interface Request */
#define USB_REQ_ENDPOINT            (2)    /* Endpoint Request */
#define USB_SET_INTERFACE           (11)
#define USB_SC_SET_SEL              (0x30) /* Set system exit latency. */
#define USB_SC_SET_ISOC_DELAY       (0x31)

#define SELF_POWERED  (0x01)
#define REMOTE_WAKEUP (0x02)
#define U1_ENABLE     (0x04)
#define U2_ENABLE     (0x08)
#define LTM_ENABLE    (0x10)

uint8_t glUsbState = 0;
uint8_t gConfig = 0;      /* Variable to hold the config info. */
uint8_t gAltSetting = 0;  /* Variable to hold the interface info. */
uint8_t gUsbDevStatus = 0;
uint8_t glCheckForDisconnect = 0;
uint8_t glInCompliance = 0;

/* 4KB of buffer area used for control endpoint transfers. */
#define gpUSBData                   (uint8_t*)(0x40077000)
#define USB_DATA_BUF_SIZE           (1024*4)

CyU3PUsbDescrPtrs   *gpUsbDescPtr; /* Pointer to the USB Descriptors */
CyFx3BootUsbEp0Pkt_t gEP0;

void myMemCopy(uint8_t* d, uint8_t* s, int32_t cnt)
{
    int32_t i;
    for (i = 0; i < cnt; i++) {
        *d++ = *s++;
    }
}

void myMemSet(uint8_t* d, uint8_t c, int32_t cnt)
{
    int32_t i;
    for (i = 0; i < cnt; i++) {
        *d++ = c;
    }
}

/************************************************
 * Request handlers
 ***********************************************/

/* Function to handle the GET_STATUS Standard request. */
int getStatus (void)
{
    uint16_t data = 0;

    switch (gEP0.bmReqType & USB_REQ_MASK)
    {
        case USB_REQ_DEV:
            if (CyFx3BootUsbGetSpeed () == CY_FX3_BOOT_SUPER_SPEED)
            {
                data = (gpUsbDescPtr->usbSSConfigDesc_p[7] & 0x40) ? 1 : 0;
                data |= gUsbDevStatus;
            }
            else
            {
                data = (gpUsbDescPtr->usbHSConfigDesc_p[7] & 0x40) ? 1 : 0;
                data |= gUsbDevStatus;
            }
            break;

        case USB_REQ_INTERFACE:
            if (!gConfig)
            {
                return eStall;
            }
            break;

        case USB_REQ_ENDPOINT:
            if (CyFx3BootUsbGetEpCfg (gEP0.bIdx0, 0, (CyBool_t *)&data) != 0)
            {
                return eStall;
            }
            break;
        default:
            return eStall;
    }

    *(uint16_t*)gEP0.pData = data;
    return eDataIn;
}

/* Function to handle the GET_DESCRIPTOR Standard request */
int getDescriptor (void)
{
    uint32_t len = 0;
    uint8_t *p = 0;
    uint8_t *cfg_p = 0;
    uint8_t usbSpeed;

    usbSpeed = CyFx3BootUsbGetSpeed();

    gpUsbDescPtr->usbHSConfigDesc_p[1] = CY_U3P_USB_CONFIG_DESCR;
    gpUsbDescPtr->usbFSConfigDesc_p[1] = CY_U3P_USB_CONFIG_DESCR;

    if (usbSpeed == CY_FX3_BOOT_HIGH_SPEED)
    {
        cfg_p = (uint8_t*)gpUsbDescPtr->usbHSConfigDesc_p;
        len = ((gpUsbDescPtr->usbHSConfigDesc_p[3] << 8) | gpUsbDescPtr->usbHSConfigDesc_p[2]);
    }
    else if (usbSpeed == CY_FX3_BOOT_SUPER_SPEED)
    {
        cfg_p = (uint8_t*)gpUsbDescPtr->usbSSConfigDesc_p;
        len = ((gpUsbDescPtr->usbSSConfigDesc_p[3] << 8) | gpUsbDescPtr->usbSSConfigDesc_p[2]);
    }
    else if (usbSpeed == CY_FX3_BOOT_FULL_SPEED)
    {
        cfg_p = (uint8_t*)gpUsbDescPtr->usbFSConfigDesc_p;
        len = ((gpUsbDescPtr->usbFSConfigDesc_p[3] << 8) | gpUsbDescPtr->usbFSConfigDesc_p[2]);
    }

    switch (gEP0.bVal1)
    {
        case CY_U3P_USB_DEVICE_DESCR:
            {
                if ((usbSpeed == CY_FX3_BOOT_HIGH_SPEED) || (usbSpeed == CY_FX3_BOOT_FULL_SPEED))
                {
                    p = (uint8_t*)gpUsbDescPtr->usbDevDesc_p;
                    len = gpUsbDescPtr->usbDevDesc_p[0];
                }
                else if (usbSpeed == CY_FX3_BOOT_SUPER_SPEED)
                {
                    p = (uint8_t*)gpUsbDescPtr->usbSSDevDesc_p;
                    len = gpUsbDescPtr->usbSSDevDesc_p[0];
                }
                break;
            }
        case CY_U3P_BOS_DESCR:
            {
                p = (uint8_t *)gpUsbDescPtr->usbSSBOSDesc_p;
                len = (gpUsbDescPtr->usbSSBOSDesc_p[3] << 8) | gpUsbDescPtr->usbSSBOSDesc_p[2];
                break;
            }
        case CY_U3P_USB_CONFIG_DESCR:
            {
                p = cfg_p;
                break;
            }
        case CY_U3P_USB_DEVQUAL_DESCR:
            {
                if ((usbSpeed == CY_FX3_BOOT_HIGH_SPEED)  || (usbSpeed == CY_FX3_BOOT_FULL_SPEED))
                {
                    p = (uint8_t*)gpUsbDescPtr->usbDevQualDesc_p;
                    len = gpUsbDescPtr->usbDevQualDesc_p[0];
                    break;
                }
                return eStall;
            }
        case CY_U3P_USB_STRING_DESCR:
            {
                /* Ensure that we do not index past the limit of the array. */
                if (gEP0.bVal0 < CY_FX3_USB_MAX_STRING_DESC_INDEX)
                {
                    p = (uint8_t*)gpUsbDescPtr->usbStringDesc_p[gEP0.bVal0];
                    if (p != 0)
                        len = p[0];
                }
                else
                    return eStall;
                break;
            }
        case CY_U3P_USB_OTHERSPEED_DESCR:
            {
                if (usbSpeed == CY_FX3_BOOT_HIGH_SPEED)
                {
                    gpUsbDescPtr->usbFSConfigDesc_p[1] = CY_U3P_USB_OTHERSPEED_DESCR;
                    p = (uint8_t*)gpUsbDescPtr->usbFSConfigDesc_p;

                    len = ((gpUsbDescPtr->usbFSConfigDesc_p[3] < 8) | gpUsbDescPtr->usbFSConfigDesc_p[2]);

                    if (len > gEP0.wLen)
                    {
                        len = gEP0.wLen;
                    }
                }
                else if (usbSpeed == CY_FX3_BOOT_FULL_SPEED)
                {
                    gpUsbDescPtr->usbHSConfigDesc_p[1] = CY_U3P_USB_OTHERSPEED_DESCR;
                    p = (uint8_t*)gpUsbDescPtr->usbHSConfigDesc_p;
                    len = ((gpUsbDescPtr->usbHSConfigDesc_p[3] < 8) | gpUsbDescPtr->usbHSConfigDesc_p[2]);

                    if (len > gEP0.wLen)
                    {
                        len = gEP0.wLen;
                    }
                }
            }
            break;
        default:
            {
                return eStall;
            }
    }

    if (p != 0)
    {
        myMemCopy (gpUSBData, p, len);
        if (gEP0.wLen > len)
        {
            gEP0.wLen = len;
        }

        return eDataIn;
    }
    else
        /* Stall EP0 if the descriptor sought is not available. */
        return eStall;
}

/* Function to handle the SET_CONFIG Standard request */
int setConfig (void)
{
    uint8_t usbSpeed = 0;
    uint32_t retVal  = 0;
    CyFx3BootUsbEpConfig_t epCfg;

    if ((gEP0.bVal0 == 0) || (gEP0.bVal0 == 1))
    {
        glUsbState = gEP0.bVal0;
        gConfig = gEP0.bVal0;

        /* Get the Bus speed */
        usbSpeed = CyFx3BootUsbGetSpeed();

        epCfg.pcktSize = 512;
        /* Based on the Bus Speed configure the endpoint packet size */
        if (usbSpeed == CY_FX3_BOOT_HIGH_SPEED)
        {
            epCfg.pcktSize = 512;
        }
        if (usbSpeed == CY_FX3_BOOT_SUPER_SPEED)
        {
            epCfg.pcktSize = 1024;
        }
        if (usbSpeed == CY_FX3_BOOT_FULL_SPEED)
        {
            epCfg.pcktSize = 64;
        }

        /* Producer Endpoint configuration */
        epCfg.enable = 1;
        epCfg.epType = CY_FX3_BOOT_USB_EP_BULK;
        epCfg.burstLen = 1;
        epCfg.streams = 0;
        epCfg.isoPkts = 0;

        /* Configure the Endpoint */
        retVal = CyFx3BootUsbSetEpConfig(0x01, &epCfg);
        if (retVal != 0)
        {
            /* TODO: Error Handling */
            return eStall;
        }

        /* Consumer Endpoint configuration */
        epCfg.enable = 1;
        epCfg.epType = CY_FX3_BOOT_USB_EP_BULK;
        epCfg.burstLen = 1;
        epCfg.streams = 0;
        epCfg.isoPkts = 0;

        /* Configure the Endpoint */
        retVal = CyFx3BootUsbSetEpConfig (0x81, &epCfg);
        if (retVal != 0)
        {
            /* TODO: Error Handling */
            return eStall;
        }

        return eStatus;
    }

    return eStall;
}

/* Function to handle the GET_INTERFACE Standard request */
int getInterface (void)
{
    if (gConfig == 0)
    {
        return eStall;
    }

    gEP0.pData = (uint8_t *)&gAltSetting;
    return eDataIn;
}

/* Function to handle the SET_INTERFACE Standard request */
int setInterface (void)
{
    gAltSetting = gEP0.bVal0;
    return eStatus;
}

/* This function returns stall for not supported requests. */
int stall (void)
{
    return eStall;
}

/* Function to handle the SET_ADDRESS Standard request. */
int setAddress (void)
{
    return eStatus;
}

/* Function to handle the CLEAR_FEATURE Standard request. */
int clearFeature (void)
{
    /* All of the actual handling for the CLEAR_FEATURE request is done in the API.
       We only need to update the device status flags here.
     */
    if (CyFx3BootUsbSetClrFeature (0, (CyBool_t)glUsbState, &gEP0) != 0)
    {
        return eStall;
    }

    if (gEP0.bmReqType == USB_REQ_DEV)
    {
        /* Update the device status flags as required. */
        if (CyFx3BootUsbGetSpeed () == CY_FX3_BOOT_SUPER_SPEED)
        {
            switch (gEP0.bVal0)
            {
            case 48:
                gUsbDevStatus &= ~U1_ENABLE;
                break;
            case 49:
                gUsbDevStatus &= ~U2_ENABLE;
                break;
            case 50:
                gUsbDevStatus &= ~LTM_ENABLE;
                break;
            default:
                break;
            }
        }
        else
        {
            if (gEP0.bVal0 == 1)
            {
                gUsbDevStatus &= ~REMOTE_WAKEUP;
            }
        }
    }

    return eStatus;
}

/* Function to handle the SET_FEATURE Standard request. */
int setFeature (void)
{
    /* All of the actual handling for the SET_FEATURE command is done in the API.
     * We only need to update the device status flags here.
     */
    if (CyFx3BootUsbSetClrFeature (1, (CyBool_t)glUsbState, &gEP0) != 0)
    {
        return eStall;
    }

    if (gEP0.bmReqType == USB_REQ_DEV)
    {
        /* Update the device status flags as required. */
        if (CyFx3BootUsbGetSpeed () == CY_FX3_BOOT_SUPER_SPEED)
        {
            switch (gEP0.bVal0)
            {
            case 48:
                gUsbDevStatus |= U1_ENABLE;
                break;
            case 49:
                gUsbDevStatus |= U2_ENABLE;
                break;
            case 50:
                gUsbDevStatus |= LTM_ENABLE;
                break;
            default:
                break;
            }
        }
        else
        {
            if (gEP0.bVal0 == 1)
            {
                gUsbDevStatus |= REMOTE_WAKEUP;
            }
        }
    }
    return eStatus;
}

/* Function to handle the GET_CONFIG Standard request. */
int getConfig (void)
{
    gEP0.pData = (uint8_t *)&gConfig;
    return eDataIn;
}

const PFI chapter9_cmds[] =
{
    getStatus,        /* USB_GET_STATUS         0 */
    clearFeature,     /* USB_CLEAR_FEATURE      1 */
    stall,            /* Reserved               2 */
    setFeature,       /* USB_SET_FEATURE        3 */
    stall,            /* Reserved               4 */
    setAddress,       /* USB_SET_ADDRESS        5 */
    getDescriptor,    /* USB_GET_DESCRIPTOR     6 */
    stall,            /* USB_SET_DESCRIPTOR     7 */
    getConfig,        /* USB_GET_CONFIGURATION  8 */
    setConfig,        /* USB_SET_CONFIGURATION  9 */
    getInterface,     /* USB_GET_INTERFACE     10 */
    setInterface,     /* USB_SET_INTERFACE     11 */
};

/* This function validates the addresses being written to/read from
   Return Value:
    0 - Address is valid
   -1 - Address is not valid
*/
int checkAddress (uint32_t address, uint32_t len)
{
    if (address & 3)
    {
        /* expect long word boundary */
        return -1;
    }

    len += address;

    if ((address >= CY_FX3_BOOT_SYSMEM_BASE1) && (len <= CY_FX3_BOOT_SYSMEM_END))
    {
        return 0;
    }

    if (len <= CY_FX3_BOOT_ITCM_END)
    {
        return 0;
    }

    return -1;
}

/* Function to handle the vendor commands. */
void vendorCmdHandler (void)
{
    int stage;
    int status;
    uint32_t address  = ((gEP0.bIdx1 << 24) | (gEP0.bIdx0 << 16) | (gEP0.bVal1 << 8) | (gEP0.bVal0));
    uint16_t len  = gEP0.wLen;
    uint16_t bReq = gEP0.bReq;
    uint16_t dir  = gEP0.bmReqType & USB_SETUP_DIR;

    if (len > USB_DATA_BUF_SIZE)
    {
        CyFx3BootUsbStall (0, CyTrue, CyFalse);
        return;
    }

    if (dir)
    {
        stage = eDataIn;
    }
    else
    {
        stage = eDataOut;
    }

    /* Vendor Command 0xA0 handling */
    if (bReq == 0xA0)
    {
	/* Note: This is a command issued by the CyControl Application to detect the legacy products.
	   As we are an FX3 device we stall the endpoint to indicate that this is not a legacy device.
	 */
	if (address == 0xE600)
	{
	    /* Stall the Endpoint */
	    CyFx3BootUsbStall (0, CyTrue, CyFalse);
	    return;
	}

        status = checkAddress (address, len);
        if (len == 0)
        {
            /* Mask the USB Interrupts and Disconnect the USB Phy. */
            CyFx3BootUsbConnect (CyFalse, CyTrue);
            /* Transfer to Program Entry */
            CyFx3BootJumpToProgramEntry (address);
            return;
        }

        if (status < 0)
        {
            /* Stall the endpoint */
            CyFx3BootUsbStall (0, CyTrue, CyFalse);
            return;
        }

        /* Validate the SYSMEM address being accessed */
        if ((address >= CY_FX3_BOOT_SYSMEM_BASE1) && (address < CY_FX3_BOOT_SYSMEM_END))
        {
            gEP0.pData = (uint8_t*)address;
        }

        CyFx3BootUsbAckSetup ();

        if (eDataIn == stage)
        {
            if ((address + gEP0.wLen) <= CY_FX3_BOOT_ITCM_END)
            {
                myMemCopy(gEP0.pData, (uint8_t *)address , len);
            }

            status = CyFx3BootUsbDmaXferData (0x80, (uint32_t)gEP0.pData, gEP0.wLen, CY_FX3_BOOT_WAIT_FOREVER);
            if (status != CY_FX3_BOOT_SUCCESS)
            {
                /* USB DMA Transfer failed. Stall the Endpoint. */
                CyFx3BootUsbStall (0, CyTrue, CyFalse);
                return;
            }
        }
        else if (stage == eDataOut)
        {
            status = CyFx3BootUsbDmaXferData (0x00, (uint32_t)gEP0.pData, gEP0.wLen, CY_FX3_BOOT_WAIT_FOREVER);
            if (status != CY_FX3_BOOT_SUCCESS)
            {
                /* USB DMA Transfer failed. Stall the Endpoint. */
                CyFx3BootUsbStall (0, CyTrue, CyFalse);
                return;
            }

            /* Validate ITCM Memory */
            if ((address + gEP0.wLen) <= CY_FX3_BOOT_ITCM_END)
            {
                /* Avoid writing to the interrupt table. */
                if (address < 0xFF) {
                    gEP0.pData += 0xFF-address;
                    gEP0.wLen -= 0xFF-address;
                    address = 0xFF;
                }
                myMemCopy((uint8_t *)address, gEP0.pData, gEP0.wLen);
            }
        }
        return;
    }

    /* Version request */
    if (dir && bReq == 0xB8) {
        uint8_t version_info[] = {1, 21, 0, 0, 0, 0};

        CyFx3BootUsbAckSetup ();
        status = CyFx3BootUsbDmaXferData (0x80, (uint32_t)version_info,
                sizeof(version_info), CY_FX3_BOOT_WAIT_FOREVER);
        if (status != CY_FX3_BOOT_SUCCESS) {
            /* USB DMA Transfer failed. Stall the Endpoint. */
            CyFx3BootUsbStall (0, CyTrue, CyFalse);
        }

        return;
    }

    /* Stall the Endpoint */
    CyFx3BootUsbStall (0, CyTrue, CyFalse);
    return;
}

/* Setup Data handler */
void setupDataHandler (uint32_t setupDat0, uint32_t setupDat1)
{
    uint32_t *p;
    int status = eStall;

    p = (uint32_t*)&gEP0;
    p[0] = setupDat0;
    p[1] = setupDat1;

    gEP0.pData = gpUSBData;

    switch (gEP0.bmReqType & USB_SETUP_MASK)
    {
        case USB_STANDARD_REQUEST:
            if (gEP0.bReq <= USB_SET_INTERFACE)
            {
                status = (*chapter9_cmds[gEP0.bReq])();
            }
            else
            {
                if (gEP0.bReq == USB_SC_SET_SEL)
                {
                    if ((CyFx3BootUsbGetSpeed () == CY_FX3_BOOT_SUPER_SPEED) && (gEP0.bIdx0 == 0) &&
                            (gEP0.bIdx1 == 0) && (gEP0.bVal0 == 0) && (gEP0.bVal1 == 0) && (gEP0.wLen == 6))
                    {
                        gEP0.wLen = 32;
                        status = eDataOut;
                    }
                    else
                    {
                        status = eStall;
                    }
                }
                else if (gEP0.bReq == USB_SC_SET_ISOC_DELAY)
                {
                    status = eStatus;
                    if ((CyFx3BootUsbGetSpeed () != CY_FX3_BOOT_SUPER_SPEED) || (gEP0.bIdx0 != 0) ||
                            (gEP0.bIdx1 != 0) || (gEP0.wLen != 0))
                    {
                        status = eStall;
                    }
                }
                else
                {
                    status = eStall;
                }
            }
            break;

        case USB_VENDOR_REQUEST:
            vendorCmdHandler();
            return;
    }

    switch (status)
    {
        case eDataIn:
            CyFx3BootUsbAckSetup ();
            status = CyFx3BootUsbDmaXferData (0x80, (uint32_t)gEP0.pData, gEP0.wLen, 1000);
            if (status != CY_FX3_BOOT_SUCCESS)
            {
                CyFx3BootUsbStall (0, CyTrue, CyFalse);
            }
            break;
        case eDataOut:
            CyFx3BootUsbAckSetup ();
            status = CyFx3BootUsbDmaXferData (0x00, (uint32_t)gEP0.pData, gEP0.wLen, CY_FX3_BOOT_WAIT_FOREVER);
            if (status != CY_FX3_BOOT_SUCCESS)
            {
                CyFx3BootUsbStall (0, CyTrue, CyFalse);
            }
            break;
        case eStatus:
            CyFx3BootUsbAckSetup ();
            break;
        default:
            CyFx3BootUsbStall (0, CyTrue, CyFalse);
            return;
    }

    return;
}

/* USB Event Handler. This function is invoked from the USB ISR and as such MUST not be
   blocked.
*/
void usbEventCallback (CyFx3BootUsbEventType_t event)
{
    if (event == CY_FX3_BOOT_USB_RESET)
    {
        gConfig              = 0;
        gAltSetting          = 0;
        gUsbDevStatus        = 0;
        glUsbState           = 0;
        glInCompliance       = 0;
    }

    if ((event == CY_FX3_BOOT_USB_CONNECT) ||
        (event == CY_FX3_BOOT_USB_DISCONNECT))
    {
        glUsbState    = 0;
        gUsbDevStatus = 0;
    }

    if (event == CY_FX3_BOOT_USB_IN_SS_DISCONNECT)
    {
        glCheckForDisconnect = CyTrue;
    }

    if (event == CY_FX3_BOOT_USB_COMPLIANCE)
    {
        glInCompliance = CyTrue;
    }

    return;
}

/* Function passed to common EEPROM functions to access the EEPROM */
void eeprom_read(uint16_t addr, uint8_t* buffer, uint8_t length)
{
    CyFx3BootI2cPreamble_t preamble;
    preamble.length    = 4;
    preamble.buffer[0] = 0xA0;
    preamble.buffer[1] = addr >> 8;
    preamble.buffer[2] = addr & 0xFF;
    preamble.buffer[3] = 0xA1;
    preamble.ctrlMask  = 0x0004;
    CyFx3BootI2cReceiveBytes(&preamble, buffer, length, 0);
}

/* USB initialization */
void usbBoot()
{
    /* Reset globals */
    gConfig              = 0;
    gAltSetting          = 0;
    gUsbDevStatus        = 0;
    glUsbState           = 0;
    glCheckForDisconnect = 0;
    glInCompliance       = 0;

    /* Init */
    CyFx3BootUsbStart (CyFalse, usbEventCallback);

    /* Run USB circuitry off of host power, not internal power. */
    CyFx3BootUsbVBattEnable (CyFalse);

    /* Register callbacks */
    CyFx3BootRegisterSetupCallback (setupDataHandler);

    /* Enable I2C for EEPROM access */
    CyFx3BootI2cInit();
    CyFx3BootI2cConfig_t i2cCfg = {
        .bitRate    = 100000,
        .isDma      = CyFalse,
        .busTimeout = 0xFFFFFFFF,
        .dmaTimeout = 0xFFFF};
    CyFx3BootI2cSetConfig(&i2cCfg);

    /* Retrieve VID and PID from EEPROM */
    const uint16_t vid = get_vid(&eeprom_read);
    const uint16_t pid = get_pid(&eeprom_read);

    /* Power down I2C */
    CyFx3BootI2cDeInit();

    /* Populate device descriptors with IDs */
    common_usb2_dev_desc[8]  = vid & 0xFF;
    common_usb2_dev_desc[9]  = vid >> 8;
    common_usb2_dev_desc[10] = pid & 0xFF;
    common_usb2_dev_desc[11] = pid >> 8;

    common_usb3_dev_desc[8]  = vid & 0xFF;
    common_usb3_dev_desc[9]  = vid >> 8;
    common_usb3_dev_desc[10] = pid & 0xFF;
    common_usb3_dev_desc[11] = pid >> 8;

    /* Copy all descriptors */
    CyFx3BootUsbSetDesc(
        CY_U3P_USB_SET_HS_DEVICE_DESCR, 0, (uint8_t*)common_usb2_dev_desc);
    CyFx3BootUsbSetDesc(
        CY_U3P_USB_SET_SS_DEVICE_DESCR, 0, (uint8_t*)common_usb3_dev_desc);

    CyFx3BootUsbSetDesc(CY_U3P_USB_SET_DEVQUAL_DESCR, 0, (uint8_t*)common_dev_qual_desc);

    CyFx3BootUsbSetDesc(CY_U3P_USB_SET_HS_CONFIG_DESCR, 0, (uint8_t*)bl_hs_config_desc);
    CyFx3BootUsbSetDesc(CY_U3P_USB_SET_FS_CONFIG_DESCR, 0, (uint8_t*)bl_fs_config_desc);
    CyFx3BootUsbSetDesc(CY_U3P_USB_SET_SS_CONFIG_DESCR, 0, (uint8_t*)bl_ss_config_desc);

    CyFx3BootUsbSetDesc(CY_U3P_USB_SET_SS_BOS_DESCR, 0, (uint8_t*)common_usb_bos_desc);

    CyFx3BootUsbSetDesc(
        CY_U3P_USB_SET_STRING_DESCR, 0, (uint8_t*)common_string_lang_id_desc);

    CyFx3BootUsbSetDesc(CY_U3P_USB_SET_STRING_DESCR, 1, (uint8_t*)bl_manufacturer_desc);
    CyFx3BootUsbSetDesc(CY_U3P_USB_SET_STRING_DESCR, 2, (uint8_t*)bl_product_desc);
    CyFx3BootUsbSetDesc(CY_U3P_USB_SET_STRING_DESCR, 3, (uint8_t*)bl_dev_serial_desc);

    gpUsbDescPtr = CyFx3BootUsbGetDesc();

    /* Connect! */
    CyFx3BootUsbConnect (CyTrue, CyTrue);
}