/*!
    \file  usbd_std.c
    \brief USB device stand routines
    \note  about USB standard, please refer to the USB2.0 protocol
*/

/*
    Copyright (C) 2017 GigaDevice

    2017-02-10, V1.0.0, firmware for GD32F30x
*/

#include "usbd_std.h"

uint8_t g_device_address = 0U;

/* USB enumeration handle functions */
static void usbd_getdescriptor     (usbd_core_handle_struct *pudev, usb_device_req_struct *req);
static void usbd_setaddress        (usbd_core_handle_struct *pudev, usb_device_req_struct *req);
static void usbd_setconfiguration  (usbd_core_handle_struct *pudev, usb_device_req_struct *req);
static void usbd_getconfiguration  (usbd_core_handle_struct *pudev, usb_device_req_struct *req);
static void usbd_getstatus         (usbd_core_handle_struct *pudev, usb_device_req_struct *req);
static void usbd_setfeature        (usbd_core_handle_struct *pudev, usb_device_req_struct *req);
static void usbd_clearfeature      (usbd_core_handle_struct *pudev, usb_device_req_struct *req);
static void usbd_reserved          (usbd_core_handle_struct *pudev, usb_device_req_struct *req);
static void usbd_setdescriptor     (usbd_core_handle_struct *pudev, usb_device_req_struct *req);
static void usbd_getinterface      (usbd_core_handle_struct *pudev, usb_device_req_struct *req);
static void usbd_setinterface      (usbd_core_handle_struct *pudev, usb_device_req_struct *req);
static void usbd_synchframe        (usbd_core_handle_struct *pudev, usb_device_req_struct *req);

static uint8_t* usbd_device_descriptor_get        (usbd_core_handle_struct *pudev, 
                                                   uint8_t index, 
                                                   uint16_t *pLen);

static uint8_t* usbd_configuration_descriptor_get (usbd_core_handle_struct *pudev, 
                                                   uint8_t index, 
                                                   uint16_t *pLen);

static uint8_t* usbd_string_descriptor_get        (usbd_core_handle_struct *pudev, 
                                                   uint8_t index, 
                                                   uint16_t *pLen);

#ifdef LPM_ENABLED
static uint8_t* usbd_bos_descriptor_get           (usbd_core_handle_struct *pudev, uint16_t *pLen);
#endif /* LPM_ENABLED */

/* standard device request handler */
static void (*standard_device_request[])(usbd_core_handle_struct *pudev, usb_device_req_struct *req) =
{
    usbd_getstatus,
    usbd_clearfeature,
    usbd_reserved,
    usbd_setfeature,
    usbd_reserved,
    usbd_setaddress,
    usbd_getdescriptor,
    usbd_setdescriptor,
    usbd_getconfiguration,
    usbd_setconfiguration,
    usbd_getinterface,
    usbd_setinterface,
    usbd_synchframe,
};

/* get standard descriptor handler */
static uint8_t* (*standard_descriptor_get[])(usbd_core_handle_struct *pudev, uint8_t index, uint16_t *pLen) = 
{
    usbd_device_descriptor_get,
    usbd_configuration_descriptor_get,
    usbd_string_descriptor_get
};

/*!
    \brief      USB setup stage processing
    \param[in]  pudev: pointer to USB device instance
    \param[out] none
    \retval     USB device operation status
*/
uint8_t  usbd_setup_transaction (usbd_core_handle_struct *pudev)
{
    usb_device_req_struct req;

    usbd_setup_request_parse (pudev, &req);

    switch (req.bmRequestType & USB_REQ_MASK) {
    /* standard device request */
    case USB_STANDARD_REQ:
        usbd_standard_request(pudev, &req);
        break;
    /* device class request */
    case USB_CLASS_REQ:
        usbd_device_class_request(pudev, &req);
        break;
    /* vendor defined request */
    case USB_VENDOR_REQ:
        usbd_vendor_request(pudev, &req);
        break;
    default:
        usbd_ep_stall(pudev, 0x00U);
        break;
    }
    return USBD_OK;
}

/*!
    \brief      data out stage processing
    \param[in]  pudev: pointer to USB device instance
    \param[in]  ep_num: endpoint identifier(0..7)
    \param[out] none
    \retval     USB device operation status
*/
uint8_t  usbd_out_transaction (usbd_core_handle_struct *pudev, uint8_t ep_num)
{
    usb_ep_struct *ep = &pudev->out_ep[ep_num];

    if (0U == ep_num) {
        if (0U != pudev->ctl_count) {
            if (ep->trs_len > ep->maxpacket) {
                /* one data packet has been received, update trs_len */
                ep->trs_len -= ep->maxpacket;

                /* continue to receive remain data */
                usbd_ep_rx(pudev, EP0_OUT, ep->trs_buf, (uint16_t)ep->trs_len);
            } else {
                if (USBD_CONFIGURED == pudev->status) {
                    /* device class handle */
                    pudev->class_data_handler(pudev, USBD_RX, EP0);
                }

                /* enter the control transaction status stage */
                USBD_CONTRL_STATUS_TX();

                pudev->ctl_count = 0U;
            }
        } else {
            /* clear endpoint status_out status */
            USBD_STATUS_OUT_CLEAR(EP0);
        }
    } else {
        if (USBD_CONFIGURED == pudev->status) {
            pudev->class_data_handler(pudev, USBD_RX, ep_num);
        }
    }
    return USBD_OK;
}

/*!
    \brief      data in stage processing
    \param[in]  pudev: pointer to USB device instance
    \param[in]  ep_num: endpoint identifier(0..7)
    \param[out] none
    \retval     USB device operation status
*/
uint8_t  usbd_in_transaction (usbd_core_handle_struct *pudev, uint8_t ep_num)
{
    usb_ep_struct *ep = &pudev->in_ep[ep_num];

    if (0U == ep_num) {
        if (0U != pudev->ctl_count) {
            if (ep->trs_len > ep->maxpacket) {
                /* one data packet has been transmited, update trs_len */
                ep->trs_len -= ep->maxpacket;

                /* continue to transmit remain data */
                usbd_ep_tx (pudev, EP0_IN, ep->trs_buf, (uint16_t)ep->trs_len);

//                usbd_ep_rx (pudev, 0, NULL, 0);
            } else {
#ifndef USB_DFU
                /* transmit length is maxpacket multiple, so send zero length packet */
                if ((0U == (ep->trs_len % ep->maxpacket)) && (ep->trs_len < pudev->ctl_count)) {
                    usbd_ep_tx (pudev, EP0_IN, NULL, 0U);

                    pudev->ctl_count = 0U;

//                    usbd_ep_rx (pudev, 0, NULL, 0);
                } else
#endif /* USB_DFU */
                {
                    ep->trs_len = 0U;

                    if (USBD_CONFIGURED == pudev->status) {
                        pudev->class_data_handler(pudev, USBD_TX, EP0); 
                    }

                    USBD_CONTRL_STATUS_RX();

                    pudev->ctl_count = 0U;
                }
            }
        } else {
            if (0U != g_device_address) {
                USBD_REG_SET(USBD_DADDR, DADDR_USBEN | g_device_address);
                g_device_address = 0U;
            }
        }
    } else {
        ep->trs_len -= ep->trs_count;

        if (0U == ep->trs_len) {
            if (USBD_CONFIGURED == pudev->status) {
                pudev->class_data_handler(pudev, USBD_TX, ep_num);
            }
        } else {
            usbd_ep_tx(pudev, ep_num, ep->trs_buf, (uint16_t)ep->trs_len);
        }
    }

    return USBD_OK;
}

/*!
    \brief      handle USB standard device request
    \param[in]  pudev: pointer to USB device instance
    \param[in]  req: pointer to USB device request
    \param[out] none
    \retval     USB device operation status
*/
uint8_t  usbd_standard_request (usbd_core_handle_struct *pudev, usb_device_req_struct *req)
{
    /* call device request handle function */
    (*standard_device_request[req->bRequest])(pudev, req);

    return USBD_OK;
}

/*!
    \brief      handle USB device class request
    \param[in]  pudev: pointer to USB device instance
    \param[in]  req: pointer to USB device class request
    \param[out] none
    \retval     USB device operation status
*/
uint8_t  usbd_device_class_request (usbd_core_handle_struct *pudev, usb_device_req_struct *req)
{
    usbd_status_enum ret;

    switch (pudev->status) {
    case USBD_CONFIGURED:
        if (LOWBYTE(req->wIndex) <= USBD_ITF_MAX_NUM) {
            /* call device class handle function */
            ret = (usbd_status_enum)(pudev->class_req_handler(pudev, req));

            if ((0U == req->wLength) && (USBD_OK == ret)) {
                /* no data stage */
                USBD_CONTRL_STATUS_TX();
            }
        } else {
            usbd_enum_error(pudev, req);
        }
        break;
    default:
        usbd_enum_error(pudev, req);
        break;
    }

    return USBD_OK;
}

/*!
    \brief      handle USB vendor request
    \param[in]  pudev: pointer to USB device instance
    \param[in]  req: pointer to USB vendor request
    \param[out] none
    \retval     USB device operation status
*/
uint8_t  usbd_vendor_request (usbd_core_handle_struct *pudev, usb_device_req_struct *req)
{
    /* added by user */

    return USBD_OK;
}

/*!
    \brief      no operation, just for reserved
    \param[in]  pudev: pointer to USB device instance
    \param[in]  req: pointer to USB device request
    \param[out] none
    \retval     none
*/
static void  usbd_reserved (usbd_core_handle_struct *pudev, usb_device_req_struct *req)
{
    /* no operation */
}

#ifdef LPM_ENABLED

static uint8_t* usbd_bos_descriptor_get (usbd_core_handle_struct *pudev, uint16_t *pLen)
{
    *pLen = pudev->bos_desc[2] | ((uint16_t)pudev->bos_desc[3] << 8);

    return pudev->bos_desc;
}

#endif /* LPM_ENABLED */

/*!
    \brief      get the device descriptor
    \param[in]  pudev: pointer to USB device instance
    \param[in]  index: no use
    \param[out] pLen: data length pointer
    \retval     descriptor buffer pointer
*/
static uint8_t* usbd_device_descriptor_get (usbd_core_handle_struct *pudev, uint8_t index, uint16_t *pLen)
{
    *pLen = pudev->dev_desc[0];

    return pudev->dev_desc;
}

/*!
    \brief      get the configuration descriptor
    \brief[in]  pudev: pointer to USB device instance
    \brief[in]  index: no use
    \param[out] pLen: data length pointer
    \retval     descriptor buffer pointer
*/
static uint8_t* usbd_configuration_descriptor_get (usbd_core_handle_struct *pudev, uint8_t index, uint16_t *pLen)
{
    *pLen = pudev->config_desc[2];

    return pudev->config_desc;
}

/*!
    \brief      get string descriptor
    \param[in]  pudev: pointer to USB device instance
    \param[in]  index: string descriptor index
    \param[out] pLen: pointer to string length
    \retval     none
*/
static uint8_t* usbd_string_descriptor_get (usbd_core_handle_struct *pudev, uint8_t index, uint16_t *pLen)
{
    uint8_t *desc = pudev->strings[index];

    *pLen = desc[0];

    return desc;
}

/*!
    \brief      handle Get_Status request
    \param[in]  pudev: pointer to USB device instance
    \param[in]  req: pointer to USB device request
    \param[out] none
    \retval     none
*/
static void  usbd_getstatus (usbd_core_handle_struct *pudev, usb_device_req_struct *req)
{
    uint8_t  ep_addr;
    uint16_t config_status = 0x0000U;
    uint16_t endp_status = 0x0000U;

    switch(req->bmRequestType & USB_REQ_RECIPIENT_MASK) {
    case USB_REQTYPE_DEVICE:
        switch (pudev->status) {
        case USBD_ADDRESSED:
        case USBD_CONFIGURED:

#ifdef USBD_SELF_POWERED
    config_status = USB_STATUS_SELF_POWERED;
#endif /* USBD_SELF_POWERED */

            if (pudev->remote_wakeup) {
                config_status |= USB_STATUS_REMOTE_WAKEUP;
            }

            usbd_ep_tx(pudev, EP0_IN, (uint8_t *)&config_status, 2U);
            break;
        default:
            break;
        }
        break;
    case USB_REQTYPE_INTERFACE:
        switch (pudev->status) {
        case USBD_ADDRESSED:
            usbd_enum_error(pudev, req);
            break;
        case USBD_CONFIGURED:
            if (LOWBYTE(req->wIndex) <= USBD_ITF_MAX_NUM) {
                usbd_ep_tx(pudev, EP0_IN, (uint8_t *)&config_status, 2U);
            } else {
                usbd_enum_error(pudev, req);
            }
            break;
        default:
            break;
        }
        break;
    case USB_REQTYPE_ENDPOINT:
        /* get enndpoint address */
        ep_addr = LOWBYTE(req->wIndex);

        switch (pudev->status) {
        case USBD_ADDRESSED:
            if (IS_NOT_EP0(ep_addr)) {
                usbd_enum_error(pudev, req);
            }
            break;
        case USBD_CONFIGURED:
            if ((ep_addr & 0x80U) == 0x80U) {
                if(pudev->in_ep[ep_addr & 0x7FU].stall) {
                    endp_status = 0x0001U;
                }
            } else {
                if (pudev->out_ep[ep_addr].stall) {
                    endp_status = 0x0001U;
                }
            }
            usbd_ep_tx(pudev, EP0_IN, (uint8_t *)&endp_status, 2U);
            break;

        default:
            break;
        }
        break;
    default:
        usbd_enum_error(pudev, req);
        break;
    }
}

/*!
    \brief      handle USB Clear_Feature request
    \param[in]  pudev: pointer to USB device instance
    \param[in]  req: USB device request
    \param[out] none
    \retval     none
*/
static void  usbd_clearfeature (usbd_core_handle_struct *pudev, usb_device_req_struct *req)
{
    uint8_t ep_addr = 0U;

    switch (req->bmRequestType & USB_REQ_RECIPIENT_MASK) {
    case USB_REQTYPE_DEVICE:
        switch (pudev->status) {
        case USBD_ADDRESSED:
        case USBD_CONFIGURED:
            /* clear device remote wakeup feature */
            if (USB_FEATURE_REMOTE_WAKEUP == req->wValue) {
                pudev->remote_wakeup = 0U;
                pudev->class_req_handler(pudev, req);
                USBD_CONTRL_STATUS_TX();
            } else if (USB_FEATURE_TEST_MODE == req->wValue) {
                /* can not clear test_mode feature */
                usbd_enum_error(pudev, req);
            } else {
                /* no operation */
            }
            break;
        default:
            break;
        }
        break;
    case USB_REQTYPE_INTERFACE:
        switch (pudev->status) {
        case USBD_ADDRESSED:
            usbd_enum_error (pudev, req);
            break;
        case USBD_CONFIGURED:
            if (LOWBYTE(req->wIndex) <= USBD_ITF_MAX_NUM) {
                /* no operation */
            } else {
                usbd_enum_error(pudev, req);
            }
            break;
        default:
            break;
        }
        break;
    case USB_REQTYPE_ENDPOINT:
        /* get endpoint address */
        ep_addr = LOWBYTE(req->wIndex);

        switch (pudev->status) {
        case USBD_ADDRESSED:
            if (IS_NOT_EP0(ep_addr)) {
                usbd_enum_error(pudev, req);
            }
            break;
        case USBD_CONFIGURED:
            /* clear endpoint halt feature */
            if (USB_FEATURE_ENDP_HALT == req->wValue) {
                if (IS_NOT_EP0(ep_addr)) {
                    usbd_ep_clear_stall(pudev, ep_addr);
                }
            }

            pudev->class_req_handler(pudev, req);

            USBD_CONTRL_STATUS_TX();
            break;
        default:
            break;
        }
        break;
    default:
        usbd_enum_error(pudev, req);
        break;
    }
}

/*!
    \brief      handle USB Set_Feature request
    \param[in]  pudev: pointer to USB device instance
    \param[in]  req: pointer to USB device request
    \param[out] none
    \retval     none
*/
static void  usbd_setfeature (usbd_core_handle_struct *pudev, usb_device_req_struct *req)
{
    uint8_t ep_addr = 0U;

    switch (req->bmRequestType & USB_REQ_RECIPIENT_MASK) {
    case USB_REQTYPE_DEVICE:
        switch (pudev->status) {
        case USBD_ADDRESSED:
        case USBD_CONFIGURED:
            /* set device remote wakeup feature */
            if (USB_FEATURE_REMOTE_WAKEUP == req->wValue) {
                pudev->remote_wakeup = 1U;

                USBD_CONTRL_STATUS_TX();
            }
            break;
        default:
            break;
        }
        break;
    case USB_REQTYPE_INTERFACE:
        switch (pudev->status) {
        case USBD_ADDRESSED:
            usbd_enum_error(pudev, req);
            break;
        case USBD_CONFIGURED:
            if (LOWBYTE(req->wIndex) <= USBD_ITF_MAX_NUM) {
                /* no operation */
            } else {
                usbd_enum_error(pudev, req);
            }
            break;
        default:
            break;
        }
        break;
    case USB_REQTYPE_ENDPOINT:
        /* get endpoint address */
        ep_addr = LOWBYTE(req->wIndex);

        switch (pudev->status) {
        case USBD_ADDRESSED:
            if (IS_NOT_EP0(ep_addr)) {
                usbd_enum_error(pudev, req);
            }
            break;
        case USBD_CONFIGURED:
            /* set endpoint halt feature */
            if (USB_FEATURE_ENDP_HALT == req->wValue) {
                if (IS_NOT_EP0(ep_addr)) {
                    usbd_ep_stall(pudev, ep_addr);
                }
            }
            USBD_CONTRL_STATUS_TX();
            break;
        default:
            break;
        }
        break;
    default:
        usbd_enum_error(pudev, req);
        break;
    }
}

/*!
    \brief      handle USB Set_Address request
    \param[in]  pudev: pointer to USB device instance
    \param[in]  req: pointer to USB device request
    \param[out] none
    \retval     none
*/
static void  usbd_setaddress (usbd_core_handle_struct *pudev, usb_device_req_struct *req)
{
    if ((0U == req->wIndex) && (0U == req->wLength)) {
        g_device_address = (uint8_t)(req->wValue) & 0x7FU;

        if (USBD_CONFIGURED == pudev->status) {
            usbd_enum_error(pudev, req);
        } else {
            USBD_CONTRL_STATUS_TX();

            if (0U != g_device_address) {
                pudev->status  = USBD_ADDRESSED;
            } else {
                pudev->status  = USBD_DEFAULT;
            }
        }
    } else {
        usbd_enum_error(pudev, req);
    }
}

/*!
    \brief      handle USB Get_Descriptor request
    \param[in]  pudev: pointer to USB device instance
    \param[in]  req: pointer to USB device request
    \param[out] none
    \retval     none
*/
static void  usbd_getdescriptor (usbd_core_handle_struct *pudev, usb_device_req_struct *req)
{
    if (USB_REQTYPE_DEVICE == (req->bmRequestType & USB_REQ_RECIPIENT_MASK)) {
        uint8_t *pbuf = NULL;
        uint8_t  desc_index = (uint8_t)(req->wValue >> 8);
        uint16_t len = 0U;

        if (desc_index <= 0x03U) {
            /* call corresponding descriptor get function */
            pbuf = standard_descriptor_get[desc_index - 1U](pudev, (uint8_t)(req->wValue) & 0xFFU, &len);
        } 
#ifdef LPM_ENABLED
        else if (USB_DESCTYPE_BOS == desc_index) {
            pbuf = usbd_bos_descriptor_get(pudev, &len);
        }
#endif /* LPM_ENABLED */
        else {
            usbd_enum_error(pudev, req);
        }

        if ((0U != len) && (0U != req->wLength)) {
            len = MIN(len, req->wLength);

            usbd_ep_tx (pudev, EP0_IN, pbuf, len);
        }
    } else if (USB_REQTYPE_INTERFACE == (req->bmRequestType & USB_REQ_RECIPIENT_MASK)) {
        if (NULL != pudev->class_req_handler) {
            /* get device class special descriptor */
            pudev->class_req_handler(pudev, req);
        }
    } else {
        
    }
}

/*!
    \brief      handle USB Set_Descriptor request
    \param[in]  pudev: pointer to USB device instance
    \param[in]  req: pointer to USB device request
    \param[out] none
    \retval     none
*/
static void  usbd_setdescriptor (usbd_core_handle_struct *pudev, usb_device_req_struct *req)
{
    /* no handle */
}

/*!
    \brief      handle USB Get_Configuration request
    \param[in]  pudev: pointer to USB device instance
    \param[in]  req: pointer to USB device request
    \param[out] none
    \retval     none
*/
static void  usbd_getconfiguration (usbd_core_handle_struct *pudev, usb_device_req_struct *req)
{
    uint32_t usbd_default_config = 0U;

    if (req->wLength != 1) {
        usbd_enum_error(pudev, req);
    } else {
        switch (pudev->status) {
        case USBD_ADDRESSED:
            usbd_ep_tx (pudev, EP0_IN, (uint8_t *)&usbd_default_config, 1U);
            break;
        case USBD_CONFIGURED:
            usbd_ep_tx (pudev, EP0_IN, &pudev->config_num, 1);
            break;
        default:
            usbd_enum_error(pudev, req);
            break;
        }
    }
}

/*!
    \brief      handle USB Set_Configuration request
    \param[in]  pudev: pointer to USB device instance
    \param[in]  req: pointer to USB device request
    \param[out] none
    \retval     none
*/
static void  usbd_setconfiguration (usbd_core_handle_struct *pudev, usb_device_req_struct *req)
{
    static uint8_t  cfgidx;

    cfgidx = (uint8_t)(req->wValue);

    if (cfgidx > USBD_CFG_MAX_NUM) {
        usbd_enum_error(pudev, req);
    } else {
        switch (pudev->status) {
        case USBD_ADDRESSED:
            if (cfgidx){
                pudev->config_num = cfgidx;
                pudev->status = USBD_CONFIGURED;
                pudev->class_init(pudev, cfgidx);
                USBD_CONTRL_STATUS_TX();
            } else {
                USBD_CONTRL_STATUS_TX();
            }
            break;
        case USBD_CONFIGURED:
            if (0U == cfgidx) {
                pudev->status = USBD_ADDRESSED;
                pudev->config_num = cfgidx;
                pudev->class_deinit(pudev, cfgidx);
                USBD_CONTRL_STATUS_TX();
            } else if (cfgidx != pudev->config_num) {
                /* clear old configuration */
                pudev->class_deinit(pudev, pudev->config_num);

                /* set new configuration */
                pudev->config_num = cfgidx;
                pudev->class_init(pudev, cfgidx);
                USBD_CONTRL_STATUS_TX();
            } else {
                USBD_CONTRL_STATUS_TX();
            }
            break;
        default:
            break;
        }
    }
}

/*!
    \brief      handle USB Get_Interface request
    \param[in]  pudev: pointer to USB device instance
    \param[in]  req: pointer to USB device request
    \param[out] none
    \retval     none
*/
static void  usbd_getinterface (usbd_core_handle_struct *pudev, usb_device_req_struct *req)
{
    switch (pudev->status) {
    case USBD_ADDRESSED:
        usbd_enum_error(pudev, req);
        break;
    case USBD_CONFIGURED:
        if (LOWBYTE(req->wIndex) <= USBD_ITF_MAX_NUM) {
            if (NULL != pudev->class_req_handler) {
                pudev->class_req_handler(pudev, req);
            }
        } else {
            usbd_enum_error(pudev, req);
        }
        break;
    default:
        break;
    }
}

/*!
    \brief      handle USB Set_Interface request
    \param[in]  pudev: pointer to USB device instance
    \param[in]  req: pointer to USB device request
    \param[out] none
    \retval     none
*/
static void  usbd_setinterface (usbd_core_handle_struct *pudev, usb_device_req_struct *req)
{
    switch (pudev->status) {
    case USBD_ADDRESSED:
        usbd_enum_error(pudev, req);
        break;
    case USBD_CONFIGURED:
        if (LOWBYTE(req->wIndex) <= USBD_ITF_MAX_NUM) {
            if (NULL != pudev->class_req_handler) {
                pudev->class_req_handler(pudev, req);
            }

            USBD_CONTRL_STATUS_TX();
        } else {
            usbd_enum_error(pudev, req);
        }
        break;
    default:
        break;
    }
}

/*!
    \brief      handle USB SynchFrame request
    \param[in]  pudev: pointer to USB device instance
    \param[in]  req: pointer to USB device request
    \param[out] none
    \retval     none
*/
static void  usbd_synchframe (usbd_core_handle_struct *pudev, usb_device_req_struct *req)
{
    /* no handle */
}

/*!
    \brief      decode setup data packet
    \param[in]  pudev: pointer to USB device instance
    \param[in]  req: pointer to USB device request
    \param[out] none
    \retval     none
*/
void  usbd_setup_request_parse (usbd_core_handle_struct *pudev, usb_device_req_struct *req)
{
    uint8_t *setup_data = pudev->setup_packet;

    req->bmRequestType = *setup_data;
    req->bRequest = *(uint8_t *)(setup_data + 1U);
    req->wValue = SWAPBYTE (setup_data + 2U);
    req->wIndex = SWAPBYTE (setup_data + 4U);
    req->wLength = SWAPBYTE (setup_data + 6U);

    pudev->ctl_count = req->wLength;
}

/*!
    \brief      handle USB enumeration error event
    \param[in]  pudev: pointer to USB device instance
    \param[in]  req: pointer to USB device request
    \param[out] none
    \retval     none
*/
void  usbd_enum_error (usbd_core_handle_struct *pudev, usb_device_req_struct *req)
{
    usbd_ep_stall(pudev, EP0);
}