/*! \file usbh_int.c \brief USB host mode interrupt handler file */ /* Copyright (C) 2017 GigaDevice 2017-02-10, V1.0.0, firmware for GD32F30x */ #include "usb_core.h" #include "usb_defines.h" #include "usbh_int.h" static uint32_t usbh_intf_sof (usb_core_handle_struct *pudev); static uint32_t usbh_intf_port (usb_core_handle_struct *pudev); static uint32_t usbh_intf_hc (usb_core_handle_struct *pudev); static uint32_t usbh_intf_hc_in (usb_core_handle_struct *pudev, uint8_t channel_num); static uint32_t usbh_intf_hc_out (usb_core_handle_struct *pudev, uint8_t channel_num); static uint32_t usbh_intf_rxfifo_noempty (usb_core_handle_struct *pudev); static uint32_t usbh_intf_nptxfifo_empty (usb_core_handle_struct *pudev); static uint32_t usbh_intf_ptxfifo_empty (usb_core_handle_struct *pudev); static uint32_t usbh_intf_disconnect (usb_core_handle_struct *pudev); static uint32_t usbh_intf_iso_incomplete_xfer (usb_core_handle_struct *pudev); /*! \brief handle global host interrupt \param[in] pudev: pointer to usb device instance \param[out] none \retval operation status */ uint32_t usbh_isr (usb_core_handle_struct *pudev) { uint32_t retval = 0U; uint32_t int_flag = 0U; /* check if host mode */ if (USB_CURRENT_MODE_GET() == HOST_MODE) { USB_CORE_INTR_READ(int_flag); if (!int_flag) { return 0U; } /* start of frame interrupt handle */ if (int_flag & GINTF_SOF) { retval |= usbh_intf_sof (pudev); } /* Rx FIFO non-empty interrupt handle */ if (int_flag & GINTF_RXFNEIF) { retval |= usbh_intf_rxfifo_noempty (pudev); } /* Non-Periodic Tx FIFO empty interrupt hanlde */ if (int_flag & GINTF_NPTXFEIF) { retval |= usbh_intf_nptxfifo_empty (pudev); } /* periodic Tx FIFO empty interrupt handle */ if (int_flag & GINTF_PTXFEIF) { retval |= usbh_intf_ptxfifo_empty (pudev); } /* host channels interrupt handle */ if (int_flag & GINTF_HCIF) { retval |= usbh_intf_hc (pudev); } /* host port interrupt handle */ if (int_flag & GINTF_HPIF) { retval |= usbh_intf_port (pudev); } /* disconnect interrupt handle */ if (int_flag & GINTF_DISCIF) { retval |= usbh_intf_disconnect (pudev); } /* isochronous IN transfer not complete interrupt handle */ if (int_flag & GINTF_ISOONCIF) { retval |= usbh_intf_iso_incomplete_xfer (pudev); } } return retval; } /*! \brief handle the start-of-frame interrupt in host mode \param[in] pudev: pointer to usb device instance \param[out] none \retval operation status */ static uint32_t usbh_intf_sof (usb_core_handle_struct *pudev) { usbh_hcd_int_fops->sof(pudev); /* clear interrupt */ USB_GINTF = GINTF_SOF; return 1U; } /*! \brief handle all host channels interrupt in host mode \param[in] pudev: pointer to usb device instance \param[out] none \retval operation status */ static uint32_t usbh_intf_hc (usb_core_handle_struct *pudev) { uint8_t i = 0U; uint32_t retval = 0U; for (i = 0U; i < pudev->cfg.host_channel_num; i++) { if ((USB_HACHINT & HACHINT_HACHINT) & ((uint32_t)1U << i)) { if ((USB_HCHxCTL((uint16_t)i) & HCHCTL_EPDIR) >> 15U) { retval |= usbh_intf_hc_in (pudev, i); } else { retval |= usbh_intf_hc_out (pudev, i); } } } return retval; } /*! \brief handle the disconnect interrupt \param[in] pudev: pointer to usb device instance \param[out] none \retval operation status */ static uint32_t usbh_intf_disconnect (usb_core_handle_struct *pudev) { usbh_hcd_int_fops->device_disconnected(pudev); /* clear interrupt */ USB_GINTF = GINTF_DISCIF; return 1U; } /*! \brief handle the non-periodic tx fifo empty interrupt \param[in] pudev: pointer to usb device instance \param[out] none \retval operation status */ static uint32_t usbh_intf_nptxfifo_empty (usb_core_handle_struct *pudev) { uint8_t channel_num = 0U; uint32_t dword_len = 0U, len = 0U; usb_hostchannel_struct *puhc; channel_num = (uint8_t)((USB_HNPTFQSTAT & HNPTFQSTAT_CNUM) >> 27U); puhc = &pudev->host.host_channel[channel_num]; dword_len = (puhc->xfer_len + 3U) / 4U; while (((USB_HNPTFQSTAT & HNPTFQSTAT_NPTXFS) > dword_len) && (0U != puhc->xfer_len)) { len = (USB_HNPTFQSTAT & HNPTFQSTAT_NPTXFS) * 4U; if (len > puhc->xfer_len) { /* last packet */ len = (uint16_t)puhc->xfer_len; USB_GINTEN &= ~GINTF_NPTXFEIF; } dword_len = (puhc->xfer_len + 3U) / 4U; usb_fifo_write (puhc->xfer_buff, channel_num, (uint16_t)len); puhc->xfer_buff += len; puhc->xfer_len -= len; puhc->xfer_count += len; } return 1U; } /*! \brief handle the periodic tx fifo empty interrupt \param[in] pudev: pointer to usb device instance \param[out] none \retval operation status */ static uint32_t usbh_intf_ptxfifo_empty (usb_core_handle_struct *pudev) { uint8_t channel_num = 0U; uint32_t dword_len = 0U, len = 0U; usb_hostchannel_struct *puhc; channel_num = (uint8_t)((USB_HPTFQSTAT & HPTFQSTAT_CNUM) >> 27U); puhc = &pudev->host.host_channel[channel_num]; dword_len = (puhc->xfer_len + 3U) / 4U; while (((USB_HPTFQSTAT & HPTFQSTAT_PTXFS) > dword_len) && (0U != puhc->xfer_len)) { len = (USB_HPTFQSTAT & HPTFQSTAT_PTXFS) * 4U; if (len > puhc->xfer_len) { len = puhc->xfer_len; /* last packet */ USB_GINTEN &= ~GINTF_PTXFEIF; } dword_len = (puhc->xfer_len + 3U) / 4U; usb_fifo_write (puhc->xfer_buff, channel_num, (uint16_t)len); puhc->xfer_buff += len; puhc->xfer_len -= len; puhc->xfer_count += len; } return 1U; } /*! \brief handle the host port interrupt \param[in] pudev: pointer to usb device instance \param[out] none \retval operation status */ static uint32_t usbh_intf_port (usb_core_handle_struct *pudev) { uint8_t port_speed = 0U; uint8_t port_reset = 0U; uint32_t retval = 0U; __IO uint32_t hostportdup = USB_HPCS; /* clear the interrupt bits in gintsts */ hostportdup &= ~HPCS_PE; hostportdup &= ~HPCS_PCD; hostportdup &= ~HPCS_PEDC; /* port connect detected */ if (USB_HPCS & HPCS_PCD) { hostportdup |= HPCS_PCD; usbh_hcd_int_fops->device_connected(pudev); retval |= 1U; } /* port enable changed */ if (USB_HPCS & HPCS_PEDC) { hostportdup |= HPCS_PEDC; if (USB_HPCS & HPCS_PE) { port_speed = (uint8_t)((USB_HPCS & HPCS_PS) >> 17U); if (HPRT_PRTSPD_LOW_SPEED == port_speed) { USB_HFT = 6000U; if (HCTLR_6_MHZ != (USB_HCTL & HCTL_CLKSEL)) { if (USB_CORE_EMBEDDED_PHY == pudev->cfg.phy_interface) { USB_FSLSCLOCK_INIT(HCTLR_6_MHZ); } port_reset = 1U; } } else if(HPRT_PRTSPD_FULL_SPEED == port_speed) { USB_HFT = 48000U; if (HCTLR_48_MHZ != (USB_HCTL & HCTL_CLKSEL)) { if (USB_CORE_EMBEDDED_PHY == pudev->cfg.phy_interface) { USB_FSLSCLOCK_INIT(HCTLR_48_MHZ); } port_reset = 1U; } } else { /* for high speed device and others */ port_reset = 1U; } } } if (port_reset) { usb_port_reset(pudev); } /* clear port interrupts */ USB_HPCS = hostportdup; return retval; } /*! \brief handle the OUT channel interrupt \param[in] pudev: pointer to usb device instance \param[in] channel_num: host channel number which is in (0..7) \param[out] none \retval operation status */ static uint32_t usbh_intf_hc_out (usb_core_handle_struct *pudev, uint8_t channel_num) { uint32_t channel_intr = USB_HCHxINTF((uint16_t)channel_num); usb_hostchannel_struct *puhc = &pudev->host.host_channel[channel_num]; channel_intr &= USB_HCHxINTEN((uint16_t)channel_num); if (channel_intr & HCHINTF_ACK) { if (URB_PING == puhc->urb_state) { puhc->err_count = 0U; USB_HCHxINTEN((uint16_t)channel_num) |= HCHINTEN_CHIE; usb_hostchannel_halt(pudev, channel_num); USB_HCHxINTF((uint16_t)channel_num) = HCHINTF_TF; puhc->status = HC_XF; } USB_HCHxINTF((uint16_t)channel_num) = HCHINTF_ACK; } else if (channel_intr & HCHINTF_REQOVR) { USB_HCHxINTEN((uint16_t)channel_num) |= HCHINTEN_CHIE; usb_hostchannel_halt(pudev, channel_num); USB_HCHxINTF((uint16_t)channel_num) = HCHINTF_REQOVR; } else if (channel_intr & HCHINTF_TF) { puhc->err_count = 0U; USB_HCHxINTEN((uint16_t)channel_num) |= HCHINTEN_CHIE; usb_hostchannel_halt(pudev, channel_num); USB_HCHxINTF((uint16_t)channel_num) = HCHINTF_TF; puhc->status = HC_XF; } else if (channel_intr & HCHINTF_STALL) { USB_HCHxINTEN((uint16_t)channel_num) |= HCHINTEN_CHIE; USB_HCHxINTF((uint16_t)channel_num) = HCHINTF_STALL; usb_hostchannel_halt(pudev, channel_num); puhc->status = HC_STALL; } else if (channel_intr & HCHINTF_NAK) { puhc->err_count = 0U; USB_HCHxINTEN((uint16_t)channel_num) |= HCHINTEN_CHIE; usb_hostchannel_halt(pudev, channel_num); USB_HCHxINTF((uint16_t)channel_num) = HCHINTF_NAK; puhc->status = HC_NAK; } else if (channel_intr & HCHINTF_USBER) { USB_HCHxINTEN((uint16_t)channel_num) |= HCHINTEN_CHIE; usb_hostchannel_halt(pudev, channel_num); puhc->err_count ++; puhc->status = HC_TRACERR; USB_HCHxINTF((uint16_t)channel_num) = HCHINTF_USBER; } else if (channel_intr & HCHINTF_NYET) { puhc->err_count = 0U; USB_HCHxINTEN((uint16_t)channel_num) |= HCHINTEN_CHIE; puhc->status = HC_NYET; USB_HCHxINTF((uint16_t)channel_num) = HCHINTF_NYET; } else if (channel_intr & HCHINTF_DTER) { USB_HCHxINTEN((uint16_t)channel_num) |= HCHINTEN_CHIE; usb_hostchannel_halt(pudev, channel_num); puhc->status= HC_DTGERR; USB_HCHxINTF((uint16_t)channel_num) = HCHINTF_DTER; } else if (channel_intr & HCHINTF_CH) { USB_HCHxINTEN((uint16_t)channel_num) &= ~HCHINTEN_CHIE; switch (puhc->status) { case HC_XF: puhc->urb_state = URB_DONE; if (USB_EPTYPE_BULK == ((USB_HCHxCTL((uint16_t)channel_num) & HCHCTL_EPTYPE) >> 18)) { puhc->data_tg_out ^= 1U; } break; case HC_NAK: puhc->urb_state = URB_NOTREADY; break; case HC_NYET: break; case HC_STALL: puhc->urb_state = URB_STALL; break; case HC_TRACERR: if (3U == puhc->err_count) { puhc->urb_state = URB_ERROR; puhc->err_count = 0U; } break; default: break; } USB_HCHxINTF((uint16_t)channel_num) = HCHINTF_CH; } else { /* no operation */ } return 1U; } /*! \brief handle the IN channel interrupt \param[in] pudev: pointer to usb device instance \param[in] channel_num: host channel number which is in (0..7) \param[out] none \retval operation status */ static uint32_t usbh_intf_hc_in (usb_core_handle_struct *pudev, uint8_t channel_num) { uint8_t endp_type = 0U; usb_hostchannel_struct *puhc = &pudev->host.host_channel[channel_num]; uint32_t channle_intf = USB_HCHxINTF((uint16_t)channel_num); __IO uint32_t channel_ctrl = USB_HCHxCTL((uint16_t)channel_num); channle_intf &= USB_HCHxINTEN((uint16_t)channel_num); endp_type = (uint8_t)((channel_ctrl & HCHCTL_EPTYPE) >> 18U); if (channle_intf & HCHINTF_ACK) { USB_HCHxINTF((uint16_t)channel_num) = HCHINTF_ACK; } else if (channle_intf & HCHINTF_STALL) { USB_HCHxINTEN((uint16_t)channel_num) |= HCHINTEN_CHIE; puhc->status = HC_STALL; USB_HCHxINTF((uint16_t)channel_num) = HCHINTF_NAK; USB_HCHxINTF((uint16_t)channel_num) = HCHINTF_STALL; /* NOTE: When there is a 'stall', reset also nak, else, the pudev->host.status = HC_STALL will be overwritten by 'nak' in code below */ channle_intf &= ~HCHINTF_NAK; usb_hostchannel_halt(pudev, channel_num); } else if (channle_intf & HCHINTF_DTER) { USB_HCHxINTEN((uint16_t)channel_num) |= HCHINTEN_CHIE; usb_hostchannel_halt(pudev, channel_num); USB_HCHxINTF((uint16_t)channel_num) = HCHINTF_NAK; puhc->status = HC_DTGERR; USB_HCHxINTF((uint16_t)channel_num) = HCHINTF_DTER; } else { /* no operation */ } if (channle_intf & HCHINTF_REQOVR) { USB_HCHxINTEN((uint16_t)channel_num) |= HCHINTEN_CHIE; usb_hostchannel_halt(pudev, channel_num); USB_HCHxINTF((uint16_t)channel_num) = HCHINTF_REQOVR; } else if (channle_intf & HCHINTF_TF) { puhc->status = HC_XF; puhc->err_count = 0U; USB_HCHxINTF((uint16_t)channel_num) = HCHINTF_TF; if ((USB_EPTYPE_CTRL == endp_type) || (USB_EPTYPE_BULK == endp_type)) { USB_HCHxINTEN((uint16_t)channel_num) |= HCHINTEN_CHIE; usb_hostchannel_halt(pudev, channel_num); USB_HCHxINTF((uint16_t)channel_num) = HCHINTF_NAK; puhc->data_tg_in ^= 1U; } else if (USB_EPTYPE_INTR == endp_type) { channel_ctrl |= HCHCTL_ODDFRM; USB_HCHxCTL((uint16_t)channel_num) = channel_ctrl; puhc->urb_state = URB_DONE; } else { /* no operation */ } } else if (channle_intf & HCHINTF_CH) { USB_HCHxINTEN((uint16_t)channel_num) &= ~HCHINTEN_CHIE; switch (puhc->status) { case HC_XF: puhc->urb_state = URB_DONE; break; case HC_TRACERR: case HC_DTGERR: puhc->err_count = 0U; puhc->urb_state = URB_ERROR; break; case HC_STALL: puhc->urb_state = URB_STALL; break; default: if (USB_EPTYPE_INTR == endp_type) { puhc->data_tg_in ^= 1U; } break; } USB_HCHxINTF((uint16_t)channel_num) = HCHINTF_CH; } else if (channle_intf & HCHINTF_USBER) { USB_HCHxINTEN((uint16_t)channel_num) |= HCHINTEN_CHIE; (puhc->err_count)++; puhc->status = HC_TRACERR; usb_hostchannel_halt(pudev, channel_num); USB_HCHxINTF((uint16_t)channel_num) = HCHINTF_USBER; } else if (channle_intf & HCHINTF_NAK) { if (USB_EPTYPE_INTR == endp_type) { USB_HCHxINTEN((uint16_t)channel_num) |= HCHINTEN_CHIE; usb_hostchannel_halt(pudev, channel_num); } else if ((USB_EPTYPE_CTRL == endp_type) || (USB_EPTYPE_BULK == endp_type)) { /* re-activate the channel */ channel_ctrl |= HCHCTL_CEN; channel_ctrl &= ~HCHCTL_CDIS; USB_HCHxCTL((uint16_t)channel_num) = channel_ctrl; } else { /* no operation */ } puhc->status = HC_NAK; USB_HCHxINTF((uint16_t)channel_num) = HCHINTF_NAK; } else { /* no operation */ } return 1U; } /*! \brief handle the rx fifo non-empty interrupt \param[in] pudev: pointer to usb device instance \param[out] none \retval operation status */ static uint32_t usbh_intf_rxfifo_noempty (usb_core_handle_struct *pudev) { uint32_t count = 0U; __IO uint8_t channel_num = 0U; __IO uint32_t rx_status = 0U; uint32_t usbh_ch_ctl_reg = 0U; usb_hostchannel_struct *puhc; /* disable the Rx status queue level interrupt */ USB_GINTEN &= ~GINTF_RXFNEIF; rx_status = USB_GRSTATP; channel_num = (uint8_t)(rx_status & GRSTATRP_CNUM); puhc = &pudev->host.host_channel[channel_num]; switch ((rx_status & GRSTATRP_RPCKST) >> 17) { case GRSTATR_RPCKST_IN: count = (rx_status & GRSTATRP_BCOUNT) >> 4; /* read the data into the host buffer. */ if ((count > 0U) && (puhc->xfer_buff != (void *)0)) { usb_fifo_read(puhc->xfer_buff, (uint16_t)count); /* manage multiple Xfer */ puhc->xfer_buff += count; puhc->xfer_count += count; if (USB_HCHxLEN((uint16_t)channel_num) & HCHLEN_PCNT) { /* re-activate the channel when more packets are expected */ usbh_ch_ctl_reg = USB_HCHxCTL((uint16_t)channel_num); usbh_ch_ctl_reg |= HCHCTL_CEN; usbh_ch_ctl_reg &= ~HCHCTL_CDIS; USB_HCHxCTL((uint16_t)channel_num) = usbh_ch_ctl_reg; } } break; case GRSTATR_RPCKST_IN_XFER_COMP: case GRSTATR_RPCKST_DATA_TOGGLE_ERR: case GRSTATR_RPCKST_CH_HALTED: default: break; } /* enable the Rx status queue level interrupt */ USB_GINTEN |= GINTF_RXFNEIF; return 1U; } /*! \brief handle the incomplete periodic transfer interrupt \param[in] pudev: pointer to usb device instance \param[out] none \retval operation status */ static uint32_t usbh_intf_iso_incomplete_xfer (usb_core_handle_struct *pudev) { __IO uint32_t gint_flag = 0U; gint_flag = USB_HCHxCTL(0U); USB_HCHxCTL(0U) = 0U; gint_flag = 0U; /* clear interrupt */ gint_flag |= GINTF_ISOONCIF; USB_GINTF = gint_flag; return 1U; }