/** * @file xmc_usbd.c * @date 2015-06-20 * * @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 * -------------- * * 2015-02-16: * - Initial Version.
* 2015-03-18: * - Updated the XMC_USBD_EndpointStall() to fix issue on USB clear stall.
* - Updated the XMC_USBD_EndpointConfigure() to fix issue in EP0 configuration.
* - Updated the XMC_USBD_IRQHandler()(Removed the DAVE_CE check on SOF event).
* 2015-06-20: * - Removed GetDriverVersion API.
* - Updated the XMC_USBD_IsEnumDone() API.
* - Updated the copy right in the file header.
* - Updated the XMC_USBD_Disable() API to gate the clock after programming the SCU registers.
* * @endcond * */ /******************************************************************************* * HEADER FILES *******************************************************************************/ #include #if defined(USB0) /**< macro to check the maximum number of endpoints used*/ #define XMC_USBD_CHECK_INPUT_MAX_NUM_EPS(usbd_max_num_eps) \ ((usbd_max_num_eps == XMC_USBD_MAX_NUM_EPS_1 ) || \ (usbd_max_num_eps == XMC_USBD_MAX_NUM_EPS_2 ) || \ (usbd_max_num_eps == XMC_USBD_MAX_NUM_EPS_3 ) || \ (usbd_max_num_eps == XMC_USBD_MAX_NUM_EPS_4 ) || \ (usbd_max_num_eps == XMC_USBD_MAX_NUM_EPS_5 ) || \ (usbd_max_num_eps == XMC_USBD_MAX_NUM_EPS_6 ) || \ (usbd_max_num_eps == XMC_USBD_MAX_NUM_EPS_7 )) /******************************************************************************* *GLOBAL DATA *******************************************************************************/ /* * Endpoint Out Fifo Size */ uint32_t XMC_USBD_EP_OUT_BUFFERSIZE[7] = {0U,0U,0U,0U,0U,0U,0U}; /* * Endpoint In Fifo Size */ uint32_t XMC_USBD_EP_IN_BUFFERSIZE[7] = {0U,0U,0U,0U,0U,0U,0U}; /* * Device definition */ XMC_USBD_DEVICE_t xmc_device; #ifdef __GNUC__ /*GCC*/ /* * Endpoint Out Fifo */ static __attribute__((aligned(4))) uint8_t XMC_USBD_EP_OUT_BUFFER[7][256] __attribute__((section("USB_RAM"))); /* * Endpoint In Fifo */ static __attribute__((aligned(4))) uint8_t XMC_USBD_EP_IN_BUFFER[7][256] __attribute__((section("USB_RAM"))); #endif #if defined(__ICCARM__) #pragma data_alignment=4 /* * Endpoint Out Fifo */ static uint8_t XMC_USBD_EP_OUT_BUFFER[7][256] @ ".dram"; /* * Endpoint In Fifo */ #pragma data_alignment=4 static uint8_t XMC_USBD_EP_IN_BUFFER[7][256] @ ".dram"; #endif #if defined(__CC_ARM) /* * Endpoint Out Fifo */ static __attribute__((aligned(4))) uint8_t XMC_USBD_EP_OUT_BUFFER[7][256] __attribute__((section ("RW_IRAM1"))); /* * Endpoint In Fifo */ static __attribute__((aligned(4))) uint8_t XMC_USBD_EP_IN_BUFFER[7][256] __attribute__((section ("RW_IRAM1"))); #endif XMC_USBD_t *usbd_init; /******************************************************************************* *LOCAL ROUTINES *******************************************************************************/ /*Local routines prototypes*/ uint8_t XMC_USBD_lDeviceActive(const XMC_USBD_t *const obj); static void XMC_USBD_lReadFifo(const uint32_t ep_num,const uint32_t byte_count); static uint32_t XMC_USBD_lWriteFifo(XMC_USBD_EP_t *ep); static void XMC_USBD_lFlushTXFifo(const uint8_t fifo_num); static void XMC_USBD_lFlushRXFifo(void); static uint8_t XMC_USBD_lAssignTXFifo(void); static void XMC_USBD_lStartReadXfer(XMC_USBD_EP_t *const ep); static void XMC_USBD_lStartWriteXfer(XMC_USBD_EP_t *const ep); static void XMC_USBD_lHandleEnumDone(void); static void XMC_USBD_lHandleOEPInt(const XMC_USBD_t *const obj); static void XMC_USBD_lHandleRxFLvl(void); static void XMC_USBD_lHandleIEPInt(const XMC_USBD_t *const obj); static void XMC_USBD_lUnassignFifo(const uint8_t fifo_nr); static void XMC_USBD_lHandleUSBReset(const XMC_USBD_t *const obj); static void XMC_USBD_lHandleOTGInt(void); static void XMC_USBD_lClearEventOTG(uint32_t event); /** * The device driver */ const XMC_USBD_DRIVER_t Driver_USBD0 = { .GetCapabilities = XMC_USBD_GetCapabilities, .Initialize = XMC_USBD_Init, .Uninitialize = XMC_USBD_Uninitialize, .DeviceConnect = XMC_USBD_DeviceConnect, .DeviceDisconnect = XMC_USBD_DeviceDisconnect, .DeviceGetState = XMC_USBD_DeviceGetState, .DeviceSetAddress = XMC_USBD_DeviceSetAddress, .EndpointConfigure = XMC_USBD_EndpointConfigure, .EndpointUnconfigure = XMC_USBD_EndpointUnconfigure, .EndpointStall = XMC_USBD_EndpointStall, .EndpointReadStart = XMC_USBD_EndpointReadStart, .EndpointRead = XMC_USBD_EndpointRead, .EndpointWrite = XMC_USBD_EndpointWrite, .EndpointAbort = XMC_USBD_EndpointAbort, .GetFrameNumber = XMC_USBD_GetFrameNumber, .IsEnumDone = XMC_USBD_IsEnumDone }; /** * @brief Checks if device is active * * Therefore the endpoint inInUse flag are checked and if one endpoint is in use, 1 is returned, * else 0 is returned. * @return 1 if an endpoint is active else 0 */ uint8_t XMC_USBD_lDeviceActive(const XMC_USBD_t *const obj) { uint8_t i; uint8_t result = 0U; for (i = 0U; i < (uint8_t)obj->usbd_max_num_eps; i++) { if (xmc_device.ep[i].inInUse || xmc_device.ep[i].outInUse) { result = 1U; } } return result; } /** * @brief Read data from the rx fifo * * The data from the fifo is copied in to the buffer specified by @ref xfer_buffer and * the transfer values get updated. If the endpoint is disabled or the buffer not existent * the function exits. * * @arg ep_num the endpoint to read for * @arg byte_count the byte count to read */ static void XMC_USBD_lReadFifo(const uint32_t ep_num,const uint32_t byte_count) { XMC_USBD_EP_t * ep = &xmc_device.ep[ep_num]; uint32_t word_count; uint32_t temp_data; uint32_t temp_word_count; volatile uint32_t *fifo = xmc_device.fifo[0U]; uint32_t i; depctl_data_t data; data.d32 = xmc_device.endpoint_out_register[ep_num]->doepctl; word_count = (byte_count >> 2U ); temp_word_count = (word_count << 2U); /* Check if ep is enabled and has buffer */ if (!data.b.usbactep) { /*Do Nothing*/ } else if (ep->xferBuffer == NULL) { /*Do Nothing*/ } else { /* store the data */ for (i = 0U;i < word_count; i++) { *(((uint32_t*)ep->xferBuffer)+i) = *fifo; } /* space is not devidable by 4 */ if (byte_count!=temp_word_count) { temp_data = *fifo; for (i = 0U;(temp_word_count + i) < byte_count;i++) { ep->xferBuffer[(word_count << 2)+i] = (uint8_t)((temp_data & ((uint32_t)0xFFU << (i * 8U))) >> (i * 8U)); } } /* save the amount of data */ ep->xferCount += byte_count; ep->xferBuffer += byte_count; } } /** * @brief Write data to an endpoint fifo * * The data from the @ref xfer_buffer gets copied in to the tx fifo of the endpoint until the buffer has been read *completely or the tx fifo is full. The transfer values are not updated. * * @arg[in] ep the endpoint to use * @return the number of bytes written to the fifo */ static uint32_t XMC_USBD_lWriteFifo(XMC_USBD_EP_t *const ep) { dtxfsts_data_t freeSpace; volatile uint32_t *fifo; uint32_t byte_count; uint32_t word_count; uint32_t result; uint32_t i; fifo = xmc_device.fifo[ep->address_u.address_st.number]; /* fifo */ freeSpace.d32 = xmc_device.endpoint_in_register[ep->address_u.address_st.number]->dtxfsts; /* calculate the length and the amount of dwords to copy based on the fifo status */ byte_count = ep->xferLength - ep->xferCount; if (!byte_count) { result = 0U; } else { /* add the unaligned bytes to the word count to compare with the fifo space */ word_count = ((uint32_t)byte_count + 3U) >> 2U; if (word_count > (uint32_t)freeSpace.b.txfspcavail ) { word_count = (uint32_t)freeSpace.b.txfspcavail; byte_count = (uint32_t)word_count << (uint32_t)2U; } /* copy data dword wise */ for (i = 0U; i < word_count;ep->xferBuffer+= 4U) { *fifo = *(uint32_t*)ep->xferBuffer; i++; } result=byte_count; } return result; } /** * @brief Flush a tx fifo * * @param[in] fifo_num Fifo number to flush * * @note Use 0x10 as parameter to flush all tx fifos. */ static void XMC_USBD_lFlushTXFifo(const uint8_t fifo_num) { volatile grstctl_t data; uint32_t count; data.d32 = 0U; /*flush fifo */ data.b.txfflsh = 1U; data.b.txfnum = fifo_num; xmc_device.global_register->grstctl = data.d32; for (count = 0U;count < 1000U; count++){} do { data.d32 = xmc_device.global_register->grstctl; } while (data.b.txfflsh); count = 0U; while (count++ < 1000U) { /* wait 3 phy clocks */ } } /** * @brief Flush the rx fifo */ static void XMC_USBD_lFlushRXFifo(void) { volatile grstctl_t data; uint32_t count; data.d32 = 0U; data.b.rxfflsh = 1U; /* flush FIFO */ xmc_device.global_register->grstctl = data.d32; do { for (count = 0U; count < 1000U; count++){} data.d32 = xmc_device.global_register->grstctl; } while (data.b.rxfflsh); count = 0U; while (count++ < 1000U) { /* wait 3 phy clocks */ } } /* * Support Functions */ /** * @brief Assign a free tx fifo * * A free tx fifo will be searched and the number will be returned. * * @return Fifo number for a free fifo */ static uint8_t XMC_USBD_lAssignTXFifo(void) { uint16_t mask = 1U; uint8_t i = 0U; uint8_t result = 0U; while( (i < (uint8_t)XMC_USBD_NUM_TX_FIFOS)&&((xmc_device.txfifomsk & mask) != 0U)) { mask = (uint16_t)(mask << 1U); i++; } if ((xmc_device.txfifomsk & mask) == 0U) { xmc_device.txfifomsk |= mask; result=i; } return result; } /** * @brief Free a tx fifo * * Mark an used tx fifo as free. * @param[in] fifo_nr Fifo number to free */ static void XMC_USBD_lUnassignFifo(const uint8_t fifo_nr) { xmc_device.txfifomsk = (uint16_t)((uint32_t)xmc_device.txfifomsk & (uint32_t)(~((uint32_t)((uint32_t)1U << fifo_nr)))); } /** * @brief Start a transfer for an out endpoint * * Based on the transfer values of the endpoint, the out endpoint registers will be programmed * to start a new out transfer. * * @note No checking of the transfer values are done in this function. Be sure, * that the transfer values are reasonable (e.g. buffer size is not exceeded). * * @param[in] ep Endpoint to start the transfer */ static void XMC_USBD_lStartReadXfer(XMC_USBD_EP_t *const ep) { deptsiz_data_t data; depctl_data_t epctl; data.d32 = 0U; if ((ep->xferTotal - ep->xferLength) > ep->maxTransferSize) { ep->xferLength += ep->maxTransferSize; } else { ep->xferLength = ep->xferTotal; } if (ep->address_u.address_st.number == 0U) { /* Setup the endpoint to receive 3 setup packages and one normal package.*/ /* Cast the data pointer to use only one variable */ deptsiz0_data_t *ep0_data = (deptsiz0_data_t*)&data; ep0_data->b.pktcnt = 0x1U; ep0_data->b.supcnt = 0x3U; ep0_data->b.xfersize = (uint8_t)ep->xferTotal; } else { /* If requested length is zero, just receive one zero length packet */ if (ep->xferLength == 0U) { data.b.xfersize = 0U; data.b.pktcnt = 1U; } else { /* setup endpoint to recive a amount of packages by given size */ data.b.pktcnt = (uint16_t)(((ep->xferLength - ep->xferCount) + (ep->maxPacketSize -(uint32_t)1U))/ep->maxPacketSize); data.b.xfersize =(uint32_t)(ep->xferLength - ep->xferCount); } } if(usbd_init->usbd_transfer_mode == XMC_USBD_USE_DMA) { /* Programm dma address if needed */ xmc_device.endpoint_out_register[ep->address_u.address_st.number]->doepdma = (uint32_t)(ep->xferBuffer); } /* setup endpoint size and enable endpoint */ xmc_device.endpoint_out_register[ep->address_u.address_st.number]->doeptsiz = data.d32; epctl.d32 = xmc_device.endpoint_out_register[ep->address_u.address_st.number]->doepctl; epctl.b.cnak = 1U; epctl.b.epena = 1U; xmc_device.endpoint_out_register[ep->address_u.address_st.number]->doepctl = epctl.d32; } /** * @brief Start a new in transfer * * Based on the transfer values of the endpoint the in endpoint registers will be programmed * to start a new in transfer * * @param[in] ep Endpoint to start the transfer */ static void XMC_USBD_lStartWriteXfer(XMC_USBD_EP_t *const ep) { deptsiz_data_t size; depctl_data_t ctl; size.d32 = 0U; ctl.d32 = xmc_device.endpoint_in_register[ep->address_u.address_st.number]->diepctl; if ((ep->xferTotal - ep->xferLength) < ep->maxTransferSize) { ep->xferLength = ep->xferTotal; } else { ep->xferLength += ep->maxTransferSize; } if (ep->xferLength == 0U) { size.b.xfersize = 0U; size.b.pktcnt = 1U; } else { if (ep->address_u.address_st.number == 0U) { size.b.pktcnt = 1U; /* ep->maxXferSize equals maxPacketSize */ size.b.xfersize = (uint32_t)(ep->xferLength - ep->xferCount); } else { size.b.xfersize =(uint32_t)(ep->xferLength - ep->xferCount); size.b.pktcnt = (uint16_t)(((uint16_t)(ep->xferLength - ep->xferCount) + (uint16_t)((uint16_t)ep->maxPacketSize - 1U))/ ep->maxPacketSize); } if(usbd_init->usbd_transfer_mode == XMC_USBD_USE_DMA) { /* Program dma*/ xmc_device.endpoint_in_register[ep->address_u.address_st.number]->diepdma = (uint32_t)ep->xferBuffer; } if(usbd_init->usbd_transfer_mode == XMC_USBD_USE_FIFO) { /* enable fifo empty interrupt */ xmc_device.device_register->dtknqr4_fifoemptymsk |= (uint32_t)((uint32_t)1U << (uint8_t)ep->address_u.address_st.number); } } /* Program size of transfer and enable endpoint */ xmc_device.endpoint_in_register[ep->address_u.address_st.number]->dieptsiz = size.d32; ctl.b.epena = 1U; ctl.b.cnak = 1U; xmc_device.endpoint_in_register[ep->address_u.address_st.number]->diepctl = ctl.d32; } /** * @brief Handles the USBD reset interrupt * * When ever the host sets the bus into reset condition the usb otg_core generates * an interrupt, which is handled by this function. It resets the complete otg_core * into the default state. */ static void XMC_USBD_lHandleUSBReset(const XMC_USBD_t *const obj) { uint32_t i; depctl_data_t epctl; dctl_data_t dctl; fifosize_data_t gnptxfsiz; daint_data_t daint; dcfg_data_t dcfg; /* Clear the Remote Wakeup Signaling */ dctl.d32 = xmc_device.device_register->dctl; dctl.b.rmtwkupsig = 1U; xmc_device.device_register->dctl = dctl.d32; /* enable naks for all eps */ for (i = 0U;i < (uint8_t)XMC_USBD_NUM_EPS;i++) { epctl.d32 = xmc_device.endpoint_out_register[i]->doepctl; epctl.b.snak = 1U; epctl.b.stall = 0U; xmc_device.endpoint_out_register[i]->doepctl = epctl.d32; } /* Configure fifos */ /* Calculate the size of the rx fifo */ xmc_device.global_register->grxfsiz = 64U; /* Calculate the size of the tx fifo for ep 0 */ gnptxfsiz.d32 = 0U; gnptxfsiz.b.depth = 16U; gnptxfsiz.b.startaddr = 64U; xmc_device.global_register->gnptxfsiz = gnptxfsiz.d32; /* calculate the size for the rest */ for (i = 1U;i < (uint8_t)XMC_USBD_NUM_TX_FIFOS;i++) { xmc_device.global_register->dtxfsiz[i- 1U] = (uint32_t)(((256U + (i*(64U)))/4U) | ((uint32_t)16U << 16U)); } /* flush the fifos for proper operation */ XMC_USBD_lFlushTXFifo(0x10U); /* 0x10 == all fifos, see doc */ XMC_USBD_lFlushTXFifo(0x0U); XMC_USBD_lFlushRXFifo(); /* Flush learning queue not needed due to fifo config */ /* enable ep0 interrupts */ daint.d32 = 0U; daint.b.inep0 = 1U; daint.b.outep0 = 1U; xmc_device.device_register->daintmsk = daint.d32; /* enable endpoint interrupts */ /* out ep interrupts */ XMC_USBD_EnableEventOUTEP(((uint32_t)XMC_USBD_EVENT_OUT_EP_TX_COMPLET | (uint32_t)XMC_USBD_EVENT_OUT_EP_DISABLED | (uint32_t)XMC_USBD_EVENT_OUT_EP_SETUP | (uint32_t)XMC_USBD_EVENT_OUT_EP_AHB_ERROR)); /*in ep interrupts */ XMC_USBD_EnableEventINEP(((uint32_t)XMC_USBD_EVENT_IN_EP_TX_COMPLET | (uint32_t)XMC_USBD_EVENT_IN_EP_DISABLED | (uint32_t)XMC_USBD_EVENT_IN_EP_AHB_ERROR | (uint32_t)XMC_USBD_EVENT_IN_EP_TIMEOUT)); /* Clear device Address */ dcfg.d32 = xmc_device.device_register->dcfg; dcfg.b.devaddr = 0U; xmc_device.device_register->dcfg = dcfg.d32; if(obj->usbd_transfer_mode == XMC_USBD_USE_FIFO) { /* Clear Empty interrupt */ xmc_device.device_register->dtknqr4_fifoemptymsk = 0U; } xmc_device.ep[0U].outInUse = 0U; xmc_device.ep[0U].inInUse = 0U; xmc_device.DeviceEvent_cb(XMC_USBD_EVENT_RESET); /* clear reset intr */ XMC_USBD_ClearEvent(XMC_USBD_EVENT_RESET); } /** * @brief Handle OTG Interrupt * * It detects especially connect and disconnect events. */ static void XMC_USBD_lHandleOTGInt(void) { gotgint_data_t data; data.d32 = xmc_device.global_register->gotgint; if (data.b.sesenddet) { xmc_device.IsPowered = 0U; xmc_device.DeviceEvent_cb(XMC_USBD_EVENT_POWER_OFF); } XMC_USBD_lClearEventOTG(data.d32); } /** * @brief Interrupt handler for device enumeration done. * * Handles the enumeration done from dwc_otg, when the host has enumerated the device. */ static void XMC_USBD_lHandleEnumDone(void) { /* Normaly we need to check dctl * We are always fullspeed, so max it up. */ depctl_data_t epctl; gusbcfg_data_t gusbcfg; epctl.d32=xmc_device.endpoint_in_register[0U]->diepctl; epctl.b.mps = 0x00U; /* 64 Byte, this is also automatically set for out ep */ xmc_device.endpoint_in_register[0U]->diepctl = epctl.d32; /* update device connected flag */ xmc_device.IsConnected = 1U; xmc_device.IsPowered = 1U; xmc_device.DeviceEvent_cb(XMC_USBD_EVENT_CONNECT); /* Set Trim */ gusbcfg.d32 = xmc_device.global_register->gusbcfg; gusbcfg.b.usbtrdtim = 9U; /* default value for LS/FS */ xmc_device.global_register->gusbcfg = gusbcfg.d32; /* clear interrupt */ XMC_USBD_ClearEvent(XMC_USBD_EVENT_ENUMDONE); } /** * @brief Handles all interrupts for all out endpoints * * The interrupt handler first checks, which endpoint has caused the interrupt and then * determines, which interrupt should be handled. */ static void XMC_USBD_lHandleOEPInt(const XMC_USBD_t *const obj) { daint_data_t daint; daint_data_t daintmsk; doepmsk_data_t doepmsk; doepint_data_t doepint; deptsiz_data_t doeptsiz; XMC_USBD_EP_t *ep; uint16_t temp; uint16_t temp1; uint16_t mask; uint8_t ep_num; daint.d32 = xmc_device.device_register->daint; daintmsk.d32 = xmc_device.device_register->daintmsk; doepmsk.d32 = xmc_device.device_register->doepmsk; mask = daint.ep.out & daintmsk.ep.out; ep_num = 0U; doeptsiz.d32 = 0U; while ((uint16_t)mask >> ep_num) { temp1 = (mask >> (uint16_t)ep_num); temp = temp1 & 0x1U; if (temp) { /* load register data for endpoint */ ep = &xmc_device.ep[ep_num]; doepint.d32 = xmc_device.endpoint_out_register[ep_num]->doepint & doepmsk.d32; if(obj->usbd_transfer_mode == XMC_USBD_USE_DMA) { doeptsiz.d32 = xmc_device.endpoint_out_register[ep_num]->doeptsiz; } /* Setup Phase Complete */ if (doepint.b.setup) { /* ep0 not stalled any more */ ep->isStalled = 0U; if(obj->usbd_transfer_mode == XMC_USBD_USE_DMA) { /* calculate size for setup packet */ ep->outBytesAvailable = (uint32_t)(((uint32_t)XMC_USBD_SETUP_COUNT - (uint32_t)((deptsiz0_data_t*)&doeptsiz)->b.supcnt)*(uint32_t)XMC_USBD_SETUP_SIZE); } if(obj->usbd_transfer_mode == XMC_USBD_USE_FIFO) { ep->outBytesAvailable += ep->xferCount; } ep->outInUse = 0U; xmc_device.EndpointEvent_cb(0U,XMC_USBD_EP_EVENT_SETUP); /* signal endpoint event */ /* clear the interrupt */ XMC_USBD_ClearEventOUTEP((uint32_t)XMC_USBD_EVENT_OUT_EP_SETUP,ep_num); } if (doepint.b.xfercompl) { if(obj->usbd_transfer_mode == XMC_USBD_USE_DMA) { uint32_t bytes = (ep->xferLength - ep->xferCount) - doeptsiz.b.xfersize; ep->xferCount += bytes; ep->xferBuffer += bytes; } if (ep->xferTotal == ep->xferLength) { ep->outBytesAvailable = ep->xferCount; ep->outInUse = 0U; xmc_device.EndpointEvent_cb(ep_num,XMC_USBD_EP_EVENT_OUT); } else { XMC_USBD_lStartReadXfer(ep); } } XMC_USBD_ClearEventOUTEP(doepint.d32,ep_num); } ep_num++; } /* clear interrupt */ XMC_USBD_ClearEvent(XMC_USBD_EVENT_OUTEP); } /** * @brief Handles all interrupts for all in endpoints * * The interrupt handler first checks, which endpoint has caused the interrupt and then * determines, which interrupt should be handled. */ static void XMC_USBD_lHandleIEPInt(const XMC_USBD_t *const obj) { XMC_USBD_EP_t *ep; daint_data_t daint; diepmsk_data_t diepmsk; diepint_data_t diepint; deptsiz_data_t dieptsiz; uint16_t temp; uint16_t temp1; uint16_t mask; uint8_t ep_num; uint32_t inepint; daint.d32 = xmc_device.device_register->daint; diepmsk.d32 = xmc_device.device_register->diepmsk; dieptsiz.d32 = 0U; mask = daint.ep.in; ep_num = 0U; while ((uint16_t)mask >> ep_num) { temp1 = ((uint16_t)mask >> (uint16_t)ep_num); temp = (uint16_t)temp1 & (uint16_t)0x1U; if ((uint16_t)temp) { ep = &xmc_device.ep[ep_num]; inepint = (uint32_t)xmc_device.endpoint_in_register[ep_num]->diepint; diepint.d32 = inepint & ((((uint32_t)((uint32_t)xmc_device.device_register->dtknqr4_fifoemptymsk >> ep->address_u.address_st.number) & 0x1U) << 7U) | (uint32_t)diepmsk.d32); if(obj->usbd_transfer_mode == XMC_USBD_USE_DMA) { dieptsiz.d32 = xmc_device.endpoint_in_register[ep_num]->dieptsiz; } if(obj->usbd_transfer_mode == XMC_USBD_USE_FIFO) { if (diepint.b.emptyintr) { uint32_t bytes; bytes = XMC_USBD_lWriteFifo(ep); ep->xferCount += bytes; ep->xferBuffer += bytes; } } if (diepint.b.xfercompl) { if(obj->usbd_transfer_mode == XMC_USBD_USE_DMA) { /* update xfer values */ if ((dieptsiz.b.pktcnt == 0U) && (dieptsiz.b.xfersize == 0U)) { uint32_t Bytes = ep->xferLength - ep->xferCount; ep->xferCount += Bytes; ep->xferBuffer += Bytes; } } if (ep->xferTotal==ep->xferLength) { ep->inInUse = 0U; if(obj->usbd_transfer_mode == XMC_USBD_USE_FIFO) { /* mask fifo empty interrupt */ xmc_device.device_register->dtknqr4_fifoemptymsk = (uint32_t)(xmc_device.device_register->dtknqr4_fifoemptymsk & ~(((uint32_t)1U << ep_num))); } xmc_device.EndpointEvent_cb(0x80U | ep_num,XMC_USBD_EP_EVENT_IN); } else { /* start next step of transfer */ XMC_USBD_lStartWriteXfer(ep); } } XMC_USBD_ClearEventINEP((uint32_t)diepint.d32,ep_num); } ep_num++; } XMC_USBD_ClearEvent(XMC_USBD_EVENT_INEP); } /** * @brief RX Fifo interrupt handler * * This function handles the interrupt, when the rx fifo is not empty anymore. */ static void XMC_USBD_lHandleRxFLvl(void) { device_grxsts_data_t data; data.d32 = xmc_device.global_register->grxstsp; switch (data.b.pktsts) { case XMC_USBD_GRXSTS_PKTSTS_GOUTNAK: break; case XMC_USBD_GRXSTS_PKTSTS_OUTCMPL: break; case XMC_USBD_GRXSTS_PKTSTS_OUTDATA: XMC_USBD_lReadFifo((uint32_t)data.b.epnum,(uint32_t)data.b.bcnt); break; case XMC_USBD_GRXSTS_PKTSTS_SETUP: XMC_USBD_lReadFifo((uint32_t)data.b.epnum,(uint32_t)data.b.bcnt); break; case XMC_USBD_GRXSTS_PKTSTS_SETUPCMPL: break; default: break; } /* no need to clear */ } /** * @brief Global interrupt handler * * The handler first checks, which global interrupt has caused the interrupt * and then dispatches interrupt to the corresponding sub-handler. */ void XMC_USBD_IRQHandler(const XMC_USBD_t *const obj) { gintmsk_data_t gintmsk; gintsts_data_t data; gintmsk.d32 = xmc_device.global_register->gintmsk; data.d32 = xmc_device.global_register->gintsts & gintmsk.d32; if (data.b.sofintr) { xmc_device.DeviceEvent_cb(XMC_USBD_EVENT_SOF); XMC_USBD_ClearEvent(XMC_USBD_EVENT_SOF); } if(obj->usbd_transfer_mode == XMC_USBD_USE_FIFO) { if (data.b.rxstsqlvl) { /* Masked that interrupt so its only done once */ gintmsk.b.rxstsqlvl = 0U; xmc_device.global_register->gintmsk = gintmsk.d32; XMC_USBD_lHandleRxFLvl(); /* handle the interrupt */ gintmsk.b.rxstsqlvl = 1U; xmc_device.global_register->gintmsk = gintmsk.d32; } } if (data.b.erlysuspend) { XMC_USBD_ClearEvent(XMC_USBD_EVENT_EARLYSUSPEND); } if (data.b.usbsuspend) { xmc_device.DeviceEvent_cb(XMC_USBD_EVENT_SUSPEND); XMC_USBD_ClearEvent(XMC_USBD_EVENT_SUSPEND); } if (data.b.wkupintr) { xmc_device.DeviceEvent_cb(XMC_USBD_EVENT_REMOTE_WAKEUP); XMC_USBD_ClearEvent(XMC_USBD_EVENT_REMOTE_WAKEUP); } if (data.b.sessreqintr) { xmc_device.IsPowered = 1U; xmc_device.DeviceEvent_cb(XMC_USBD_EVENT_POWER_ON); XMC_USBD_ClearEvent(XMC_USBD_EVENT_POWER_ON); } if (data.b.usbreset) { XMC_USBD_lHandleUSBReset(obj); } if (data.b.enumdone) { XMC_USBD_lHandleEnumDone(); } if (data.b.inepint) { XMC_USBD_lHandleIEPInt(obj); } if (data.b.outepintr) { XMC_USBD_lHandleOEPInt(obj); } if (data.b.otgintr) { XMC_USBD_lHandleOTGInt(); } } /******************************************************************************* * API IMPLEMENTATION *******************************************************************************/ /** * Enables the USB0 module **/ void XMC_USBD_Enable(void) { #if defined(CLOCK_GATING_SUPPORTED) XMC_SCU_CLOCK_UngatePeripheralClock(XMC_SCU_PERIPHERAL_CLOCK_USB0); #endif /* Reset and power up */ XMC_SCU_RESET_DeassertPeripheralReset(XMC_SCU_PERIPHERAL_RESET_USB0); XMC_SCU_POWER_EnableUsb(); } /** * Disables the USB0 module **/ void XMC_USBD_Disable(void) { /* Clear Reset and power up */ XMC_SCU_RESET_AssertPeripheralReset(XMC_SCU_PERIPHERAL_RESET_USB0); #if defined(CLOCK_GATING_SUPPORTED) XMC_SCU_CLOCK_GatePeripheralClock(XMC_SCU_PERIPHERAL_CLOCK_USB0); #endif XMC_SCU_POWER_DisableUsb(); } /** * Clear the USB device event **/ void XMC_USBD_ClearEvent(const XMC_USBD_EVENT_t event) { gintsts_data_t clear; clear.d32 = 0U; switch(event) { case (XMC_USBD_EVENT_POWER_ON): clear.b.sessreqintr = 1U; break; case (XMC_USBD_EVENT_RESET): clear.b.usbreset = 1U; break; case (XMC_USBD_EVENT_SUSPEND): clear.b.usbsuspend = 1U; break; case (XMC_USBD_EVENT_RESUME): clear.b.wkupintr = 1U; break; case (XMC_USBD_EVENT_REMOTE_WAKEUP): clear.b.wkupintr = 1U; break; case (XMC_USBD_EVENT_SOF): clear.b.sofintr = 1U; break; case (XMC_USBD_EVENT_EARLYSUSPEND): clear.b.erlysuspend = 1U; break; case (XMC_USBD_EVENT_ENUMDONE): clear.b.enumdone = 1U; break; case (XMC_USBD_EVENT_OUTEP): clear.b.outepintr = 1U; break; default: break; } xmc_device.global_register->gintsts = clear.d32; } /** * Clear the USB OTG events **/ static void XMC_USBD_lClearEventOTG(uint32_t event) { gotgint_data_t clear = { .d32 = 0U}; clear.d32 = event; xmc_device.global_register->gotgint = clear.d32; } /** * Clear the USB IN EP events **/ void XMC_USBD_ClearEventINEP(uint32_t event,const uint8_t ep_num) { diepint_data_t clear; clear.d32 = event; xmc_device.endpoint_in_register[ep_num]->diepint = clear.d32; } /** * Clear the USB OUT EP events **/ void XMC_USBD_ClearEventOUTEP(uint32_t event,const uint8_t ep_num) { doepint_data_t clear; clear.d32 = event; xmc_device.endpoint_out_register[ep_num]->doepint = clear.d32; } /** * Enable the USB OUT EP events **/ void XMC_USBD_EnableEventOUTEP(uint32_t event) { doepint_data_t doepint; doepint.d32 = event; xmc_device.device_register->doepmsk |= doepint.d32; } /** * Enable the USB IN EP events **/ void XMC_USBD_EnableEventINEP(uint32_t event) { diepint_data_t diepint; diepint.d32 = event; xmc_device.device_register->diepmsk |= diepint.d32; } /** * Gets the USB device capabilities **/ XMC_USBD_CAPABILITIES_t XMC_USBD_GetCapabilities() { XMC_USBD_CAPABILITIES_t cap={0U}; cap.event_connect = 1U; cap.event_disconnect = 1U; #if UC_SERIES == 45 cap.event_power_off = 1U; cap.event_power_on = 1U; #else cap.event_power_off = 0U; cap.event_power_on = 0U; #endif cap.event_high_speed = 0U; cap.event_remote_wakeup = 1U; cap.event_reset = 1U; cap.event_resume = 1U; cap.event_suspend = 1U; cap.reserved = 0U; return cap; } /** * Initializes the USB device **/ XMC_USBD_STATUS_t XMC_USBD_Init(XMC_USBD_t *obj) { uint8_t *XMC_USBD_BASE_ADDRESS; uint32_t i; gahbcfg_data_t gahbcfg; gusbcfg_data_t gusbcfg; dcfg_data_t dcfg; dctl_data_t dctl; gintmsk_data_t gintmsk; XMC_ASSERT("XMC_USBD_Init: obj.usbd_max_num_eps not of type XMC_USBD_MAX_NUM_EPS_t", XMC_USBD_CHECK_INPUT_MAX_NUM_EPS(obj->usbd_max_num_eps)) XMC_USBD_Enable(); usbd_init = obj; /* Filling out buffer size */ for(i = 0U;i < (uint32_t)XMC_USBD_NUM_EPS;i++) { XMC_USBD_EP_OUT_BUFFERSIZE[i] = XMC_USBD_EP0_BUFFER_SIZE; XMC_USBD_EP_IN_BUFFERSIZE[i] = XMC_USBD_EP0_BUFFER_SIZE; } /* clear device status */ memset((void*)&xmc_device,0x0U,sizeof(XMC_USBD_DEVICE_t)); /* assign callbacks */ xmc_device.DeviceEvent_cb = obj->cb_xmc_device_event; xmc_device.EndpointEvent_cb = obj->cb_endpoint_event; XMC_USBD_BASE_ADDRESS = (uint8_t *)(obj->usbd); /* assign register address */ xmc_device.global_register = (dwc_otg_core_global_regs_t*)(obj->usbd); xmc_device.device_register = ((dwc_otg_device_global_regs_t*)(XMC_USBD_BASE_ADDRESS + DWC_DEV_GLOBAL_REG_OFFSET)); for (i = 0U;i < (uint32_t)XMC_USBD_NUM_EPS;i++) { xmc_device.endpoint_in_register[i] = (dwc_otg_dev_in_ep_regs_t*)(XMC_USBD_BASE_ADDRESS + DWC_DEV_IN_EP_REG_OFFSET + ((uint32_t)DWC_EP_REG_OFFSET*i)); } for (i = 0U;i < (uint32_t)XMC_USBD_NUM_EPS;i++) { xmc_device.endpoint_out_register[i] = (dwc_otg_dev_out_ep_regs_t*)(XMC_USBD_BASE_ADDRESS + DWC_DEV_OUT_EP_REG_OFFSET + ((uint32_t)DWC_EP_REG_OFFSET*i)); } for (i = 0U;i < (uint32_t)XMC_USBD_NUM_TX_FIFOS;i++) { xmc_device.fifo[i] = (uint32_t*)(XMC_USBD_BASE_ADDRESS + XMC_USBD_TX_FIFO_REG_OFFSET + (i * XMC_USBD_TX_FIFO_OFFSET)); } /* obj data structure for endpoint 0 */ /* Done by driver core */ /* configure ahb details */ gahbcfg.d32 = xmc_device.global_register->gahbcfg; gahbcfg.b.glblintrmsk = 1U; /* enable interrupts ( global mask ) */ gahbcfg.b.nptxfemplvl_txfemplvl = 1U; if(obj->usbd_transfer_mode == XMC_USBD_USE_DMA) { /* Enable dma if needed */ gahbcfg.b.dmaenable = 1U; /* enable dma if needed */ } else { gahbcfg.b.dmaenable = 0U; } xmc_device.global_register->gahbcfg = gahbcfg.d32; /* configure usb details */ gusbcfg.d32= xmc_device.global_register->gusbcfg; gusbcfg.b.force_dev_mode = 1U; /* force us into device mode */ gusbcfg.b.srpcap = 1U; /* enable session request protocoll */ xmc_device.global_register->gusbcfg = gusbcfg.d32; /* Device init */ /* configure device speed */ dcfg.d32 = xmc_device.device_register->dcfg; dcfg.b.devspd = XMC_USBD_DCFG_DEVSPD_FS; dcfg.b.descdma = 0U; xmc_device.device_register->dcfg = dcfg.d32; /* configure device functions */ dctl.d32 = xmc_device.device_register->dctl; dctl.b.sftdiscon = 1U; /* disconnect the device until its connected by the user */ /* all other config is done by default register value */ xmc_device.device_register->dctl = dctl.d32; /* flush the fifos for proper operation */ XMC_USBD_lFlushTXFifo((uint8_t)0x10U); /* 0x10 == all fifos, see doc */ XMC_USBD_lFlushRXFifo(); /* Enable Global Interrupts */ /* clear interrupt status bits prior to unmasking */ xmc_device.global_register->gintmsk = 0U; /* disable all interrupts */ xmc_device.global_register->gintsts = 0xFFFFFFFFU; /* clear all interrupts */ gintmsk.d32 = 0U; /* enable common interrupts */ gintmsk.b.modemismatch = 1U; gintmsk.b.otgintr = 1U; gintmsk.b.sessreqintr = 1U; /* enable device interrupts */ gintmsk.b.usbreset = 1U; gintmsk.b.enumdone = 1U; gintmsk.b.erlysuspend = 1U; gintmsk.b.usbsuspend = 1U; gintmsk.b.wkupintr = 1U; gintmsk.b.sofintr = 1U; if(obj->usbd_transfer_mode == XMC_USBD_USE_FIFO) { gintmsk.b.rxstsqlvl = 1U; } gintmsk.b.outepintr = 1U; gintmsk.b.inepintr = 1U; xmc_device.global_register->gintmsk = gintmsk.d32; return XMC_USBD_STATUS_OK; } /** * Uninitializes the USB device **/ XMC_USBD_STATUS_t XMC_USBD_Uninitialize() { /* Disconnect the device */ dctl_data_t dctl; dctl.d32 = xmc_device.device_register->dctl; dctl.b.sftdiscon = 1U; xmc_device.device_register->dctl = dctl.d32; /* clean up */ memset((void*)&xmc_device,0U,sizeof(xmc_device)); return XMC_USBD_STATUS_OK; } /** * Connects the USB device to host **/ XMC_USBD_STATUS_t XMC_USBD_DeviceConnect() { /* Just disable softdisconnect */ dctl_data_t dctl; dctl.d32 = xmc_device.device_register->dctl; dctl.b.sftdiscon = 0U; xmc_device.device_register->dctl = dctl.d32; return XMC_USBD_STATUS_OK; } /** * Disconnects the USB device from host **/ XMC_USBD_STATUS_t XMC_USBD_DeviceDisconnect() { dctl_data_t dctl; dctl.d32 = xmc_device.device_register->dctl; dctl.b.sftdiscon = 1U; xmc_device.device_register->dctl = dctl.d32; return XMC_USBD_STATUS_OK; } /** * Gets the USB device state. **/ XMC_USBD_STATE_t XMC_USBD_DeviceGetState(const XMC_USBD_t *const obj) { XMC_USBD_STATE_t state={0U}; state.speed = XMC_USBD_SPEED_FULL; state.connected = xmc_device.IsConnected; state.active = XMC_USBD_lDeviceActive(obj); state.powered = xmc_device.IsPowered; return state; } /** * Prepares the endpoint to read next OUT packet **/ XMC_USBD_STATUS_t XMC_USBD_EndpointReadStart(const uint8_t ep_addr, uint32_t size) { XMC_USBD_EP_t *ep = &xmc_device.ep[ep_addr & (uint8_t)XMC_USBD_EP_NUM_MASK]; XMC_USBD_STATUS_t result; if (ep->outInUse || !ep->isConfigured) { result = XMC_USBD_STATUS_ERROR; } else { /* short the length to buffer size if needed */ if (size > ep->outBufferSize) { size = ep->outBufferSize; } /* set ep values */ ep->xferTotal = size; ep->xferCount = 0U; ep->xferLength = 0U; ep->xferBuffer = ep->outBuffer; ep->outBytesAvailable = 0U; XMC_USBD_lStartReadXfer(ep); result= XMC_USBD_STATUS_OK; } return result; } /** * Reads the number of bytes from the USB OUT endpoint **/ int32_t XMC_USBD_EndpointRead(const uint8_t ep_num,uint8_t * buffer,uint32_t length) { XMC_USBD_EP_t *ep = &xmc_device.ep[ep_num]; if (length > ep->outBytesAvailable) { length = ep->outBytesAvailable; } memcpy(buffer,&ep->outBuffer[ep->outOffset],length); ep->outBytesAvailable -= length; if (ep->outBytesAvailable) { ep->outOffset += length; } else { ep->outOffset = 0U; } return (int32_t)length; } /** * Writes number of bytes in to the USB IN endpoint. **/ int32_t XMC_USBD_EndpointWrite(const uint8_t ep_num,const uint8_t * buffer,uint32_t length) { XMC_USBD_EP_t * ep = &xmc_device.ep[ep_num & (uint8_t)XMC_USBD_EP_NUM_MASK]; int32_t result; if (!ep->isConfigured) { result = (int32_t)XMC_USBD_STATUS_ERROR; } else if (ep->inInUse == 1U) { result=(int32_t)0; } else { if (length > ep->inBufferSize) { length = ep->inBufferSize; } /* copy data into input buffer for DMA and FIFO mode */ memcpy(ep->inBuffer,(const void *)buffer,length); ep->xferBuffer = ep->inBuffer; ep->xferTotal = length; /* set transfer values */ ep->xferLength = 0U; ep->xferCount = 0U; ep->inInUse = 1U; /* start the transfer */ XMC_USBD_lStartWriteXfer(ep); result=(int32_t)ep->xferTotal; } return result; } /** * Sets the USB device address. **/ XMC_USBD_STATUS_t XMC_USBD_DeviceSetAddress(const uint8_t address,const XMC_USBD_SET_ADDRESS_STAGE_t stage) { dcfg_data_t data; data.d32 = xmc_device.device_register->dcfg; if (stage == XMC_USBD_SET_ADDRESS_STAGE_SETUP) { data.b.devaddr = address; xmc_device.device_register->dcfg = data.d32; } return XMC_USBD_STATUS_OK; } /** * Set/clear stall on the selected endpoint. **/ XMC_USBD_STATUS_t XMC_USBD_EndpointStall(const uint8_t ep_addr, const bool stall) { depctl_data_t data; XMC_USBD_EP_t *ep = &xmc_device.ep[(ep_addr & (uint8_t)XMC_USBD_EP_NUM_MASK)]; if (stall) { if (ep_addr & (uint8_t)XMC_USBD_ENDPOINT_DIRECTION_MASK) { /*set stall bit */ data.d32 = xmc_device.endpoint_in_register[ep->address_u.address_st.number]->diepctl; data.b.stall = 1U; xmc_device.endpoint_in_register[ep->address_u.address_st.number]->diepctl = data.d32; } else { /*set stall bit */ data.d32 = xmc_device.endpoint_out_register[ep->address_u.address_st.number]->doepctl; data.b.stall = 1U; xmc_device.endpoint_out_register[ep->address_u.address_st.number]->doepctl = data.d32; } ep->isStalled = 1U; } else { /* just clear stall bit */ if (ep_addr & (uint8_t)XMC_USBD_ENDPOINT_DIRECTION_MASK) { data.d32 = xmc_device.endpoint_in_register[ep->address_u.address_st.number]->diepctl; data.b.stall = 0U; data.b.setd0pid = 1U; /* reset pid to 0 */ xmc_device.endpoint_in_register[ep->address_u.address_st.number]->diepctl = data.d32; } else { data.d32 = xmc_device.endpoint_out_register[ep->address_u.address_st.number]->doepctl; data.b.stall = 0U; data.b.setd0pid = 1U; /* reset pid to 0 */ xmc_device.endpoint_out_register[ep->address_u.address_st.number]->doepctl = data.d32; } ep->isStalled = 0U; } return XMC_USBD_STATUS_OK; } /** * Aborts the data transfer on the selected endpoint **/ XMC_USBD_STATUS_t XMC_USBD_EndpointAbort(const uint8_t ep_addr) { XMC_USBD_EP_t *ep = &xmc_device.ep[ep_addr & (uint8_t)XMC_USBD_ENDPOINT_NUMBER_MASK]; if (ep->address_u.address_st.direction) { ep->inInUse = 0U; } if (!ep->address_u.address_st.direction) { ep->outInUse = 0U; } ep->isStalled = 0U; ep->outBytesAvailable = 0U; ep->outOffset = 0U; ep->xferLength = 0U; ep->xferCount = 0U; ep->xferTotal = 0U; return XMC_USBD_STATUS_OK; } /** * Configures the given endpoint **/ XMC_USBD_STATUS_t XMC_USBD_EndpointConfigure(const uint8_t ep_addr, const XMC_USBD_ENDPOINT_TYPE_t ep_type, const uint16_t ep_max_packet_size) { daint_data_t daintmsk; XMC_USBD_EP_t *ep; daintmsk.d32 = xmc_device.device_register->daintmsk; ep =&xmc_device.ep[ep_addr & (uint32_t)XMC_USBD_ENDPOINT_NUMBER_MASK]; memset((void*)ep,0x0U,sizeof(XMC_USBD_EP_t)); /* clear endpoint structure */ /* do ep configuration */ ep->address_u.address = ep_addr; ep->isConfigured = 1U; ep->maxPacketSize = (uint8_t)ep_max_packet_size; if (ep->address_u.address != 0U) { ep->maxTransferSize = (uint32_t)XMC_USBD_MAX_TRANSFER_SIZE; } else { ep->maxTransferSize = (uint32_t)XMC_USBD_MAX_TRANSFER_SIZE_EP0; } /* transfer buffer */ ep->inBuffer = XMC_USBD_EP_IN_BUFFER[ep->address_u.address_st.number]; ep->outBuffer = XMC_USBD_EP_OUT_BUFFER[ep->address_u.address_st.number]; /* buffer size*/ ep->inBufferSize = XMC_USBD_EP_IN_BUFFERSIZE[ep->address_u.address_st.number]; ep->outBufferSize = XMC_USBD_EP_OUT_BUFFERSIZE[ep->address_u.address_st.number]; /* is in */ if ((ep->address_u.address_st.direction == 1U) || (ep_type == XMC_USBD_ENDPOINT_TYPE_CONTROL)) { depctl_data_t data; data.d32 = xmc_device.endpoint_in_register[ep->address_u.address_st.number]->diepctl; /*enable endpoint */ data.b.usbactep = 1U; /* set ep type */ data.b.eptype = (uint8_t)ep_type; /* set mps */ if (ep_type == XMC_USBD_ENDPOINT_TYPE_CONTROL) { switch(ep_max_packet_size) { case (64U): data.b.mps = 0x0U; break; case (32U): data.b.mps = 0x1U; break; case (16U): data.b.mps = 0x2U; break; case (8U): data.b.mps = 0x3U; break; default: break; } } else { data.b.mps = ep_max_packet_size; } /* set first data0 pid */ data.b.setd0pid = 1U; /* clear stall */ data.b.stall = 0U; /* set tx fifo */ ep->txFifoNum = XMC_USBD_lAssignTXFifo(); /* get tx fifo */ data.b.txfnum = ep->txFifoNum; xmc_device.endpoint_in_register[ep->address_u.address_st.number]->diepctl = data.d32; /* configure endpoint */ daintmsk.ep.in |= (uint16_t)((uint16_t)1U << (uint8_t)ep->address_u.address_st.number); /* enable interrupts for endpoint */ } if ((ep->address_u.address_st.direction == 0U) || (ep_type == XMC_USBD_ENDPOINT_TYPE_CONTROL)) { /* is out */ depctl_data_t data; data.d32 = xmc_device.endpoint_out_register[ep->address_u.address_st.number]->doepctl; /*enable endpoint */ data.b.usbactep = 1U; /* set ep type */ data.b.eptype = (uint8_t)ep_type; /* set mps */ if (ep_type == XMC_USBD_ENDPOINT_TYPE_CONTROL) { switch(ep_max_packet_size) { case (64U): data.b.mps = 0x0U; break; case (32U): data.b.mps = 0x1U; break; case (16U): data.b.mps = 0x2U; break; case (8U): data.b.mps = 0x3U; break; default: break; } } else { data.b.mps = ep_max_packet_size; } /* set first data0 pid */ data.b.setd0pid = 1U; /* clear stall */ data.b.stall =(uint8_t) 0U; xmc_device.endpoint_out_register[ep->address_u.address_st.number]->doepctl = data.d32; /* configure endpoint */ daintmsk.ep.out |=(uint16_t) ((uint16_t)1U << (uint8_t)ep->address_u.address_st.number); /* enable interrupts */ } xmc_device.device_register->daintmsk = daintmsk.d32; return XMC_USBD_STATUS_OK; } /** * Unconfigure the selected endpoint. **/ XMC_USBD_STATUS_t XMC_USBD_EndpointUnconfigure(const uint8_t ep_addr) { XMC_USBD_EP_t *ep = &xmc_device.ep[ep_addr & (uint8_t)XMC_USBD_ENDPOINT_NUMBER_MASK]; depctl_data_t data; daint_data_t daintmsk; XMC_USBD_STATUS_t result; uint32_t number_temp; data.d32 = 0U; daintmsk.d32 = xmc_device.device_register->daintmsk; number_temp = (uint32_t)((uint32_t)1U << (uint8_t)ep->address_u.address_st.number); /* if not configured return an error */ if (!ep->isConfigured) { result = XMC_USBD_STATUS_ERROR; } else { /* disable the endpoint, deactivate it and only send naks */ data.b.usbactep = 0U; data.b.epdis = 1U; data.b.snak = 1U; data.b.stall = 0U; ep->isConfigured = 0U; ep->isStalled = 0U; ep->outInUse = 0U; ep->inInUse = 0U; /* chose register based on the direction. Control Endpoint need both */ if ((ep->address_u.address_st.direction == 1U) || (ep->type == (uint8_t)XMC_USBD_ENDPOINT_TYPE_CONTROL)) { /* disable endpoint configuration */ xmc_device.endpoint_in_register[ep->address_u.address_st.number]->diepctl = data.d32; /* disable interrupts */ daintmsk.ep.in = (uint16_t)((uint32_t)daintmsk.ep.in & (~(uint32_t)number_temp)); } if ((ep->address_u.address_st.direction == 0U) || (ep->type == (uint8_t)XMC_USBD_ENDPOINT_TYPE_CONTROL)) { xmc_device.endpoint_out_register[ep->address_u.address_st.number]->doepctl = data.d32; daintmsk.ep.out = (uint16_t)((uint32_t)daintmsk.ep.out & (~(uint32_t)number_temp)); if(usbd_init->usbd_transfer_mode == XMC_USBD_USE_FIFO) { xmc_device.device_register->dtknqr4_fifoemptymsk &= ~number_temp; } } xmc_device.device_register->daintmsk = daintmsk.d32; XMC_USBD_lUnassignFifo(ep->txFifoNum); /* free fifo */ result = XMC_USBD_STATUS_OK; } return result; } /** * Gets the current USB frame number **/ uint16_t XMC_USBD_GetFrameNumber(void) { uint16_t result; dsts_data_t dsts; dsts.d32 = xmc_device.device_register->dsts; result = (uint16_t)dsts.b.soffn; return result; } /** * Gets the USB speed enumeration completion status. * This should not be used for the actual USB enumeration completion status. For the actual USB enumeration status, * the application layer should check for the completion of USB standard request Set configuration. **/ uint32_t XMC_USBD_IsEnumDone(void) { return (uint32_t)((uint8_t)xmc_device.IsConnected && (uint8_t)xmc_device.IsPowered); } /*** * MISRA C 2004 Deviations * * 1. cast from pointer to pointer [MISRA 2004 Rule 11.4] * 2. cast from pointer to unsigned int [Encompasses MISRA 2004 Rule 11.1], [MISRA 2004 Rule 11.3] * 3. call to function 'memset()' not made in the presence of a prototype [MISRA 2004 Rule 8.1] * 4. No explicit type for symbol '_Bool', int assumed */ #endif /* defined(USB0) */