/** * @file xmc_usbh.c * @date 2016-06-30 * * @cond ********************************************************************************** * XMClib v2.1.12 - XMC Peripheral Driver Library * * Copyright (c) 2015-2017, Infineon Technologies AG * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification,are permitted provided that the * following conditions are met: * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following * disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided with the distribution. * * Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY,OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * (To improve the quality of the software, users are encouraged to share modifications, enhancements or bug fixes with * Infineon Technologies AG dave@infineon.com). * ********************************************************************************************************************* * * Change History * -------------- * * 2016-06-30: * - Initial
* 2016-09-01: * - Removed Keil specific exclusion
* * @endcond * */ #include #include #include "xmc_usbh.h" #if((UC_SERIES == XMC45) || (UC_SERIES == XMC44) || (UC_SERIES == XMC43) || (UC_SERIES == XMC47) || (UC_SERIES == XMC48)) /*Function provides transfer result*/ static uint32_t XMC_USBH_PipeTransferGetResult (XMC_USBH_PIPE_HANDLE pipe_hndl); /*Updates the power state of the driver*/ static int32_t XMC_USBH_PowerControl (XMC_USBH_POWER_STATE_t state); /*********************************************************** USBH Driver ***************************************************************** */ /*Macro to represent USB host driver version*/ #define XMC_USBH_DRV_VERSION ((uint16_t)((uint16_t)XMC_LIB_MINOR_VERSION << 8U)|XMC_LIB_PATCH_VERSION) /*Macro used to gate PHY clock and AHB clock*/ #define XMC_USBH_PHY_CLK_STOP (0x03U) /*Macro used to ungate PHY clock and AHB clock*/ #define XMC_USBH_PHY_CLK_UNGATE (0x100U) /* Driver Version */ static const XMC_USBH_DRIVER_VERSION_t xmc_usbh_driver_version = { XMC_USBH_API_VERSION, XMC_USBH_DRV_VERSION }; /*Variables to hold selected VBUS port pin*/ XMC_GPIO_PORT_t * VBUS_port = XMC_GPIO_PORT3; uint32_t VBUS_pin = 2U; /*Array to track nack events on each pipe*/ bool is_nack[USBH0_MAX_PIPE_NUM]; /* Driver Capabilities */ static const XMC_USBH_CAPABILITIES_t xmc_usbh_driver_capabilities = { 0x0001U, /* Root HUB available Ports Mask */ 0U, /* Automatic SPLIT packet handling */ 1U, /* Signal Connect event */ 1U, /* Signal Disconnect event */ 0U /* Signal Overcurrent event */ }; /* Driver state and registers */ static XMC_USBH0_DEVICE_t XMC_USBH0_device/* __attribute__((section ("RW_IRAM1")))*/ = { (USB0_GLOBAL_TypeDef *)(USB0_BASE), /** Global register interface */ ((USB0_CH_TypeDef *)(USB0_CH0_BASE)), /** Host channel interface */ 0, /** Port event callback; set during init */ 0, /** Pipe event callback; set during init */ false, /** init status */ XMC_USBH_POWER_OFF, /** USB Power status */ false /** Port reset state */ }; /*USB host pipe information. The array stores information related to packet id, data toggle, * pending data transfer information, periodic transfer interval, received data size etc for each * pipe.*/ volatile XMC_USBH0_pipe_t pipe[USBH0_MAX_PIPE_NUM]; /* FIFO sizes in bytes (total available memory for FIFOs is 1.25 kB) */ #define RX_FIFO_SIZE (1128U) /* RxFIFO size */ #define TX_FIFO_SIZE_NON_PERI (64U) /* Non-periodic Tx FIFO size */ #define TX_FIFO_SIZE_PERI (1024U) /* Periodic Tx FIFO size */ /*Stores data FIFO pointer for each pipe*/ static uint32_t *XMC_USBH0_dfifo_ptr[USBH0_MAX_PIPE_NUM]; /* Local functions */ /** * @param enable Enable (XMC_USBH_CLOCK_GATING_ENABLE) or disable(XMC_USBH_CLOCK_GATING_DISABLE) clock gating * @return None * \parDescription:
* Enable/disable clock gating depending if feature is supported. */ __INLINE static void XMC_lClockGating(uint8_t enable) { #if defined(CLOCK_GATING_SUPPORTED) if (enable == XMC_USBH_CLOCK_GATING_ENABLE) XMC_SCU_CLOCK_GatePeripheralClock(XMC_SCU_PERIPHERAL_CLOCK_USB0); if (enable == XMC_USBH_CLOCK_GATING_DISABLE) XMC_SCU_CLOCK_UngatePeripheralClock(XMC_SCU_PERIPHERAL_CLOCK_USB0); #endif return; } /** * @param ptr_ch Pointer to Channel * @return None * \parDescription:
* Triggers halt of a channel. */ __INLINE static void XMC_lTriggerHaltChannel(USB0_CH_TypeDef *ptr_ch) { ptr_ch->HCINTMSK = USB_CH_HCINT_ChHltd_Msk; /* Enable halt interrupt */ ptr_ch->HCCHAR |= (uint32_t)(USB_CH_HCCHAR_ChEna_Msk | USB_CH_HCCHAR_ChDis_Msk); return; } /** * @param ptr_pipe Pointer to Pipe * @param ptr_ch Pointer to Channel * @return bool \n * true = success,\n * false = fail * \parDescription:
* Start transfer on Pipe. The function uses transfer complete interrupts to transfer data more than maximum * packet size. It takes care of updating data toggle information in subsequent packets related to the same data transfer. */ static bool XMC_lStartTransfer (XMC_USBH0_pipe_t *ptr_pipe, USB0_CH_TypeDef *ptr_ch) { uint32_t hcchar; uint32_t hctsiz; uint32_t hcintmsk; uint32_t num_remaining_transfer; uint32_t num_remaining_fifo; uint32_t num_remaining_queue; uint32_t txsts = 0U; uint32_t pckt_num; uint32_t max_pckt_size; uint8_t *ptr_src = ptr_pipe->data; uint32_t *ptr_dest = NULL; uint16_t cnt; uint32_t loc_index; bool status; if (!(XMC_USBH0_device.global_register->HPRT & USB_HPRT_PrtConnSts_Msk)) { status = false; } else { /* Save channel characteristic register to local variable */ hcchar = ptr_ch->HCCHAR; /* Save transfer size register to local variable */ hctsiz = ptr_ch->HCTSIZ_BUFFERMODE; hcintmsk = 0U; cnt = 0U; /* Prepare transfer */ /* Reset EPDir (transfer direction = output) and enable channel */ hcchar &= (uint32_t)(~(uint32_t)(USB_CH_HCCHAR_EPDir_Msk | USB_CH_HCCHAR_ChDis_Msk)); hcchar |= (uint32_t)USB_CH_HCCHAR_ChEna_Msk; /* Enable default interrupts needed for all transfers */ hcintmsk = (USB_CH_HCINTMSK_XactErrMsk_Msk | USB_CH_HCINTMSK_XferComplMsk_Msk | USB_CH_HCINTMSK_NakMsk_Msk | USB_CH_HCINTMSK_StallMsk_Msk) ; /* Keep PID */ hctsiz &= (uint32_t)USB_CH_HCTSIZ_BUFFERMODE_Pid_Msk; /* Packet specific setup */ switch (ptr_pipe->packet & XMC_USBH_PACKET_TOKEN_Msk) { case XMC_USBH_PACKET_IN: /* set transfer direction to input */ hcchar |= (uint32_t)USB_CH_HCCHAR_EPDir_Msk; /* Enable IN transfer specific interrupts */ hcintmsk |= (uint32_t)( USB_CH_HCINTMSK_DataTglErrMsk_Msk | USB_CH_HCINTMSK_BblErrMsk_Msk | USB_CH_HCINTMSK_AckMsk_Msk | USB_CH_HCINTMSK_NakMsk_Msk ) ; break; case XMC_USBH_PACKET_OUT: break; case XMC_USBH_PACKET_SETUP: hctsiz &= (uint32_t)~USB_CH_HCTSIZ_BUFFERMODE_Pid_Msk ; hctsiz |= (uint32_t)USB_CH_HCTSIZx_DPID_MDATA; break; default: break; } /* Prepare PID */ switch (ptr_pipe->packet & XMC_USBH_PACKET_DATA_Msk) { case XMC_USBH_PACKET_DATA0: hctsiz &= (uint32_t)~USB_CH_HCTSIZ_BUFFERMODE_Pid_Msk; hctsiz |= (uint32_t)USB_CH_HCTSIZx_DPID_DATA0; break; case XMC_USBH_PACKET_DATA1: hctsiz &= (uint32_t)~USB_CH_HCTSIZ_BUFFERMODE_Pid_Msk; hctsiz |= (uint32_t)USB_CH_HCTSIZx_DPID_DATA1; break; default: break; } /* Prepare odd/even frame */ if ((XMC_USBH0_device.global_register->HFNUM & 1U) != 0U) { hcchar &= (uint32_t)~USB_CH_HCCHAR_OddFrm_Msk; } else { hcchar |= (uint32_t)USB_CH_HCCHAR_OddFrm_Msk; } /* Get transfer type specific status */ switch (ptr_pipe->ep_type) { case XMC_USBH_ENDPOINT_CONTROL: case XMC_USBH_ENDPOINT_BULK: if (!(hcchar & USB_CH_HCCHAR_EPDir_Msk)) { txsts = XMC_USBH0_device.global_register->GNPTXSTS; } break; case XMC_USBH_ENDPOINT_ISOCHRONOUS: case XMC_USBH_ENDPOINT_INTERRUPT: if (!(hcchar & USB_CH_HCCHAR_EPDir_Msk)) { txsts = XMC_USBH0_device.global_register->HPTXSTS; } break; default: break; } /* Calculate remaining transfer size */ num_remaining_transfer = ptr_pipe->num - ptr_pipe->num_transferred_total; /* Limit transfer to available space inside fifo/queue if OUT transaction */ if ((uint32_t)(hcchar & USB_CH_HCCHAR_EPDir_Msk) == 0U) { max_pckt_size = ptr_pipe->ep_max_packet_size; num_remaining_fifo = (uint32_t)((uint32_t)(txsts & 0x0000FFFFU) << 2); num_remaining_queue = (uint32_t)((uint32_t)(txsts & 0x00FF0000U) >> 16); if (num_remaining_transfer > num_remaining_fifo) { num_remaining_transfer = num_remaining_fifo; } pckt_num = (uint32_t)((num_remaining_transfer + (max_pckt_size - 1U)) / max_pckt_size); if (pckt_num > num_remaining_queue) { pckt_num = num_remaining_queue; } if (num_remaining_transfer > (pckt_num * max_pckt_size)) { num_remaining_transfer = pckt_num * max_pckt_size; } cnt = (uint16_t)((num_remaining_transfer + 3U) / 4U); ptr_src = ptr_pipe->data + ptr_pipe->num_transferred_total; loc_index = ((USB0_CH_TypeDef *)ptr_ch - (USB0_CH_TypeDef *)(XMC_USBH0_device.host_channel_registers)); ptr_dest = (uint32_t *)XMC_USBH0_dfifo_ptr[loc_index]; /* For OUT/SETUP transfer num_transferring represents num of bytes to be sent */ ptr_pipe->num_transferring = num_remaining_transfer; } else { /* For IN transfer num_transferring is zero */ ptr_pipe->num_transferring = 0U; } /* Set packet count and transfer size */ if (num_remaining_transfer != 0U) { hctsiz |= (((num_remaining_transfer + ptr_pipe->ep_max_packet_size) - 1U) / ptr_pipe->ep_max_packet_size) << 19U; hctsiz |= num_remaining_transfer; } else { /* Zero length packet */ hctsiz |= ((uint32_t)1U << USB_CH_HCTSIZ_BUFFERMODE_PktCnt_Pos); /* Packet count = 1 */ hctsiz |= 0U; /* Transfer size = 0 */ } NVIC_DisableIRQ (USB0_0_IRQn); ptr_ch->HCINTMSK = hcintmsk; /* Enable channel interrupts */ ptr_ch->HCTSIZ_BUFFERMODE = hctsiz; /* Write ch transfer size */ ptr_ch->HCCHAR = hcchar; /* Write ch characteristics */ while (cnt != 0U) { /* Load data */ #if defined __TASKING__/*tasking*/ *ptr_dest = *((__unaligned uint32_t *)ptr_src); #else/* defined (__GNUC__) || defined (__CC_ARM) || defined (__ICCARM__)*/ *ptr_dest = *((__packed uint32_t *)ptr_src); #endif ptr_src += 4U; cnt--; } NVIC_EnableIRQ (USB0_0_IRQn); /* Enable OTG interrupt */ status = true; } return status; } /* USB driver API functions */ /** * @return \ref XMC_USBH_DRIVER_VERSION_t * \parDescription:
* Get driver version. */ static XMC_USBH_DRIVER_VERSION_t XMC_USBH_GetVersion (void) { return xmc_usbh_driver_version; } /** * @return \ref XMC_USBH_CAPABILITIES_t * \parDescription:
* Get driver capabilities. */ static XMC_USBH_CAPABILITIES_t XMC_USBH_GetCapabilities (void) { return xmc_usbh_driver_capabilities; } /** * @param cb_port_event Pointer to port event callback function \ref ARM_USBH_SignalPortEvent * @param cb_pipe_event Pointer to pipe event callback function \ref ARM_USBH_SignalPipeEvent * @return int32_t \ref Execution_status. 0 if execution is successful. * * \parDescription:
* Initialize USB Host Interface. Registers callback functions to be executed on port event and pipe event. * Initializes FIFO address for each pipe. Configures P3.2 as the VBUS charge pump enable pin.\n * * \parRelated APIs:
* XMC_USBH_Select_VBUS(), XMC_USBH_Uninitialize() \n */ static int32_t XMC_USBH_Initialize (XMC_USBH_SignalPortEvent_t cb_port_event, XMC_USBH_SignalPipeEvent_t cb_pipe_event) { uint32_t channel; int32_t status = XMC_USBH_DRIVER_OK; if (XMC_USBH0_device.init_done == true) { /*return ok since initialized*/ } else { /* assign callbacks */ XMC_USBH0_device.SignalPortEvent_cb = cb_port_event; XMC_USBH0_device.SignalPipeEvent_cb = cb_pipe_event; /* assign fifo start addresses */ for (channel = 0U; channel < USBH0_MAX_PIPE_NUM; channel++) { XMC_USBH0_dfifo_ptr[channel] = (uint32_t *)((uint32_t)USB0_BASE + ((channel + 1U) * 0x01000U)); } XMC_GPIO_SetMode(VBUS_port, (uint8_t)VBUS_pin, XMC_GPIO_MODE_OUTPUT_PUSH_PULL_ALT1); XMC_USBH0_device.init_done = true; } return status; } /** * @return int32_t \ref Execution_status. Returns 0 to indicate success. * \parDescription:
* De-initialize USB Host Interface. Sets the driver power state as powered off. Disables VBUS.\n * * \parRelated APIs:
* XMC_USBH_Select_VBUS(), XMC_USBH_Initialize(), XMC_USBH_PortVbusOnOff() \n */ static int32_t XMC_USBH_Uninitialize (void) { XMC_USBH0_device.init_done = false; (void)XMC_USBH_PowerControl(XMC_USBH_POWER_OFF); return XMC_USBH_DRIVER_OK; } /** * @param state Power state. \ref XMC_USBH_POWER_STATE_t * @return int32_t \ref Execution_status. Returns 0 if successful. * \parDescription:
* Control USB Host Interface Power. If power state is set to \ref XMC_USBH_POWER_FULL, * it initializes the peripheral and enables VBUS. If power state is set to \ref XMC_USBH_POWER_OFF, * disables the peripheral and the VBUS.\n * * \parRelated APIs:
* XMC_USBH_Select_VBUS(), XMC_USBH_Initialize(), XMC_USBH_PortVbusOnOff(), XMC_USBH_Uninitialize() \n */ static int32_t XMC_USBH_PowerControl (XMC_USBH_POWER_STATE_t state) { int32_t status = XMC_USBH_DRIVER_OK; uint32_t loc_value; switch (state) { case XMC_USBH_POWER_LOW: status = XMC_USBH_DRIVER_ERROR_UNSUPPORTED; break; case XMC_USBH_POWER_OFF: NVIC_DisableIRQ (USB0_0_IRQn); NVIC_ClearPendingIRQ (USB0_0_IRQn); /* Clear pending interrupt */ XMC_USBH0_device.power_state = state; /* Clear powered flag */ XMC_USBH0_device.global_register->GAHBCFG &= (uint32_t)(~USB_GAHBCFG_GlblIntrMsk_Msk); /* Disable USB interrupts */ XMC_lClockGating((uint8_t)XMC_USBH_CLOCK_GATING_ENABLE); /* Enable Clock Gating */ XMC_USBH0_device.global_register->PCGCCTL |= (uint32_t)USB_PCGCCTL_StopPclk_Msk; /* Stop PHY clock */ XMC_SCU_POWER_DisableUsb(); /* Disable Power USB */ XMC_SCU_RESET_AssertPeripheralReset(XMC_SCU_PERIPHERAL_RESET_USB0); /* reset USB */ XMC_USBH0_device.port_reset_active = false; /* Reset variables */ memset((void *)(pipe), 0, (USBH0_MAX_PIPE_NUM * sizeof(XMC_USBH0_pipe_t))); break; case XMC_USBH_POWER_FULL: if (XMC_USBH0_device.init_done == false) { status = XMC_USBH_DRIVER_ERROR; break; } /* not initialized */ if (XMC_USBH0_device.power_state == XMC_USBH_POWER_FULL) { status = XMC_USBH_DRIVER_OK; break; } /* already powered */ XMC_lClockGating((uint8_t)XMC_USBH_CLOCK_GATING_DISABLE); /* disable clock gating */ (void)XMC_USBH_osDelay(2U); XMC_SCU_RESET_DeassertPeripheralReset(XMC_SCU_PERIPHERAL_RESET_USB0); /* deassert reset USB */ (void)XMC_USBH_osDelay(2U); (void)XMC_USBH_osDelay(100U); XMC_SCU_POWER_EnableUsb(); /* Enable Power USB */ /* On-chip Full-speed PHY */ XMC_USBH0_device.global_register->PCGCCTL &= (uint32_t)~USB_PCGCCTL_StopPclk_Msk; /* Start PHY clock */ XMC_USBH0_device.global_register->GUSBCFG |= (uint32_t)USB_GUSBCFG_PHYSel_Msk; /* Full-speed transceiver */ while ((XMC_USBH0_device.global_register->GRSTCTL & USB_GRSTCTL_AHBIdle_Msk) == 0U) /* wait until AHB master state machine is idle */ { /*Wait*/ } XMC_USBH0_device.global_register->GRSTCTL |= (uint32_t)USB_GRSTCTL_CSftRst_Msk; /* Core soft reset */ while ((XMC_USBH0_device.global_register->GRSTCTL & USB_GRSTCTL_CSftRst_Msk) != 0U) /* wait soft reset confirmation */ { /*Wait*/ } (void)XMC_USBH_osDelay(100U); XMC_USBH0_device.port_reset_active = false; /* Reset variables */ memset((void *)(pipe), 0, (USBH0_MAX_PIPE_NUM * sizeof(XMC_USBH0_pipe_t))); /*Created local copy of GUSBCFG to avoid side effects*/ loc_value = XMC_USBH0_device.global_register->GUSBCFG; if (((loc_value & USB_GUSBCFG_ForceHstMode_Msk) == 0U) || \ ((loc_value & USB_GUSBCFG_ForceDevMode_Msk) != 0U)) { XMC_USBH0_device.global_register->GUSBCFG &= (uint32_t)~USB_GUSBCFG_ForceDevMode_Msk; /* Clear force device mode */ XMC_USBH0_device.global_register->GUSBCFG |= (uint32_t)USB_GUSBCFG_ForceHstMode_Msk; /* Force host mode */ (void)XMC_USBH_osDelay(100U); } /* FS only, even if HS is supported */ XMC_USBH0_device.global_register->HCFG |= (uint32_t)(0x200U | USB_CH_HCFG_FSLSSUP(1)); /* Rx FIFO setting */ XMC_USBH0_device.global_register->GRXFSIZ = (RX_FIFO_SIZE/4U); /* Non-periodic Tx FIFO setting */ XMC_USBH0_device.global_register->GNPTXFSIZ_HOSTMODE = (((uint32_t)(TX_FIFO_SIZE_NON_PERI/4U) << 16) | (RX_FIFO_SIZE / 4U)); /* Periodic Tx FIFO setting */ XMC_USBH0_device.global_register->HPTXFSIZ = ((uint32_t)(TX_FIFO_SIZE_PERI / 4U) << 16U) | ((RX_FIFO_SIZE + TX_FIFO_SIZE_NON_PERI) / 4U); /* Enable channel interrupts */ XMC_USBH0_device.global_register->HAINTMSK = ((uint32_t)1U << USBH0_MAX_PIPE_NUM) - 1U; /* Unmask interrupts */ XMC_USBH0_device.global_register->GINTMSK_HOSTMODE = ( USB_GINTSTS_HOSTMODE_DisconnInt_Msk | USB_GINTMSK_HOSTMODE_HChIntMsk_Msk | USB_GINTMSK_HOSTMODE_PrtIntMsk_Msk | USB_GINTMSK_HOSTMODE_RxFLvlMsk_Msk | USB_GINTMSK_HOSTMODE_SofMsk_Msk | USB_GINTMSK_HOSTMODE_WkUpIntMsk_Msk ) ; /* Set powered state */ XMC_USBH0_device.power_state = state; /* Enable interrupts */ XMC_USBH0_device.global_register->GAHBCFG |= (uint32_t)USB_GAHBCFG_GlblIntrMsk_Msk; /* Set highest interrupt priority */ NVIC_SetPriority (USB0_0_IRQn, 0U); NVIC_EnableIRQ (USB0_0_IRQn); break; default: status = XMC_USBH_DRIVER_ERROR_UNSUPPORTED; } return status; } /** * @param port Root HUB Port Number. Only one port(0) is supported. * @param vbus VBUS state - \n * - \b false VBUS off * - \b true VBUS on * @return int32_t \ref Execution_status. Returns 0 if successful. * * \parDescription:
* Set USB port VBUS on/off. */ static int32_t XMC_USBH_PortVbusOnOff (uint8_t port, bool vbus) { int32_t status = XMC_USBH_DRIVER_OK; if (XMC_USBH0_device.power_state == XMC_USBH_POWER_OFF) { status = XMC_USBH_DRIVER_ERROR; } else { if (port != 0U) { status = XMC_USBH_DRIVER_ERROR_PARAMETER; } else { if (vbus != 0U) { /* Port power on */ XMC_USBH0_device.global_register->HPRT |= (uint32_t)USB_HPRT_PrtPwr_Msk; XMC_GPIO_SetMode(VBUS_port, (uint8_t)VBUS_pin, XMC_GPIO_MODE_OUTPUT_PUSH_PULL_ALT1); } else { /* Port power off */ XMC_USBH0_device.global_register->HPRT &= (uint32_t)~USB_HPRT_PrtPwr_Msk; XMC_GPIO_SetMode(VBUS_port, (uint8_t)VBUS_pin, XMC_GPIO_MODE_INPUT_TRISTATE); } } } return status; } /** * @param port Root HUB Port Number. Only one port(0) is supported. * @return int32_t Execution status. \ref Execution_status * \parDescription:
* Do USB port reset. Port reset should honor the requirement of 50ms delay before enabling. * The function depends on implementation of XMC_USBH_osDelay() for 1ms delay to achieve required delay. * */ static int32_t XMC_USBH_PortReset (uint8_t port) { uint32_t hprt; int32_t status = XMC_USBH_DRIVER_OK; if (XMC_USBH0_device.power_state == XMC_USBH_POWER_OFF) { status = XMC_USBH_DRIVER_ERROR; } else { if (port != 0U) { status = XMC_USBH_DRIVER_ERROR_PARAMETER; } XMC_USBH0_device.port_reset_active = true; hprt = XMC_USBH0_device.global_register->HPRT; hprt &= (uint32_t)~USB_HPRT_PrtEna_Msk; /* Disable port */ hprt |= (uint32_t)USB_HPRT_PrtRst_Msk; /* Port reset */ XMC_USBH0_device.global_register->HPRT = hprt; (void)XMC_USBH_osDelay(50U); /* wait at least 50ms */ hprt &= (uint32_t)~USB_HPRT_PrtRst_Msk; /* Clear port reset */ XMC_USBH0_device.global_register->HPRT = hprt; (void)XMC_USBH_osDelay(50U); /* wait for ISR */ /*Wait for the port to be enabled*/ while ((XMC_USBH0_device.global_register->HPRT & USB_HPRT_PrtEna_Msk) == 0U) { /*wait*/ } if (XMC_USBH0_device.port_reset_active == true) { XMC_USBH0_device.port_reset_active = false; status = XMC_USBH_DRIVER_ERROR; /* reset not confirmed inside ISR */ } } return status; } /** * @param port USB port number. Only one port(0) is supported. * @return \ref Execution_status * \parDescription:
* Suspend USB Port (stop generating SOFs).\n * * \parRelated APIs:
* XMC_USBH_PortResume() \n */ static int32_t XMC_USBH_PortSuspend (uint8_t port) { int32_t status = XMC_USBH_DRIVER_OK; uint32_t hprt; if (XMC_USBH0_device.power_state == XMC_USBH_POWER_OFF) { status = XMC_USBH_DRIVER_ERROR; } else { if (port != 0U) { status = XMC_USBH_DRIVER_ERROR_PARAMETER; } else { hprt = XMC_USBH0_device.global_register->HPRT; hprt &= (uint32_t)~(USB_HPRT_PrtEna_Msk); hprt |= (uint32_t)USB_HPRT_PrtSusp_Msk; XMC_USBH0_device.global_register->HPRT = hprt; /* Stop PHY clock after suspending the bus*/ XMC_USBH0_device.global_register->PCGCCTL |= XMC_USBH_PHY_CLK_STOP; } } return status; } /** * @param port USB port number. Only one port(0) is supported. * @return \ref Execution_status * \parDescription:
* Resume suspended USB port (start generating SOFs).\n * * \parRelated APIs:
* XMC_USBH_PortSuspend() \n */ static int32_t XMC_USBH_PortResume (uint8_t port) { int32_t status = XMC_USBH_DRIVER_OK; uint32_t hprt; if (XMC_USBH0_device.power_state == XMC_USBH_POWER_OFF) { status = XMC_USBH_DRIVER_ERROR; } else { if (port != 0U) { status = XMC_USBH_DRIVER_ERROR_PARAMETER; } else { /*Ungate PHY clock*/ XMC_USBH0_device.global_register->PCGCCTL = XMC_USBH_PHY_CLK_UNGATE; /*Set resume bit*/ hprt = XMC_USBH0_device.global_register->HPRT; hprt &= (uint32_t)~(USB_HPRT_PrtEna_Msk); hprt |= (uint32_t)USB_HPRT_PrtRes_Msk; XMC_USBH0_device.global_register->HPRT = hprt; (void)XMC_USBH_osDelay(20U); hprt = XMC_USBH0_device.global_register->HPRT; hprt &= (uint32_t)~(USB_HPRT_PrtEna_Msk); hprt &= (uint32_t)~((uint32_t)USB_HPRT_PrtRes_Msk); XMC_USBH0_device.global_register->HPRT = hprt; } } return status; } /** * @param port USB port number. Only one port(0) is supported. * @return XMC_USBH_PORT_STATE_t Port State * * \parDescription:
* Get current USB port state. The state indicates if the port is connected, port speed * and port overcurrent status. */ static XMC_USBH_PORT_STATE_t XMC_USBH_PortGetState (uint8_t port) { XMC_USBH_PORT_STATE_t port_state = { 0U, 0U, 0U }; uint32_t hprt; if (XMC_USBH0_device.power_state == XMC_USBH_POWER_OFF) { /*Do not update the port state*/ } else { if (port != 0U) { /*Do not update the port state*/ } else { hprt = XMC_USBH0_device.global_register->HPRT; if(((hprt & USB_HPRT_PrtConnSts_Msk) != 0U)) { port_state.connected = 1U; } else { port_state.connected = 0U; } port_state.overcurrent = 0U; switch ((uint32_t)((uint32_t)(hprt & USB_HPRT_PrtSpd_Msk) >> USB_HPRT_PrtSpd_Pos)) { case 1U: /* Full speed */ port_state.speed = XMC_USBH_SPEED_FULL; break; default: break; } } } return port_state; } /** * @param dev_addr Device address * @param dev_speed Device speed * @param hub_addr Hub address. This value should be 0 since hub is not supported. * @param hub_port USB port number. Only one port(0) is supported. * @param ep_addr Device endpoint address \n * - ep_addr.0..3: Address \n * - ep_addr.7: Direction\n * @param ep_type Endpoint type (ARM_USB_ENDPOINT_xxx) * @param ep_max_packet_size Endpoint maximum packet size * @param ep_interval Endpoint polling interval * @return XMC_USBH_PIPE_HANDLE Pipe handle is a pointer to pipe hardware base address. * * \parDescription:
* Create/allocate a pipe configured with input parameters. The function looks for an unused pipe and configures with input parameters. * * \parRelated APIs:
* XMC_USBH_PipeModify(), XMC_USBH_PipeDelete(), XMC_USBH_PipeReset(), XMC_USBH_PipeTransfer() \n */ static XMC_USBH_PIPE_HANDLE XMC_USBH_PipeCreate (uint8_t dev_addr, uint8_t dev_speed, uint8_t hub_addr, uint8_t hub_port, uint8_t ep_addr, uint8_t ep_type, uint16_t ep_max_packet_size, uint8_t ep_interval) { XMC_USBH0_pipe_t *ptr_pipe; USB0_CH_TypeDef *ptr_ch; uint32_t i; uint32_t loc_val; if (XMC_USBH0_device.power_state == XMC_USBH_POWER_OFF) { ptr_ch = (USB0_CH_TypeDef *)NULL; } else { /* get first free pipe available */ ptr_ch = (USB0_CH_TypeDef *)(XMC_USBH0_device.host_channel_registers); for (i = 0U; i < USBH0_MAX_PIPE_NUM; i++) { if ((ptr_ch->HCCHAR & 0x3FFFFFFFU) == 0U) { break; } ptr_ch++; } /* free pipe found? */ if (i == USBH0_MAX_PIPE_NUM) { ptr_ch = (USB0_CH_TypeDef *)NULL; } else { ptr_pipe = (XMC_USBH0_pipe_t *)(&pipe[(ptr_ch - (USB0_CH_TypeDef *)(XMC_USBH0_device.host_channel_registers))]); memset((void *)ptr_pipe, 0, sizeof(XMC_USBH0_pipe_t)); /* Initialize pipe structure */ /* Fill in all fields of Endpoint Descriptor */ /*Get the end point direction from the MSB of address*/ loc_val = 0U; if (((ep_addr >> 7U) & 0x1U) == 0U) { loc_val = 1U; } ptr_ch->HCCHAR = ((uint32_t)(USB_CH_HCCHARx_MPS(ep_max_packet_size))| USB_CH_HCCHARx_EPNUM(ep_addr)) | (uint32_t)(USB_CH_HCCHAR_EPDir_Msk * loc_val) | (USB_CH_HCCHARx_EPTYPE (ep_type) ) | (USB_CH_HCCHARx_DEVADDR (dev_addr) ) ; /* Store Pipe settings */ ptr_pipe->ep_max_packet_size = ep_max_packet_size; ptr_pipe->ep_type = ep_type; switch (ep_type) { case XMC_USBH_ENDPOINT_CONTROL: case XMC_USBH_ENDPOINT_BULK: break; case XMC_USBH_ENDPOINT_ISOCHRONOUS: case XMC_USBH_ENDPOINT_INTERRUPT: if (ep_interval > 0U) { ptr_pipe->interval_reload = ep_interval; } ptr_pipe->interval = ptr_pipe->interval_reload; loc_val = ((((uint32_t)ep_max_packet_size >> 11U) + 1U) & 3U); ptr_ch->HCCHAR |= (uint32_t)USB_CH_HCCHARx_MCEC(loc_val); break; default: break; } } } return ((XMC_USBH_EP_HANDLE)ptr_ch); } /** * @param pipe_hndl Pointer returned by the pipe create function. It is the hardware based address of a USB channel. * @param dev_addr Device address to be configured for the pipe. * @param dev_speed Device speed class. * @param hub_addr Hub address. It should be 0 since hub is not supported. * @param hub_port USB port number. Only one port(0) is supported. * @param ep_max_packet_size Endpoint maximum packet size * @return Execution_status * * \parDescription:
* Modify an existing pipe with input parameters. It can be used to configure the pipe after receiving configuration details from the device. * * \parRelated APIs:
* XMC_USBH_PipeCreate(), XMC_USBH_PipeDelete(), XMC_USBH_PipeReset(), XMC_USBH_PipeTransfer() \n */ static int32_t XMC_USBH_PipeModify (XMC_USBH_PIPE_HANDLE pipe_hndl, uint8_t dev_addr, uint8_t dev_speed, uint8_t hub_addr, uint8_t hub_port, uint16_t ep_max_packet_size) { XMC_USBH0_pipe_t *ptr_pipe; USB0_CH_TypeDef *ptr_ch; uint32_t hcchar; int32_t status = XMC_USBH_DRIVER_OK; if (XMC_USBH0_device.power_state == XMC_USBH_POWER_OFF) { status = XMC_USBH_DRIVER_ERROR; } else { if (pipe_hndl == 0U) { status = XMC_USBH_DRIVER_ERROR_PARAMETER; } else { ptr_ch = (USB0_CH_TypeDef *)(pipe_hndl); ptr_pipe = (XMC_USBH0_pipe_t *)(&pipe[(ptr_ch - (USB0_CH_TypeDef *)(XMC_USBH0_device.host_channel_registers))]); if (ptr_pipe->in_use != 0U) { status = XMC_USBH_DRIVER_ERROR_BUSY; } else { /* Fill in all fields of channel */ hcchar = ptr_ch->HCCHAR; /* Clear fields */ hcchar &= (uint32_t)~(USB_CH_HCCHAR_MPS_Msk | USB_CH_HCCHAR_DevAddr_Msk) ; /* Set fields */ hcchar |= (uint32_t)(USB_CH_HCCHARx_MPS(ep_max_packet_size) | (USB_CH_HCCHARx_DEVADDR(dev_addr))); ptr_ch->HCCHAR = hcchar; ptr_pipe->ep_max_packet_size = ep_max_packet_size; } } } return status; } /** * @param pipe_hndl Pointer returned by the pipe create function. It is the hardware based address of a USB channel. * @return Execution_status * * \parDescription:
* Delete pipe from active pipes list. After it is deleted, it can be assigned to new pipe request. * * \parRelated APIs:
* XMC_USBH_PipeCreate(), XMC_USBH_PipeModify(), XMC_USBH_PipeReset(), XMC_USBH_PipeTransfer() \n */ static int32_t XMC_USBH_PipeDelete (XMC_USBH_PIPE_HANDLE pipe_hndl) { XMC_USBH0_pipe_t *ptr_pipe; USB0_CH_TypeDef *ptr_ch; int32_t status = XMC_USBH_DRIVER_OK; if (XMC_USBH0_device.power_state == XMC_USBH_POWER_OFF) { status = XMC_USBH_DRIVER_ERROR; } else { if (pipe_hndl == 0U) { status = XMC_USBH_DRIVER_ERROR_PARAMETER; } else { ptr_ch = (USB0_CH_TypeDef *)(pipe_hndl); ptr_pipe = (XMC_USBH0_pipe_t *)(&pipe[(ptr_ch - (USB0_CH_TypeDef *)(XMC_USBH0_device.host_channel_registers))]); if (ptr_pipe->in_use != 0U) { status = XMC_USBH_DRIVER_ERROR_BUSY; } else { ptr_ch->HCCHAR = 0U; ptr_ch->HCINT = 0U; ptr_ch->HCINTMSK = 0U; ptr_ch->HCTSIZ_BUFFERMODE = 0U; memset((void *)ptr_pipe, 0, sizeof(XMC_USBH0_pipe_t)); } } } return status; } /** * @param pipe_hndl Pointer returned by the pipe create function. It is the hardware based address of a USB channel. * @return Execution_status * \parDescription:
* Reset pipe by clearing the interrupt mask and resetting the transfer control register.\n * * \parRelated APIs:
* XMC_USBH_PipeCreate(), XMC_USBH_PipeModify(), XMC_USBH_PipeDelete(), XMC_USBH_PipeTransfer() \n */ static int32_t XMC_USBH_PipeReset (XMC_USBH_PIPE_HANDLE pipe_hndl) { XMC_USBH0_pipe_t *ptr_pipe; USB0_CH_TypeDef *ptr_ch; int32_t status = XMC_USBH_DRIVER_OK; if (XMC_USBH0_device.power_state == XMC_USBH_POWER_OFF) { status = XMC_USBH_DRIVER_ERROR; } else { if (pipe_hndl == 0U) { status = XMC_USBH_DRIVER_ERROR_PARAMETER; } else { ptr_ch = (USB0_CH_TypeDef *)(pipe_hndl); ptr_pipe = (XMC_USBH0_pipe_t *)(&pipe[(ptr_ch - (USB0_CH_TypeDef *)(XMC_USBH0_device.host_channel_registers))]); if (ptr_pipe->in_use != 0U) { status = XMC_USBH_DRIVER_ERROR_BUSY; } else { ptr_ch->HCINT = 0U; ptr_ch->HCINTMSK = 0U; ptr_ch->HCTSIZ_BUFFERMODE = 0U; } } } return status; } /** * @param pipe_hndl Pointer returned by the pipe create function. It is the hardware based address of a USB channel. * @param packet Packet information with bit masks to represent packet data toggle information and packet type.\n * \ref XMC_USBH_PACKET_DATA0 / \ref XMC_USBH_PACKET_DATA1, \ref XMC_USBH_PACKET_SETUP / * \ref XMC_USBH_PACKET_OUT / \ref XMC_USBH_PACKET_IN * @param data Pointer to buffer with data to send or for received data to be stored. * @param num Number of data bytes to transfer * @return Execution_status * * \parDescription:
* Transfer packets through USB Pipe. Handles transfer of multiple packets using the pipe transfer complete event. * The pipe event callback function will be called when the transfer is completed.\n * * \parRelated APIs:
* XMC_USBH_PipeCreate(), XMC_USBH_PipeModify(), XMC_USBH_PipeDelete(), XMC_USBH_PipeReset() \n */ static int32_t XMC_USBH_PipeTransfer (XMC_USBH_PIPE_HANDLE pipe_hndl, uint32_t packet, uint8_t *data, uint32_t num) { XMC_USBH0_pipe_t *ptr_pipe; int32_t status = XMC_USBH_DRIVER_OK; if (XMC_USBH0_device.power_state == XMC_USBH_POWER_OFF) { status = XMC_USBH_DRIVER_ERROR; } else { if(!(((((packet & XMC_USBH_PACKET_TOKEN_Msk) == XMC_USBH_PACKET_OUT) || ((packet & XMC_USBH_PACKET_TOKEN_Msk) == XMC_USBH_PACKET_IN))) || ((packet & XMC_USBH_PACKET_TOKEN_Msk) == XMC_USBH_PACKET_SETUP ))) { status = XMC_USBH_DRIVER_ERROR_PARAMETER; } else { if (pipe_hndl == 0U) { status = XMC_USBH_DRIVER_ERROR_PARAMETER; } else { if ((XMC_USBH0_device.global_register->HPRT & USB_HPRT_PrtConnSts_Msk) == 0U) { status = XMC_USBH_DRIVER_ERROR; } else { ptr_pipe = (XMC_USBH0_pipe_t *)(&pipe[((USB0_CH_TypeDef *)pipe_hndl - (USB0_CH_TypeDef *)(XMC_USBH0_device.host_channel_registers))]); if (ptr_pipe->in_use != 0U) { status = XMC_USBH_DRIVER_ERROR_BUSY; } else { /* Prepare transfer information */ ptr_pipe->packet = packet; ptr_pipe->data = data; ptr_pipe->num = num; ptr_pipe->num_transferred_total = 0U; ptr_pipe->num_transferring = 0U; ptr_pipe->in_use = 0U; ptr_pipe->transfer_active = 0U; ptr_pipe->interrupt_triggered = 0U; ptr_pipe->event = 0U; if ((ptr_pipe->ep_type == (uint8_t)XMC_USBH_ENDPOINT_INTERRUPT) && (ptr_pipe->interval != 0U)) { ptr_pipe->in_use = 1U; /* transfer will be started inside interrupt (SOF) */ } else { ptr_pipe->transfer_active = 1U; ptr_pipe->in_use = 1U; if(XMC_lStartTransfer (ptr_pipe, (USB0_CH_TypeDef *)pipe_hndl) == false) { status = XMC_USBH_DRIVER_ERROR; } } } } } } } return status; } /** * @param pipe_hndl Pointer returned by the pipe create function. It is the hardware based address of a USB channel. * @return uint32_t Number of successfully transferred data bytes * * \parDescription:
* Get result of USB Pipe transfer. * * \parRelated APIs:
* XMC_USBH_PipeCreate(), XMC_USBH_PipeModify(), XMC_USBH_PipeDelete(), XMC_USBH_PipeTransfer() \n */ static uint32_t XMC_USBH_PipeTransferGetResult (XMC_USBH_PIPE_HANDLE pipe_hndl) { uint32_t status; if (pipe_hndl == 0U) { status = 0U; } else { status = (pipe[((USB0_CH_TypeDef *)pipe_hndl - (USB0_CH_TypeDef *)(XMC_USBH0_device.host_channel_registers))].num_transferred_total); } return status; } /** * @param pipe_hndl Pointer returned by the pipe create function. It is the hardware based address of a USB channel. * @return Execution_status * * \parDescription:
* Abort current USB Pipe transfer.\n * * \parRelated APIs:
* XMC_USBH_PipeCreate(), XMC_USBH_PipeModify(), XMC_USBH_PipeDelete(), XMC_USBH_PipeTransfer() \n */ static int32_t XMC_USBH_PipeTransferAbort (XMC_USBH_PIPE_HANDLE pipe_hndl) { XMC_USBH0_pipe_t *ptr_pipe; USB0_CH_TypeDef *ptr_ch; uint32_t timeout; int32_t status = XMC_USBH_DRIVER_ERROR; ptr_ch = (USB0_CH_TypeDef *) pipe_hndl; if (XMC_USBH0_device.power_state == XMC_USBH_POWER_OFF) { /*Error in power state*/ } else { if (pipe_hndl == 0U) { status = XMC_USBH_DRIVER_ERROR_PARAMETER; } else { ptr_pipe = (XMC_USBH0_pipe_t *)(&pipe[(ptr_ch - (USB0_CH_TypeDef *)(XMC_USBH0_device.host_channel_registers))]); if (ptr_pipe->in_use != 0U) { ptr_pipe->in_use = 0U; /* Disable channel if not yet halted */ if ((ptr_ch->HCINT & USB_CH_HCINT_ChHltd_Msk) == 0U) { if (ptr_ch->HCCHAR & USB_CH_HCCHAR_ChEna_Msk) { ptr_ch->HCINTMSK = 0U; (void)XMC_USBH_osDelay(1U); if (ptr_ch->HCINT & USB_CH_HCINT_NAK_Msk) { ptr_ch->HCINT = USB_CH_HCINTx_ALL; /* Clear all interrupts */ status = XMC_USBH_DRIVER_OK; } else { ptr_ch->HCINT = USB_CH_HCINTx_ALL; /* Clear all interrupts */ ptr_ch->HCCHAR = (uint32_t)(ptr_ch->HCCHAR | USB_CH_HCCHAR_ChEna_Msk | USB_CH_HCCHAR_ChDis_Msk); /* wait until channel is halted */ for (timeout = 0U; timeout < 5000U; timeout++) { if (ptr_ch->HCINT & USB_CH_HCINT_ChHltd_Msk) { ptr_ch->HCINT = USB_CH_HCINTx_ALL; status = XMC_USBH_DRIVER_OK; } } } } } } } } return status; } /** * @return Frame number. * * \parDescription:
* Get current USB Frame Number. */ static uint16_t XMC_USBH_GetFrameNumber (void) { uint16_t status; if (XMC_USBH0_device.power_state == XMC_USBH_POWER_OFF) { status = 0U; } else { status = (uint16_t)((XMC_USBH0_device.global_register->HFNUM) & 0xFFFU); } return status; } /** * @param gintsts USB port interrupt status flag. * * \parDescription:
* USB host interrupt handler. It updates port and pipe state information based on different events * generated by the peripheral. It propagates the port events to the callback function registered by the user * during initialization. When a pipe transfer complete event is detected, it checks if any further data is available * to be transmitted on the same pipe and continues transmission until data is available. A pipe event is also propagated * to the user provided pipe event callback function. A transfer complete event will be propagated only when all the data * is transmitted for an OUT transaction. * */ void XMC_USBH_HandleIrq (uint32_t gintsts) { XMC_USBH0_pipe_t *ptr_pipe; USB0_CH_TypeDef *ptr_ch; uint32_t hprt, haint, hcint, pktcnt, mpsiz; uint32_t ch; uint8_t *ptr_data; uint32_t *dfifo; uint32_t grxsts, bcnt, dat, len, len_rest; /* Host port interrupt */ if ((gintsts & USB_GINTSTS_HOSTMODE_PrtInt_Msk) != 0U) { hprt = XMC_USBH0_device.global_register->HPRT; /* Clear port enable */ XMC_USBH0_device.global_register->HPRT = hprt & (uint32_t)(~USB_HPRT_PrtEna_Msk); if ((hprt & USB_HPRT_PrtConnDet_Msk) != 0U) { XMC_USBH0_device.global_register->HCFG = (0x200U | (USB_CH_HCFG_FSLSPCS(1) | USB_CH_HCFG_FSLSSUP(1))); /* Ignore connect under reset */ if (XMC_USBH0_device.port_reset_active == false) { XMC_USBH0_device.SignalPortEvent_cb(0U, XMC_USBH_EVENT_CONNECT); } } if ((hprt & USB_HPRT_PrtEnChng_Msk) != 0U) { /* If port enable changed */ if ((hprt & USB_HPRT_PrtEna_Msk) != 0U) { /* If device connected */ if (XMC_USBH0_device.port_reset_active == true) { XMC_USBH0_device.port_reset_active = false; XMC_USBH0_device.SignalPortEvent_cb(0U, XMC_USBH_EVENT_RESET); } } } } /* Disconnect interrupt */ if ((gintsts & USB_GINTSTS_HOSTMODE_DisconnInt_Msk) != 0U) { XMC_USBH0_device.global_register->GINTSTS_HOSTMODE = USB_GINTSTS_HOSTMODE_DisconnInt_Msk; /* Clear disconnect interrupt */ /* Ignore disconnect under reset */ if ( XMC_USBH0_device.port_reset_active == false) { ptr_ch = (USB0_CH_TypeDef *)(XMC_USBH0_device.host_channel_registers); ptr_pipe = (XMC_USBH0_pipe_t *)(pipe); for (ch = 0U; ch < USBH0_MAX_PIPE_NUM; ch++) { if (ptr_pipe->in_use != 0U) { ptr_pipe->in_use = 0U; ptr_ch->HCINT = USB_CH_HCINTx_ALL; /* Clear all interrupts */ ptr_ch->HCINTMSK = USB_CH_HCINT_ChHltd_Msk; /* Enable halt interrupt */ ptr_ch->HCCHAR |= (uint32_t)(USB_CH_HCCHAR_ChEna_Msk | USB_CH_HCCHAR_ChDis_Msk); /* Activate Halt */ XMC_USBH0_device.SignalPipeEvent_cb((XMC_USBH_EP_HANDLE)ptr_ch, XMC_USBH_EVENT_BUS_ERROR); } ptr_ch++; ptr_pipe++; } XMC_USBH0_device.SignalPortEvent_cb(0U, XMC_USBH_EVENT_DISCONNECT); } } /* Handle receive fifo not-empty interrupt */ if ((gintsts & USB_GINTSTS_HOSTMODE_RxFLvl_Msk) != 0U) { XMC_USBH0_device.global_register->GINTMSK_HOSTMODE &= (uint32_t)~USB_GINTMSK_HOSTMODE_RxFLvlMsk_Msk; grxsts = (XMC_USBH0_device.global_register->GRXSTSP_HOSTMODE); /* IN Data Packet received ? */ if ((uint32_t)((grxsts >> 17U) & 0x0FU) == (uint32_t)USB_GRXSTSR_HOSTMODE_PktSts_IN_DATA_PKT) { ch = (uint32_t)(grxsts & USB_GRXSTSR_DEVICEMODE_EPNum_Msk); bcnt = ((uint32_t)(grxsts & USB_GRXSTSR_DEVICEMODE_BCnt_Msk) >> USB_GRXSTSR_DEVICEMODE_BCnt_Pos); dfifo = (uint32_t *)XMC_USBH0_dfifo_ptr[ch]; ptr_data = pipe[ch].data + pipe[ch].num_transferred_total; len = bcnt / 4U; /* Received number of 32-bit data */ len_rest = bcnt & 3U; /* Number of bytes left */ /* Read data from fifo */ /* Read 32 bit sized data */ while (len != 0U) { #if defined __TASKING__/*tasking*/ *((__unaligned uint32_t *)ptr_data) = *dfifo; #else /* defined (__GNUC__) || defined (__CC_ARM) || defined (__ICCARM__)*/ *((__packed uint32_t *)ptr_data) = *dfifo; #endif ptr_data += 4U; len--; } /* Read 8 bit sized data */ if (len_rest != 0U) { #if defined __TASKING__/*tasking*/ dat = *((__unaligned uint32_t *)dfifo); #else /* defined (__GNUC__) || defined (__CC_ARM) || defined (__ICCARM__)*/ dat = *((__packed uint32_t *)dfifo); #endif while (len_rest != 0U) { *ptr_data = (uint8_t)dat; ptr_data++; dat >>= 8; len_rest--; } } pipe[ch].num_transferring += bcnt; pipe[ch].num_transferred_total += bcnt; } XMC_USBH0_device.global_register->GINTMSK_HOSTMODE |= (uint32_t)USB_GINTMSK_HOSTMODE_RxFLvlMsk_Msk; } /* Handle sof interrupt */ if ((gintsts & USB_GINTSTS_HOSTMODE_Sof_Msk) != 0U) { /* If start of frame interrupt */ XMC_USBH0_device.global_register->GINTSTS_HOSTMODE = USB_GINTSTS_HOSTMODE_Sof_Msk; /* Clear SOF interrupt */ ptr_pipe = (XMC_USBH0_pipe_t *)(pipe); for (ch = 0U; ch < USBH0_MAX_PIPE_NUM; ch++) { /* If interrupt transfer is active handle period (interval) */ if ((ptr_pipe->ep_type == (uint8_t)XMC_USBH_ENDPOINT_INTERRUPT) && (ptr_pipe->in_use == 1U)) { if (ptr_pipe->interval != 0U) { ptr_pipe->interval--; if (ptr_pipe->interval == 0U) { ptr_pipe->interval = ptr_pipe->interval_reload; ptr_pipe->interrupt_triggered = 1U; } } } ptr_pipe++; } } /* Handle host ctrl interrupt */ if ((gintsts & USB_GINTSTS_HOSTMODE_HChInt_Msk) != 0U) { haint = XMC_USBH0_device.global_register->HAINT; for (ch = 0U; ch < USBH0_MAX_PIPE_NUM; ch++) { /* Check for interrupt of all channels */ if ((haint & (uint32_t)((uint32_t)1U << ch)) != 0U) { haint &= (uint32_t)~((uint32_t)1U << ch); ptr_ch = (USB0_CH_TypeDef *)(XMC_USBH0_device.host_channel_registers) + ch; ptr_pipe = (XMC_USBH0_pipe_t *)(&pipe[ch]); /*Local variable for HCINT*/ dat = ptr_ch->HCINT; hcint = (uint32_t)(dat & ptr_ch->HCINTMSK); if ((hcint & USB_CH_HCINT_ChHltd_Msk) != 0U) { /* channel halted ? */ ptr_ch->HCINTMSK = 0U; /* disable all channel interrupts */ ptr_ch->HCINT = USB_CH_HCINTx_ALL; /* clear all interrupts */ ptr_pipe->transfer_active = 0U; /* set status transfer not active */ hcint = 0U; } if ((hcint & USB_CH_HCINT_XferCompl_Msk) != 0U) { /* data transfer finished ? */ ptr_ch->HCINT = USB_CH_HCINTx_ALL; /* clear all interrupts */ if ((ptr_ch->HCCHAR & USB_CH_HCCHAR_EPDir_Msk) == 0U) { /* endpoint OUT ? */ ptr_ch->HCINTMSK = 0U; /* disable all channel interrupts */ ptr_pipe->transfer_active = 0U; /* transfer not in progress */ ptr_pipe->num_transferred_total += ptr_pipe->num_transferring; /* admin OUT transfer status */ ptr_pipe->num_transferring = 0U; /* admin OUT transfer status */ if (ptr_pipe->num_transferred_total == ptr_pipe->num) { /* all bytes transferred ? */ ptr_pipe->in_use = 0U; /* release pipe */ ptr_pipe->event = (uint8_t)XMC_USBH_EVENT_TRANSFER_COMPLETE; /* prepare event notification */ } hcint = 0U; } if ((ptr_ch->HCCHAR & USB_CH_HCCHAR_EPDir_Msk) != 0U) { /* endpoint IN ? */ ptr_pipe->in_use = 0U; /* release pipe */ ptr_pipe->event = (uint8_t)XMC_USBH_EVENT_TRANSFER_COMPLETE; /* prepare event notification */ XMC_lTriggerHaltChannel(ptr_ch); /* trigger channel halt */ } } if ((hcint & USB_CH_HCINTMSK_AckMsk_Msk) != 0U) { /* ACK received ? */ ptr_ch->HCINT = USB_CH_HCINTMSK_AckMsk_Msk; /* clear ACK interrupt */ is_nack[ch] = false; if ((ptr_ch->HCCHAR & USB_CH_HCCHAR_EPDir_Msk) != 0U) { /* endpoint IN ? */ if ((ptr_pipe->num != ptr_pipe->num_transferred_total) && /* if all data was not transferred */ (ptr_pipe->num_transferring != 0U) && /* if zero-length packet was not received */ ((ptr_pipe->num_transferred_total%ptr_pipe->ep_max_packet_size) == 0U)){ /* if short packet was not received */ ptr_ch->HCCHAR |= (uint32_t)USB_CH_HCCHAR_ChEna_Msk; /* trigger next transfer */ } } else { /* endpoint OUT */ XMC_lTriggerHaltChannel(ptr_ch); /* trigger channel halt */ } hcint = 0U; } /*local variable for HCCHAR*/ dat = ptr_ch->HCCHAR; if (((hcint & (USB_CH_HCINTMSK_StallMsk_Msk | /* STALL */ USB_CH_HCINTMSK_NakMsk_Msk | /* or NAK */ USB_CH_HCINTx_ERRORS )) != 0U) && /* or transaction error */ ((dat & USB_CH_HCCHAR_EPDir_Msk) == 0U)) { /* and endpoint OUT */ pktcnt = (uint32_t)((ptr_ch->HCTSIZ_BUFFERMODE & USB_CH_HCTSIZ_BUFFERMODE_PktCnt_Msk) /* administrate OUT transfer status */ >> USB_CH_HCTSIZ_BUFFERMODE_PktCnt_Pos); mpsiz = (ptr_ch->HCCHAR ) & 0x000007FFU; if ((ptr_pipe->num_transferring >= mpsiz) && (pktcnt > 0U)) { ptr_pipe->num_transferred_total += (uint32_t)(ptr_pipe->num_transferring - (mpsiz * pktcnt)); } ptr_pipe->num_transferring = 0U; } if ((hcint & USB_CH_HCINTMSK_NakMsk_Msk)!=0U) { /* if NAK */ is_nack[ch] = true; ptr_pipe->event |= (uint8_t)XMC_USBH_EVENT_HANDSHAKE_NAK; ptr_ch->HCINT = USB_CH_HCINTMSK_NakMsk_Msk; /* clear NAK interrupt */ if ((ptr_ch->HCCHAR & USB_CH_HCCHAR_EPDir_Msk) != 0U) { /* endpoint IN ? */ if (ptr_pipe->ep_type == (uint8_t)XMC_USBH_ENDPOINT_INTERRUPT) { /* is endpoint of type interrupt ? */ XMC_lTriggerHaltChannel(ptr_ch); /* trigger channel halt (after halted will be restarted in next sof) */ } else { /* is endpoint not of type interrupt ?*/ ptr_ch->HCCHAR |= (uint32_t)USB_CH_HCCHAR_ChEna_Msk; /* trigger next transfer */ } } else { /* If endpoint OUT */ /* endpoint OUT ? */ XMC_lTriggerHaltChannel(ptr_ch); /* trigger channel halt */ } hcint = 0U; } if ((hcint & USB_CH_HCINTMSK_StallMsk_Msk) != 0U) { /* if STALL */ /*Reset the packet data toggle*/ ptr_ch->HCINT = USB_CH_HCINTMSK_StallMsk_Msk; /* clear STALL interrupt */ ptr_pipe->in_use = 0U; /* release pipe */ ptr_pipe->packet &= (uint32_t)(~XMC_USBH_PACKET_DATA_Msk); ptr_pipe->packet |= (uint32_t)XMC_USBH_PACKET_DATA0; ptr_pipe->event = (uint8_t)XMC_USBH_EVENT_HANDSHAKE_STALL; /* prepare event notification */ XMC_lTriggerHaltChannel(ptr_ch); /* trigger channel halt */ hcint = 0U; } if ((hcint & USB_CH_HCINTx_ERRORS) != 0U) { /* if transaction error */ ptr_ch->HCINT = USB_CH_HCINTx_ERRORS; /* clear all error interrupt */ ptr_pipe->in_use = 0U; /* release pipe */ ptr_pipe->event = (uint8_t)XMC_USBH_EVENT_BUS_ERROR; /* prepare event notification */ XMC_lTriggerHaltChannel(ptr_ch); /* trigger channel halt */ hcint = 0U; } if ((ptr_pipe->transfer_active == 0U) && (ptr_pipe->in_use == 0U) && (ptr_pipe->event != 0U)) { XMC_USBH0_device.SignalPipeEvent_cb((XMC_USBH_EP_HANDLE)ptr_ch, (uint32_t)ptr_pipe->event); ptr_pipe->event = 0U; } } } } /*Check if remote wakeup event detected*/ if ((gintsts & USB_GINTSTS_HOSTMODE_WkUpInt_Msk) != 0U) { XMC_USBH0_device.global_register->GINTSTS_HOSTMODE = USB_GINTSTS_HOSTMODE_WkUpInt_Msk; /* Clear wakeup interrupt */ /*Recover PHY clock*/ XMC_USBH0_device.global_register->PCGCCTL = XMC_USBH_PHY_CLK_UNGATE; /*Callback function execution*/ XMC_USBH0_device.SignalPortEvent_cb(0U, XMC_USBH_EVENT_REMOTE_WAKEUP); } /* Handle restarts of unfinished transfers (due to NAK or ACK) */ ptr_pipe = (XMC_USBH0_pipe_t *)(pipe); for (ch = 0U; ch < USBH0_MAX_PIPE_NUM; ch++) { if ((ptr_pipe->in_use == 1U) && (ptr_pipe->transfer_active == 0U)) { /* Restart periodic transfer if not in progress and interval expired */ if (ptr_pipe->ep_type != (uint8_t)XMC_USBH_ENDPOINT_INTERRUPT) { /*Data toggle if NACK not received*/ if (!is_nack[ch]) { switch (ptr_pipe->packet & (uint32_t)XMC_USBH_PACKET_DATA_Msk) { case XMC_USBH_PACKET_DATA0: ptr_pipe->packet &= (uint32_t)~XMC_USBH_PACKET_DATA_Msk; ptr_pipe->packet |= (uint32_t)XMC_USBH_PACKET_DATA1; break; case XMC_USBH_PACKET_DATA1: ptr_pipe->packet &= (uint32_t)~XMC_USBH_PACKET_DATA_Msk; ptr_pipe->packet |= (uint32_t)XMC_USBH_PACKET_DATA0; break; default: break; } } else { is_nack[ch] = false; } } if (((ptr_pipe->ep_type == (uint8_t)XMC_USBH_ENDPOINT_INTERRUPT)&&(ptr_pipe->interrupt_triggered == 1U))|| (ptr_pipe->ep_type != (uint8_t)XMC_USBH_ENDPOINT_INTERRUPT)) { ptr_pipe->interrupt_triggered = 0U; ptr_pipe->transfer_active = 1U; (void)XMC_lStartTransfer (ptr_pipe, (((USB0_CH_TypeDef *)(XMC_USBH0_device.host_channel_registers)) + ch)); } } ptr_pipe++; } } /*Function provides host mode interrupt status*/ uint32_t XMC_USBH_GetInterruptStatus(void) { return XMC_USBH0_device.global_register->GINTSTS_HOSTMODE; } /*Function selects the port pin used as DRIVEVBUS*/ void XMC_USBH_Select_VBUS(XMC_GPIO_PORT_t* port, uint32_t pin) { VBUS_port = port; VBUS_pin = pin; /*Configure the port pin alternate function*/ XMC_GPIO_SetMode(VBUS_port, (uint8_t)VBUS_pin, XMC_GPIO_MODE_OUTPUT_PUSH_PULL_ALT1); } /*Function asserts the remote wakeup request by device by clearing the resume bit*/ void XMC_USBH_TurnOffResumeBit(void) { uint32_t hprt; /*Clear resume bit*/ hprt = XMC_USBH0_device.global_register->HPRT; hprt &= (uint32_t)~(USB_HPRT_PrtEna_Msk); hprt &= (uint32_t)~((uint32_t)USB_HPRT_PrtRes_Msk); XMC_USBH0_device.global_register->HPRT = hprt; } /*USB host driver assembling all the implementation into a single CMSIS compliant structure type*/ XMC_USBH_DRIVER_t Driver_USBH0 = { XMC_USBH_GetVersion, XMC_USBH_GetCapabilities, XMC_USBH_Initialize, XMC_USBH_Uninitialize, XMC_USBH_PowerControl, XMC_USBH_PortVbusOnOff, XMC_USBH_PortReset, XMC_USBH_PortSuspend, XMC_USBH_PortResume, XMC_USBH_PortGetState, XMC_USBH_PipeCreate, XMC_USBH_PipeModify, XMC_USBH_PipeDelete, XMC_USBH_PipeReset, XMC_USBH_PipeTransfer, XMC_USBH_PipeTransferGetResult, XMC_USBH_PipeTransferAbort, XMC_USBH_GetFrameNumber }; /*Weak definition of delay function*/ __WEAK uint8_t XMC_USBH_osDelay(uint32_t MS) { /*A precise time delay implementation for this function has to be provided*/ while (1) { /*Wait*/ } } #endif