From c0e19ef5339ac051413f3b4540440b6cbb65dbd4 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 7 Jun 2024 12:21:01 +0700 Subject: [PATCH 01/14] add tinyusb driver for ch32: usbfs, stm32fsdev, usbhs --- src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c | 1227 +++++++++++++++++ src/portable/st/stm32_fsdev/fsdev_ch32.h | 232 ++++ src/portable/st/stm32_fsdev/fsdev_common.h | 391 ++++++ src/portable/st/stm32_fsdev/fsdev_stm32.h | 292 ++++ src/portable/wch/ch32_usbfs_reg.h | 111 ++ src/portable/wch/ch32_usbhs_reg.h | 383 +++++ src/portable/wch/dcd_ch32_usbfs.c | 344 +++++ src/portable/wch/dcd_ch32_usbhs.c | 420 ++++++ 8 files changed, 3400 insertions(+) create mode 100644 src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c create mode 100644 src/portable/st/stm32_fsdev/fsdev_ch32.h create mode 100644 src/portable/st/stm32_fsdev/fsdev_common.h create mode 100644 src/portable/st/stm32_fsdev/fsdev_stm32.h create mode 100644 src/portable/wch/ch32_usbfs_reg.h create mode 100644 src/portable/wch/ch32_usbhs_reg.h create mode 100644 src/portable/wch/dcd_ch32_usbfs.c create mode 100644 src/portable/wch/dcd_ch32_usbhs.c diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c new file mode 100644 index 00000000..9ce37f99 --- /dev/null +++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c @@ -0,0 +1,1227 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Nathan Conrad + * + * Portions: + * Copyright (c) 2016 STMicroelectronics + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * Copyright (c) 2022 Simon Küppers (skuep) + * Copyright (c) 2022 HiFiPhile + * + * 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. + */ + +/********************************************** + * This driver has been tested with the following MCUs: + * - F070, F072, L053, F042F6 + * + * It also should work with minimal changes for any ST MCU with an "USB A"/"PCD"/"HCD" peripheral. This + * covers: + * + * F04x, F072, F078, 070x6/B 1024 byte buffer + * F102, F103 512 byte buffer; no internal D+ pull-up (maybe many more changes?) + * F302xB/C, F303xB/C, F373 512 byte buffer; no internal D+ pull-up + * F302x6/8, F302xD/E2, F303xD/E 1024 byte buffer; no internal D+ pull-up + * L0x2, L0x3 1024 byte buffer + * L1 512 byte buffer + * L4x2, L4x3 1024 byte buffer + * G0 2048 byte buffer + * + * To use this driver, you must: + * - If you are using a device with crystal-less USB, set up the clock recovery system (CRS) + * - Remap pins to be D+/D- on devices that they are shared (for example: F042Fx) + * - This is different to the normal "alternate function" GPIO interface, needs to go through SYSCFG->CFGRx register + * - Enable USB clock; Perhaps use __HAL_RCC_USB_CLK_ENABLE(); + * - (Optionally configure GPIO HAL to tell it the USB driver is using the USB pins) + * - call tusb_init(); + * - periodically call tusb_task(); + * + * Assumptions of the driver: + * - You are not using CAN (it must share the packet buffer) + * - APB clock is >= 10 MHz + * - On some boards, series resistors are required, but not on others. + * - On some boards, D+ pull up resistor (1.5kohm) is required, but not on others. + * - You don't have long-running interrupts; some USB packets must be quickly responded to. + * - You have the ST CMSIS library linked into the project. HAL is not used. + * + * Current driver limitations (i.e., a list of features for you to add): + * - STALL handled, but not tested. + * - Does it work? No clue. + * - All EP BTABLE buffers are created based on max packet size of first EP opened with that address. + * - Packet buffer memory is copied in the interrupt. + * - This is better for performance, but means interrupts are disabled for longer + * - DMA may be the best choice, but it could also be pushed to the USBD task. + * - No double-buffering + * - No DMA + * - Minimal error handling + * - Perhaps error interrupts should be reported to the stack, or cause a device reset? + * - Assumes a single USB peripheral; I think that no hardware has multiple so this is fine. + * - Add a callback for enabling/disabling the D+ PU on devices without an internal PU. + * - F3 models use three separate interrupts. I think we could only use the LP interrupt for + * everything? However, the interrupts are configurable so the DisableInt and EnableInt + * below functions could be adjusting the wrong interrupts (if they had been reconfigured) + * - LPM is not used correctly, or at all? + * + * USB documentation and Reference implementations + * - STM32 Reference manuals + * - STM32 USB Hardware Guidelines AN4879 + * + * - STM32 HAL (much of this driver is based on this) + * - libopencm3/lib/stm32/common/st_usbfs_core.c + * - Keil USB Device http://www.keil.com/pack/doc/mw/USB/html/group__usbd.html + * + * - YouTube OpenTechLab 011; https://www.youtube.com/watch?v=4FOkJLp_PUw + * + * Advantages over HAL driver: + * - Tiny (saves RAM, assumes a single USB peripheral) + * + * Notes: + * - The buffer table is allocated as endpoints are opened. The allocation is only + * cleared when the device is reset. This may be bad if the USB device needs + * to be reconfigured. + */ + +#include "tusb_option.h" + +#if CFG_TUD_ENABLED && defined(TUP_USBIP_FSDEV) && \ + !(defined(TUP_USBIP_FSDEV_CH32) && CFG_TUD_WCH_USBIP_FSDEV == 0) + +#include "device/dcd.h" + +#if defined(TUP_USBIP_FSDEV_STM32) + // Undefine to reduce the dependence on HAL + #undef USE_HAL_DRIVER + #include "fsdev_stm32.h" +#elif defined(TUP_USBIP_FSDEV_CH32) + #include "fsdev_ch32.h" +#else + #error "Unknown USB IP" +#endif + +#include "fsdev_common.h" + +//--------------------------------------------------------------------+ +// Configuration +//--------------------------------------------------------------------+ + +// hardware limit endpoint +#define FSDEV_EP_COUNT 8 + +// If sharing with CAN, one can set this to be non-zero to give CAN space where it wants it +// Both of these MUST be a multiple of 2, and are in byte units. +#ifndef DCD_STM32_BTABLE_BASE +#define DCD_STM32_BTABLE_BASE 0U +#endif + +#ifndef DCD_STM32_BTABLE_SIZE +#define DCD_STM32_BTABLE_SIZE (FSDEV_PMA_SIZE - DCD_STM32_BTABLE_BASE) +#endif + +TU_VERIFY_STATIC(((DCD_STM32_BTABLE_BASE) + (DCD_STM32_BTABLE_SIZE)) <= (FSDEV_PMA_SIZE), "BTABLE does not fit in PMA RAM"); +TU_VERIFY_STATIC(((DCD_STM32_BTABLE_BASE) % 8) == 0, "BTABLE base must be aligned to 8 bytes"); + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ + +// One of these for every EP IN & OUT, uses a bit of RAM.... +typedef struct { + uint8_t *buffer; + tu_fifo_t *ff; + uint16_t total_len; + uint16_t queued_len; + uint16_t max_packet_size; + uint8_t ep_idx; // index for USB_EPnR register + bool iso_in_sending; // Workaround for ISO IN EP doesn't have interrupt mask +} xfer_ctl_t; + +// EP allocator +typedef struct { + uint8_t ep_num; + uint8_t ep_type; + bool allocated[2]; +} ep_alloc_t; + +static xfer_ctl_t xfer_status[CFG_TUD_ENDPPOINT_MAX][2]; + +static ep_alloc_t ep_alloc_status[FSDEV_EP_COUNT]; + +static TU_ATTR_ALIGNED(4) uint32_t _setup_packet[6]; + +static uint8_t remoteWakeCountdown; // When wake is requested + +//--------------------------------------------------------------------+ +// Prototypes +//--------------------------------------------------------------------+ + +// into the stack. +static void dcd_handle_bus_reset(void); +static void dcd_transmit_packet(xfer_ctl_t *xfer, uint16_t ep_ix); +static bool edpt_xfer(uint8_t rhport, uint8_t ep_addr); +static void dcd_ep_ctr_handler(void); + +// PMA allocation/access +static uint16_t ep_buf_ptr; ///< Points to first free memory location +static uint32_t dcd_pma_alloc(uint16_t length, bool dbuf); +static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type); +static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t wNBytes); +static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t wNBytes); + +static bool dcd_write_packet_memory_ff(tu_fifo_t *ff, uint16_t dst, uint16_t wNBytes); +static bool dcd_read_packet_memory_ff(tu_fifo_t *ff, uint16_t src, uint16_t wNBytes); + +//--------------------------------------------------------------------+ +// Inline helper +//--------------------------------------------------------------------+ + +TU_ATTR_ALWAYS_INLINE static inline xfer_ctl_t *xfer_ctl_ptr(uint32_t ep_addr) +{ + uint8_t epnum = tu_edpt_number(ep_addr); + uint8_t dir = tu_edpt_dir(ep_addr); + // Fix -Werror=null-dereference + TU_ASSERT(epnum < CFG_TUD_ENDPPOINT_MAX, &xfer_status[0][0]); + + return &xfer_status[epnum][dir]; +} + +//--------------------------------------------------------------------+ +// Controller API +//--------------------------------------------------------------------+ + +void dcd_init(uint8_t rhport) +{ + /* Clocks should already be enabled */ + /* Use __HAL_RCC_USB_CLK_ENABLE(); to enable the clocks before calling this function */ + + /* The RM mentions to use a special ordering of PDWN and FRES, but this isn't done in HAL. + * Here, the RM is followed. */ + + for (uint32_t i = 0; i < 200; i++) { // should be a few us + asm("NOP"); + } + // Perform USB peripheral reset + USB->CNTR = USB_CNTR_FRES | USB_CNTR_PDWN; + for (uint32_t i = 0; i < 200; i++) { // should be a few us + asm("NOP"); + } + + USB->CNTR &= ~USB_CNTR_PDWN; + + // Wait startup time, for F042 and F070, this is <= 1 us. + for (uint32_t i = 0; i < 200; i++) { // should be a few us + asm("NOP"); + } + USB->CNTR = 0; // Enable USB + +#if !defined(STM32G0) && !defined(STM32H5) // BTABLE register does not exist any more on STM32G0, it is fixed to USB SRAM base address + USB->BTABLE = DCD_STM32_BTABLE_BASE; +#endif + USB->ISTR = 0; // Clear pending interrupts + + // Reset endpoints to disabled + for (uint32_t i = 0; i < FSDEV_EP_COUNT; i++) { + // This doesn't clear all bits since some bits are "toggle", but does set the type to DISABLED. + pcd_set_endpoint(USB, i, 0u); + } + + USB->CNTR |= USB_CNTR_RESETM | USB_CNTR_ESOFM | USB_CNTR_CTRM | USB_CNTR_SUSPM | USB_CNTR_WKUPM; + dcd_handle_bus_reset(); + + // Enable pull-up if supported + dcd_connect(rhport); +} + + +void dcd_sof_enable(uint8_t rhport, bool en) +{ + (void)rhport; + (void)en; + + if (en) { + USB->CNTR |= USB_CNTR_SOFM; + } else { + USB->CNTR &= ~USB_CNTR_SOFM; + } +} + +// 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 + dcd_edpt_xfer(rhport, TUSB_DIR_IN_MASK | 0x00, NULL, 0); + + // DCD can only set address after status for this request is complete. + // do it at dcd_edpt0_status_complete() +} + +void dcd_remote_wakeup(uint8_t rhport) +{ + (void)rhport; + + USB->CNTR |= USB_CNTR_RESUME; + remoteWakeCountdown = 4u; // required to be 1 to 15 ms, ESOF should trigger every 1ms. +} + +static const tusb_desc_endpoint_t ep0OUT_desc = { + .bLength = sizeof(tusb_desc_endpoint_t), + .bDescriptorType = TUSB_DESC_ENDPOINT, + .bEndpointAddress = 0x00, + .bmAttributes = {.xfer = TUSB_XFER_CONTROL}, + .wMaxPacketSize = CFG_TUD_ENDPOINT0_SIZE, + .bInterval = 0 +}; + +static const tusb_desc_endpoint_t ep0IN_desc = { + .bLength = sizeof(tusb_desc_endpoint_t), + .bDescriptorType = TUSB_DESC_ENDPOINT, + .bEndpointAddress = 0x80, + .bmAttributes = {.xfer = TUSB_XFER_CONTROL}, + .wMaxPacketSize = CFG_TUD_ENDPOINT0_SIZE, + .bInterval = 0 +}; + +static void dcd_handle_bus_reset(void) +{ + USB->DADDR = 0u; // disable USB peripheral by clearing the EF flag + + for (uint32_t i = 0; i < FSDEV_EP_COUNT; i++) { + // Clear EP allocation status + ep_alloc_status[i].ep_num = 0xFF; + ep_alloc_status[i].ep_type = 0xFF; + ep_alloc_status[i].allocated[0] = false; + ep_alloc_status[i].allocated[1] = false; + } + + // Reset PMA allocation + ep_buf_ptr = DCD_STM32_BTABLE_BASE + 8 * CFG_TUD_ENDPPOINT_MAX; + + dcd_edpt_open(0, &ep0OUT_desc); + dcd_edpt_open(0, &ep0IN_desc); + + USB->DADDR = USB_DADDR_EF; // Set enable flag, and leaving the device address as zero. +} + +// Handle CTR interrupt for the TX/IN direction +// +// Upon call, (wIstr & USB_ISTR_DIR) == 0U +static void dcd_ep_ctr_tx_handler(uint32_t wIstr) +{ + uint32_t EPindex = wIstr & USB_ISTR_EP_ID; + uint32_t wEPRegVal = pcd_get_endpoint(USB, EPindex); + uint8_t ep_addr = (wEPRegVal & USB_EPADDR_FIELD) | TUSB_DIR_IN_MASK; + + // Verify the CTR_TX bit is set. This was in the ST Micro code, + // but I'm not sure it's actually necessary? + if ((wEPRegVal & USB_EP_CTR_TX) == 0U) { + return; + } + + /* clear int flag */ + pcd_clear_tx_ep_ctr(USB, EPindex); + + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); + + if ((wEPRegVal & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS) { + // Ignore spurious interrupts that we don't schedule + // host can send IN token while there is no data to send, since ISO does not have NAK + // this will result to zero length packet --> trigger interrupt (which cannot be masked) + if (!xfer->iso_in_sending) { + return; + } + xfer->iso_in_sending = false; + + if (wEPRegVal & USB_EP_DTOG_TX) { + pcd_set_ep_tx_dbuf0_cnt(USB, EPindex, 0); + } else { + pcd_set_ep_tx_dbuf1_cnt(USB, EPindex, 0); + } + } + + if ((xfer->total_len != xfer->queued_len)) { + dcd_transmit_packet(xfer, EPindex); + } else { + dcd_event_xfer_complete(0, ep_addr, xfer->total_len, XFER_RESULT_SUCCESS, true); + } +} + +// Handle CTR interrupt for the RX/OUT direction +// Upon call, (wIstr & USB_ISTR_DIR) == 0U +static void dcd_ep_ctr_rx_handler(uint32_t wIstr) +{ +#ifdef FSDEV_BUS_32BIT + /* https://www.st.com/resource/en/errata_sheet/es0561-stm32h503cbebkbrb-device-errata-stmicroelectronics.pdf + * From STM32H503 errata 2.15.1: Buffer description table update completes after CTR interrupt triggers + * Description: + * - During OUT transfers, the correct transfer interrupt (CTR) is triggered a little before the last USB SRAM accesses + * have completed. If the software responds quickly to the interrupt, the full buffer contents may not be correct. + * Workaround: + * - Software should ensure that a small delay is included before accessing the SRAM contents. This delay + * should be 800 ns in Full Speed mode and 6.4 μs in Low Speed mode + * - Since H5 can run up to 250Mhz -> 1 cycle = 4ns. Per errata, we need to wait 200 cycles. Though executing code + * also takes time, so we'll wait 60 cycles (count = 20). + * - Since Low Speed mode is not supported/popular, we will ignore it for now. + * + * Note: this errata also seems to apply to G0, U5, H5 etc. + */ + volatile uint32_t cycle_count = 20; // defined as PCD_RX_PMA_CNT in stm32 hal_driver + while (cycle_count > 0U) { + cycle_count--; // each count take 3 cycles (1 for sub, jump, and compare) + } +#endif + + uint32_t EPindex = wIstr & USB_ISTR_EP_ID; + uint32_t wEPRegVal = pcd_get_endpoint(USB, EPindex); + uint8_t ep_addr = wEPRegVal & USB_EPADDR_FIELD; + + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); + + // Verify the CTR_RX bit is set. This was in the ST Micro code, + // but I'm not sure it's actually necessary? + if ((wEPRegVal & USB_EP_CTR_RX) == 0U) { + return; + } + + if ((ep_addr == 0U) && ((wEPRegVal & USB_EP_SETUP) != 0U)) { + /* Setup packet */ + uint32_t count = pcd_get_ep_rx_cnt(USB, EPindex); + // Setup packet should always be 8 bytes. If not, ignore it, and try again. + if (count == 8) { + // Must reset EP to NAK (in case it had been stalling) (though, maybe too late here) + pcd_set_ep_rx_status(USB, 0u, USB_EP_RX_NAK); + pcd_set_ep_tx_status(USB, 0u, USB_EP_TX_NAK); +#ifdef FSDEV_BUS_32BIT + dcd_event_setup_received(0, (uint8_t *)(USB_PMAADDR + pcd_get_ep_rx_address(USB, EPindex)), true); +#else + // The setup_received function uses memcpy, so this must first copy the setup data into + // user memory, to allow for the 32-bit access that memcpy performs. + uint8_t userMemBuf[8]; + dcd_read_packet_memory(userMemBuf, pcd_get_ep_rx_address(USB, EPindex), 8); + dcd_event_setup_received(0, (uint8_t *)userMemBuf, true); +#endif + } + } else { + // Clear RX CTR interrupt flag + if (ep_addr != 0u) { + pcd_clear_rx_ep_ctr(USB, EPindex); + } + + uint32_t count; + uint16_t addr; + /* Read from correct register when ISOCHRONOUS (double buffered) */ + if ((wEPRegVal & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS) { + if (wEPRegVal & USB_EP_DTOG_RX) { + count = pcd_get_ep_dbuf0_cnt(USB, EPindex); + addr = pcd_get_ep_dbuf0_address(USB, EPindex); + } else { + count = pcd_get_ep_dbuf1_cnt(USB, EPindex); + addr = pcd_get_ep_dbuf1_address(USB, EPindex); + } + } else { + count = pcd_get_ep_rx_cnt(USB, EPindex); + addr = pcd_get_ep_rx_address(USB, EPindex); + } + + TU_ASSERT(count <= xfer->max_packet_size, /**/); + + if (count != 0U) { + if (xfer->ff) { + dcd_read_packet_memory_ff(xfer->ff, addr, count); + } else { + dcd_read_packet_memory(&(xfer->buffer[xfer->queued_len]), addr, count); + } + + xfer->queued_len = (uint16_t)(xfer->queued_len + count); + } + + if ((count < xfer->max_packet_size) || (xfer->queued_len == xfer->total_len)) { + // all bytes received or short packet + dcd_event_xfer_complete(0, ep_addr, xfer->queued_len, XFER_RESULT_SUCCESS, true); + } else { + /* Set endpoint active again for receiving more data. + * Note that isochronous endpoints stay active always */ + if ((wEPRegVal & USB_EP_TYPE_MASK) != USB_EP_ISOCHRONOUS) { + uint16_t remaining = xfer->total_len - xfer->queued_len; + uint16_t cnt = tu_min16(remaining, xfer->max_packet_size); + pcd_set_ep_rx_cnt(USB, EPindex, cnt); + } + pcd_set_ep_rx_status(USB, EPindex, USB_EP_RX_VALID); + } + } + + // For EP0, prepare to receive another SETUP packet. + // Clear CTR last so that a new packet does not overwrite the packing being read. + // (Based on the docs, it seems SETUP will always be accepted after CTR is cleared) + if (ep_addr == 0u) { + // Always be prepared for a status packet... + pcd_set_ep_rx_cnt(USB, EPindex, CFG_TUD_ENDPOINT0_SIZE); + pcd_clear_rx_ep_ctr(USB, EPindex); + } +} + +static void dcd_ep_ctr_handler(void) +{ + uint32_t wIstr; + + /* stay in loop while pending interrupts */ + while (((wIstr = USB->ISTR) & USB_ISTR_CTR) != 0U) { + if ((wIstr & USB_ISTR_DIR) == 0U) { + /* TX/IN */ + dcd_ep_ctr_tx_handler(wIstr); + } else { + /* RX/OUT*/ + dcd_ep_ctr_rx_handler(wIstr); + } + } +} + +void dcd_int_handler(uint8_t rhport) +{ + (void)rhport; + + uint32_t int_status = USB->ISTR; + // const uint32_t handled_ints = USB_ISTR_CTR | USB_ISTR_RESET | USB_ISTR_WKUP + // | USB_ISTR_SUSP | USB_ISTR_SOF | USB_ISTR_ESOF; + // unused IRQs: (USB_ISTR_PMAOVR | USB_ISTR_ERR | USB_ISTR_L1REQ ) + + // The ST driver loops here on the CTR bit, but that loop has been moved into the + // dcd_ep_ctr_handler(), so less need to loop here. The other interrupts shouldn't + // be triggered repeatedly. + + /* Put SOF flag at the beginning of ISR in case to get least amount of jitter if it is used for timing purposes */ + if (int_status & USB_ISTR_SOF) { + USB->ISTR = (fsdev_bus_t)~USB_ISTR_SOF; + dcd_event_sof(0, USB->FNR & USB_FNR_FN, true); + } + + if (int_status & USB_ISTR_RESET) { + // USBRST is start of reset. + USB->ISTR = (fsdev_bus_t)~USB_ISTR_RESET; + dcd_handle_bus_reset(); + dcd_event_bus_reset(0, TUSB_SPEED_FULL, true); + return; // Don't do the rest of the things here; perhaps they've been cleared? + } + + if (int_status & USB_ISTR_CTR) { + /* servicing of the endpoint correct transfer interrupt */ + /* clear of the CTR flag into the sub */ + dcd_ep_ctr_handler(); + } + + if (int_status & USB_ISTR_WKUP) { + USB->CNTR &= ~USB_CNTR_LPMODE; + USB->CNTR &= ~USB_CNTR_FSUSP; + + USB->ISTR = (fsdev_bus_t)~USB_ISTR_WKUP; + dcd_event_bus_signal(0, DCD_EVENT_RESUME, true); + } + + if (int_status & USB_ISTR_SUSP) { + /* Suspend is asserted for both suspend and unplug events. without Vbus monitoring, + * these events cannot be differentiated, so we only trigger suspend. */ + + /* Force low-power mode in the macrocell */ + USB->CNTR |= USB_CNTR_FSUSP; + USB->CNTR |= USB_CNTR_LPMODE; + + /* clear of the ISTR bit must be done after setting of CNTR_FSUSP */ + USB->ISTR = (fsdev_bus_t)~USB_ISTR_SUSP; + dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true); + } + + if (int_status & USB_ISTR_ESOF) { + if (remoteWakeCountdown == 1u) { + USB->CNTR &= ~USB_CNTR_RESUME; + } + if (remoteWakeCountdown > 0u) { + remoteWakeCountdown--; + } + USB->ISTR = (fsdev_bus_t)~USB_ISTR_ESOF; + } +} + +//--------------------------------------------------------------------+ +// Endpoint API +//--------------------------------------------------------------------+ + +// Invoked when a control transfer's status stage is complete. +// May help DCD to prepare for next control transfer, this API is optional. +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 && + request->bRequest == TUSB_REQ_SET_ADDRESS) { + uint8_t const dev_addr = (uint8_t)request->wValue; + + // Setting new address after the whole request is complete + USB->DADDR &= ~USB_DADDR_ADD; + USB->DADDR |= dev_addr; // leave the enable bit set + } +} + +/*** + * Allocate a section of PMA + * In case of double buffering, high 16bit is the address of 2nd buffer + * During failure, TU_ASSERT is used. If this happens, rework/reallocate memory manually. + */ +static uint32_t dcd_pma_alloc(uint16_t length, bool dbuf) +{ + // Ensure allocated buffer is aligned +#ifdef FSDEV_BUS_32BIT + length = (length + 3) & ~0x03; +#else + length = (length + 1) & ~0x01; +#endif + + uint32_t addr = ep_buf_ptr; + ep_buf_ptr = (uint16_t)(ep_buf_ptr + length); // increment buffer pointer + + if (dbuf) { + addr |= ((uint32_t)ep_buf_ptr) << 16; + ep_buf_ptr = (uint16_t)(ep_buf_ptr + length); // increment buffer pointer + } + + // Verify packet buffer is not overflowed + TU_ASSERT(ep_buf_ptr <= FSDEV_PMA_SIZE, 0xFFFF); + + return addr; +} + +/*** + * Allocate hardware endpoint + */ +static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type) +{ + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + + for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) { + // Check if already allocated + if (ep_alloc_status[i].allocated[dir] && + ep_alloc_status[i].ep_type == ep_type && + ep_alloc_status[i].ep_num == epnum) { + return i; + } + + // If EP of current direction is not allocated + // Except for ISO endpoint, both direction should be free + if (!ep_alloc_status[i].allocated[dir] && + (ep_type != TUSB_XFER_ISOCHRONOUS || !ep_alloc_status[i].allocated[dir ^ 1])) { + // Check if EP number is the same + if (ep_alloc_status[i].ep_num == 0xFF || ep_alloc_status[i].ep_num == epnum) { + // One EP pair has to be the same type + if (ep_alloc_status[i].ep_type == 0xFF || ep_alloc_status[i].ep_type == ep_type) { + ep_alloc_status[i].ep_num = epnum; + ep_alloc_status[i].ep_type = ep_type; + ep_alloc_status[i].allocated[dir] = true; + + return i; + } + } + } + } + + // Allocation failed + TU_ASSERT(0); +} + +// The STM32F0 doesn't seem to like |= or &= to manipulate the EP#R registers, +// so I'm using the #define from HAL here, instead. + +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *p_endpoint_desc) +{ + (void)rhport; + uint8_t const ep_addr = p_endpoint_desc->bEndpointAddress; + uint8_t const ep_idx = dcd_ep_alloc(ep_addr, p_endpoint_desc->bmAttributes.xfer); + uint8_t const dir = tu_edpt_dir(ep_addr); + const uint16_t packet_size = tu_edpt_packet_size(p_endpoint_desc); + const uint16_t buffer_size = pcd_aligned_buffer_size(packet_size); + uint16_t pma_addr; + uint32_t wType; + + TU_ASSERT(ep_idx < FSDEV_EP_COUNT); + TU_ASSERT(buffer_size <= 64); + + // Set type + switch (p_endpoint_desc->bmAttributes.xfer) { + case TUSB_XFER_CONTROL: + wType = USB_EP_CONTROL; + break; + case TUSB_XFER_BULK: + wType = USB_EP_CONTROL; + break; + + case TUSB_XFER_INTERRUPT: + wType = USB_EP_INTERRUPT; + break; + + default: + // Note: ISO endpoint should use alloc / active functions + TU_ASSERT(false); + } + + pcd_set_eptype(USB, ep_idx, wType); + pcd_set_ep_address(USB, ep_idx, tu_edpt_number(ep_addr)); + + /* Create a packet memory buffer area. */ + pma_addr = dcd_pma_alloc(buffer_size, false); + + if (dir == TUSB_DIR_IN) { + pcd_set_ep_tx_address(USB, ep_idx, pma_addr); + pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_NAK); + pcd_clear_tx_dtog(USB, ep_idx); + } else { + pcd_set_ep_rx_address(USB, ep_idx, pma_addr); + pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_NAK); + pcd_clear_rx_dtog(USB, ep_idx); + } + + xfer_ctl_ptr(ep_addr)->max_packet_size = packet_size; + xfer_ctl_ptr(ep_addr)->ep_idx = ep_idx; + + return true; +} + +void dcd_edpt_close_all(uint8_t rhport) +{ + (void)rhport; + + for (uint32_t i = 1; i < FSDEV_EP_COUNT; i++) { + // Reset endpoint + pcd_set_endpoint(USB, i, 0); + // Clear EP allocation status + ep_alloc_status[i].ep_num = 0xFF; + ep_alloc_status[i].ep_type = 0xFF; + ep_alloc_status[i].allocated[0] = false; + ep_alloc_status[i].allocated[1] = false; + } + + // Reset PMA allocation + ep_buf_ptr = DCD_STM32_BTABLE_BASE + 8 * CFG_TUD_ENDPPOINT_MAX + 2 * CFG_TUD_ENDPOINT0_SIZE; +} + +/** + * Close an endpoint. + * + * This function may be called with interrupts enabled or disabled. + * + * This also clears transfers in progress, should there be any. + */ +void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) +{ + (void)rhport; + + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); + uint8_t const ep_idx = xfer->ep_idx; + uint8_t const dir = tu_edpt_dir(ep_addr); + + if (dir == TUSB_DIR_IN) { + pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_DIS); + } else { + pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_DIS); + } +} + +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) +{ + (void)rhport; + + uint8_t const ep_idx = dcd_ep_alloc(ep_addr, TUSB_XFER_ISOCHRONOUS); + const uint16_t buffer_size = pcd_aligned_buffer_size(largest_packet_size); + + /* Create a packet memory buffer area. Enable double buffering for devices with 2048 bytes PMA, + for smaller devices double buffering occupy too much space. */ +#if FSDEV_PMA_SIZE > 1024u + uint32_t pma_addr = dcd_pma_alloc(buffer_size, true); + uint16_t pma_addr2 = pma_addr >> 16; +#else + uint32_t pma_addr = dcd_pma_alloc(buffer_size, true); + uint16_t pma_addr2 = pma_addr; +#endif + pcd_set_ep_tx_address(USB, ep_idx, pma_addr); + pcd_set_ep_rx_address(USB, ep_idx, pma_addr2); + + pcd_set_eptype(USB, ep_idx, USB_EP_ISOCHRONOUS); + + xfer_ctl_ptr(ep_addr)->ep_idx = ep_idx; + + return true; +} + +bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const *p_endpoint_desc) +{ + (void)rhport; + uint8_t const ep_addr = p_endpoint_desc->bEndpointAddress; + uint8_t const ep_idx = xfer_ctl_ptr(ep_addr)->ep_idx; + uint8_t const dir = tu_edpt_dir(ep_addr); + const uint16_t packet_size = tu_edpt_packet_size(p_endpoint_desc); + + pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_DIS); + pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_DIS); + + pcd_set_ep_address(USB, ep_idx, tu_edpt_number(ep_addr)); + + pcd_clear_tx_dtog(USB, ep_idx); + pcd_clear_rx_dtog(USB, ep_idx); + + if (dir == TUSB_DIR_IN) { + pcd_rx_dtog(USB, ep_idx); + } else { + pcd_tx_dtog(USB, ep_idx); + } + + xfer_ctl_ptr(ep_addr)->max_packet_size = packet_size; + + return true; +} + +// Currently, single-buffered, and only 64 bytes at a time (max) + +static void dcd_transmit_packet(xfer_ctl_t *xfer, uint16_t ep_ix) +{ + uint16_t len = (uint16_t)(xfer->total_len - xfer->queued_len); + if (len > xfer->max_packet_size) { + len = xfer->max_packet_size; + } + + uint16_t ep_reg = pcd_get_endpoint(USB, ep_ix); + bool const is_iso = (ep_reg & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS; + uint16_t addr_ptr; + + if (is_iso) { + if (ep_reg & USB_EP_DTOG_TX) { + addr_ptr = pcd_get_ep_dbuf1_address(USB, ep_ix); + pcd_set_ep_tx_dbuf1_cnt(USB, ep_ix, len); + } else { + addr_ptr = pcd_get_ep_dbuf0_address(USB, ep_ix); + pcd_set_ep_tx_dbuf0_cnt(USB, ep_ix, len); + } + } else { + addr_ptr = pcd_get_ep_tx_address(USB, ep_ix); + pcd_set_ep_tx_cnt(USB, ep_ix, len); + } + + if (xfer->ff) { + dcd_write_packet_memory_ff(xfer->ff, addr_ptr, len); + } else { + dcd_write_packet_memory(addr_ptr, &(xfer->buffer[xfer->queued_len]), len); + } + xfer->queued_len = (uint16_t)(xfer->queued_len + len); + + dcd_int_disable(0); + pcd_set_ep_tx_status(USB, ep_ix, USB_EP_TX_VALID); + if (is_iso) { + xfer->iso_in_sending = true; + } + dcd_int_enable(0); +} + +static bool edpt_xfer(uint8_t rhport, uint8_t ep_addr) +{ + (void)rhport; + + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); + uint8_t const ep_idx = xfer->ep_idx; + uint8_t const dir = tu_edpt_dir(ep_addr); + + if (dir == TUSB_DIR_IN) { + dcd_transmit_packet(xfer, ep_idx); + } else { + // A setup token can occur immediately after an OUT STATUS packet so make sure we have a valid + // buffer for the control endpoint. + if (ep_idx == 0 && xfer->buffer == NULL) { + xfer->buffer = (uint8_t *)_setup_packet; + } + + uint32_t cnt = (uint32_t ) tu_min16(xfer->total_len, xfer->max_packet_size); + uint16_t ep_reg = pcd_get_endpoint(USB, ep_idx); + + if ((ep_reg & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS) { + pcd_set_ep_rx_dbuf0_cnt(USB, ep_idx, cnt); + pcd_set_ep_rx_dbuf1_cnt(USB, ep_idx, cnt); + } else { + pcd_set_ep_rx_cnt(USB, ep_idx, cnt); + } + + pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_VALID); + } + + return true; +} + +bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) +{ + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); + + xfer->buffer = buffer; + xfer->ff = NULL; + xfer->total_len = total_bytes; + xfer->queued_len = 0; + + return edpt_xfer(rhport, ep_addr); +} + +bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff, uint16_t total_bytes) +{ + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); + xfer->buffer = NULL; + xfer->ff = ff; + xfer->total_len = total_bytes; + xfer->queued_len = 0; + + return edpt_xfer(rhport, ep_addr); +} + +void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) +{ + (void)rhport; + + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); + uint8_t const ep_idx = xfer->ep_idx; + uint8_t const dir = tu_edpt_dir(ep_addr); + + if (dir == TUSB_DIR_IN) { + pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_STALL); + } else { + pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_STALL); + } +} + +void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) +{ + (void)rhport; + + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr); + uint8_t const ep_idx = xfer->ep_idx; + uint8_t const dir = tu_edpt_dir(ep_addr); + + if (dir == TUSB_DIR_IN) { // IN + if (pcd_get_eptype(USB, ep_idx) != USB_EP_ISOCHRONOUS) { + pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_NAK); + } + + /* Reset to DATA0 if clearing stall condition. */ + pcd_clear_tx_dtog(USB, ep_idx); + } else { // OUT + if (pcd_get_eptype(USB, ep_idx) != USB_EP_ISOCHRONOUS) { + pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_NAK); + } + /* Reset to DATA0 if clearing stall condition. */ + pcd_clear_rx_dtog(USB, ep_idx); + } +} + +#ifdef FSDEV_BUS_32BIT +static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t wNBytes) +{ + const uint8_t *srcVal = src; + volatile uint32_t *dst32 = (volatile uint32_t *)(USB_PMAADDR + dst); + + for (uint32_t n = wNBytes / 4; n > 0; --n) { + *dst32++ = tu_unaligned_read32(srcVal); + srcVal += 4; + } + + wNBytes = wNBytes & 0x03; + if (wNBytes) { + uint32_t wrVal = *srcVal; + wNBytes--; + + if (wNBytes) { + wrVal |= *++srcVal << 8; + wNBytes--; + + if (wNBytes) { + wrVal |= *++srcVal << 16; + } + } + + *dst32 = wrVal; + } + + return true; +} +#else +// Packet buffer access can only be 8- or 16-bit. +/** + * @brief Copy a buffer from user memory area to packet memory area (PMA). + * This uses byte-access for user memory (so support non-aligned buffers) + * and 16-bit access for packet memory. + * @param dst, byte address in PMA; must be 16-bit aligned + * @param src pointer to user memory area. + * @param wPMABufAddr address into PMA. + * @param wNBytes no. of bytes to be copied. + * @retval None + */ +static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t wNBytes) +{ + uint32_t n = (uint32_t)wNBytes >> 1U; + uint16_t temp1, temp2; + const uint8_t *srcVal; + + // The GCC optimizer will combine access to 32-bit sizes if we let it. Force + // it volatile so that it won't do that. + __IO uint16_t *pdwVal; + + srcVal = src; + pdwVal = &pma[FSDEV_PMA_STRIDE * (dst >> 1)]; + + while (n--) { + temp1 = (uint16_t)*srcVal; + srcVal++; + temp2 = temp1 | ((uint16_t)(((uint16_t)(*srcVal)) << 8U)); + *pdwVal = temp2; + pdwVal += FSDEV_PMA_STRIDE; + srcVal++; + } + + if (wNBytes) { + temp1 = (uint16_t) *srcVal; + *pdwVal = temp1; + } + + return true; +} +#endif + +/** + * @brief Copy from FIFO to packet memory area (PMA). + * Uses byte-access of system memory and 16-bit access of packet memory + * @param wNBytes no. of bytes to be copied. + * @retval None + */ +static bool dcd_write_packet_memory_ff(tu_fifo_t *ff, uint16_t dst, uint16_t wNBytes) +{ + // Since we copy from a ring buffer FIFO, a wrap might occur making it necessary to conduct two copies + tu_fifo_buffer_info_t info; + tu_fifo_get_read_info(ff, &info); + + uint16_t cnt_lin = TU_MIN(wNBytes, info.len_lin); + uint16_t cnt_wrap = TU_MIN(wNBytes - cnt_lin, info.len_wrap); + + // We want to read from the FIFO and write it into the PMA, if LIN part is ODD and has WRAPPED part, + // last lin byte will be combined with wrapped part + // To ensure PMA is always access aligned (dst aligned to 16 or 32 bit) +#ifdef FSDEV_BUS_32BIT + if ((cnt_lin & 0x03) && cnt_wrap) { + // Copy first linear part + dcd_write_packet_memory(dst, info.ptr_lin, cnt_lin & ~0x03); + dst += cnt_lin & ~0x03; + + // Copy last linear bytes & first wrapped bytes to buffer + uint32_t i; + uint8_t tmp[4]; + for (i = 0; i < (cnt_lin & 0x03); i++) { + tmp[i] = ((uint8_t *)info.ptr_lin)[(cnt_lin & ~0x03) + i]; + } + uint32_t wCnt = cnt_wrap; + for (; i < 4 && wCnt > 0; i++, wCnt--) { + tmp[i] = *(uint8_t *)info.ptr_wrap; + info.ptr_wrap = (uint8_t *)info.ptr_wrap + 1; + } + + // Write unaligned buffer + dcd_write_packet_memory(dst, &tmp, 4); + dst += 4; + + // Copy rest of wrapped byte + if (wCnt) + dcd_write_packet_memory(dst, info.ptr_wrap, wCnt); + } +#else + if ((cnt_lin & 0x01) && cnt_wrap) { + // Copy first linear part + dcd_write_packet_memory(dst, info.ptr_lin, cnt_lin & ~0x01); + dst += cnt_lin & ~0x01; + + // Copy last linear byte & first wrapped byte + uint16_t tmp = ((uint8_t *)info.ptr_lin)[cnt_lin - 1] | ((uint16_t)(((uint8_t *)info.ptr_wrap)[0]) << 8U); + dcd_write_packet_memory(dst, &tmp, 2); + dst += 2; + + // Copy rest of wrapped byte + dcd_write_packet_memory(dst, ((uint8_t *)info.ptr_wrap) + 1, cnt_wrap - 1); + } +#endif + else { + // Copy linear part + dcd_write_packet_memory(dst, info.ptr_lin, cnt_lin); + dst += info.len_lin; + + if (info.len_wrap) { + // Copy wrapped byte + dcd_write_packet_memory(dst, info.ptr_wrap, cnt_wrap); + } + } + + tu_fifo_advance_read_pointer(ff, cnt_lin + cnt_wrap); + + return true; +} + +#ifdef FSDEV_BUS_32BIT +static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t wNBytes) +{ + uint8_t *dstVal = dst; + volatile uint32_t *src32 = (volatile uint32_t *)(USB_PMAADDR + src); + + for (uint32_t n = wNBytes / 4; n > 0; --n) { + tu_unaligned_write32(dstVal, *src32++); + dstVal += 4; + } + + wNBytes = wNBytes & 0x03; + if (wNBytes) { + uint32_t rdVal = *src32; + + *dstVal = tu_u32_byte0(rdVal); + wNBytes--; + + if (wNBytes) { + *++dstVal = tu_u32_byte1(rdVal); + wNBytes--; + + if (wNBytes) { + *++dstVal = tu_u32_byte2(rdVal); + } + } + } + + return true; +} +#else +/** + * @brief Copy a buffer from packet memory area (PMA) to user memory area. + * Uses byte-access of system memory and 16-bit access of packet memory + * @param wNBytes no. of bytes to be copied. + * @retval None + */ +static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t wNBytes) +{ + uint32_t n = (uint32_t)wNBytes >> 1U; + // The GCC optimizer will combine access to 32-bit sizes if we let it. Force + // it volatile so that it won't do that. + __IO const uint16_t *pdwVal; + uint32_t temp; + + pdwVal = &pma[FSDEV_PMA_STRIDE * (src >> 1)]; + uint8_t *dstVal = (uint8_t *)dst; + + while (n--) { + temp = *pdwVal; + pdwVal += FSDEV_PMA_STRIDE; + *dstVal++ = ((temp >> 0) & 0xFF); + *dstVal++ = ((temp >> 8) & 0xFF); + } + + if (wNBytes & 0x01) { + temp = *pdwVal; + pdwVal += FSDEV_PMA_STRIDE; + *dstVal++ = ((temp >> 0) & 0xFF); + } + return true; +} +#endif + +/** + * @brief Copy a buffer from user packet memory area (PMA) to FIFO. + * Uses byte-access of system memory and 16-bit access of packet memory + * @param wNBytes no. of bytes to be copied. + * @retval None + */ +static bool dcd_read_packet_memory_ff(tu_fifo_t *ff, uint16_t src, uint16_t wNBytes) +{ + // Since we copy into a ring buffer FIFO, a wrap might occur making it necessary to conduct two copies + // Check for first linear part + tu_fifo_buffer_info_t info; + tu_fifo_get_write_info(ff, &info); // We want to read from the FIFO + + uint16_t cnt_lin = TU_MIN(wNBytes, info.len_lin); + uint16_t cnt_wrap = TU_MIN(wNBytes - cnt_lin, info.len_wrap); + + // We want to read from PMA and write it into the FIFO, if LIN part is ODD and has WRAPPED part, + // last lin byte will be combined with wrapped part + // To ensure PMA is always access aligned (src aligned to 16 or 32 bit) +#ifdef FSDEV_BUS_32BIT + if ((cnt_lin & 0x03) && cnt_wrap) { + // Copy first linear part + dcd_read_packet_memory(info.ptr_lin, src, cnt_lin & ~0x03); + src += cnt_lin & ~0x03; + + // Copy last linear bytes & first wrapped bytes + uint8_t tmp[4]; + dcd_read_packet_memory(tmp, src, 4); + src += 4; + + uint32_t i; + for (i = 0; i < (cnt_lin & 0x03); i++) { + ((uint8_t *)info.ptr_lin)[(cnt_lin & ~0x03) + i] = tmp[i]; + } + uint32_t wCnt = cnt_wrap; + for (; i < 4 && wCnt > 0; i++, wCnt--) { + *(uint8_t *)info.ptr_wrap = tmp[i]; + info.ptr_wrap = (uint8_t *)info.ptr_wrap + 1; + } + + // Copy rest of wrapped byte + if (wCnt) + dcd_read_packet_memory(info.ptr_wrap, src, wCnt); + } +#else + if ((cnt_lin & 0x01) && cnt_wrap) { + // Copy first linear part + dcd_read_packet_memory(info.ptr_lin, src, cnt_lin & ~0x01); + src += cnt_lin & ~0x01; + + // Copy last linear byte & first wrapped byte + uint8_t tmp[2]; + dcd_read_packet_memory(tmp, src, 2); + src += 2; + + ((uint8_t *)info.ptr_lin)[cnt_lin - 1] = tmp[0]; + ((uint8_t *)info.ptr_wrap)[0] = tmp[1]; + + // Copy rest of wrapped byte + dcd_read_packet_memory(((uint8_t *)info.ptr_wrap) + 1, src, cnt_wrap - 1); + } +#endif + else { + // Copy linear part + dcd_read_packet_memory(info.ptr_lin, src, cnt_lin); + src += cnt_lin; + + if (info.len_wrap) { + // Copy wrapped byte + dcd_read_packet_memory(info.ptr_wrap, src, cnt_wrap); + } + } + + tu_fifo_advance_write_pointer(ff, cnt_lin + cnt_wrap); + + return true; +} + +#endif diff --git a/src/portable/st/stm32_fsdev/fsdev_ch32.h b/src/portable/st/stm32_fsdev/fsdev_ch32.h new file mode 100644 index 00000000..85fe3266 --- /dev/null +++ b/src/portable/st/stm32_fsdev/fsdev_ch32.h @@ -0,0 +1,232 @@ +/* +* The MIT License (MIT) + * + * Copyright (c) 2024, 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. + * + */ +/**

© Copyright (c) 2016 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + */ + +#ifndef TUSB_FSDEV_CH32_H +#define TUSB_FSDEV_CH32_H + +#include "common/tusb_compiler.h" + +#if CFG_TUSB_MCU == OPT_MCU_CH32V20X + #include + +#elif CFG_TUSB_MCU == OPT_MCU_CH32F20X + #include +#endif + +#define FSDEV_PMA_SIZE (512u) + +// volatile 32-bit aligned +#define _va32 volatile TU_ATTR_ALIGNED(4) + +typedef struct { + _va32 uint16_t EP0R; // 00: USB Endpoint 0 register + _va32 uint16_t EP1R; // 04: USB Endpoint 1 register + _va32 uint16_t EP2R; // 08: USB Endpoint 2 register + _va32 uint16_t EP3R; // 0C: USB Endpoint 3 register + _va32 uint16_t EP4R; // 10: USB Endpoint 4 register + _va32 uint16_t EP5R; // 14: USB Endpoint 5 register + _va32 uint16_t EP6R; // 18: USB Endpoint 6 register + _va32 uint16_t EP7R; // 1C: USB Endpoint 7 register + _va32 uint16_t RESERVED7[16]; // Reserved + _va32 uint16_t CNTR; // 40: Control register + _va32 uint16_t ISTR; // 44: Interrupt status register + _va32 uint16_t FNR; // 48: Frame number register + _va32 uint16_t DADDR; // 4C: Device address register + _va32 uint16_t BTABLE; // 50: Buffer Table address register +} USB_TypeDef; + +TU_VERIFY_STATIC(sizeof(USB_TypeDef) == 0x54, "Size is not correct"); +TU_VERIFY_STATIC(offsetof(USB_TypeDef, CNTR) == 0x40, "Wrong offset"); + +#define USB_BASE (APB1PERIPH_BASE + 0x00005C00UL) /*!< USB_IP Peripheral Registers base address */ +#define USB_PMAADDR (APB1PERIPH_BASE + 0x00006000UL) /*!< USB_IP Packet Memory Area base address */ +#define USB ((USB_TypeDef *)USB_BASE) + +/******************************************************************************/ +/* */ +/* USB Device General registers */ +/* */ +/******************************************************************************/ +#define USB_CNTR (USB_BASE + 0x40U) /*!< Control register */ +#define USB_ISTR (USB_BASE + 0x44U) /*!< Interrupt status register */ +#define USB_FNR (USB_BASE + 0x48U) /*!< Frame number register */ +#define USB_DADDR (USB_BASE + 0x4CU) /*!< Device address register */ +#define USB_BTABLE (USB_BASE + 0x50U) /*!< Buffer Table address register */ + +/**************************** ISTR interrupt events *************************/ +#define USB_ISTR_CTR ((uint16_t)0x8000U) /*!< Correct TRansfer (clear-only bit) */ +#define USB_ISTR_PMAOVR ((uint16_t)0x4000U) /*!< DMA OVeR/underrun (clear-only bit) */ +#define USB_ISTR_ERR ((uint16_t)0x2000U) /*!< ERRor (clear-only bit) */ +#define USB_ISTR_WKUP ((uint16_t)0x1000U) /*!< WaKe UP (clear-only bit) */ +#define USB_ISTR_SUSP ((uint16_t)0x0800U) /*!< SUSPend (clear-only bit) */ +#define USB_ISTR_RESET ((uint16_t)0x0400U) /*!< RESET (clear-only bit) */ +#define USB_ISTR_SOF ((uint16_t)0x0200U) /*!< Start Of Frame (clear-only bit) */ +#define USB_ISTR_ESOF ((uint16_t)0x0100U) /*!< Expected Start Of Frame (clear-only bit) */ +#define USB_ISTR_DIR ((uint16_t)0x0010U) /*!< DIRection of transaction (read-only bit) */ +#define USB_ISTR_EP_ID ((uint16_t)0x000FU) /*!< EndPoint IDentifier (read-only bit) */ + +/* Legacy defines */ +#define USB_ISTR_PMAOVRM USB_ISTR_PMAOVR + +#define USB_CLR_CTR (~USB_ISTR_CTR) /*!< clear Correct TRansfer bit */ +#define USB_CLR_PMAOVR (~USB_ISTR_PMAOVR) /*!< clear DMA OVeR/underrun bit*/ +#define USB_CLR_ERR (~USB_ISTR_ERR) /*!< clear ERRor bit */ +#define USB_CLR_WKUP (~USB_ISTR_WKUP) /*!< clear WaKe UP bit */ +#define USB_CLR_SUSP (~USB_ISTR_SUSP) /*!< clear SUSPend bit */ +#define USB_CLR_RESET (~USB_ISTR_RESET) /*!< clear RESET bit */ +#define USB_CLR_SOF (~USB_ISTR_SOF) /*!< clear Start Of Frame bit */ +#define USB_CLR_ESOF (~USB_ISTR_ESOF) /*!< clear Expected Start Of Frame bit */ + +/* Legacy defines */ +#define USB_CLR_PMAOVRM USB_CLR_PMAOVR + +/************************* CNTR control register bits definitions ***********/ +#define USB_CNTR_CTRM ((uint16_t)0x8000U) /*!< Correct TRansfer Mask */ +#define USB_CNTR_PMAOVR ((uint16_t)0x4000U) /*!< DMA OVeR/underrun Mask */ +#define USB_CNTR_ERRM ((uint16_t)0x2000U) /*!< ERRor Mask */ +#define USB_CNTR_WKUPM ((uint16_t)0x1000U) /*!< WaKe UP Mask */ +#define USB_CNTR_SUSPM ((uint16_t)0x0800U) /*!< SUSPend Mask */ +#define USB_CNTR_RESETM ((uint16_t)0x0400U) /*!< RESET Mask */ +#define USB_CNTR_SOFM ((uint16_t)0x0200U) /*!< Start Of Frame Mask */ +#define USB_CNTR_ESOFM ((uint16_t)0x0100U) /*!< Expected Start Of Frame Mask */ +#define USB_CNTR_RESUME ((uint16_t)0x0010U) /*!< RESUME request */ +#define USB_CNTR_FSUSP ((uint16_t)0x0008U) /*!< Force SUSPend */ +#define USB_CNTR_LPMODE ((uint16_t)0x0004U) /*!< Low-power MODE */ +#define USB_CNTR_PDWN ((uint16_t)0x0002U) /*!< Power DoWN */ +#define USB_CNTR_FRES ((uint16_t)0x0001U) /*!< Force USB RESet */ + +/* Legacy defines */ +#define USB_CNTR_PMAOVRM USB_CNTR_PMAOVR +#define USB_CNTR_LP_MODE USB_CNTR_LPMODE + +/******************** FNR Frame Number Register bit definitions ************/ +#define USB_FNR_RXDP ((uint16_t)0x8000U) /*!< status of D+ data line */ +#define USB_FNR_RXDM ((uint16_t)0x4000U) /*!< status of D- data line */ +#define USB_FNR_LCK ((uint16_t)0x2000U) /*!< LoCKed */ +#define USB_FNR_LSOF ((uint16_t)0x1800U) /*!< Lost SOF */ +#define USB_FNR_FN ((uint16_t)0x07FFU) /*!< Frame Number */ + +/******************** DADDR Device ADDRess bit definitions ****************/ +#define USB_DADDR_EF ((uint8_t)0x80U) /*!< USB device address Enable Function */ +#define USB_DADDR_ADD ((uint8_t)0x7FU) /*!< USB device address */ + +/****************************** Endpoint register *************************/ +#define USB_EP0R USB_BASE /*!< endpoint 0 register address */ +#define USB_EP1R (USB_BASE + 0x04U) /*!< endpoint 1 register address */ +#define USB_EP2R (USB_BASE + 0x08U) /*!< endpoint 2 register address */ +#define USB_EP3R (USB_BASE + 0x0CU) /*!< endpoint 3 register address */ +#define USB_EP4R (USB_BASE + 0x10U) /*!< endpoint 4 register address */ +#define USB_EP5R (USB_BASE + 0x14U) /*!< endpoint 5 register address */ +#define USB_EP6R (USB_BASE + 0x18U) /*!< endpoint 6 register address */ +#define USB_EP7R (USB_BASE + 0x1CU) /*!< endpoint 7 register address */ +/* bit positions */ +#define USB_EP_CTR_RX ((uint16_t)0x8000U) /*!< EndPoint Correct TRansfer RX */ +#define USB_EP_DTOG_RX ((uint16_t)0x4000U) /*!< EndPoint Data TOGGLE RX */ +#define USB_EPRX_STAT ((uint16_t)0x3000U) /*!< EndPoint RX STATus bit field */ +#define USB_EP_SETUP ((uint16_t)0x0800U) /*!< EndPoint SETUP */ +#define USB_EP_T_FIELD ((uint16_t)0x0600U) /*!< EndPoint TYPE */ +#define USB_EP_KIND ((uint16_t)0x0100U) /*!< EndPoint KIND */ +#define USB_EP_CTR_TX ((uint16_t)0x0080U) /*!< EndPoint Correct TRansfer TX */ +#define USB_EP_DTOG_TX ((uint16_t)0x0040U) /*!< EndPoint Data TOGGLE TX */ +#define USB_EPTX_STAT ((uint16_t)0x0030U) /*!< EndPoint TX STATus bit field */ +#define USB_EPADDR_FIELD ((uint16_t)0x000FU) /*!< EndPoint ADDRess FIELD */ + +/* EndPoint REGister MASK (no toggle fields) */ +#define USB_EPREG_MASK (USB_EP_CTR_RX|USB_EP_SETUP|USB_EP_T_FIELD|USB_EP_KIND|USB_EP_CTR_TX|USB_EPADDR_FIELD) + /*!< EP_TYPE[1:0] EndPoint TYPE */ +#define USB_EP_TYPE_MASK ((uint16_t)0x0600U) /*!< EndPoint TYPE Mask */ +#define USB_EP_BULK ((uint16_t)0x0000U) /*!< EndPoint BULK */ +#define USB_EP_CONTROL ((uint16_t)0x0200U) /*!< EndPoint CONTROL */ +#define USB_EP_ISOCHRONOUS ((uint16_t)0x0400U) /*!< EndPoint ISOCHRONOUS */ +#define USB_EP_INTERRUPT ((uint16_t)0x0600U) /*!< EndPoint INTERRUPT */ +#define USB_EP_T_MASK ((uint16_t) ~USB_EP_T_FIELD & USB_EPREG_MASK) + +#define USB_EPKIND_MASK ((uint16_t) ~USB_EP_KIND & USB_EPREG_MASK) /*!< EP_KIND EndPoint KIND */ + /*!< STAT_TX[1:0] STATus for TX transfer */ +#define USB_EP_TX_DIS ((uint16_t)0x0000U) /*!< EndPoint TX DISabled */ +#define USB_EP_TX_STALL ((uint16_t)0x0010U) /*!< EndPoint TX STALLed */ +#define USB_EP_TX_NAK ((uint16_t)0x0020U) /*!< EndPoint TX NAKed */ +#define USB_EP_TX_VALID ((uint16_t)0x0030U) /*!< EndPoint TX VALID */ +#define USB_EPTX_DTOG1 ((uint16_t)0x0010U) /*!< EndPoint TX Data TOGgle bit1 */ +#define USB_EPTX_DTOG2 ((uint16_t)0x0020U) /*!< EndPoint TX Data TOGgle bit2 */ +#define USB_EPTX_DTOGMASK (USB_EPTX_STAT|USB_EPREG_MASK) + /*!< STAT_RX[1:0] STATus for RX transfer */ +#define USB_EP_RX_DIS ((uint16_t)0x0000U) /*!< EndPoint RX DISabled */ +#define USB_EP_RX_STALL ((uint16_t)0x1000U) /*!< EndPoint RX STALLed */ +#define USB_EP_RX_NAK ((uint16_t)0x2000U) /*!< EndPoint RX NAKed */ +#define USB_EP_RX_VALID ((uint16_t)0x3000U) /*!< EndPoint RX VALID */ +#define USB_EPRX_DTOG1 ((uint16_t)0x1000U) /*!< EndPoint RX Data TOGgle bit1 */ +#define USB_EPRX_DTOG2 ((uint16_t)0x2000U) /*!< EndPoint RX Data TOGgle bit1 */ +#define USB_EPRX_DTOGMASK (USB_EPRX_STAT|USB_EPREG_MASK) + + +//--------------------------------------------------------------------+ +// +//--------------------------------------------------------------------+ + +#if CFG_TUSB_MCU == OPT_MCU_CH32V20X +static const IRQn_Type fsdev_irq[] = { + USB_HP_CAN1_TX_IRQn, + USB_LP_CAN1_RX0_IRQn, + USBWakeUp_IRQn +}; +enum { FSDEV_IRQ_NUM = TU_ARRAY_SIZE(fsdev_irq) }; +#else + #error "Unsupported MCU" +#endif + +void dcd_int_enable(uint8_t rhport) { + (void)rhport; + for(uint8_t i=0; i < FSDEV_IRQ_NUM; i++) { + NVIC_EnableIRQ(fsdev_irq[i]); + } +} + +void dcd_int_disable(uint8_t rhport) { + (void)rhport; + for(uint8_t i=0; i < FSDEV_IRQ_NUM; i++) { + NVIC_DisableIRQ(fsdev_irq[i]); + } +} + +void dcd_disconnect(uint8_t rhport) { + (void) rhport; + EXTEN->EXTEN_CTR &= ~EXTEN_USBD_PU_EN; +} + +void dcd_connect(uint8_t rhport) { + (void) rhport; + EXTEN->EXTEN_CTR |= EXTEN_USBD_PU_EN; +} + +#endif diff --git a/src/portable/st/stm32_fsdev/fsdev_common.h b/src/portable/st/stm32_fsdev/fsdev_common.h new file mode 100644 index 00000000..af5d8afa --- /dev/null +++ b/src/portable/st/stm32_fsdev/fsdev_common.h @@ -0,0 +1,391 @@ +/* + * The MIT License (MIT) + * + * Copyright(c) 2016 STMicroelectronics + * Copyright(c) N Conrad + * Copyright (c) 2024, 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. + * + */ + +#ifndef TUSB_FSDEV_COMMON_H +#define TUSB_FSDEV_COMMON_H + +#ifdef __cplusplus + extern "C" { +#endif + +#include "stdint.h" + +// FSDEV_PMA_SIZE is PMA buffer size in bytes. +// On 512-byte devices, access with a stride of two words (use every other 16-bit address) +// On 1024-byte devices, access with a stride of one word (use every 16-bit address) + +// For purposes of accessing the packet +#if ((FSDEV_PMA_SIZE) == 512u) + #define FSDEV_PMA_STRIDE (2u) +#elif ((FSDEV_PMA_SIZE) == 1024u) + #define FSDEV_PMA_STRIDE (1u) +#endif + +// The fsdev_bus_t type can be used for both register and PMA access necessities +// For type-safety create a new macro for the volatile address of PMAADDR +// The compiler should warn us if we cast it to a non-volatile type? +#ifdef FSDEV_BUS_32BIT +typedef uint32_t fsdev_bus_t; +static volatile uint32_t * const pma32 = (volatile uint32_t*)USB_PMAADDR; + +#else +typedef uint16_t fsdev_bus_t; +// Volatile is also needed to prevent the optimizer from changing access to 32-bit (as 32-bit access is forbidden) +static volatile uint16_t * const pma = (volatile uint16_t*)USB_PMAADDR; + +TU_ATTR_ALWAYS_INLINE static inline volatile uint16_t * pcd_btable_word_ptr(USB_TypeDef * USBx, size_t x) { + size_t total_word_offset = (((USBx)->BTABLE)>>1) + x; + total_word_offset *= FSDEV_PMA_STRIDE; + return &(pma[total_word_offset]); +} + +TU_ATTR_ALWAYS_INLINE static inline volatile uint16_t* pcd_ep_tx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpIdx) { + return pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 1u); +} + +TU_ATTR_ALWAYS_INLINE static inline volatile uint16_t* pcd_ep_rx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpIdx) { + return pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 3u); +} +#endif + +/* Aligned buffer size according to hardware */ +TU_ATTR_ALWAYS_INLINE static inline uint16_t pcd_aligned_buffer_size(uint16_t size) { + /* The STM32 full speed USB peripheral supports only a limited set of + * buffer sizes given by the RX buffer entry format in the USB_BTABLE. */ + uint16_t blocksize = (size > 62) ? 32 : 2; + + // Round up while dividing requested size by blocksize + uint16_t numblocks = (size + blocksize - 1) / blocksize ; + + return numblocks * blocksize; +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_endpoint(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wRegValue) { +#ifdef FSDEV_BUS_32BIT + (void) USBx; + volatile uint32_t *reg = (volatile uint32_t *)(USB_DRD_BASE + bEpIdx*4); + *reg = wRegValue; +#else + volatile uint16_t *reg = (volatile uint16_t *)((&USBx->EP0R) + bEpIdx*2u); + *reg = (uint16_t)wRegValue; +#endif +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_endpoint(USB_TypeDef * USBx, uint32_t bEpIdx) { +#ifdef FSDEV_BUS_32BIT + (void) USBx; + volatile const uint32_t *reg = (volatile const uint32_t *)(USB_DRD_BASE + bEpIdx*4); +#else + volatile const uint16_t *reg = (volatile const uint16_t *)((&USBx->EP0R) + bEpIdx*2u); +#endif + return *reg; +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_eptype(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wType) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal &= (uint32_t)USB_EP_T_MASK; + regVal |= wType; + regVal |= USB_EP_CTR_RX | USB_EP_CTR_TX; // These clear on write0, so must set high + pcd_set_endpoint(USBx, bEpIdx, regVal); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_eptype(USB_TypeDef * USBx, uint32_t bEpIdx) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal &= USB_EP_T_FIELD; + return regVal; +} + +/** + * @brief Clears bit CTR_RX / CTR_TX in the endpoint register. + * @param USBx USB peripheral instance register address. + * @param bEpIdx Endpoint Number. + * @retval None + */ +TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_rx_ep_ctr(USB_TypeDef * USBx, uint32_t bEpIdx) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal &= USB_EPREG_MASK; + regVal &= ~USB_EP_CTR_RX; + regVal |= USB_EP_CTR_TX; // preserve CTR_TX (clears on writing 0) + pcd_set_endpoint(USBx, bEpIdx, regVal); +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_tx_ep_ctr(USB_TypeDef * USBx, uint32_t bEpIdx) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal &= USB_EPREG_MASK; + regVal &= ~USB_EP_CTR_TX; + regVal |= USB_EP_CTR_RX; // preserve CTR_RX (clears on writing 0) + pcd_set_endpoint(USBx, bEpIdx,regVal); +} + +/** + * @brief gets counter of the tx buffer. + * @param USBx USB peripheral instance register address. + * @param bEpIdx Endpoint Number. + * @retval Counter value + */ +TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx) { +#ifdef FSDEV_BUS_32BIT + (void) USBx; + return (pma32[2*bEpIdx] & 0x03FF0000) >> 16; +#else + volatile const uint16_t *regPtr = pcd_ep_tx_cnt_ptr(USBx, bEpIdx); + return *regPtr & 0x3ffU; +#endif +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx) { +#ifdef FSDEV_BUS_32BIT + (void) USBx; + return (pma32[2*bEpIdx + 1] & 0x03FF0000) >> 16; +#else + volatile const uint16_t *regPtr = pcd_ep_rx_cnt_ptr(USBx, bEpIdx); + return *regPtr & 0x3ffU; +#endif +} + +#define pcd_get_ep_dbuf0_cnt pcd_get_ep_tx_cnt +#define pcd_get_ep_dbuf1_cnt pcd_get_ep_rx_cnt + +/** + * @brief Sets address in an endpoint register. + * @param USBx USB peripheral instance register address. + * @param bEpIdx Endpoint Number. + * @param bAddr Address. + * @retval None + */ +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t bAddr) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal &= USB_EPREG_MASK; + regVal |= bAddr; + regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX; + pcd_set_endpoint(USBx, bEpIdx,regVal); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_address(USB_TypeDef * USBx, uint32_t bEpIdx) { +#ifdef FSDEV_BUS_32BIT + (void) USBx; + return pma32[2*bEpIdx] & 0x0000FFFFu ; +#else + return *pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 0u); +#endif +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_address(USB_TypeDef * USBx, uint32_t bEpIdx) { +#ifdef FSDEV_BUS_32BIT + (void) USBx; + return pma32[2*bEpIdx + 1] & 0x0000FFFFu; +#else + return *pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 2u); +#endif +} + +#define pcd_get_ep_dbuf0_address pcd_get_ep_tx_address +#define pcd_get_ep_dbuf1_address pcd_get_ep_rx_address + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t addr) { +#ifdef FSDEV_BUS_32BIT + (void) USBx; + pma32[2*bEpIdx] = (pma32[2*bEpIdx] & 0xFFFF0000u) | (addr & 0x0000FFFCu); +#else + *pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 0u) = addr; +#endif +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t addr) { +#ifdef FSDEV_BUS_32BIT + (void) USBx; + pma32[2*bEpIdx + 1] = (pma32[2*bEpIdx + 1] & 0xFFFF0000u) | (addr & 0x0000FFFCu); +#else + *pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 2u) = addr; +#endif +} + +#define pcd_set_ep_dbuf0_address pcd_set_ep_tx_address +#define pcd_set_ep_dbuf1_address pcd_set_ep_rx_address + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) { +#ifdef FSDEV_BUS_32BIT + (void) USBx; + pma32[2*bEpIdx] = (pma32[2*bEpIdx] & ~0x03FF0000u) | ((wCount & 0x3FFu) << 16); +#else + volatile uint16_t * reg = pcd_ep_tx_cnt_ptr(USBx, bEpIdx); + *reg = (uint16_t) (*reg & (uint16_t) ~0x3FFU) | (wCount & 0x3FFU); +#endif +} + +#define pcd_set_ep_tx_dbuf0_cnt pcd_set_ep_tx_cnt + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_dbuf1_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) { +#ifdef FSDEV_BUS_32BIT + (void) USBx; + pma32[2*bEpIdx + 1] = (pma32[2*bEpIdx + 1] & ~0x03FF0000u) | ((wCount & 0x3FFu) << 16); +#else + volatile uint16_t * reg = pcd_ep_rx_cnt_ptr(USBx, bEpIdx); + *reg = (uint16_t) (*reg & (uint16_t) ~0x3FFU) | (wCount & 0x3FFU); +#endif +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_blsize_num_blocks(USB_TypeDef * USBx, uint32_t rxtx_idx, + uint32_t blocksize, uint32_t numblocks) { + /* Encode into register. When BLSIZE==1, we need to subtract 1 block count */ +#ifdef FSDEV_BUS_32BIT + (void) USBx; + pma32[rxtx_idx] = (pma32[rxtx_idx] & 0x0000FFFFu) | (blocksize << 31) | ((numblocks - blocksize) << 26); +#else + volatile uint16_t *pdwReg = pcd_btable_word_ptr(USBx, rxtx_idx*2u + 1u); + *pdwReg = (blocksize << 15) | ((numblocks - blocksize) << 10); +#endif +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_bufsize(USB_TypeDef * USBx, uint32_t rxtx_idx, uint32_t wCount) { + wCount = pcd_aligned_buffer_size(wCount); + + /* We assume that the buffer size is already aligned to hardware requirements. */ + uint16_t blocksize = (wCount > 62) ? 1 : 0; + uint16_t numblocks = wCount / (blocksize ? 32 : 2); + + /* There should be no remainder in the above calculation */ + TU_ASSERT((wCount - (numblocks * (blocksize ? 32 : 2))) == 0, /**/); + + /* Encode into register. When BLSIZE==1, we need to subtract 1 block count */ + pcd_set_ep_blsize_num_blocks(USBx, rxtx_idx, blocksize, numblocks); +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_dbuf0_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) { + pcd_set_ep_bufsize(USBx, 2*bEpIdx, wCount); +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) { + pcd_set_ep_bufsize(USBx, 2*bEpIdx + 1, wCount); +} + +#define pcd_set_ep_rx_dbuf1_cnt pcd_set_ep_rx_cnt + +/** + * @brief sets the status for tx transfer (bits STAT_TX[1:0]). + * @param USBx USB peripheral instance register address. + * @param bEpIdx Endpoint Number. + * @param wState new state + * @retval None + */ +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_status(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wState) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal &= USB_EPTX_DTOGMASK; + + /* toggle first bit ? */ + if((USB_EPTX_DTOG1 & (wState))!= 0U) + { + regVal ^= USB_EPTX_DTOG1; + } + /* toggle second bit ? */ + if((USB_EPTX_DTOG2 & ((uint32_t)(wState)))!= 0U) + { + regVal ^= USB_EPTX_DTOG2; + } + + regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX; + pcd_set_endpoint(USBx, bEpIdx, regVal); +} + +/** + * @brief sets the status for rx transfer (bits STAT_TX[1:0]) + * @param USBx USB peripheral instance register address. + * @param bEpIdx Endpoint Number. + * @param wState new state + * @retval None + */ + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_status(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wState) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal &= USB_EPRX_DTOGMASK; + + /* toggle first bit ? */ + if((USB_EPRX_DTOG1 & wState)!= 0U) { + regVal ^= USB_EPRX_DTOG1; + } + /* toggle second bit ? */ + if((USB_EPRX_DTOG2 & wState)!= 0U) { + regVal ^= USB_EPRX_DTOG2; + } + + regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX; + pcd_set_endpoint(USBx, bEpIdx, regVal); +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_status(USB_TypeDef * USBx, uint32_t bEpIdx) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + return (regVal & USB_EPRX_STAT) >> (12u); +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_rx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal &= USB_EPREG_MASK; + regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX|USB_EP_DTOG_RX; + pcd_set_endpoint(USBx, bEpIdx, regVal); +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_tx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal &= USB_EPREG_MASK; + regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX|USB_EP_DTOG_TX; + pcd_set_endpoint(USBx, bEpIdx, regVal); +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_rx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + if((regVal & USB_EP_DTOG_RX) != 0) { + pcd_rx_dtog(USBx,bEpIdx); + } +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_tx_dtog(USB_TypeDef * USBx, uint32_t bEpIdx) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + if((regVal & USB_EP_DTOG_TX) != 0) { + pcd_tx_dtog(USBx,bEpIdx); + } +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_kind(USB_TypeDef * USBx, uint32_t bEpIdx) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal |= USB_EP_KIND; + regVal &= USB_EPREG_MASK; + regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX; + pcd_set_endpoint(USBx, bEpIdx, regVal); +} + +TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_ep_kind(USB_TypeDef * USBx, uint32_t bEpIdx) { + uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx); + regVal &= USB_EPKIND_MASK; + regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX; + pcd_set_endpoint(USBx, bEpIdx, regVal); +} + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/src/portable/st/stm32_fsdev/fsdev_stm32.h b/src/portable/st/stm32_fsdev/fsdev_stm32.h new file mode 100644 index 00000000..b3fa11b8 --- /dev/null +++ b/src/portable/st/stm32_fsdev/fsdev_stm32.h @@ -0,0 +1,292 @@ +/* + * The MIT License (MIT) + * + * Copyright(c) N Conrad + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of STMicroelectronics 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. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef TUSB_FSDEV_STM32_H +#define TUSB_FSDEV_STM32_H + +#if CFG_TUSB_MCU == OPT_MCU_STM32F0 + #include "stm32f0xx.h" + #define FSDEV_PMA_SIZE (1024u) + // F0x2 models are crystal-less + // All have internal D+ pull-up + // 070RB: 2 x 16 bits/word memory LPM Support, BCD Support + // PMA dedicated to USB (no sharing with CAN) + +#elif CFG_TUSB_MCU == OPT_MCU_STM32F1 + #include "stm32f1xx.h" + #define FSDEV_PMA_SIZE (512u) + // NO internal Pull-ups + // *B, and *C: 2 x 16 bits/word + + // F1 names this differently from the rest + #define USB_CNTR_LPMODE USB_CNTR_LP_MODE + +#elif defined(STM32F302xB) || defined(STM32F302xC) || \ + defined(STM32F303xB) || defined(STM32F303xC) || \ + defined(STM32F373xC) + #include "stm32f3xx.h" + #define FSDEV_PMA_SIZE (512u) + // NO internal Pull-ups + // *B, and *C: 1 x 16 bits/word + // PMA dedicated to USB (no sharing with CAN) + +#elif defined(STM32F302x6) || defined(STM32F302x8) || \ + defined(STM32F302xD) || defined(STM32F302xE) || \ + defined(STM32F303xD) || defined(STM32F303xE) + #include "stm32f3xx.h" + #define FSDEV_PMA_SIZE (1024u) + // NO internal Pull-ups + // *6, *8, *D, and *E: 2 x 16 bits/word LPM Support + // When CAN clock is enabled, USB can use first 768 bytes ONLY. + +#elif CFG_TUSB_MCU == OPT_MCU_STM32L0 + #include "stm32l0xx.h" + #define FSDEV_PMA_SIZE (1024u) + +#elif CFG_TUSB_MCU == OPT_MCU_STM32L1 + #include "stm32l1xx.h" + #define FSDEV_PMA_SIZE (512u) + +#elif CFG_TUSB_MCU == OPT_MCU_STM32G4 + #include "stm32g4xx.h" + #define FSDEV_PMA_SIZE (1024u) + +#elif CFG_TUSB_MCU == OPT_MCU_STM32G0 + #include "stm32g0xx.h" + #define FSDEV_BUS_32BIT + #define FSDEV_PMA_SIZE (2048u) + #undef USB_PMAADDR + #define USB_PMAADDR USB_DRD_PMAADDR + #define USB_TypeDef USB_DRD_TypeDef + #define EP0R CHEP0R + #define USB_EP_CTR_RX USB_EP_VTRX + #define USB_EP_CTR_TX USB_EP_VTTX + #define USB_EP_T_FIELD USB_CHEP_UTYPE + #define USB_EPREG_MASK USB_CHEP_REG_MASK + #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK + #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK + #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1 + #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2 + #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1 + #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2 + #define USB_EPRX_STAT USB_CH_RX_VALID + #define USB_EPKIND_MASK USB_EP_KIND_MASK + #define USB USB_DRD_FS + #define USB_CNTR_FRES USB_CNTR_USBRST + #define USB_CNTR_RESUME USB_CNTR_L2RES + #define USB_ISTR_EP_ID USB_ISTR_IDN + #define USB_EPADDR_FIELD USB_CHEP_ADDR + #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY + #define USB_CNTR_FSUSP USB_CNTR_SUSPEN + +#elif CFG_TUSB_MCU == OPT_MCU_STM32H5 + #include "stm32h5xx.h" + #define FSDEV_BUS_32BIT + + #if !defined(USB_DRD_BASE) && defined(USB_DRD_FS_BASE) + #define USB_DRD_BASE USB_DRD_FS_BASE + #endif + + #define FSDEV_PMA_SIZE (2048u) + #undef USB_PMAADDR + #define USB_PMAADDR USB_DRD_PMAADDR + #define USB_TypeDef USB_DRD_TypeDef + #define EP0R CHEP0R + #define USB_EP_CTR_RX USB_EP_VTRX + #define USB_EP_CTR_TX USB_EP_VTTX + #define USB_EP_T_FIELD USB_CHEP_UTYPE + #define USB_EPREG_MASK USB_CHEP_REG_MASK + #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK + #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK + #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1 + #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2 + #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1 + #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2 + #define USB_EPRX_STAT USB_CH_RX_VALID + #define USB_EPKIND_MASK USB_EP_KIND_MASK + #define USB USB_DRD_FS + #define USB_CNTR_FRES USB_CNTR_USBRST + #define USB_CNTR_RESUME USB_CNTR_L2RES + #define USB_ISTR_EP_ID USB_ISTR_IDN + #define USB_EPADDR_FIELD USB_CHEP_ADDR + #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY + #define USB_CNTR_FSUSP USB_CNTR_SUSPEN + +#elif CFG_TUSB_MCU == OPT_MCU_STM32WB + #include "stm32wbxx.h" + #define FSDEV_PMA_SIZE (1024u) + /* ST provided header has incorrect value */ + #undef USB_PMAADDR + #define USB_PMAADDR USB1_PMAADDR + +#elif CFG_TUSB_MCU == OPT_MCU_STM32L4 + #include "stm32l4xx.h" + #define FSDEV_PMA_SIZE (1024u) + +#elif CFG_TUSB_MCU == OPT_MCU_STM32L5 + #include "stm32l5xx.h" + #define FSDEV_PMA_SIZE (1024u) + + #ifndef USB_PMAADDR + #define USB_PMAADDR (USB_BASE + (USB_PMAADDR_NS - USB_BASE_NS)) + #endif + +#else + #error You are using an untested or unimplemented STM32 variant. Please update the driver. + // This includes L1x0, L1x1, L1x2, L4x2 and L4x3, G1x1, G1x3, and G1x4 +#endif + +// This checks if the device has "LPM" +#if defined(USB_ISTR_L1REQ) +#define USB_ISTR_L1REQ_FORCED (USB_ISTR_L1REQ) +#else +#define USB_ISTR_L1REQ_FORCED ((uint16_t)0x0000U) +#endif + +#define USB_ISTR_ALL_EVENTS (USB_ISTR_PMAOVR | USB_ISTR_ERR | USB_ISTR_WKUP | USB_ISTR_SUSP | \ + USB_ISTR_RESET | USB_ISTR_SOF | USB_ISTR_ESOF | USB_ISTR_L1REQ_FORCED ) + +//--------------------------------------------------------------------+ +// +//--------------------------------------------------------------------+ + +#if TU_CHECK_MCU(OPT_MCU_STM32L1) && !defined(USBWakeUp_IRQn) + #define USBWakeUp_IRQn USB_FS_WKUP_IRQn +#endif + +static const IRQn_Type fsdev_irq[] = { + #if TU_CHECK_MCU(OPT_MCU_STM32F0, OPT_MCU_STM32L0, OPT_MCU_STM32L4) + USB_IRQn, + #elif CFG_TUSB_MCU == OPT_MCU_STM32F1 + USB_HP_CAN1_TX_IRQn, + USB_LP_CAN1_RX0_IRQn, + USBWakeUp_IRQn, + #elif CFG_TUSB_MCU == OPT_MCU_STM32F3 + // USB remap handles dcd functions + USB_HP_CAN_TX_IRQn, + USB_LP_CAN_RX0_IRQn, + USBWakeUp_IRQn, + #elif CFG_TUSB_MCU == OPT_MCU_STM32G0 + #ifdef STM32G0B0xx + USB_IRQn, + #else + USB_UCPD1_2_IRQn, + #endif + #elif TU_CHECK_MCU(OPT_MCU_STM32G4, OPT_MCU_STM32L1) + USB_HP_IRQn, + USB_LP_IRQn, + USBWakeUp_IRQn, + #elif CFG_TUSB_MCU == OPT_MCU_STM32H5 + USB_DRD_FS_IRQn, + #elif CFG_TUSB_MCU == OPT_MCU_STM32L5 + USB_FS_IRQn, + #elif CFG_TUSB_MCU == OPT_MCU_STM32WB + USB_HP_IRQn, + USB_LP_IRQn, + #else + #error Unknown arch in USB driver + #endif +}; +enum { FSDEV_IRQ_NUM = TU_ARRAY_SIZE(fsdev_irq) }; + +void dcd_int_enable(uint8_t rhport) { + (void)rhport; + + // forces write to RAM before allowing ISR to execute + __DSB(); __ISB(); + + #if CFG_TUSB_MCU == OPT_MCU_STM32F3 && defined(SYSCFG_CFGR1_USB_IT_RMP) + // Some STM32F302/F303 devices allow to remap the USB interrupt vectors from + // shared USB/CAN IRQs to separate CAN and USB IRQs. + // This dynamically checks if this remap is active to enable the right IRQs. + if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP) { + NVIC_EnableIRQ(USB_HP_IRQn); + NVIC_EnableIRQ(USB_LP_IRQn); + NVIC_EnableIRQ(USBWakeUp_RMP_IRQn); + } else + #endif + { + for (uint8_t i = 0; i < FSDEV_IRQ_NUM; i++) { + NVIC_EnableIRQ(fsdev_irq[i]); + } + } +} + +void dcd_int_disable(uint8_t rhport) { + (void)rhport; + + #if CFG_TUSB_MCU == OPT_MCU_STM32F3 && defined(SYSCFG_CFGR1_USB_IT_RMP) + // Some STM32F302/F303 devices allow to remap the USB interrupt vectors from + // shared USB/CAN IRQs to separate CAN and USB IRQs. + // This dynamically checks if this remap is active to enable the right IRQs. + if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP) { + NVIC_DisableIRQ(USB_HP_IRQn); + NVIC_DisableIRQ(USB_LP_IRQn); + NVIC_DisableIRQ(USBWakeUp_RMP_IRQn); + } else + #endif + { + for (uint8_t i = 0; i < FSDEV_IRQ_NUM; i++) { + NVIC_DisableIRQ(fsdev_irq[i]); + } + } + + // CMSIS has a membar after disabling interrupts +} + +// Define only on MCU with internal pull-up. BSP can define on MCU without internal PU. +#if defined(USB_BCDR_DPPU) + +void dcd_disconnect(uint8_t rhport) { + (void)rhport; + USB->BCDR &= ~(USB_BCDR_DPPU); +} + +void dcd_connect(uint8_t rhport) { + (void)rhport; + USB->BCDR |= USB_BCDR_DPPU; +} + +#elif defined(SYSCFG_PMC_USB_PU) // works e.g. on STM32L151 + +void dcd_disconnect(uint8_t rhport) { + (void)rhport; + SYSCFG->PMC &= ~(SYSCFG_PMC_USB_PU); +} + +void dcd_connect(uint8_t rhport) { + (void)rhport; + SYSCFG->PMC |= SYSCFG_PMC_USB_PU; +} +#endif + + +#endif /* TUSB_FSDEV_STM32_H */ diff --git a/src/portable/wch/ch32_usbfs_reg.h b/src/portable/wch/ch32_usbfs_reg.h new file mode 100644 index 00000000..0a50a616 --- /dev/null +++ b/src/portable/wch/ch32_usbfs_reg.h @@ -0,0 +1,111 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2024 Matthew Tran + * Copyright (c) 2024 hathach + * + * 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. + */ + +#ifndef USB_CH32_USBFS_REG_H +#define USB_CH32_USBFS_REG_H + +#if CFG_TUSB_MCU == OPT_MCU_CH32V307 + #include + #define USBHD_IRQn OTG_FS_IRQn + +#elif CFG_TUSB_MCU == OPT_MCU_CH32V20X + #include + +#elif CFG_TUSB_MCU == OPT_MCU_CH32F20X + #include +#endif + +// CTRL +#define USBFS_CTRL_DMA_EN (1 << 0) +#define USBFS_CTRL_CLR_ALL (1 << 1) +#define USBFS_CTRL_RESET_SIE (1 << 2) +#define USBFS_CTRL_INT_BUSY (1 << 3) +#define USBFS_CTRL_SYS_CTRL (1 << 4) +#define USBFS_CTRL_DEV_PUEN (1 << 5) +#define USBFS_CTRL_LOW_SPEED (1 << 6) +#define USBFS_CTRL_HOST_MODE (1 << 7) + +// INT_EN +#define USBFS_INT_EN_BUS_RST (1 << 0) +#define USBFS_INT_EN_DETECT (1 << 0) +#define USBFS_INT_EN_TRANSFER (1 << 1) +#define USBFS_INT_EN_SUSPEND (1 << 2) +#define USBFS_INT_EN_HST_SOF (1 << 3) +#define USBFS_INT_EN_FIFO_OV (1 << 4) +#define USBFS_INT_EN_DEV_NAK (1 << 6) +#define USBFS_INT_EN_DEV_SOF (1 << 7) + +// INT_FG +#define USBFS_INT_FG_BUS_RST (1 << 0) +#define USBFS_INT_FG_DETECT (1 << 0) +#define USBFS_INT_FG_TRANSFER (1 << 1) +#define USBFS_INT_FG_SUSPEND (1 << 2) +#define USBFS_INT_FG_HST_SOF (1 << 3) +#define USBFS_INT_FG_FIFO_OV (1 << 4) +#define USBFS_INT_FG_SIE_FREE (1 << 5) +#define USBFS_INT_FG_TOG_OK (1 << 6) +#define USBFS_INT_FG_IS_NAK (1 << 7) + +// INT_ST +#define USBFS_INT_ST_MASK_UIS_ENDP(x) (((x) >> 0) & 0x0F) +#define USBFS_INT_ST_MASK_UIS_TOKEN(x) (((x) >> 4) & 0x03) + +// UDEV_CTRL +#define USBFS_UDEV_CTRL_PORT_EN (1 << 0) +#define USBFS_UDEV_CTRL_GP_BIT (1 << 1) +#define USBFS_UDEV_CTRL_LOW_SPEED (1 << 2) +#define USBFS_UDEV_CTRL_DM_PIN (1 << 4) +#define USBFS_UDEV_CTRL_DP_PIN (1 << 5) +#define USBFS_UDEV_CTRL_PD_DIS (1 << 7) + +// TX_CTRL +#define USBFS_EP_T_RES_MASK (3 << 0) +#define USBFS_EP_T_TOG (1 << 2) +#define USBFS_EP_T_AUTO_TOG (1 << 3) + +#define USBFS_EP_T_RES_ACK (0 << 0) +#define USBFS_EP_T_RES_NYET (1 << 0) +#define USBFS_EP_T_RES_NAK (2 << 0) +#define USBFS_EP_T_RES_STALL (3 << 0) + +// RX_CTRL +#define USBFS_EP_R_RES_MASK (3 << 0) +#define USBFS_EP_R_TOG (1 << 2) +#define USBFS_EP_R_AUTO_TOG (1 << 3) + +#define USBFS_EP_R_RES_ACK (0 << 0) +#define USBFS_EP_R_RES_NYET (1 << 0) +#define USBFS_EP_R_RES_NAK (2 << 0) +#define USBFS_EP_R_RES_STALL (3 << 0) + +// token PID +#define PID_OUT 0 +#define PID_SOF 1 +#define PID_IN 2 +#define PID_SETUP 3 + +#endif // USB_CH32_USBFS_REG_H diff --git a/src/portable/wch/ch32_usbhs_reg.h b/src/portable/wch/ch32_usbhs_reg.h new file mode 100644 index 00000000..130e4f22 --- /dev/null +++ b/src/portable/wch/ch32_usbhs_reg.h @@ -0,0 +1,383 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2024 Matthew Tran + * Copyright (c) 2024 hathach + * + * 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. + */ + +#ifndef USB_CH32_USBHS_REG_H +#define USB_CH32_USBHS_REG_H + +#if CFG_TUSB_MCU == OPT_MCU_CH32V307 + #include +#elif CFG_TUSB_MCU == OPT_MCU_CH32F20X + #include +#endif + +/******************* GLOBAL ******************/ + +// USB CONTROL +#define USBHS_CONTROL_OFFSET 0x00 +#define USBHS_DMA_EN (1 << 0) +#define USBHS_ALL_CLR (1 << 1) +#define USBHS_FORCE_RST (1 << 2) +#define USBHS_INT_BUSY_EN (1 << 3) +#define USBHS_DEV_PU_EN (1 << 4) +#define USBHS_SPEED_MASK (3 << 5) +#define USBHS_FULL_SPEED (0 << 5) +#define USBHS_HIGH_SPEED (1 << 5) +#define USBHS_LOW_SPEED (2 << 5) +#define USBHS_HOST_MODE (1 << 7) + +// USB_INT_EN +#define USBHS_INT_EN_OFFSET 0x02 +#define USBHS_BUS_RST_EN (1 << 0) +#define USBHS_DETECT_EN (1 << 0) +#define USBHS_TRANSFER_EN (1 << 1) +#define USBHS_SUSPEND_EN (1 << 2) +#define USBHS_SOF_ACT_EN (1 << 3) +#define USBHS_FIFO_OV_EN (1 << 4) +#define USBHS_SETUP_ACT_EN (1 << 5) +#define USBHS_ISO_ACT_EN (1 << 6) +#define USBHS_DEV_NAK_EN (1 << 7) + +// USB DEV AD +#define USBHS_DEV_AD_OFFSET 0x03 + +// USB FRAME_NO +#define USBHS_FRAME_NO_OFFSET 0x04 +#define USBHS_FRAME_NO_NUM_MASK (0x7FF) +#define USBHS_FRAME_NO_MICROFRAME_SHIFT (11) +#define USBHS_FRAME_NO_MICROFRAME_MASK (0x7 << USBHS_FRAME_NO_MICROFRAME_SHIFT) + +// USB SUSPEND +#define USBHS_SUSPEND_OFFSET 0x06 +#define USBHS_DEV_REMOTE_WAKEUP (1 << 2) +#define USBHS_LINESTATE_MASK (2 << 4) /* Read Only */ + +// RESERVED0 + +// USB SPEED TYPE +#define USBHS_SPEED_TYPE_OFFSET 0x08 +#define USBHS_SPEED_TYPE_MASK 0x03 +#define USBHS_SPEED_TYPE_FULL 0 +#define USBHS_SPEED_TYPE_HIGH 1 +#define USBHS_SPEED_TYPE_LOW 2 + +// USB_MIS_ST +#define USBHS_MIS_ST_OFFSET 0x09 +#define USBHS_SPLIT_CAN (1 << 0) +#define USBHS_ATTACH (1 << 1) +#define USBHS_SUSPEND (1 << 2) +#define USBHS_BUS_RESET (1 << 3) +#define USBHS_R_FIFO_RDY (1 << 4) +#define USBHS_SIE_FREE (1 << 5) +#define USBHS_SOF_ACT (1 << 6) +#define USBHS_SOF_PRES (1 << 7) + +// INT_FLAG +#define USBHS_INT_FLAG_OFFSET 0x0A +#define USBHS_BUS_RST_FLAG (1 << 0) +#define USBHS_DETECT_FLAG (1 << 0) +#define USBHS_TRANSFER_FLAG (1 << 1) +#define USBHS_SUSPEND_FLAG (1 << 2) +#define USBHS_HST_SOF_FLAG (1 << 3) +#define USBHS_FIFO_OV_FLAG (1 << 4) +#define USBHS_SETUP_FLAG (1 << 5) +#define USBHS_ISO_ACT_FLAG (1 << 6) + +// INT_ST +#define USBHS_INT_ST_OFFSET 0x0B +#define USBHS_DEV_UIS_IS_NAK (1 << 7) +#define USBHS_DEV_UIS_TOG_OK (1 << 6) +#define MASK_UIS_TOKEN (3 << 4) +#define USBHS_TOKEN_PID_OUT (0 << 4) +#define USBHS_TOKEN_PID_SOF (1 << 4) +#define USBHS_TOKEN_PID_IN (2 << 4) +#define USBHS_TOKEN_PID_SETUP (3 << 4) +#define MASK_UIS_ENDP (0x0F) +#define MASK_UIS_H_RES (0x0F) + +#define USBHS_TOGGLE_OK (0x40) +#define USBHS_HOST_RES (0x0f) + +//USB_RX_LEN +#define USBHS_RX_LEN_OFFSET 0x0C +/******************* DEVICE ******************/ + +//UEP_CONFIG +#define USBHS_UEP_CONFIG_OFFSET 0x10 +#define USBHS_EP0_T_EN (1 << 0) +#define USBHS_EP0_R_EN (1 << 16) + +#define USBHS_EP1_T_EN (1 << 1) +#define USBHS_EP1_R_EN (1 << 17) + +#define USBHS_EP2_T_EN (1 << 2) +#define USBHS_EP2_R_EN (1 << 18) + +#define USBHS_EP3_T_EN (1 << 3) +#define USBHS_EP3_R_EN (1 << 19) + +#define USBHS_EP4_T_EN (1 << 4) +#define USBHS_EP4_R_EN (1 << 20) + +#define USBHS_EP5_T_EN (1 << 5) +#define USBHS_EP5_R_EN (1 << 21) + +#define USBHS_EP6_T_EN (1 << 6) +#define USBHS_EP6_R_EN (1 << 22) + +#define USBHS_EP7_T_EN (1 << 7) +#define USBHS_EP7_R_EN (1 << 23) + +#define USBHS_EP8_T_EN (1 << 8) +#define USBHS_EP8_R_EN (1 << 24) + +#define USBHS_EP9_T_EN (1 << 9) +#define USBHS_EP9_R_EN (1 << 25) + +#define USBHS_EP10_T_EN (1 << 10) +#define USBHS_EP10_R_EN (1 << 26) + +#define USBHS_EP11_T_EN (1 << 11) +#define USBHS_EP11_R_EN (1 << 27) + +#define USBHS_EP12_T_EN (1 << 12) +#define USBHS_EP12_R_EN (1 << 28) + +#define USBHS_EP13_T_EN (1 << 13) +#define USBHS_EP13_R_EN (1 << 29) + +#define USBHS_EP14_T_EN (1 << 14) +#define USBHS_EP14_R_EN (1 << 30) + +#define USBHS_EP15_T_EN (1 << 15) +#define USBHS_EP15_R_EN (1 << 31) + +//UEP_TYPE +#define USBHS_UEP_TYPE_OFFSET 0x14 +#define USBHS_EP0_T_TYP (1 << 0) +#define USBHS_EP0_R_TYP (1 << 16) + +#define USBHS_EP1_T_TYP (1 << 1) +#define USBHS_EP1_R_TYP (1 << 17) + +#define USBHS_EP2_T_TYP (1 << 2) +#define USBHS_EP2_R_TYP (1 << 18) + +#define USBHS_EP3_T_TYP (1 << 3) +#define USBHS_EP3_R_TYP (1 << 19) + +#define USBHS_EP4_T_TYP (1 << 4) +#define USBHS_EP4_R_TYP (1 << 20) + +#define USBHS_EP5_T_TYP (1 << 5) +#define USBHS_EP5_R_TYP (1 << 21) + +#define USBHS_EP6_T_TYP (1 << 6) +#define USBHS_EP6_R_TYP (1 << 22) + +#define USBHS_EP7_T_TYP (1 << 7) +#define USBHS_EP7_R_TYP (1 << 23) + +#define USBHS_EP8_T_TYP (1 << 8) +#define USBHS_EP8_R_TYP (1 << 24) + +#define USBHS_EP9_T_TYP (1 << 8) +#define USBHS_EP9_R_TYP (1 << 25) + +#define USBHS_EP10_T_TYP (1 << 10) +#define USBHS_EP10_R_TYP (1 << 26) + +#define USBHS_EP11_T_TYP (1 << 11) +#define USBHS_EP11_R_TYP (1 << 27) + +#define USBHS_EP12_T_TYP (1 << 12) +#define USBHS_EP12_R_TYP (1 << 28) + +#define USBHS_EP13_T_TYP (1 << 13) +#define USBHS_EP13_R_TYP (1 << 29) + +#define USBHS_EP14_T_TYP (1 << 14) +#define USBHS_EP14_R_TYP (1 << 30) + +#define USBHS_EP15_T_TYP (1 << 15) +#define USBHS_EP15_R_TYP (1 << 31) + +/* BUF_MOD UEP1~15 */ +#define USBHS_BUF_MOD_OFFSET 0x18 +#define USBHS_EP0_BUF_MOD (1 << 0) +#define USBHS_EP0_ISO_BUF_MOD (1 << 16) + +#define USBHS_EP1_BUF_MOD (1 << 1) +#define USBHS_EP1_ISO_BUF_MOD (1 << 17) + +#define USBHS_EP2_BUF_MOD (1 << 2) +#define USBHS_EP2_ISO_BUF_MOD (1 << 18) + +#define USBHS_EP3_BUF_MOD (1 << 3) +#define USBHS_EP3_ISO_BUF_MOD (1 << 19) + +#define USBHS_EP4_BUF_MOD (1 << 4) +#define USBHS_EP4_ISO_BUF_MOD (1 << 20) + +#define USBHS_EP5_BUF_MOD (1 << 5) +#define USBHS_EP5_ISO_BUF_MOD (1 << 21) + +#define USBHS_EP6_BUF_MOD (1 << 6) +#define USBHS_EP6_ISO_BUF_MOD (1 << 22) + +#define USBHS_EP7_BUF_MOD (1 << 7) +#define USBHS_EP7_ISO_BUF_MOD (1 << 23) + +#define USBHS_EP8_BUF_MOD (1 << 8) +#define USBHS_EP8_ISO_BUF_MOD (1 << 24) + +#define USBHS_EP9_BUF_MOD (1 << 9) +#define USBHS_EP9_ISO_BUF_MOD (1 << 25) + +#define USBHS_EP10_BUF_MOD (1 << 10) +#define USBHS_EP10_ISO_BUF_MOD (1 << 26) + +#define USBHS_EP11_BUF_MOD (1 << 11) +#define USBHS_EP11_ISO_BUF_MOD (1 << 27) + +#define USBHS_EP12_BUF_MOD (1 << 12) +#define USBHS_EP12_ISO_BUF_MOD (1 << 28) + +#define USBHS_EP13_BUF_MOD (1 << 13) +#define USBHS_EP13_ISO_BUF_MOD (1 << 29) + +#define USBHS_EP14_BUF_MOD (1 << 14) +#define USBHS_EP14_ISO_BUF_MOD (1 << 30) + +#define USBHS_EP15_BUF_MOD (1 << 15) +#define USBHS_EP15_ISO_BUF_MOD (1 << 31) +//USBHS_EPn_T_EN USBHS_EPn_R_EN USBHS_EPn_BUF_MOD Description: Arrange from low to high with UEPn_DMA as the starting address +// 0 0 x The endpoint is disabled and the UEPn_*_DMA buffers are not used. +// 1 0 0 The first address of the receive (OUT) buffer is UEPn_RX_DMA +// 1 0 1 RB_UEPn_RX_TOG[0]=0, use buffer UEPn_RX_DMA RB_UEPn_RX_TOG[0]=1, use buffer UEPn_TX_DMA +// 0 1 0 The first address of the transmit (IN) buffer is UEPn_TX_DMA. +// 0 1 1 RB_UEPn_TX_TOG[0]=0, use buffer UEPn_TX_DMA RB_UEPn_TX_TOG[0]=1, use buffer UEPn_RX_DMA + +/* USB0_DMA */ +#define USBHS_UEP0_DMA_OFFSET(n) (0x1C) // endpoint 0 DMA buffer address + +/* USBX_RX_DMA */ +#define USBHS_UEPx_RX_DMA_OFFSET(n) (0x1C + 4 * (n)) // endpoint x DMA buffer address + +#define USBHS_UEPx_TX_DMA_OFFSET(n) (0x58 + 4 * (n)) // endpoint x DMA buffer address + +#define USBHS_UEPx_MAX_LEN_OFFSET(n) (0x98 + 4 * (n)) // endpoint x DMA buffer address + +#define USBHS_UEPx_T_LEN_OFFSET(n) (0xD8 + 4 * (n)) // endpoint x DMA buffer address +#define USBHS_UEPx_TX_CTRL_OFFSET(n) (0xD8 + 4 * (n) + 2) // endpoint x DMA buffer address +#define USBHS_UEPx_RX_CTRL_OFFSET(n) (0xD8 + 4 * (n) + 3) // endpoint x DMA buffer address + +// UEPn_T_LEN +#define USBHS_EP_T_LEN_MASK (0x7FF) + +//UEPn_TX_CTRL +#define USBHS_EP_T_RES_MASK (3 << 0) +#define USBHS_EP_T_RES_ACK (0 << 0) +#define USBHS_EP_T_RES_NYET (1 << 0) +#define USBHS_EP_T_RES_NAK (2 << 0) +#define USBHS_EP_T_RES_STALL (3 << 0) + +#define USBHS_EP_T_TOG_MASK (3 << 3) +#define USBHS_EP_T_TOG_0 (0 << 3) +#define USBHS_EP_T_TOG_1 (1 << 3) +#define USBHS_EP_T_TOG_2 (2 << 3) +#define USBHS_EP_T_TOG_M (3 << 3) + +#define USBHS_EP_T_AUTOTOG (1 << 5) + +//UEPn_RX_CTRL +#define USBHS_EP_R_RES_MASK (3 << 0) +#define USBHS_EP_R_RES_ACK (0 << 0) +#define USBHS_EP_R_RES_NYET (1 << 0) +#define USBHS_EP_R_RES_NAK (2 << 0) +#define USBHS_EP_R_RES_STALL (3 << 0) + +#define USBHS_EP_R_TOG_MASK (3 << 3) +#define USBHS_EP_R_TOG_0 (0 << 3) +#define USBHS_EP_R_TOG_1 (1 << 3) +#define USBHS_EP_R_TOG_2 (2 << 3) +#define USBHS_EP_R_TOG_M (3 << 3) + +#define USBHS_EP_R_AUTOTOG (1 << 5) + +#define USBHS_TOG_MATCH (1 << 6) + +/******************* HOST ******************/ +// USB HOST_CTRL +#define USBHS_SEND_BUS_RESET (1 << 0) +#define USBHS_SEND_BUS_SUSPEND (1 << 1) +#define USBHS_SEND_BUS_RESUME (1 << 2) +#define USBHS_REMOTE_WAKE (1 << 3) +#define USBHS_PHY_SUSPENDM (1 << 4) +#define USBHS_UH_SOFT_FREE (1 << 6) +#define USBHS_SEND_SOF_EN (1 << 7) + +//UH_CONFIG +#define USBHS_HOST_TX_EN (1 << 3) +#define USBHS_HOST_RX_EN (1 << 18) + +// HOST_EP_TYPE +#define USBHS_ENDP_TX_ISO (1 << 3) +#define USBHS_ENDP_RX_ISO (1 << (16 + 2)) + +// R32_UH_EP_PID +#define USBHS_HOST_MASK_TOKEN (0x0f) +#define USBHS_HOST_MASK_ENDP (0x0f << 4) + +//R8_UH_RX_CTRL +#define USBHS_EP_R_RES_MASK (3 << 0) +#define USBHS_EP_R_RES_ACK (0 << 0) +#define USBHS_EP_R_RES_NYET (1 << 0) +#define USBHS_EP_R_RES_NAK (2 << 0) +#define USBHS_EP_R_RES_STALL (3 << 0) + +#define USBHS_UH_R_RES_NO (1 << 2) +#define USBHS_UH_R_TOG_1 (1 << 3) +#define USBHS_UH_R_TOG_2 (2 << 3) +#define USBHS_UH_R_TOG_3 (3 << 3) +#define USBHS_UH_R_TOG_AUTO (1 << 5) +#define USBHS_UH_R_DATA_NO (1 << 6) +//R8_UH_TX_CTRL +#define USBHS_UH_T_RES_MASK (3 << 0) +#define USBHS_UH_T_RES_ACK (0 << 0) +#define USBHS_UH_T_RES_NYET (1 << 0) +#define USBHS_UH_T_RES_NAK (2 << 0) +#define USBHS_UH_T_RES_STALL (3 << 0) + +#define USBHS_UH_T_RES_NO (1 << 2) +#define USBHS_UH_T_TOG_1 (1 << 3) +#define USBHS_UH_T_TOG_2 (2 << 3) +#define USBHS_UH_T_TOG_3 (3 << 3) +#define USBHS_UH_T_TOG_AUTO (1 << 5) +#define USBHS_UH_T_DATA_NO (1 << 6) + + +#endif diff --git a/src/portable/wch/dcd_ch32_usbfs.c b/src/portable/wch/dcd_ch32_usbfs.c new file mode 100644 index 00000000..9ed370b4 --- /dev/null +++ b/src/portable/wch/dcd_ch32_usbfs.c @@ -0,0 +1,344 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2024 Matthew Tran + * Copyright (c) 2024 hathach + * + * 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" + +#if CFG_TUD_ENABLED && defined(TUP_USBIP_WCH_USBFS) && CFG_TUD_WCH_USBIP_USBFS + +#include "device/dcd.h" +#include "ch32_usbfs_reg.h" + +/* private defines */ +#define EP_MAX (8) + +#define EP_DMA(ep) ((&USBOTG_FS->UEP0_DMA)[ep]) +#define EP_TX_LEN(ep) ((&USBOTG_FS->UEP0_TX_LEN)[2 * ep]) +#define EP_TX_CTRL(ep) ((&USBOTG_FS->UEP0_TX_CTRL)[4 * ep]) +#define EP_RX_CTRL(ep) ((&USBOTG_FS->UEP0_RX_CTRL)[4 * ep]) + +/* private data */ +struct usb_xfer { + bool valid; + uint8_t* buffer; + size_t len; + size_t processed_len; + size_t max_size; +}; + +static struct { + bool ep0_tog; + bool isochronous[EP_MAX]; + struct usb_xfer xfer[EP_MAX][2]; + TU_ATTR_ALIGNED(4) uint8_t buffer[EP_MAX][2][64]; + TU_ATTR_ALIGNED(4) struct { + // OUT transfers >64 bytes will overwrite queued IN data! + uint8_t out[64]; + uint8_t in[1023]; + uint8_t pad; + } ep3_buffer; +} data; + +/* private helpers */ +static void update_in(uint8_t rhport, uint8_t ep, bool force) { + struct usb_xfer* xfer = &data.xfer[ep][TUSB_DIR_IN]; + if (xfer->valid) { + if (force || xfer->len) { + size_t len = TU_MIN(xfer->max_size, xfer->len); + if (ep == 0) { + memcpy(data.buffer[ep][TUSB_DIR_OUT], xfer->buffer, len); // ep0 uses same chunk + } else if (ep == 3) { + memcpy(data.ep3_buffer.in, xfer->buffer, len); + } else { + memcpy(data.buffer[ep][TUSB_DIR_IN], xfer->buffer, len); + } + xfer->buffer += len; + xfer->len -= len; + xfer->processed_len += len; + + EP_TX_LEN(ep) = len; + if (ep == 0) { + EP_TX_CTRL(0) = USBFS_EP_T_RES_ACK | (data.ep0_tog ? USBFS_EP_T_TOG : 0); + data.ep0_tog = !data.ep0_tog; + } else if (data.isochronous[ep]) { + EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~(USBFS_EP_T_RES_MASK)) | USBFS_EP_T_RES_NYET; + } else { + EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~(USBFS_EP_T_RES_MASK)) | USBFS_EP_T_RES_ACK; + } + } else { + xfer->valid = false; + EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~(USBFS_EP_T_RES_MASK)) | USBFS_EP_T_RES_NAK; + dcd_event_xfer_complete( + rhport, ep | TUSB_DIR_IN_MASK, xfer->processed_len, + XFER_RESULT_SUCCESS, true); + } + } +} + +static void update_out(uint8_t rhport, uint8_t ep, size_t rx_len) { + struct usb_xfer* xfer = &data.xfer[ep][TUSB_DIR_OUT]; + if (xfer->valid) { + size_t len = TU_MIN(xfer->max_size, TU_MIN(xfer->len, rx_len)); + if (ep == 3) { + memcpy(xfer->buffer, data.ep3_buffer.out, len); + } else { + memcpy(xfer->buffer, data.buffer[ep][TUSB_DIR_OUT], len); + } + xfer->buffer += len; + xfer->len -= len; + xfer->processed_len += len; + + if (xfer->len == 0 || len < xfer->max_size) { + xfer->valid = false; + dcd_event_xfer_complete(rhport, ep, xfer->processed_len, XFER_RESULT_SUCCESS, true); + } + + if (ep == 0) { + EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK; + } + } +} + +/* public functions */ +void dcd_init(uint8_t rhport) { + // init registers + USBOTG_FS->BASE_CTRL = USBFS_CTRL_SYS_CTRL | USBFS_CTRL_INT_BUSY | USBFS_CTRL_DMA_EN; + USBOTG_FS->UDEV_CTRL = USBFS_UDEV_CTRL_PD_DIS | USBFS_UDEV_CTRL_PORT_EN; + USBOTG_FS->DEV_ADDR = 0x00; + + USBOTG_FS->INT_FG = 0xFF; + USBOTG_FS->INT_EN = USBFS_INT_EN_BUS_RST | USBFS_INT_EN_TRANSFER | USBFS_INT_EN_SUSPEND; + + // setup endpoint 0 + EP_DMA(0) = (uint32_t) &data.buffer[0][0]; + EP_TX_LEN(0) = 0; + EP_TX_CTRL(0) = USBFS_EP_T_RES_NAK; + EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK; + + // enable other endpoints but NAK everything + USBOTG_FS->UEP4_1_MOD = 0xCC; + USBOTG_FS->UEP2_3_MOD = 0xCC; + USBOTG_FS->UEP5_6_MOD = 0xCC; + USBOTG_FS->UEP7_MOD = 0x0C; + + for (uint8_t ep = 1; ep < EP_MAX; ep++) { + EP_DMA(ep) = (uint32_t) &data.buffer[ep][0]; + EP_TX_LEN(ep) = 0; + EP_TX_CTRL(ep) = USBFS_EP_T_AUTO_TOG | USBFS_EP_T_RES_NAK; + EP_RX_CTRL(ep) = USBFS_EP_R_AUTO_TOG | USBFS_EP_R_RES_NAK; + } + EP_DMA(3) = (uint32_t) &data.ep3_buffer.out[0]; + + dcd_connect(rhport); +} + +void dcd_int_handler(uint8_t rhport) { + (void) rhport; + uint8_t status = USBOTG_FS->INT_FG; + if (status & USBFS_INT_FG_TRANSFER) { + uint8_t ep = USBFS_INT_ST_MASK_UIS_ENDP(USBOTG_FS->INT_ST); + uint8_t token = USBFS_INT_ST_MASK_UIS_TOKEN(USBOTG_FS->INT_ST); + + switch (token) { + case PID_OUT: { + uint16_t rx_len = USBOTG_FS->RX_LEN; + update_out(rhport, ep, rx_len); + break; + } + + case PID_IN: + update_in(rhport, ep, false); + break; + + case PID_SETUP: + // setup clears stall + EP_TX_CTRL(0) = USBFS_EP_T_RES_NAK; + EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK; + + data.ep0_tog = true; + dcd_event_setup_received(rhport, &data.buffer[0][TUSB_DIR_OUT][0], true); + break; + } + + USBOTG_FS->INT_FG = USBFS_INT_FG_TRANSFER; + } else if (status & USBFS_INT_FG_BUS_RST) { + data.ep0_tog = true; + data.xfer[0][TUSB_DIR_OUT].max_size = 64; + data.xfer[0][TUSB_DIR_IN].max_size = 64; + + dcd_event_bus_signal(rhport, DCD_EVENT_BUS_RESET, true); + + USBOTG_FS->DEV_ADDR = 0x00; + EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK; + + USBOTG_FS->INT_FG = USBFS_INT_FG_BUS_RST; + } else if (status & USBFS_INT_FG_SUSPEND) { + dcd_event_t event = {.rhport = rhport, .event_id = DCD_EVENT_SUSPEND}; + dcd_event_handler(&event, true); + USBOTG_FS->INT_FG = USBFS_INT_FG_SUSPEND; + } +} + +void dcd_int_enable(uint8_t rhport) { + (void) rhport; + NVIC_EnableIRQ(USBHD_IRQn); +} + +void dcd_int_disable(uint8_t rhport) { + (void) rhport; + NVIC_DisableIRQ(USBHD_IRQn); +} + +void dcd_set_address(uint8_t rhport, uint8_t dev_addr) { + (void) dev_addr; + dcd_edpt_xfer(rhport, 0x80, NULL, 0); // zlp status response +} + +void dcd_remote_wakeup(uint8_t rhport) { + (void) rhport; + // TODO optional +} + +void dcd_connect(uint8_t rhport) { + (void) rhport; + USBOTG_FS->BASE_CTRL |= USBFS_CTRL_DEV_PUEN; +} + +void dcd_disconnect(uint8_t rhport) { + (void) rhport; + USBOTG_FS->BASE_CTRL &= ~USBFS_CTRL_DEV_PUEN; +} + +void dcd_sof_enable(uint8_t rhport, bool en) { + (void) rhport; + (void) en; + + // TODO implement later +} + +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 && + request->bRequest == TUSB_REQ_SET_ADDRESS) { + USBOTG_FS->DEV_ADDR = (uint8_t) request->wValue; + } + EP_TX_CTRL(0) = USBFS_EP_T_RES_NAK; + EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK; +} + +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_ep) { + (void) rhport; + uint8_t ep = tu_edpt_number(desc_ep->bEndpointAddress); + uint8_t dir = tu_edpt_dir(desc_ep->bEndpointAddress); + TU_ASSERT(ep < EP_MAX); + + data.isochronous[ep] = desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS; + data.xfer[ep][dir].max_size = tu_edpt_packet_size(desc_ep); + + if (ep != 0) { + if (dir == TUSB_DIR_OUT) { + if (data.isochronous[ep]) { + EP_RX_CTRL(ep) = USBFS_EP_R_AUTO_TOG | USBFS_EP_R_RES_NYET; + } else { + EP_RX_CTRL(ep) = USBFS_EP_R_AUTO_TOG | USBFS_EP_R_RES_ACK; + } + } else { + EP_TX_LEN(ep) = 0; + EP_TX_CTRL(ep) = USBFS_EP_T_AUTO_TOG | USBFS_EP_T_RES_NAK; + } + } + return true; +} + +void dcd_edpt_close_all(uint8_t rhport) { + (void) rhport; + // TODO optional +} + +void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) { + (void) rhport; + (void) ep_addr; + // TODO optional +} + +bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) { + (void) rhport; + uint8_t ep = tu_edpt_number(ep_addr); + uint8_t dir = tu_edpt_dir(ep_addr); + + struct usb_xfer* xfer = &data.xfer[ep][dir]; + dcd_int_disable(rhport); + xfer->valid = true; + xfer->buffer = buffer; + xfer->len = total_bytes; + xfer->processed_len = 0; + dcd_int_enable(rhport); + + if (dir == TUSB_DIR_IN) { + update_in(rhport, ep, true); + } + return true; +} + +void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { + (void) rhport; + uint8_t ep = tu_edpt_number(ep_addr); + uint8_t dir = tu_edpt_dir(ep_addr); + if (ep == 0) { + if (dir == TUSB_DIR_OUT) { + EP_RX_CTRL(0) = USBFS_EP_R_RES_STALL; + } else { + EP_TX_LEN(0) = 0; + EP_TX_CTRL(0) = USBFS_EP_T_RES_STALL; + } + } else { + if (dir == TUSB_DIR_OUT) { + EP_RX_CTRL(ep) = (EP_RX_CTRL(ep) & ~USBFS_EP_R_RES_MASK) | USBFS_EP_R_RES_STALL; + } else { + EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~USBFS_EP_T_RES_MASK) | USBFS_EP_T_RES_STALL; + } + } +} + +void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { + (void) rhport; + uint8_t ep = tu_edpt_number(ep_addr); + uint8_t dir = tu_edpt_dir(ep_addr); + if (ep == 0) { + if (dir == TUSB_DIR_OUT) { + EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK; + } + } else { + if (dir == TUSB_DIR_OUT) { + EP_RX_CTRL(ep) = (EP_RX_CTRL(ep) & ~(USBFS_EP_R_RES_MASK | USBFS_EP_R_TOG)) | USBFS_EP_R_RES_ACK; + } else { + EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~(USBFS_EP_T_RES_MASK | USBFS_EP_T_TOG)) | USBFS_EP_T_RES_NAK; + } + } +} + +#endif diff --git a/src/portable/wch/dcd_ch32_usbhs.c b/src/portable/wch/dcd_ch32_usbhs.c new file mode 100644 index 00000000..622f9c50 --- /dev/null +++ b/src/portable/wch/dcd_ch32_usbhs.c @@ -0,0 +1,420 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2022 Greg Davill + * Copyright (c) 2023 Denis Krasutski + * + * 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" + +#if CFG_TUD_ENABLED && defined(TUP_USBIP_WCH_USBHS) && CFG_TUD_WCH_USBIP_USBHS +#include "ch32_usbhs_reg.h" + +#include "device/dcd.h" + +// Max number of bi-directional endpoints including EP0 +#define EP_MAX 16 + +typedef struct { + uint8_t* buffer; + uint16_t total_len; + uint16_t queued_len; + uint16_t max_size; + bool is_last_packet; + bool is_iso; +} xfer_ctl_t; + +typedef enum { + EP_RESPONSE_ACK, + EP_RESPONSE_NAK, +} ep_response_list_t; + +#define XFER_CTL_BASE(_ep, _dir) &xfer_status[_ep][_dir] +static xfer_ctl_t xfer_status[EP_MAX][2]; + +#define EP_TX_LEN(ep) *(volatile uint16_t *)((volatile uint16_t *)&(USBHSD->UEP0_TX_LEN) + (ep) * 2) +#define EP_TX_CTRL(ep) *(volatile uint8_t *)((volatile uint8_t *)&(USBHSD->UEP0_TX_CTRL) + (ep) * 4) +#define EP_RX_CTRL(ep) *(volatile uint8_t *)((volatile uint8_t *)&(USBHSD->UEP0_RX_CTRL) + (ep) * 4) +#define EP_RX_MAX_LEN(ep) *(volatile uint16_t *)((volatile uint16_t *)&(USBHSD->UEP0_MAX_LEN) + (ep) * 2) + +#define EP_TX_DMA_ADDR(ep) *(volatile uint32_t *)((volatile uint32_t *)&(USBHSD->UEP1_TX_DMA) + (ep - 1)) +#define EP_RX_DMA_ADDR(ep) *(volatile uint32_t *)((volatile uint32_t *)&(USBHSD->UEP1_RX_DMA) + (ep - 1)) + +/* Endpoint Buffer */ +TU_ATTR_ALIGNED(4) static uint8_t ep0_buffer[CFG_TUD_ENDPOINT0_SIZE]; + +static void ep_set_response_and_toggle(uint8_t ep_num, tusb_dir_t ep_dir, ep_response_list_t response_type) { + if (ep_dir == TUSB_DIR_IN) { + uint8_t response = (response_type == EP_RESPONSE_ACK) ? USBHS_EP_T_RES_ACK : USBHS_EP_T_RES_NAK; + if (ep_num == 0) { + if (response_type == EP_RESPONSE_ACK) { + if (EP_TX_LEN(ep_num) == 0) { + EP_TX_CTRL(ep_num) |= USBHS_EP_T_TOG_1; + } else { + EP_TX_CTRL(ep_num) ^= USBHS_EP_T_TOG_1; + } + } + } + if (xfer_status[ep_num][TUSB_DIR_IN].is_iso == true) { + EP_TX_CTRL(ep_num) = USBHS_EP_T_AUTOTOG; + } else { + EP_TX_CTRL(ep_num) = (EP_TX_CTRL(ep_num) & ~(USBHS_EP_T_RES_MASK)) | response; + } + } else { + uint8_t response = (response_type == EP_RESPONSE_ACK) ? USBHS_EP_R_RES_ACK : USBHS_EP_R_RES_NAK; + if (ep_num == 0) { + if (response_type == EP_RESPONSE_ACK) { + if (xfer_status[ep_num][TUSB_DIR_OUT].queued_len == 0) { + EP_RX_CTRL(ep_num) |= USBHS_EP_R_TOG_1; + } + } else { + EP_RX_CTRL(ep_num) ^= USBHS_EP_R_TOG_1; + } + } + EP_RX_CTRL(ep_num) = (EP_RX_CTRL(ep_num) & ~(USBHS_EP_R_RES_MASK)) | response; + } +} + +static void xfer_data_packet(uint8_t ep_num, tusb_dir_t ep_dir, xfer_ctl_t* xfer) { + if (ep_dir == TUSB_DIR_IN) { + uint16_t remaining = xfer->total_len - xfer->queued_len; + uint16_t next_tx_size = TU_MIN(remaining, xfer->max_size); + + if (ep_num == 0) { + memcpy(ep0_buffer, &xfer->buffer[xfer->queued_len], next_tx_size); + } else { + EP_TX_DMA_ADDR(ep_num) = (uint32_t) &xfer->buffer[xfer->queued_len]; + } + + EP_TX_LEN(ep_num) = next_tx_size; + xfer->queued_len += next_tx_size; + if (xfer->queued_len == xfer->total_len) { + xfer->is_last_packet = true; + } + if (xfer->is_iso == true) { + /* Enable EP to generate ISA_ACT interrupt */ + USBHSD->ENDP_CONFIG |= (USBHS_EP0_T_EN << ep_num); + } + } else { /* TUSB_DIR_OUT */ + uint16_t left_to_receive = xfer->total_len - xfer->queued_len; + uint16_t max_possible_rx_size = TU_MIN(xfer->max_size, left_to_receive); + + if (max_possible_rx_size == left_to_receive) { + xfer->is_last_packet = true; + } + + if (ep_num > 0) { + EP_RX_DMA_ADDR(ep_num) = (uint32_t) &xfer->buffer[xfer->queued_len]; + EP_RX_MAX_LEN(ep_num) = max_possible_rx_size; + } + } + ep_set_response_and_toggle(ep_num, ep_dir, USBHS_EP_R_RES_ACK); +} + +void dcd_init(uint8_t rhport) { + (void) rhport; + + memset(&xfer_status, 0, sizeof(xfer_status)); + + USBHSD->HOST_CTRL = 0x00; + USBHSD->HOST_CTRL = USBHS_PHY_SUSPENDM; + + USBHSD->CONTROL = 0; + +#if TUD_OPT_HIGH_SPEED + USBHSD->CONTROL = USBHS_DMA_EN | USBHS_INT_BUSY_EN | USBHS_HIGH_SPEED; +#else + #error OPT_MODE_FULL_SPEED not currently supported on CH32 + USBHSD->CONTROL = USBHS_DMA_EN | USBHS_INT_BUSY_EN | USBHS_FULL_SPEED; +#endif + + USBHSD->INT_EN = 0; + USBHSD->INT_EN = USBHS_SETUP_ACT_EN | USBHS_TRANSFER_EN | USBHS_BUS_RST_EN | USBHS_SUSPEND_EN | USBHS_ISO_ACT_EN; + + USBHSD->ENDP_CONFIG = USBHS_EP0_T_EN | USBHS_EP0_R_EN; + USBHSD->ENDP_TYPE = 0x00; + USBHSD->BUF_MODE = 0x00; + + for (int ep = 0; ep < EP_MAX; ep++) { + EP_TX_LEN(ep) = 0; + EP_TX_CTRL(ep) = USBHS_EP_T_AUTOTOG | USBHS_EP_T_RES_NAK; + EP_RX_CTRL(ep) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_NAK; + + EP_RX_MAX_LEN(ep) = 0; + } + + USBHSD->UEP0_DMA = (uint32_t) ep0_buffer; + USBHSD->UEP0_MAX_LEN = CFG_TUD_ENDPOINT0_SIZE; + xfer_status[0][TUSB_DIR_OUT].max_size = CFG_TUD_ENDPOINT0_SIZE; + xfer_status[0][TUSB_DIR_IN].max_size = CFG_TUD_ENDPOINT0_SIZE; + + USBHSD->DEV_AD = 0; + USBHSD->CONTROL |= USBHS_DEV_PU_EN; +} + +void dcd_int_enable(uint8_t rhport) { + (void) rhport; + NVIC_EnableIRQ(USBHS_IRQn); +} + +void dcd_int_disable(uint8_t rhport) { + (void) rhport; + NVIC_DisableIRQ(USBHS_IRQn); +} + +void dcd_edpt_close_all(uint8_t rhport) { + (void) rhport; + + for (size_t ep = 1; ep < EP_MAX; ep++) { + EP_TX_LEN(ep) = 0; + EP_TX_CTRL(ep) = USBHS_EP_T_AUTOTOG | USBHS_EP_T_RES_NAK; + EP_RX_CTRL(ep) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_NAK; + + EP_RX_MAX_LEN(ep) = 0; + } + + USBHSD->ENDP_CONFIG = USBHS_EP0_T_EN | USBHS_EP0_R_EN; +} + +void dcd_set_address(uint8_t rhport, uint8_t dev_addr) { + (void) dev_addr; + + // Response with zlp status + dcd_edpt_xfer(rhport, 0x80, NULL, 0); +} + +void dcd_remote_wakeup(uint8_t rhport) { + (void) rhport; +} + +void dcd_sof_enable(uint8_t rhport, bool en) { + (void) rhport; + if (en) { + USBHSD->INT_EN |= USBHS_SOF_ACT_EN; + } else { + USBHSD->INT_EN &= ~(USBHS_SOF_ACT_EN); + } +} + +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 && + request->bRequest == TUSB_REQ_SET_ADDRESS) { + USBHSD->DEV_AD = (uint8_t) request->wValue; + } + + EP_TX_CTRL(0) = USBHS_EP_T_RES_NAK | USBHS_EP_T_TOG_0; + EP_RX_CTRL(0) = USBHS_EP_R_RES_NAK | USBHS_EP_R_TOG_0; +} + +bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) { + (void) rhport; + + uint8_t const ep_num = tu_edpt_number(desc_edpt->bEndpointAddress); + tusb_dir_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress); + + TU_ASSERT(ep_num < EP_MAX); + + if (ep_num == 0) { + return true; + } + + xfer_ctl_t* xfer = XFER_CTL_BASE(ep_num, dir); + xfer->max_size = tu_edpt_packet_size(desc_edpt); + + xfer->is_iso = (desc_edpt->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS); + if (dir == TUSB_DIR_OUT) { + USBHSD->ENDP_CONFIG |= (USBHS_EP0_R_EN << ep_num); + EP_RX_CTRL(ep_num) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_NAK; + if (xfer->is_iso == true) { + USBHSD->ENDP_TYPE |= (USBHS_EP0_R_TYP << ep_num); + } + EP_RX_MAX_LEN(ep_num) = xfer->max_size; + } else { + if (xfer->is_iso == true) { + USBHSD->ENDP_TYPE |= (USBHS_EP0_T_TYP << ep_num); + } else { + /* Enable all types except Isochronous to avoid ISO_ACT interrupt generation */ + USBHSD->ENDP_CONFIG |= (USBHS_EP0_T_EN << ep_num); + } + EP_TX_LEN(ep_num) = 0; + EP_TX_CTRL(ep_num) = USBHS_EP_T_AUTOTOG | USBHS_EP_T_RES_NAK | USBHS_EP_T_TOG_0; + } + + return true; +} + +void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) { + (void) rhport; + + uint8_t const ep_num = tu_edpt_number(ep_addr); + tusb_dir_t const dir = tu_edpt_dir(ep_addr); + + if (dir == TUSB_DIR_OUT) { + EP_RX_CTRL(ep_num) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_NAK; + EP_RX_MAX_LEN(ep_num) = 0; + USBHSD->ENDP_TYPE &= ~(USBHS_EP0_R_TYP << ep_num); + USBHSD->ENDP_CONFIG &= ~(USBHS_EP0_R_EN << ep_num); + } else { // TUSB_DIR_IN + EP_TX_CTRL(ep_num) = USBHS_EP_T_AUTOTOG | USBHS_EP_T_RES_NAK | USBHS_EP_T_TOG_0; + EP_TX_LEN(ep_num) = 0; + USBHSD->ENDP_TYPE &= ~(USBHS_EP0_T_TYP << ep_num); + USBHSD->ENDP_CONFIG &= ~(USBHS_EP0_T_EN << ep_num); + } +} + +void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { + (void) rhport; + + uint8_t const ep_num = tu_edpt_number(ep_addr); + tusb_dir_t const dir = tu_edpt_dir(ep_addr); + + if (dir == TUSB_DIR_OUT) { + EP_RX_CTRL(ep_num) = USBHS_EP_R_RES_STALL; + } else { + EP_TX_LEN(0) = 0; + EP_TX_CTRL(ep_num) = USBHS_EP_T_RES_STALL; + } +} + +void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { + (void) rhport; + + uint8_t const ep_num = tu_edpt_number(ep_addr); + tusb_dir_t const dir = tu_edpt_dir(ep_addr); + + if (dir == TUSB_DIR_OUT) { + EP_RX_CTRL(ep_num) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_NAK; + } else { + EP_TX_CTRL(ep_num) = USBHS_EP_T_AUTOTOG | USBHS_EP_R_RES_NAK; + } +} + +bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) { + (void) rhport; + uint8_t const ep_num = tu_edpt_number(ep_addr); + tusb_dir_t const dir = tu_edpt_dir(ep_addr); + + xfer_ctl_t* xfer = XFER_CTL_BASE(ep_num, dir); + xfer->buffer = buffer; + xfer->total_len = total_bytes; + xfer->queued_len = 0; + xfer->is_last_packet = false; + + xfer_data_packet(ep_num, dir, xfer); + + return true; +} + +void dcd_int_handler(uint8_t rhport) { + (void) rhport; + + uint8_t int_flag = USBHSD->INT_FG; + uint8_t int_status = USBHSD->INT_ST; + + if (int_flag & (USBHS_ISO_ACT_FLAG | USBHS_TRANSFER_FLAG)) { + uint8_t const token = int_status & MASK_UIS_TOKEN; + + if (token == USBHS_TOKEN_PID_SOF) { + uint32_t frame_count = USBHSD->FRAME_NO & USBHS_FRAME_NO_NUM_MASK; + dcd_event_sof(rhport, frame_count, true); + }else { + uint8_t const ep_num = int_status & MASK_UIS_ENDP; + tusb_dir_t const ep_dir = (token == USBHS_TOKEN_PID_IN) ? TUSB_DIR_IN : TUSB_DIR_OUT; + uint8_t const ep_addr = tu_edpt_addr(ep_num, ep_dir); + xfer_ctl_t* xfer = XFER_CTL_BASE(ep_num, ep_dir); + + if (token == USBHS_TOKEN_PID_OUT) { + uint16_t rx_len = USBHSD->RX_LEN; + + if (ep_num == 0) { + memcpy(&xfer->buffer[xfer->queued_len], ep0_buffer, rx_len); + } + + xfer->queued_len += rx_len; + if (rx_len < xfer->max_size) { + xfer->is_last_packet = true; + } + } else if (token == USBHS_TOKEN_PID_IN) { + if (xfer->is_iso && xfer->is_last_packet) { + /* Disable EP to avoid ISO_ACT interrupt generation */ + USBHSD->ENDP_CONFIG &= ~(USBHS_EP0_T_EN << ep_num); + } else { + // Do nothing, no need to update xfer->is_last_packet, it is already updated in xfer_data_packet + } + } + + if (xfer->is_last_packet == true) { + ep_set_response_and_toggle(ep_num, ep_dir, EP_RESPONSE_NAK); + dcd_event_xfer_complete(0, ep_addr, xfer->queued_len, XFER_RESULT_SUCCESS, true); + } else { + /* prepare next part of packet to xref */ + xfer_data_packet(ep_num, ep_dir, xfer); + } + } + + USBHSD->INT_FG = (int_flag & (USBHS_ISO_ACT_FLAG | USBHS_TRANSFER_FLAG)); /* Clear flag */ + } else if (int_flag & USBHS_SETUP_FLAG) { + ep_set_response_and_toggle(0, TUSB_DIR_IN, EP_RESPONSE_NAK); + ep_set_response_and_toggle(0, TUSB_DIR_OUT, EP_RESPONSE_NAK); + dcd_event_setup_received(0, ep0_buffer, true); + + USBHSD->INT_FG = USBHS_SETUP_FLAG; /* Clear flag */ + } else if (int_flag & USBHS_BUS_RST_FLAG) { + // TODO CH32 does not detect actual speed at this time (should be known at end of reset) + // This interrupt probably triggered at start of bus reset +// tusb_speed_t actual_speed; +// switch(USBHSD->SPEED_TYPE & USBHS_SPEED_TYPE_MASK){ +// case USBHS_SPEED_TYPE_HIGH: +// actual_speed = TUSB_SPEED_HIGH; +// break; +// case USBHS_SPEED_TYPE_FULL: +// actual_speed = TUSB_SPEED_FULL; +// break; +// case USBHS_SPEED_TYPE_LOW: +// actual_speed = TUSB_SPEED_LOW; +// break; +// default: +// TU_ASSERT(0,); +// break; +// } +// dcd_event_bus_reset(0, actual_speed, true); + + dcd_event_bus_reset(0, TUSB_SPEED_HIGH, true); + + USBHSD->DEV_AD = 0; + EP_RX_CTRL(0) = USBHS_EP_R_RES_ACK | USBHS_EP_R_TOG_0; + EP_TX_CTRL(0) = USBHS_EP_T_RES_NAK | USBHS_EP_T_TOG_0; + + USBHSD->INT_FG = USBHS_BUS_RST_FLAG; /* Clear flag */ + } else if (int_flag & USBHS_SUSPEND_FLAG) { + dcd_event_t event = {.rhport = rhport, .event_id = DCD_EVENT_SUSPEND}; + dcd_event_handler(&event, true); + + USBHSD->INT_FG = USBHS_SUSPEND_FLAG; /* Clear flag */ + } +} + +#endif From 2e8f1b5c3362f0b2c1e6986df9b8f3ce5844bc94 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 7 Jun 2024 13:24:50 +0700 Subject: [PATCH 02/14] initial support for ch32 with usbd --- .github/workflows/githubci.yml | 2 - .../MassStorage/msc_ramdisk/msc_ramdisk.ino | 14 +- src/Adafruit_TinyUSB.h | 7 + src/arduino/Adafruit_USBD_Device.cpp | 5 + src/arduino/Adafruit_USBD_Device.h | 1 + .../ports/ch32/Adafruit_TinyUSB_ch32.cpp | 130 +++++++++++++ src/arduino/ports/ch32/tusb_config_ch32.h | 179 ++++++++++++++++++ src/tusb_config.h | 8 +- 8 files changed, 335 insertions(+), 11 deletions(-) create mode 100644 src/arduino/ports/ch32/Adafruit_TinyUSB_ch32.cpp create mode 100644 src/arduino/ports/ch32/tusb_config_ch32.h diff --git a/.github/workflows/githubci.yml b/.github/workflows/githubci.yml index ab9226ff..2ec7a516 100644 --- a/.github/workflows/githubci.yml +++ b/.github/workflows/githubci.yml @@ -62,7 +62,6 @@ jobs: uses: actions/checkout@v4 with: repository: adafruit/ci-arduino - ref: importable-build_platform path: ci - name: pre-install @@ -95,7 +94,6 @@ jobs: uses: actions/checkout@v4 with: repository: adafruit/ci-arduino - ref: importable-build_platform path: ci - name: pre-install diff --git a/examples/MassStorage/msc_ramdisk/msc_ramdisk.ino b/examples/MassStorage/msc_ramdisk/msc_ramdisk.ino index 1a7348f5..0a434f4b 100644 --- a/examples/MassStorage/msc_ramdisk/msc_ramdisk.ino +++ b/examples/MassStorage/msc_ramdisk/msc_ramdisk.ino @@ -37,11 +37,10 @@ Adafruit_USBD_MSC usb_msc; // the setup function runs once when you press reset or power the board void setup() { -#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040) - // Manual begin() is required on core without built-in support for TinyUSB such as - // - mbed rp2040 - TinyUSB_Device_Init(0); -#endif + // Manual begin() is required on core without built-in support e.g. mbed rp2040 + if (!TinyUSBDevice.isInitialized()) { + TinyUSBDevice.begin(0); + } #ifdef BTN_EJECT pinMode(BTN_EJECT, activeState ? INPUT_PULLDOWN : INPUT_PULLUP); @@ -69,7 +68,10 @@ void setup() { } void loop() { - // nothing to do + #ifdef TINYUSB_NEED_POLLING_TASK + // Manual call tud_task since it isn't called by Core's background + TinyUSBDevice.task(); + #endif } // Callback invoked when received READ10 command. diff --git a/src/Adafruit_TinyUSB.h b/src/Adafruit_TinyUSB.h index bb836b3b..1587204d 100644 --- a/src/Adafruit_TinyUSB.h +++ b/src/Adafruit_TinyUSB.h @@ -25,6 +25,13 @@ #ifndef ADAFRUIT_TINYUSB_H_ #define ADAFRUIT_TINYUSB_H_ +// Core that has built-in support: Adafruit SAMD, nRF, rp2040, esp32 +#if !(defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_NRF52_ADAFRUIT) || \ + defined(ARDUINO_ARCH_ESP32) || \ + (defined(ARDUINO_ARCH_RP2040) && !defined(ARDUINO_ARCH_MBED))) +#define TINYUSB_NEED_POLLING_TASK +#endif + // Error message for Core that must select TinyUSB via menu #if !defined(USE_TINYUSB) && \ (defined(ARDUINO_ARCH_SAMD) || \ diff --git a/src/arduino/Adafruit_USBD_Device.cpp b/src/arduino/Adafruit_USBD_Device.cpp index f65d59b3..68dbbe3d 100644 --- a/src/arduino/Adafruit_USBD_Device.cpp +++ b/src/arduino/Adafruit_USBD_Device.cpp @@ -269,6 +269,11 @@ bool Adafruit_USBD_Device::begin(uint8_t rhport) { return true; } +bool Adafruit_USBD_Device::isInitialized(uint8_t rhport) { + (void)rhport; + return tud_inited(); +} + static int strcpy_utf16(const char *s, uint16_t *buf, int bufsize); uint8_t Adafruit_USBD_Device::getSerialDescriptor(uint16_t *serial_utf16) { diff --git a/src/arduino/Adafruit_USBD_Device.h b/src/arduino/Adafruit_USBD_Device.h index 16477495..7bbc25dc 100644 --- a/src/arduino/Adafruit_USBD_Device.h +++ b/src/arduino/Adafruit_USBD_Device.h @@ -115,6 +115,7 @@ class Adafruit_USBD_Device { //------------- Control -------------// bool begin(uint8_t rhport = 0); + bool isInitialized(uint8_t rhport = 0); void task(void); // physical disable/enable pull-up diff --git a/src/arduino/ports/ch32/Adafruit_TinyUSB_ch32.cpp b/src/arduino/ports/ch32/Adafruit_TinyUSB_ch32.cpp new file mode 100644 index 00000000..04c791f2 --- /dev/null +++ b/src/arduino/ports/ch32/Adafruit_TinyUSB_ch32.cpp @@ -0,0 +1,130 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019, hathach for Adafruit + * + * 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. + */ + +#include "tusb_option.h" + +#if defined(CH32V20x) && CFG_TUD_ENABLED + +#include "Arduino.h" +#include "arduino/Adafruit_USBD_Device.h" + +//--------------------------------------------------------------------+ +// MACRO TYPEDEF CONSTANT ENUM DECLARATION +//--------------------------------------------------------------------+ + +//--------------------------------------------------------------------+ +// Forward USB interrupt events to TinyUSB IRQ Handler +//--------------------------------------------------------------------+ +extern "C" { + +// USBFS +__attribute__((interrupt("WCH-Interrupt-fast"))) void USBHD_IRQHandler(void) { +#if CFG_TUD_WCH_USBIP_USBFS + tud_int_handler(0); +#endif +} + +__attribute__((interrupt("WCH-Interrupt-fast"))) void +USBHDWakeUp_IRQHandler(void) { +#if CFG_TUD_WCH_USBIP_USBFS + tud_int_handler(0); +#endif +} + +// USBD (fsdev) +__attribute__((interrupt("WCH-Interrupt-fast"))) void +USB_LP_CAN1_RX0_IRQHandler(void) { +#if CFG_TUD_WCH_USBIP_FSDEV + tud_int_handler(0); +#endif +} + +__attribute__((interrupt("WCH-Interrupt-fast"))) void +USB_HP_CAN1_TX_IRQHandler(void) { +#if CFG_TUD_WCH_USBIP_FSDEV + tud_int_handler(0); +#endif +} + +__attribute__((interrupt("WCH-Interrupt-fast"))) void +USBWakeUp_IRQHandler(void) { +#if CFG_TUD_WCH_USBIP_FSDEV + tud_int_handler(0); +#endif +} + +void yield(void) { + tud_task(); + // flush cdc +} +} + +//--------------------------------------------------------------------+ +// Porting API +//--------------------------------------------------------------------+ + +void TinyUSB_Port_InitDevice(uint8_t rhport) { + uint8_t usb_div; + switch (SystemCoreClock) { + case 48000000: + usb_div = RCC_USBCLKSource_PLLCLK_Div1; + break; + case 96000000: + usb_div = RCC_USBCLKSource_PLLCLK_Div2; + break; + case 144000000: + usb_div = RCC_USBCLKSource_PLLCLK_Div3; + break; + default: + return; // unsupported + } + RCC_USBCLKConfig(usb_div); + +#if CFG_TUD_WCH_USBIP_FSDEV + RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE); +#endif + +#if CFG_TUD_WCH_USBIP_USBFS + RCC_AHBPeriphClockCmd(RCC_AHBPeriph_OTG_FS, ENABLE); +#endif + + tud_init(rhport); +} + +void TinyUSB_Port_EnterDFU(void) { + // Reset to Bootloader + // enterSerialDfu(); +} + +uint8_t TinyUSB_Port_GetSerialNumber(uint8_t serial_id[16]) { + volatile uint32_t *ch32_uuid = ((volatile uint32_t *)0x1FFFF7E8UL); + uint32_t *serial_32 = (uint32_t *)serial_id; + serial_32[0] = ch32_uuid[0]; // TODO maybe __builtin_bswap32() + serial_32[1] = ch32_uuid[1]; + serial_32[2] = ch32_uuid[2]; + + return 12; +} + +#endif diff --git a/src/arduino/ports/ch32/tusb_config_ch32.h b/src/arduino/ports/ch32/tusb_config_ch32.h new file mode 100644 index 00000000..0235e1cf --- /dev/null +++ b/src/arduino/ports/ch32/tusb_config_ch32.h @@ -0,0 +1,179 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018, hathach for Adafruit + * + * 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. + */ + +#ifndef TUSB_CONFIG_CH32_H_ +#define TUSB_CONFIG_CH32_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +//-------------------------------------------------------------------- +// COMMON CONFIGURATION +//-------------------------------------------------------------------- +#if defined(CH32V20x) +#define CFG_TUSB_MCU OPT_MCU_CH32V20X +#define CFG_TUD_WCH_USBIP_FSDEV 1 // use USBD +#elif defined(CH32V30x) +#define CFG_TUSB_MCU OPT_MCU_CH32V307 +#endif + +#define CFG_TUSB_OS OPT_OS_NONE + +#ifndef CFG_TUSB_DEBUG +#define CFG_TUSB_DEBUG 0 +#endif + +// For selectively disable device log (when > CFG_TUSB_DEBUG) +// #define CFG_TUD_LOG_LEVEL 3 +// #define CFG_TUH_LOG_LEVEL 3 + +#define CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_ALIGN __attribute__((aligned(4))) + +#define CFG_TUD_ENABLED 1 + +// #ifdef USE_TINYUSB +//// Enable device stack +// #define CFG_TUD_ENABLED 1 +// +//// Enable host stack with MAX3421E (host shield) +// #define CFG_TUH_ENABLED 1 +// #define CFG_TUH_MAX3421 1 +// +// #else +// #define CFG_TUD_ENABLED 0 +// #define CFG_TUH_ENABLED 0 +// #endif + +//-------------------------------------------------------------------- +// DEVICE CONFIGURATION +//-------------------------------------------------------------------- + +#define CFG_TUD_ENDPOINT0_SIZE 64 + +//------------- CLASS -------------// +#ifndef CFG_TUD_CDC +#define CFG_TUD_CDC 1 +#endif + +#ifndef CFG_TUD_MSC +#define CFG_TUD_MSC 1 +#endif + +#ifndef CFG_TUD_HID +#define CFG_TUD_HID 2 +#endif + +#ifndef CFG_TUD_MIDI +#define CFG_TUD_MIDI 1 +#endif + +#ifndef CFG_TUD_VENDOR +#define CFG_TUD_VENDOR 1 +#endif + +#ifndef CFG_TUD_VIDEO +#define CFG_TUD_VIDEO 1 // number of video control interfaces +#endif + +#ifndef CFG_TUD_VIDEO_STREAMING +#define CFG_TUD_VIDEO_STREAMING 1 // number of video streaming interfaces +#endif + +// video streaming endpoint buffer size +#define CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE 256 + +// CDC FIFO size of TX and RX +#define CFG_TUD_CDC_RX_BUFSIZE 256 +#define CFG_TUD_CDC_TX_BUFSIZE 256 + +// MSC Buffer size of Device Mass storage +#define CFG_TUD_MSC_EP_BUFSIZE 512 + +// HID buffer size Should be sufficient to hold ID (if any) + Data +#define CFG_TUD_HID_EP_BUFSIZE 64 + +// MIDI FIFO size of TX and RX +#define CFG_TUD_MIDI_RX_BUFSIZE 128 +#define CFG_TUD_MIDI_TX_BUFSIZE 128 + +// Vendor FIFO size of TX and RX +#ifndef CFG_TUD_VENDOR_RX_BUFSIZE +#define CFG_TUD_VENDOR_RX_BUFSIZE 64 +#endif + +#ifndef CFG_TUD_VENDOR_TX_BUFSIZE +#define CFG_TUD_VENDOR_TX_BUFSIZE 64 +#endif + +//-------------------------------------------------------------------- +// Host Configuration +//-------------------------------------------------------------------- + +// Size of buffer to hold descriptors and other data used for enumeration +#define CFG_TUH_ENUMERATION_BUFSIZE 256 + +// Number of hub devices +#define CFG_TUH_HUB 1 + +// max device support (excluding hub device): 1 hub typically has 4 ports +#define CFG_TUH_DEVICE_MAX (3 * CFG_TUH_HUB + 1) + +// Enable tuh_edpt_xfer() API +// #define CFG_TUH_API_EDPT_XFER 1 + +// Number of mass storage +#define CFG_TUH_MSC 1 + +// Number of HIDs +// typical keyboard + mouse device can have 3,4 HID interfaces +#define CFG_TUH_HID (3 * CFG_TUH_DEVICE_MAX) + +// Number of CDC interfaces +// FTDI and CP210x are not part of CDC class, only to re-use CDC driver API +#define CFG_TUH_CDC 1 +#define CFG_TUH_CDC_FTDI 1 +#define CFG_TUH_CDC_CP210X 1 +#define CFG_TUH_CDC_CH34X 1 + +// RX & TX fifo size +#define CFG_TUH_CDC_RX_BUFSIZE 64 +#define CFG_TUH_CDC_TX_BUFSIZE 64 + +// Set Line Control state on enumeration/mounted: +// DTR ( bit 0), RTS (bit 1) +#define CFG_TUH_CDC_LINE_CONTROL_ON_ENUM 0x03 + +// Set Line Coding on enumeration/mounted, value for cdc_line_coding_t +// bit rate = 115200, 1 stop bit, no parity, 8 bit data width +// This need Pico-PIO-USB at least 0.5.1 +#define CFG_TUH_CDC_LINE_CODING_ON_ENUM \ + { 115200, CDC_LINE_CONDING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 } + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/tusb_config.h b/src/tusb_config.h index b5635068..51099a98 100644 --- a/src/tusb_config.h +++ b/src/tusb_config.h @@ -22,8 +22,8 @@ * THE SOFTWARE. */ -#ifndef _TUSB_CONFIG_ARDUINO_H_ -#define _TUSB_CONFIG_ARDUINO_H_ +#ifndef TUSB_CONFIG_ARDUINO_H_ +#define TUSB_CONFIG_ARDUINO_H_ #ifdef __cplusplus extern "C" { @@ -51,6 +51,8 @@ // Note: For platformio prioritize this file over the one in BSP in all cases +#elif defined(CH32V20x) || defined(CH32V30x) // using build.series + #include "arduino/ports/ch32/tusb_config_ch32.h" #else #error TinyUSB Arduino Library does not support your core yet #endif @@ -64,4 +66,4 @@ } #endif -#endif /* _TUSB_CONFIG_ARDUINO_H_ */ +#endif From 2179aeb6d2f08729c34605b879dc91a0c702b86d Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 7 Jun 2024 21:04:53 +0700 Subject: [PATCH 03/14] most example works with ch32: hid,msc,midi though mouse_ramdisk does not. --- examples/CDC/no_serial/no_serial.ino | 21 +- .../.feather52833.test.skip | 0 .../.pico_rp2040_tinyusb_host.test.skip | 0 .../mouse_external_flash.ino | 200 ------------------ .../Composite/mouse_ramdisk/mouse_ramdisk.ino | 86 ++++---- .../hid_boot_keyboard/hid_boot_keyboard.ino | 120 ++++------- .../HID/hid_boot_mouse/hid_boot_mouse.ino | 71 ++++--- examples/HID/hid_composite/hid_composite.ino | 97 +++++---- .../hid_composite_joy_featherwing.ino | 82 +++---- .../hid_dual_interfaces.ino | 85 +++++--- examples/HID/hid_gamepad/hid_gamepad.ino | 102 ++++----- .../hid_generic_inout/hid_generic_inout.ino | 31 ++- .../midi_multi_ports/midi_multi_ports.ino | 25 ++- examples/MIDI/midi_test/midi_test.ino | 58 ++--- .../msc_ramdisk_dual/msc_ramdisk_dual.ino | 42 ++-- .../Video/video_capture/video_capture.ino | 10 + examples/WebUSB/webusb_rgb/webusb_rgb.ino | 35 +-- .../WebUSB/webusb_serial/webusb_serial.ino | 49 ++--- src/arduino/Adafruit_USBD_Interface.h | 6 + 19 files changed, 487 insertions(+), 633 deletions(-) delete mode 100644 examples/Composite/mouse_external_flash/.feather52833.test.skip delete mode 100644 examples/Composite/mouse_external_flash/.pico_rp2040_tinyusb_host.test.skip delete mode 100644 examples/Composite/mouse_external_flash/mouse_external_flash.ino diff --git a/examples/CDC/no_serial/no_serial.ino b/examples/CDC/no_serial/no_serial.ino index c7538d5b..6883a444 100644 --- a/examples/CDC/no_serial/no_serial.ino +++ b/examples/CDC/no_serial/no_serial.ino @@ -23,6 +23,11 @@ int led = LED_BUILTIN; void setup() { + // Manual begin() is required on core without built-in support e.g. mbed rp2040 + if (!TinyUSBDevice.isInitialized()) { + TinyUSBDevice.begin(0); + } + // clear configuration will remove all USB interfaces including CDC (Serial) TinyUSBDevice.clearConfiguration(); @@ -31,8 +36,16 @@ void setup() void loop() { - digitalWrite(led, HIGH); - delay(1000); - digitalWrite(led, LOW); - delay(1000); + #ifdef TINYUSB_NEED_POLLING_TASK + // Manual call tud_task since it isn't called by Core's background + TinyUSBDevice.task(); + #endif + + // toggle LED + static uint32_t ms = 0; + static uint8_t led_state = 0; + if (millis() - ms > 1000) { + ms = millis(); + digitalWrite(LED_BUILTIN, 1-led_state); + } } diff --git a/examples/Composite/mouse_external_flash/.feather52833.test.skip b/examples/Composite/mouse_external_flash/.feather52833.test.skip deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/Composite/mouse_external_flash/.pico_rp2040_tinyusb_host.test.skip b/examples/Composite/mouse_external_flash/.pico_rp2040_tinyusb_host.test.skip deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/Composite/mouse_external_flash/mouse_external_flash.ino b/examples/Composite/mouse_external_flash/mouse_external_flash.ino deleted file mode 100644 index 8cdbbb1d..00000000 --- a/examples/Composite/mouse_external_flash/mouse_external_flash.ino +++ /dev/null @@ -1,200 +0,0 @@ -/********************************************************************* - Adafruit invests time and resources providing this open source code, - please support Adafruit and open-source hardware by purchasing - products from Adafruit! - - MIT license, check LICENSE for more information - Copyright (c) 2019 Ha Thach for Adafruit Industries - All text above, and the splash screen below must be included in - any redistribution -*********************************************************************/ - -/* This sketch demonstrates USB Mass Storage and HID mouse (and CDC) - * - Enumerated as disk using on-board external flash - * - Press button pin will move mouse toward bottom right of monitor - */ - -#include "SPI.h" -#include "SdFat.h" -#include "Adafruit_SPIFlash.h" -#include "Adafruit_TinyUSB.h" - -//--------------------------------------------------------------------+ -// MSC External Flash Config -//--------------------------------------------------------------------+ - -// Un-comment to run example with custom SPI SPI and SS e.g with FRAM breakout -// #define CUSTOM_CS A5 -// #define CUSTOM_SPI SPI - -#if defined(CUSTOM_CS) && defined(CUSTOM_SPI) - Adafruit_FlashTransport_SPI flashTransport(CUSTOM_CS, CUSTOM_SPI); - -#elif defined(ARDUINO_ARCH_ESP32) - // ESP32 use same flash device that store code. - // Therefore there is no need to specify the SPI and SS - Adafruit_FlashTransport_ESP32 flashTransport; - -#elif defined(ARDUINO_ARCH_RP2040) - // RP2040 use same flash device that store code. - // Therefore there is no need to specify the SPI and SS - // Use default (no-args) constructor to be compatible with CircuitPython partition scheme - Adafruit_FlashTransport_RP2040 flashTransport; - - // For generic usage: - // Adafruit_FlashTransport_RP2040 flashTransport(start_address, size) - // If start_address and size are both 0, value that match filesystem setting in - // 'Tools->Flash Size' menu selection will be used - -#else - // On-board external flash (QSPI or SPI) macros should already - // defined in your board variant if supported - // - EXTERNAL_FLASH_USE_QSPI - // - EXTERNAL_FLASH_USE_CS/EXTERNAL_FLASH_USE_SPI - #if defined(EXTERNAL_FLASH_USE_QSPI) - Adafruit_FlashTransport_QSPI flashTransport; - - #elif defined(EXTERNAL_FLASH_USE_SPI) - Adafruit_FlashTransport_SPI flashTransport(EXTERNAL_FLASH_USE_CS, EXTERNAL_FLASH_USE_SPI); - - #else - #error No QSPI/SPI flash are defined on your board variant.h ! - #endif -#endif - -Adafruit_SPIFlash flash(&flashTransport); - -Adafruit_USBD_MSC usb_msc; - -//--------------------------------------------------------------------+ -// HID Config -//--------------------------------------------------------------------+ - -// HID report descriptor using TinyUSB's template -// Single Report (no ID) descriptor -uint8_t const desc_hid_report[] = { - TUD_HID_REPORT_DESC_MOUSE() -}; - -// USB HID object -Adafruit_USBD_HID usb_hid; - -#if defined(ARDUINO_SAMD_CIRCUITPLAYGROUND_EXPRESS) || defined(ARDUINO_NRF52840_CIRCUITPLAY) - const int pin = 4; // Left Button - bool activeState = true; - -#elif defined(ARDUINO_FUNHOUSE_ESP32S2) - const int pin = BUTTON_DOWN; - bool activeState = true; - -#elif defined PIN_BUTTON1 - const int pin = PIN_BUTTON1; - bool activeState = false; - -#elif defined(ARDUINO_ARCH_ESP32) - const int pin = 0; - bool activeState = false; - -#else - const int pin = 12; - bool activeState = false; -#endif - - -// the setup function runs once when you press reset or power the board -void setup() -{ -#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040) - // Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040 - TinyUSB_Device_Init(0); -#endif - - flash.begin(); - - pinMode(LED_BUILTIN, OUTPUT); - - // Set disk vendor id, product id and revision with string up to 8, 16, 4 characters respectively - usb_msc.setID("Adafruit", "External Flash", "1.0"); - - // Set callback - usb_msc.setReadWriteCallback(msc_read_cb, msc_write_cb, msc_flush_cb); - - // Set disk size, block size should be 512 regardless of spi flash page size - usb_msc.setCapacity(flash.size()/512, 512); - - // MSC is ready for read/write - usb_msc.setUnitReady(true); - - usb_msc.begin(); - - // Set up button - pinMode(pin, activeState ? INPUT_PULLDOWN : INPUT_PULLUP); - - // Set up HID - usb_hid.setReportDescriptor(desc_hid_report, sizeof(desc_hid_report)); - usb_hid.setBootProtocol(HID_ITF_PROTOCOL_NONE); - usb_hid.setPollInterval(2); - usb_hid.begin(); - - Serial.begin(115200); - //while ( !Serial ) delay(10); // wait for native usb - - Serial.println("Adafruit TinyUSB Mouse + Mass Storage (external flash) example"); -} - -void loop() -{ - // poll gpio once each 10 ms - delay(10); - - // button is active low - uint32_t const btn = (digitalRead(pin) == activeState); - - // Remote wakeup - if ( TinyUSBDevice.suspended() && btn ) - { - // Wake up host if we are in suspend mode - // and REMOTE_WAKEUP feature is enabled by host - tud_remote_wakeup(); - } - - /*------------- Mouse -------------*/ - if ( usb_hid.ready() ) - { - if ( btn ) - { - int8_t const delta = 5; - usb_hid.mouseMove(0, delta, delta); // no ID: right + down - - // delay a bit before attempt to send keyboard report - delay(10); - } - } -} - -// Callback invoked when received READ10 command. -// Copy disk's data to buffer (up to bufsize) and -// return number of copied bytes (must be multiple of block size) -int32_t msc_read_cb (uint32_t lba, void* buffer, uint32_t bufsize) -{ - // Note: SPIFLash Bock API: readBlocks/writeBlocks/syncBlocks - // already include 4K sector caching internally. We don't need to cache it, yahhhh!! - return flash.readBlocks(lba, (uint8_t*) buffer, bufsize/512) ? bufsize : -1; -} - -// Callback invoked when received WRITE10 command. -// Process data in buffer to disk's storage and -// return number of written bytes (must be multiple of block size) -int32_t msc_write_cb (uint32_t lba, uint8_t* buffer, uint32_t bufsize) -{ - // Note: SPIFLash Bock API: readBlocks/writeBlocks/syncBlocks - // already include 4K sector caching internally. We don't need to cache it, yahhhh!! - return flash.writeBlocks(lba, buffer, bufsize/512) ? bufsize : -1; -} - -// Callback invoked when WRITE10 command is completed (status received and accepted by host). -// used to flush any pending cache. -void msc_flush_cb (void) -{ - flash.syncBlocks(); -} diff --git a/examples/Composite/mouse_ramdisk/mouse_ramdisk.ino b/examples/Composite/mouse_ramdisk/mouse_ramdisk.ino index 09da8690..08c6e113 100644 --- a/examples/Composite/mouse_ramdisk/mouse_ramdisk.ino +++ b/examples/Composite/mouse_ramdisk/mouse_ramdisk.ino @@ -23,6 +23,7 @@ // 8KB is the smallest size that windows allow to mount #define DISK_BLOCK_NUM 16 #define DISK_BLOCK_SIZE 512 + #include "ramdisk.h" Adafruit_USBD_MSC usb_msc; @@ -41,37 +42,41 @@ uint8_t const desc_hid_report[] = { Adafruit_USBD_HID usb_hid; #if defined(ARDUINO_SAMD_CIRCUITPLAYGROUND_EXPRESS) || defined(ARDUINO_NRF52840_CIRCUITPLAY) - const int pin = 4; // Left Button - bool activeState = true; +const int pin = 4; // Left Button +bool activeState = true; #elif defined(ARDUINO_FUNHOUSE_ESP32S2) - const int pin = BUTTON_DOWN; - bool activeState = true; +const int pin = BUTTON_DOWN; +bool activeState = true; #elif defined PIN_BUTTON1 - const int pin = PIN_BUTTON1; - bool activeState = false; +const int pin = PIN_BUTTON1; +bool activeState = false; #elif defined(ARDUINO_ARCH_ESP32) - const int pin = 0; - bool activeState = false; +const int pin = 0; +bool activeState = false; + +#elif defined(ARDUINO_ARCH_RP2040) +const int pin = D0; +bool activeState = false; #else - const int pin = 12; - bool activeState = false; +const int pin = A0; +bool activeState = false; #endif + // the setup function runs once when you press reset or power the board -void setup() -{ -#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040) - // Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040 - TinyUSB_Device_Init(0); -#endif +void setup() { + // Manual begin() is required on core without built-in support e.g. mbed rp2040 + if (!TinyUSBDevice.isInitialized()) { + TinyUSBDevice.begin(0); + } // Set disk vendor id, product id and revision with string up to 8, 16, 4 characters respectively usb_msc.setID("Adafruit", "Mass Storage", "1.0"); - + // Set disk size usb_msc.setCapacity(DISK_BLOCK_NUM, DISK_BLOCK_SIZE); @@ -80,7 +85,7 @@ void setup() // Set Lun ready (RAM disk is always ready) usb_msc.setUnitReady(true); - + usb_msc.begin(); // Set up button @@ -93,32 +98,23 @@ void setup() usb_hid.begin(); Serial.begin(115200); - while( !TinyUSBDevice.mounted() ) delay(1); // wait for native usb - Serial.println("Adafruit TinyUSB Mouse + Mass Storage (ramdisk) example"); } -void loop() -{ - // poll gpio once each 10 ms - delay(10); - +void process_hid() { // button is active low uint32_t const btn = (digitalRead(pin) == activeState); // Remote wakeup - if ( TinyUSBDevice.suspended() && btn ) - { + if (TinyUSBDevice.suspended() && btn) { // Wake up host if we are in suspend mode // and REMOTE_WAKEUP feature is enabled by host tud_remote_wakeup(); } /*------------- Mouse -------------*/ - if ( usb_hid.ready() ) - { - if ( btn ) - { + if (usb_hid.ready()) { + if (btn) { int8_t const delta = 5; usb_hid.mouseMove(0, delta, delta); // no ID: right + down @@ -128,11 +124,29 @@ void loop() } } +void loop() { + #ifdef TINYUSB_NEED_POLLING_TASK + // Manual call tud_task since it isn't called by Core's background + TinyUSBDevice.task(); + #endif + + // not enumerated()/mounted() yet: nothing to do + if (!TinyUSBDevice.mounted()) { + return; + } + + // poll gpio once each 10 ms + static uint32_t ms = 0; + if (millis() - ms > 10) { + ms = millis(); + process_hid(); + } +} + // Callback invoked when received READ10 command. // Copy disk's data to buffer (up to bufsize) and // return number of copied bytes (must be multiple of block size) -int32_t msc_read_cb (uint32_t lba, void* buffer, uint32_t bufsize) -{ +int32_t msc_read_cb(uint32_t lba, void* buffer, uint32_t bufsize) { uint8_t const* addr = msc_disk[lba]; memcpy(buffer, addr, bufsize); @@ -142,8 +156,7 @@ int32_t msc_read_cb (uint32_t lba, void* buffer, uint32_t bufsize) // Callback invoked when received WRITE10 command. // Process data in buffer to disk's storage and // return number of written bytes (must be multiple of block size) -int32_t msc_write_cb (uint32_t lba, uint8_t* buffer, uint32_t bufsize) -{ +int32_t msc_write_cb(uint32_t lba, uint8_t* buffer, uint32_t bufsize) { uint8_t* addr = msc_disk[lba]; memcpy(addr, buffer, bufsize); @@ -152,7 +165,6 @@ int32_t msc_write_cb (uint32_t lba, uint8_t* buffer, uint32_t bufsize) // Callback invoked when WRITE10 command is completed (status received and accepted by host). // used to flush any pending cache. -void msc_flush_cb (void) -{ +void msc_flush_cb(void) { // nothing to do } diff --git a/examples/HID/hid_boot_keyboard/hid_boot_keyboard.ino b/examples/HID/hid_boot_keyboard/hid_boot_keyboard.ino index c5bca727..38395220 100644 --- a/examples/HID/hid_boot_keyboard/hid_boot_keyboard.ino +++ b/examples/HID/hid_boot_keyboard/hid_boot_keyboard.ino @@ -10,10 +10,9 @@ *********************************************************************/ #include "Adafruit_TinyUSB.h" -#include /* This sketch demonstrates USB HID keyboard. - * - PIN A0-A5 is used to send digit '0' to '5' respectively + * - PIN A0-A3 is used to send digit '0' to '3' respectively * (On the RP2040, pins D0-D5 used) * - LED and/or Neopixels will be used as Capslock indicator */ @@ -21,7 +20,7 @@ // HID report descriptor using TinyUSB's template // Single Report (no ID) descriptor uint8_t const desc_hid_report[] = { - TUD_HID_REPORT_DESC_KEYBOARD() + TUD_HID_REPORT_DESC_KEYBOARD() }; // USB HID object. For ESP32 these values cannot be changed after this declaration @@ -32,45 +31,29 @@ Adafruit_USBD_HID usb_hid; // Array of pins and its keycode. // Notes: these pins can be replaced by PIN_BUTTONn if defined in setup() #ifdef ARDUINO_ARCH_RP2040 - uint8_t pins[] = { D0, D1, D2, D3 }; +uint8_t pins[] = { D0, D1, D2, D3 }; #else - uint8_t pins[] = { A0, A1, A2, A3 }; +uint8_t pins[] = {A0, A1, A2, A3}; #endif // number of pins -uint8_t pincount = sizeof(pins)/sizeof(pins[0]); +uint8_t pincount = sizeof(pins) / sizeof(pins[0]); // For keycode definition check out https://github.com/hathach/tinyusb/blob/master/src/class/hid/hid.h -uint8_t hidcode[] = { HID_KEY_ARROW_RIGHT, HID_KEY_ARROW_LEFT, HID_KEY_ARROW_DOWN, HID_KEY_ARROW_UP }; +uint8_t hidcode[] = {HID_KEY_0, HID_KEY_1, HID_KEY_2, HID_KEY_3}; #if defined(ARDUINO_SAMD_CIRCUITPLAYGROUND_EXPRESS) || defined(ARDUINO_NRF52840_CIRCUITPLAY) || defined(ARDUINO_FUNHOUSE_ESP32S2) - bool activeState = true; +bool activeState = true; #else - bool activeState = false; +bool activeState = false; #endif -//------------- Neopixel -------------// -// #define PIN_NEOPIXEL 8 -#ifdef PIN_NEOPIXEL - -// How many NeoPixels are attached to the Arduino? -// use on-board defined NEOPIXEL_NUM if existed -#ifndef NEOPIXEL_NUM - #define NEOPIXEL_NUM 10 -#endif - -Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NEOPIXEL_NUM, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800); - -#endif - - // the setup function runs once when you press reset or power the board -void setup() -{ -#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040) - // Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040 - TinyUSB_Device_Init(0); -#endif +void setup() { + // Manual begin() is required on core without built-in support e.g. mbed rp2040 + if (!TinyUSBDevice.isInitialized()) { + TinyUSBDevice.begin(0); + } // Setup HID usb_hid.setBootProtocol(HID_ITF_PROTOCOL_KEYBOARD); @@ -87,17 +70,6 @@ void setup() pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, LOW); - // neopixel if existed -#ifdef PIN_NEOPIXEL - pixels.begin(); - pixels.setBrightness(50); - - #ifdef NEOPIXEL_POWER - pinMode(NEOPIXEL_POWER, OUTPUT); - digitalWrite(NEOPIXEL_POWER, NEOPIXEL_POWER_ON); - #endif -#endif - // overwrite input pin with PIN_BUTTONx #ifdef PIN_BUTTON1 pins[0] = PIN_BUTTON1; @@ -116,32 +88,21 @@ void setup() #endif // Set up pin as input - for (uint8_t i=0; i 2) { + ms = millis(); + process_hid(); + } +} + // Output report callback for LED indicator such as Caplocks -void hid_report_callback(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) -{ +void hid_report_callback(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) { (void) report_id; (void) bufsize; // LED indicator is output report with only 1 byte length - if ( report_type != HID_REPORT_TYPE_OUTPUT ) return; + if (report_type != HID_REPORT_TYPE_OUTPUT) return; // The LED bit map is as follows: (also defined by KEYBOARD_LED_* ) // Kana (4) | Compose (3) | ScrollLock (2) | CapsLock (1) | Numlock (0) @@ -196,9 +171,4 @@ void hid_report_callback(uint8_t report_id, hid_report_type_t report_type, uint8 // turn on LED if capslock is set digitalWrite(LED_BUILTIN, ledIndicator & KEYBOARD_LED_CAPSLOCK); - -#ifdef PIN_NEOPIXEL - pixels.fill(ledIndicator & KEYBOARD_LED_CAPSLOCK ? 0xff0000 : 0x000000); - pixels.show(); -#endif } diff --git a/examples/HID/hid_boot_mouse/hid_boot_mouse.ino b/examples/HID/hid_boot_mouse/hid_boot_mouse.ino index 1715d768..39455822 100644 --- a/examples/HID/hid_boot_mouse/hid_boot_mouse.ino +++ b/examples/HID/hid_boot_mouse/hid_boot_mouse.ino @@ -19,43 +19,45 @@ * and its active state (when pressed) are different */ #if defined(ARDUINO_SAMD_CIRCUITPLAYGROUND_EXPRESS) || defined(ARDUINO_NRF52840_CIRCUITPLAY) - const int pin = 4; // Left Button - bool activeState = true; +const int pin = 4; // Left Button +bool activeState = true; #elif defined(ARDUINO_FUNHOUSE_ESP32S2) - const int pin = BUTTON_DOWN; - bool activeState = true; +const int pin = BUTTON_DOWN; +bool activeState = true; #elif defined PIN_BUTTON1 - const int pin = PIN_BUTTON1; - bool activeState = false; +const int pin = PIN_BUTTON1; +bool activeState = false; #elif defined(ARDUINO_ARCH_ESP32) - const int pin = 0; - bool activeState = false; +const int pin = 0; +bool activeState = false; +#elif defined(ARDUINO_ARCH_RP2040) +const int pin = D0; +bool activeState = false; #else - const int pin = 12; - bool activeState = false; +const int pin = A0; +bool activeState = false; #endif // HID report descriptor using TinyUSB's template // Single Report (no ID) descriptor uint8_t const desc_hid_report[] = { - TUD_HID_REPORT_DESC_MOUSE() + TUD_HID_REPORT_DESC_MOUSE() }; // USB HID object Adafruit_USBD_HID usb_hid; // the setup function runs once when you press reset or power the board -void setup() -{ -#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040) - // Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040 - TinyUSB_Device_Init(0); -#endif +void setup() { + // Manual begin() is required on core without built-in support e.g. mbed rp2040 + if (!TinyUSBDevice.isInitialized()) { + TinyUSBDevice.begin(0); + } // Set up button, pullup opposite to active state pinMode(pin, activeState ? INPUT_PULLDOWN : INPUT_PULLUP); @@ -69,18 +71,10 @@ void setup() usb_hid.begin(); Serial.begin(115200); - - // wait until device mounted - while( !TinyUSBDevice.mounted() ) delay(1); - Serial.println("Adafruit TinyUSB HID Mouse example"); } -void loop() -{ - // poll gpio once each 10 ms - delay(10); - +void process_hid() { // Whether button is pressed bool btn_pressed = (digitalRead(pin) == activeState); @@ -88,17 +82,34 @@ void loop() if (!btn_pressed) return; // Remote wakeup - if ( TinyUSBDevice.suspended() ) - { + if (TinyUSBDevice.suspended()) { // Wake up host if we are in suspend mode // and REMOTE_WAKEUP feature is enabled by host TinyUSBDevice.remoteWakeup(); } - if ( usb_hid.ready() ) - { + if (usb_hid.ready()) { uint8_t const report_id = 0; // no ID int8_t const delta = 5; usb_hid.mouseMove(report_id, delta, delta); // right + down } } + +void loop() { + #ifdef TINYUSB_NEED_POLLING_TASK + // Manual call tud_task since it isn't called by Core's background + TinyUSBDevice.task(); + #endif + + // not enumerated()/mounted() yet: nothing to do + if (!TinyUSBDevice.mounted()) { + return; + } + + // poll gpio once each 10 ms + static uint32_t ms = 0; + if (millis() - ms > 10) { + ms = millis(); + process_hid(); + } +} diff --git a/examples/HID/hid_composite/hid_composite.ino b/examples/HID/hid_composite/hid_composite.ino index 3f179260..f2b9fdf4 100644 --- a/examples/HID/hid_composite/hid_composite.ino +++ b/examples/HID/hid_composite/hid_composite.ino @@ -20,30 +20,34 @@ * and its active state (when pressed) are different */ #if defined(ARDUINO_SAMD_CIRCUITPLAYGROUND_EXPRESS) || defined(ARDUINO_NRF52840_CIRCUITPLAY) - const int pin = 4; // Left Button - bool activeState = true; +const int pin = 4; // Left Button +bool activeState = true; #elif defined(ARDUINO_FUNHOUSE_ESP32S2) - const int pin = BUTTON_DOWN; - bool activeState = true; +const int pin = BUTTON_DOWN; +bool activeState = true; #elif defined PIN_BUTTON1 - const int pin = PIN_BUTTON1; - bool activeState = false; +const int pin = PIN_BUTTON1; +bool activeState = false; #elif defined(ARDUINO_ARCH_ESP32) - const int pin = 0; - bool activeState = false; +const int pin = 0; +bool activeState = false; + +#elif defined(ARDUINO_ARCH_RP2040) +const int pin = D0; +bool activeState = false; #else - const int pin = 12; - bool activeState = false; +const int pin = A0; +bool activeState = false; + #endif // Report ID -enum -{ +enum { RID_KEYBOARD = 1, RID_MOUSE, RID_CONSUMER_CONTROL, // Media, volume etc .. @@ -51,17 +55,21 @@ enum // HID report descriptor using TinyUSB's template uint8_t const desc_hid_report[] = { - TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(RID_KEYBOARD) ), - TUD_HID_REPORT_DESC_MOUSE ( HID_REPORT_ID(RID_MOUSE) ), - TUD_HID_REPORT_DESC_CONSUMER( HID_REPORT_ID(RID_CONSUMER_CONTROL) ) + TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(RID_KEYBOARD)), + TUD_HID_REPORT_DESC_MOUSE (HID_REPORT_ID(RID_MOUSE)), + TUD_HID_REPORT_DESC_CONSUMER(HID_REPORT_ID(RID_CONSUMER_CONTROL)) }; // USB HID object. Adafruit_USBD_HID usb_hid; // the setup function runs once when you press reset or power the board -void setup() -{ +void setup() { + // Manual begin() is required on core without built-in support e.g. mbed rp2040 + if (!TinyUSBDevice.isInitialized()) { + TinyUSBDevice.begin(0); + } + // Set up HID usb_hid.setPollInterval(2); usb_hid.setReportDescriptor(desc_hid_report, sizeof(desc_hid_report)); @@ -73,32 +81,22 @@ void setup() pinMode(pin, activeState ? INPUT_PULLDOWN : INPUT_PULLUP); Serial.begin(115200); - - // wait until device mounted - while( !TinyUSBDevice.mounted() ) delay(1); - Serial.println("Adafruit TinyUSB HID Composite example"); } -void loop() -{ - // poll gpio once each 10 ms - delay(10); - +void process_hid() { // Whether button is pressed bool btn_pressed = (digitalRead(pin) == activeState); // Remote wakeup - if ( TinyUSBDevice.suspended() && btn_pressed ) - { + if (TinyUSBDevice.suspended() && btn_pressed) { // Wake up host if we are in suspend mode // and REMOTE_WAKEUP feature is enabled by host TinyUSBDevice.remoteWakeup(); } /*------------- Mouse -------------*/ - if ( usb_hid.ready() && btn_pressed ) - { + if (usb_hid.ready() && btn_pressed) { int8_t const delta = 5; usb_hid.mouseMove(RID_MOUSE, delta, delta); // right + down @@ -107,21 +105,18 @@ void loop() } /*------------- Keyboard -------------*/ - if ( usb_hid.ready() ) - { + if (usb_hid.ready()) { // use to send key release report static bool has_key = false; - if ( btn_pressed ) - { - uint8_t keycode[6] = { 0 }; + if (btn_pressed) { + uint8_t keycode[6] = {0}; keycode[0] = HID_KEY_A; usb_hid.keyboardReport(RID_KEYBOARD, 0, keycode); has_key = true; - }else - { + } else { // send empty key report if previously has key pressed if (has_key) usb_hid.keyboardRelease(RID_KEYBOARD); has_key = false; @@ -132,8 +127,7 @@ void loop() } /*------------- Consumer Control -------------*/ - if ( usb_hid.ready() ) - { + if (usb_hid.ready()) { // Consumer Control is used to control Media playback, Volume, Brightness etc ... // Consumer report is 2-byte containing the control code of the key // For list of control check out https://github.com/hathach/tinyusb/blob/master/src/class/hid/hid.h @@ -141,16 +135,33 @@ void loop() // use to send consumer release report static bool has_consumer_key = false; - if ( btn_pressed ) - { + if (btn_pressed) { // send volume down (0x00EA) usb_hid.sendReport16(RID_CONSUMER_CONTROL, HID_USAGE_CONSUMER_VOLUME_DECREMENT); has_consumer_key = true; - }else - { + } else { // release the consume key by sending zero (0x0000) if (has_consumer_key) usb_hid.sendReport16(RID_CONSUMER_CONTROL, 0); has_consumer_key = false; } } } + +void loop() { + #ifdef TINYUSB_NEED_POLLING_TASK + // Manual call tud_task since it isn't called by Core's background + TinyUSBDevice.task(); + #endif + + // not enumerated()/mounted() yet: nothing to do + if (!TinyUSBDevice.mounted()) { + return; + } + + // poll gpio once each 10 ms + static uint32_t ms = 0; + if (millis() - ms > 10) { + ms = millis(); + process_hid(); + } +} diff --git a/examples/HID/hid_composite_joy_featherwing/hid_composite_joy_featherwing.ino b/examples/HID/hid_composite_joy_featherwing/hid_composite_joy_featherwing.ino index 01f4481d..11bcf414 100644 --- a/examples/HID/hid_composite_joy_featherwing/hid_composite_joy_featherwing.ino +++ b/examples/HID/hid_composite_joy_featherwing/hid_composite_joy_featherwing.ino @@ -33,17 +33,15 @@ uint32_t button_mask = (1 << BUTTON_A) | (1 << BUTTON_B) | Adafruit_seesaw ss; // Report ID -enum -{ +enum { RID_KEYBOARD = 1, RID_MOUSE }; // HID report descriptor using TinyUSB's template -uint8_t const desc_hid_report[] = -{ - TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(RID_KEYBOARD) ), - TUD_HID_REPORT_DESC_MOUSE ( HID_REPORT_ID(RID_MOUSE) ) +uint8_t const desc_hid_report[] = { + TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(RID_KEYBOARD)), + TUD_HID_REPORT_DESC_MOUSE (HID_REPORT_ID(RID_MOUSE)) }; // USB HID object. For ESP32 these values cannot be changed after this declaration @@ -53,20 +51,23 @@ Adafruit_USBD_HID usb_hid(desc_hid_report, sizeof(desc_hid_report), HID_ITF_PROT int last_x, last_y; // the setup function runs once when you press reset or power the board -void setup() -{ +void setup() { + // Manual begin() is required on core without built-in support e.g. mbed rp2040 + if (!TinyUSBDevice.isInitialized()) { + TinyUSBDevice.begin(0); + } + // Notes: following commented-out functions has no affect on ESP32 // usb_hid.setPollInterval(2); // usb_hid.setReportDescriptor(desc_hid_report, sizeof(desc_hid_report)); - usb_hid.begin(); Serial.begin(115200); Serial.println("Adafruit TinyUSB HID Mouse with Joy FeatherWing example"); - if(!ss.begin(0x49)){ + if (!ss.begin(0x49)) { Serial.println("ERROR! seesaw not found"); - while(1); + while (1) {} } else { Serial.println("seesaw started"); Serial.print("version: "); @@ -77,16 +78,9 @@ void setup() last_y = ss.analogRead(2); last_x = ss.analogRead(3); - - // wait until device mounted - while( !TinyUSBDevice.mounted() ) delay(1); } -void loop() -{ - // poll gpio once each 10 ms - delay(10); - +void process_hid() { // If either analog stick or any buttons is pressed bool has_action = false; @@ -98,12 +92,10 @@ void loop() int dx = (x - last_x) / 2; int dy = (y - last_y) / 2; - if ( (abs(dx) > 3) || (abs(dy) > 3) ) - { + if ((abs(dx) > 3) || (abs(dy) > 3)) { has_action = true; - if ( usb_hid.ready() ) - { + if (usb_hid.ready()) { usb_hid.mouseMove(RID_MOUSE, dx, dy); // no ID: right + down last_x = x; @@ -114,31 +106,27 @@ void loop() } } - /*------------- Keyboard -------------*/ // button is active low, invert read value for convenience uint32_t buttons = ~ss.digitalReadBulk(button_mask); - if ( usb_hid.ready() ) - { + if (usb_hid.ready()) { // use to prevent sending multiple consecutive zero report static bool has_key = false; - if ( buttons & button_mask ) - { + if (buttons & button_mask) { has_action = true; has_key = true; - uint8_t keycode[6] = { 0 }; - - if ( buttons & (1 << BUTTON_A) ) keycode[0] = HID_KEY_A; - if ( buttons & (1 << BUTTON_B) ) keycode[0] = HID_KEY_B; - if ( buttons & (1 << BUTTON_X) ) keycode[0] = HID_KEY_X; - if ( buttons & (1 << BUTTON_Y) ) keycode[0] = HID_KEY_Y; + uint8_t keycode[6] = {0}; + + if (buttons & (1 << BUTTON_A)) keycode[0] = HID_KEY_A; + if (buttons & (1 << BUTTON_B)) keycode[0] = HID_KEY_B; + if (buttons & (1 << BUTTON_X)) keycode[0] = HID_KEY_X; + if (buttons & (1 << BUTTON_Y)) keycode[0] = HID_KEY_Y; usb_hid.keyboardReport(RID_KEYBOARD, 0, keycode); - }else - { + } else { // send empty key report if previously has key pressed if (has_key) usb_hid.keyboardRelease(RID_KEYBOARD); has_key = false; @@ -147,10 +135,28 @@ void loop() /*------------- Remote Wakeup -------------*/ // Remote wakeup if PC is suspended and we has user interaction with joy feather wing - if ( has_action && TinyUSBDevice.suspended() ) - { + if (has_action && TinyUSBDevice.suspended()) { // Wake up only works if REMOTE_WAKEUP feature is enable by host // Usually this is the case with Mouse/Keyboard device TinyUSBDevice.remoteWakeup(); } } + +void loop() { + #ifdef TINYUSB_NEED_POLLING_TASK + // Manual call tud_task since it isn't called by Core's background + TinyUSBDevice.task(); + #endif + + // not enumerated()/mounted() yet: nothing to do + if (!TinyUSBDevice.mounted()) { + return; + } + + // poll gpio once each 10 ms + static uint32_t ms = 0; + if (millis() - ms > 10) { + ms = millis(); + process_hid(); + } +} diff --git a/examples/HID/hid_dual_interfaces/hid_dual_interfaces.ino b/examples/HID/hid_dual_interfaces/hid_dual_interfaces.ino index 43f5445b..7a863c06 100644 --- a/examples/HID/hid_dual_interfaces/hid_dual_interfaces.ino +++ b/examples/HID/hid_dual_interfaces/hid_dual_interfaces.ino @@ -19,37 +19,41 @@ * and its active state (when pressed) are different */ #if defined(ARDUINO_SAMD_CIRCUITPLAYGROUND_EXPRESS) || defined(ARDUINO_NRF52840_CIRCUITPLAY) - const int pin = 4; // Left Button - bool activeState = true; +const int pin = 4; // Left Button +bool activeState = true; #elif defined(ARDUINO_FUNHOUSE_ESP32S2) - const int pin = BUTTON_DOWN; - bool activeState = true; +const int pin = BUTTON_DOWN; +bool activeState = true; #elif defined PIN_BUTTON1 - const int pin = PIN_BUTTON1; - bool activeState = false; +const int pin = PIN_BUTTON1; +bool activeState = false; #elif defined PIN_BUTTON - const int pin = PIN_BUTTON; - bool activeState = false; +const int pin = PIN_BUTTON; +bool activeState = false; #elif defined(ARDUINO_ARCH_ESP32) - const int pin = 0; - bool activeState = false; +const int pin = 0; +bool activeState = false; +#elif defined(ARDUINO_ARCH_RP2040) +const int pin = D0; +bool activeState = false; #else - const int pin = 12; - bool activeState = false; + +const int pin = A0; +bool activeState = false; #endif // HID report descriptor using TinyUSB's template uint8_t const desc_keyboard_report[] = { - TUD_HID_REPORT_DESC_KEYBOARD() + TUD_HID_REPORT_DESC_KEYBOARD() }; uint8_t const desc_mouse_report[] = { - TUD_HID_REPORT_DESC_MOUSE() + TUD_HID_REPORT_DESC_MOUSE() }; // USB HID objects @@ -57,8 +61,12 @@ Adafruit_USBD_HID usb_keyboard; Adafruit_USBD_HID usb_mouse; // the setup function runs once when you press reset or power the board -void setup() -{ +void setup() { + // Manual begin() is required on core without built-in support e.g. mbed rp2040 + if (!TinyUSBDevice.isInitialized()) { + TinyUSBDevice.begin(0); + } + // HID Keyboard usb_keyboard.setPollInterval(2); usb_keyboard.setBootProtocol(HID_ITF_PROTOCOL_KEYBOARD); @@ -77,54 +85,61 @@ void setup() pinMode(pin, activeState ? INPUT_PULLDOWN : INPUT_PULLUP); Serial.begin(115200); - - // wait until device mounted - //while( !TinyUSBDevice.mounted() ) delay(1); Serial.println("Adafruit TinyUSB HID Composite example"); } -void loop() -{ - // poll gpio once each 10 ms - delay(10); - +void process_hid() { // Whether button is pressed bool btn_pressed = (digitalRead(pin) == activeState); // Remote wakeup - if ( TinyUSBDevice.suspended() && btn_pressed ) - { + if (TinyUSBDevice.suspended() && btn_pressed) { // Wake up host if we are in suspend mode // and REMOTE_WAKEUP feature is enabled by host TinyUSBDevice.remoteWakeup(); } /*------------- Mouse -------------*/ - if (usb_mouse.ready() && btn_pressed ) - { + if (usb_mouse.ready() && btn_pressed) { int8_t const delta = 5; usb_mouse.mouseMove(0, delta, delta); // right + down } /*------------- Keyboard -------------*/ - if ( usb_keyboard.ready() ) - { + if (usb_keyboard.ready()) { // use to send key release report static bool has_key = false; - if ( btn_pressed ) - { - uint8_t keycode[6] = { 0 }; + if (btn_pressed) { + uint8_t keycode[6] = {0}; keycode[0] = HID_KEY_A; usb_keyboard.keyboardReport(0, 0, keycode); has_key = true; - }else - { + } else { // send empty key report if previously has key pressed if (has_key) usb_keyboard.keyboardRelease(0); has_key = false; } } +} + +void loop() { + #ifdef TINYUSB_NEED_POLLING_TASK + // Manual call tud_task since it isn't called by Core's background + TinyUSBDevice.task(); + #endif + + // not enumerated()/mounted() yet: nothing to do + if (!TinyUSBDevice.mounted()) { + return; + } + + // poll gpio once each 10 ms + static uint32_t ms = 0; + if (millis() - ms > 10) { + ms = millis(); + process_hid(); + } } \ No newline at end of file diff --git a/examples/HID/hid_gamepad/hid_gamepad.ino b/examples/HID/hid_gamepad/hid_gamepad.ino index 84b3289c..79fc8f07 100644 --- a/examples/HID/hid_gamepad/hid_gamepad.ino +++ b/examples/HID/hid_gamepad/hid_gamepad.ino @@ -23,7 +23,7 @@ // HID report descriptor using TinyUSB's template // Single Report (no ID) descriptor uint8_t const desc_hid_report[] = { - TUD_HID_REPORT_DESC_GAMEPAD() + TUD_HID_REPORT_DESC_GAMEPAD() }; // USB HID object @@ -32,31 +32,35 @@ Adafruit_USBD_HID usb_hid; // Report payload defined in src/class/hid/hid.h // - For Gamepad Button Bit Mask see hid_gamepad_button_bm_t // - For Gamepad Hat Bit Mask see hid_gamepad_hat_t -hid_gamepad_report_t gp; +hid_gamepad_report_t gp; -void setup() -{ -#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040) - // Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040 - TinyUSB_Device_Init(0); -#endif +void setup() { + // Manual begin() is required on core without built-in support e.g. mbed rp2040 + if (!TinyUSBDevice.isInitialized()) { + TinyUSBDevice.begin(0); + } Serial.begin(115200); - + // Setup HID usb_hid.setPollInterval(2); usb_hid.setReportDescriptor(desc_hid_report, sizeof(desc_hid_report)); - usb_hid.begin(); - // wait until device mounted - while( !TinyUSBDevice.mounted() ) delay(1); - Serial.println("Adafruit TinyUSB HID Gamepad example"); } -void loop() -{ +void loop() { + #ifdef TINYUSB_NEED_POLLING_TASK + // Manual call tud_task since it isn't called by Core's background + TinyUSBDevice.task(); + #endif + + // not enumerated()/mounted() yet: nothing to do + if (!TinyUSBDevice.mounted()) { + return; + } + // // Remote wakeup // if ( TinyUSBDevice.suspended() && btn ) // { @@ -65,22 +69,22 @@ void loop() // TinyUSBDevice.remoteWakeup(); // } - if ( !usb_hid.ready() ) return; + if (!usb_hid.ready()) return; // Reset buttons Serial.println("No pressing buttons"); - gp.x = 0; - gp.y = 0; - gp.z = 0; - gp.rz = 0; - gp.rx = 0; - gp.ry = 0; - gp.hat = 0; + gp.x = 0; + gp.y = 0; + gp.z = 0; + gp.rz = 0; + gp.rx = 0; + gp.ry = 0; + gp.hat = 0; gp.buttons = 0; usb_hid.sendReport(0, &gp, sizeof(gp)); delay(2000); - + // Hat/DPAD UP Serial.println("Hat/DPAD UP"); gp.hat = 1; // GAMEPAD_HAT_UP; @@ -105,12 +109,12 @@ void loop() usb_hid.sendReport(0, &gp, sizeof(gp)); delay(2000); - // Hat/DPAD DOWN + // Hat/DPAD DOWN Serial.println("Hat/DPAD DOWN"); gp.hat = 5; // GAMEPAD_HAT_DOWN; usb_hid.sendReport(0, &gp, sizeof(gp)); delay(2000); - + // Hat/DPAD DOWN LEFT Serial.println("Hat/DPAD DOWN LEFT"); gp.hat = 6; // GAMEPAD_HAT_DOWN_LEFT; @@ -135,14 +139,14 @@ void loop() usb_hid.sendReport(0, &gp, sizeof(gp)); delay(2000); - + // Joystick 1 UP Serial.println("Joystick 1 UP"); gp.x = 0; gp.y = -127; usb_hid.sendReport(0, &gp, sizeof(gp)); delay(2000); - + // Joystick 1 DOWN Serial.println("Joystick 1 DOWN"); gp.x = 0; @@ -156,7 +160,7 @@ void loop() gp.y = 0; usb_hid.sendReport(0, &gp, sizeof(gp)); delay(2000); - + // Joystick 1 LEFT Serial.println("Joystick 1 LEFT"); gp.x = -127; @@ -174,35 +178,35 @@ void loop() // Joystick 2 UP Serial.println("Joystick 2 UP"); - gp.z = 0; + gp.z = 0; gp.rz = 127; usb_hid.sendReport(0, &gp, sizeof(gp)); delay(2000); - + // Joystick 2 DOWN Serial.println("Joystick 2 DOWN"); - gp.z = 0; + gp.z = 0; gp.rz = -127; usb_hid.sendReport(0, &gp, sizeof(gp)); delay(2000); // Joystick 2 RIGHT Serial.println("Joystick 2 RIGHT"); - gp.z = 127; + gp.z = 127; gp.rz = 0; usb_hid.sendReport(0, &gp, sizeof(gp)); delay(2000); - + // Joystick 2 LEFT Serial.println("Joystick 2 LEFT"); - gp.z = -127; + gp.z = -127; gp.rz = 0; usb_hid.sendReport(0, &gp, sizeof(gp)); delay(2000); // Joystick 2 CENTER Serial.println("Joystick 2 CENTER"); - gp.z = 0; + gp.z = 0; gp.rz = 0; usb_hid.sendReport(0, &gp, sizeof(gp)); delay(2000); @@ -213,7 +217,7 @@ void loop() gp.rx = 127; usb_hid.sendReport(0, &gp, sizeof(gp)); delay(2000); - + // Analog Trigger 1 DOWN Serial.println("Analog Trigger 1 DOWN"); gp.rx = -127; @@ -232,7 +236,7 @@ void loop() gp.ry = 127; usb_hid.sendReport(0, &gp, sizeof(gp)); delay(2000); - + // Analog Trigger 2 DOWN Serial.println("Analog Trigger 2 DOWN"); gp.ry = -127; @@ -245,11 +249,11 @@ void loop() usb_hid.sendReport(0, &gp, sizeof(gp)); delay(2000); - + // Test buttons (up to 32 buttons) - for (int i=0; i<32; ++i) - { - Serial.print("Pressing button "); Serial.println(i); + for (int i = 0; i < 32; ++i) { + Serial.print("Pressing button "); + Serial.println(i); gp.buttons = (1U << i); usb_hid.sendReport(0, &gp, sizeof(gp)); delay(1000); @@ -258,13 +262,13 @@ void loop() // Random touch Serial.println("Random touch"); - gp.x = random(-127, 128); - gp.y = random(-127, 128); - gp.z = random(-127, 128); - gp.rz = random(-127, 128); - gp.rx = random(-127, 128); - gp.ry = random(-127, 128); - gp.hat = random(0, 9); + gp.x = random(-127, 128); + gp.y = random(-127, 128); + gp.z = random(-127, 128); + gp.rz = random(-127, 128); + gp.rx = random(-127, 128); + gp.ry = random(-127, 128); + gp.hat = random(0, 9); gp.buttons = random(0, 0xffff); usb_hid.sendReport(0, &gp, sizeof(gp)); delay(2000); diff --git a/examples/HID/hid_generic_inout/hid_generic_inout.ino b/examples/HID/hid_generic_inout/hid_generic_inout.ino index b543dd78..ab4974ef 100644 --- a/examples/HID/hid_generic_inout/hid_generic_inout.ino +++ b/examples/HID/hid_generic_inout/hid_generic_inout.ino @@ -40,19 +40,18 @@ // HID report descriptor using TinyUSB's template // Generic In Out with 64 bytes report (max) uint8_t const desc_hid_report[] = { - TUD_HID_REPORT_DESC_GENERIC_INOUT(64) + TUD_HID_REPORT_DESC_GENERIC_INOUT(64) }; // USB HID object Adafruit_USBD_HID usb_hid; // the setup function runs once when you press reset or power the board -void setup() -{ -#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040) - // Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040 - TinyUSB_Device_Init(0); -#endif +void setup() { + // Manual begin() is required on core without built-in support e.g. mbed rp2040 + if (!TinyUSBDevice.isInitialized()) { + TinyUSBDevice.begin(0); + } // Notes: following commented-out functions has no affect on ESP32 usb_hid.enableOutEndpoint(true); @@ -64,23 +63,20 @@ void setup() usb_hid.begin(); Serial.begin(115200); - - // wait until device mounted - while( !TinyUSBDevice.mounted() ) delay(1); - Serial.println("Adafruit TinyUSB HID Generic In Out example"); } -void loop() -{ - // nothing to do +void loop() { + #ifdef TINYUSB_NEED_POLLING_TASK + // Manual call tud_task since it isn't called by Core's background + TinyUSBDevice.task(); + #endif } // Invoked when received GET_REPORT control request // Application must fill buffer report's content and return its length. // Return zero will cause the stack to STALL request -uint16_t get_report_callback (uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) -{ +uint16_t get_report_callback(uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) { // not used in this example (void) report_id; (void) report_type; @@ -91,8 +87,7 @@ uint16_t get_report_callback (uint8_t report_id, hid_report_type_t report_type, // Invoked when received SET_REPORT control request or // received data on OUT endpoint ( Report ID = 0, Type = 0 ) -void set_report_callback(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) -{ +void set_report_callback(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) { // This example doesn't use multiple report and report ID (void) report_id; (void) report_type; diff --git a/examples/MIDI/midi_multi_ports/midi_multi_ports.ino b/examples/MIDI/midi_multi_ports/midi_multi_ports.ino index 8d39c06c..c7edd8ee 100644 --- a/examples/MIDI/midi_multi_ports/midi_multi_ports.ino +++ b/examples/MIDI/midi_multi_ports/midi_multi_ports.ino @@ -23,10 +23,10 @@ void setup() { pinMode(LED_BUILTIN, OUTPUT); -#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040) - // Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040 - TinyUSB_Device_Init(0); -#endif + // Manual begin() is required on core without built-in support e.g. mbed rp2040 + if (!TinyUSBDevice.isInitialized()) { + TinyUSBDevice.begin(0); + } // Set name for each cable, must be done before usb_midi.begin() usb_midi.setCableName(1, "Keyboard"); @@ -38,9 +38,16 @@ void setup() void loop() { - digitalWrite(LED_BUILTIN, HIGH); - delay(1000); - - digitalWrite(LED_BUILTIN, LOW); - delay(1000); + #ifdef TINYUSB_NEED_POLLING_TASK + // Manual call tud_task since it isn't called by Core's background + TinyUSBDevice.task(); + #endif + + // toggle LED + static uint32_t ms = 0; + static uint8_t led_state = 0; + if (millis() - ms > 1000) { + ms = millis(); + digitalWrite(LED_BUILTIN, 1-led_state); + } } diff --git a/examples/MIDI/midi_test/midi_test.ino b/examples/MIDI/midi_test/midi_test.ino index ce41970b..22cd124c 100644 --- a/examples/MIDI/midi_test/midi_test.ino +++ b/examples/MIDI/midi_test/midi_test.ino @@ -31,20 +31,19 @@ uint32_t position = 0; // Store example melody as an array of note values byte note_sequence[] = { - 74,78,81,86,90,93,98,102,57,61,66,69,73,78,81,85,88,92,97,100,97,92,88,85,81,78, - 74,69,66,62,57,62,66,69,74,78,81,86,90,93,97,102,97,93,90,85,81,78,73,68,64,61, - 56,61,64,68,74,78,81,86,90,93,98,102 + 74, 78, 81, 86, 90, 93, 98, 102, 57, 61, 66, 69, 73, 78, 81, 85, 88, 92, 97, 100, 97, 92, 88, 85, 81, 78, + 74, 69, 66, 62, 57, 62, 66, 69, 74, 78, 81, 86, 90, 93, 97, 102, 97, 93, 90, 85, 81, 78, 73, 68, 64, 61, + 56, 61, 64, 68, 74, 78, 81, 86, 90, 93, 98, 102 }; -void setup() -{ -#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040) - // Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040 - TinyUSB_Device_Init(0); -#endif +void setup() { + // Manual begin() is required on core without built-in support e.g. mbed rp2040 + if (!TinyUSBDevice.isInitialized()) { + TinyUSBDevice.begin(0); + } pinMode(LED_BUILTIN, OUTPUT); - + usb_midi.setStringDescriptor("TinyUSB MIDI"); // Initialize MIDI, and listen to all MIDI channels @@ -59,37 +58,42 @@ void setup() MIDI.setHandleNoteOff(handleNoteOff); Serial.begin(115200); - - // wait until device mounted - while( !TinyUSBDevice.mounted() ) delay(1); } -void loop() -{ +void loop() { + #ifdef TINYUSB_NEED_POLLING_TASK + // Manual call tud_task since it isn't called by Core's background + TinyUSBDevice.task(); + #endif + + // not enumerated()/mounted() yet: nothing to do + if (!TinyUSBDevice.mounted()) { + return; + } + static uint32_t start_ms = 0; - if ( millis() - start_ms > 266 ) - { + if (millis() - start_ms > 266) { start_ms += 266; - + // Setup variables for the current and previous // positions in the note sequence. int previous = position - 1; - + // If we currently are at position 0, set the // previous position to the last note in the sequence. if (previous < 0) { previous = sizeof(note_sequence) - 1; } - + // Send Note On for current position at full velocity (127) on channel 1. MIDI.sendNoteOn(note_sequence[position], 127, 1); - + // Send Note Off for previous note. MIDI.sendNoteOff(note_sequence[previous], 0, 1); - + // Increment position position++; - + // If we are at the end of the sequence, start over. if (position >= sizeof(note_sequence)) { position = 0; @@ -97,11 +101,10 @@ void loop() } // read any new MIDI messages - MIDI.read(); + MIDI.read(); } -void handleNoteOn(byte channel, byte pitch, byte velocity) -{ +void handleNoteOn(byte channel, byte pitch, byte velocity) { // Log when a note is pressed. Serial.print("Note on: channel = "); Serial.print(channel); @@ -113,8 +116,7 @@ void handleNoteOn(byte channel, byte pitch, byte velocity) Serial.println(velocity); } -void handleNoteOff(byte channel, byte pitch, byte velocity) -{ +void handleNoteOff(byte channel, byte pitch, byte velocity) { // Log when a note is released. Serial.print("Note off: channel = "); Serial.print(channel); diff --git a/examples/MassStorage/msc_ramdisk_dual/msc_ramdisk_dual.ino b/examples/MassStorage/msc_ramdisk_dual/msc_ramdisk_dual.ino index 302fb7e4..27322cba 100644 --- a/examples/MassStorage/msc_ramdisk_dual/msc_ramdisk_dual.ino +++ b/examples/MassStorage/msc_ramdisk_dual/msc_ramdisk_dual.ino @@ -14,20 +14,20 @@ // 8KB is the smallest size that windows allow to mount #define DISK_BLOCK_NUM 16 #define DISK_BLOCK_SIZE 512 + #include "ramdisk.h" Adafruit_USBD_MSC usb_msc; // the setup function runs once when you press reset or power the board -void setup() -{ -#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040) - // Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040 - TinyUSB_Device_Init(0); -#endif +void setup() { + // Manual begin() is required on core without built-in support e.g. mbed rp2040 + if (!TinyUSBDevice.isInitialized()) { + TinyUSBDevice.begin(0); + } usb_msc.setMaxLun(2); - + // Set disk size and callback for Logical Unit 0 (LUN 0) usb_msc.setID(0, "Adafruit", "Lun0", "1.0"); usb_msc.setCapacity(0, DISK_BLOCK_NUM, DISK_BLOCK_SIZE); @@ -39,7 +39,7 @@ void setup() usb_msc.setCapacity(1, DISK_BLOCK_NUM, DISK_BLOCK_SIZE); usb_msc.setReadWriteCallback(1, ram1_read_cb, ram1_write_cb, ram1_flush_cb); usb_msc.setUnitReady(1, true); - + usb_msc.begin(); Serial.begin(115200); @@ -48,9 +48,11 @@ void setup() Serial.println("Adafruit TinyUSB Mass Storage Dual RAM Disks example"); } -void loop() -{ - // nothing to do +void loop() { + #ifdef TINYUSB_NEED_POLLING_TASK + // Manual call tud_task since it isn't called by Core's background + TinyUSBDevice.task(); + #endif } @@ -61,8 +63,7 @@ void loop() // Callback invoked when received READ10 command. // Copy disk's data to buffer (up to bufsize) and // return number of copied bytes (must be multiple of block size) -int32_t ram0_read_cb (uint32_t lba, void* buffer, uint32_t bufsize) -{ +int32_t ram0_read_cb(uint32_t lba, void* buffer, uint32_t bufsize) { uint8_t const* addr = msc_disk0[lba]; memcpy(buffer, addr, bufsize); @@ -72,8 +73,7 @@ int32_t ram0_read_cb (uint32_t lba, void* buffer, uint32_t bufsize) // Callback invoked when received WRITE10 command. // Process data in buffer to disk's storage and // return number of written bytes (must be multiple of block size) -int32_t ram0_write_cb (uint32_t lba, uint8_t* buffer, uint32_t bufsize) -{ +int32_t ram0_write_cb(uint32_t lba, uint8_t* buffer, uint32_t bufsize) { uint8_t* addr = msc_disk0[lba]; memcpy(addr, buffer, bufsize); @@ -82,8 +82,7 @@ int32_t ram0_write_cb (uint32_t lba, uint8_t* buffer, uint32_t bufsize) // Callback invoked when WRITE10 command is completed (status received and accepted by host). // used to flush any pending cache. -void ram0_flush_cb (void) -{ +void ram0_flush_cb(void) { // nothing to do } @@ -95,8 +94,7 @@ void ram0_flush_cb (void) // Callback invoked when received READ10 command. // Copy disk's data to buffer (up to bufsize) and // return number of copied bytes (must be multiple of block size) -int32_t ram1_read_cb (uint32_t lba, void* buffer, uint32_t bufsize) -{ +int32_t ram1_read_cb(uint32_t lba, void* buffer, uint32_t bufsize) { uint8_t const* addr = msc_disk1[lba]; memcpy(buffer, addr, bufsize); @@ -106,8 +104,7 @@ int32_t ram1_read_cb (uint32_t lba, void* buffer, uint32_t bufsize) // Callback invoked when received WRITE10 command. // Process data in buffer to disk's storage and // return number of written bytes (must be multiple of block size) -int32_t ram1_write_cb (uint32_t lba, uint8_t* buffer, uint32_t bufsize) -{ +int32_t ram1_write_cb(uint32_t lba, uint8_t* buffer, uint32_t bufsize) { uint8_t* addr = msc_disk1[lba]; memcpy(addr, buffer, bufsize); @@ -116,7 +113,6 @@ int32_t ram1_write_cb (uint32_t lba, uint8_t* buffer, uint32_t bufsize) // Callback invoked when WRITE10 command is completed (status received and accepted by host). // used to flush any pending cache. -void ram1_flush_cb (void) -{ +void ram1_flush_cb(void) { // nothing to do } diff --git a/examples/Video/video_capture/video_capture.ino b/examples/Video/video_capture/video_capture.ino index 353489b1..6cf47587 100644 --- a/examples/Video/video_capture/video_capture.ino +++ b/examples/Video/video_capture/video_capture.ino @@ -120,6 +120,11 @@ static unsigned already_sent = 0; static void fill_color_bar(uint8_t* buffer, unsigned start_position); void setup() { + // Manual begin() is required on core without built-in support e.g. mbed rp2040 + if (!TinyUSBDevice.isInitialized()) { + TinyUSBDevice.begin(0); + } + Serial.begin(115200); usb_video.addTerminal(&desc_camera_terminal); @@ -132,6 +137,11 @@ void setup() { } void loop() { + #ifdef TINYUSB_NEED_POLLING_TASK + // Manual call tud_task since it isn't called by Core's background + TinyUSBDevice.task(); + #endif + if (!tud_video_n_streaming(0, 0)) { already_sent = 0; frame_num = 0; diff --git a/examples/WebUSB/webusb_rgb/webusb_rgb.ino b/examples/WebUSB/webusb_rgb/webusb_rgb.ino index 72bc32bb..f1849801 100644 --- a/examples/WebUSB/webusb_rgb/webusb_rgb.ino +++ b/examples/WebUSB/webusb_rgb/webusb_rgb.ino @@ -54,12 +54,11 @@ Adafruit_USBD_WebUSB usb_web; WEBUSB_URL_DEF(landingPage, 1 /*https*/, "example.tinyusb.org/webusb-rgb/index.html"); // the setup function runs once when you press reset or power the board -void setup() -{ -#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040) - // Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040 - TinyUSB_Device_Init(0); -#endif +void setup() { + // Manual begin() is required on core without built-in support e.g. mbed rp2040 + if (!TinyUSBDevice.isInitialized()) { + TinyUSBDevice.begin(0); + } //usb_web.setStringDescriptor("TinyUSB WebUSB"); usb_web.setLandingPage(&landingPage); usb_web.setLineStateCallback(line_state_callback); @@ -79,21 +78,24 @@ void setup() pixels.show(); // wait until device mounted - while( !TinyUSBDevice.mounted() ) delay(1); + while (!TinyUSBDevice.mounted()) delay(1); Serial.println("TinyUSB WebUSB RGB example"); } // convert a hex character to number -uint8_t char2num(char c) -{ +uint8_t char2num(char c) { if (c >= 'a') return c - 'a' + 10; if (c >= 'A') return c - 'A' + 10; - return c - '0'; + return c - '0'; } -void loop() -{ +void loop() { + #ifdef TINYUSB_NEED_POLLING_TASK + // Manual call tud_task since it isn't called by Core's background + TinyUSBDevice.task(); + #endif + // Landing Page 7 characters as hex color '#RRGGBB' if (usb_web.available() < 7) return; @@ -104,17 +106,16 @@ void loop() Serial.write(input, 7); Serial.println(); - uint8_t red = 16*char2num(input[1]) + char2num(input[2]); - uint8_t green = 16*char2num(input[3]) + char2num(input[4]); - uint8_t blue = 16*char2num(input[5]) + char2num(input[6]); + uint8_t red = 16 * char2num(input[1]) + char2num(input[2]); + uint8_t green = 16 * char2num(input[3]) + char2num(input[4]); + uint8_t blue = 16 * char2num(input[5]) + char2num(input[6]); uint32_t color = (red << 16) | (green << 8) | blue; pixels.fill(color); pixels.show(); } -void line_state_callback(bool connected) -{ +void line_state_callback(bool connected) { // connected = green, disconnected = red pixels.fill(connected ? 0x00ff00 : 0xff0000); pixels.show(); diff --git a/examples/WebUSB/webusb_serial/webusb_serial.ino b/examples/WebUSB/webusb_serial/webusb_serial.ino index 6dd52d28..90cb9ee7 100644 --- a/examples/WebUSB/webusb_serial/webusb_serial.ino +++ b/examples/WebUSB/webusb_serial/webusb_serial.ino @@ -37,16 +37,15 @@ WEBUSB_URL_DEF(landingPage, 1 /*https*/, "example.tinyusb.org/webusb-serial/inde int led_pin = LED_BUILTIN; // the setup function runs once when you press reset or power the board -void setup() -{ -#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040) - // Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040 - TinyUSB_Device_Init(0); -#endif +void setup() { + // Manual begin() is required on core without built-in support e.g. mbed rp2040 + if (!TinyUSBDevice.isInitialized()) { + TinyUSBDevice.begin(0); + } pinMode(led_pin, OUTPUT); digitalWrite(led_pin, LOW); - + usb_web.setLandingPage(&landingPage); usb_web.setLineStateCallback(line_state_callback); //usb_web.setStringDescriptor("TinyUSB WebUSB"); @@ -55,57 +54,53 @@ void setup() Serial.begin(115200); // wait until device mounted - while( !TinyUSBDevice.mounted() ) delay(1); + while (!TinyUSBDevice.mounted()) delay(1); Serial.println("TinyUSB WebUSB Serial example"); } // function to echo to both Serial and WebUSB -void echo_all(uint8_t buf[], uint32_t count) -{ - if (usb_web.connected()) - { +void echo_all(uint8_t buf[], uint32_t count) { + if (usb_web.connected()) { usb_web.write(buf, count); usb_web.flush(); } - if ( Serial ) - { - for(uint32_t i=0; i #include +#if defined(CH32V20x) || defined(CH32V30x) +// HACK: required for ch32 core version 1.0.4 or prior, removed when 1.0.5 is +// released +extern "C" void yield(void); +#endif + class Adafruit_USBD_Interface { protected: uint8_t _strid; From 8f7fe509d1defc83610351d3f28221e3e8fb1079 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 10 Jun 2024 16:07:10 +0700 Subject: [PATCH 04/14] - add CDC/serial_echo example using SerialTinyUSB - call CDC flush in task() for core without tinyusb builtin support --- .../.pico_rp2040_tinyusb_host.test.skip | 0 examples/CDC/serial_echo/serial_echo.ino | 40 +++++++++++++++++++ src/Adafruit_TinyUSB.h | 7 ---- src/arduino/Adafruit_TinyUSB_API.h | 7 ++++ src/arduino/Adafruit_USBD_Device.cpp | 11 ++++- 5 files changed, 57 insertions(+), 8 deletions(-) create mode 100644 examples/CDC/serial_echo/.pico_rp2040_tinyusb_host.test.skip create mode 100644 examples/CDC/serial_echo/serial_echo.ino diff --git a/examples/CDC/serial_echo/.pico_rp2040_tinyusb_host.test.skip b/examples/CDC/serial_echo/.pico_rp2040_tinyusb_host.test.skip new file mode 100644 index 00000000..e69de29b diff --git a/examples/CDC/serial_echo/serial_echo.ino b/examples/CDC/serial_echo/serial_echo.ino new file mode 100644 index 00000000..fb611c80 --- /dev/null +++ b/examples/CDC/serial_echo/serial_echo.ino @@ -0,0 +1,40 @@ +/********************************************************************* + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing + products from Adafruit! + + MIT license, check LICENSE for more information + Copyright (c) 2019 Ha Thach for Adafruit Industries + All text above, and the splash screen below must be included in + any redistribution +*********************************************************************/ + +#include "Adafruit_TinyUSB.h" + +/* This sketch demonstrates USB CDC Serial echo (convert to upper case) using SerialTinyUSB which + * is available for both core with built-in USB support and without. + */ + +void setup() { + // Manual begin() is required on core without built-in support e.g. mbed rp2040 + if (!TinyUSBDevice.isInitialized()) { + TinyUSBDevice.begin(0); + } +} + +void loop() { + #ifdef TINYUSB_NEED_POLLING_TASK + // Manual call tud_task since it isn't called by Core's background + TinyUSBDevice.task(); + #endif + + uint8_t buf[64]; + uint32_t count = 0; + while (SerialTinyUSB.available()) { + buf[count++] = (uint8_t) toupper(SerialTinyUSB.read()); + } + + if (count) { + SerialTinyUSB.write(buf, count); + } +} diff --git a/src/Adafruit_TinyUSB.h b/src/Adafruit_TinyUSB.h index 1587204d..bb836b3b 100644 --- a/src/Adafruit_TinyUSB.h +++ b/src/Adafruit_TinyUSB.h @@ -25,13 +25,6 @@ #ifndef ADAFRUIT_TINYUSB_H_ #define ADAFRUIT_TINYUSB_H_ -// Core that has built-in support: Adafruit SAMD, nRF, rp2040, esp32 -#if !(defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_NRF52_ADAFRUIT) || \ - defined(ARDUINO_ARCH_ESP32) || \ - (defined(ARDUINO_ARCH_RP2040) && !defined(ARDUINO_ARCH_MBED))) -#define TINYUSB_NEED_POLLING_TASK -#endif - // Error message for Core that must select TinyUSB via menu #if !defined(USE_TINYUSB) && \ (defined(ARDUINO_ARCH_SAMD) || \ diff --git a/src/arduino/Adafruit_TinyUSB_API.h b/src/arduino/Adafruit_TinyUSB_API.h index 14dbbfd5..92ebbd16 100644 --- a/src/arduino/Adafruit_TinyUSB_API.h +++ b/src/arduino/Adafruit_TinyUSB_API.h @@ -32,6 +32,13 @@ // TinyUSB_API, USBD_CDC, USBD_Device, USBD_Interface, #define TINYUSB_API_VERSION 30000 +// Core that has built-in support: Adafruit SAMD, Adafruit nRF, rp2040, esp32 +#if !(defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_NRF52_ADAFRUIT) || \ + defined(ARDUINO_ARCH_ESP32) || \ + (defined(ARDUINO_ARCH_RP2040) && !defined(ARDUINO_ARCH_MBED))) +#define TINYUSB_NEED_POLLING_TASK +#endif + //--------------------------------------------------------------------+ // Core API // Should be called by BSP Core to initialize, process task diff --git a/src/arduino/Adafruit_USBD_Device.cpp b/src/arduino/Adafruit_USBD_Device.cpp index 68dbbe3d..068a9890 100644 --- a/src/arduino/Adafruit_USBD_Device.cpp +++ b/src/arduino/Adafruit_USBD_Device.cpp @@ -150,7 +150,16 @@ uint8_t Adafruit_USBD_Device::addStringDescriptor(const char *s) { return index; } -void Adafruit_USBD_Device::task(void) { tud_task(); } +void Adafruit_USBD_Device::task(void) { + tud_task(); + +#ifdef TINYUSB_NEED_POLLING_TASK + // can also be used with port with built-in support + if (SerialTinyUSB) { + SerialTinyUSB.flush(); + } +#endif +} bool Adafruit_USBD_Device::mounted(void) { return tud_mounted(); } From 6d6cd1788b5178a4ac8e964d13137782bf4de290 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 10 Jun 2024 16:22:38 +0700 Subject: [PATCH 05/14] use env for ARDUINO_LIBS, try to build ch32 --- .github/workflows/githubci.yml | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/.github/workflows/githubci.yml b/.github/workflows/githubci.yml index 2ec7a516..b23ce12f 100644 --- a/.github/workflows/githubci.yml +++ b/.github/workflows/githubci.yml @@ -6,6 +6,18 @@ concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true +env: + ARDUINO_LIBS: + - 'Adafruit SPIFlash' + - 'MIDI Library' + - 'Adafruit seesaw Library' + - 'Adafruit NeoPixel' + - 'SdFat - Adafruit Fork' + - 'SD' + - 'Adafruit Circuit Playground' + - 'Adafruit InternalFlash' + - 'Pico PIO USB' + jobs: pre-commit: runs-on: ubuntu-latest @@ -34,6 +46,9 @@ jobs: PRETTYNAME : "Adafruit TinyUSB Library" run: bash ci/doxy_gen_and_deploy.sh + # --------------------------------------- + # Main + # --------------------------------------- build: runs-on: ubuntu-latest needs: pre-commit @@ -53,7 +68,8 @@ jobs: # SAMD - 'metro_m0_tinyusb' - 'metro_m4_tinyusb' - + # Ch32v2 + - 'CH32V20x_EVT' steps: - name: Checkout code uses: actions/checkout@v4 @@ -62,17 +78,28 @@ jobs: uses: actions/checkout@v4 with: repository: adafruit/ci-arduino + ref: add-ch32v2 path: ci - name: pre-install run: bash ci/actions_install.sh - - name: Install Libraries for building examples - run: arduino-cli lib install "Adafruit SPIFlash" "MIDI Library" "Adafruit seesaw Library" "Adafruit NeoPixel" "SdFat - Adafruit Fork" "SD" "Adafruit Circuit Playground" "Adafruit InternalFlash" "Pico PIO USB" + - name: Install Libraries + run: | + LIBS_STRING="" + for lib in "${{ env.ARDUINO_LIBS[@] }}"; do + LIBS_STRING+="\"$lib\" " + done + echo "$LIBS_STRING" + # arduino-cli lib install "Adafruit SPIFlash" "MIDI Library" "Adafruit seesaw Library" "Adafruit NeoPixel" "SdFat - Adafruit Fork" "SD" "Adafruit Circuit Playground" "Adafruit InternalFlash" "Pico PIO USB" + arduino-cli lib install $LIBS_STRING - name: test platforms run: python3 ci/build_platform.py ${{ matrix.arduino-platform }} + # --------------------------------------- + # Build ESP32 v2 + # --------------------------------------- build-esp32-v2: if: false runs-on: ubuntu-latest From 75fd7542126e4e1ebb0f1182240f7d1efe8cee49 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 10 Jun 2024 16:38:31 +0700 Subject: [PATCH 06/14] use arduino lib as escape string --- .github/workflows/githubci.yml | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/.github/workflows/githubci.yml b/.github/workflows/githubci.yml index b23ce12f..f029fae8 100644 --- a/.github/workflows/githubci.yml +++ b/.github/workflows/githubci.yml @@ -7,16 +7,7 @@ concurrency: cancel-in-progress: true env: - ARDUINO_LIBS: - - 'Adafruit SPIFlash' - - 'MIDI Library' - - 'Adafruit seesaw Library' - - 'Adafruit NeoPixel' - - 'SdFat - Adafruit Fork' - - 'SD' - - 'Adafruit Circuit Playground' - - 'Adafruit InternalFlash' - - 'Pico PIO USB' + ARDUINO_LIBS: "\"Adafruit SPIFlash\" \"MIDI Library\" \"Adafruit seesaw Library\" \"Adafruit NeoPixel\" \"SdFat - Adafruit Fork\" \"SD\" \"Adafruit Circuit Playground\" \"Adafruit InternalFlash\" \"Pico PIO USB\"" jobs: pre-commit: @@ -86,13 +77,9 @@ jobs: - name: Install Libraries run: | - LIBS_STRING="" - for lib in "${{ env.ARDUINO_LIBS[@] }}"; do - LIBS_STRING+="\"$lib\" " - done - echo "$LIBS_STRING" + echo ${{ env.ARDUINO_LIBS }} # arduino-cli lib install "Adafruit SPIFlash" "MIDI Library" "Adafruit seesaw Library" "Adafruit NeoPixel" "SdFat - Adafruit Fork" "SD" "Adafruit Circuit Playground" "Adafruit InternalFlash" "Pico PIO USB" - arduino-cli lib install $LIBS_STRING + arduino-cli lib install ${{ env.ARDUINO_LIBS }} - name: test platforms run: python3 ci/build_platform.py ${{ matrix.arduino-platform }} From ee005588d0df56d25cacc3b144ad32c71c279293 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 10 Jun 2024 17:52:48 +0700 Subject: [PATCH 07/14] define SerialTinyUSB Serial for esp32 --- .github/workflows/githubci.yml | 9 ++++----- src/arduino/Adafruit_USBD_CDC.h | 1 + tools/build_all.py | 5 ++++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/githubci.yml b/.github/workflows/githubci.yml index f029fae8..cea09c7c 100644 --- a/.github/workflows/githubci.yml +++ b/.github/workflows/githubci.yml @@ -3,11 +3,11 @@ name: Build on: [pull_request, push, repository_dispatch] concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true env: - ARDUINO_LIBS: "\"Adafruit SPIFlash\" \"MIDI Library\" \"Adafruit seesaw Library\" \"Adafruit NeoPixel\" \"SdFat - Adafruit Fork\" \"SD\" \"Adafruit Circuit Playground\" \"Adafruit InternalFlash\" \"Pico PIO USB\"" + ARDUINO_LIBS: "\"Adafruit SPIFlash\" \"Adafruit seesaw Library\" \"Adafruit NeoPixel\" \"Adafruit Circuit Playground\" \"Adafruit InternalFlash\" \"SdFat - Adafruit Fork\" \"SD\" \"MIDI Library\" \"Pico PIO USB\"" jobs: pre-commit: @@ -77,9 +77,8 @@ jobs: - name: Install Libraries run: | - echo ${{ env.ARDUINO_LIBS }} - # arduino-cli lib install "Adafruit SPIFlash" "MIDI Library" "Adafruit seesaw Library" "Adafruit NeoPixel" "SdFat - Adafruit Fork" "SD" "Adafruit Circuit Playground" "Adafruit InternalFlash" "Pico PIO USB" arduino-cli lib install ${{ env.ARDUINO_LIBS }} + arduino-cli lib list - name: test platforms run: python3 ci/build_platform.py ${{ matrix.arduino-platform }} @@ -118,7 +117,7 @@ jobs: BSP_URLS: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json run: | arduino-cli core install esp32:esp32@${{ matrix.esp32-version }} --additional-urls $BSP_URLS - arduino-cli lib install "Adafruit SPIFlash" "MIDI Library" "Adafruit seesaw Library" "Adafruit NeoPixel" "SdFat - Adafruit Fork" "SD" "Adafruit Circuit Playground" "Adafruit InternalFlash" "Pico PIO USB" + arduino-cli lib install ${{ env.ARDUINO_LIBS }} arduino-cli core list arduino-cli lib list diff --git a/src/arduino/Adafruit_USBD_CDC.h b/src/arduino/Adafruit_USBD_CDC.h index 877661d5..d713d2c7 100644 --- a/src/arduino/Adafruit_USBD_CDC.h +++ b/src/arduino/Adafruit_USBD_CDC.h @@ -33,6 +33,7 @@ // For ESP32 use USBCDC as it is compatible #define Adafruit_USBD_CDC USBCDC +#define SerialTinyUSB Serial #else diff --git a/tools/build_all.py b/tools/build_all.py index 2248232e..e51dfda5 100644 --- a/tools/build_all.py +++ b/tools/build_all.py @@ -22,8 +22,10 @@ ['feather_rp2040_tinyusb', 'rp2040:rp2040:adafruit_feather:flash=8388608_0,freq=125,dbgport=Disabled,dbglvl=None,usbstack=tinyusb'], ['metroesp32s2', 'espressif:esp32:adafruit_metro_esp32s2:CDCOnBoot=cdc,MSCOnBoot=default,DFUOnBoot=default,UploadMode=cdc,PSRAM=enabled,PartitionScheme=tinyuf2'], #[' ', 'espressif:esp32:adafruit_feather_esp32s3:FlashMode=qio,LoopCore=1,EventsCore=1,USBMode=default,CDCOnBoot=cdc,MSCOnBoot=default,DFUOnBoot=default,UploadMode=cdc,PartitionScheme=tinyuf2'], + ['ch32v20x', 'WCH:ch32v:CH32V20x_EVT'] ] + # return [succeeded, failed, skipped] def build_sketch(variant, sketch): ret = [0, 0, 0] @@ -91,7 +93,8 @@ def build_variant(variant): else: build_boards = all_boards - all_examples = list(glob.iglob('examples/**/*.ino', recursive=True)) + #all_examples = list(glob.iglob('examples/**/*.ino', recursive=True)) + all_examples = [f for f in glob.iglob('examples/**/*.ino', recursive=True) if 'examples/DualRole' not in f] all_examples.sort() total_time = time.monotonic() From 4d5e4231e227d3b97623d1735acbc2a3ec9a6090 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 10 Jun 2024 23:52:59 +0700 Subject: [PATCH 08/14] fix ci --- .github/workflows/githubci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/githubci.yml b/.github/workflows/githubci.yml index cea09c7c..019f5652 100644 --- a/.github/workflows/githubci.yml +++ b/.github/workflows/githubci.yml @@ -69,7 +69,6 @@ jobs: uses: actions/checkout@v4 with: repository: adafruit/ci-arduino - ref: add-ch32v2 path: ci - name: pre-install From f2286b88a8e778616104ee5ae5d67a439a41f672 Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 11 Jun 2024 12:33:29 +0700 Subject: [PATCH 09/14] test skip.txt --- .github/workflows/githubci.yml | 1 + .../CDC/serial_host_bridge/.pico_rp2040_tinyusb_host.test.skip | 0 examples/DualRole/CDC/serial_host_bridge/.skip.txt | 2 ++ 3 files changed, 3 insertions(+) delete mode 100644 examples/DualRole/CDC/serial_host_bridge/.pico_rp2040_tinyusb_host.test.skip create mode 100644 examples/DualRole/CDC/serial_host_bridge/.skip.txt diff --git a/.github/workflows/githubci.yml b/.github/workflows/githubci.yml index 019f5652..6bfd0e9d 100644 --- a/.github/workflows/githubci.yml +++ b/.github/workflows/githubci.yml @@ -106,6 +106,7 @@ jobs: uses: actions/checkout@v4 with: repository: adafruit/ci-arduino + ref: skip-list-file path: ci - name: pre-install diff --git a/examples/DualRole/CDC/serial_host_bridge/.pico_rp2040_tinyusb_host.test.skip b/examples/DualRole/CDC/serial_host_bridge/.pico_rp2040_tinyusb_host.test.skip deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/DualRole/CDC/serial_host_bridge/.skip.txt b/examples/DualRole/CDC/serial_host_bridge/.skip.txt new file mode 100644 index 00000000..f8e761cb --- /dev/null +++ b/examples/DualRole/CDC/serial_host_bridge/.skip.txt @@ -0,0 +1,2 @@ +pico_rp2040_tinyusb_host +CH32V20x_EVT From 1c1a65c834a13a6043433a4e6ff9e930bb5214fc Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 11 Jun 2024 12:50:23 +0700 Subject: [PATCH 10/14] add more .skip.txt --- .github/workflows/githubci.yml | 2 +- .../HID/hid_device_report/.pico_rp2040_tinyusb_host.test.skip | 0 examples/DualRole/HID/hid_device_report/.skip.txt | 2 ++ .../hid_mouse_log_filter/.pico_rp2040_tinyusb_host.test.skip | 0 examples/DualRole/HID/hid_mouse_log_filter/.skip.txt | 2 ++ .../hid_mouse_tremor_filter/.pico_rp2040_tinyusb_host.test.skip | 0 examples/DualRole/HID/hid_mouse_tremor_filter/.skip.txt | 2 ++ .../HID/hid_remapper/.pico_rp2040_tinyusb_host.test.skip | 0 examples/DualRole/HID/hid_remapper/.skip.txt | 2 ++ 9 files changed, 9 insertions(+), 1 deletion(-) delete mode 100644 examples/DualRole/HID/hid_device_report/.pico_rp2040_tinyusb_host.test.skip create mode 100644 examples/DualRole/HID/hid_device_report/.skip.txt delete mode 100644 examples/DualRole/HID/hid_mouse_log_filter/.pico_rp2040_tinyusb_host.test.skip create mode 100644 examples/DualRole/HID/hid_mouse_log_filter/.skip.txt delete mode 100644 examples/DualRole/HID/hid_mouse_tremor_filter/.pico_rp2040_tinyusb_host.test.skip create mode 100644 examples/DualRole/HID/hid_mouse_tremor_filter/.skip.txt delete mode 100644 examples/DualRole/HID/hid_remapper/.pico_rp2040_tinyusb_host.test.skip create mode 100644 examples/DualRole/HID/hid_remapper/.skip.txt diff --git a/.github/workflows/githubci.yml b/.github/workflows/githubci.yml index 6bfd0e9d..20993d4a 100644 --- a/.github/workflows/githubci.yml +++ b/.github/workflows/githubci.yml @@ -69,6 +69,7 @@ jobs: uses: actions/checkout@v4 with: repository: adafruit/ci-arduino + ref: skip-list-file path: ci - name: pre-install @@ -106,7 +107,6 @@ jobs: uses: actions/checkout@v4 with: repository: adafruit/ci-arduino - ref: skip-list-file path: ci - name: pre-install diff --git a/examples/DualRole/HID/hid_device_report/.pico_rp2040_tinyusb_host.test.skip b/examples/DualRole/HID/hid_device_report/.pico_rp2040_tinyusb_host.test.skip deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/DualRole/HID/hid_device_report/.skip.txt b/examples/DualRole/HID/hid_device_report/.skip.txt new file mode 100644 index 00000000..f8e761cb --- /dev/null +++ b/examples/DualRole/HID/hid_device_report/.skip.txt @@ -0,0 +1,2 @@ +pico_rp2040_tinyusb_host +CH32V20x_EVT diff --git a/examples/DualRole/HID/hid_mouse_log_filter/.pico_rp2040_tinyusb_host.test.skip b/examples/DualRole/HID/hid_mouse_log_filter/.pico_rp2040_tinyusb_host.test.skip deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/DualRole/HID/hid_mouse_log_filter/.skip.txt b/examples/DualRole/HID/hid_mouse_log_filter/.skip.txt new file mode 100644 index 00000000..f8e761cb --- /dev/null +++ b/examples/DualRole/HID/hid_mouse_log_filter/.skip.txt @@ -0,0 +1,2 @@ +pico_rp2040_tinyusb_host +CH32V20x_EVT diff --git a/examples/DualRole/HID/hid_mouse_tremor_filter/.pico_rp2040_tinyusb_host.test.skip b/examples/DualRole/HID/hid_mouse_tremor_filter/.pico_rp2040_tinyusb_host.test.skip deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/DualRole/HID/hid_mouse_tremor_filter/.skip.txt b/examples/DualRole/HID/hid_mouse_tremor_filter/.skip.txt new file mode 100644 index 00000000..f8e761cb --- /dev/null +++ b/examples/DualRole/HID/hid_mouse_tremor_filter/.skip.txt @@ -0,0 +1,2 @@ +pico_rp2040_tinyusb_host +CH32V20x_EVT diff --git a/examples/DualRole/HID/hid_remapper/.pico_rp2040_tinyusb_host.test.skip b/examples/DualRole/HID/hid_remapper/.pico_rp2040_tinyusb_host.test.skip deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/DualRole/HID/hid_remapper/.skip.txt b/examples/DualRole/HID/hid_remapper/.skip.txt new file mode 100644 index 00000000..f8e761cb --- /dev/null +++ b/examples/DualRole/HID/hid_remapper/.skip.txt @@ -0,0 +1,2 @@ +pico_rp2040_tinyusb_host +CH32V20x_EVT From 6f0f7e6a05839243a18d2483cfd82a21ac40f0fa Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 11 Jun 2024 13:08:38 +0700 Subject: [PATCH 11/14] skip non-compilable for ch32v2 --- .../msc_data_logger/.pico_rp2040_tinyusb_host.test.skip | 0 examples/DualRole/MassStorage/msc_data_logger/.skip.txt | 2 ++ .../msc_file_explorer/.pico_rp2040_tinyusb_host.test.skip | 0 examples/DualRole/MassStorage/msc_file_explorer/.skip.txt | 2 ++ examples/DualRole/Simple/device_info/.skip.txt | 1 + .../.pico_rp2040_tinyusb_host.test.skip | 0 examples/DualRole/Simple/device_info_max3421e/.skip.txt | 2 ++ .../msc_external_flash/.feather52833.test.skip | 0 .../.pico_rp2040_tinyusb_host.test.skip | 0 examples/MassStorage/msc_external_flash/.skip.txt | 2 ++ .../msc_external_flash_sdcard/.feather52833.test.skip | 0 .../.pico_rp2040_tinyusb_host.test.skip | 0 examples/MassStorage/msc_external_flash_sdcard/.skip.txt | 2 ++ .../msc_ramdisk/.pico_rp2040_tinyusb_host.test.skip | 0 examples/MassStorage/msc_ramdisk/.skip.txt | 1 + .../msc_ramdisk_dual/.pico_rp2040_tinyusb_host.test.skip | 0 examples/MassStorage/msc_ramdisk_dual/.skip.txt | 2 ++ examples/MassStorage/msc_sd/.feather_esp32s2.test.skip | 0 examples/MassStorage/msc_sd/.feather_esp32s3.test.skip | 0 .../MassStorage/msc_sd/.feather_rp2040_tinyusb.test.skip | 0 examples/MassStorage/msc_sd/.funhouse.test.skip | 0 examples/MassStorage/msc_sd/.magtag.test.skip | 0 examples/MassStorage/msc_sd/.metroesp32s2.test.skip | 0 .../msc_sd/.pico_rp2040_tinyusb_host.test.skip | 0 examples/MassStorage/msc_sd/.skip.txt | 8 ++++++++ .../msc_sdfat/.pico_rp2040_tinyusb_host.test.skip | 0 examples/MassStorage/msc_sdfat/.skip.txt | 1 + .../.pico_rp2040_tinyusb_host.test.skip | 0 examples/Vendor/i2c_tiny_usb_adapter/.skip.txt | 2 ++ .../video_capture/.pico_rp2040_tinyusb_host.test.skip | 0 examples/Video/video_capture/.skip.txt | 2 ++ .../WebUSB/webusb_rgb/.pico_rp2040_tinyusb_host.test.skip | 0 examples/WebUSB/webusb_rgb/.skip.txt | 3 +++ .../webusb_serial/.pico_rp2040_tinyusb_host.test.skip | 0 examples/WebUSB/webusb_serial/.skip.txt | 3 +++ 35 files changed, 33 insertions(+) delete mode 100644 examples/DualRole/MassStorage/msc_data_logger/.pico_rp2040_tinyusb_host.test.skip create mode 100644 examples/DualRole/MassStorage/msc_data_logger/.skip.txt delete mode 100644 examples/DualRole/MassStorage/msc_file_explorer/.pico_rp2040_tinyusb_host.test.skip create mode 100644 examples/DualRole/MassStorage/msc_file_explorer/.skip.txt create mode 100644 examples/DualRole/Simple/device_info/.skip.txt delete mode 100644 examples/DualRole/Simple/device_info_max3421e/.pico_rp2040_tinyusb_host.test.skip create mode 100644 examples/DualRole/Simple/device_info_max3421e/.skip.txt delete mode 100644 examples/MassStorage/msc_external_flash/.feather52833.test.skip delete mode 100644 examples/MassStorage/msc_external_flash/.pico_rp2040_tinyusb_host.test.skip create mode 100644 examples/MassStorage/msc_external_flash/.skip.txt delete mode 100644 examples/MassStorage/msc_external_flash_sdcard/.feather52833.test.skip delete mode 100644 examples/MassStorage/msc_external_flash_sdcard/.pico_rp2040_tinyusb_host.test.skip create mode 100644 examples/MassStorage/msc_external_flash_sdcard/.skip.txt delete mode 100644 examples/MassStorage/msc_ramdisk/.pico_rp2040_tinyusb_host.test.skip create mode 100644 examples/MassStorage/msc_ramdisk/.skip.txt delete mode 100644 examples/MassStorage/msc_ramdisk_dual/.pico_rp2040_tinyusb_host.test.skip create mode 100644 examples/MassStorage/msc_ramdisk_dual/.skip.txt delete mode 100644 examples/MassStorage/msc_sd/.feather_esp32s2.test.skip delete mode 100644 examples/MassStorage/msc_sd/.feather_esp32s3.test.skip delete mode 100644 examples/MassStorage/msc_sd/.feather_rp2040_tinyusb.test.skip delete mode 100644 examples/MassStorage/msc_sd/.funhouse.test.skip delete mode 100644 examples/MassStorage/msc_sd/.magtag.test.skip delete mode 100644 examples/MassStorage/msc_sd/.metroesp32s2.test.skip delete mode 100644 examples/MassStorage/msc_sd/.pico_rp2040_tinyusb_host.test.skip create mode 100644 examples/MassStorage/msc_sd/.skip.txt delete mode 100644 examples/MassStorage/msc_sdfat/.pico_rp2040_tinyusb_host.test.skip create mode 100644 examples/MassStorage/msc_sdfat/.skip.txt delete mode 100644 examples/Vendor/i2c_tiny_usb_adapter/.pico_rp2040_tinyusb_host.test.skip create mode 100644 examples/Vendor/i2c_tiny_usb_adapter/.skip.txt delete mode 100644 examples/Video/video_capture/.pico_rp2040_tinyusb_host.test.skip create mode 100644 examples/Video/video_capture/.skip.txt delete mode 100644 examples/WebUSB/webusb_rgb/.pico_rp2040_tinyusb_host.test.skip create mode 100644 examples/WebUSB/webusb_rgb/.skip.txt delete mode 100644 examples/WebUSB/webusb_serial/.pico_rp2040_tinyusb_host.test.skip create mode 100644 examples/WebUSB/webusb_serial/.skip.txt diff --git a/examples/DualRole/MassStorage/msc_data_logger/.pico_rp2040_tinyusb_host.test.skip b/examples/DualRole/MassStorage/msc_data_logger/.pico_rp2040_tinyusb_host.test.skip deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/DualRole/MassStorage/msc_data_logger/.skip.txt b/examples/DualRole/MassStorage/msc_data_logger/.skip.txt new file mode 100644 index 00000000..f8e761cb --- /dev/null +++ b/examples/DualRole/MassStorage/msc_data_logger/.skip.txt @@ -0,0 +1,2 @@ +pico_rp2040_tinyusb_host +CH32V20x_EVT diff --git a/examples/DualRole/MassStorage/msc_file_explorer/.pico_rp2040_tinyusb_host.test.skip b/examples/DualRole/MassStorage/msc_file_explorer/.pico_rp2040_tinyusb_host.test.skip deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/DualRole/MassStorage/msc_file_explorer/.skip.txt b/examples/DualRole/MassStorage/msc_file_explorer/.skip.txt new file mode 100644 index 00000000..f8e761cb --- /dev/null +++ b/examples/DualRole/MassStorage/msc_file_explorer/.skip.txt @@ -0,0 +1,2 @@ +pico_rp2040_tinyusb_host +CH32V20x_EVT diff --git a/examples/DualRole/Simple/device_info/.skip.txt b/examples/DualRole/Simple/device_info/.skip.txt new file mode 100644 index 00000000..98d71f56 --- /dev/null +++ b/examples/DualRole/Simple/device_info/.skip.txt @@ -0,0 +1 @@ +CH32V20x_EVT diff --git a/examples/DualRole/Simple/device_info_max3421e/.pico_rp2040_tinyusb_host.test.skip b/examples/DualRole/Simple/device_info_max3421e/.pico_rp2040_tinyusb_host.test.skip deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/DualRole/Simple/device_info_max3421e/.skip.txt b/examples/DualRole/Simple/device_info_max3421e/.skip.txt new file mode 100644 index 00000000..f8e761cb --- /dev/null +++ b/examples/DualRole/Simple/device_info_max3421e/.skip.txt @@ -0,0 +1,2 @@ +pico_rp2040_tinyusb_host +CH32V20x_EVT diff --git a/examples/MassStorage/msc_external_flash/.feather52833.test.skip b/examples/MassStorage/msc_external_flash/.feather52833.test.skip deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/MassStorage/msc_external_flash/.pico_rp2040_tinyusb_host.test.skip b/examples/MassStorage/msc_external_flash/.pico_rp2040_tinyusb_host.test.skip deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/MassStorage/msc_external_flash/.skip.txt b/examples/MassStorage/msc_external_flash/.skip.txt new file mode 100644 index 00000000..f8e761cb --- /dev/null +++ b/examples/MassStorage/msc_external_flash/.skip.txt @@ -0,0 +1,2 @@ +pico_rp2040_tinyusb_host +CH32V20x_EVT diff --git a/examples/MassStorage/msc_external_flash_sdcard/.feather52833.test.skip b/examples/MassStorage/msc_external_flash_sdcard/.feather52833.test.skip deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/MassStorage/msc_external_flash_sdcard/.pico_rp2040_tinyusb_host.test.skip b/examples/MassStorage/msc_external_flash_sdcard/.pico_rp2040_tinyusb_host.test.skip deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/MassStorage/msc_external_flash_sdcard/.skip.txt b/examples/MassStorage/msc_external_flash_sdcard/.skip.txt new file mode 100644 index 00000000..f8e761cb --- /dev/null +++ b/examples/MassStorage/msc_external_flash_sdcard/.skip.txt @@ -0,0 +1,2 @@ +pico_rp2040_tinyusb_host +CH32V20x_EVT diff --git a/examples/MassStorage/msc_ramdisk/.pico_rp2040_tinyusb_host.test.skip b/examples/MassStorage/msc_ramdisk/.pico_rp2040_tinyusb_host.test.skip deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/MassStorage/msc_ramdisk/.skip.txt b/examples/MassStorage/msc_ramdisk/.skip.txt new file mode 100644 index 00000000..a0185409 --- /dev/null +++ b/examples/MassStorage/msc_ramdisk/.skip.txt @@ -0,0 +1 @@ +pico_rp2040_tinyusb_host diff --git a/examples/MassStorage/msc_ramdisk_dual/.pico_rp2040_tinyusb_host.test.skip b/examples/MassStorage/msc_ramdisk_dual/.pico_rp2040_tinyusb_host.test.skip deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/MassStorage/msc_ramdisk_dual/.skip.txt b/examples/MassStorage/msc_ramdisk_dual/.skip.txt new file mode 100644 index 00000000..f8e761cb --- /dev/null +++ b/examples/MassStorage/msc_ramdisk_dual/.skip.txt @@ -0,0 +1,2 @@ +pico_rp2040_tinyusb_host +CH32V20x_EVT diff --git a/examples/MassStorage/msc_sd/.feather_esp32s2.test.skip b/examples/MassStorage/msc_sd/.feather_esp32s2.test.skip deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/MassStorage/msc_sd/.feather_esp32s3.test.skip b/examples/MassStorage/msc_sd/.feather_esp32s3.test.skip deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/MassStorage/msc_sd/.feather_rp2040_tinyusb.test.skip b/examples/MassStorage/msc_sd/.feather_rp2040_tinyusb.test.skip deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/MassStorage/msc_sd/.funhouse.test.skip b/examples/MassStorage/msc_sd/.funhouse.test.skip deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/MassStorage/msc_sd/.magtag.test.skip b/examples/MassStorage/msc_sd/.magtag.test.skip deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/MassStorage/msc_sd/.metroesp32s2.test.skip b/examples/MassStorage/msc_sd/.metroesp32s2.test.skip deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/MassStorage/msc_sd/.pico_rp2040_tinyusb_host.test.skip b/examples/MassStorage/msc_sd/.pico_rp2040_tinyusb_host.test.skip deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/MassStorage/msc_sd/.skip.txt b/examples/MassStorage/msc_sd/.skip.txt new file mode 100644 index 00000000..b5c66f7f --- /dev/null +++ b/examples/MassStorage/msc_sd/.skip.txt @@ -0,0 +1,8 @@ +feather_esp32s2 +feather_esp32s3 +funhouse +magtag +metroesp32s2 +feather_rp2040_tinyusb +pico_rp2040_tinyusb_host +CH32V20x_EVT diff --git a/examples/MassStorage/msc_sdfat/.pico_rp2040_tinyusb_host.test.skip b/examples/MassStorage/msc_sdfat/.pico_rp2040_tinyusb_host.test.skip deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/MassStorage/msc_sdfat/.skip.txt b/examples/MassStorage/msc_sdfat/.skip.txt new file mode 100644 index 00000000..a0185409 --- /dev/null +++ b/examples/MassStorage/msc_sdfat/.skip.txt @@ -0,0 +1 @@ +pico_rp2040_tinyusb_host diff --git a/examples/Vendor/i2c_tiny_usb_adapter/.pico_rp2040_tinyusb_host.test.skip b/examples/Vendor/i2c_tiny_usb_adapter/.pico_rp2040_tinyusb_host.test.skip deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/Vendor/i2c_tiny_usb_adapter/.skip.txt b/examples/Vendor/i2c_tiny_usb_adapter/.skip.txt new file mode 100644 index 00000000..f8e761cb --- /dev/null +++ b/examples/Vendor/i2c_tiny_usb_adapter/.skip.txt @@ -0,0 +1,2 @@ +pico_rp2040_tinyusb_host +CH32V20x_EVT diff --git a/examples/Video/video_capture/.pico_rp2040_tinyusb_host.test.skip b/examples/Video/video_capture/.pico_rp2040_tinyusb_host.test.skip deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/Video/video_capture/.skip.txt b/examples/Video/video_capture/.skip.txt new file mode 100644 index 00000000..f8e761cb --- /dev/null +++ b/examples/Video/video_capture/.skip.txt @@ -0,0 +1,2 @@ +pico_rp2040_tinyusb_host +CH32V20x_EVT diff --git a/examples/WebUSB/webusb_rgb/.pico_rp2040_tinyusb_host.test.skip b/examples/WebUSB/webusb_rgb/.pico_rp2040_tinyusb_host.test.skip deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/WebUSB/webusb_rgb/.skip.txt b/examples/WebUSB/webusb_rgb/.skip.txt new file mode 100644 index 00000000..32e60fa6 --- /dev/null +++ b/examples/WebUSB/webusb_rgb/.skip.txt @@ -0,0 +1,3 @@ +pico_rp2040_tinyusb_host +CH32V20x_EVT +# CH32V20x_EVT is not supported due to Adafruit_Neopixel \ No newline at end of file diff --git a/examples/WebUSB/webusb_serial/.pico_rp2040_tinyusb_host.test.skip b/examples/WebUSB/webusb_serial/.pico_rp2040_tinyusb_host.test.skip deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/WebUSB/webusb_serial/.skip.txt b/examples/WebUSB/webusb_serial/.skip.txt new file mode 100644 index 00000000..2842ebbb --- /dev/null +++ b/examples/WebUSB/webusb_serial/.skip.txt @@ -0,0 +1,3 @@ +pico_rp2040_tinyusb_host +CH32V20x_EVT +# CH32V20x_EVT is not supported due to lacking of HardwareSerial::read(uint8_t [64], int) \ No newline at end of file From e7941194dc801ba0564c98ae749045461a62db1d Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 11 Jun 2024 13:14:33 +0700 Subject: [PATCH 12/14] skip more --- .../.pico_rp2040_tinyusb_host.test.skip | 0 examples/HID/hid_composite_joy_featherwing/.skip.txt | 2 ++ 2 files changed, 2 insertions(+) delete mode 100644 examples/HID/hid_composite_joy_featherwing/.pico_rp2040_tinyusb_host.test.skip create mode 100644 examples/HID/hid_composite_joy_featherwing/.skip.txt diff --git a/examples/HID/hid_composite_joy_featherwing/.pico_rp2040_tinyusb_host.test.skip b/examples/HID/hid_composite_joy_featherwing/.pico_rp2040_tinyusb_host.test.skip deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/HID/hid_composite_joy_featherwing/.skip.txt b/examples/HID/hid_composite_joy_featherwing/.skip.txt new file mode 100644 index 00000000..f8e761cb --- /dev/null +++ b/examples/HID/hid_composite_joy_featherwing/.skip.txt @@ -0,0 +1,2 @@ +pico_rp2040_tinyusb_host +CH32V20x_EVT From efa33be67c40543baee0f8ba313bb104dac382ce Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 11 Jun 2024 14:17:02 +0700 Subject: [PATCH 13/14] clean up --- .github/workflows/githubci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/githubci.yml b/.github/workflows/githubci.yml index 20993d4a..019f5652 100644 --- a/.github/workflows/githubci.yml +++ b/.github/workflows/githubci.yml @@ -69,7 +69,6 @@ jobs: uses: actions/checkout@v4 with: repository: adafruit/ci-arduino - ref: skip-list-file path: ci - name: pre-install From 7ca12e368821e2138580f8a80f1ca8c82363e6b4 Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 11 Jun 2024 16:10:19 +0700 Subject: [PATCH 14/14] update build_all.py --- tools/build_all.py | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/tools/build_all.py b/tools/build_all.py index e51dfda5..1d19ea92 100644 --- a/tools/build_all.py +++ b/tools/build_all.py @@ -19,32 +19,45 @@ ['metro_m0_tinyusb', 'adafruit:samd:adafruit_metro_m0:usbstack=tinyusb'], ['metro_m4_tinyusb', 'adafruit:samd:adafruit_metro_m4:speed=120,usbstack=tinyusb'], ['nrf52840', 'adafruit:nrf52:feather52840'], - ['feather_rp2040_tinyusb', 'rp2040:rp2040:adafruit_feather:flash=8388608_0,freq=125,dbgport=Disabled,dbglvl=None,usbstack=tinyusb'], - ['metroesp32s2', 'espressif:esp32:adafruit_metro_esp32s2:CDCOnBoot=cdc,MSCOnBoot=default,DFUOnBoot=default,UploadMode=cdc,PSRAM=enabled,PartitionScheme=tinyuf2'], - #[' ', 'espressif:esp32:adafruit_feather_esp32s3:FlashMode=qio,LoopCore=1,EventsCore=1,USBMode=default,CDCOnBoot=cdc,MSCOnBoot=default,DFUOnBoot=default,UploadMode=cdc,PartitionScheme=tinyuf2'], - ['ch32v20x', 'WCH:ch32v:CH32V20x_EVT'] + ['feather_rp2040_tinyusb', + 'rp2040:rp2040:adafruit_feather:flash=8388608_0,freq=125,dbgport=Disabled,dbglvl=None,usbstack=tinyusb'], + ['metroesp32s2', + 'espressif:esp32:adafruit_metro_esp32s2:CDCOnBoot=cdc,MSCOnBoot=default,DFUOnBoot=default,UploadMode=cdc,PSRAM=enabled,PartitionScheme=tinyuf2'], + # [' ', 'espressif:esp32:adafruit_feather_esp32s3:FlashMode=qio,LoopCore=1,EventsCore=1,USBMode=default,CDCOnBoot=cdc,MSCOnBoot=default,DFUOnBoot=default,UploadMode=cdc,PartitionScheme=tinyuf2'], + ['CH32V20x_EVT', 'WCH:ch32v:CH32V20x_EVT'] ] # return [succeeded, failed, skipped] def build_sketch(variant, sketch): ret = [0, 0, 0] - name = variant[0] fqbn = variant[1] - start_time = time.monotonic() - # Skip if contains: ".board.test.skip" or ".all.test.skip" - # Skip if not contains: ".board.test.only" for a specific board + + # Skip if + # - contains ".board.test.skip" + # - contains any of ".board.test.only" but not this one + # - conntain .skip.txt has this board in it sketchdir = os.path.dirname(sketch) - if os.path.exists(sketchdir + '/.all.test.skip') or os.path.exists(sketchdir + '/.' + name + '.test.skip') or \ + if os.path.exists(sketchdir + '/.' + name + '.test.skip') or \ (glob.glob(sketchdir + "/.*.test.only") and not os.path.exists(sketchdir + '/.' + name + '.test.only')): - success = SKIPPED ret[2] = 1 + + skip_txt = f"{sketchdir}/.skip.txt" + if os.path.exists(skip_txt): + with open(skip_txt) as f: + lines = f.readlines() + for line in lines: + if line.strip() == name: + ret[2] = 1 + break + + if ret[2] == 1: + success = SKIPPED else: build_result = subprocess.run("arduino-cli compile --warnings all --fqbn {} {}".format(fqbn, sketch), shell=True, stdout=PIPE, stderr=PIPE) - # get stderr into a form where warning/error was output to stderr if build_result.returncode != 0: success = FAILED @@ -93,8 +106,7 @@ def build_variant(variant): else: build_boards = all_boards - #all_examples = list(glob.iglob('examples/**/*.ino', recursive=True)) - all_examples = [f for f in glob.iglob('examples/**/*.ino', recursive=True) if 'examples/DualRole' not in f] + all_examples = list(glob.iglob('examples/**/*.ino', recursive=True)) all_examples.sort() total_time = time.monotonic()