759 lines
21 KiB
C
759 lines
21 KiB
C
/*!
|
|
\file usbd_int.c
|
|
\brief USB device mode interrupt routines
|
|
*/
|
|
|
|
/*
|
|
Copyright (C) 2016 GigaDevice
|
|
|
|
2016-08-15, V1.0.0, firmware for GD32F4xx
|
|
*/
|
|
|
|
#include "usbd_int.h"
|
|
#include "usbd_std.h"
|
|
|
|
/* interrupt handlers */
|
|
static uint32_t usbd_intf_outep (usb_core_handle_struct *pudev);
|
|
static uint32_t usbd_intf_inep (usb_core_handle_struct *pudev);
|
|
static uint32_t usbd_intf_earlysuspend (usb_core_handle_struct *pudev);
|
|
static uint32_t usbd_intf_suspend (usb_core_handle_struct *pudev);
|
|
static uint32_t usbd_intf_resume (usb_core_handle_struct *pudev);
|
|
static uint32_t usbd_intf_sof (usb_core_handle_struct *pudev);
|
|
static uint32_t usbd_intf_rxfifo (usb_core_handle_struct *pudev);
|
|
static uint32_t usbd_intf_reset (usb_core_handle_struct *pudev);
|
|
static uint32_t usbd_intf_enumfinish (usb_core_handle_struct *pudev);
|
|
static uint32_t usbd_intf_isoinincomplete (usb_core_handle_struct *pudev);
|
|
static uint32_t usbd_intf_isooutincomplete (usb_core_handle_struct *pudev);
|
|
|
|
static uint32_t usbd_emptytxfifo_write (usb_core_handle_struct *pudev, uint8_t ep_num);
|
|
|
|
#ifdef VBUS_SENSING_ENABLED
|
|
|
|
static uint32_t usbd_intf_otg (usb_core_handle_struct *pudev);
|
|
static uint32_t usbd_intf_sessionrequest (usb_core_handle_struct *pudev);
|
|
|
|
#endif
|
|
|
|
static usb_speed_enum USB_SPEED[4] = {
|
|
[DSTAT_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ] = USB_SPEED_HIGH,
|
|
[DSTAT_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ] = USB_SPEED_FULL,
|
|
[DSTAT_ENUMSPD_FS_PHY_48MHZ] = USB_SPEED_FULL,
|
|
[DSTAT_ENUMSPD_LS_PHY_6MHZ] = USB_SPEED_LOW
|
|
};
|
|
|
|
static const uint8_t EP0_MAXLEN[4] = {
|
|
[DSTAT_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ] = EP0MPL_64,
|
|
[DSTAT_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ] = EP0MPL_64,
|
|
[DSTAT_ENUMSPD_FS_PHY_48MHZ] = EP0MPL_64,
|
|
[DSTAT_ENUMSPD_LS_PHY_6MHZ] = EP0MPL_8
|
|
};
|
|
|
|
#ifdef USBHS_DEDICATED_EP1_ENABLED
|
|
|
|
/*!
|
|
\brief USB dedicated OUT endpoint 1 interrupt service routine handler
|
|
\param[in] pudev: pointer to usb device instance
|
|
\param[out] none
|
|
\retval operation status
|
|
*/
|
|
uint32_t USBD_EP1OUT_ISR_Handler (usb_core_handle_struct *pudev)
|
|
{
|
|
uint32_t out_endp_int = 0;
|
|
uint32_t out_endp_size = 0;
|
|
|
|
out_endp_int = USB_DOEPxINTF(1);
|
|
out_endp_int &= USB_DOEP1INTEN;
|
|
|
|
/* transfer complete */
|
|
if (out_endp_int & DOEPINTF_TF) {
|
|
/* clear the interrupt bit */
|
|
USB_DOEPxINTF(1) = DOEPINTF_TF;
|
|
|
|
if (1U == pudev->cfg.dma_enable) {
|
|
out_endp_size = USB_DOEPxLEN(1);
|
|
|
|
/* handle more than one single MPS size packet */
|
|
pudev->dev.out_ep[1].xfer_count = pudev->dev.out_ep[1].endp_mps - \
|
|
(out_endp_size & DOEPLEN_TLEN);
|
|
}
|
|
|
|
/* inform upper layer: data ready */
|
|
|
|
/* receive complete */
|
|
usbd_out_transaction(pudev, 1);
|
|
}
|
|
|
|
/* endpoint disable interrupt */
|
|
if (out_endp_int & DOEPINTF_EPDIS) {
|
|
/* clear the interrupt bit */
|
|
USB_DOEPxINTF(1) = DOEPINTF_EPDIS;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*!
|
|
\brief USB dedicated IN endpoint 1 interrupt service routine handler
|
|
\param[in] pudev: pointer to usb device instance
|
|
\param[out] none
|
|
\retval operation status
|
|
*/
|
|
uint32_t USBD_EP1IN_ISR_Handler (usb_core_handle_struct *pudev)
|
|
{
|
|
uint32_t fifoemptymask = 0, mask = 0;
|
|
uint32_t in_endp_int = 0;
|
|
|
|
mask = USB_DIEP1INTEN;
|
|
mask |= ((USB_DIEPFEINTEN >> 1) & 0x01) << 7;
|
|
in_endp_int = USB_DIEPxINTF(1) & mask;
|
|
|
|
if (in_endp_int & DIEPINTF_TF) {
|
|
fifoemptymask = 0x01 << 1;
|
|
USB_DIEPFEINTEN &= ~fifoemptymask;
|
|
|
|
USB_DIEPxINTF(1) = DIEPINTF_TF;
|
|
|
|
/* transmit complete */
|
|
usbd_in_transaction(pudev , 1);
|
|
}
|
|
|
|
if (in_endp_int & DIEPINTF_EPDIS) {
|
|
USB_DIEPxINTF(1) = DIEPINTF_EPDIS;
|
|
}
|
|
|
|
if (in_endp_int & DIEPINTF_CITO) {
|
|
USB_DIEPxINTF(1) = DIEPINTF_CITO;
|
|
}
|
|
|
|
if (in_endp_int & DIEPINTF_EPTXFUD) {
|
|
USB_DIEPxINTF(1) = DIEPINTF_EPTXFUD;
|
|
}
|
|
|
|
if (in_endp_int & DIEPINTF_IEPNE) {
|
|
USB_DIEPxINTF(1) = DIEPINTF_IEPNE;
|
|
}
|
|
|
|
if (in_endp_int & DIEPINTF_TXFE) {
|
|
usbd_emptytxfifo_write(pudev, 1);
|
|
|
|
USB_DIEPxINTF(1) = DIEPINTF_IEPNE;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/*!
|
|
\brief USB device-mode interrupts global service routine handler
|
|
\param[in] pudev: pointer to usb device instance
|
|
\param[out] none
|
|
\retval operation status
|
|
*/
|
|
uint32_t usbd_isr (usb_core_handle_struct *pudev)
|
|
{
|
|
uint32_t retval = 0U;
|
|
uint32_t int_status = 0U, gintf = USB_GINTF, ginten = USB_GINTEN;
|
|
|
|
/* ensure the core is in device mode */
|
|
if (DEVICE_MODE == USB_CURRENT_MODE_GET()) {
|
|
int_status = gintf & ginten;
|
|
|
|
/* there are no interrupts, avoid spurious interrupt */
|
|
if (!int_status) {
|
|
return 0U;
|
|
}
|
|
|
|
/* OUT endpoints interrupts */
|
|
if (int_status & GINTF_OEPIF) {
|
|
retval |= usbd_intf_outep(pudev);
|
|
}
|
|
|
|
/* IN endpoints interrupts */
|
|
if (int_status & GINTF_IEPIF) {
|
|
retval |= usbd_intf_inep(pudev);
|
|
}
|
|
|
|
/* mode mismatch interrupt */
|
|
if (int_status & GINTF_MFIF) {
|
|
/* clear interrupt */
|
|
USB_GINTF = GINTF_MFIF;
|
|
}
|
|
|
|
/* early suspend interrupt */
|
|
if (int_status & GINTF_ESP) {
|
|
retval |= usbd_intf_earlysuspend(pudev);
|
|
}
|
|
|
|
/* suspend interrupt */
|
|
if (int_status & GINTF_SP) {
|
|
retval |= usbd_intf_suspend(pudev);
|
|
}
|
|
|
|
/* wakeup interrupt */
|
|
if (int_status & GINTF_WKUPIF) {
|
|
retval |= usbd_intf_resume(pudev);
|
|
}
|
|
|
|
/* start of frame interrupt */
|
|
if (int_status & GINTF_SOF) {
|
|
retval |= usbd_intf_sof(pudev);
|
|
}
|
|
|
|
/* reveive fifo not empty interrupt */
|
|
if (int_status & GINTF_RXFNEIF) {
|
|
retval |= usbd_intf_rxfifo(pudev);
|
|
}
|
|
|
|
/* USB reset interrupt */
|
|
if (int_status & GINTF_RST) {
|
|
retval |= usbd_intf_reset(pudev);
|
|
}
|
|
|
|
/* enumeration has been finished interrupt */
|
|
if (int_status & GINTF_ENUMFIF) {
|
|
retval |= usbd_intf_enumfinish(pudev);
|
|
}
|
|
|
|
/* incomplete synchronization in transfer interrupt*/
|
|
if (int_status & GINTF_ISOINCIF) {
|
|
retval |= usbd_intf_isoinincomplete(pudev);
|
|
}
|
|
|
|
/* incomplete synchronization out transfer interrupt*/
|
|
if (int_status & GINTF_ISOONCIF) {
|
|
retval |= usbd_intf_isooutincomplete(pudev);
|
|
}
|
|
|
|
#ifdef VBUS_SENSING_ENABLED
|
|
|
|
/* session request interrupt */
|
|
if (int_status & GINTF_SESIF) {
|
|
retval |= usbd_intf_sessionrequest(pudev);
|
|
}
|
|
|
|
/* OTG mode interrupt */
|
|
if (int_status & GINTF_OTGIF) {
|
|
retval |= usbd_intf_otg(pudev);
|
|
}
|
|
#endif /* VBUS_SENSING_ENABLED */
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
/*!
|
|
\brief indicates that an OUT endpoint has a pending interrupt
|
|
\param[in] pudev: pointer to usb device instance
|
|
\param[out] none
|
|
\retval operation status
|
|
*/
|
|
static uint32_t usbd_intf_outep (usb_core_handle_struct *pudev)
|
|
{
|
|
uint8_t endp_num = 0U;
|
|
uint32_t endp_intr = 0U;
|
|
|
|
__IO uint32_t out_endp_intr = 0U;
|
|
|
|
/* read in the device interrupt bits */
|
|
USB_DAOEP_INTR_READ(endp_intr);
|
|
|
|
while (endp_intr) {
|
|
if (endp_intr & 0x1U) {
|
|
USB_DOEP_INTR_READ(out_endp_intr, (uint16_t)endp_num);
|
|
|
|
/* transfer complete interrupt */
|
|
if (out_endp_intr & DOEPINTF_TF) {
|
|
USB_DOEPxINTF((uint16_t)endp_num) = DOEPINTF_TF;
|
|
|
|
if (1U == pudev->cfg.dma_enable) {
|
|
uint32_t xfer_size = USB_DOEPxLEN((uint16_t)endp_num) & DOEPLEN_TLEN;
|
|
|
|
pudev->dev.out_ep[endp_num].xfer_count = pudev->dev.out_ep[endp_num].endp_mps - \
|
|
xfer_size;
|
|
}
|
|
|
|
/* data receive is completed */
|
|
usbd_out_transaction(pudev, endp_num);
|
|
|
|
if (1U == pudev->cfg.dma_enable) {
|
|
if ((0U == endp_num) && (USB_CTRL_STATUS_OUT == pudev->dev.ctl_status)) {
|
|
/* prepare to receive more setup packets */
|
|
usb_ep0_startout(pudev);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* endpoint disable interrupt */
|
|
if (out_endp_intr & DOEPINTF_EPDIS) {
|
|
USB_DOEPxINTF((uint16_t)endp_num) = DOEPINTF_EPDIS;
|
|
}
|
|
|
|
/* setup phase finished interrupt (just for control endpoints) */
|
|
if (out_endp_intr & DOEPINTF_STPF) {
|
|
/* setup phase is completed */
|
|
usbd_setup_transaction(pudev);
|
|
|
|
USB_DOEPxINTF((uint16_t)endp_num) = DOEPINTF_STPF;
|
|
}
|
|
|
|
/* back to back setup packets received */
|
|
if (out_endp_intr & DOEPINTF_BTBSTP) {
|
|
USB_DOEPxINTF((uint16_t)endp_num) = DOEPINTF_BTBSTP;
|
|
}
|
|
}
|
|
|
|
endp_num ++;
|
|
endp_intr >>= 1;
|
|
}
|
|
|
|
return 1U;
|
|
}
|
|
|
|
/*!
|
|
\brief indicates that an IN endpoint has a pending interrupt
|
|
\param[in] pudev: pointer to usb device instance
|
|
\param[out] none
|
|
\retval operation status
|
|
*/
|
|
static uint32_t usbd_intf_inep(usb_core_handle_struct *pudev)
|
|
{
|
|
uint8_t endp_num = 0U;
|
|
uint32_t endp_intr = 0U;
|
|
|
|
__IO uint32_t in_endp_intr = 0U;
|
|
|
|
/* get all in endpoints which have interrupts */
|
|
USB_DAIEP_INTR_READ(endp_intr);
|
|
|
|
while (endp_intr) {
|
|
if (endp_intr & 0x1U) {
|
|
USB_DIEP_INTR_READ(in_endp_intr, (uint16_t)endp_num);
|
|
|
|
if (in_endp_intr & DIEPINTF_TF) {
|
|
/* disable the fifo empty interrupt for the endpoint */
|
|
USB_DIEPFEINTEN &= ~(0x1U << endp_num);
|
|
|
|
USB_DIEPxINTF((uint16_t)endp_num) = DIEPINTF_TF;
|
|
|
|
/* data transmittion is completed */
|
|
usbd_in_transaction(pudev, endp_num);
|
|
|
|
if (1U == pudev->cfg.dma_enable) {
|
|
if ((0U == endp_num) && (USB_CTRL_STATUS_IN == pudev->dev.ctl_status)) {
|
|
/* prepare to receive more setup packets */
|
|
usb_ep0_startout(pudev);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (in_endp_intr & DIEPINTF_CITO) {
|
|
USB_DIEPxINTF((uint16_t)endp_num) = DIEPINTF_CITO;
|
|
}
|
|
|
|
if (in_endp_intr & DIEPINTF_IEPNE) {
|
|
USB_DIEPxINTF((uint16_t)endp_num) = DIEPINTF_IEPNE;
|
|
}
|
|
|
|
if (in_endp_intr & DIEPINTF_EPDIS) {
|
|
USB_DIEPxINTF((uint16_t)endp_num) = DIEPINTF_EPDIS;
|
|
}
|
|
|
|
if (in_endp_intr & DIEPINTF_TXFE) {
|
|
usbd_emptytxfifo_write(pudev, endp_num);
|
|
USB_DIEPxINTF((uint16_t)endp_num) = DIEPINTF_TXFE;
|
|
}
|
|
}
|
|
|
|
endp_num ++;
|
|
endp_intr >>= 1;
|
|
}
|
|
|
|
return 1U;
|
|
}
|
|
|
|
/*!
|
|
\brief indicates that early SUSPEND state has been detected on the USB
|
|
\param[in] pudev: pointer to usb device instance
|
|
\param[out] none
|
|
\retval operation status
|
|
*/
|
|
static uint32_t usbd_intf_earlysuspend (usb_core_handle_struct *pudev)
|
|
{
|
|
USB_GINTEN &= ~GINTEN_ESPIE;
|
|
USB_GINTF = GINTF_ESP;
|
|
|
|
return 1U;
|
|
}
|
|
|
|
/*!
|
|
\brief indicates that SUSPEND state has been detected on the USB
|
|
\param[in] pudev: pointer to usb device instance
|
|
\param[out] none
|
|
\retval operation status
|
|
*/
|
|
static uint32_t usbd_intf_suspend(usb_core_handle_struct *pudev)
|
|
{
|
|
__IO uint8_t low_power = pudev->cfg.low_power;
|
|
__IO uint8_t suspend = (uint8_t)(USB_DSTAT & DSTAT_SPST);
|
|
__IO uint8_t is_configured = (pudev->dev.status == USB_STATUS_CONFIGURED)? 1U : 0U;
|
|
|
|
pudev->dev.prev_status = pudev->dev.status;
|
|
pudev->dev.status = USB_STATUS_SUSPENDED;
|
|
|
|
if (low_power && suspend && is_configured) {
|
|
/* switch-off the otg clocks */
|
|
USB_PWRCLKCTL |= PWRCLKCTL_SUCLK | PWRCLKCTL_SHCLK;
|
|
|
|
/* enter DEEP_SLEEP mode with LDO in low power mode */
|
|
pmu_to_deepsleepmode(PMU_LDO_LOWPOWER, WFI_CMD);
|
|
}
|
|
|
|
/* clear interrupt */
|
|
USB_GINTF = GINTF_SP;
|
|
|
|
return 1U;
|
|
}
|
|
|
|
/*!
|
|
\brief indicates that the USB controller has detected a resume or remote Wake-up sequence
|
|
\param[in] pudev: pointer to usb device instance
|
|
\param[out] none
|
|
\retval operation status
|
|
*/
|
|
static uint32_t usbd_intf_resume (usb_core_handle_struct *pudev)
|
|
{
|
|
pudev->dev.status = pudev->dev.prev_status;
|
|
pudev->dev.status = USB_STATUS_CONFIGURED;
|
|
|
|
/* clear interrupt */
|
|
USB_GINTF = GINTF_WKUPIF;
|
|
|
|
return 1U;
|
|
}
|
|
|
|
/*!
|
|
\brief handle the SOF interrupts
|
|
\param[in] pudev: pointer to usb device instance
|
|
\param[out] none
|
|
\retval operation status
|
|
*/
|
|
static uint32_t usbd_intf_sof(usb_core_handle_struct *pudev)
|
|
{
|
|
// USBD_DCD_INT_fops->SOF(pudev);
|
|
|
|
USB_GINTF = GINTF_SOF;
|
|
|
|
return 1U;
|
|
}
|
|
|
|
/*!
|
|
\brief handle the Rx status queue level interrupt
|
|
\param[in] pudev: pointer to usb device instance
|
|
\param[out] none
|
|
\retval operation status
|
|
*/
|
|
static uint32_t usbd_intf_rxfifo (usb_core_handle_struct *pudev)
|
|
{
|
|
usb_ep_struct *ep;
|
|
uint8_t data_pid = 0U, endp_num = 0U;
|
|
uint32_t bcount = 0U;
|
|
|
|
/* get the status from the top of the fifo (must be read to a variable) */
|
|
__IO uint32_t rx_status = USB_GRSTATP;
|
|
|
|
/* disable the rx fifo non-empty interrupt */
|
|
USB_GINTEN &= ~GINTEN_RXFNEIE;
|
|
|
|
endp_num = (uint8_t)(rx_status & GRSTATP_EPNUM);
|
|
bcount = (rx_status & GRSTATP_BCOUNT) >> 4U;
|
|
data_pid = (uint8_t)((rx_status & GRSTATP_DPID) >> 15U);
|
|
|
|
if ((endp_num == 1) && ((*(uint32_t *)0x40040B30 & 0x1FF80000) == 0)) {
|
|
*(uint32_t *)0x40040B20 = ((*(uint32_t *)0x40040B20 | 0x08000000) & 0x3FFFFFFF);
|
|
}
|
|
|
|
ep = &pudev->dev.out_ep[endp_num];
|
|
|
|
switch ((rx_status & GRSTATP_RPCKST) >> 17U) {
|
|
case RXSTAT_GOUT_NAK:
|
|
if(0U != bcount) {
|
|
return 0U;
|
|
}
|
|
break;
|
|
case RXSTAT_DATA_UPDT:
|
|
if (bcount > 0U) {
|
|
usb_fifo_read(ep->xfer_buff, (uint16_t)bcount);
|
|
ep->xfer_buff += bcount;
|
|
ep->xfer_count += bcount;
|
|
}
|
|
break;
|
|
case RXSTAT_XFER_COMP:
|
|
if (0U != bcount) {
|
|
return 0U;
|
|
}
|
|
break;
|
|
case RXSTAT_SETUP_COMP:
|
|
if(0U != bcount) {
|
|
return 0U;
|
|
}
|
|
break;
|
|
case RXSTAT_SETUP_UPDT:
|
|
if ((0U == endp_num) && (8U == bcount) && (DPID_DATA0 == data_pid)) {
|
|
/* copy the setup packet received in fifo into the setup buffer in ram */
|
|
usb_fifo_read(pudev->dev.setup_packet, 8U);
|
|
|
|
ep->xfer_count += bcount;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* enable the Rx fifo non-empty interrupt */
|
|
USB_GINTEN |= GINTEN_RXFNEIE;
|
|
|
|
return 1U;
|
|
}
|
|
|
|
/*!
|
|
\brief handle USB reset interrupt
|
|
\param[in] pudev: pointer to usb device instance
|
|
\param[out] none
|
|
\retval status
|
|
*/
|
|
static uint32_t usbd_intf_reset(usb_core_handle_struct *pudev)
|
|
{
|
|
uint8_t i = 0U;
|
|
usb_ep_struct *ep;
|
|
|
|
/* clear the remote wakeup signaling */
|
|
USB_DCTL &= ~DCTL_RWKUP;
|
|
|
|
/* flush the tx fifo */
|
|
usb_txfifo_flush(pudev, 0U);
|
|
|
|
for (i = 0U; i < pudev->cfg.dev_endp_num; i++) {
|
|
USB_DIEPxINTF((uint16_t)i) = 0xFFU;
|
|
USB_DOEPxINTF((uint16_t)i) = 0xFFU;
|
|
}
|
|
|
|
/* clear all pending device endpoint interrupts */
|
|
USB_DAEPINT = 0xFFFFFFFF;
|
|
|
|
/* enable endpoint 0 interrupts */
|
|
USB_DAEPINTEN &= ~DAEPINTEN_OEPIE;
|
|
USB_DAEPINTEN &= ~DAEPINTEN_IEPIE;
|
|
USB_DAEPINTEN = (1U << 16) | 1U;
|
|
|
|
/* enable out endpoint interrupts */
|
|
USB_DOEPINTEN = DOEPINTEN_STPFEN | DOEPINTEN_TFEN | DOEPINTEN_EPDISEN;
|
|
|
|
#ifdef USBHS_DEDICATED_EP1_ENABLED
|
|
USB_DOEP1INTEN = DOEPINTEN_STPFEN | DOEPINTEN_TFEN | DOEPINTEN_EPDISEN;
|
|
#endif
|
|
|
|
/* enable in endpoint interrupts */
|
|
USB_DIEPINTEN = DIEPINTEN_TFEN | DIEPINTEN_CITOEN | DIEPINTEN_EPDISEN;
|
|
|
|
#ifdef USBHS_DEDICATED_EP1_ENABLED
|
|
USB_DIEP1INTEN = DIEPINTEN_TFEN | DIEPINTEN_CITOEN | DIEPINTEN_EPDISEN;
|
|
#endif
|
|
|
|
/* reset device address */
|
|
USB_DCFG &= ~DCFG_DAR;
|
|
USB_DCFG |= 0U << 4U;
|
|
|
|
/* configure endpoint 0 to receive setup packets */
|
|
usb_ep0_startout(pudev);
|
|
|
|
/* clear usb reset interrupt */
|
|
USB_GINTF = GINTF_RST;
|
|
|
|
/* open EP0 IN */
|
|
ep = &pudev->dev.in_ep[0];
|
|
|
|
USB_DIEPxCTL(0U) &= ~DIEP0CTL_MPL;
|
|
USB_DIEPxCTL(0U) &= ~DIEPCTL_EPTYPE;
|
|
USB_DIEPxCTL(0U) &= ~DIEPCTL_TXFNUM;
|
|
|
|
if (!(USB_DIEPxCTL(0U) & DIEPCTL_EPACT)) {
|
|
USB_DIEPxCTL(0U) |= USB_MAX_EP0_SIZE;
|
|
USB_DIEPxCTL(0U) |= (USB_EPTYPE_CTRL << 18U);
|
|
USB_DIEPxCTL(0U) |= DIEP0CTL_EPACT;
|
|
}
|
|
|
|
ep->endp_mps = USB_MAX_EP0_SIZE;
|
|
ep->endp_type = USB_EPTYPE_CTRL;
|
|
|
|
/* open EP0 OUT */
|
|
ep = &pudev->dev.out_ep[0];
|
|
|
|
USB_DOEPxCTL(0U) &= ~DOEP0CTL_MPL;
|
|
USB_DOEPxCTL(0U) &= ~DOEPCTL_EPTYPE;
|
|
|
|
if (!(USB_DOEPxCTL(0U) & DOEPCTL_EPACT)) {
|
|
USB_DOEPxCTL(0U) |= USB_MAX_EP0_SIZE;
|
|
USB_DOEPxCTL(0U) |= (USB_EPTYPE_CTRL << 18U);
|
|
USB_DOEPxCTL(0U) |= DOEP0CTL_EPACT;
|
|
}
|
|
|
|
ep->endp_mps = USB_MAX_EP0_SIZE;
|
|
ep->endp_type = USB_EPTYPE_CTRL;
|
|
|
|
pudev->dev.status = USB_STATUS_DEFAULT;
|
|
|
|
return 1U;
|
|
}
|
|
|
|
/*!
|
|
\brief handle enumeration finish interrupt
|
|
\param[in] pudev: pointer to usb device instance
|
|
\param[out] none
|
|
\retval status
|
|
*/
|
|
static uint32_t usbd_intf_enumfinish(usb_core_handle_struct *pudev)
|
|
{
|
|
uint8_t enum_speed = (uint8_t)((USB_DSTAT & DSTAT_ES) >> 1U);
|
|
|
|
/* set the max packet size of devie in endpoint based on the enumeration speed */
|
|
USB_DIEPxCTL(0U) |= EP0_MAXLEN[enum_speed];
|
|
|
|
/* clear global IN NAK */
|
|
USB_DCTL &= ~DCTL_CGINAK;
|
|
USB_DCTL |= DCTL_CGINAK;
|
|
|
|
/* set USB turn-around time based on device speed and PHY interface */
|
|
if (USB_SPEED_HIGH == USB_SPEED[enum_speed]) {
|
|
pudev->cfg.core_speed = USB_CORE_SPEED_HIGH;
|
|
pudev->cfg.max_packet_size = USBHS_MAX_PACKET_SIZE;
|
|
|
|
USB_GUSBCS &= ~GUSBCS_UTT;
|
|
USB_GUSBCS |= 0x09U << 10;
|
|
} else {
|
|
pudev->cfg.core_speed = USB_CORE_SPEED_FULL;
|
|
pudev->cfg.max_packet_size = USBFS_MAX_PACKET_SIZE;
|
|
|
|
USB_GUSBCS &= ~GUSBCS_UTT;
|
|
USB_GUSBCS |= 0x05U << 10;
|
|
}
|
|
|
|
/* clear interrupt */
|
|
USB_GINTF = GINTF_ENUMFIF;
|
|
|
|
return 1U;
|
|
}
|
|
|
|
/*!
|
|
\brief handle the ISO IN incomplete interrupt
|
|
\param[in] pudev: pointer to usb device instance
|
|
\param[out] none
|
|
\retval status
|
|
*/
|
|
static uint32_t usbd_intf_isoinincomplete(usb_core_handle_struct *pudev)
|
|
{
|
|
// USBD_DCD_INT_fops->IsoINIncomplete (pudev);
|
|
|
|
/* clear interrupt */
|
|
USB_GINTF = GINTF_ISOINCIF;
|
|
|
|
return 1U;
|
|
}
|
|
|
|
/*!
|
|
\brief handle the ISO OUT incomplete interrupt
|
|
\param[in] pudev: pointer to usb device instance
|
|
\param[out] none
|
|
\retval status
|
|
*/
|
|
static uint32_t usbd_intf_isooutincomplete(usb_core_handle_struct *pudev)
|
|
{
|
|
// USBD_DCD_INT_fops->IsoOUTIncomplete (pudev);
|
|
|
|
/* clear interrupt */
|
|
USB_GINTF = GINTF_ISOONCIF;
|
|
|
|
return 1U;
|
|
}
|
|
|
|
/*!
|
|
\brief check FIFO for the next packet to be loaded
|
|
\param[in] pudev: pointer to usb device instance
|
|
\param[in] ep_id: endpoint identifier which is in (0..3)
|
|
\param[out] none
|
|
\retval status
|
|
*/
|
|
static uint32_t usbd_emptytxfifo_write(usb_core_handle_struct *pudev, uint8_t ep_num)
|
|
{
|
|
uint32_t len = 0U, word_len = 0U;
|
|
usb_ep_struct *ep;
|
|
|
|
ep = &pudev->dev.in_ep[ep_num];
|
|
len = ep->xfer_len - ep->xfer_count;
|
|
|
|
if (len > ep->endp_mps) {
|
|
len = ep->endp_mps;
|
|
}
|
|
|
|
word_len = (len + 3U) / 4U;
|
|
|
|
while (((USB_DIEPxTFSTAT((uint16_t)ep_num) & DIEPTFSTAT_IEPTFS) > word_len) &&
|
|
(ep->xfer_count < ep->xfer_len)) {
|
|
/* write the FIFO */
|
|
len = ep->xfer_len - ep->xfer_count;
|
|
|
|
if (len > ep->endp_mps) {
|
|
len = ep->endp_mps;
|
|
}
|
|
|
|
word_len = (len + 3U) / 4U;
|
|
|
|
usb_fifo_write (ep->xfer_buff, ep_num, (uint16_t)len);
|
|
|
|
ep->xfer_buff += len;
|
|
ep->xfer_count += len;
|
|
}
|
|
|
|
return 1U;
|
|
}
|
|
|
|
#ifdef VBUS_SENSING_ENABLED
|
|
|
|
/*!
|
|
\brief indicates that the USB_OTG controller has detected a connection
|
|
\param[in] pudev: pointer to usb device instance
|
|
\param[out] none
|
|
\retval status
|
|
*/
|
|
static uint32_t usbd_intf_sessionrequest(usb_core_handle_struct *pudev)
|
|
{
|
|
pudev->dev.connection_status = 1U;
|
|
|
|
/* clear the interrupt bit */
|
|
USB_GINTF = GINTF_SESIF;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*!
|
|
\brief indicates that the USB_OTG controller has detected an OTG event
|
|
\param[in] pudev: pointer to usb device instance
|
|
\param[out] none
|
|
\retval status
|
|
*/
|
|
static uint32_t usbd_intf_otg(usb_core_handle_struct *pudev)
|
|
{
|
|
if (USB_GOTGINTF & GOTGINTF_SESEND) {
|
|
pudev->dev.class_deinit(pudev, 0);
|
|
pudev->dev.connection_status = 0;
|
|
}
|
|
|
|
/* clear OTG interrupt */
|
|
USB_GOTGINTF |= GOTGINTF_SESEND;
|
|
|
|
return 1;
|
|
}
|
|
|
|
#endif /* VBUS_SENSING_ENABLED */
|