From 37c5eeb51de532dfbd46ae528d4d99220932d6bd Mon Sep 17 00:00:00 2001 From: Gordon McNab Date: Wed, 6 Oct 2021 16:50:55 +0100 Subject: Rename directories to indicate that FT930 and FT900 devices are both covered by the same SDK and src/portable code. Board makefile includes __FT900__ macro for FT90x and __FT930__ macro for boards with FT93x. --- src/portable/bridgetek/ft90x/dcd_ft90x.c | 1101 ------------------------------ src/portable/bridgetek/ft9xx/dcd_ft9xx.c | 1099 +++++++++++++++++++++++++++++ 2 files changed, 1099 insertions(+), 1101 deletions(-) delete mode 100644 src/portable/bridgetek/ft90x/dcd_ft90x.c create mode 100644 src/portable/bridgetek/ft9xx/dcd_ft9xx.c (limited to 'src') diff --git a/src/portable/bridgetek/ft90x/dcd_ft90x.c b/src/portable/bridgetek/ft90x/dcd_ft90x.c deleted file mode 100644 index ac13f11d1..000000000 --- a/src/portable/bridgetek/ft90x/dcd_ft90x.c +++ /dev/null @@ -1,1101 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2018, hathach (tinyusb.org) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * This file is part of the TinyUSB stack. - */ - -#include "tusb_option.h" -#include "bsp/board.h" - -#if TUSB_OPT_DEVICE_ENABLED && CFG_TUSB_MCU == OPT_MCU_FT90X - -#define USBD_USE_STREAMS - -#include -#include -#include -#include - -#include "device/dcd.h" - -//--------------------------------------------------------------------+ -// MACRO TYPEDEF CONSTANT ENUM DECLARATION -//--------------------------------------------------------------------+ - -// Board code will determine the state of VBUS from USB host. -extern int8_t board_ft90x_vbus(void); - -// Static array to store an incoming SETUP request for processing by tinyusb. -static uint8_t _ft90x_setup_packet[8]; - -struct ft90x_xfer_state -{ - volatile int16_t total_size; // Total transfer size in bytes for this transfer. - volatile int16_t remain_size; // Total remaining in transfer. - volatile uint8_t *buff_ptr; // Pointer to buffer to transmit from or receive to. - volatile uint8_t valid; // Transfer is pending and total_size, remain_size, and buff_ptr are valid. - - uint8_t type; // Endpoint type. Of type USBD_ENDPOINT_TYPE from endpoint descriptor. - uint8_t dir; // Endpoint direction. TUSB_DIR_OUT or TUSB_DIR_IN. For control endpoint this is the current direction. - uint16_t buff_size; // Actual size of buffer RAM used by endpoint. - uint16_t size; // Max packet size for endpoint from endpoint descriptor. -}; -// Endpoint description array for each endpoint. -static struct ft90x_xfer_state ep_xfer[USBD_MAX_ENDPOINT_COUNT]; -// USB speed. -static tusb_speed_t _speed; - -// Interrupt handlers. -void _ft90x_usbd_ISR(void); // Interrupt handler for USB device. -void ft90x_usbd_pm_ISR(void); // Interrupt handler for USB device for power management (called by board). - -// Internal functions forward declarations. -static uint16_t _ft90x_edpt_xfer_out(uint8_t ep_number, uint8_t *buffer, uint16_t xfer_bytes); -static uint16_t _ft90x_edpt_xfer_in(uint8_t ep_number, uint8_t *buffer, uint16_t xfer_bytes); -static void _ft90x_reset_edpts(void); -static inline void _ft90x_phy_enable(bool en); -static void _ft90x_usb_speed(void); -static void _dcd_ft90x_attach(void); -static void _dcd_ft90x_detach(void) __attribute__((unused)); -static uint16_t _ft90x_dusb_in(uint8_t ep_number, const uint8_t *buffer, uint16_t length); -static uint16_t _ft90x_dusb_out(uint8_t ep_number, uint8_t *buffer, uint16_t length); - -// Internal functions. - -// Manage an OUT transfer from the host. -// This can be up-to the maximum packet size of the endpoint. -// Continuation of a transfer beyond the maximum packet size is performed -// by the interrupt handler. -static uint16_t _ft90x_edpt_xfer_out(uint8_t ep_number, uint8_t *buffer, uint16_t xfer_bytes) -{ - //Note: this is called from only the interrupt handler when an OUT transfer is called. - uint16_t ep_size = ep_xfer[ep_number].size; - (void)ep_size; - if (xfer_bytes > ep_size) - { - xfer_bytes = ep_size; - } - - // Wait until the endpoint has finished - it should be complete! - //while (!(USBD_EP_SR_REG(ep_number) & MASK_USBD_EPxSR_OPRDY)) - //; - - // Send the first packet of max packet size - xfer_bytes = _ft90x_dusb_out(ep_number, (uint8_t *)buffer, xfer_bytes); - if (ep_number == USBD_EP_0) - { - // Set flags to indicate data ready. - USBD_EP_SR_REG(USBD_EP_0) = (MASK_USBD_EP0SR_OPRDY); - } - else - { - USBD_EP_SR_REG(ep_number) = (MASK_USBD_EPxSR_OPRDY); - } - - return xfer_bytes; -} - -// Manage an IN transfer to the host. -// This can be up-to the maximum packet size of the endpoint. -// Continuation of a transfer beyond the maximum packet size is performed -// by the interrupt handler. -static uint16_t _ft90x_edpt_xfer_in(uint8_t ep_number, uint8_t *buffer, uint16_t xfer_bytes) -{ - //Note: this may be called from the interrupt handler or from normal code. - uint8_t end = 0; - uint16_t ep_size = ep_xfer[ep_number].size; - (void)ep_size; - if ((xfer_bytes == 0) || (xfer_bytes < ep_size)) - { - end = 1; - } - else - { - xfer_bytes = ep_size; - } - - if (ep_number == USBD_EP_0) - { - // An IN direction SETUP can be interrupted by an OUT packet. - // This will result in a STALL generated by the silicon. - while (USBD_EP_SR_REG(USBD_EP_0) & MASK_USBD_EP0SR_STALL) - { - // Clear the STALL and finish the transaction. - USBD_EP_SR_REG(USBD_EP_0) = (MASK_USBD_EP0SR_STALL); - } - } - else - { - uint8_t sr_reg; - // If there is data to transmit then wait until the IN buffer - // for the endpoint is empty. - do - { - sr_reg = USBD_EP_SR_REG(ep_number); - } while (sr_reg & MASK_USBD_EPxSR_INPRDY); - - } - - xfer_bytes = _ft90x_dusb_in(ep_number, (uint8_t *)buffer, xfer_bytes); - - if (ep_number == USBD_EP_0) - { - if (end) - { - // Set flags to indicate data ready and transfer complete. - USBD_EP_SR_REG(USBD_EP_0) = MASK_USBD_EP0SR_INPRDY | MASK_USBD_EP0SR_DATAEND; - } - else - { - // Set flags to indicate data ready. - USBD_EP_SR_REG(USBD_EP_0) = (MASK_USBD_EP0SR_INPRDY); - } - } - else - { - // Set flags to indicate data ready. - USBD_EP_SR_REG(ep_number) = (MASK_USBD_EPxSR_INPRDY); - } - - return xfer_bytes; -} - -// Reset all endpoints to a default state. -// Control endpoint enabled and ready. All others disabled. -static void _ft90x_reset_edpts(void) -{ - // Disable all endpoints and remove configuration values. - // Clear settings. - tu_memclr(ep_xfer, sizeof(ep_xfer)); - for (int i = 0; i < USBD_MAX_ENDPOINT_COUNT; i++) - { - // Disable hardware. - USBD_EP_CR_REG(i) = 0; - } - - // Setup the control endpoint only. -#if CFG_TUD_ENDPOINT0_SIZE == 64 - USBD_EP_CR_REG(USBD_EP_0) = (USBD_EP0_MAX_SIZE_64 << BIT_USBD_EP0_MAX_SIZE); -#elif CFG_TUD_ENDPOINT0_SIZE == 32 - USBD_EP_CR_REG(USBD_EP_0) = (USBD_EP0_MAX_SIZE_32 << BIT_USBD_EP0_MAX_SIZE); -#elif CFG_TUD_ENDPOINT0_SIZE == 16 - USBD_EP_CR_REG(USBD_EP_0) = (USBD_EP0_MAX_SIZE_16 << BIT_USBD_EP0_MAX_SIZE); -#elif CFG_TUD_ENDPOINT0_SIZE == 8 - USBD_EP_CR_REG(USBD_EP_0) = (USBD_EP0_MAX_SIZE_8 << BIT_USBD_EP0_MAX_SIZE); -#else -#error "CFG_TUD_ENDPOINT0_SIZE must be defined with a value of 8, 16, 32 or 64." -#endif - // Configure the control endpoint. - ep_xfer[USBD_EP_0].size = CFG_TUD_ENDPOINT0_SIZE; - ep_xfer[USBD_EP_0].type = TUSB_XFER_CONTROL; - - // Enable interrupts from USB device control. - USBD_REG(cmie) = MASK_USBD_CMIE_ALL; - // Enable interrupts on EP0. - USBD_REG(epie) = (MASK_USBD_EPIE_EP0IE); -} - -// Enable or disable the USB PHY. -static inline void _ft90x_phy_enable(bool en) -{ - if (en) - SYS->PMCFG_L |= MASK_SYS_PMCFG_DEV_PHY_EN; - else - SYS->PMCFG_L &= ~MASK_SYS_PMCFG_DEV_PHY_EN; -} - -// Safely connect to the USB. -static void _dcd_ft90x_attach(void) -{ - uint8_t reg; - - CRITICAL_SECTION_BEGIN - // Disable device responses. - USBD_REG(faddr) = 0; - - // Reset USB Device. - SYS->MSC0CFG = SYS->MSC0CFG | MASK_SYS_MSC0CFG_DEV_RESET_ALL; - // Disable device connect/disconnect/host reset detection. - SYS->PMCFG_H = MASK_SYS_PMCFG_DEV_DIS_DEV; - SYS->PMCFG_H = MASK_SYS_PMCFG_DEV_CONN_DEV; - SYS->PMCFG_L = SYS->PMCFG_L & (~MASK_SYS_PMCFG_DEV_DETECT_EN); - - // Enable Chip USB device clock/PM configuration. - sys_enable(sys_device_usb_device); - CRITICAL_SECTION_END; - - // Wait a short time to get started. - delayms(1); - - CRITICAL_SECTION_BEGIN - // Turn off the device enable bit. -#if CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED - USBD_REG(fctrl) = 0; -#else - //Set the full speed only bit if required. - USBD_REG(fctrl) = MASK_USBD_FCTRL_MODE_FS_ONLY; -#endif - - // Clear first reset and suspend interrupts. - do - { - reg = USBD_REG(cmif); - USBD_REG(cmif) = reg; - } while (reg); - // Clear any endpoint interrupts. - reg = USBD_REG(epif); - USBD_REG(epif) = reg; - - // Disable all interrupts from USB device control before attaching interrupt. - USBD_REG(cmie) = 0; - CRITICAL_SECTION_END; - - // Enable device connect/disconnect/host reset detection. - // Set device detect and remote wakeup enable interrupt enables. - SYS->PMCFG_L = SYS->PMCFG_L | MASK_SYS_PMCFG_DEV_DETECT_EN; - -#if defined(__FT930__) - // Setup VBUS detect - SYS->MSC0CFG = SYS->MSC0CFG | MASK_SYS_MSC0CFG_USB_VBUS_EN; -#endif -} - -// Gracefully disconnect from the USB. -static void _dcd_ft90x_detach(void) -{ - // Disable device connect/disconnect/host reset detection. - SYS->PMCFG_L = SYS->PMCFG_L & (~MASK_SYS_PMCFG_DEV_DETECT_EN); - -#if defined(__FT930__) - // Disable VBUS detection. - SYS->MSC0CFG = SYS->MSC0CFG & (~MASK_SYS_MSC0CFG_USB_VBUS_EN); -#endif - CRITICAL_SECTION_BEGIN - USBD_REG(epie) = 0; - USBD_REG(cmie) = 0; - CRITICAL_SECTION_END; - - // Disable the USB function. - USBD_REG(fctrl) = 0; - delayms(1); - - // Disable USB PHY - dcd_disconnect(0); - delayms(1); - - // Disable Chip USB device clock/PM configuration. - sys_disable(sys_device_usb_device); - - // Reset USB Device... Needed for Back voltage D+ to be <400mV - SYS->MSC0CFG = SYS->MSC0CFG | MASK_SYS_MSC0CFG_DEV_RESET_ALL; - - delayms(1); - // Set device detect and remote wakeup enable interrupt enables. - SYS->PMCFG_L = SYS->PMCFG_L | MASK_SYS_PMCFG_DEV_DETECT_EN; - -#if defined(__FT930__) - // Setup VBUS detect - SYS->MSC0CFG = SYS->MSC0CFG | MASK_SYS_MSC0CFG_USB_VBUS_EN; -#endif -} - -// Determine the speed of the USB to which we are connected. -// Set the speed of the PHY accordingly. -// High speed can be disabled through CFG_TUSB_RHPORT0_MODE settings. -static void _ft90x_usb_speed(void) -{ - uint8_t fctrl_val; - - // If USB device function is already enabled then disable it. - if (USBD_REG(fctrl) & MASK_USBD_FCTRL_USB_DEV_EN) { - USBD_REG(fctrl) = (USBD_REG(fctrl) & (~MASK_USBD_FCTRL_USB_DEV_EN)); - delayus(200); - } - -#if CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED - - /* Detect high or full speed */ - fctrl_val = MASK_USBD_FCTRL_USB_DEV_EN; -#if defined(__FT900__) - if (!sys_check_ft900_revB())//if 90x series is rev C - { - fctrl_val |= MASK_USBD_FCTRL_IMP_PERF; - } -#endif - USBD_REG(fctrl) = fctrl_val; - -#if defined(__FT930__) - delayus(200); - - _speed = (SYS->MSC0CFG & MASK_SYS_MSC0CFG_HIGH_SPED_MODE) ? - TUSB_SPEED_HIGH : TUSB_SPEED_FULL; -#else /* __FT930__ */ - /* Detection by SOF */ - while (!(USBD_REG(cmif) & MASK_USBD_CMIF_SOFIRQ)); - USBD_REG(cmif) = MASK_USBD_CMIF_SOFIRQ; - delayus(125 + 5); - _speed = (USBD_REG(cmif) & MASK_USBD_CMIF_SOFIRQ) ? - TUSB_SPEED_HIGH : TUSB_SPEED_FULL; - dcd_event_bus_reset(0, _speed, true); - -#endif /* !__FT930__ */ - -#else /* CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED */ - - /* User force set to full speed */ - _speed = TUSB_SPEED_FULL; - fctrl_val = - MASK_USBD_FCTRL_USB_DEV_EN | MASK_USBD_FCTRL_MODE_FS_ONLY; -#if defined(__FT900__) - if (!sys_check_ft900_revB())//if 90x series is rev C - { - fctrl_val |= MASK_USBD_FCTRL_IMP_PERF; - } -#endif - USBD_REG(fctrl) = fctrl_val; - dcd_event_bus_reset(0, _speed, true); - return; - -#endif /* CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED */ -} - -// Send a buffer to the USB IN FIFO. -// When the macro USBD_USE_STREAMS is defined this will stream a buffer of data -// to the FIFO using the most efficient MCU streamout combination. -// If streaming is disabled then it will send each byte of the buffer in turn -// to the FIFO. The is no reason to not stream. -// The total number of bytes sent to the FIFO is returned. -static uint16_t _ft90x_dusb_in(uint8_t ep_number, const uint8_t *buffer, uint16_t length) -{ - uint16_t bytes_read = 0; - uint16_t buff_size = length; - -#ifdef USBD_USE_STREAMS - volatile uint8_t *data_reg; - - data_reg = (uint8_t *)&(USBD->ep[ep_number].epxfifo); - if (buff_size) - { - if (((uint32_t)buffer) % 4 == 0) - { - uint16_t aligned = buff_size & (~3); - uint16_t left = buff_size & 3; - - if (aligned) - { - __asm__ volatile("streamout.l %0,%1,%2" - : - : "r"(data_reg), "r"(buffer), "r"(aligned)); - buffer += aligned; - } - if (left) - { - __asm__ volatile("streamout.b %0,%1,%2" - : - : "r"(data_reg), "r"(buffer), "r"(left)); - } - } - else - { - __asm__ volatile("streamout.b %0,%1,%2" - : - : "r"(data_reg), "r"(buffer), "r"(buff_size)); - } - bytes_read = buff_size; - } -#else // USBD_USE_STREAMS - - bytes_read = buff_size; - while (buff_size--) - { - USBD_EP_FIFO_REG(ep_number) = *buffer++; - }; - -#endif // USBD_USE_STREAMS - - return bytes_read; -} - -// Receive a buffer from the USB OUT FIFO. -// When the macro USBD_USE_STREAMS is defined this will stream from the FIFO -// to a buffer of data using the most efficient MCU streamin combination. -// If streaming is disabled then it will receive each byte from the FIFO in turn -// to the buffer. The is no reason to not stream. -// The total number of bytes received from the FIFO is returned. -static uint16_t _ft90x_dusb_out(uint8_t ep_number, uint8_t *buffer, uint16_t length) -{ -#ifdef USBD_USE_STREAMS - volatile uint8_t *data_reg; -#endif // USBD_USE_STREAMS - uint16_t bytes_read = 0; - uint16_t buff_size = length; - - if (length > 0) - { - if (ep_number == USBD_EP_0) - { - buff_size = USBD_EP_CNT_REG(USBD_EP_0); - } - else - { - if (USBD_EP_SR_REG(ep_number) & (MASK_USBD_EPxSR_OPRDY)) - { - buff_size = USBD_EP_CNT_REG(ep_number); - } - } - } - - // Only read as many bytes as we have space for. - if (buff_size > length) - buff_size = length; - -#ifdef USBD_USE_STREAMS - data_reg = (uint8_t *)&(USBD->ep[ep_number].epxfifo); - if (buff_size) - { - if ((uint32_t)buffer % 4 == 0) - { - uint16_t aligned = buff_size & (~3); - uint16_t left = buff_size & 3; - - if (aligned) - { - __asm__ volatile("streamin.l %0,%1,%2" - : - : "r"(buffer), "r"(data_reg), "r"(aligned)); - buffer += aligned; - } - if (left) - { - __asm__ volatile("streamin.b %0,%1,%2" - : - : "r"(buffer), "r"(data_reg), "r"(left)); - } - } - else - { - __asm__ volatile("streamin.b %0,%1,%2" - : - : "r"(buffer), "r"(data_reg), "r"(buff_size)); - } - bytes_read = buff_size; - } -#else // USBD_USE_STREAMS - - bytes_read = buff_size; - while (buff_size--) - { - *buffer++ = USBD_EP_FIFO_REG(ep_number); - } - -#endif // USBD_USE_STREAMS - - return bytes_read; -} - -/*------------------------------------------------------------------*/ -/* Device API - *------------------------------------------------------------------*/ - -// Initialize controller to device mode -void dcd_init(uint8_t rhport) -{ - TU_LOG2("FT90x initialisation\r\n"); - - _dcd_ft90x_attach(); - - //_ft90x_reset_edpts(void);//tu_memclr(ep_xfer, sizeof(ep_xfer)); - - interrupt_attach(interrupt_usb_device, (int8_t)interrupt_usb_device, _ft90x_usbd_ISR); - - dcd_connect(rhport); -} - -// Enable device interrupt -void dcd_int_enable(uint8_t rhport) -{ - (void)rhport; - TU_LOG3("FT90x int enable\r\n"); - - // Peripheral devices interrupt enable. - interrupt_enable_globally(); -} - -// Disable device interrupt -void dcd_int_disable(uint8_t rhport) -{ - (void)rhport; - TU_LOG3("FT90x int disable\r\n"); - - // Peripheral devices interrupt disable. - interrupt_disable_globally(); -} - -// Receive Set Address request, mcu port must also include status IN response -void dcd_set_address(uint8_t rhport, uint8_t dev_addr) -{ - (void)rhport; - (void)dev_addr; - - // Respond with status. There is no checking that the address is in range. - dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); - - // Set the update bit for the address register. - dev_addr |= 0x80; - - // Modify the address register within a critical section. - CRITICAL_SECTION_BEGIN - { - USBD_REG(faddr) = dev_addr; - } - CRITICAL_SECTION_END; -} - -// Invoked when a control transfer's status stage is complete. -// May help DCD to prepare for next control transfer, this API is optional. -#if 0 // never called -void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request) -{ - (void) rhport; - - if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE && - request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD ) - { - if (request->bRequest == TUSB_REQ_SET_ADDRESS) - { - } - else if (request->bRequest == TUSB_REQ_SET_CONFIGURATION) - { - } - } -} -#endif // 0 - -// Wake up host -void dcd_remote_wakeup(uint8_t rhport) -{ - (void)rhport; - - SYS->MSC0CFG = SYS->MSC0CFG | MASK_SYS_MSC0CFG_DEV_RMWAKEUP; - - // Atleast 2 ms of delay needed for RESUME Data K state. - delayms(2); - - SYS->MSC0CFG &= ~MASK_SYS_MSC0CFG_DEV_RMWAKEUP; - - // Enable USB PHY and determine current bus speed. - dcd_connect(0); -} - -// Connect by enabling internal pull-up resistor on D+/D- -void dcd_connect(uint8_t rhport) -{ - (void)rhport; - TU_LOG2("FT90x connect\r\n"); - - CRITICAL_SECTION_BEGIN - // Is device connected? - if (board_ft90x_vbus()) - { - // Clear/disable address register. - USBD_REG(faddr) = 0; - _ft90x_phy_enable(true); - - // Determine bus speed and signal speed to tusb. - _ft90x_usb_speed(); - } - CRITICAL_SECTION_END; - - // Restore default endpoint state. - _ft90x_reset_edpts(); -} - -// Disconnect by disabling internal pull-up resistor on D+/D- -void dcd_disconnect(uint8_t rhport) -{ - (void)rhport; - TU_LOG2("FT90x disconnect\r\n"); - - // Disable the USB PHY. - _ft90x_phy_enable(false); -} - - -//--------------------------------------------------------------------+ -// Endpoint API -//--------------------------------------------------------------------+ - -// Configure endpoint's registers according to descriptor -bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *ep_desc) -{ - (void)rhport; - uint8_t const ep_number = tu_edpt_number(ep_desc->bEndpointAddress); - uint8_t const ep_dir = tu_edpt_dir(ep_desc->bEndpointAddress); - uint8_t const ep_type = ep_desc->bmAttributes.xfer; - uint16_t const ep_size = ep_desc->wMaxPacketSize.size; - uint16_t ep_buff_size; - uint8_t ep_reg_size = USBD_EP_MAX_SIZE_8; - uint8_t ep_reg_data = 0; - int16_t total_ram; - - TU_LOG2("FT90x endpoint open %d %c\r\n", ep_number, ep_dir?'I':'O'); - - // Check that the requested endpoint number is allowable. - if (ep_number >= USBD_MAX_ENDPOINT_COUNT) - { - TU_LOG1("FT90x endpoint not valid: requested %d max %d\r\n", ep_number, USBD_MAX_ENDPOINT_COUNT); - return false; - } - - // Calculate the physical size of the endpoint as a power of 2. This may be more than - // the requested size. - while (ep_desc->wMaxPacketSize.size > (8 * (1 << ep_reg_size))) - { - ep_reg_size++; - } - if (ep_reg_size > USBD_EP_MAX_SIZE_1024) - { - TU_LOG1("FT90x endpoint size not valid: requested %d max 1024\r\n", ep_desc->wMaxPacketSize.size); - return false; - } - // Calculate actual amount of buffer RAM used by this endpoint. This may be more than the - // requested size. - ep_buff_size = 8 << ep_reg_size; - - if (ep_number > 0) - { - // Set EP cmd parameters... - ep_reg_data |= (ep_reg_size << BIT_USBD_EP_MAX_SIZE); - - if (ep_xfer[ep_number].type != USBD_EP_TYPE_DISABLED) - { - // This could be because an endpoint has been assigned with the same number. - // On FT90x, IN and OUT endpoints may not have the same number. e.g. There - // cannot been an 0x81 and 0x01 endpoint. - TU_LOG1("FT90x endpoint %d already assigned\r\n", ep_number); - return false; - } - - // Check that there is enough buffer RAM to allocate to this new endpoint. - // Available buffer RAM depends on the device revision. - // The IN and OUT buffer RAM should be the same size. - if (ep_dir == USBD_DIR_IN) - total_ram = USBD_RAMTOTAL_IN; - else - total_ram = USBD_RAMTOTAL_OUT; - // Work out how much has been allocated to existing endpoints. - // The total RAM allocated shoudl alsyes be a positive number as this - // algorithm should not let it go below zero. - for (int i = 1; i < USBD_MAX_ENDPOINT_COUNT; i++) - { - if (ep_xfer[i].type != USBD_EP_TYPE_DISABLED) - { - if (ep_xfer[i].dir == ep_dir) - { - total_ram -= ep_xfer[i].buff_size; - } - } - } - // The control endpoint is taken into account as well. - total_ram -= ep_xfer[0].buff_size; - // Make sure we have enough space. The corner case is having zero bytes - // free which means that total_ram must be signed as zero bytes free is - // allowable. - if (total_ram < ep_buff_size) - { - TU_LOG1("FT90x insufficient buffer RAM for endpoint %d\r\n", ep_number); - return false; - } - - // Set the type of this endpoint in the control register. - if (ep_type == USBD_EP_BULK) - ep_reg_data |= (USBD_EP_DIS_BULK << BIT_USBD_EP_CONTROL_DIS); - else if (ep_type == USBD_EP_INT) - ep_reg_data |= (USBD_EP_DIS_INT << BIT_USBD_EP_CONTROL_DIS); - else if (ep_type == USBD_EP_ISOC) - ep_reg_data |= (USBD_EP_DIS_ISO << BIT_USBD_EP_CONTROL_DIS); - // Set the direction of this endpoint in the control register. - if (ep_dir == USBD_DIR_IN) - ep_reg_data |= MASK_USBD_EPxCR_DIR; - // Do not perform double buffering. - //if ( != USBD_DB_OFF) - //ep_reg_data |= MASK_USBD_EPxCR_DB; - // Set the control endpoint for this endpoint. - USBD_EP_CR_REG(ep_number) = ep_reg_data; - TU_LOG2("FT90x endpoint setting %x\r\n", ep_reg_data); - } - else - { - // Set the control register for endpoint zero. - USBD_EP_CR_REG(USBD_EP_0) = (ep_reg_size << BIT_USBD_EP0_MAX_SIZE); - } - - // Store the endpoint characteristics for later reference. - ep_xfer[ep_number].dir = ep_dir; - ep_xfer[ep_number].type = ep_type; - ep_xfer[ep_number].size = ep_size; - ep_xfer[ep_number].buff_size = ep_buff_size; - - CRITICAL_SECTION_BEGIN - // Clear register transaction continuation and signalling state. - ep_xfer[ep_number].valid = 0; - ep_xfer[ep_number].buff_ptr = NULL; - ep_xfer[ep_number].total_size = 0; - ep_xfer[ep_number].remain_size = 0; - CRITICAL_SECTION_END - - return true; -} - -// Close all endpoints. -void dcd_edpt_close_all(uint8_t rhport) -{ - (void)rhport; - // Reset the endpoint configurations. - _ft90x_reset_edpts(); -} - -// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack -bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) -{ - (void)rhport; - uint8_t ep_number = tu_edpt_number(ep_addr); - uint8_t dir = tu_edpt_dir(ep_addr); - uint16_t xfer_bytes; - bool status = false; - - // We will attempt to transfer the buffer. If it is less than or equal to the endpoint - // maximum packet size then the whole buffer will be transferred. If it is larger then - // the interrupt handler will transfer the remainder. - // ep_xfer is used to tell the interrupt handler what to do. - // ep_xfer can be used at interrupt level to continue transfers. - CRITICAL_SECTION_BEGIN - // Transfer currently in progress. - if (ep_xfer[ep_number].valid == 0) - { - status = true; - - ep_xfer[ep_number].total_size = total_bytes; - ep_xfer[ep_number].remain_size = total_bytes; - ep_xfer[ep_number].buff_ptr = buffer; - ep_xfer[ep_number].valid = 1; - - if (ep_number == USBD_EP_0) - { - ep_xfer[USBD_EP_0].dir = dir; - } - else - { - // Enable the interrupt for this endpoint allowing the interrupt handler to report - // continue the transfer and signal completion. - USBD_REG(epie) = USBD_REG(epie) | (1 << ep_number); - } - - if (dir == TUSB_DIR_IN) - { - // For IN transfers send the first packet as a starter. Interrupt handler to complete - // this if it is larger than one packet. - xfer_bytes = _ft90x_edpt_xfer_in(ep_number, buffer, total_bytes); - - ep_xfer[ep_number].buff_ptr += xfer_bytes; - ep_xfer[ep_number].remain_size -= xfer_bytes; - } - } - CRITICAL_SECTION_END - - return status; -} - -// Submit a transfer where is managed by FIFO, When complete dcd_event_xfer_complete() is invoked to notify the stack - optional, however, must be listed in usbd.c -bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff, uint16_t total_bytes) -{ - (void)rhport; - (void)ep_addr; - (void)ff; - (void)total_bytes; - bool status = false; - return status; -} - -// Stall endpoint (non-control endpoint) -void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) -{ - uint8_t ep_number = tu_edpt_number(ep_addr); - (void)rhport; - - CRITICAL_SECTION_BEGIN - if (ep_number == USBD_EP_0) - { - USBD_EP_CR_REG(USBD_EP_0) = USBD_EP_CR_REG(USBD_EP_0) | - MASK_USBD_EP0CR_SDSTL; - } - else - { - USBD_EP_CR_REG(ep_number) = USBD_EP_CR_REG(ep_number) | - MASK_USBD_EPxCR_SDSTL; - USBD_EP_SR_REG(ep_number) = MASK_USBD_EPxSR_CLR_TOGGLE | - MASK_USBD_EPxSR_FIFO_FLUSH; - } - CRITICAL_SECTION_END -} - -// Clear stall (non-control endpoint), data toggle is also reset to DATA0 -void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) -{ - uint8_t ep_number = tu_edpt_number(ep_addr); - (void)rhport; - - if (ep_number > USBD_EP_0) - { - CRITICAL_SECTION_BEGIN - USBD_EP_CR_REG(ep_number) = USBD_EP_CR_REG(ep_number) & - (~MASK_USBD_EPxCR_SDSTL); - USBD_EP_SR_REG(ep_number) = MASK_USBD_EPxSR_CLR_TOGGLE; - - // Allow transfers to restart. - ep_xfer[ep_number].valid = 0; - ep_xfer[ep_number].remain_size = 0; - CRITICAL_SECTION_END - } -} - -// Interrupt handling. - -void _ft90x_usbd_ISR(void) -{ - tud_int_handler(0); // Resolves to dcd_int_handler(). -} - -void dcd_int_handler(uint8_t rhport) -{ - (void)rhport; - - // Read the Common Interrupt Flag Register. - uint8_t cmif = USBD_REG(cmif); - // Read the Endpoint Interrupt Flag Register. -#if defined(__FT930__) - // This is 16 bits on FT93x. - uint16_t epif = USBD_REG(epif); -#else - // This is 8 bits on FT90x. - uint8_t epif = USBD_REG(epif); -#endif - - if (cmif & MASK_USBD_CMIF_ALL) - { - // Clear all CMIF bits. - USBD_REG(cmif) = MASK_USBD_CMIF_ALL; - if (cmif & MASK_USBD_CMIF_PHYIRQ) //Handle PHY interrupt - { - } - if (cmif & MASK_USBD_CMIF_PIDIRQ) //Handle PIDIRQ interrupt - { - } - if (cmif & MASK_USBD_CMIF_CRC16IRQ) //Handle CRC16IRQ interrupt - { - } - if (cmif & MASK_USBD_CMIF_CRC5IRQ) //Handle CRC5 interrupt - { - } - if (cmif & MASK_USBD_CMIF_RSTIRQ) //Handle Reset interrupt - { - // Reset endpoints to default state. - _ft90x_reset_edpts(); - dcd_event_bus_reset(0, _speed, true); - } - if (cmif & MASK_USBD_CMIF_SUSIRQ) //Handle Suspend interrupt - { - dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true); - } - if (cmif & MASK_USBD_CMIF_RESIRQ) //Handle Resume interrupt - { - dcd_event_bus_signal(0, DCD_EVENT_RESUME, true); - } - if (cmif & MASK_USBD_CMIF_SOFIRQ) //Handle SOF interrupt - { - dcd_event_bus_signal(0, DCD_EVENT_SOF, true); - } - } - // Handle endpoint interrupts. - if (epif) - { - uint16_t xfer_bytes; - - // Check for EP0 interrupts pending. - if (epif & MASK_USBD_EPIF_EP0IRQ) - { - // Clear interrupt register. - USBD_REG(epif) = MASK_USBD_EPIF_EP0IRQ; - - // Test for an incoming SETUP request on the control endpoint. - if (USBD_EP_SR_REG(USBD_EP_0) & MASK_USBD_EP0SR_SETUP) - { - // If protocol STALL, End the STALL signalling. - if (USBD_EP_CR_REG(USBD_EP_0) & MASK_USBD_EP0CR_SDSTL) - { - // STALL end. - USBD_EP_CR_REG(USBD_EP_0) = USBD_EP_CR_REG(USBD_EP_0) & - (~MASK_USBD_EP0CR_SDSTL); - // Clear STALL send. - USBD_EP_SR_REG(USBD_EP_0) = MASK_USBD_EP0SR_STALL; - } - - // Host has sent a SETUP packet. Recieve this into the setup packet store. - _ft90x_dusb_out(USBD_EP_0, (uint8_t *)_ft90x_setup_packet, sizeof(USB_device_request)); - - // Send the packet to tinyusb. - dcd_event_setup_received(0, _ft90x_setup_packet, true); - - // Clear the interrupt that signals a SETUP packet is received. - USBD_EP_SR_REG(USBD_EP_0) = (MASK_USBD_EP0SR_SETUP); - - // Allow new transfers on the control endpoint. - ep_xfer[USBD_EP_0].valid = 0; - return; - } - else - { - // Check for a complete or partially complete transfers on EP0. - if (ep_xfer[USBD_EP_0].valid) - { - xfer_bytes = (uint16_t)ep_xfer[USBD_EP_0].total_size; - - // Transfer incoming data from an OUT packet to the buffer supplied. - if (ep_xfer[USBD_EP_0].dir == TUSB_DIR_OUT) - { - xfer_bytes = _ft90x_edpt_xfer_out(USBD_EP_0, (uint8_t *)ep_xfer[USBD_EP_0].buff_ptr, xfer_bytes); - } - // Now signal completion of data packet. - dcd_event_xfer_complete(0, (ep_xfer[USBD_EP_0].dir ? TUSB_DIR_IN_MASK : 0), xfer_bytes, XFER_RESULT_SUCCESS, true); - - // Allow new transfers on the control endpoint. - ep_xfer[USBD_EP_0].valid = 0; - } - } - } - else // !(epif & MASK_USBD_EPIF_EP0IRQ) - { - // Mask out currently disabled endpoints. - epif &= USBD_REG(epie); - - // Handle complete and partially complete transfers for each endpoint. - for (uint8_t ep_number = 1; ep_number < USBD_MAX_ENDPOINT_COUNT; ep_number++) - { - if ((epif & MASK_USBD_EPIF_IRQ(ep_number)) == 0) - { - // No pending interrupt for this endpoint. - continue; - } - - if (ep_xfer[ep_number].valid) - { - xfer_bytes = 0; - uint8_t ep_dirmask = (ep_xfer[ep_number].dir ? TUSB_DIR_IN_MASK : 0); - - // Clear interrupt register for this endpoint. - USBD_REG(epif) = MASK_USBD_EPIF_IRQ(ep_number); - - // Start or continue an OUT transfer. - if (ep_xfer[ep_number].dir == TUSB_DIR_OUT) - { - xfer_bytes = _ft90x_edpt_xfer_out(ep_number, - (uint8_t *)ep_xfer[ep_number].buff_ptr, - (uint16_t)ep_xfer[ep_number].remain_size); - - ep_xfer[ep_number].buff_ptr += xfer_bytes; - ep_xfer[ep_number].remain_size -= xfer_bytes; - } - // continue an IN transfer - else // if (ep_xfer[ep_number].dir == TUSB_DIR_IN) - { - if (ep_xfer[ep_number].remain_size > 0) - { - xfer_bytes = _ft90x_edpt_xfer_in(ep_number, - (uint8_t *)ep_xfer[ep_number].buff_ptr, - (uint16_t)ep_xfer[ep_number].remain_size); - - ep_xfer[ep_number].buff_ptr += xfer_bytes; - ep_xfer[ep_number].remain_size -= xfer_bytes; - } - } - - // When the transfer is complete... - if (ep_xfer[ep_number].remain_size == 0) - { - // Signal tinyUSB. - dcd_event_xfer_complete(0, ep_number | ep_dirmask, ep_xfer[ep_number].total_size, XFER_RESULT_SUCCESS, true); - - // Allow new transfers on this endpoint. - ep_xfer[ep_number].valid = 0; - - // Disable the interrupt for this endpoint now it is complete. - USBD_REG(epie) = USBD_REG(epie) & (~(1 << ep_number)); - } - } - } - } - } -} - -// Power management interrupt handler. -// This handles USB device related power management interrupts only. -void ft90x_usbd_pm_ISR(void) -{ - uint16_t pmcfg = SYS->PMCFG_H; - - // Main interrupt handler is responible for - if (pmcfg & MASK_SYS_PMCFG_DEV_CONN_DEV) - { - // Signal connection interrupt - SYS->PMCFG_H = MASK_SYS_PMCFG_PM_GPIO_IRQ_PEND; - dcd_event_bus_signal(0, DCD_EVENT_RESUME, true); - } - - if (pmcfg & MASK_SYS_PMCFG_DEV_DIS_DEV) - { - // Signal disconnection interrupt - SYS->PMCFG_H = MASK_SYS_PMCFG_PM_GPIO_IRQ_PEND; - dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, true); - } - - if (pmcfg & MASK_SYS_PMCFG_HOST_RST_DEV) - { - // Signal Host Reset interrupt - SYS->PMCFG_H = MASK_SYS_PMCFG_PM_GPIO_IRQ_PEND; - dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true); - } - - if (pmcfg & MASK_SYS_PMCFG_HOST_RESUME_DEV) - { - // Signal Host Resume interrupt - SYS->PMCFG_H = MASK_SYS_PMCFG_PM_GPIO_IRQ_PEND; - if (!(SYS->MSC0CFG & MASK_SYS_MSC0CFG_DEV_RMWAKEUP)) - { - // If we are driving K-state on Device USB port; - // We must maintain the 1ms requirement before resuming the phy - dcd_event_bus_signal(0, DCD_EVENT_RESUME, true); - } - } -} - -#endif diff --git a/src/portable/bridgetek/ft9xx/dcd_ft9xx.c b/src/portable/bridgetek/ft9xx/dcd_ft9xx.c new file mode 100644 index 000000000..69b51fac7 --- /dev/null +++ b/src/portable/bridgetek/ft9xx/dcd_ft9xx.c @@ -0,0 +1,1099 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018, hathach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" +#include "bsp/board.h" + +#if TUSB_OPT_DEVICE_ENABLED && CFG_TUSB_MCU == OPT_MCU_FT90X + +#define USBD_USE_STREAMS + +#include +#include +#include +#include + +#include "device/dcd.h" + +//--------------------------------------------------------------------+ +// MACRO TYPEDEF CONSTANT ENUM DECLARATION +//--------------------------------------------------------------------+ + +// Board code will determine the state of VBUS from USB host. +extern int8_t board_ft90x_vbus(void); + +// Static array to store an incoming SETUP request for processing by tinyusb. +static uint8_t _ft90x_setup_packet[8]; + +struct ft90x_xfer_state +{ + volatile int16_t total_size; // Total transfer size in bytes for this transfer. + volatile int16_t remain_size; // Total remaining in transfer. + volatile uint8_t *buff_ptr; // Pointer to buffer to transmit from or receive to. + volatile uint8_t valid; // Transfer is pending and total_size, remain_size, and buff_ptr are valid. + + uint8_t type; // Endpoint type. Of type USBD_ENDPOINT_TYPE from endpoint descriptor. + uint8_t dir; // Endpoint direction. TUSB_DIR_OUT or TUSB_DIR_IN. For control endpoint this is the current direction. + uint16_t buff_size; // Actual size of buffer RAM used by endpoint. + uint16_t size; // Max packet size for endpoint from endpoint descriptor. +}; +// Endpoint description array for each endpoint. +static struct ft90x_xfer_state ep_xfer[USBD_MAX_ENDPOINT_COUNT]; +// USB speed. +static tusb_speed_t _speed; + +// Interrupt handlers. +void _ft90x_usbd_ISR(void); // Interrupt handler for USB device. +void ft90x_usbd_pm_ISR(void); // Interrupt handler for USB device for power management (called by board). + +// Internal functions forward declarations. +static uint16_t _ft90x_edpt_xfer_out(uint8_t ep_number, uint8_t *buffer, uint16_t xfer_bytes); +static uint16_t _ft90x_edpt_xfer_in(uint8_t ep_number, uint8_t *buffer, uint16_t xfer_bytes); +static void _ft90x_reset_edpts(void); +static inline void _ft90x_phy_enable(bool en); +static void _ft90x_usb_speed(void); +static void _dcd_ft90x_attach(void); +static void _dcd_ft90x_detach(void) __attribute__((unused)); +static uint16_t _ft90x_dusb_in(uint8_t ep_number, const uint8_t *buffer, uint16_t length); +static uint16_t _ft90x_dusb_out(uint8_t ep_number, uint8_t *buffer, uint16_t length); + +// Internal functions. + +// Manage an OUT transfer from the host. +// This can be up-to the maximum packet size of the endpoint. +// Continuation of a transfer beyond the maximum packet size is performed +// by the interrupt handler. +static uint16_t _ft90x_edpt_xfer_out(uint8_t ep_number, uint8_t *buffer, uint16_t xfer_bytes) +{ + //Note: this is called from only the interrupt handler when an OUT transfer is called. + uint16_t ep_size = ep_xfer[ep_number].size; + (void)ep_size; + if (xfer_bytes > ep_size) + { + xfer_bytes = ep_size; + } + + // Wait until the endpoint has finished - it should be complete! + //while (!(USBD_EP_SR_REG(ep_number) & MASK_USBD_EPxSR_OPRDY)) + //; + + // Send the first packet of max packet size + xfer_bytes = _ft90x_dusb_out(ep_number, (uint8_t *)buffer, xfer_bytes); + if (ep_number == USBD_EP_0) + { + // Set flags to indicate data ready. + USBD_EP_SR_REG(USBD_EP_0) = (MASK_USBD_EP0SR_OPRDY); + } + else + { + USBD_EP_SR_REG(ep_number) = (MASK_USBD_EPxSR_OPRDY); + } + + return xfer_bytes; +} + +// Manage an IN transfer to the host. +// This can be up-to the maximum packet size of the endpoint. +// Continuation of a transfer beyond the maximum packet size is performed +// by the interrupt handler. +static uint16_t _ft90x_edpt_xfer_in(uint8_t ep_number, uint8_t *buffer, uint16_t xfer_bytes) +{ + //Note: this may be called from the interrupt handler or from normal code. + uint8_t end = 0; + uint16_t ep_size = ep_xfer[ep_number].size; + (void)ep_size; + if ((xfer_bytes == 0) || (xfer_bytes < ep_size)) + { + end = 1; + } + else + { + xfer_bytes = ep_size; + } + + if (ep_number == USBD_EP_0) + { + // An IN direction SETUP can be interrupted by an OUT packet. + // This will result in a STALL generated by the silicon. + while (USBD_EP_SR_REG(USBD_EP_0) & MASK_USBD_EP0SR_STALL) + { + // Clear the STALL and finish the transaction. + USBD_EP_SR_REG(USBD_EP_0) = (MASK_USBD_EP0SR_STALL); + } + } + else + { + uint8_t sr_reg; + // If there is data to transmit then wait until the IN buffer + // for the endpoint is empty. + do + { + sr_reg = USBD_EP_SR_REG(ep_number); + } while (sr_reg & MASK_USBD_EPxSR_INPRDY); + + } + + xfer_bytes = _ft90x_dusb_in(ep_number, (uint8_t *)buffer, xfer_bytes); + + if (ep_number == USBD_EP_0) + { + if (end) + { + // Set flags to indicate data ready and transfer complete. + USBD_EP_SR_REG(USBD_EP_0) = MASK_USBD_EP0SR_INPRDY | MASK_USBD_EP0SR_DATAEND; + } + else + { + // Set flags to indicate data ready. + USBD_EP_SR_REG(USBD_EP_0) = (MASK_USBD_EP0SR_INPRDY); + } + } + else + { + // Set flags to indicate data ready. + USBD_EP_SR_REG(ep_number) = (MASK_USBD_EPxSR_INPRDY); + } + + return xfer_bytes; +} + +// Reset all endpoints to a default state. +// Control endpoint enabled and ready. All others disabled. +static void _ft90x_reset_edpts(void) +{ + // Disable all endpoints and remove configuration values. + // Clear settings. + tu_memclr(ep_xfer, sizeof(ep_xfer)); + for (int i = 0; i < USBD_MAX_ENDPOINT_COUNT; i++) + { + // Disable hardware. + USBD_EP_CR_REG(i) = 0; + } + + // Setup the control endpoint only. +#if CFG_TUD_ENDPOINT0_SIZE == 64 + USBD_EP_CR_REG(USBD_EP_0) = (USBD_EP0_MAX_SIZE_64 << BIT_USBD_EP0_MAX_SIZE); +#elif CFG_TUD_ENDPOINT0_SIZE == 32 + USBD_EP_CR_REG(USBD_EP_0) = (USBD_EP0_MAX_SIZE_32 << BIT_USBD_EP0_MAX_SIZE); +#elif CFG_TUD_ENDPOINT0_SIZE == 16 + USBD_EP_CR_REG(USBD_EP_0) = (USBD_EP0_MAX_SIZE_16 << BIT_USBD_EP0_MAX_SIZE); +#elif CFG_TUD_ENDPOINT0_SIZE == 8 + USBD_EP_CR_REG(USBD_EP_0) = (USBD_EP0_MAX_SIZE_8 << BIT_USBD_EP0_MAX_SIZE); +#else +#error "CFG_TUD_ENDPOINT0_SIZE must be defined with a value of 8, 16, 32 or 64." +#endif + // Configure the control endpoint. + ep_xfer[USBD_EP_0].size = CFG_TUD_ENDPOINT0_SIZE; + ep_xfer[USBD_EP_0].type = TUSB_XFER_CONTROL; + + // Enable interrupts from USB device control. + USBD_REG(cmie) = MASK_USBD_CMIE_ALL; + // Enable interrupts on EP0. + USBD_REG(epie) = (MASK_USBD_EPIE_EP0IE); +} + +// Enable or disable the USB PHY. +static inline void _ft90x_phy_enable(bool en) +{ + if (en) + SYS->PMCFG_L |= MASK_SYS_PMCFG_DEV_PHY_EN; + else + SYS->PMCFG_L &= ~MASK_SYS_PMCFG_DEV_PHY_EN; +} + +// Safely connect to the USB. +static void _dcd_ft90x_attach(void) +{ + uint8_t reg; + + CRITICAL_SECTION_BEGIN + // Disable device responses. + USBD_REG(faddr) = 0; + + // Reset USB Device. + SYS->MSC0CFG = SYS->MSC0CFG | MASK_SYS_MSC0CFG_DEV_RESET_ALL; + // Disable device connect/disconnect/host reset detection. + SYS->PMCFG_H = MASK_SYS_PMCFG_DEV_DIS_DEV; + SYS->PMCFG_H = MASK_SYS_PMCFG_DEV_CONN_DEV; + SYS->PMCFG_L = SYS->PMCFG_L & (~MASK_SYS_PMCFG_DEV_DETECT_EN); + + // Enable Chip USB device clock/PM configuration. + sys_enable(sys_device_usb_device); + CRITICAL_SECTION_END; + + // Wait a short time to get started. + delayms(1); + + CRITICAL_SECTION_BEGIN + // Turn off the device enable bit. +#if CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED + USBD_REG(fctrl) = 0; +#else + //Set the full speed only bit if required. + USBD_REG(fctrl) = MASK_USBD_FCTRL_MODE_FS_ONLY; +#endif + + // Clear first reset and suspend interrupts. + do + { + reg = USBD_REG(cmif); + USBD_REG(cmif) = reg; + } while (reg); + // Clear any endpoint interrupts. + reg = USBD_REG(epif); + USBD_REG(epif) = reg; + + // Disable all interrupts from USB device control before attaching interrupt. + USBD_REG(cmie) = 0; + CRITICAL_SECTION_END; + + // Enable device connect/disconnect/host reset detection. + // Set device detect and remote wakeup enable interrupt enables. + SYS->PMCFG_L = SYS->PMCFG_L | MASK_SYS_PMCFG_DEV_DETECT_EN; + +#if defined(__FT930__) + // Setup VBUS detect + SYS->MSC0CFG = SYS->MSC0CFG | MASK_SYS_MSC0CFG_USB_VBUS_EN; +#endif +} + +// Gracefully disconnect from the USB. +static void _dcd_ft90x_detach(void) +{ + // Disable device connect/disconnect/host reset detection. + SYS->PMCFG_L = SYS->PMCFG_L & (~MASK_SYS_PMCFG_DEV_DETECT_EN); + +#if defined(__FT930__) + // Disable VBUS detection. + SYS->MSC0CFG = SYS->MSC0CFG & (~MASK_SYS_MSC0CFG_USB_VBUS_EN); +#endif + CRITICAL_SECTION_BEGIN + USBD_REG(epie) = 0; + USBD_REG(cmie) = 0; + CRITICAL_SECTION_END; + + // Disable the USB function. + USBD_REG(fctrl) = 0; + delayms(1); + + // Disable USB PHY + dcd_disconnect(0); + delayms(1); + + // Disable Chip USB device clock/PM configuration. + sys_disable(sys_device_usb_device); + + // Reset USB Device... Needed for Back voltage D+ to be <400mV + SYS->MSC0CFG = SYS->MSC0CFG | MASK_SYS_MSC0CFG_DEV_RESET_ALL; + + delayms(1); + // Set device detect and remote wakeup enable interrupt enables. + SYS->PMCFG_L = SYS->PMCFG_L | MASK_SYS_PMCFG_DEV_DETECT_EN; + +#if defined(__FT930__) + // Setup VBUS detect + SYS->MSC0CFG = SYS->MSC0CFG | MASK_SYS_MSC0CFG_USB_VBUS_EN; +#endif +} + +// Determine the speed of the USB to which we are connected. +// Set the speed of the PHY accordingly. +// High speed can be disabled through CFG_TUSB_RHPORT0_MODE settings. +static void _ft90x_usb_speed(void) +{ + uint8_t fctrl_val; + + // If USB device function is already enabled then disable it. + if (USBD_REG(fctrl) & MASK_USBD_FCTRL_USB_DEV_EN) { + USBD_REG(fctrl) = (USBD_REG(fctrl) & (~MASK_USBD_FCTRL_USB_DEV_EN)); + delayus(200); + } + +#if CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED + + /* Detect high or full speed */ + fctrl_val = MASK_USBD_FCTRL_USB_DEV_EN; +#if defined(__FT900__) + if (!sys_check_ft900_revB())//if 90x series is rev C + { + fctrl_val |= MASK_USBD_FCTRL_IMP_PERF; + } +#endif + USBD_REG(fctrl) = fctrl_val; + +#if defined(__FT930__) + delayus(200); + + _speed = (SYS->MSC0CFG & MASK_SYS_MSC0CFG_HIGH_SPED_MODE) ? + TUSB_SPEED_HIGH : TUSB_SPEED_FULL; +#else /* __FT930__ */ + /* Detection by SOF */ + while (!(USBD_REG(cmif) & MASK_USBD_CMIF_SOFIRQ)); + USBD_REG(cmif) = MASK_USBD_CMIF_SOFIRQ; + delayus(125 + 5); + _speed = (USBD_REG(cmif) & MASK_USBD_CMIF_SOFIRQ) ? + TUSB_SPEED_HIGH : TUSB_SPEED_FULL; + dcd_event_bus_reset(0, _speed, true); + +#endif /* !__FT930__ */ + +#else /* CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED */ + + /* User force set to full speed */ + _speed = TUSB_SPEED_FULL; + fctrl_val = + MASK_USBD_FCTRL_USB_DEV_EN | MASK_USBD_FCTRL_MODE_FS_ONLY; +#if defined(__FT900__) + if (!sys_check_ft900_revB())//if 90x series is rev C + { + fctrl_val |= MASK_USBD_FCTRL_IMP_PERF; + } +#endif + USBD_REG(fctrl) = fctrl_val; + dcd_event_bus_reset(0, _speed, true); + return; + +#endif /* CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED */ +} + +// Send a buffer to the USB IN FIFO. +// When the macro USBD_USE_STREAMS is defined this will stream a buffer of data +// to the FIFO using the most efficient MCU streamout combination. +// If streaming is disabled then it will send each byte of the buffer in turn +// to the FIFO. The is no reason to not stream. +// The total number of bytes sent to the FIFO is returned. +static uint16_t _ft90x_dusb_in(uint8_t ep_number, const uint8_t *buffer, uint16_t length) +{ + uint16_t bytes_read = 0; + uint16_t buff_size = length; + +#ifdef USBD_USE_STREAMS + volatile uint8_t *data_reg; + + data_reg = (uint8_t *)&(USBD->ep[ep_number].epxfifo); + if (buff_size) + { + if (((uint32_t)buffer) % 4 == 0) + { + uint16_t aligned = buff_size & (~3); + uint16_t left = buff_size & 3; + + if (aligned) + { + __asm__ volatile("streamout.l %0,%1,%2" + : + : "r"(data_reg), "r"(buffer), "r"(aligned)); + buffer += aligned; + } + if (left) + { + __asm__ volatile("streamout.b %0,%1,%2" + : + : "r"(data_reg), "r"(buffer), "r"(left)); + } + } + else + { + __asm__ volatile("streamout.b %0,%1,%2" + : + : "r"(data_reg), "r"(buffer), "r"(buff_size)); + } + bytes_read = buff_size; + } +#else // USBD_USE_STREAMS + + bytes_read = buff_size; + while (buff_size--) + { + USBD_EP_FIFO_REG(ep_number) = *buffer++; + }; + +#endif // USBD_USE_STREAMS + + return bytes_read; +} + +// Receive a buffer from the USB OUT FIFO. +// When the macro USBD_USE_STREAMS is defined this will stream from the FIFO +// to a buffer of data using the most efficient MCU streamin combination. +// If streaming is disabled then it will receive each byte from the FIFO in turn +// to the buffer. The is no reason to not stream. +// The total number of bytes received from the FIFO is returned. +static uint16_t _ft90x_dusb_out(uint8_t ep_number, uint8_t *buffer, uint16_t length) +{ +#ifdef USBD_USE_STREAMS + volatile uint8_t *data_reg; +#endif // USBD_USE_STREAMS + uint16_t bytes_read = 0; + uint16_t buff_size = length; + + if (length > 0) + { + if (ep_number == USBD_EP_0) + { + buff_size = USBD_EP_CNT_REG(USBD_EP_0); + } + else + { + if (USBD_EP_SR_REG(ep_number) & (MASK_USBD_EPxSR_OPRDY)) + { + buff_size = USBD_EP_CNT_REG(ep_number); + } + } + } + + // Only read as many bytes as we have space for. + if (buff_size > length) + buff_size = length; + +#ifdef USBD_USE_STREAMS + data_reg = (uint8_t *)&(USBD->ep[ep_number].epxfifo); + if (buff_size) + { + if ((uint32_t)buffer % 4 == 0) + { + uint16_t aligned = buff_size & (~3); + uint16_t left = buff_size & 3; + + if (aligned) + { + __asm__ volatile("streamin.l %0,%1,%2" + : + : "r"(buffer), "r"(data_reg), "r"(aligned)); + buffer += aligned; + } + if (left) + { + __asm__ volatile("streamin.b %0,%1,%2" + : + : "r"(buffer), "r"(data_reg), "r"(left)); + } + } + else + { + __asm__ volatile("streamin.b %0,%1,%2" + : + : "r"(buffer), "r"(data_reg), "r"(buff_size)); + } + bytes_read = buff_size; + } +#else // USBD_USE_STREAMS + + bytes_read = buff_size; + while (buff_size--) + { + *buffer++ = USBD_EP_FIFO_REG(ep_number); + } + +#endif // USBD_USE_STREAMS + + return bytes_read; +} + +/*------------------------------------------------------------------*/ +/* Device API + *------------------------------------------------------------------*/ + +// Initialize controller to device mode +void dcd_init(uint8_t rhport) +{ + TU_LOG2("FT90x initialisation\r\n"); + + _dcd_ft90x_attach(); + + interrupt_attach(interrupt_usb_device, (int8_t)interrupt_usb_device, _ft90x_usbd_ISR); + + dcd_connect(rhport); +} + +// Enable device interrupt +void dcd_int_enable(uint8_t rhport) +{ + (void)rhport; + TU_LOG3("FT90x int enable\r\n"); + + // Peripheral devices interrupt enable. + interrupt_enable_globally(); +} + +// Disable device interrupt +void dcd_int_disable(uint8_t rhport) +{ + (void)rhport; + TU_LOG3("FT90x int disable\r\n"); + + // Peripheral devices interrupt disable. + interrupt_disable_globally(); +} + +// Receive Set Address request, mcu port must also include status IN response +void dcd_set_address(uint8_t rhport, uint8_t dev_addr) +{ + (void)rhport; + (void)dev_addr; + + // Respond with status. There is no checking that the address is in range. + dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0); + + // Set the update bit for the address register. + dev_addr |= 0x80; + + // Modify the address register within a critical section. + CRITICAL_SECTION_BEGIN + { + USBD_REG(faddr) = dev_addr; + } + CRITICAL_SECTION_END; +} + +// Invoked when a control transfer's status stage is complete. +// May help DCD to prepare for next control transfer, this API is optional. +#if 0 // never called +void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request) +{ + (void) rhport; + + if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE && + request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD ) + { + if (request->bRequest == TUSB_REQ_SET_ADDRESS) + { + } + else if (request->bRequest == TUSB_REQ_SET_CONFIGURATION) + { + } + } +} +#endif // 0 + +// Wake up host +void dcd_remote_wakeup(uint8_t rhport) +{ + (void)rhport; + + SYS->MSC0CFG = SYS->MSC0CFG | MASK_SYS_MSC0CFG_DEV_RMWAKEUP; + + // Atleast 2 ms of delay needed for RESUME Data K state. + delayms(2); + + SYS->MSC0CFG &= ~MASK_SYS_MSC0CFG_DEV_RMWAKEUP; + + // Enable USB PHY and determine current bus speed. + dcd_connect(0); +} + +// Connect by enabling internal pull-up resistor on D+/D- +void dcd_connect(uint8_t rhport) +{ + (void)rhport; + TU_LOG2("FT90x connect\r\n"); + + CRITICAL_SECTION_BEGIN + // Is device connected? + if (board_ft90x_vbus()) + { + // Clear/disable address register. + USBD_REG(faddr) = 0; + _ft90x_phy_enable(true); + + // Determine bus speed and signal speed to tusb. + _ft90x_usb_speed(); + } + CRITICAL_SECTION_END; + + // Restore default endpoint state. + _ft90x_reset_edpts(); +} + +// Disconnect by disabling internal pull-up resistor on D+/D- +void dcd_disconnect(uint8_t rhport) +{ + (void)rhport; + TU_LOG2("FT90x disconnect\r\n"); + + // Disable the USB PHY. + _ft90x_phy_enable(false); +} + + +//--------------------------------------------------------------------+ +// Endpoint API +//--------------------------------------------------------------------+ + +// Configure endpoint's registers according to descriptor +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *ep_desc) +{ + (void)rhport; + uint8_t const ep_number = tu_edpt_number(ep_desc->bEndpointAddress); + uint8_t const ep_dir = tu_edpt_dir(ep_desc->bEndpointAddress); + uint8_t const ep_type = ep_desc->bmAttributes.xfer; + uint16_t const ep_size = ep_desc->wMaxPacketSize.size; + uint16_t ep_buff_size; + uint8_t ep_reg_size = USBD_EP_MAX_SIZE_8; + uint8_t ep_reg_data = 0; + int16_t total_ram; + + TU_LOG2("FT90x endpoint open %d %c\r\n", ep_number, ep_dir?'I':'O'); + + // Check that the requested endpoint number is allowable. + if (ep_number >= USBD_MAX_ENDPOINT_COUNT) + { + TU_LOG1("FT90x endpoint not valid: requested %d max %d\r\n", ep_number, USBD_MAX_ENDPOINT_COUNT); + return false; + } + + // Calculate the physical size of the endpoint as a power of 2. This may be more than + // the requested size. + while (ep_desc->wMaxPacketSize.size > (8 * (1 << ep_reg_size))) + { + ep_reg_size++; + } + if (ep_reg_size > USBD_EP_MAX_SIZE_1024) + { + TU_LOG1("FT90x endpoint size not valid: requested %d max 1024\r\n", ep_desc->wMaxPacketSize.size); + return false; + } + // Calculate actual amount of buffer RAM used by this endpoint. This may be more than the + // requested size. + ep_buff_size = 8 << ep_reg_size; + + if (ep_number > 0) + { + // Set EP cmd parameters... + ep_reg_data |= (ep_reg_size << BIT_USBD_EP_MAX_SIZE); + + if (ep_xfer[ep_number].type != USBD_EP_TYPE_DISABLED) + { + // This could be because an endpoint has been assigned with the same number. + // On FT90x, IN and OUT endpoints may not have the same number. e.g. There + // cannot been an 0x81 and 0x01 endpoint. + TU_LOG1("FT90x endpoint %d already assigned\r\n", ep_number); + return false; + } + + // Check that there is enough buffer RAM to allocate to this new endpoint. + // Available buffer RAM depends on the device revision. + // The IN and OUT buffer RAM should be the same size. + if (ep_dir == USBD_DIR_IN) + total_ram = USBD_RAMTOTAL_IN; + else + total_ram = USBD_RAMTOTAL_OUT; + // Work out how much has been allocated to existing endpoints. + // The total RAM allocated shoudl alsyes be a positive number as this + // algorithm should not let it go below zero. + for (int i = 1; i < USBD_MAX_ENDPOINT_COUNT; i++) + { + if (ep_xfer[i].type != USBD_EP_TYPE_DISABLED) + { + if (ep_xfer[i].dir == ep_dir) + { + total_ram -= ep_xfer[i].buff_size; + } + } + } + // The control endpoint is taken into account as well. + total_ram -= ep_xfer[0].buff_size; + // Make sure we have enough space. The corner case is having zero bytes + // free which means that total_ram must be signed as zero bytes free is + // allowable. + if (total_ram < ep_buff_size) + { + TU_LOG1("FT90x insufficient buffer RAM for endpoint %d\r\n", ep_number); + return false; + } + + // Set the type of this endpoint in the control register. + if (ep_type == USBD_EP_BULK) + ep_reg_data |= (USBD_EP_DIS_BULK << BIT_USBD_EP_CONTROL_DIS); + else if (ep_type == USBD_EP_INT) + ep_reg_data |= (USBD_EP_DIS_INT << BIT_USBD_EP_CONTROL_DIS); + else if (ep_type == USBD_EP_ISOC) + ep_reg_data |= (USBD_EP_DIS_ISO << BIT_USBD_EP_CONTROL_DIS); + // Set the direction of this endpoint in the control register. + if (ep_dir == USBD_DIR_IN) + ep_reg_data |= MASK_USBD_EPxCR_DIR; + // Do not perform double buffering. + //if ( != USBD_DB_OFF) + //ep_reg_data |= MASK_USBD_EPxCR_DB; + // Set the control endpoint for this endpoint. + USBD_EP_CR_REG(ep_number) = ep_reg_data; + TU_LOG2("FT90x endpoint setting %x\r\n", ep_reg_data); + } + else + { + // Set the control register for endpoint zero. + USBD_EP_CR_REG(USBD_EP_0) = (ep_reg_size << BIT_USBD_EP0_MAX_SIZE); + } + + // Store the endpoint characteristics for later reference. + ep_xfer[ep_number].dir = ep_dir; + ep_xfer[ep_number].type = ep_type; + ep_xfer[ep_number].size = ep_size; + ep_xfer[ep_number].buff_size = ep_buff_size; + + CRITICAL_SECTION_BEGIN + // Clear register transaction continuation and signalling state. + ep_xfer[ep_number].valid = 0; + ep_xfer[ep_number].buff_ptr = NULL; + ep_xfer[ep_number].total_size = 0; + ep_xfer[ep_number].remain_size = 0; + CRITICAL_SECTION_END + + return true; +} + +// Close all endpoints. +void dcd_edpt_close_all(uint8_t rhport) +{ + (void)rhport; + // Reset the endpoint configurations. + _ft90x_reset_edpts(); +} + +// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack +bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) +{ + (void)rhport; + uint8_t ep_number = tu_edpt_number(ep_addr); + uint8_t dir = tu_edpt_dir(ep_addr); + uint16_t xfer_bytes; + bool status = false; + + // We will attempt to transfer the buffer. If it is less than or equal to the endpoint + // maximum packet size then the whole buffer will be transferred. If it is larger then + // the interrupt handler will transfer the remainder. + // ep_xfer is used to tell the interrupt handler what to do. + // ep_xfer can be used at interrupt level to continue transfers. + CRITICAL_SECTION_BEGIN + // Transfer currently in progress. + if (ep_xfer[ep_number].valid == 0) + { + status = true; + + ep_xfer[ep_number].total_size = total_bytes; + ep_xfer[ep_number].remain_size = total_bytes; + ep_xfer[ep_number].buff_ptr = buffer; + ep_xfer[ep_number].valid = 1; + + if (ep_number == USBD_EP_0) + { + ep_xfer[USBD_EP_0].dir = dir; + } + else + { + // Enable the interrupt for this endpoint allowing the interrupt handler to report + // continue the transfer and signal completion. + USBD_REG(epie) = USBD_REG(epie) | (1 << ep_number); + } + + if (dir == TUSB_DIR_IN) + { + // For IN transfers send the first packet as a starter. Interrupt handler to complete + // this if it is larger than one packet. + xfer_bytes = _ft90x_edpt_xfer_in(ep_number, buffer, total_bytes); + + ep_xfer[ep_number].buff_ptr += xfer_bytes; + ep_xfer[ep_number].remain_size -= xfer_bytes; + } + } + CRITICAL_SECTION_END + + return status; +} + +// Submit a transfer where is managed by FIFO, When complete dcd_event_xfer_complete() is invoked to notify the stack - optional, however, must be listed in usbd.c +bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff, uint16_t total_bytes) +{ + (void)rhport; + (void)ep_addr; + (void)ff; + (void)total_bytes; + bool status = false; + return status; +} + +// Stall endpoint (non-control endpoint) +void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) +{ + uint8_t ep_number = tu_edpt_number(ep_addr); + (void)rhport; + + CRITICAL_SECTION_BEGIN + if (ep_number == USBD_EP_0) + { + USBD_EP_CR_REG(USBD_EP_0) = USBD_EP_CR_REG(USBD_EP_0) | + MASK_USBD_EP0CR_SDSTL; + } + else + { + USBD_EP_CR_REG(ep_number) = USBD_EP_CR_REG(ep_number) | + MASK_USBD_EPxCR_SDSTL; + USBD_EP_SR_REG(ep_number) = MASK_USBD_EPxSR_CLR_TOGGLE | + MASK_USBD_EPxSR_FIFO_FLUSH; + } + CRITICAL_SECTION_END +} + +// Clear stall (non-control endpoint), data toggle is also reset to DATA0 +void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) +{ + uint8_t ep_number = tu_edpt_number(ep_addr); + (void)rhport; + + if (ep_number > USBD_EP_0) + { + CRITICAL_SECTION_BEGIN + USBD_EP_CR_REG(ep_number) = USBD_EP_CR_REG(ep_number) & + (~MASK_USBD_EPxCR_SDSTL); + USBD_EP_SR_REG(ep_number) = MASK_USBD_EPxSR_CLR_TOGGLE; + + // Allow transfers to restart. + ep_xfer[ep_number].valid = 0; + ep_xfer[ep_number].remain_size = 0; + CRITICAL_SECTION_END + } +} + +// Interrupt handling. + +void _ft90x_usbd_ISR(void) +{ + tud_int_handler(0); // Resolves to dcd_int_handler(). +} + +void dcd_int_handler(uint8_t rhport) +{ + (void)rhport; + + // Read the Common Interrupt Flag Register. + uint8_t cmif = USBD_REG(cmif); + // Read the Endpoint Interrupt Flag Register. +#if defined(__FT930__) + // This is 16 bits on FT93x. + uint16_t epif = USBD_REG(epif); +#else + // This is 8 bits on FT90x. + uint8_t epif = USBD_REG(epif); +#endif + + if (cmif & MASK_USBD_CMIF_ALL) + { + // Clear all CMIF bits. + USBD_REG(cmif) = MASK_USBD_CMIF_ALL; + if (cmif & MASK_USBD_CMIF_PHYIRQ) //Handle PHY interrupt + { + } + if (cmif & MASK_USBD_CMIF_PIDIRQ) //Handle PIDIRQ interrupt + { + } + if (cmif & MASK_USBD_CMIF_CRC16IRQ) //Handle CRC16IRQ interrupt + { + } + if (cmif & MASK_USBD_CMIF_CRC5IRQ) //Handle CRC5 interrupt + { + } + if (cmif & MASK_USBD_CMIF_RSTIRQ) //Handle Reset interrupt + { + // Reset endpoints to default state. + _ft90x_reset_edpts(); + dcd_event_bus_reset(0, _speed, true); + } + if (cmif & MASK_USBD_CMIF_SUSIRQ) //Handle Suspend interrupt + { + dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true); + } + if (cmif & MASK_USBD_CMIF_RESIRQ) //Handle Resume interrupt + { + dcd_event_bus_signal(0, DCD_EVENT_RESUME, true); + } + if (cmif & MASK_USBD_CMIF_SOFIRQ) //Handle SOF interrupt + { + dcd_event_bus_signal(0, DCD_EVENT_SOF, true); + } + } + // Handle endpoint interrupts. + if (epif) + { + uint16_t xfer_bytes; + + // Check for EP0 interrupts pending. + if (epif & MASK_USBD_EPIF_EP0IRQ) + { + // Clear interrupt register. + USBD_REG(epif) = MASK_USBD_EPIF_EP0IRQ; + + // Test for an incoming SETUP request on the control endpoint. + if (USBD_EP_SR_REG(USBD_EP_0) & MASK_USBD_EP0SR_SETUP) + { + // If protocol STALL, End the STALL signalling. + if (USBD_EP_CR_REG(USBD_EP_0) & MASK_USBD_EP0CR_SDSTL) + { + // STALL end. + USBD_EP_CR_REG(USBD_EP_0) = USBD_EP_CR_REG(USBD_EP_0) & + (~MASK_USBD_EP0CR_SDSTL); + // Clear STALL send. + USBD_EP_SR_REG(USBD_EP_0) = MASK_USBD_EP0SR_STALL; + } + + // Host has sent a SETUP packet. Recieve this into the setup packet store. + _ft90x_dusb_out(USBD_EP_0, (uint8_t *)_ft90x_setup_packet, sizeof(USB_device_request)); + + // Send the packet to tinyusb. + dcd_event_setup_received(0, _ft90x_setup_packet, true); + + // Clear the interrupt that signals a SETUP packet is received. + USBD_EP_SR_REG(USBD_EP_0) = (MASK_USBD_EP0SR_SETUP); + + // Allow new transfers on the control endpoint. + ep_xfer[USBD_EP_0].valid = 0; + return; + } + else + { + // Check for a complete or partially complete transfers on EP0. + if (ep_xfer[USBD_EP_0].valid) + { + xfer_bytes = (uint16_t)ep_xfer[USBD_EP_0].total_size; + + // Transfer incoming data from an OUT packet to the buffer supplied. + if (ep_xfer[USBD_EP_0].dir == TUSB_DIR_OUT) + { + xfer_bytes = _ft90x_edpt_xfer_out(USBD_EP_0, (uint8_t *)ep_xfer[USBD_EP_0].buff_ptr, xfer_bytes); + } + // Now signal completion of data packet. + dcd_event_xfer_complete(0, (ep_xfer[USBD_EP_0].dir ? TUSB_DIR_IN_MASK : 0), xfer_bytes, XFER_RESULT_SUCCESS, true); + + // Allow new transfers on the control endpoint. + ep_xfer[USBD_EP_0].valid = 0; + } + } + } + else // !(epif & MASK_USBD_EPIF_EP0IRQ) + { + // Mask out currently disabled endpoints. + epif &= USBD_REG(epie); + + // Handle complete and partially complete transfers for each endpoint. + for (uint8_t ep_number = 1; ep_number < USBD_MAX_ENDPOINT_COUNT; ep_number++) + { + if ((epif & MASK_USBD_EPIF_IRQ(ep_number)) == 0) + { + // No pending interrupt for this endpoint. + continue; + } + + if (ep_xfer[ep_number].valid) + { + xfer_bytes = 0; + uint8_t ep_dirmask = (ep_xfer[ep_number].dir ? TUSB_DIR_IN_MASK : 0); + + // Clear interrupt register for this endpoint. + USBD_REG(epif) = MASK_USBD_EPIF_IRQ(ep_number); + + // Start or continue an OUT transfer. + if (ep_xfer[ep_number].dir == TUSB_DIR_OUT) + { + xfer_bytes = _ft90x_edpt_xfer_out(ep_number, + (uint8_t *)ep_xfer[ep_number].buff_ptr, + (uint16_t)ep_xfer[ep_number].remain_size); + + ep_xfer[ep_number].buff_ptr += xfer_bytes; + ep_xfer[ep_number].remain_size -= xfer_bytes; + } + // continue an IN transfer + else // if (ep_xfer[ep_number].dir == TUSB_DIR_IN) + { + if (ep_xfer[ep_number].remain_size > 0) + { + xfer_bytes = _ft90x_edpt_xfer_in(ep_number, + (uint8_t *)ep_xfer[ep_number].buff_ptr, + (uint16_t)ep_xfer[ep_number].remain_size); + + ep_xfer[ep_number].buff_ptr += xfer_bytes; + ep_xfer[ep_number].remain_size -= xfer_bytes; + } + } + + // When the transfer is complete... + if (ep_xfer[ep_number].remain_size == 0) + { + // Signal tinyUSB. + dcd_event_xfer_complete(0, ep_number | ep_dirmask, ep_xfer[ep_number].total_size, XFER_RESULT_SUCCESS, true); + + // Allow new transfers on this endpoint. + ep_xfer[ep_number].valid = 0; + + // Disable the interrupt for this endpoint now it is complete. + USBD_REG(epie) = USBD_REG(epie) & (~(1 << ep_number)); + } + } + } + } + } +} + +// Power management interrupt handler. +// This handles USB device related power management interrupts only. +void ft90x_usbd_pm_ISR(void) +{ + uint16_t pmcfg = SYS->PMCFG_H; + + // Main interrupt handler is responible for + if (pmcfg & MASK_SYS_PMCFG_DEV_CONN_DEV) + { + // Signal connection interrupt + SYS->PMCFG_H = MASK_SYS_PMCFG_PM_GPIO_IRQ_PEND; + dcd_event_bus_signal(0, DCD_EVENT_RESUME, true); + } + + if (pmcfg & MASK_SYS_PMCFG_DEV_DIS_DEV) + { + // Signal disconnection interrupt + SYS->PMCFG_H = MASK_SYS_PMCFG_PM_GPIO_IRQ_PEND; + dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, true); + } + + if (pmcfg & MASK_SYS_PMCFG_HOST_RST_DEV) + { + // Signal Host Reset interrupt + SYS->PMCFG_H = MASK_SYS_PMCFG_PM_GPIO_IRQ_PEND; + dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true); + } + + if (pmcfg & MASK_SYS_PMCFG_HOST_RESUME_DEV) + { + // Signal Host Resume interrupt + SYS->PMCFG_H = MASK_SYS_PMCFG_PM_GPIO_IRQ_PEND; + if (!(SYS->MSC0CFG & MASK_SYS_MSC0CFG_DEV_RMWAKEUP)) + { + // If we are driving K-state on Device USB port; + // We must maintain the 1ms requirement before resuming the phy + dcd_event_bus_signal(0, DCD_EVENT_RESUME, true); + } + } +} + +#endif -- cgit v1.3.1