#include "usbd_core.h"
#include "ch585_usbhs_reg.h"

/**
 * @brief   Related register macro
 */
#define USB_BASE        0x40009000u
#define CH585_USBHS_DEV ((USBHSD_TypeDef *)USB_BASE)

#define R16_PIN_CONFIG (*((PUINT16V)0x4000101A))
#define R32_PIN_CONFIG (*((PUINT32V)0x40001018)) // RW, I/O pin configuration
#define RB_PIN_USB2_EN 0x20

#define USB_SET_RX_DMA(ep_idx, addr) (*(volatile uint32_t *)((uint32_t)(&CH585_USBHS_DEV->UEP1_RX_DMA) + 4 * (ep_idx - 1)) = addr)
#define USB_SET_TX_DMA(ep_idx, addr) (*(volatile uint32_t *)((uint32_t)(&CH585_USBHS_DEV->UEP1_TX_DMA) + 4 * (ep_idx - 1)) = addr)

#define USB_SET_MAX_LEN(ep_idx, len) (*(volatile uint16_t *)((uint32_t)(&CH585_USBHS_DEV->UEP0_MAX_LEN) + 4 * ep_idx) = len)
#define USB_SET_TX_LEN(ep_idx, len)  (*(volatile uint16_t *)((uint32_t)(&CH585_USBHS_DEV->UEP0_TX_LEN) + 4 * ep_idx) = len)
#define USB_GET_TX_LEN(ep_idx)       (*(volatile uint16_t *)((uint32_t)(&CH585_USBHS_DEV->UEP0_TX_LEN) + 4 * ep_idx))
#define USB_SET_TX_CTRL(ep_idx, val) (*(volatile uint8_t *)((uint32_t)(&CH585_USBHS_DEV->UEP0_TX_CTRL) + 4 * ep_idx) = val)
#define USB_GET_TX_CTRL(ep_idx)      (*(volatile uint8_t *)((uint32_t)(&CH585_USBHS_DEV->UEP0_TX_CTRL) + 4 * ep_idx))
#define USB_SET_RX_CTRL(ep_idx, val) (*(volatile uint8_t *)((uint32_t)(&CH585_USBHS_DEV->UEP0_RX_CTRL) + 4 * ep_idx) = val)
#define USB_GET_RX_CTRL(ep_idx)      (*(volatile uint8_t *)((uint32_t)(&CH585_USBHS_DEV->UEP0_RX_CTRL) + 4 * ep_idx))
#define EPn_SET_TX_NAK(ep_idx)       USB_SET_TX_CTRL(ep_idx, (USB_GET_TX_CTRL(ep_idx) & ~USBHS_UEP_T_RES_MASK) | USBHS_UEP_T_RES_NAK)
#define EPn_SET_TX_VALID(ep_idx)     USB_SET_TX_CTRL(ep_idx, (USB_GET_TX_CTRL(ep_idx) & ~USBHS_UEP_T_RES_MASK) | USBHS_UEP_T_RES_ACK)
#define EPn_SET_RX_NAK(ep_idx)       USB_SET_RX_CTRL(ep_idx, (USB_GET_RX_CTRL(ep_idx) & ~USBHS_UEP_R_RES_MASK) | USBHS_UEP_R_RES_NAK)
#define EPn_SET_RX_VALID(ep_idx)     USB_SET_RX_CTRL(ep_idx, (USB_GET_RX_CTRL(ep_idx) & ~USBHS_UEP_R_RES_MASK) | USBHS_UEP_R_RES_ACK)
#define EPn_GET_RX_LEN(ep_idx)       (*(volatile uint16_t *)((uint32_t)(&CH585_USBHS_DEV->USB_EP0_RX_LEN) + 4 * ep_idx))
#define EPn_SET_TX_LEN(ep_idx, len)  (*(volatile uint16_t *)((uint32_t)(&CH585_USBHS_DEV->UEP0_TX_LEN) + 4 * ep_idx) = len)
#define EPn_CLEAR_TX_DONE(ep_idx)    USB_SET_TX_CTRL(ep_idx, USB_GET_TX_CTRL(ep_idx) & ~USBHS_UEP_T_DONE)
#define EPn_CLEAR_RX_DONE(ep_idx)    USB_SET_RX_CTRL(ep_idx, USB_GET_RX_CTRL(ep_idx) & ~USBHS_UEP_R_DONE)
#define EPn_SET_TX_ISO_VALID(ep_idx)
#define EPn_SET_RX_ISO_VALID(ep_idx)

/* ep nums */
#ifndef CONFIG_USBDEV_EP_NUM
#define CONFIG_USBDEV_EP_NUM 8
#endif

/**
 * @brief   Endpoint information structure
 */
typedef struct _usbd_ep_info {
    uint8_t mps;       /* Maximum packet length of endpoint */
    uint8_t eptype;    /* Endpoint Type */
    uint8_t ep_enable; /* Endpoint enable */
    uint8_t *xfer_buf;
    uint32_t xfer_len;
    uint32_t actual_xfer_len;
} usbd_ep_info;

/* ch58x usb */
static struct _ch58x_core_prvi {
    uint8_t address; /* Address */
    usbd_ep_info ep_in[CONFIG_USBDEV_EP_NUM];
    usbd_ep_info ep_out[CONFIG_USBDEV_EP_NUM];
    struct usb_setup_packet setup;
} usb_dc_cfg;

__WEAK void usb_dc_low_level_init(void)
{
}

__WEAK void usb_dc_low_level_deinit(void)
{
}

/**
 * @brief            USB initialization
 * @pre              None
 * @param[in]        None
 * @retval           >=0 success otherwise failure
 */
int usb_dc_init(uint8_t busid)
{
    R8_USBHS_PLL_CTRL = USBHS_PLL_EN;
    R16_PIN_CONFIG |= RB_PIN_USB2_EN;
    CH585_USBHS_DEV->CONTROL = USBHS_UD_RST_LINK | USBHS_UD_PHY_SUSPENDM;
    CH585_USBHS_DEV->INT_EN = USBHS_UDIE_BUS_RST | USBHS_UDIE_SUSPEND | USBHS_UDIE_BUS_SLEEP | USBHS_UDIE_LPM_ACT | USBHS_UDIE_TRANSFER | USBHS_UDIE_LINK_RDY;
    /* Enable all end points */
    CH585_USBHS_DEV->UEP_TX_EN = 0xffff;
    CH585_USBHS_DEV->UEP_RX_EN = 0xffff;

    CH585_USBHS_DEV->BASE_MODE = USBHS_UD_SPEED_HIGH;
    CH585_USBHS_DEV->CONTROL = USBHS_UD_DEV_EN | USBHS_UD_DMA_EN | USBHS_UD_LPM_EN | USBHS_UD_PHY_SUSPENDM;

    CH585_USBHS_DEV->UEP_T_TOG_AUTO = 0xfe;
    CH585_USBHS_DEV->UEP_R_TOG_AUTO = 0xfe;
    usb_dc_low_level_init();
    return 0;
}

int usb_dc_deinit(uint8_t busid)
{
    R8_USBHS_PLL_CTRL &= ~USBHS_PLL_EN;
    R32_PIN_CONFIG &= ~RB_PIN_USB2_EN;

    CH585_USBHS_DEV->CONTROL |= USBHS_UD_RST_SIE;
    CH585_USBHS_DEV->CONTROL &= ~USBHS_UD_RST_SIE;
    usb_dc_low_level_deinit();
    return 0;
}

/**
 * @brief            Set address
 * @pre              None
 * @param[in]        address ：8-bit valid address
 * @retval           >=0 success otherwise failure
 */
int usbd_set_address(uint8_t busid, const uint8_t address)
{
    if (address == 0) {
        CH585_USBHS_DEV->DEV_AD = (CH585_USBHS_DEV->DEV_AD & 0x80) | address;
    }
    usb_dc_cfg.address = address;
    return 0;
}

int usbd_set_remote_wakeup(uint8_t busid)
{
    return -1;
}

uint8_t usbd_get_port_speed(uint8_t busid)
{
    return USB_SPEED_HIGH;
}

/**
 * @brief            Open endpoint
 * @pre              None
 * @param[in]        ep_cfg : Endpoint configuration structure pointer
 * @retval           >=0 success otherwise failure
 */
int usbd_ep_open(uint8_t busid, const struct usb_endpoint_descriptor *ep)
{
    uint8_t epid = USB_EP_GET_IDX(ep->bEndpointAddress);
    if (epid > (CONFIG_USBDEV_EP_NUM - 1)) {
        /**
         * If you use ch58x, you can change the CONFIG_USBDEV_EP_NUM set to 8
         */
        USB_LOG_ERR("Ep addr %02x overflow\r\n", ep->bEndpointAddress);
        return -1;
    }

    uint8_t mps = USB_GET_MAXPACKETSIZE(ep->wMaxPacketSize);
    USB_SET_MAX_LEN(epid, mps);

    if (USB_EP_DIR_IS_IN(ep->bEndpointAddress)) {
        usb_dc_cfg.ep_in[epid].ep_enable = true;
        usb_dc_cfg.ep_in[epid].mps = mps;
        usb_dc_cfg.ep_in[epid].eptype = USB_GET_ENDPOINT_TYPE(ep->bmAttributes);
        USB_SET_TX_CTRL(epid, USBHS_UEP_T_RES_NAK);
        EPn_CLEAR_TX_DONE(epid);
    } else if (USB_EP_DIR_IS_OUT(ep->bEndpointAddress)) {
        usb_dc_cfg.ep_out[epid].ep_enable = true;
        usb_dc_cfg.ep_out[epid].mps = mps;
        usb_dc_cfg.ep_out[epid].eptype = USB_GET_ENDPOINT_TYPE(ep->bmAttributes);
        USB_SET_RX_CTRL(epid, USBHS_UEP_R_RES_NAK);
    }

    return 0;
}

/**
 * @brief            Close endpoint
 * @pre              None
 * @param[in]        ep ： Endpoint address
 * @retval           >=0 success otherwise failure
 */
int usbd_ep_close(uint8_t busid, const uint8_t ep)
{
    uint8_t epid = USB_EP_GET_IDX(ep);
    if (USB_EP_DIR_IS_IN(ep)) {
        usb_dc_cfg.ep_in[epid].ep_enable = false;
    } else if (USB_EP_DIR_IS_OUT(ep)) {
        usb_dc_cfg.ep_out[epid].ep_enable = false;
    }
    return 0;
}

/**
 * @brief            Endpoint setting stall
 * @pre              None
 * @param[in]        ep ： Endpoint address
 * @retval           >=0 success otherwise failure
 */
int usbd_ep_set_stall(uint8_t busid, const uint8_t ep)
{
    uint8_t ep_idx = USB_EP_GET_IDX(ep);
    if (USB_EP_DIR_IS_OUT(ep)) {
        if (ep_idx == 0) {
            CH585_USBHS_DEV->UEP0_RX_CTRL = USBHS_UEP_R_RES_STALL;
        } else {
            USB_SET_RX_CTRL(ep_idx, (USB_GET_RX_CTRL(ep_idx) & ~USBHS_UEP_R_RES_MASK) | USBHS_UEP_R_RES_STALL);
        }
    } else {
        if (ep_idx == 0) {
            CH585_USBHS_DEV->UEP0_TX_CTRL = USBHS_UEP_T_RES_STALL;
        } else {
            USB_SET_TX_CTRL(ep_idx, (USB_GET_TX_CTRL(ep_idx) & ~USBHS_UEP_T_RES_MASK) | USBHS_UEP_T_RES_STALL);
        }
    }
    return 0;
}

/**
 * @brief            Endpoint clear stall
 * @pre              None
 * @param[in]        ep ： Endpoint address
 * @retval           >=0 success otherwise failure
 */
int usbd_ep_clear_stall(uint8_t busid, const uint8_t ep)
{
    uint8_t ep_idx = USB_EP_GET_IDX(ep);

    if (USB_EP_DIR_IS_OUT(ep)) {
        USB_SET_RX_CTRL(ep_idx, USBHS_UEP_R_RES_ACK | USBHS_UEP_R_TOG_DATA0);
    } else {
        USB_SET_TX_CTRL(ep_idx, USBHS_UEP_T_RES_NAK | USBHS_UEP_T_TOG_DATA0);
    }
    return 0;
}

/**
 * @brief            Check endpoint status
 * @pre              None
 * @param[in]        ep ： Endpoint address
 * @param[out]       stalled ： Outgoing endpoint status
 * @retval           >=0 success otherwise failure
 */
int usbd_ep_is_stalled(uint8_t busid, const uint8_t ep, uint8_t *stalled)
{
    if (USB_EP_DIR_IS_OUT(ep)) {
    } else {
    }
    return 0;
}

/**
 * @brief Setup in ep transfer setting and start transfer.
 *
 * This function is asynchronous.
 * This function is similar to uart with tx dma.
 *
 * This function is called to write data to the specified endpoint. The
 * supplied usbd_endpoint_callback function will be called when data is transmitted
 * out.
 *
 * @param[in]  ep        Endpoint address corresponding to the one
 *                       listed in the device configuration table
 * @param[in]  data      Pointer to data to write
 * @param[in]  data_len  Length of the data requested to write. This may
 *                       be zero for a zero length status packet.
 * @return 0 on success, negative errno code on fail.
 */
int usbd_ep_start_write(uint8_t busid, const uint8_t ep, const uint8_t *data, uint32_t data_len)
{
    uint8_t ep_idx = USB_EP_GET_IDX(ep);

    if (!data && data_len) {
        return -1;
    }
    if (!usb_dc_cfg.ep_in[ep_idx].ep_enable) {
        return -2;
    }
    if ((uint32_t)data & 0x03) {
        return -3;
    }

    usb_dc_cfg.ep_in[ep_idx].xfer_buf = (uint8_t *)data;
    usb_dc_cfg.ep_in[ep_idx].xfer_len = data_len;
    usb_dc_cfg.ep_in[ep_idx].actual_xfer_len = 0;

    if (ep_idx == 0) {
        if (data_len == 0) {
            USB_SET_TX_LEN(ep_idx, 0);
        } else {
            data_len = MIN(data_len, usb_dc_cfg.ep_in[ep_idx].mps);
            USB_SET_TX_LEN(ep_idx, data_len);
            CH585_USBHS_DEV->UEP0_DMA = (uint32_t)data;
        }
    } else {
        if (data_len == 0) {
            USB_SET_TX_LEN(ep_idx, 0);
        } else {
            data_len = MIN(data_len, usb_dc_cfg.ep_in[ep_idx].mps);
            USB_SET_TX_LEN(ep_idx, data_len);
            USB_SET_TX_DMA(ep_idx, (uint32_t)data);
        }
    }

    EPn_SET_TX_VALID(ep_idx);
    return 0;
}

/**
 * @brief Setup out ep transfer setting and start transfer.
 *
 * This function is asynchronous.
 * This function is similar to uart with rx dma.
 *
 * This function is called to read data to the specified endpoint. The
 * supplied usbd_endpoint_callback function will be called when data is received
 * in.
 *
 * @param[in]  ep        Endpoint address corresponding to the one
 *                       listed in the device configuration table
 * @param[in]  data      Pointer to data to read
 * @param[in]  data_len  Max length of the data requested to read.
 *
 * @return 0 on success, negative errno code on fail.
 */
int usbd_ep_start_read(uint8_t busid, const uint8_t ep, uint8_t *data, uint32_t data_len)
{
    uint8_t ep_idx = USB_EP_GET_IDX(ep);

    if (!data && data_len) {
        return -1;
    }
    if (!usb_dc_cfg.ep_out[ep_idx].ep_enable) {
        return -2;
    }
    if ((uint32_t)data & 0x03) {
        return -3;
    }

    usb_dc_cfg.ep_out[ep_idx].xfer_buf = (uint8_t *)data;
    usb_dc_cfg.ep_out[ep_idx].xfer_len = data_len;
    usb_dc_cfg.ep_out[ep_idx].actual_xfer_len = 0;

    if (ep_idx == 0) {
        if (data_len == 0) {
        } else {
            CH585_USBHS_DEV->UEP0_DMA = (uint32_t)data;
        }
    } else {
        USB_SET_RX_DMA(ep_idx, (uint32_t)data);
    }

    EPn_SET_RX_VALID(ep_idx);
    return 0;
}

static inline void handle_ep0_in(void)
{
    switch (usb_dc_cfg.setup.bmRequestType >> USB_REQUEST_DIR_SHIFT) {
        case 1:
            CH585_USBHS_DEV->UEP0_TX_CTRL ^= USBHS_UEP_T_TOG_DATA1;
            EPn_SET_TX_NAK(0);
            if (usb_dc_cfg.ep_in[0].xfer_len > usb_dc_cfg.ep_in[0].mps) {
                usb_dc_cfg.ep_in[0].xfer_len -= usb_dc_cfg.ep_in[0].mps;
                usb_dc_cfg.ep_in[0].actual_xfer_len += usb_dc_cfg.ep_in[0].mps;
                usbd_event_ep_in_complete_handler(0, 0 | 0x80, usb_dc_cfg.ep_in[0].actual_xfer_len);
            } else {
                usb_dc_cfg.ep_in[0].actual_xfer_len += usb_dc_cfg.ep_in[0].xfer_len;
                usb_dc_cfg.ep_in[0].xfer_len = 0;
                usbd_event_ep_in_complete_handler(0, 0 | 0x80, usb_dc_cfg.ep_in[0].actual_xfer_len);
            }
            break;
        case 0:
            /* Set */
            switch (usb_dc_cfg.setup.bRequest) {
                case USB_REQUEST_SET_ADDRESS:
                    /* Fill in the equipment address */
                    CH585_USBHS_DEV->DEV_AD = usb_dc_cfg.address;
                    CH585_USBHS_DEV->UEP0_DMA = (uint32_t)&usb_dc_cfg.setup;
                    EPn_SET_TX_NAK(0);
                    EPn_SET_RX_VALID(0);
                    break;
                default:
                    /* Normal out state phase */
                    CH585_USBHS_DEV->UEP0_DMA = (uint32_t)&usb_dc_cfg.setup;
                    EPn_SET_TX_NAK(0);
                    EPn_SET_RX_VALID(0);
                    break;
            }
            break;
    }
}

static inline void handle_non_ep0_in(uint8_t epid)
{
    EPn_SET_TX_NAK(epid);
    if (usb_dc_cfg.ep_in[epid].xfer_len > usb_dc_cfg.ep_in[epid].mps) {
        /* Need start in again */
        usb_dc_cfg.ep_in[epid].xfer_buf += usb_dc_cfg.ep_in[epid].mps;
        usb_dc_cfg.ep_in[epid].xfer_len -= usb_dc_cfg.ep_in[epid].mps;
        usb_dc_cfg.ep_in[epid].actual_xfer_len += usb_dc_cfg.ep_in[epid].mps;

        uint32_t write_count = MIN(usb_dc_cfg.ep_in[epid].xfer_len, usb_dc_cfg.ep_in[epid].mps);
        USB_SET_TX_LEN(epid, write_count);
        USB_SET_TX_DMA(epid, (uint32_t)usb_dc_cfg.ep_in[epid].xfer_buf);

        if (usb_dc_cfg.ep_in[epid].eptype != USB_ENDPOINT_TYPE_ISOCHRONOUS) {
            EPn_SET_TX_VALID(epid);
        } else {
            EPn_SET_TX_ISO_VALID(epid);
        }
    } else {
        usb_dc_cfg.ep_in[epid].actual_xfer_len += usb_dc_cfg.ep_in[epid].xfer_len;
        usb_dc_cfg.ep_in[epid].xfer_len = 0;
        usbd_event_ep_in_complete_handler(0, epid | 0x80, usb_dc_cfg.ep_in[epid].actual_xfer_len);
    }
}

static inline void usb_process_ep_in(uint8_t epid)
{
    if (epid == 0) {
        handle_ep0_in();
    } else {
        handle_non_ep0_in(epid);
    }

    EPn_CLEAR_TX_DONE(epid);
}

static inline void usb_process_ep_out(uint8_t epid)
{
    EPn_SET_RX_NAK(epid);
    if (epid == 0) {
        if (CH585_USBHS_DEV->UEP0_RX_CTRL & USBHS_UEP_R_SETUP_IS) {
            CH585_USBHS_DEV->UEP0_RX_CTRL |= USBHS_UEP_R_TOG_DATA1;
            CH585_USBHS_DEV->UEP0_TX_CTRL |= USBHS_UEP_T_TOG_DATA1;
            if (usb_dc_cfg.setup.bmRequestType >> USB_REQUEST_DIR_SHIFT == 0) {
                /**
                 * Ep0 The next in must be the status stage.
                 * The device must reply to the host data 0 length packet.
                 * Here, set the transmission length to 0 and the transmission status to ACK,
                 * and wait for the host to send the in token to retrieve
                 */
                EPn_SET_TX_LEN(0, 0);
                EPn_SET_TX_VALID(0);
            }
            usbd_event_ep0_setup_complete_handler(0, (uint8_t *)&usb_dc_cfg.setup);
        } else {
            CH585_USBHS_DEV->UEP0_RX_CTRL ^= USBHS_UEP_R_TOG_DATA1;
            uint32_t read_count = EPn_GET_RX_LEN(0);
            usb_dc_cfg.ep_out[0].actual_xfer_len += read_count;
            usb_dc_cfg.ep_out[0].xfer_len -= read_count;
            usbd_event_ep_out_complete_handler(0, 0x00, usb_dc_cfg.ep_out[0].actual_xfer_len);
            if (read_count == 0) {
                /* Out status, start reading setup */
                CH585_USBHS_DEV->UEP0_DMA = (uint32_t)&usb_dc_cfg.setup;
                EPn_SET_RX_VALID(0);
            }
        }
    } else {
        if (USB_GET_RX_CTRL(epid) & USBHS_UEP_R_TOG_MATCH) {
            uint32_t read_count = EPn_GET_RX_LEN(epid);
            usb_dc_cfg.ep_out[epid].xfer_buf += read_count;
            usb_dc_cfg.ep_out[epid].actual_xfer_len += read_count;
            usb_dc_cfg.ep_out[epid].xfer_len -= read_count;

            if ((read_count < usb_dc_cfg.ep_out[epid].mps) || (usb_dc_cfg.ep_out[epid].xfer_len == 0)) {
                usbd_event_ep_out_complete_handler(0, ((epid) & 0x7f), usb_dc_cfg.ep_out[epid].actual_xfer_len);
            } else {
                USB_SET_RX_DMA(epid, (uint32_t)usb_dc_cfg.ep_out[epid].xfer_buf);
                if (usb_dc_cfg.ep_out[epid].eptype != USB_ENDPOINT_TYPE_ISOCHRONOUS) {
                    EPn_SET_RX_VALID(epid);
                } else {
                    EPn_SET_RX_ISO_VALID(epid);
                }
            }
        }
    }

    EPn_CLEAR_RX_DONE(epid);
}

static inline void usb_trans_end_process(void)
{
    uint8_t epid = (CH585_USBHS_DEV->INT_ST & USBHS_UDIS_EP_ID_MASK);
    switch (CH585_USBHS_DEV->INT_ST & USBHS_UDIS_EP_DIR) {
        case USBHS_UDIS_EP_DIR: /* in */
            usb_process_ep_in(epid);
            break;
        case 0: /* setup or out */
            usb_process_ep_out(epid);
            break;
        default:
            break;
    }
}

/**
 * @brief            USB interrupt processing function
 * @pre              None
 * @param[in]        None
 * @retval           None
 */
void USBD_IRQHandler(uint8_t busid)
{
    volatile uint8_t intflag = 0;
    intflag = CH585_USBHS_DEV->INT_FG;

    if (intflag & USBHS_UDIF_TRANSFER) {
        usb_trans_end_process();
    } else if (intflag & USBHS_UDIF_BUS_RST) {
        /* Reset */
        CH585_USBHS_DEV->DEV_AD = 0;
        usbd_event_reset_handler(0);
        /* Set ep0 rx vaild to start receive setup packet */
        CH585_USBHS_DEV->UEP0_DMA = (uint32_t)&usb_dc_cfg.setup;
        // EPn_SET_RX_VALID(0);
        R8_U2EP0_TX_CTRL = USBHS_UEP_T_RES_NAK;
        R8_U2EP0_RX_CTRL = USBHS_UEP_R_RES_ACK;
        CH585_USBHS_DEV->INT_FG = USBHS_UDIF_BUS_RST;
    } else if (intflag & USBHS_UDIF_SUSPEND) {
        if (CH585_USBHS_DEV->MIS_ST & 0x04) {
            /* Suspend */
        } else {
            /* Wake up */
        }
        CH585_USBHS_DEV->INT_FG = USBHS_UDIF_SUSPEND;
    } else {
        CH585_USBHS_DEV->INT_FG = intflag;
    }
}

void USB2_DEVICE_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast"))) __attribute__((section(".highcode")));
void USB2_DEVICE_IRQHandler(void)
{
    extern void USBD_IRQHandler(uint8_t busid);
    USBD_IRQHandler(0);
}