فهرست منبع

Merge branch 'master' into group-boards-into-family

hathach 5 سال پیش
والد
کامیت
0cf2b02791
58فایلهای تغییر یافته به همراه3479 افزوده شده و 328 حذف شده
  1. 0 10
      .github/ISSUE_TEMPLATE/question.md
  2. 3 0
      .gitmodules
  3. 3 0
      CONTRIBUTORS.md
  4. 3 2
      README.md
  5. 5 0
      docs/boards.md
  6. 0 0
      examples/device/cdc_msc_freertos/.skip.MCU_MKL25ZXX
  7. 0 0
      examples/device/msc_dual_lun/.skip.MCU_MKL25ZXX
  8. 0 0
      examples/device/net_lwip_webserver/.skip.MCU_MKL25ZXX
  9. 59 0
      examples/host/cdc_msc_hid/src/keyboard_helper.h
  10. 5 0
      examples/make.mk
  11. 8 7
      examples/rules.mk
  12. 7 0
      hw/bsp/board.h
  13. 4 1
      hw/bsp/board_mcu.h
  14. 1 1
      hw/bsp/da14695_dk_usb/board.mk
  15. 1 1
      hw/bsp/da1469x_dk_pro/board.mk
  16. 234 0
      hw/bsp/double_m33_express/LPC55S69_cm33_core0_uf2.ld
  17. 55 0
      hw/bsp/double_m33_express/board.mk
  18. 275 0
      hw/bsp/double_m33_express/double_m33_express.c
  19. 46 0
      hw/bsp/frdm_kl25z/board.mk
  20. 148 0
      hw/bsp/frdm_kl25z/frdm_kl25z.c
  21. 8 2
      hw/bsp/nrf/family.mk
  22. 99 0
      hw/bsp/raspberry_pi_pico/board_raspberry_pi_pico.c
  23. 1 1
      hw/mcu/nxp
  24. 1 0
      lib/sct_neopixel
  25. 1 1
      src/class/hid/hid_device.h
  26. 4 2
      src/class/hid/hid_host.c
  27. 1 1
      src/class/msc/msc.h
  28. 175 173
      src/class/msc/msc_device.c
  29. 1 0
      src/common/tusb_compiler.h
  30. 8 2
      src/common/tusb_fifo.c
  31. 21 1
      src/device/usbd.c
  32. 8 0
      src/device/usbd_control.c
  33. 6 0
      src/host/usbh.c
  34. 1 0
      src/host/usbh.h
  35. 2 0
      src/osal/osal.h
  36. 192 0
      src/osal/osal_pico.h
  37. 3 4
      src/portable/dialog/da146xx/dcd_da146xx.c
  38. 0 1
      src/portable/espressif/esp32s2/dcd_esp32s2.c
  39. 12 3
      src/portable/microchip/samd/dcd_samd.c
  40. 1 1
      src/portable/microchip/samg/dcd_samg.c
  41. 1 1
      src/portable/nordic/nrf5x/dcd_nrf5x.c
  42. 1 2
      src/portable/nuvoton/nuc120/dcd_nuc120.c
  43. 1 1
      src/portable/nuvoton/nuc121/dcd_nuc121.c
  44. 477 0
      src/portable/nxp/khci/dcd_khci.c
  45. 1 1
      src/portable/nxp/lpc17_40/dcd_lpc17_40.c
  46. 2 2
      src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c
  47. 482 0
      src/portable/raspberrypi/rp2040/dcd_rp2040.c
  48. 549 0
      src/portable/raspberrypi/rp2040/hcd_rp2040.c
  49. 279 0
      src/portable/raspberrypi/rp2040/rp2040_usb.c
  50. 124 0
      src/portable/raspberrypi/rp2040/rp2040_usb.h
  51. 1 1
      src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
  52. 124 91
      src/portable/st/synopsys/dcd_synopsys.c
  53. 3 3
      src/portable/ti/msp430x5xx/dcd_msp430x5xx.c
  54. 1 1
      src/portable/valentyusb/eptri/dcd_eptri.c
  55. 13 6
      src/tusb_option.h
  56. 1 1
      test/test/device/msc/test_msc_device.c
  57. 3 3
      test/test/support/tusb_config.h
  58. 14 1
      tools/top.mk

+ 0 - 10
.github/ISSUE_TEMPLATE/question.md

@@ -1,10 +0,0 @@
----
-name: Question
-about: Question for this project
-title: ''
-labels: Q&A
-assignees: ''
-
----
-
-**Describe what the question is**

+ 3 - 0
.gitmodules

@@ -109,3 +109,6 @@
 [submodule "lib/CMSIS_5"]
 	path = lib/CMSIS_5
 	url = https://github.com/ARM-software/CMSIS_5.git
+[submodule "lib/sct_neopixel"]
+	path = lib/sct_neopixel
+	url = https://github.com/gsteiert/sct_neopixel

+ 3 - 0
CONTRIBUTORS.md

@@ -31,6 +31,9 @@
 - **[Kay Sievers](https://github.com/kaysievers)**
   - Improve MIDI driver with packet API
 
+- **[Koji KITAYAMA](https://github.com/kkitayam)**
+  - Add new DCD port for **NXP Kinetis KL25**
+
 - **[Nathan Conrad](https://github.com/pigrew)**
   - Add new DCD port for **STM32 fsdev** Fullspeed device for STM32 L0, F0, F1, F3 etc ...
   - Add new class driver for **USB Test and Measurement Class (USBTMC)**

+ 3 - 2
README.md

@@ -35,9 +35,10 @@ The stack supports the following MCUs:
 - **MicroChip:** SAMD11, SAMD21, SAMD51, SAME5x, SAMG55
 - **NordicSemi:** nRF52833, nRF52840
 - **Nuvoton:** NUC120, NUC121/NUC125, NUC126, NUC505
-- **NXP:** 
-  - LPC Series: 11Uxx, 13xx, 175x_6x, 177x_8x, 18xx, 40xx, 43xx, 51Uxx, 54xxx, 55xx
+- **NXP:**
   - iMX RT Series: RT1011, RT1015, RT1021, RT1052, RT1062, RT1064
+  - Kinetis: KL25
+  - LPC Series: 11Uxx, 13xx, 175x_6x, 177x_8x, 18xx, 40xx, 43xx, 51Uxx, 54xxx, 55xx
 - **Sony:** CXD56
 - **ST:** STM32 series: L0, F0, F1, F2, F3, F4, F7, H7 both FullSpeed and HighSpeed
 - **TI:** MSP430

+ 5 - 0
docs/boards.md

@@ -68,6 +68,10 @@ This code base already had supported for a handful of following boards (sorted a
 - [MIMX RT1064 Evaluation Kit](https://www.nxp.com/design/development-boards/i.mx-evaluation-and-development-boards/mimxrt1064-evk-i.mx-rt1064-evaluation-kit:MIMXRT1064-EVK)
 - [Teensy 4.0 Development Board](https://www.pjrc.com/store/teensy40.html)
 
+### NXP Kinetis
+
+- [FRDM-KL25Z](https://www.nxp.com/design/development-boards/freedom-development-boards/mcu-boards/freedom-development-platform-for-kinetis-kl14-kl15-kl24-kl25-mcus:FRDM-KL25Z)
+
 ### NXP LPC
 
 - [ARM mbed LPC1768](https://www.nxp.com/products/processors-and-microcontrollers/arm-microcontrollers/general-purpose-mcus/lpc1700-cortex-m3/arm-mbed-lpc1768-board:OM11043)
@@ -82,6 +86,7 @@ This code base already had supported for a handful of following boards (sorted a
 - [LPCXpresso 54114](https://www.nxp.com/design/microcontrollers-developer-resources/lpcxpresso-boards/lpcxpresso54114-board:OM13089)
 - [LPCXpresso 55s69 EVK](https://www.nxp.com/products/processors-and-microcontrollers/arm-microcontrollers/general-purpose-mcus/lpc5500-cortex-m33/lpcxpresso55s69-development-board:LPC55S69-EVK)
 - [NGX LPC4330-Xplorer](https://www.nxp.com/design/designs/lpc4330-xplorer-board:OM13027)
+- [Double M33 Express](https://www.crowdsupply.com/steiert-solutions/double-m33-express)
 
 ### Sony
 

+ 0 - 0
examples/device/cdc_msc_freertos/.skip.MCU_MKL25ZXX


+ 0 - 0
examples/device/msc_dual_lun/.skip.MCU_MKL25ZXX


+ 0 - 0
examples/device/net_lwip_webserver/.skip.MCU_MKL25ZXX


+ 59 - 0
examples/host/cdc_msc_hid/src/keyboard_helper.h

@@ -0,0 +1,59 @@
+#ifndef KEYBOARD_HELPER_H
+#define KEYBAORD_HELPER_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "tusb.h"
+
+// look up new key in previous keys
+inline bool find_key_in_report(hid_keyboard_report_t const *p_report, uint8_t keycode)
+{
+  for(uint8_t i = 0; i < 6; i++)
+  {
+    if (p_report->keycode[i] == keycode)  return true;
+  }
+
+  return false;
+}
+
+inline uint8_t keycode_to_ascii(uint8_t modifier, uint8_t keycode)
+{
+  return keycode > 128 ? 0 :
+    hid_keycode_to_ascii_tbl [keycode][modifier & (KEYBOARD_MODIFIER_LEFTSHIFT | KEYBOARD_MODIFIER_RIGHTSHIFT) ? 1 : 0];
+}
+
+void print_kbd_report(hid_keyboard_report_t *prev_report, hid_keyboard_report_t const *new_report)
+{
+
+  printf("Report: ");
+  uint8_t c;
+
+  // I assume it's possible to have up to 6 keypress events per report?
+  for (uint8_t i = 0; i < 6; i++)
+  {
+    // Check for key presses
+    if (new_report->keycode[i])
+    {
+      // If not in prev report then it is newly pressed
+      if ( !find_key_in_report(prev_report, new_report->keycode[i]) )
+        c = keycode_to_ascii(new_report->modifier, new_report->keycode[i]);
+        printf("press %c ", c);
+    }
+
+    // Check for key depresses (i.e. was present in prev report but not here)
+    if (prev_report->keycode[i])
+    {
+      // If not present in the current report then depressed
+      if (!find_key_in_report(new_report, prev_report->keycode[i]))
+      {
+        c = keycode_to_ascii(prev_report->modifier, prev_report->keycode[i]);
+        printf("depress %c ", c);
+      }
+    }
+  }
+
+  printf("\n");
+}
+
+#endif

+ 5 - 0
examples/make.mk

@@ -54,9 +54,14 @@ CXX = $(CROSS_COMPILE)g++
 OBJCOPY = $(CROSS_COMPILE)objcopy
 SIZE = $(CROSS_COMPILE)size
 MKDIR = mkdir
+ifeq ($(CMDEXE),1)
+CP = copy
+RM = del
+else
 SED = sed
 CP = cp
 RM = rm
+endif
 
 #-------------- Source files and compiler flags --------------
 

+ 8 - 7
examples/rules.mk

@@ -95,7 +95,11 @@ uf2: $(BUILD)/$(BOARD)-firmware.uf2
 OBJ_DIRS = $(sort $(dir $(OBJ)))
 $(OBJ): | $(OBJ_DIRS)
 $(OBJ_DIRS):
+ifeq ($(CMDEXE),1)
+	@$(MKDIR) $(subst /,\,$@)
+else
 	@$(MKDIR) -p $@
+endif
 
 $(BUILD)/$(BOARD)-firmware.elf: $(OBJ)
 	@echo LINK $@
@@ -121,13 +125,6 @@ vpath %.c . $(TOP)
 $(BUILD)/obj/%.o: %.c
 	@echo CC $(notdir $@)
 	@$(CC) $(CFLAGS) -c -MD -o $@ $<
-	@# The following fixes the dependency file.
-	@# See http://make.paulandlesley.org/autodep.html for details.
-	@# Regex adjusted from the above to play better with Windows paths, etc.
-	@$(CP) $(@:.o=.d) $(@:.o=.P); \
-	  $(SED) -e 's/#.*//' -e 's/^.*:  *//' -e 's/ *\\$$//' \
-	      -e '/^$$/ d' -e 's/$$/ :/' < $(@:.o=.d) >> $(@:.o=.P); \
-	  $(RM) $(@:.o=.d)
 
 # ASM sources lower case .s
 vpath %.s . $(TOP)
@@ -148,7 +145,11 @@ size: $(BUILD)/$(BOARD)-firmware.elf
 
 .PHONY: clean
 clean:
+ifeq ($(CMDEXE),1)
+	rd /S /Q $(subst /,\,$(BUILD))
+else
 	$(RM) -rf $(BUILD)
+endif
 
 # Print out the value of a make variable.
 # https://stackoverflow.com/questions/16467718/how-to-print-out-a-variable-in-makefile

+ 7 - 0
hw/bsp/board.h

@@ -80,6 +80,13 @@ int board_uart_write(void const * buf, int len);
     return os_time_ticks_to_ms32( os_time_get() );
   }
 
+#elif CFG_TUSB_OS == OPT_OS_PICO
+#include "pico/time.h"
+static inline uint32_t board_millis(void)
+  {
+    return to_ms_since_boot(get_absolute_time());
+  }
+
 #else
   #error "board_millis() is not implemented for this OS"
 #endif

+ 4 - 1
hw/bsp/board_mcu.h

@@ -46,7 +46,7 @@
   #include "chip.h"
 
 #elif CFG_TUSB_MCU == OPT_MCU_LPC51UXX || CFG_TUSB_MCU == OPT_MCU_LPC54XXX || \
-      CFG_TUSB_MCU == OPT_MCU_LPC55XX
+      CFG_TUSB_MCU == OPT_MCU_LPC55XX  || CFG_TUSB_MCU == OPT_MCU_MKL25ZXX
   #include "fsl_device_registers.h"
 
 #elif CFG_TUSB_MCU == OPT_MCU_NRF5X
@@ -117,6 +117,9 @@
 #elif CFG_TUSB_MCU == OPT_MCU_DA1469X
   #include "DA1469xAB.h"
 
+#elif CFG_TUSB_MCU == OPT_MCU_RP2040
+  #include "pico.h"
+
 #else
   #error "Missing MCU header"
 #endif

+ 1 - 1
hw/bsp/da14695_dk_usb/board.mk

@@ -21,7 +21,7 @@ SRC_C += \
 	$(MCU_FAMILY_DIR)/src/da1469x_clock.c \
 	$(MCU_FAMILY_DIR)/src/hal_gpio.c \
 
-SRC_S += $(TOP)/hw/bsp/$(BOARD)/gcc_startup_da1469x.S
+SRC_S += hw/bsp/$(BOARD)/gcc_startup_da1469x.S
 
 INC += \
 	$(TOP)/hw/bsp/$(BOARD) \

+ 1 - 1
hw/bsp/da1469x_dk_pro/board.mk

@@ -21,7 +21,7 @@ SRC_C += \
 	$(MCU_FAMILY_DIR)/src/da1469x_clock.c \
 	$(MCU_FAMILY_DIR)/src/hal_gpio.c \
 
-SRC_S += $(TOP)/hw/bsp/$(BOARD)/gcc_startup_da1469x.S
+SRC_S += hw/bsp/$(BOARD)/gcc_startup_da1469x.S
 
 INC += \
 	$(TOP)/hw/bsp/$(BOARD) \

+ 234 - 0
hw/bsp/double_m33_express/LPC55S69_cm33_core0_uf2.ld

@@ -0,0 +1,234 @@
+/*
+** ###################################################################
+**     Processors:          LPC55S69JBD100_cm33_core0
+**                          LPC55S69JBD64_cm33_core0
+**                          LPC55S69JEV98_cm33_core0
+**
+**     Compiler:            GNU C Compiler
+**     Reference manual:    LPC55S6x/LPC55S2x/LPC552x User manual(UM11126) Rev.1.3  16 May 2019
+**     Version:             rev. 1.1, 2019-05-16
+**     Build:               b191008
+**
+**     Abstract:
+**         Linker file for the GNU C Compiler
+**
+**     Copyright 2016 Freescale Semiconductor, Inc.
+**     Copyright 2016-2019 NXP
+**     All rights reserved.
+**
+**     SPDX-License-Identifier: BSD-3-Clause
+**
+**     http:                 www.nxp.com
+**     mail:                 support@nxp.com
+**
+** ###################################################################
+*/
+
+
+
+/* Entry Point */
+ENTRY(Reset_Handler)
+
+HEAP_SIZE  = DEFINED(__heap_size__)  ? __heap_size__  : 0x0400;
+STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x0800;
+RPMSG_SHMEM_SIZE = DEFINED(__use_shmem__) ? 0x1800 : 0;
+
+/* Specify the memory areas */
+MEMORY
+{
+  m_interrupts          (RX)  : ORIGIN = 0x00010000, LENGTH = 0x00000200
+  m_text                (RX)  : ORIGIN = 0x00010200, LENGTH = 0x0007FE00
+  m_core1_image         (RX)  : ORIGIN = 0x00090000, LENGTH = 0x00008000
+  m_data                (RW)  : ORIGIN = 0x20000000, LENGTH = 0x00033000 - RPMSG_SHMEM_SIZE
+  rpmsg_sh_mem          (RW)  : ORIGIN = 0x20033000 - RPMSG_SHMEM_SIZE, LENGTH = RPMSG_SHMEM_SIZE
+  m_usb_sram            (RW)  : ORIGIN = 0x40100000, LENGTH = 0x00004000
+}
+
+/* Define output sections */
+SECTIONS
+{
+  /* section for storing the secondary core image */
+  .m0code :
+  {
+     . = ALIGN(4) ;
+    KEEP (*(.m0code))
+     *(.m0code*)
+     . = ALIGN(4) ;
+  } > m_core1_image
+
+  /* NOINIT section for rpmsg_sh_mem */
+  .noinit_rpmsg_sh_mem (NOLOAD) : ALIGN(4)
+  {
+     __RPMSG_SH_MEM_START__ = .;
+     *(.noinit.$rpmsg_sh_mem*)
+     . = ALIGN(4) ;
+     __RPMSG_SH_MEM_END__ = .;
+  } > rpmsg_sh_mem
+
+  /* The startup code goes first into internal flash */
+  .interrupts :
+  {
+    . = ALIGN(4);
+    KEEP(*(.isr_vector))     /* Startup code */
+    . = ALIGN(4);
+  } > m_interrupts
+
+  /* The program code and other data goes into internal flash */
+  .text :
+  {
+    . = ALIGN(4);
+    *(.text)                 /* .text sections (code) */
+    *(.text*)                /* .text* sections (code) */
+    *(.rodata)               /* .rodata sections (constants, strings, etc.) */
+    *(.rodata*)              /* .rodata* sections (constants, strings, etc.) */
+    *(.glue_7)               /* glue arm to thumb code */
+    *(.glue_7t)              /* glue thumb to arm code */
+    *(.eh_frame)
+    KEEP (*(.init))
+    KEEP (*(.fini))
+    . = ALIGN(4);
+  } > m_text
+
+  .ARM.extab :
+  {
+    *(.ARM.extab* .gnu.linkonce.armextab.*)
+  } > m_text
+
+  .ARM :
+  {
+    __exidx_start = .;
+    *(.ARM.exidx*)
+    __exidx_end = .;
+  } > m_text
+
+ .ctors :
+  {
+    __CTOR_LIST__ = .;
+    /* gcc uses crtbegin.o to find the start of
+       the constructors, so we make sure it is
+       first.  Because this is a wildcard, it
+       doesn't matter if the user does not
+       actually link against crtbegin.o; the
+       linker won't look for a file to match a
+       wildcard.  The wildcard also means that it
+       doesn't matter which directory crtbegin.o
+       is in.  */
+    KEEP (*crtbegin.o(.ctors))
+    KEEP (*crtbegin?.o(.ctors))
+    /* We don't want to include the .ctor section from
+       from the crtend.o file until after the sorted ctors.
+       The .ctor section from the crtend file contains the
+       end of ctors marker and it must be last */
+    KEEP (*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors))
+    KEEP (*(SORT(.ctors.*)))
+    KEEP (*(.ctors))
+    __CTOR_END__ = .;
+  } > m_text
+
+  .dtors :
+  {
+    __DTOR_LIST__ = .;
+    KEEP (*crtbegin.o(.dtors))
+    KEEP (*crtbegin?.o(.dtors))
+    KEEP (*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors))
+    KEEP (*(SORT(.dtors.*)))
+    KEEP (*(.dtors))
+    __DTOR_END__ = .;
+  } > m_text
+
+  .preinit_array :
+  {
+    PROVIDE_HIDDEN (__preinit_array_start = .);
+    KEEP (*(.preinit_array*))
+    PROVIDE_HIDDEN (__preinit_array_end = .);
+  } > m_text
+
+  .init_array :
+  {
+    PROVIDE_HIDDEN (__init_array_start = .);
+    KEEP (*(SORT(.init_array.*)))
+    KEEP (*(.init_array*))
+    PROVIDE_HIDDEN (__init_array_end = .);
+  } > m_text
+
+  .fini_array :
+  {
+    PROVIDE_HIDDEN (__fini_array_start = .);
+    KEEP (*(SORT(.fini_array.*)))
+    KEEP (*(.fini_array*))
+    PROVIDE_HIDDEN (__fini_array_end = .);
+  } > m_text
+
+  __etext = .;    /* define a global symbol at end of code */
+  __DATA_ROM = .; /* Symbol is used by startup for data initialization */
+
+  .data : AT(__DATA_ROM)
+  {
+    . = ALIGN(4);
+    __DATA_RAM = .;
+    __data_start__ = .;      /* create a global symbol at data start */
+    *(.ramfunc*)             /* for functions in ram */
+    *(.data)                 /* .data sections */
+    *(.data*)                /* .data* sections */
+    KEEP(*(.jcr*))
+    . = ALIGN(4);
+    __data_end__ = .;        /* define a global symbol at data end */
+  } > m_data
+
+  __DATA_END = __DATA_ROM + (__data_end__ - __data_start__);
+  text_end = ORIGIN(m_text) + LENGTH(m_text);
+  ASSERT(__DATA_END <= text_end, "region m_text overflowed with text and data")
+
+  /* Uninitialized data section */
+  .bss :
+  {
+    /* This is used by the startup in order to initialize the .bss section */
+    . = ALIGN(4);
+    __START_BSS = .;
+    __bss_start__ = .;
+    *(.bss)
+    *(.bss*)
+    *(COMMON)
+    . = ALIGN(4);
+    __bss_end__ = .;
+    __END_BSS = .;
+  } > m_data
+
+  .heap :
+  {
+    . = ALIGN(8);
+    __end__ = .;
+    PROVIDE(end = .);
+    __HeapBase = .;
+    . += HEAP_SIZE;
+    __HeapLimit = .;
+    __heap_limit = .; /* Add for _sbrk */
+  } > m_data
+
+  .stack :
+  {
+    . = ALIGN(8);
+    . += STACK_SIZE;
+  } > m_data
+
+  m_usb_bdt (NOLOAD) :
+  {
+    . = ALIGN(512);
+    *(m_usb_bdt)
+  } > m_usb_sram
+
+  m_usb_global (NOLOAD) :
+  {
+    *(m_usb_global)
+  } > m_usb_sram
+
+  /* Initializes stack on the end of block */
+  __StackTop   = ORIGIN(m_data) + LENGTH(m_data);
+  __StackLimit = __StackTop - STACK_SIZE;
+  PROVIDE(__stack = __StackTop);
+
+  .ARM.attributes 0 : { *(.ARM.attributes) }
+
+  ASSERT(__StackLimit >= __HeapLimit, "region m_data overflowed with stack and heap")
+}
+

+ 55 - 0
hw/bsp/double_m33_express/board.mk

@@ -0,0 +1,55 @@
+CFLAGS += \
+  -flto \
+  -mthumb \
+  -mabi=aapcs \
+  -mcpu=cortex-m33 \
+  -mfloat-abi=hard \
+  -mfpu=fpv5-sp-d16 \
+  -DCPU_LPC55S69JBD100_cm33_core0 \
+  -DCFG_TUSB_MCU=OPT_MCU_LPC55XX \
+  -DCFG_TUSB_MEM_SECTION='__attribute__((section(".data")))' \
+  -DCFG_TUSB_MEM_ALIGN='__attribute__((aligned(64)))'
+
+# mcu driver cause following warnings
+CFLAGS += -Wno-error=unused-parameter -Wno-error=float-equal
+
+MCU_DIR = hw/mcu/nxp/sdk/devices/LPC55S69
+
+# All source paths should be relative to the top level.
+LD_FILE = hw/bsp/$(BOARD)/LPC55S69_cm33_core0_uf2.ld
+
+SRC_C += \
+	$(MCU_DIR)/system_LPC55S69_cm33_core0.c \
+	$(MCU_DIR)/drivers/fsl_clock.c \
+	$(MCU_DIR)/drivers/fsl_gpio.c \
+	$(MCU_DIR)/drivers/fsl_power.c \
+	$(MCU_DIR)/drivers/fsl_reset.c \
+	$(MCU_DIR)/drivers/fsl_usart.c \
+	$(MCU_DIR)/drivers/fsl_flexcomm.c \
+	lib/sct_neopixel/sct_neopixel.c 
+
+INC += \
+    $(TOP)/hw/bsp/ \
+	$(TOP)/hw/bsp/$(BOARD) \
+	$(TOP)/lib/sct_neopixel \
+	$(TOP)/$(MCU_DIR)/../../CMSIS/Include \
+	$(TOP)/$(MCU_DIR) \
+	$(TOP)/$(MCU_DIR)/drivers
+
+SRC_S += $(MCU_DIR)/gcc/startup_LPC55S69_cm33_core0.S
+
+LIBS += $(TOP)/$(MCU_DIR)/gcc/libpower_hardabi.a
+
+# For TinyUSB port source
+VENDOR = nxp
+CHIP_FAMILY = lpc_ip3511
+
+# For freeRTOS port source
+FREERTOS_PORT = ARM_CM33_NTZ/non_secure
+
+# For flash-jlink target
+JLINK_DEVICE = LPC55S69
+
+# flash using pyocd
+flash: $(BUILD)/$(BOARD)-firmware.hex
+	pyocd flash -t LPC55S69 $<

+ 275 - 0
hw/bsp/double_m33_express/double_m33_express.c

@@ -0,0 +1,275 @@
+/* 
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018, hathach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "../board.h"
+#include "fsl_device_registers.h"
+#include "fsl_gpio.h"
+#include "fsl_power.h"
+#include "fsl_iocon.h"
+#include "fsl_usart.h"
+#include "fsl_sctimer.h"
+#include "sct_neopixel.h"
+
+//--------------------------------------------------------------------+
+// Forward USB interrupt events to TinyUSB IRQ Handler
+//--------------------------------------------------------------------+
+void USB0_IRQHandler(void)
+{
+  tud_int_handler(0);
+}
+
+void USB1_IRQHandler(void)
+{
+  tud_int_handler(1);
+}
+
+//--------------------------------------------------------------------+
+// MACRO TYPEDEF CONSTANT ENUM
+//--------------------------------------------------------------------+
+#define LED_PORT              0
+#define LED_PIN               1
+#define LED_STATE_ON          1
+
+// WAKE button
+#define BUTTON_PORT           0
+#define BUTTON_PIN            5
+#define BUTTON_STATE_ACTIVE   0
+
+// Number of neopixels
+#define NEOPIXEL_NUMBER       2
+#define NEOPIXEL_PORT         0
+#define NEOPIXEL_PIN          27
+#define NEOPIXEL_CH           6
+#define NEOPIXEL_TYPE         0  
+
+// UART
+#define UART_DEV              USART0
+
+// IOCON pin mux
+#define IOCON_PIO_DIGITAL_EN 0x0100u  /*!<@brief Enables digital function */
+#define IOCON_PIO_FUNC0 0x00u         /*!<@brief Selects pin function 0 */
+#define IOCON_PIO_FUNC1 0x01u         /*!<@brief Selects pin function 1 */
+#define IOCON_PIO_FUNC4 0x04u         /*!<@brief Selects pin function 4 */
+#define IOCON_PIO_FUNC7 0x07u         /*!<@brief Selects pin function 7 */
+#define IOCON_PIO_INV_DI 0x00u        /*!<@brief Input function is not inverted */
+#define IOCON_PIO_MODE_INACT 0x00u    /*!<@brief No addition pin function */
+#define IOCON_PIO_OPENDRAIN_DI 0x00u  /*!<@brief Open drain is disabled */
+#define IOCON_PIO_SLEW_STANDARD 0x00u /*!<@brief Standard mode, output slew rate control is enabled */
+
+#define IOCON_PIO_DIG_FUNC0_EN   (IOCON_PIO_DIGITAL_EN | IOCON_PIO_FUNC0) /*!<@brief Digital pin function 0 enabled */
+#define IOCON_PIO_DIG_FUNC1_EN   (IOCON_PIO_DIGITAL_EN | IOCON_PIO_FUNC1) /*!<@brief Digital pin function 1 enabled */
+#define IOCON_PIO_DIG_FUNC4_EN   (IOCON_PIO_DIGITAL_EN | IOCON_PIO_FUNC4) /*!<@brief Digital pin function 2 enabled */
+#define IOCON_PIO_DIG_FUNC7_EN   (IOCON_PIO_DIGITAL_EN | IOCON_PIO_FUNC7) /*!<@brief Digital pin function 2 enabled */
+
+
+// Global Variables
+uint32_t pixelData[NEOPIXEL_NUMBER];
+
+/****************************************************************
+name: BOARD_BootClockFROHF96M
+outputs:
+- {id: SYSTICK_clock.outFreq, value: 96 MHz}
+- {id: System_clock.outFreq, value: 96 MHz}
+settings:
+- {id: SYSCON.MAINCLKSELA.sel, value: SYSCON.fro_hf}
+sources:
+- {id: SYSCON.fro_hf.outFreq, value: 96 MHz}
+******************************************************************/
+void BootClockFROHF96M(void)
+{
+  /*!< Set up the clock sources */
+  /*!< Set up FRO */
+  POWER_DisablePD(kPDRUNCFG_PD_FRO192M); /*!< Ensure FRO is on  */
+  CLOCK_SetupFROClocking(12000000U);     /*!< Set up FRO to the 12 MHz, just for sure */
+  CLOCK_AttachClk(kFRO12M_to_MAIN_CLK); /*!< Switch to FRO 12MHz first to ensure we can change voltage without
+                                             accidentally being below the voltage for current speed */
+
+  CLOCK_SetupFROClocking(96000000U); /*!< Set up high frequency FRO output to selected frequency */
+
+  POWER_SetVoltageForFreq(96000000U); /*!< Set voltage for the one of the fastest clock outputs: System clock output */
+  CLOCK_SetFLASHAccessCyclesForFreq(96000000U); /*!< Set FLASH wait states for core */
+
+  /*!< Set up dividers */
+  CLOCK_SetClkDiv(kCLOCK_DivAhbClk, 1U, false);     /*!< Set AHBCLKDIV divider to value 1 */
+
+  /*!< Set up clock selectors - Attach clocks to the peripheries */
+  CLOCK_AttachClk(kFRO_HF_to_MAIN_CLK); /*!< Switch MAIN_CLK to FRO_HF */
+
+  /*!< Set SystemCoreClock variable. */
+  SystemCoreClock = 96000000U;
+}
+
+void board_init(void)
+{
+  // Enable IOCON clock
+  CLOCK_EnableClock(kCLOCK_Iocon);
+
+  // Init 96 MHz clock
+  BootClockFROHF96M();
+
+#if CFG_TUSB_OS == OPT_OS_NONE
+  // 1ms tick timer
+  SysTick_Config(SystemCoreClock / 1000);
+#elif CFG_TUSB_OS == OPT_OS_FREERTOS
+  // If freeRTOS is used, IRQ priority is limit by max syscall ( smaller is higher )
+  NVIC_SetPriority(USB0_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY );
+#endif
+
+  GPIO_PortInit(GPIO, 0);
+  GPIO_PortInit(GPIO, 1);
+
+  // LED
+  /* PORT0 PIN1 configured as PIO0_1 */
+  IOCON_PinMuxSet(IOCON, 0U, 1U, IOCON_PIO_DIG_FUNC0_EN);
+
+  gpio_pin_config_t const led_config = { kGPIO_DigitalOutput, 1};
+  GPIO_PinInit(GPIO, LED_PORT, LED_PIN, &led_config);
+
+  // Neopixel
+  /* PORT0 PIN27 configured as SCT0_OUT6 */
+  IOCON_PinMuxSet(IOCON, NEOPIXEL_PORT, NEOPIXEL_PIN, IOCON_PIO_DIG_FUNC4_EN);
+
+  sctpix_init(NEOPIXEL_TYPE);
+  sctpix_addCh(NEOPIXEL_CH, pixelData, NEOPIXEL_NUMBER);
+  sctpix_setPixel(NEOPIXEL_CH, 0, 0x100010);
+  sctpix_setPixel(NEOPIXEL_CH, 1, 0x100010);
+  sctpix_show();
+
+
+  // Button
+  /* PORT0 PIN5 configured as PIO0_5 */
+  IOCON_PinMuxSet(IOCON, BUTTON_PORT, BUTTON_PIN, IOCON_PIO_DIG_FUNC0_EN);
+
+  gpio_pin_config_t const button_config = { kGPIO_DigitalInput, 0};
+  GPIO_PinInit(GPIO, BUTTON_PORT, BUTTON_PIN, &button_config);
+
+  // UART
+  /* PORT0 PIN29 (coords: 92) is configured as FC0_RXD_SDA_MOSI_DATA */
+  IOCON_PinMuxSet(IOCON, 0U, 29U, IOCON_PIO_DIG_FUNC1_EN);
+  /* PORT0 PIN30 (coords: 94) is configured as FC0_TXD_SCL_MISO_WS */
+  IOCON_PinMuxSet(IOCON, 0U, 30U, IOCON_PIO_DIG_FUNC1_EN);
+
+#if defined(UART_DEV) && CFG_TUSB_DEBUG
+  // Enable UART when debug log is on
+  CLOCK_AttachClk(kFRO12M_to_FLEXCOMM0);
+  usart_config_t uart_config;
+  USART_GetDefaultConfig(&uart_config);
+  uart_config.baudRate_Bps = CFG_BOARD_UART_BAUDRATE;
+  uart_config.enableTx     = true;
+  uart_config.enableRx     = true;
+  USART_Init(UART_DEV, &uart_config, 12000000);
+#endif
+
+  // USB VBUS
+  /* PORT0 PIN22 configured as USB0_VBUS */
+  IOCON_PinMuxSet(IOCON, 0U, 22U, IOCON_PIO_DIG_FUNC7_EN);
+
+  // USB Controller
+  POWER_DisablePD(kPDRUNCFG_PD_USB0_PHY); /*Turn on USB0 Phy */
+  POWER_DisablePD(kPDRUNCFG_PD_USB1_PHY); /*< Turn on USB1 Phy */
+
+  /* reset the IP to make sure it's in reset state. */
+  RESET_PeripheralReset(kUSB0D_RST_SHIFT_RSTn);
+  RESET_PeripheralReset(kUSB0HSL_RST_SHIFT_RSTn);
+  RESET_PeripheralReset(kUSB0HMR_RST_SHIFT_RSTn);
+  RESET_PeripheralReset(kUSB1H_RST_SHIFT_RSTn);
+  RESET_PeripheralReset(kUSB1D_RST_SHIFT_RSTn);
+  RESET_PeripheralReset(kUSB1_RST_SHIFT_RSTn);
+  RESET_PeripheralReset(kUSB1RAM_RST_SHIFT_RSTn);
+
+#if (defined CFG_TUSB_RHPORT1_MODE) && (CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE)
+  CLOCK_EnableClock(kCLOCK_Usbh1);
+  /* Put PHY powerdown under software control */
+  USBHSH->PORTMODE = USBHSH_PORTMODE_SW_PDCOM_MASK;
+  /* According to reference mannual, device mode setting has to be set by access usb host register */
+  USBHSH->PORTMODE |= USBHSH_PORTMODE_DEV_ENABLE_MASK;
+  /* enable usb1 host clock */
+  CLOCK_DisableClock(kCLOCK_Usbh1);
+#endif
+
+#if (defined CFG_TUSB_RHPORT0_MODE) && (CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE)
+  // Enable USB Clock Adjustments to trim the FRO for the full speed controller
+  ANACTRL->FRO192M_CTRL |= ANACTRL_FRO192M_CTRL_USBCLKADJ_MASK;
+  CLOCK_SetClkDiv(kCLOCK_DivUsb0Clk, 1, false);
+  CLOCK_AttachClk(kFRO_HF_to_USB0_CLK);
+  /* enable usb0 host clock */
+  CLOCK_EnableClock(kCLOCK_Usbhsl0);
+  /*According to reference mannual, device mode setting has to be set by access usb host register */
+  USBFSH->PORTMODE |= USBFSH_PORTMODE_DEV_ENABLE_MASK;
+  /* disable usb0 host clock */
+  CLOCK_DisableClock(kCLOCK_Usbhsl0);
+  CLOCK_EnableUsbfs0DeviceClock(kCLOCK_UsbfsSrcFro, CLOCK_GetFreq(kCLOCK_FroHf)); /* enable USB Device clock */
+#endif
+
+}
+
+//--------------------------------------------------------------------+
+// Board porting API
+//--------------------------------------------------------------------+
+
+void board_led_write(bool state)
+{
+  GPIO_PinWrite(GPIO, LED_PORT, LED_PIN, state ? LED_STATE_ON : (1-LED_STATE_ON));
+  if (state) {
+    sctpix_setPixel(NEOPIXEL_CH, 0, 0x100000);
+    sctpix_setPixel(NEOPIXEL_CH, 1, 0x101010);
+  } else {
+    sctpix_setPixel(NEOPIXEL_CH, 0, 0x001000);
+    sctpix_setPixel(NEOPIXEL_CH, 1, 0x000010);
+  }
+  sctpix_show();
+}
+
+uint32_t board_button_read(void)
+{
+  // active low
+  return BUTTON_STATE_ACTIVE == GPIO_PinRead(GPIO, BUTTON_PORT, BUTTON_PIN);
+}
+
+int board_uart_read(uint8_t* buf, int len)
+{
+  (void) buf; (void) len;
+  return 0;
+}
+
+int board_uart_write(void const * buf, int len)
+{
+  (void) buf; (void) len;
+  return 0;
+}
+
+#if CFG_TUSB_OS == OPT_OS_NONE
+volatile uint32_t system_ticks = 0;
+void SysTick_Handler(void)
+{
+  system_ticks++;
+}
+
+uint32_t board_millis(void)
+{
+  return system_ticks;
+}
+#endif

+ 46 - 0
hw/bsp/frdm_kl25z/board.mk

@@ -0,0 +1,46 @@
+CFLAGS += \
+  -mthumb \
+  -mabi=aapcs \
+  -mcpu=cortex-m0plus \
+  -DCPU_MKL25Z128VLK4 \
+  -DCFG_TUSB_MCU=OPT_MCU_MKL25ZXX
+
+# mcu driver cause following warnings
+CFLAGS += -Wno-error=unused-parameter
+
+MCU_DIR = hw/mcu/nxp/sdk/devices/MKL25Z4
+
+# All source paths should be relative to the top level.
+LD_FILE = $(MCU_DIR)/gcc/MKL25Z128xxx4_flash.ld
+
+SRC_C += \
+	$(MCU_DIR)/system_MKL25Z4.c \
+	$(MCU_DIR)/project_template/clock_config.c \
+	$(MCU_DIR)/drivers/fsl_clock.c \
+	$(MCU_DIR)/drivers/fsl_gpio.c \
+	$(MCU_DIR)/drivers/fsl_lpsci.c
+
+INC += \
+	$(TOP)/hw/bsp/$(BOARD) \
+	$(TOP)/$(MCU_DIR)/../../CMSIS/Include \
+	$(TOP)/$(MCU_DIR) \
+	$(TOP)/$(MCU_DIR)/drivers \
+	$(TOP)/$(MCU_DIR)/project_template \
+
+SRC_S += $(MCU_DIR)/gcc/startup_MKL25Z4.S
+
+# For TinyUSB port source
+VENDOR = nxp
+CHIP_FAMILY = khci
+
+# For freeRTOS port source
+FREERTOS_PORT = ARM_CM0
+
+# For flash-jlink target
+JLINK_DEVICE = MKL25Z128xxx4
+
+# For flash-pyocd target
+PYOCD_TARGET = mkl25zl128
+
+# flash using pyocd
+flash: flash-pyocd

+ 148 - 0
hw/bsp/frdm_kl25z/frdm_kl25z.c

@@ -0,0 +1,148 @@
+/* 
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018, hathach (tinyusb.org)
+ * Copyright (c) 2020, Koji Kitayama
+ *
+ * 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 "../board.h"
+#include "fsl_device_registers.h"
+#include "fsl_gpio.h"
+#include "fsl_port.h"
+#include "fsl_clock.h"
+#include "fsl_lpsci.h"
+
+#include "clock_config.h"
+
+//--------------------------------------------------------------------+
+// Forward USB interrupt events to TinyUSB IRQ Handler
+//--------------------------------------------------------------------+
+void USB0_IRQHandler(void)
+{
+  tud_int_handler(0);
+}
+
+//--------------------------------------------------------------------+
+// MACRO TYPEDEF CONSTANT ENUM DECLARATION
+//--------------------------------------------------------------------+
+// LED
+#define LED_PINMUX            IOMUXC_GPIO_AD_B0_09_GPIO1_IO09
+#define LED_PORT              GPIOB
+#define LED_PIN_CLOCK         kCLOCK_PortB
+#define LED_PIN_PORT          PORTB
+#define LED_PIN               19U
+#define LED_PIN_FUNCTION      kPORT_MuxAsGpio
+#define LED_STATE_ON          0
+
+// UART
+#define UART_PORT             UART0
+#define UART_PIN_CLOCK        kCLOCK_PortA
+#define UART_PIN_PORT         PORTA
+#define UART_PIN_RX           1u
+#define UART_PIN_TX           2u
+#define UART_PIN_FUNCTION     kPORT_MuxAlt2
+#define SOPT5_UART0RXSRC_UART_RX      0x00u   /*!< UART0 receive data source select: UART0_RX pin */
+#define SOPT5_UART0TXSRC_UART_TX      0x00u   /*!< UART0 transmit data source select: UART0_TX pin */
+
+const uint8_t dcd_data[] = { 0x00 };
+
+void board_init(void)
+{
+  BOARD_BootClockRUN();
+  SystemCoreClockUpdate();
+
+#if CFG_TUSB_OS == OPT_OS_NONE
+  // 1ms tick timer
+  SysTick_Config(SystemCoreClock / 1000);
+#elif CFG_TUSB_OS == OPT_OS_FREERTOS
+  // If freeRTOS is used, IRQ priority is limit by max syscall ( smaller is higher )
+  NVIC_SetPriority(USB0_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY );
+#endif
+
+  // LED
+  CLOCK_EnableClock(LED_PIN_CLOCK);
+  PORT_SetPinMux(LED_PIN_PORT, LED_PIN, LED_PIN_FUNCTION);
+  gpio_pin_config_t led_config = { kGPIO_DigitalOutput, 0 };
+  GPIO_PinInit(LED_PORT, LED_PIN, &led_config);
+  board_led_write(true);
+
+  // UART
+  CLOCK_EnableClock(UART_PIN_CLOCK);
+  PORT_SetPinMux(UART_PIN_PORT, UART_PIN_RX, UART_PIN_FUNCTION);
+  PORT_SetPinMux(UART_PIN_PORT, UART_PIN_TX, UART_PIN_FUNCTION);
+  SIM->SOPT5 = ((SIM->SOPT5 &
+    (~(SIM_SOPT5_UART0TXSRC_MASK | SIM_SOPT5_UART0RXSRC_MASK)))
+      | SIM_SOPT5_UART0TXSRC(SOPT5_UART0TXSRC_UART_TX)
+      | SIM_SOPT5_UART0RXSRC(SOPT5_UART0RXSRC_UART_RX)
+    );
+
+  lpsci_config_t uart_config;
+  CLOCK_SetLpsci0Clock(1);
+  LPSCI_GetDefaultConfig(&uart_config);
+  uart_config.baudRate_Bps = CFG_BOARD_UART_BAUDRATE;
+  uart_config.enableTx = true;
+  uart_config.enableRx = true;
+  LPSCI_Init(UART_PORT, &uart_config, CLOCK_GetPllFllSelClkFreq());
+
+  // USB
+  CLOCK_EnableUsbfs0Clock(kCLOCK_UsbSrcPll0, CLOCK_GetFreq(kCLOCK_PllFllSelClk));
+}
+
+//--------------------------------------------------------------------+
+// Board porting API
+//--------------------------------------------------------------------+
+
+void board_led_write(bool state)
+{
+  GPIO_WritePinOutput(LED_PORT, LED_PIN, state ? LED_STATE_ON : (1-LED_STATE_ON));
+}
+
+uint32_t board_button_read(void)
+{
+  return 0;
+}
+
+int board_uart_read(uint8_t* buf, int len)
+{
+  LPSCI_ReadBlocking(UART_PORT, buf, len);
+  return len;
+}
+
+int board_uart_write(void const * buf, int len)
+{
+  LPSCI_WriteBlocking(UART_PORT, (uint8_t*)buf, len);
+  return len;
+}
+
+#if CFG_TUSB_OS == OPT_OS_NONE
+volatile uint32_t system_ticks = 0;
+void SysTick_Handler(void)
+{
+  system_ticks++;
+}
+
+uint32_t board_millis(void)
+{
+  return system_ticks;
+}
+#endif

+ 8 - 2
hw/bsp/nrf/family.mk

@@ -15,8 +15,14 @@ CFLAGS += -Wno-error=undef -Wno-error=unused-parameter -Wno-error=cast-align
 
 # due to tusb_hal_nrf_power_event
 GCCVERSION = $(firstword $(subst ., ,$(shell arm-none-eabi-gcc -dumpversion)))
-ifeq ($(shell expr $(GCCVERSION) \>= 8), 1)
-CFLAGS += -Wno-error=cast-function-type
+ifeq ($(CMDEXE),1)
+  ifeq ($(shell if $(GCCVERSION) geq 8 echo 1), 1)
+  CFLAGS += -Wno-error=cast-function-type
+  endif
+else
+  ifeq ($(shell expr $(GCCVERSION) \>= 8), 1)
+  CFLAGS += -Wno-error=cast-function-type
+  endif
 endif
 
 # All source paths should be relative to the top level.

+ 99 - 0
hw/bsp/raspberry_pi_pico/board_raspberry_pi_pico.c

@@ -0,0 +1,99 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * 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 "pico/stdlib.h"
+#include "../board.h"
+
+#ifndef LED_PIN
+#define LED_PIN PICO_DEFAULT_LED_PIN
+#endif
+
+void board_init(void)
+{
+    setup_default_uart();
+    gpio_init(LED_PIN);
+    gpio_set_dir(LED_PIN, 1);
+
+    // Button
+
+    // todo probably set up device mode?
+
+#if TUSB_OPT_HOST_ENABLED
+    // set portfunc to host !!!
+#endif
+}
+
+////--------------------------------------------------------------------+
+//// USB Interrupt Handler
+////--------------------------------------------------------------------+
+//void USB_IRQHandler(void)
+//{
+//#if CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST
+//    tuh_isr(0);
+//#endif
+//
+//#if CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE
+//    tud_int_handler(0);
+//#endif
+//}
+
+//--------------------------------------------------------------------+
+// Board porting API
+//--------------------------------------------------------------------+
+
+void board_led_write(bool state)
+{
+    gpio_put(LED_PIN, state);
+}
+
+uint32_t board_button_read(void)
+{
+    return 0;
+}
+
+int board_uart_read(uint8_t* buf, int len)
+{
+    for(int i=0;i<len;i++) {
+        buf[i] = uart_getc(uart_default);
+    }
+    return 0;
+}
+
+int board_uart_write(void const * buf, int len)
+{
+//  UART_Send(BOARD_UART_PORT, &c, 1, BLOCKING);
+    for(int i=0;i<len;i++) {
+        uart_putc(uart_default, ((char *)buf)[i]);
+    }
+    return 0;
+}
+
+#if CFG_TUSB_OS == OPT_OS_NONE
+uint32_t board_millis(void)
+{
+    return to_ms_since_boot(get_absolute_time());
+}
+#endif

+ 1 - 1
hw/mcu/nxp

@@ -1 +1 @@
-Subproject commit b618cb1d521cc9e133bdcd0fca154dee2d925dfe
+Subproject commit 587c65766538a5e1cfb6188ac611ded61f2eb859

+ 1 - 0
lib/sct_neopixel

@@ -0,0 +1 @@
+Subproject commit e73e04ca63495672d955f9268e003cffe168fcd8

+ 1 - 1
src/class/hid/hid_device.h

@@ -46,7 +46,7 @@
 #endif
 
 #ifndef CFG_TUD_HID_EP_BUFSIZE
-  #define CFG_TUD_HID_EP_BUFSIZE     16
+  #define CFG_TUD_HID_EP_BUFSIZE     64
 #endif
 
 //--------------------------------------------------------------------+

+ 4 - 2
src/class/hid/hid_host.c

@@ -39,6 +39,7 @@ typedef struct {
   uint8_t  itf_num;
   uint8_t  ep_in;
   uint8_t  ep_out;
+  bool     valid;
 
   uint16_t report_size;
 }hidh_interface_t;
@@ -53,6 +54,7 @@ static inline bool hidh_interface_open(uint8_t rhport, uint8_t dev_addr, uint8_t
   p_hid->ep_in       = p_endpoint_desc->bEndpointAddress;
   p_hid->report_size = p_endpoint_desc->wMaxPacketSize.size; // TODO get size from report descriptor
   p_hid->itf_num     = interface_number;
+  p_hid->valid       = true;
 
   return true;
 }
@@ -246,14 +248,14 @@ bool hidh_set_config(uint8_t dev_addr, uint8_t itf_num)
   usbh_driver_set_config_complete(dev_addr, itf_num);
 
 #if CFG_TUH_HID_KEYBOARD
-  if ( keyboardh_data[dev_addr-1].itf_num == itf_num)
+  if (( keyboardh_data[dev_addr-1].itf_num == itf_num) && keyboardh_data[dev_addr-1].valid)
   {
     tuh_hid_keyboard_mounted_cb(dev_addr);
   }
 #endif
 
 #if CFG_TUH_HID_MOUSE
-  if ( mouseh_data[dev_addr-1].ep_in == itf_num )
+  if (( mouseh_data[dev_addr-1].ep_in == itf_num ) &&  mouseh_data[dev_addr-1].valid)
   {
     tuh_hid_mouse_mounted_cb(dev_addr);
   }

+ 1 - 1
src/class/msc/msc.h

@@ -255,7 +255,7 @@ typedef struct TU_ATTR_PACKED
 
   uint8_t : 3;
   uint8_t disable_block_descriptor : 1;
-  uint8_t : 0;
+  uint8_t : 4;
 
   uint8_t page_code : 6;
   uint8_t page_control : 2;

+ 175 - 173
src/class/msc/msc_device.c

@@ -71,6 +71,7 @@ CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t _mscd_buf[CFG_TUD_MSC_EP_
 //--------------------------------------------------------------------+
 // INTERNAL OBJECT & FUNCTION DECLARATION
 //--------------------------------------------------------------------+
+static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize);
 static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc);
 static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc);
 
@@ -223,179 +224,6 @@ bool mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
   return true;
 }
 
-// return response's length (copied to buffer). Negative if it is not an built-in command or indicate Failed status (CSW)
-// In case of a failed status, sense key must be set for reason of failure
-int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize)
-{
-  (void) bufsize; // TODO refractor later
-  int32_t resplen;
-
-  switch ( scsi_cmd[0] )
-  {
-    case SCSI_CMD_TEST_UNIT_READY:
-      resplen = 0;
-      if ( !tud_msc_test_unit_ready_cb(lun) )
-      {
-        // Failed status response
-        resplen = - 1;
-
-        // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
-        if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
-      }
-    break;
-
-    case SCSI_CMD_START_STOP_UNIT:
-      resplen = 0;
-
-      if (tud_msc_start_stop_cb)
-      {
-        scsi_start_stop_unit_t const * start_stop = (scsi_start_stop_unit_t const *) scsi_cmd;
-        if ( !tud_msc_start_stop_cb(lun, start_stop->power_condition, start_stop->start, start_stop->load_eject) )
-        {
-          // Failed status response
-          resplen = - 1;
-
-          // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
-          if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
-        }
-      }
-    break;
-
-    case SCSI_CMD_READ_CAPACITY_10:
-    {
-      uint32_t block_count;
-      uint32_t block_size;
-      uint16_t block_size_u16;
-
-      tud_msc_capacity_cb(lun, &block_count, &block_size_u16);
-      block_size = (uint32_t) block_size_u16;
-
-      // Invalid block size/count from callback, possibly unit is not ready
-      // stall this request, set sense key to NOT READY
-      if (block_count == 0 || block_size == 0)
-      {
-        resplen = -1;
-
-        // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
-        if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
-      }else
-      {
-        scsi_read_capacity10_resp_t read_capa10;
-
-        read_capa10.last_lba = tu_htonl(block_count-1);
-        read_capa10.block_size = tu_htonl(block_size);
-
-        resplen = sizeof(read_capa10);
-        memcpy(buffer, &read_capa10, resplen);
-      }
-    }
-    break;
-
-    case SCSI_CMD_READ_FORMAT_CAPACITY:
-    {
-      scsi_read_format_capacity_data_t read_fmt_capa =
-      {
-          .list_length     = 8,
-          .block_num       = 0,
-          .descriptor_type = 2, // formatted media
-          .block_size_u16  = 0
-      };
-
-      uint32_t block_count;
-      uint16_t block_size;
-
-      tud_msc_capacity_cb(lun, &block_count, &block_size);
-
-      // Invalid block size/count from callback, possibly unit is not ready
-      // stall this request, set sense key to NOT READY
-      if (block_count == 0 || block_size == 0)
-      {
-        resplen = -1;
-
-        // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
-        if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
-      }else
-      {
-        read_fmt_capa.block_num = tu_htonl(block_count);
-        read_fmt_capa.block_size_u16 = tu_htons(block_size);
-
-        resplen = sizeof(read_fmt_capa);
-        memcpy(buffer, &read_fmt_capa, resplen);
-      }
-    }
-    break;
-
-    case SCSI_CMD_INQUIRY:
-    {
-      scsi_inquiry_resp_t inquiry_rsp =
-      {
-          .is_removable         = 1,
-          .version              = 2,
-          .response_data_format = 2,
-      };
-
-      // vendor_id, product_id, product_rev is space padded string
-      memset(inquiry_rsp.vendor_id  , ' ', sizeof(inquiry_rsp.vendor_id));
-      memset(inquiry_rsp.product_id , ' ', sizeof(inquiry_rsp.product_id));
-      memset(inquiry_rsp.product_rev, ' ', sizeof(inquiry_rsp.product_rev));
-
-      tud_msc_inquiry_cb(lun, inquiry_rsp.vendor_id, inquiry_rsp.product_id, inquiry_rsp.product_rev);
-
-      resplen = sizeof(inquiry_rsp);
-      memcpy(buffer, &inquiry_rsp, resplen);
-    }
-    break;
-
-    case SCSI_CMD_MODE_SENSE_6:
-    {
-      scsi_mode_sense6_resp_t mode_resp =
-      {
-          .data_len = 3,
-          .medium_type = 0,
-          .write_protected = false,
-          .reserved = 0,
-          .block_descriptor_len = 0  // no block descriptor are included
-      };
-
-      bool writable = true;
-      if (tud_msc_is_writable_cb) {
-          writable = tud_msc_is_writable_cb(lun);
-      }
-      mode_resp.write_protected = !writable;
-
-      resplen = sizeof(mode_resp);
-      memcpy(buffer, &mode_resp, resplen);
-    }
-    break;
-
-    case SCSI_CMD_REQUEST_SENSE:
-    {
-      scsi_sense_fixed_resp_t sense_rsp =
-      {
-          .response_code = 0x70,
-          .valid         = 1
-      };
-
-      sense_rsp.add_sense_len = sizeof(scsi_sense_fixed_resp_t) - 8;
-
-      sense_rsp.sense_key           = _mscd_itf.sense_key;
-      sense_rsp.add_sense_code      = _mscd_itf.add_sense_code;
-      sense_rsp.add_sense_qualifier = _mscd_itf.add_sense_qualifier;
-
-      resplen = sizeof(sense_rsp);
-      memcpy(buffer, &sense_rsp, resplen);
-
-      // Clear sense data after copy
-      tud_msc_set_sense(lun, 0, 0, 0);
-    }
-    break;
-
-    default: resplen = -1; break;
-  }
-
-  return resplen;
-}
-
 bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
 {
   mscd_interface_t* p_msc = &_mscd_itf;
@@ -640,6 +468,180 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
 /*------------------------------------------------------------------*/
 /* SCSI Command Process
  *------------------------------------------------------------------*/
+
+// return response's length (copied to buffer). Negative if it is not an built-in command or indicate Failed status (CSW)
+// In case of a failed status, sense key must be set for reason of failure
+static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize)
+{
+  (void) bufsize; // TODO refractor later
+  int32_t resplen;
+
+  switch ( scsi_cmd[0] )
+  {
+    case SCSI_CMD_TEST_UNIT_READY:
+      resplen = 0;
+      if ( !tud_msc_test_unit_ready_cb(lun) )
+      {
+        // Failed status response
+        resplen = - 1;
+
+        // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
+        if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
+      }
+    break;
+
+    case SCSI_CMD_START_STOP_UNIT:
+      resplen = 0;
+
+      if (tud_msc_start_stop_cb)
+      {
+        scsi_start_stop_unit_t const * start_stop = (scsi_start_stop_unit_t const *) scsi_cmd;
+        if ( !tud_msc_start_stop_cb(lun, start_stop->power_condition, start_stop->start, start_stop->load_eject) )
+        {
+          // Failed status response
+          resplen = - 1;
+
+          // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
+          if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
+        }
+      }
+    break;
+
+    case SCSI_CMD_READ_CAPACITY_10:
+    {
+      uint32_t block_count;
+      uint32_t block_size;
+      uint16_t block_size_u16;
+
+      tud_msc_capacity_cb(lun, &block_count, &block_size_u16);
+      block_size = (uint32_t) block_size_u16;
+
+      // Invalid block size/count from callback, possibly unit is not ready
+      // stall this request, set sense key to NOT READY
+      if (block_count == 0 || block_size == 0)
+      {
+        resplen = -1;
+
+        // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
+        if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
+      }else
+      {
+        scsi_read_capacity10_resp_t read_capa10;
+
+        read_capa10.last_lba = tu_htonl(block_count-1);
+        read_capa10.block_size = tu_htonl(block_size);
+
+        resplen = sizeof(read_capa10);
+        memcpy(buffer, &read_capa10, resplen);
+      }
+    }
+    break;
+
+    case SCSI_CMD_READ_FORMAT_CAPACITY:
+    {
+      scsi_read_format_capacity_data_t read_fmt_capa =
+      {
+          .list_length     = 8,
+          .block_num       = 0,
+          .descriptor_type = 2, // formatted media
+          .block_size_u16  = 0
+      };
+
+      uint32_t block_count;
+      uint16_t block_size;
+
+      tud_msc_capacity_cb(lun, &block_count, &block_size);
+
+      // Invalid block size/count from callback, possibly unit is not ready
+      // stall this request, set sense key to NOT READY
+      if (block_count == 0 || block_size == 0)
+      {
+        resplen = -1;
+
+        // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
+        if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
+      }else
+      {
+        read_fmt_capa.block_num = tu_htonl(block_count);
+        read_fmt_capa.block_size_u16 = tu_htons(block_size);
+
+        resplen = sizeof(read_fmt_capa);
+        memcpy(buffer, &read_fmt_capa, resplen);
+      }
+    }
+    break;
+
+    case SCSI_CMD_INQUIRY:
+    {
+      scsi_inquiry_resp_t inquiry_rsp =
+      {
+          .is_removable         = 1,
+          .version              = 2,
+          .response_data_format = 2,
+      };
+
+      // vendor_id, product_id, product_rev is space padded string
+      memset(inquiry_rsp.vendor_id  , ' ', sizeof(inquiry_rsp.vendor_id));
+      memset(inquiry_rsp.product_id , ' ', sizeof(inquiry_rsp.product_id));
+      memset(inquiry_rsp.product_rev, ' ', sizeof(inquiry_rsp.product_rev));
+
+      tud_msc_inquiry_cb(lun, inquiry_rsp.vendor_id, inquiry_rsp.product_id, inquiry_rsp.product_rev);
+
+      resplen = sizeof(inquiry_rsp);
+      memcpy(buffer, &inquiry_rsp, resplen);
+    }
+    break;
+
+    case SCSI_CMD_MODE_SENSE_6:
+    {
+      scsi_mode_sense6_resp_t mode_resp =
+      {
+          .data_len = 3,
+          .medium_type = 0,
+          .write_protected = false,
+          .reserved = 0,
+          .block_descriptor_len = 0  // no block descriptor are included
+      };
+
+      bool writable = true;
+      if (tud_msc_is_writable_cb) {
+          writable = tud_msc_is_writable_cb(lun);
+      }
+      mode_resp.write_protected = !writable;
+
+      resplen = sizeof(mode_resp);
+      memcpy(buffer, &mode_resp, resplen);
+    }
+    break;
+
+    case SCSI_CMD_REQUEST_SENSE:
+    {
+      scsi_sense_fixed_resp_t sense_rsp =
+      {
+          .response_code = 0x70,
+          .valid         = 1
+      };
+
+      sense_rsp.add_sense_len = sizeof(scsi_sense_fixed_resp_t) - 8;
+
+      sense_rsp.sense_key           = _mscd_itf.sense_key;
+      sense_rsp.add_sense_code      = _mscd_itf.add_sense_code;
+      sense_rsp.add_sense_qualifier = _mscd_itf.add_sense_qualifier;
+
+      resplen = sizeof(sense_rsp);
+      memcpy(buffer, &sense_rsp, resplen);
+
+      // Clear sense data after copy
+      tud_msc_set_sense(lun, 0, 0, 0);
+    }
+    break;
+
+    default: resplen = -1; break;
+  }
+
+  return resplen;
+}
+
 static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
 {
   msc_cbw_t const * p_cbw = &p_msc->cbw;

+ 1 - 0
src/common/tusb_compiler.h

@@ -102,6 +102,7 @@
   #define TU_BSWAP32(u32) (__builtin_bswap32(u32))
 
 #elif defined(__ICCARM__)
+  #include <intrinsics.h>
   #define TU_ATTR_ALIGNED(Bytes)        __attribute__ ((aligned(Bytes)))
   #define TU_ATTR_SECTION(sec_name)     __attribute__ ((section(#sec_name)))
   #define TU_ATTR_PACKED                __attribute__ ((packed))

+ 8 - 2
src/common/tusb_fifo.c

@@ -30,6 +30,12 @@
 #include "osal/osal.h"
 #include "tusb_fifo.h"
 
+// Supress IAR warning
+// Warning[Pa082]: undefined behavior: the order of volatile accesses is undefined in this statement
+#if defined(__ICCARM__)
+#pragma diag_suppress = Pa082
+#endif
+
 // implement mutex lock and unlock
 #if CFG_FIFO_MUTEX
 
@@ -106,7 +112,7 @@ static void _ff_push_n(tu_fifo_t* f, void const * data, uint16_t n, uint16_t wRe
     memcpy(f->buffer + (wRel * f->item_size), data, nLin*f->item_size);
 
     // Write data wrapped around
-    memcpy(f->buffer, data + nLin*f->item_size, (n - nLin) * f->item_size);
+    memcpy(f->buffer, ((uint8_t const*) data) + nLin*f->item_size, (n - nLin) * f->item_size);
   }
 }
 
@@ -131,7 +137,7 @@ static void _ff_pull_n(tu_fifo_t* f, void * p_buffer, uint16_t n, uint16_t rRel)
     memcpy(p_buffer, f->buffer + (rRel * f->item_size), nLin*f->item_size);
 
     // Read data wrapped part
-    memcpy(p_buffer + nLin*f->item_size, f->buffer, (n - nLin) * f->item_size);
+    memcpy((uint8_t*)p_buffer + nLin*f->item_size, f->buffer, (n - nLin) * f->item_size);
   }
 }
 

+ 21 - 1
src/device/usbd.c

@@ -517,7 +517,7 @@ void tud_task (void)
           TU_ASSERT(driver, );
 
           TU_LOG2("  %s xfer callback\r\n", driver->name);
-          driver->xfer_cb(event.rhport, ep_addr, event.xfer_complete.result, event.xfer_complete.len);
+          driver->xfer_cb(event.rhport, ep_addr, (xfer_result_t)event.xfer_complete.result, event.xfer_complete.len);
         }
       }
       break;
@@ -933,6 +933,7 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const
     break;
 
     case TUSB_DESC_STRING:
+    {
       TU_LOG2(" String[%u]\r\n", desc_index);
 
       // String Descriptor always uses the desc set from user
@@ -941,6 +942,7 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const
 
       // first byte of descriptor is its size
       return tud_control_xfer(rhport, p_request, (void*) desc_str, desc_str[0]);
+    }
     break;
 
     case TUSB_DESC_DEVICE_QUALIFIER:
@@ -1101,6 +1103,24 @@ bool usbd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep)
 {
   TU_LOG2("  Open EP %02X with Size = %u\r\n", desc_ep->bEndpointAddress, desc_ep->wMaxPacketSize.size);
 
+  if (TUSB_XFER_ISOCHRONOUS == desc_ep->bmAttributes.xfer)
+  {
+    TU_ASSERT(desc_ep->wMaxPacketSize.size <= (_usbd_dev.speed == TUSB_SPEED_HIGH ? 1024 : 1023));
+  }
+  else
+  {
+    uint16_t const max_epsize = (_usbd_dev.speed == TUSB_SPEED_HIGH ? 512 : 64);
+
+    if (TUSB_XFER_BULK == desc_ep->bmAttributes.xfer)
+    {
+      // Bulk must be EXACTLY 512/64 bytes
+      TU_ASSERT(desc_ep->wMaxPacketSize.size == max_epsize);
+    }else
+    {
+      TU_ASSERT(desc_ep->wMaxPacketSize.size <= max_epsize);
+    }
+  }
+
   return dcd_edpt_open(rhport, desc_ep);
 }
 

+ 8 - 0
src/device/usbd_control.c

@@ -140,6 +140,14 @@ bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const * request, vo
 // USBD API
 //--------------------------------------------------------------------+
 
+//--------------------------------------------------------------------+
+// Prototypes
+//--------------------------------------------------------------------+
+void usbd_control_reset(void);
+void usbd_control_set_request(tusb_control_request_t const *request);
+void usbd_control_set_complete_callback( usbd_control_xfer_cb_t fp );
+bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
+
 void usbd_control_reset(void)
 {
   tu_varclr(&_ctrl_xfer);

+ 6 - 0
src/host/usbh.c

@@ -151,6 +151,12 @@ tusb_device_state_t tuh_device_get_state (uint8_t const dev_addr)
   return (tusb_device_state_t) _usbh_devices[dev_addr].state;
 }
 
+tusb_speed_t tuh_device_get_speed (uint8_t const dev_addr)
+{
+  TU_ASSERT( dev_addr <= CFG_TUSB_HOST_DEVICE_MAX, TUSB_DEVICE_STATE_UNPLUG);
+  return _usbh_devices[dev_addr].speed;
+}
+
 void osal_task_delay(uint32_t msec)
 {
   (void) msec;

+ 1 - 0
src/host/usbh.h

@@ -86,6 +86,7 @@ extern void hcd_int_handler(uint8_t rhport);
 #define tuh_int_handler   hcd_int_handler
 
 tusb_device_state_t tuh_device_get_state (uint8_t dev_addr);
+tusb_speed_t tuh_device_get_speed (uint8_t dev_addr);
 static inline bool tuh_device_is_configured(uint8_t dev_addr)
 {
   return tuh_device_get_state(dev_addr) == TUSB_DEVICE_STATE_CONFIGURED;

+ 2 - 0
src/osal/osal.h

@@ -53,6 +53,8 @@ typedef void (*osal_task_func_t)( void * );
   #include "osal_freertos.h"
 #elif CFG_TUSB_OS == OPT_OS_MYNEWT
   #include "osal_mynewt.h"
+#elif CFG_TUSB_OS == OPT_OS_PICO
+  #include "osal_pico.h"
 #elif CFG_TUSB_OS == OPT_OS_CUSTOM
   #include "tusb_os_custom.h" // implemented by application
 #else

+ 192 - 0
src/osal/osal_pico.h

@@ -0,0 +1,192 @@
+/* 
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * 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 _TUSB_OSAL_PICO_H_
+#define _TUSB_OSAL_PICO_H_
+
+#include "pico/time.h"
+#include "pico/sem.h"
+#include "pico/mutex.h"
+#include "pico/critical_section.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// TASK API
+//--------------------------------------------------------------------+
+#ifndef RP2040_USB_HOST_MODE
+static inline void osal_task_delay(uint32_t msec)
+{
+    sleep_ms(msec);
+}
+#endif
+
+//--------------------------------------------------------------------+
+// Binary Semaphore API
+//--------------------------------------------------------------------+
+typedef struct semaphore osal_semaphore_def_t, *osal_semaphore_t;
+
+static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef)
+{
+  sem_init(semdef, 0, 255);
+  return semdef;
+}
+
+static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr)
+{
+  sem_release(sem_hdl);
+  return true;
+}
+
+static inline bool osal_semaphore_wait (osal_semaphore_t sem_hdl, uint32_t msec)
+{
+  return sem_acquire_timeout_ms(sem_hdl, msec);
+}
+
+static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl)
+{
+  sem_reset(sem_hdl, 0);
+}
+
+//--------------------------------------------------------------------+
+// MUTEX API
+// Within tinyusb, mutex is never used in ISR context
+//--------------------------------------------------------------------+
+typedef struct mutex osal_mutex_def_t, *osal_mutex_t;
+
+static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef)
+{
+    mutex_init(mdef);
+    return mdef;
+}
+
+static inline bool osal_mutex_lock (osal_mutex_t mutex_hdl, uint32_t msec)
+{
+    return mutex_enter_timeout_ms(mutex_hdl, msec);
+}
+
+static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl)
+{
+    mutex_exit(mutex_hdl);
+    return true;
+}
+
+//--------------------------------------------------------------------+
+// QUEUE API
+//--------------------------------------------------------------------+
+#include "common/tusb_fifo.h"
+
+#if TUSB_OPT_HOST_ENABLED
+extern void hcd_int_disable(uint8_t rhport);
+extern void hcd_int_enable(uint8_t rhport);
+#endif
+
+typedef struct
+{
+    tu_fifo_t ff;
+    struct critical_section critsec; // osal_queue may be used in IRQs, so need critical section
+} osal_queue_def_t;
+
+typedef osal_queue_def_t* osal_queue_t;
+
+// role device/host is used by OS NONE for mutex (disable usb isr) only
+#define OSAL_QUEUE_DEF(_role, _name, _depth, _type) \
+  uint8_t _name##_buf[_depth*sizeof(_type)];        \
+  osal_queue_def_t _name = {                        \
+    .ff = {                                         \
+      .buffer       = _name##_buf,                  \
+      .depth        = _depth,                       \
+      .item_size    = sizeof(_type),                \
+      .overwritable = false,                        \
+    }\
+  }
+
+// lock queue by disable USB interrupt
+static inline void _osal_q_lock(osal_queue_t qhdl)
+{
+    critical_section_enter_blocking(&qhdl->critsec);
+}
+
+// unlock queue
+static inline void _osal_q_unlock(osal_queue_t qhdl)
+{
+    critical_section_exit(&qhdl->critsec);
+}
+
+static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef)
+{
+  critical_section_init(&qdef->critsec);
+  tu_fifo_clear(&qdef->ff);
+  return (osal_queue_t) qdef;
+}
+
+static inline bool osal_queue_receive(osal_queue_t qhdl, void* data)
+{
+  // TODO: revisit... docs say that mutexes are never used from IRQ context,
+  //  however osal_queue_recieve may be. therefore my assumption is that
+  //  the fifo mutex is not populated for queues used from an IRQ context
+  assert(!qhdl->ff.mutex);
+
+  _osal_q_lock(qhdl);
+  bool success = tu_fifo_read(&qhdl->ff, data);
+  _osal_q_unlock(qhdl);
+
+  return success;
+}
+
+static inline bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr)
+{
+  // TODO: revisit... docs say that mutexes are never used from IRQ context,
+  //  however osal_queue_recieve may be. therefore my assumption is that
+  //  the fifo mutex is not populated for queues used from an IRQ context
+  assert(!qhdl->ff.mutex);
+
+  _osal_q_lock(qhdl);
+  bool success = tu_fifo_write(&qhdl->ff, data);
+  _osal_q_unlock(qhdl);
+
+  TU_ASSERT(success);
+
+  return success;
+}
+
+static inline bool osal_queue_empty(osal_queue_t qhdl)
+{
+  // TODO: revisit; whether this is true or not currently, tu_fifo_empty is a single
+  //  volatile read.
+
+  // Skip queue lock/unlock since this function is primarily called
+  // with interrupt disabled before going into low power mode
+  return tu_fifo_empty(&qhdl->ff);
+}
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_OSAL_PICO_H_ */

+ 3 - 4
src/portable/dialog/da146xx/dcd_da146xx.c

@@ -664,7 +664,7 @@ static void handle_bus_reset(void)
   (void)USB->USB_ALTEV_REG;
   _dcd.in_reset = true;
 
-  dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
+  dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
   USB->USB_DMA_CTRL_REG = 0;
 
   USB->USB_MAMSK_REG = USB_USB_MAMSK_REG_USB_M_INTR_Msk |
@@ -821,14 +821,13 @@ void dcd_disconnect(uint8_t rhport)
 
 bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
 {
+  (void)rhport;
+
   uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress);
   uint8_t const dir   = tu_edpt_dir(desc_edpt->bEndpointAddress);
   xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
   uint8_t iso_mask = 0;
-  
-  (void)rhport;
 
-  TU_ASSERT(desc_edpt->wMaxPacketSize.size <= 1023);
   TU_ASSERT(epnum < EP_MAX);
 
   xfer->max_packet_size = desc_edpt->wMaxPacketSize.size;

+ 0 - 1
src/portable/espressif/esp32s2/dcd_esp32s2.c

@@ -252,7 +252,6 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_edpt)
   uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress);
   uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress);
 
-  TU_ASSERT(desc_edpt->wMaxPacketSize.size <= 64);
   TU_ASSERT(epnum < EP_MAX);
 
   xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, dir);

+ 12 - 3
src/portable/microchip/samd/dcd_samd.c

@@ -37,14 +37,23 @@
 /* MACRO TYPEDEF CONSTANT ENUM
  *------------------------------------------------------------------*/
 static TU_ATTR_ALIGNED(4) UsbDeviceDescBank sram_registers[8][2];
-static TU_ATTR_ALIGNED(4) uint8_t _setup_packet[8];
+
+// Setup packet is only 8 bytes in length. However under certain scenario,
+// USB DMA controller may decide to overwrite/overflow the buffer  with
+// 2 extra bytes of CRC. From datasheet's "Management of SETUP Transactions" section
+//    If the number of received data bytes is the maximum data payload specified by
+//    PCKSIZE.SIZE minus one, only the first CRC data is written to the data buffer.
+//    If the number of received data is equal or less than the data payload specified
+//    by PCKSIZE.SIZE minus two, both CRC data bytes are written to the data buffer.
+// Therefore we will need to increase it to 10 bytes here.
+static TU_ATTR_ALIGNED(4) uint8_t _setup_packet[8+2];
 
 // ready for receiving SETUP packet
 static inline void prepare_setup(void)
 {
   // Only make sure the EP0 OUT buffer is ready
   sram_registers[0][0].ADDR.reg = (uint32_t) _setup_packet;
-  sram_registers[0][0].PCKSIZE.bit.MULTI_PACKET_SIZE = sizeof(_setup_packet);
+  sram_registers[0][0].PCKSIZE.bit.MULTI_PACKET_SIZE = sizeof(tusb_control_request_t);
   sram_registers[0][0].PCKSIZE.bit.BYTE_COUNT = 0;
 }
 
@@ -378,7 +387,7 @@ void dcd_int_handler (uint8_t rhport)
     USB->DEVICE.INTENCLR.reg = USB_DEVICE_INTFLAG_WAKEUP | USB_DEVICE_INTFLAG_SUSPEND;
 
     bus_reset();
-    dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
+    dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
   }
 
   // Handle SETUP packet

+ 1 - 1
src/portable/microchip/samg/dcd_samg.c

@@ -338,7 +338,7 @@ void dcd_int_handler(uint8_t rhport)
   if (intr_status & UDP_ISR_ENDBUSRES_Msk)
   {
     bus_reset();
-    dcd_event_bus_signal(rhport, DCD_EVENT_BUS_RESET, true);
+    dcd_event_bus_reset(rhport, TUSB_SPEED_FULL, true);
   }
 
   // SOF

+ 1 - 1
src/portable/nordic/nrf5x/dcd_nrf5x.c

@@ -533,7 +533,7 @@ void dcd_int_handler(uint8_t rhport)
   if ( int_status & USBD_INTEN_USBRESET_Msk )
   {
     bus_reset();
-    dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
+    dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
   }
 
   // ISOIN: Data was moved to endpoint buffer, client will be notified in SOF

+ 1 - 2
src/portable/nuvoton/nuc120/dcd_nuc120.c

@@ -329,8 +329,7 @@ void dcd_int_handler(uint8_t rhport)
       USBD->ATTR |= USBD_ATTR_USB_EN_Msk | USBD_ATTR_PHY_EN_Msk;
 
       bus_reset();
-
-      dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
+      dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
     }
 
     if(state & USBD_STATE_SUSPEND)

+ 1 - 1
src/portable/nuvoton/nuc121/dcd_nuc121.c

@@ -340,7 +340,7 @@ void dcd_int_handler(uint8_t rhport)
 
       bus_reset();
 
-      dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
+      dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
     }
 
     if(state & USBD_ATTR_SUSPEND_Msk)

+ 477 - 0
src/portable/nxp/khci/dcd_khci.c

@@ -0,0 +1,477 @@
+/* 
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Koji Kitayama
+ *
+ * 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 TUSB_OPT_DEVICE_ENABLED && ( CFG_TUSB_MCU == OPT_MCU_MKL25ZXX )
+
+#include "fsl_device_registers.h"
+#define KHCI        USB0
+
+#include "device/dcd.h"
+
+//--------------------------------------------------------------------+
+// MACRO TYPEDEF CONSTANT ENUM DECLARATION
+//--------------------------------------------------------------------+
+
+enum {
+  TOK_PID_OUT   = 0x1u,
+  TOK_PID_IN    = 0x9u,
+  TOK_PID_SETUP = 0xDu,
+};
+
+typedef struct TU_ATTR_PACKED
+{
+  union {
+    uint32_t head;
+    struct {
+      union {
+        struct {
+          uint16_t          :  2;
+          uint16_t tok_pid  :  4;
+          uint16_t data     :  1;
+          uint16_t own      :  1;
+          uint16_t          :  8;
+        };
+        struct {
+          uint16_t          :  2;
+          uint16_t bdt_stall:  1;
+          uint16_t dts      :  1;
+          uint16_t ninc     :  1;
+          uint16_t keep     :  1;
+          uint16_t          : 10;
+        };
+      };
+      uint16_t bc          : 10;
+      uint16_t             :  6;
+    };
+  };
+  uint8_t *addr;
+}buffer_descriptor_t;
+
+TU_VERIFY_STATIC( sizeof(buffer_descriptor_t) == 8, "size is not correct" );
+
+typedef struct TU_ATTR_PACKED
+{
+  union {
+    uint32_t state;
+    struct {
+      uint32_t max_packet_size :11;
+      uint32_t                 : 5;
+      uint32_t odd             : 1;
+      uint32_t                 :15;
+    };
+  };
+  uint16_t length;
+  uint16_t remaining;
+}endpoint_state_t;
+
+TU_VERIFY_STATIC( sizeof(endpoint_state_t) == 8, "size is not correct" );
+
+typedef struct
+{
+  union {
+    /* [#EP][OUT,IN][EVEN,ODD] */
+    buffer_descriptor_t bdt[16][2][2];
+    uint16_t            bda[512];
+  };
+  TU_ATTR_ALIGNED(4) union {
+    endpoint_state_t endpoint[16][2];
+    endpoint_state_t endpoint_unified[16 * 2];
+  };
+  uint8_t setup_packet[8];
+  uint8_t addr;
+}dcd_data_t;
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+// BDT(Buffer Descriptor Table) must be 256-byte aligned
+CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(512) static dcd_data_t _dcd;
+
+TU_VERIFY_STATIC( sizeof(_dcd.bdt) == 512, "size is not correct" );
+
+static void prepare_next_setup_packet(uint8_t rhport)
+{
+  const unsigned out_odd = _dcd.endpoint[0][0].odd;
+  const unsigned in_odd  = _dcd.endpoint[0][1].odd;
+  if (_dcd.bdt[0][0][out_odd].own) {
+    TU_LOG1("DCD fail to prepare the next SETUP %d %d\r\n", out_odd, in_odd);
+    return;
+  }
+  _dcd.bdt[0][0][out_odd].data     = 0;
+  _dcd.bdt[0][0][out_odd ^ 1].data = 1;
+  _dcd.bdt[0][1][in_odd].data      = 1;
+  _dcd.bdt[0][1][in_odd ^ 1].data  = 0;
+  dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_OUT),
+                _dcd.setup_packet, sizeof(_dcd.setup_packet));
+}
+
+static void process_stall(uint8_t rhport)
+{
+  if (KHCI->ENDPOINT[0].ENDPT & USB_ENDPT_EPSTALL_MASK) {
+    /* clear stall condition of the control pipe */
+    prepare_next_setup_packet(rhport);
+    KHCI->ENDPOINT[0].ENDPT &= ~USB_ENDPT_EPSTALL_MASK;
+  }
+}
+
+static void process_tokdne(uint8_t rhport)
+{
+  const unsigned s = KHCI->STAT;
+  KHCI->ISTAT = USB_ISTAT_TOKDNE_MASK; /* fetch the next token if received */
+  buffer_descriptor_t *bd = (buffer_descriptor_t *)&_dcd.bda[s];
+  endpoint_state_t    *ep = &_dcd.endpoint_unified[s >> 3];
+  unsigned odd = (s & USB_STAT_ODD_MASK) ? 1 : 0;
+
+  /* fetch pid before discarded by the next steps */
+  const unsigned pid = bd->tok_pid;
+  /* reset values for a next transfer */
+  bd->bdt_stall = 0;
+  bd->dts       = 1;
+  bd->ninc      = 0;
+  bd->keep      = 0;
+  /* update the odd variable to prepare for the next transfer */
+  ep->odd       = odd ^ 1;
+  if (pid == TOK_PID_SETUP) {
+    dcd_event_setup_received(rhport, bd->addr, true);
+    KHCI->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
+    return;
+  }
+  if (s >> 4) {
+    TU_LOG1("TKDNE %x\r\n", s);
+  }
+
+  const unsigned bc = bd->bc;
+  const unsigned remaining = ep->remaining - bc;
+  if (remaining && bc == ep->max_packet_size) {
+    /* continue the transferring consecutive data */
+    ep->remaining = remaining;
+    const int next_remaining = remaining - ep->max_packet_size;
+    if (next_remaining > 0) {
+      /* prepare to the after next transfer */
+      bd->addr += ep->max_packet_size * 2;
+      bd->bc    = next_remaining > ep->max_packet_size ? ep->max_packet_size: next_remaining;
+      __DSB();
+      bd->own   = 1; /* the own bit must set after addr */
+    }
+    return;
+  }
+  const unsigned length = ep->length;
+  dcd_event_xfer_complete(rhport,
+                          ((s & USB_STAT_TX_MASK) << 4) | (s >> USB_STAT_ENDP_SHIFT),
+                          length - remaining, XFER_RESULT_SUCCESS, true);
+  if (0 == (s & USB_STAT_ENDP_MASK) && 0 == length) {
+    /* After completion a ZLP of control transfer,
+     * it prepares for the next steup transfer. */
+    if (_dcd.addr) {
+      /* When the transfer was the SetAddress,
+       * the device address should be updated here. */
+      KHCI->ADDR = _dcd.addr;
+      _dcd.addr  = 0;
+    }
+    prepare_next_setup_packet(rhport);
+  }
+}
+
+static void process_bus_reset(uint8_t rhport)
+{
+  KHCI->USBCTRL &= ~USB_USBCTRL_SUSP_MASK;
+  KHCI->CTL     |= USB_CTL_ODDRST_MASK;
+  KHCI->ADDR     = 0;
+  KHCI->INTEN    = (KHCI->INTEN & ~USB_INTEN_RESUMEEN_MASK) | USB_INTEN_SLEEPEN_MASK;
+
+  KHCI->ENDPOINT[0].ENDPT = USB_ENDPT_EPHSHK_MASK | USB_ENDPT_EPRXEN_MASK | USB_ENDPT_EPTXEN_MASK;
+  for (unsigned i = 1; i < 16; ++i) {
+    KHCI->ENDPOINT[i].ENDPT = 0;
+  }
+  buffer_descriptor_t *bd = _dcd.bdt[0][0];
+  for (unsigned i = 0; i < sizeof(_dcd.bdt)/sizeof(*bd); ++i, ++bd) {
+    bd->head = 0;
+  }
+  const endpoint_state_t ep0 = {
+    .max_packet_size = CFG_TUD_ENDPOINT0_SIZE,
+    .odd             = 0,
+    .length          = 0,
+    .remaining       = 0,
+  };
+  _dcd.endpoint[0][0] = ep0;
+  _dcd.endpoint[0][1] = ep0;
+  tu_memclr(_dcd.endpoint[1], sizeof(_dcd.endpoint) - sizeof(_dcd.endpoint[0]));
+  _dcd.addr = 0;
+  prepare_next_setup_packet(rhport);
+  KHCI->CTL &= ~USB_CTL_ODDRST_MASK;
+  dcd_event_bus_reset(rhport, TUSB_SPEED_FULL, true);
+}
+
+static void process_bus_inactive(uint8_t rhport)
+{
+  (void) rhport;
+  const unsigned inten = KHCI->INTEN;
+  KHCI->INTEN    = (inten & ~USB_INTEN_SLEEPEN_MASK) | USB_INTEN_RESUMEEN_MASK;
+  KHCI->USBCTRL |= USB_USBCTRL_SUSP_MASK;
+  dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
+}
+
+static void process_bus_active(uint8_t rhport)
+{
+  (void) rhport;
+  KHCI->USBCTRL &= ~USB_USBCTRL_SUSP_MASK;
+  const unsigned inten = KHCI->INTEN;
+  KHCI->INTEN    = (inten & ~USB_INTEN_RESUMEEN_MASK) | USB_INTEN_SLEEPEN_MASK;
+  dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
+}
+
+/*------------------------------------------------------------------*/
+/* Device API
+ *------------------------------------------------------------------*/
+void dcd_init(uint8_t rhport)
+{
+  (void) rhport;
+
+  KHCI->USBTRC0 |= USB_USBTRC0_USBRESET_MASK;
+  while (KHCI->USBTRC0 & USB_USBTRC0_USBRESET_MASK);
+  tu_memclr(&_dcd, sizeof(_dcd));
+  KHCI->USBTRC0 |= TU_BIT(6); /* software must set this bit to 1 */
+  KHCI->BDTPAGE1 = (uint8_t)((uintptr_t)_dcd.bdt >>  8);
+  KHCI->BDTPAGE2 = (uint8_t)((uintptr_t)_dcd.bdt >> 16);
+  KHCI->BDTPAGE3 = (uint8_t)((uintptr_t)_dcd.bdt >> 24);
+
+  dcd_connect(rhport);
+  NVIC_ClearPendingIRQ(USB0_IRQn);
+}
+
+void dcd_int_enable(uint8_t rhport)
+{
+  (void) rhport;
+  KHCI->INTEN = USB_INTEN_USBRSTEN_MASK | USB_INTEN_TOKDNEEN_MASK |
+    USB_INTEN_SLEEPEN_MASK | USB_INTEN_ERROREN_MASK | USB_INTEN_STALLEN_MASK;
+  NVIC_EnableIRQ(USB0_IRQn);
+}
+
+void dcd_int_disable(uint8_t rhport)
+{
+  (void) rhport;
+  NVIC_DisableIRQ(USB0_IRQn);
+  KHCI->INTEN = 0;
+}
+
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
+{
+  (void) rhport;
+  _dcd.addr = dev_addr & 0x7F;
+  /* Response with status first before changing device address */
+  dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
+}
+
+void dcd_remote_wakeup(uint8_t rhport)
+{
+  (void) rhport;
+  unsigned cnt = SystemCoreClock / 100;
+  KHCI->CTL |= USB_CTL_RESUME_MASK;
+  while (cnt--) __NOP();
+  KHCI->CTL &= ~USB_CTL_RESUME_MASK;
+}
+
+void dcd_connect(uint8_t rhport)
+{
+  (void) rhport;
+  KHCI->USBCTRL  = 0;
+  KHCI->CONTROL |= USB_CONTROL_DPPULLUPNONOTG_MASK;
+  KHCI->CTL     |= USB_CTL_USBENSOFEN_MASK;
+}
+
+void dcd_disconnect(uint8_t rhport)
+{
+  (void) rhport;
+  KHCI->CTL      = 0;
+  KHCI->CONTROL &= ~USB_CONTROL_DPPULLUPNONOTG_MASK;
+}
+
+//--------------------------------------------------------------------+
+// Endpoint API
+//--------------------------------------------------------------------+
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
+{
+  (void) rhport;
+
+  const unsigned ep_addr  = ep_desc->bEndpointAddress;
+  const unsigned epn      = ep_addr & 0xFu;
+  const unsigned dir      = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
+  const unsigned xfer     = ep_desc->bmAttributes.xfer;
+  endpoint_state_t *ep    = &_dcd.endpoint[epn][dir];
+  const unsigned odd      = ep->odd;
+  buffer_descriptor_t *bd = &_dcd.bdt[epn][dir][0];
+
+  /* No support for control transfer */
+  TU_ASSERT(epn && (xfer != TUSB_XFER_CONTROL));
+
+  ep->max_packet_size = ep_desc->wMaxPacketSize.size;
+  unsigned val = USB_ENDPT_EPCTLDIS_MASK;
+  val |= (xfer != TUSB_XFER_ISOCHRONOUS) ? USB_ENDPT_EPHSHK_MASK: 0;
+  val |= dir ? USB_ENDPT_EPTXEN_MASK : USB_ENDPT_EPRXEN_MASK;
+  KHCI->ENDPOINT[epn].ENDPT |= val;
+
+  if (xfer != TUSB_XFER_ISOCHRONOUS) {
+    bd[odd].dts      = 1;
+    bd[odd].data     = 0;
+    bd[odd ^ 1].dts  = 1;
+    bd[odd ^ 1].data = 1;
+  }
+
+  return true;
+}
+
+void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
+{
+  (void) rhport;
+
+  const unsigned epn      = ep_addr & 0xFu;
+  const unsigned dir      = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
+  endpoint_state_t *ep    = &_dcd.endpoint[epn][dir];
+  buffer_descriptor_t *bd = &_dcd.bdt[epn][dir][0];
+  const unsigned msk      = dir ? USB_ENDPT_EPTXEN_MASK : USB_ENDPT_EPRXEN_MASK;
+  KHCI->ENDPOINT[epn].ENDPT &= ~msk;
+  ep->max_packet_size = 0;
+  ep->length          = 0;
+  ep->remaining       = 0;
+  bd->head            = 0;
+}
+
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes)
+{
+  (void) rhport;
+  NVIC_DisableIRQ(USB0_IRQn);
+  const unsigned epn = ep_addr & 0xFu;
+  const unsigned dir = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
+  endpoint_state_t    *ep = &_dcd.endpoint[epn][dir];
+  buffer_descriptor_t *bd = &_dcd.bdt[epn][dir][ep->odd];
+
+  if (bd->own) {
+    TU_LOG1("DCD XFER fail %x %d %lx %lx\r\n", ep_addr, total_bytes, ep->state, bd->head);
+    return false; /* The last transfer has not completed */
+  }
+  ep->length    = total_bytes;
+  ep->remaining = total_bytes;
+
+  const unsigned mps = ep->max_packet_size;
+  if (total_bytes > mps) {
+    buffer_descriptor_t *next = ep->odd ? bd - 1: bd + 1;
+    /* When total_bytes is greater than the max packet size,
+     * it prepares to the next transfer to avoid NAK in advance. */
+    next->bc   = total_bytes >= 2 * mps ? mps: total_bytes - mps;
+    next->addr = buffer + mps;
+    next->own  = 1;
+  }
+  bd->bc        = total_bytes >= mps ? mps: total_bytes;
+  bd->addr      = buffer;
+  __DSB();
+  bd->own  = 1; /* the own bit must set after addr */
+  NVIC_EnableIRQ(USB0_IRQn);
+  return true;
+}
+
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
+{
+  (void) rhport;
+  const unsigned epn = ep_addr & 0xFu;
+  if (0 == epn) {
+    KHCI->ENDPOINT[epn].ENDPT |=  USB_ENDPT_EPSTALL_MASK;
+  } else {
+    const unsigned dir      = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
+    buffer_descriptor_t *bd = _dcd.bdt[epn][dir];
+    bd[0].bdt_stall = 1;
+    bd[1].bdt_stall = 1;
+  }
+}
+
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
+{
+  (void) rhport;
+  const unsigned epn      = ep_addr & 0xFu;
+  const unsigned dir      = (ep_addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
+  const unsigned odd      = _dcd.endpoint[epn][dir].odd;
+  buffer_descriptor_t *bd = _dcd.bdt[epn][dir];
+
+  bd[odd ^ 1].own       = 0;
+  bd[odd ^ 1].data      = 1;
+  bd[odd ^ 1].bdt_stall = 0;
+  bd[odd].own           = 0;
+  bd[odd].data          = 0;
+  bd[odd].bdt_stall     = 0;
+}
+
+//--------------------------------------------------------------------+
+// ISR
+//--------------------------------------------------------------------+
+void dcd_int_handler(uint8_t rhport)
+{
+  (void) rhport;
+
+  uint32_t is  = KHCI->ISTAT;
+  uint32_t msk = KHCI->INTEN;
+  KHCI->ISTAT = is & ~msk;
+  is &= msk;
+  if (is & USB_ISTAT_ERROR_MASK) {
+    /* TODO: */
+    uint32_t es = KHCI->ERRSTAT;
+    KHCI->ERRSTAT = es;
+    KHCI->ISTAT   = is; /* discard any pending events */
+    return;
+  }
+
+  if (is & USB_ISTAT_USBRST_MASK) {
+    KHCI->ISTAT = is; /* discard any pending events */
+    process_bus_reset(rhport);
+    return;
+  }
+  if (is & USB_ISTAT_SLEEP_MASK) {
+    KHCI->ISTAT = USB_ISTAT_SLEEP_MASK;
+    process_bus_inactive(rhport);
+    return;
+  }
+  if (is & USB_ISTAT_RESUME_MASK) {
+    KHCI->ISTAT = USB_ISTAT_RESUME_MASK;
+    process_bus_active(rhport);
+    return;
+  }
+  if (is & USB_ISTAT_SOFTOK_MASK) {
+    KHCI->ISTAT = USB_ISTAT_SOFTOK_MASK;
+    dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
+    return;
+  }
+  if (is & USB_ISTAT_STALL_MASK) {
+    KHCI->ISTAT = USB_ISTAT_STALL_MASK;
+    process_stall(rhport);
+    return;
+  }
+  if (is & USB_ISTAT_TOKDNE_MASK) {
+    process_tokdne(rhport);
+    return;
+  }
+}
+
+#endif

+ 1 - 1
src/portable/nxp/lpc17_40/dcd_lpc17_40.c

@@ -471,7 +471,7 @@ static void bus_event_isr(uint8_t rhport)
   if (dev_status & SIE_DEV_STATUS_RESET_MASK)
   {
     bus_reset();
-    dcd_event_bus_signal(rhport, DCD_EVENT_BUS_RESET, true);
+    dcd_event_bus_reset(rhport, TUSB_SPEED_FULL, true);
   }
 
   if (dev_status & SIE_DEV_STATUS_CONNECT_CHANGE_MASK)

+ 2 - 2
src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c

@@ -243,7 +243,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
   (void) rhport;
 
   // TODO not support ISO yet
-  if (p_endpoint_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) return false;
+  TU_VERIFY(p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS);
 
   //------------- Prepare Queue Head -------------//
   uint8_t ep_id = ep_addr2id(p_endpoint_desc->bEndpointAddress);
@@ -357,7 +357,7 @@ void dcd_int_handler(uint8_t rhport)
     if ( cmd_stat & CMDSTAT_RESET_CHANGE_MASK) // bus reset
     {
       bus_reset();
-      dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
+      dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
     }
 
     if (cmd_stat & CMDSTAT_CONNECT_CHANGE_MASK)

+ 482 - 0
src/portable/raspberrypi/rp2040/dcd_rp2040.c

@@ -0,0 +1,482 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * 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 TUSB_OPT_DEVICE_ENABLED && CFG_TUSB_MCU == OPT_MCU_RP2040
+
+#include "pico.h"
+#include "rp2040_usb.h"
+
+#if TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX
+#include "pico/fix/rp2040_usb_device_enumeration.h"
+#endif
+
+
+#include "device/dcd.h"
+
+#include "pico/stdlib.h"
+
+/*------------------------------------------------------------------*/
+/* Low level controller
+ *------------------------------------------------------------------*/
+
+#define usb_hw_set hw_set_alias(usb_hw)
+#define usb_hw_clear hw_clear_alias(usb_hw)
+
+// Init these in dcd_init
+static uint8_t assigned_address;
+static uint8_t *next_buffer_ptr;
+
+// Endpoints 0-15, direction 0 for out and 1 for in.
+static struct hw_endpoint hw_endpoints[16][2] = {0};
+
+static inline struct hw_endpoint *hw_endpoint_get_by_num(uint8_t num, uint8_t in)
+{
+    return &hw_endpoints[num][in];
+}
+
+static struct hw_endpoint *hw_endpoint_get_by_addr(uint8_t ep_addr)
+{
+    uint8_t num = tu_edpt_number(ep_addr);
+    uint8_t in = (ep_addr & TUSB_DIR_IN_MASK) ? 1 : 0;
+    return hw_endpoint_get_by_num(num, in);
+}
+static void _hw_endpoint_alloc(struct hw_endpoint *ep)
+{
+    uint size = 64;
+    if (ep->wMaxPacketSize > 64)
+    {
+        size = ep->wMaxPacketSize;
+    }
+
+    // Assumes single buffered for now
+    ep->hw_data_buf = next_buffer_ptr;
+    next_buffer_ptr += size;
+    // Bits 0-5 are ignored by the controller so make sure these are 0
+    if ((uintptr_t)next_buffer_ptr & 0b111111u)
+    {
+        // Round up to the next 64
+        uint32_t fixptr = (uintptr_t)next_buffer_ptr;
+        fixptr &= ~0b111111u;
+        fixptr += 64;
+        pico_info("Rounding non 64 byte boundary buffer up from %x to %x\n", (uintptr_t)next_buffer_ptr, fixptr);
+        next_buffer_ptr = (uint8_t*)fixptr;
+    }
+    assert(((uintptr_t)next_buffer_ptr & 0b111111u) == 0);
+    uint dpram_offset = hw_data_offset(ep->hw_data_buf);
+    assert(hw_data_offset(next_buffer_ptr) <= USB_DPRAM_MAX);
+
+    pico_info("Alloced %d bytes at offset 0x%x (0x%p) for ep %d %s\n",
+                size,
+                dpram_offset,
+                ep->hw_data_buf,
+                ep->num,
+                ep_dir_string[ep->in]);
+
+    // Fill in endpoint control register with buffer offset
+    uint32_t reg =  EP_CTRL_ENABLE_BITS
+                  | EP_CTRL_INTERRUPT_PER_BUFFER
+                  | (ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB)
+                  | dpram_offset;
+
+    *ep->endpoint_control = reg;
+}
+
+static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t ep_addr, uint wMaxPacketSize, uint8_t transfer_type)
+{
+    uint8_t num = tu_edpt_number(ep_addr);
+    bool in = ep_addr & TUSB_DIR_IN_MASK;
+    ep->ep_addr = ep_addr;
+    ep->in = in;
+    // For device, IN is a tx transfer and OUT is an rx transfer
+    ep->rx = in == false;
+    ep->num = num;
+    // Response to a setup packet on EP0 starts with pid of 1
+    ep->next_pid = num == 0 ? 1u : 0u;
+
+    // Add some checks around the max packet size
+    if (transfer_type == TUSB_XFER_ISOCHRONOUS)
+    {
+        if (wMaxPacketSize > USB_MAX_ISO_PACKET_SIZE)
+        {
+            panic("Isochronous wMaxPacketSize %d too large", wMaxPacketSize);
+        }
+    }
+    else
+    {
+        if (wMaxPacketSize > USB_MAX_PACKET_SIZE)
+        {
+            panic("Isochronous wMaxPacketSize %d too large", wMaxPacketSize);
+        }
+    }
+
+    ep->wMaxPacketSize = wMaxPacketSize;
+    ep->transfer_type = transfer_type;
+
+    // Every endpoint has a buffer control register in dpram
+    if (ep->in)
+    {
+        ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].in;
+    }
+    else
+    {
+        ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].out;
+    }
+
+    // Clear existing buffer control state
+    *ep->buffer_control = 0;
+
+    if (ep->num == 0)
+    {
+        // EP0 has no endpoint control register because
+        // the buffer offsets are fixed
+        ep->endpoint_control = NULL;
+
+        // Buffer offset is fixed
+        ep->hw_data_buf = (uint8_t*)&usb_dpram->ep0_buf_a[0];
+    }
+    else
+    {
+        // Set the endpoint control register (starts at EP1, hence num-1)
+        if (in)
+        {
+            ep->endpoint_control = &usb_dpram->ep_ctrl[num-1].in;
+        }
+        else
+        {
+            ep->endpoint_control = &usb_dpram->ep_ctrl[num-1].out;
+        }
+
+        // Now alloc a buffer and fill in endpoint control register
+        _hw_endpoint_alloc(ep);
+    }
+
+    ep->configured = true;
+}
+
+#if 0 // todo unused
+static void _hw_endpoint_close(struct hw_endpoint *ep)
+{
+    // Clear hardware registers and then zero the struct
+    // Clears endpoint enable
+    *ep->endpoint_control = 0;
+    // Clears buffer available, etc
+    *ep->buffer_control = 0;
+    // Clear any endpoint state
+    memset(ep, 0, sizeof(struct hw_endpoint));
+}
+
+static void hw_endpoint_close(uint8_t ep_addr)
+{
+    struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
+    _hw_endpoint_close(ep);
+}
+#endif
+
+static void hw_endpoint_init(uint8_t ep_addr, uint wMaxPacketSize, uint8_t bmAttributes)
+{
+    struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
+    _hw_endpoint_init(ep, ep_addr, wMaxPacketSize, bmAttributes);
+}
+
+static void hw_endpoint_xfer(uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes, bool start)
+{
+    struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
+    _hw_endpoint_xfer(ep, buffer, total_bytes, start);
+}
+
+static void hw_handle_buff_status(void)
+{
+    uint32_t remaining_buffers = usb_hw->buf_status;
+    pico_trace("buf_status 0x%08x\n", remaining_buffers);
+    uint bit = 1u;
+    for (uint i = 0; remaining_buffers && i < USB_MAX_ENDPOINTS * 2; i++)
+    {
+        if (remaining_buffers & bit)
+        {
+            uint __unused which = (usb_hw->buf_cpu_should_handle & bit) ? 1 : 0;
+            // Should be single buffered
+            assert(which == 0);
+            // clear this in advance
+            usb_hw_clear->buf_status = bit;
+            // IN transfer for even i, OUT transfer for odd i
+            struct hw_endpoint *ep = hw_endpoint_get_by_num(i >> 1u, !(i & 1u));
+            // Continue xfer
+            bool done = _hw_endpoint_xfer_continue(ep);
+            if (done)
+            {
+                // Notify
+                dcd_event_xfer_complete(0, ep->ep_addr, ep->len, XFER_RESULT_SUCCESS, true);
+                hw_endpoint_reset_transfer(ep);
+            }
+            remaining_buffers &= ~bit;
+        }
+        bit <<= 1u;
+    }
+}
+
+static void reset_ep0(void)
+{
+    // If we have finished this transfer on EP0 set pid back to 1 for next
+    // setup transfer. Also clear a stall in case
+    uint8_t addrs[] = {0x0, 0x80};
+    for (uint i = 0 ; i < count_of(addrs); i++)
+    {
+        struct hw_endpoint *ep = hw_endpoint_get_by_addr(addrs[i]);
+        ep->next_pid = 1u;
+        ep->stalled  = 0;
+    }
+}
+
+static void ep0_0len_status(void)
+{
+    // Send 0len complete response on EP0 IN
+    reset_ep0();
+    hw_endpoint_xfer(0x80, NULL, 0, true);
+}
+
+static void _hw_endpoint_stall(struct hw_endpoint *ep)
+{
+    assert(!ep->stalled);
+    if (ep->num == 0)
+    {
+        // A stall on EP0 has to be armed so it can be cleared on the next setup packet
+        usb_hw_set->ep_stall_arm = ep->in ? USB_EP_STALL_ARM_EP0_IN_BITS : USB_EP_STALL_ARM_EP0_OUT_BITS;
+    }
+    _hw_endpoint_buffer_control_set_mask32(ep, USB_BUF_CTRL_STALL);
+    ep->stalled = true;
+}
+
+static void hw_endpoint_stall(uint8_t ep_addr)
+{
+    struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
+    _hw_endpoint_stall(ep);
+}
+
+static void _hw_endpoint_clear_stall(struct hw_endpoint *ep)
+{
+    if (ep->num == 0)
+    {
+        // Probably already been cleared but no harm
+        usb_hw_clear->ep_stall_arm = ep->in ? USB_EP_STALL_ARM_EP0_IN_BITS : USB_EP_STALL_ARM_EP0_OUT_BITS;
+    }
+    _hw_endpoint_buffer_control_clear_mask32(ep, USB_BUF_CTRL_STALL);
+    ep->stalled = false;
+}
+
+static void hw_endpoint_clear_stall(uint8_t ep_addr)
+{
+    struct hw_endpoint *ep = hw_endpoint_get_by_addr(ep_addr);
+    _hw_endpoint_clear_stall(ep);
+}
+
+static void dcd_rp2040_irq(void)
+{
+    uint32_t status = usb_hw->ints;
+    uint32_t handled = 0;
+
+    if (status & USB_INTS_SETUP_REQ_BITS)
+    {
+        handled |= USB_INTS_SETUP_REQ_BITS;
+        uint8_t const *setup = (uint8_t const *)&usb_dpram->setup_packet;
+        // Clear stall bits and reset pid
+        reset_ep0();
+        // Pass setup packet to tiny usb
+        dcd_event_setup_received(0, setup, true);
+        usb_hw_clear->sie_status = USB_SIE_STATUS_SETUP_REC_BITS;
+    }
+
+    if (status & USB_INTS_BUFF_STATUS_BITS)
+    {
+        handled |= USB_INTS_BUFF_STATUS_BITS;
+        hw_handle_buff_status();
+    }
+
+    if (status & USB_INTS_BUS_RESET_BITS)
+    {
+        pico_trace("BUS RESET (addr %d -> %d)\n", assigned_address, 0);
+        assigned_address = 0;
+        usb_hw->dev_addr_ctrl = assigned_address;
+        handled |= USB_INTS_BUS_RESET_BITS;
+        dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
+        usb_hw_clear->sie_status = USB_SIE_STATUS_BUS_RESET_BITS;
+#if TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX
+        rp2040_usb_device_enumeration_fix();
+#endif
+    }
+
+    if (status ^ handled)
+    {
+        panic("Unhandled IRQ 0x%x\n", (uint) (status ^ handled));
+    }
+}
+
+#define USB_INTS_ERROR_BITS ( \
+    USB_INTS_ERROR_DATA_SEQ_BITS      |  \
+    USB_INTS_ERROR_BIT_STUFF_BITS     |  \
+    USB_INTS_ERROR_CRC_BITS           |  \
+    USB_INTS_ERROR_RX_OVERFLOW_BITS   |  \
+    USB_INTS_ERROR_RX_TIMEOUT_BITS)
+
+/*------------------------------------------------------------------*/
+/* Controller API
+ *------------------------------------------------------------------*/
+void dcd_init (uint8_t rhport)
+{
+    pico_trace("dcd_init %d\n", rhport);
+    assert(rhport == 0);
+
+    // Reset hardware to default state
+    rp2040_usb_init();
+
+    irq_set_exclusive_handler(USBCTRL_IRQ, dcd_rp2040_irq);
+    memset(hw_endpoints, 0, sizeof(hw_endpoints));
+    assigned_address = 0;
+    next_buffer_ptr = &usb_dpram->epx_data[0];
+
+    // EP0 always exists so init it now
+    // EP0 OUT
+    hw_endpoint_init(0x0, 64, 0);
+    // EP0 IN
+    hw_endpoint_init(0x80, 64, 0);
+
+    // Initializes the USB peripheral for device mode and enables it.
+    // Don't need to enable the pull up here. Force VBUS
+    usb_hw->main_ctrl = USB_MAIN_CTRL_CONTROLLER_EN_BITS;
+
+    // Enable individual controller IRQS here. Processor interrupt enable will be used
+    // for the global interrupt enable...
+    usb_hw->sie_ctrl = USB_SIE_CTRL_EP0_INT_1BUF_BITS; 
+    usb_hw->inte     = USB_INTS_BUFF_STATUS_BITS | USB_INTS_BUS_RESET_BITS | USB_INTS_SETUP_REQ_BITS;
+
+    dcd_connect(rhport);
+}
+
+void dcd_int_enable(uint8_t rhport)
+{
+    assert(rhport == 0);
+    irq_set_enabled(USBCTRL_IRQ, true);
+}
+
+void dcd_int_disable(uint8_t rhport)
+{
+    assert(rhport == 0);
+    irq_set_enabled(USBCTRL_IRQ, false);
+}
+
+void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
+{
+    pico_trace("dcd_set_address %d %d\n", rhport, dev_addr);
+    assert(rhport == 0);
+
+    // Can't set device address in hardware until status xfer has complete
+    assigned_address = dev_addr;
+
+    ep0_0len_status();
+}
+
+void dcd_remote_wakeup(uint8_t rhport)
+{
+    panic("dcd_remote_wakeup %d\n", rhport);
+    assert(rhport == 0);
+}
+
+// disconnect by disabling internal pull-up resistor on D+/D-
+void dcd_disconnect(uint8_t rhport)
+{
+    pico_info("dcd_disconnect %d\n", rhport);
+    assert(rhport == 0);
+    usb_hw_clear->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS;
+}
+
+// connect by enabling internal pull-up resistor on D+/D-
+void dcd_connect(uint8_t rhport)
+{
+    pico_info("dcd_connect %d\n", rhport);
+    assert(rhport == 0);
+    usb_hw_set->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS;
+}
+
+/*------------------------------------------------------------------*/
+/* DCD Endpoint port
+ *------------------------------------------------------------------*/
+
+void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request)
+{
+    pico_trace("dcd_edpt0_status_complete %d\n", rhport);
+    assert(rhport == 0);
+
+    if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
+        request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
+        request->bRequest == TUSB_REQ_SET_ADDRESS)
+    {
+        pico_trace("Set HW address %d\n", assigned_address);
+        usb_hw->dev_addr_ctrl = assigned_address;
+    }
+
+    reset_ep0();
+}
+
+bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
+{
+    pico_info("dcd_edpt_open %d %02x\n", rhport, desc_edpt->bEndpointAddress);
+    assert(rhport == 0);
+    hw_endpoint_init(desc_edpt->bEndpointAddress, desc_edpt->wMaxPacketSize.size, desc_edpt->bmAttributes.xfer);
+    return true;
+}
+
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
+{
+    assert(rhport == 0);
+    // True means start new xfer
+    hw_endpoint_xfer(ep_addr, buffer, total_bytes, true);
+    return true;
+}
+
+void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
+{
+    pico_trace("dcd_edpt_stall %d %02x\n", rhport, ep_addr);
+    assert(rhport == 0);
+    hw_endpoint_stall(ep_addr);
+}
+
+void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
+{
+    pico_trace("dcd_edpt_clear_stall %d %02x\n", rhport, ep_addr);
+    assert(rhport == 0);
+    hw_endpoint_clear_stall(ep_addr);
+}
+
+
+void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr)
+{
+    // usbd.c says: In progress transfers on this EP may be delivered after this call
+    pico_trace("dcd_edpt_close %d %02x\n", rhport, ep_addr);
+
+}
+
+#endif

+ 549 - 0
src/portable/raspberrypi/rp2040/hcd_rp2040.c

@@ -0,0 +1,549 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * 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 TUSB_OPT_HOST_ENABLED && CFG_TUSB_MCU == OPT_MCU_RP2040
+
+#include "pico.h"
+#include "rp2040_usb.h"
+
+//--------------------------------------------------------------------+
+// INCLUDE
+//--------------------------------------------------------------------+
+#include "osal/osal.h"
+
+#include "host/hcd.h"
+#include "host/usbh.h"
+#include "host/usbh_hcd.h"
+
+#define ROOT_PORT 0
+
+//--------------------------------------------------------------------+
+// Low level rp2040 controller functions
+//--------------------------------------------------------------------+
+
+#ifndef PICO_USB_HOST_INTERRUPT_ENDPOINTS
+#define PICO_USB_HOST_INTERRUPT_ENDPOINTS (USB_MAX_ENDPOINTS - 1)
+#endif
+static_assert(PICO_USB_HOST_INTERRUPT_ENDPOINTS <= USB_MAX_ENDPOINTS, "");
+
+// Host mode uses one shared endpoint register for non-interrupt endpoint
+struct hw_endpoint eps[1 + PICO_USB_HOST_INTERRUPT_ENDPOINTS];
+#define epx (eps[0])
+
+#define usb_hw_set hw_set_alias(usb_hw)
+#define usb_hw_clear hw_clear_alias(usb_hw)
+
+// Used for hcd pipe busy.
+// todo still a bit wasteful
+// top bit set if valid
+uint8_t dev_ep_map[CFG_TUSB_HOST_DEVICE_MAX][1 + PICO_USB_HOST_INTERRUPT_ENDPOINTS][2];
+
+// Flags we set by default in sie_ctrl (we add other bits on top)
+static uint32_t sie_ctrl_base = USB_SIE_CTRL_SOF_EN_BITS | 
+                                USB_SIE_CTRL_KEEP_ALIVE_EN_BITS | 
+                                USB_SIE_CTRL_PULLDOWN_EN_BITS | 
+                                USB_SIE_CTRL_EP0_INT_1BUF_BITS;
+
+static struct hw_endpoint *get_dev_ep(uint8_t dev_addr, uint8_t ep_addr)
+{
+    uint8_t num = tu_edpt_number(ep_addr);
+    if (num == 0) {
+        return &epx;
+    }
+    uint8_t in = (ep_addr & TUSB_DIR_IN_MASK) ? 1 : 0;
+    uint mapping = dev_ep_map[dev_addr-1][num][in];
+    pico_trace("Get dev addr %d ep %d = %d\n", dev_addr, ep_addr, mapping);
+    return mapping >= 128 ? eps + (mapping & 0x7fu) : NULL;
+}
+
+static void set_dev_ep(uint8_t dev_addr, uint8_t ep_addr, struct hw_endpoint *ep)
+{
+    uint8_t num = tu_edpt_number(ep_addr);
+    uint8_t in = (ep_addr & TUSB_DIR_IN_MASK) ? 1 : 0;
+    uint32_t index = ep - eps;
+    hard_assert(index < count_of(eps));
+    // todo revisit why dev_addr can be 0 here
+    if (dev_addr) {
+        dev_ep_map[dev_addr-1][num][in] = 128u | index;
+    }
+    pico_trace("Set dev addr %d ep %d = %d\n", dev_addr, ep_addr, index);
+}
+
+static inline uint8_t dev_speed(void)
+{
+    return (usb_hw->sie_status & USB_SIE_STATUS_SPEED_BITS) >> USB_SIE_STATUS_SPEED_LSB;
+}
+
+static bool need_pre(uint8_t dev_addr)
+{
+    // If this device is different to the speed of the root device
+    // (i.e. is a low speed device on a full speed hub) then need pre
+    return hcd_port_speed_get(0) != tuh_device_get_speed(dev_addr);
+}
+
+static void hw_xfer_complete(struct hw_endpoint *ep, xfer_result_t xfer_result)
+{
+    // Mark transfer as done before we tell the tinyusb stack
+    uint8_t dev_addr = ep->dev_addr;
+    uint8_t ep_addr = ep->ep_addr;
+    uint total_len = ep->total_len;
+    hw_endpoint_reset_transfer(ep);
+    hcd_event_xfer_complete(dev_addr, ep_addr, total_len, xfer_result, true);
+}
+
+static void _handle_buff_status_bit(uint bit, struct hw_endpoint *ep)
+{
+    usb_hw_clear->buf_status = bit;
+    bool done = _hw_endpoint_xfer_continue(ep);
+    if (done)
+    {
+        hw_xfer_complete(ep, XFER_RESULT_SUCCESS);
+    }
+}
+
+static void hw_handle_buff_status(void)
+{
+    uint32_t remaining_buffers = usb_hw->buf_status;
+    pico_trace("buf_status 0x%08x\n", remaining_buffers);
+
+    // Check EPX first
+    uint bit = 0b1;
+    if (remaining_buffers & bit)
+    {
+        remaining_buffers &= ~bit;
+        struct hw_endpoint *ep = &epx;
+        _handle_buff_status_bit(bit, ep);
+    }
+
+    // Check interrupt endpoints
+    for (uint i = 1; i <= USB_HOST_INTERRUPT_ENDPOINTS && remaining_buffers; i++)
+    {
+        // EPX is bit 0
+        // IEP1 is bit 2
+        // IEP2 is bit 4
+        // IEP3 is bit 6
+        // etc
+        bit = 1 << (i*2);
+
+        if (remaining_buffers & bit)
+        {
+            remaining_buffers &= ~bit;
+            _handle_buff_status_bit(bit, &eps[i]);
+        }
+    }
+
+    if (remaining_buffers)
+    {
+        panic("Unhandled buffer %d\n", remaining_buffers);
+    }
+}
+
+static void hw_trans_complete(void)
+{
+    struct hw_endpoint *ep = &epx;
+    assert(ep->active);
+
+    if (ep->sent_setup)
+    {
+        pico_trace("Sent setup packet\n");
+        hw_xfer_complete(ep, XFER_RESULT_SUCCESS);
+    }
+    else
+    {
+        // Don't care. Will handle this in buff status
+        return;
+    }
+}
+
+static void hcd_rp2040_irq(void)
+{
+    uint32_t status = usb_hw->ints;
+    uint32_t handled = 0;
+
+    if (status & USB_INTS_HOST_CONN_DIS_BITS)
+    {
+        handled |= USB_INTS_HOST_CONN_DIS_BITS;
+        
+        if (dev_speed())
+        {
+            hcd_event_device_attach(ROOT_PORT, true);
+        }
+        else
+        {
+            hcd_event_device_remove(ROOT_PORT, true);
+        }
+
+        // Clear speed change interrupt
+        usb_hw_clear->sie_status = USB_SIE_STATUS_SPEED_BITS;
+    }
+
+    if (status & USB_INTS_TRANS_COMPLETE_BITS)
+    {
+        handled |= USB_INTS_TRANS_COMPLETE_BITS;
+        usb_hw_clear->sie_status = USB_SIE_STATUS_TRANS_COMPLETE_BITS;
+        hw_trans_complete();
+    }
+
+    if (status & USB_INTS_BUFF_STATUS_BITS)
+    {
+        handled |= USB_INTS_BUFF_STATUS_BITS;
+        hw_handle_buff_status();
+    }
+
+    if (status & USB_INTS_STALL_BITS)
+    {
+        // We have rx'd a stall from the device
+        pico_trace("Stall REC\n");
+        handled |= USB_INTS_STALL_BITS;
+        usb_hw_clear->sie_status = USB_SIE_STATUS_STALL_REC_BITS;
+        hw_xfer_complete(&epx, XFER_RESULT_STALLED);
+    }
+
+    if (status & USB_INTS_ERROR_RX_TIMEOUT_BITS)
+    {
+        handled |= USB_INTS_ERROR_RX_TIMEOUT_BITS;
+        usb_hw_clear->sie_status = USB_SIE_STATUS_RX_TIMEOUT_BITS;
+    }
+
+    if (status & USB_INTS_ERROR_DATA_SEQ_BITS)
+    {
+        usb_hw_clear->sie_status = USB_SIE_STATUS_DATA_SEQ_ERROR_BITS;
+        panic("Data Seq Error \n");
+    }
+
+    if (status ^ handled)
+    {
+        panic("Unhandled IRQ 0x%x\n", (uint) (status ^ handled));
+    }
+}
+
+static struct hw_endpoint *_next_free_interrupt_ep(void)
+{
+    struct hw_endpoint *ep = NULL;
+    for (uint i = 1; i < count_of(eps); i++)
+    {
+        ep = &eps[i];
+        if (!ep->configured)
+        {
+            // Will be configured by _hw_endpoint_init / _hw_endpoint_allocate
+            ep->interrupt_num = i - 1;
+            return ep;
+        }
+    }
+    return ep;
+}
+
+static struct hw_endpoint *_hw_endpoint_allocate(uint8_t transfer_type)
+{
+    struct hw_endpoint *ep = NULL;
+    if (transfer_type == TUSB_XFER_INTERRUPT)
+    {
+        ep = _next_free_interrupt_ep();
+        pico_info("Allocate interrupt ep %d\n", ep->interrupt_num);
+        assert(ep);
+        ep->buffer_control = &usbh_dpram->int_ep_buffer_ctrl[ep->interrupt_num].ctrl;
+        ep->endpoint_control = &usbh_dpram->int_ep_ctrl[ep->interrupt_num].ctrl;
+        // 0x180 for epx
+        // 0x1c0 for intep0
+        // 0x200 for intep1
+        // etc
+        ep->hw_data_buf = &usbh_dpram->epx_data[64 * (ep->interrupt_num + 1)];
+    }
+    else
+    {
+        ep = &epx;
+        ep->buffer_control = &usbh_dpram->epx_buf_ctrl;
+        ep->endpoint_control = &usbh_dpram->epx_ctrl;
+        ep->hw_data_buf = &usbh_dpram->epx_data[0];
+    }
+    return ep;
+}
+
+static void _hw_endpoint_init(struct hw_endpoint *ep, uint8_t dev_addr, uint8_t ep_addr, uint wMaxPacketSize, uint8_t transfer_type, uint8_t bmInterval)
+{
+    // Already has data buffer, endpoint control, and buffer control allocated at this point
+    assert(ep->endpoint_control);
+    assert(ep->buffer_control);
+    assert(ep->hw_data_buf);
+
+    uint8_t num = tu_edpt_number(ep_addr);
+    bool in = ep_addr & TUSB_DIR_IN_MASK;
+    ep->ep_addr = ep_addr;
+    ep->dev_addr = dev_addr;
+    ep->in = in;
+    // For host, IN to host == RX, anything else rx == false
+    ep->rx = in == true;
+    ep->num = num;
+    // Response to a setup packet on EP0 starts with pid of 1
+    ep->next_pid = num == 0 ? 1u : 0u;
+    ep->wMaxPacketSize = wMaxPacketSize;
+    ep->transfer_type = transfer_type;
+
+    pico_trace("hw_endpoint_init dev %d ep %d %s xfer %d\n", ep->dev_addr, ep->num, ep_dir_string[ep->in], ep->transfer_type);
+    pico_trace("dev %d ep %d %s setup buffer @ 0x%p\n", ep->dev_addr, ep->num, ep_dir_string[ep->in], ep->hw_data_buf);
+    uint dpram_offset = hw_data_offset(ep->hw_data_buf);
+    // Bits 0-5 should be 0
+    assert(!(dpram_offset & 0b111111));
+
+    // Fill in endpoint control register with buffer offset
+    uint32_t ep_reg =  EP_CTRL_ENABLE_BITS
+                  | EP_CTRL_INTERRUPT_PER_BUFFER
+                  | (ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB)
+                  | dpram_offset;
+    ep_reg |= bmInterval ? (bmInterval - 1) << EP_CTRL_HOST_INTERRUPT_INTERVAL_LSB : 0;
+    *ep->endpoint_control = ep_reg;
+    pico_trace("endpoint control (0x%p) <- 0x%x\n", ep->endpoint_control, ep_reg);
+    ep->configured = true;
+
+    if (bmInterval)
+    {
+        // This is an interrupt endpoint
+        // so need to set up interrupt endpoint address control register with:
+        // device address
+        // endpoint number / direction
+        // preamble
+        uint32_t reg = dev_addr | (ep->num << USB_ADDR_ENDP1_ENDPOINT_LSB);
+        // Assert the interrupt endpoint is IN_TO_HOST
+        assert(ep->in);
+
+        if (need_pre(dev_addr))
+        {
+            reg |= USB_ADDR_ENDP1_INTEP_PREAMBLE_BITS;
+        }
+        usb_hw->int_ep_addr_ctrl[ep->interrupt_num] = reg;
+
+        // Finally, enable interrupt that endpoint
+        usb_hw_set->int_ep_ctrl = 1 << (ep->interrupt_num + 1);
+
+        // If it's an interrupt endpoint we need to set up the buffer control
+        // register
+
+    }
+}
+
+static void hw_endpoint_init(uint8_t dev_addr, const tusb_desc_endpoint_t *ep_desc)
+{
+    // Allocated differently based on if it's an interrupt endpoint or not
+    struct hw_endpoint *ep = _hw_endpoint_allocate(ep_desc->bmAttributes.xfer);
+    _hw_endpoint_init(ep,
+        dev_addr,
+        ep_desc->bEndpointAddress,
+        ep_desc->wMaxPacketSize.size,
+        ep_desc->bmAttributes.xfer,
+        ep_desc->bInterval);
+    // Map this struct to ep@device address
+    set_dev_ep(dev_addr, ep_desc->bEndpointAddress, ep);
+}
+
+//--------------------------------------------------------------------+
+// HCD API
+//--------------------------------------------------------------------+
+bool hcd_init(void)
+{
+    pico_trace("hcd_init\n");
+
+    // Reset any previous state
+    rp2040_usb_init();
+
+    irq_set_exclusive_handler(USBCTRL_IRQ, hcd_rp2040_irq);
+
+    // clear epx and interrupt eps
+    memset(&eps, 0, sizeof(eps));
+
+    // Enable in host mode with SOF / Keep alive on
+    usb_hw->main_ctrl = USB_MAIN_CTRL_CONTROLLER_EN_BITS | USB_MAIN_CTRL_HOST_NDEVICE_BITS;
+    usb_hw->sie_ctrl = sie_ctrl_base;
+    usb_hw->inte = USB_INTE_BUFF_STATUS_BITS      | 
+                   USB_INTE_HOST_CONN_DIS_BITS    | 
+                   USB_INTE_HOST_RESUME_BITS      | 
+                   USB_INTE_STALL_BITS            | 
+                   USB_INTE_TRANS_COMPLETE_BITS   |
+                   USB_INTE_ERROR_RX_TIMEOUT_BITS |
+                   USB_INTE_ERROR_DATA_SEQ_BITS   ;
+
+    return true;
+}
+
+void hcd_port_reset(uint8_t rhport)
+{
+    pico_trace("hcd_port_reset\n");
+    assert(rhport == 0);
+    // TODO: Nothing to do here yet. Perhaps need to reset some state?
+}
+
+bool hcd_port_connect_status(uint8_t rhport)
+{
+    pico_trace("hcd_port_connect_status\n");
+    assert(rhport == 0);
+    return usb_hw->sie_status & USB_SIE_STATUS_SPEED_BITS;
+}
+
+tusb_speed_t hcd_port_speed_get(uint8_t rhport)
+{
+    pico_trace("hcd_port_speed_get\n");
+    assert(rhport == 0);
+    // TODO: Should enumval this register
+    switch (dev_speed())
+    {
+        case 1:
+            return TUSB_SPEED_LOW;
+        case 2:
+            return TUSB_SPEED_FULL;
+        default:
+            panic("Invalid speed\n");
+    }
+}
+
+// Close all opened endpoint belong to this device
+void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
+{
+    pico_trace("hcd_device_close %d\n", dev_addr);
+}
+
+void hcd_int_enable(uint8_t rhport)
+{
+    assert(rhport == 0);
+    irq_set_enabled(USBCTRL_IRQ, true);
+}
+
+void hcd_int_disable(uint8_t rhport)
+{
+    // todo we should check this is disabling from the correct core; note currently this is never called
+    assert(rhport == 0);
+    irq_set_enabled(USBCTRL_IRQ, false);
+}
+
+bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen)
+{
+    pico_info("hcd_edpt_xfer dev_addr %d, ep_addr 0x%x, len %d\n", dev_addr, ep_addr, buflen);
+    
+    // Get appropriate ep. Either EPX or interrupt endpoint
+    struct hw_endpoint *ep = get_dev_ep(dev_addr, ep_addr);
+
+    if (ep_addr != ep->ep_addr)
+    {
+        // Direction has flipped so re init it but with same properties
+        _hw_endpoint_init(ep, dev_addr, ep_addr, ep->wMaxPacketSize, ep->transfer_type, 0);
+    }
+
+    // True indicates this is the start of the transfer
+    _hw_endpoint_xfer(ep, buffer, buflen, true);
+
+    // If a normal transfer (non-interrupt) then initiate using
+    // sie ctrl registers. Otherwise interrupt ep registers should
+    // already be configured
+    if (ep == &epx) {
+        // That has set up buffer control, endpoint control etc
+        // for host we have to initiate the transfer
+        usb_hw->dev_addr_ctrl = dev_addr | ep->num << USB_ADDR_ENDP_ENDPOINT_LSB;
+        uint32_t flags = USB_SIE_CTRL_START_TRANS_BITS | sie_ctrl_base;
+        flags |= ep->rx ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS;
+        // Set pre if we are a low speed device on full speed hub
+        flags |= need_pre(dev_addr) ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0;
+        usb_hw->sie_ctrl = flags;
+    }
+
+    return true;
+}
+
+bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8])
+{
+    pico_info("hcd_setup_send dev_addr %d\n", dev_addr);
+    
+    // Copy data into setup packet buffer
+    memcpy((void*)&usbh_dpram->setup_packet[0], setup_packet, 8);
+
+    // Configure EP0 struct with setup info for the trans complete
+    struct hw_endpoint *ep = _hw_endpoint_allocate(0);
+    // EP0 out
+    _hw_endpoint_init(ep, dev_addr, 0x00, ep->wMaxPacketSize, 0, 0);
+    assert(ep->configured);
+    assert(ep->num == 0 && !ep->in);
+    ep->total_len = 8;
+    ep->transfer_size = 8;
+    ep->active = true;
+    ep->sent_setup = true;
+
+    // Set device address
+    usb_hw->dev_addr_ctrl = dev_addr;
+    // Set pre if we are a low speed device on full speed hub
+    uint32_t flags = sie_ctrl_base | USB_SIE_CTRL_SEND_SETUP_BITS | USB_SIE_CTRL_START_TRANS_BITS;
+    flags |= need_pre(dev_addr) ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0;
+    usb_hw->sie_ctrl = flags;
+    return true;
+}
+
+uint32_t hcd_uframe_number(uint8_t rhport)
+{
+    // Microframe number is (125us) but we are max full speed so return miliseconds * 8
+    return usb_hw->sof_rd * 8;
+}
+
+bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc)
+{
+    pico_trace("hcd_edpt_open dev_addr %d, ep_addr %d\n", dev_addr, ep_desc->bEndpointAddress);
+    hw_endpoint_init(dev_addr, ep_desc);
+    return true;
+}
+
+bool hcd_edpt_busy(uint8_t dev_addr, uint8_t ep_addr)
+{
+    // EPX is shared, so multiple device addresses and endpoint addresses share that
+    // so if any transfer is active on epx, we are busy. Interrupt endpoints have their own
+    // EPX so ep->active will only be busy if there is a pending transfer on that interrupt endpoint
+    // on that device
+    pico_trace("hcd_edpt_busy dev addr %d ep_addr 0x%x\n", dev_addr, ep_addr);
+    struct hw_endpoint *ep = get_dev_ep(dev_addr, ep_addr);
+    assert(ep);
+    bool busy = ep->active;
+    pico_trace("busy == %d\n", busy);
+    return busy;
+}
+
+bool hcd_edpt_stalled(uint8_t dev_addr, uint8_t ep_addr)
+{
+    panic("hcd_pipe_stalled");
+}
+
+bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr)
+{
+    panic("hcd_clear_stall");
+    return true;
+}
+
+bool hcd_pipe_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t buffer[], uint16_t total_bytes, bool int_on_complete)
+{
+    pico_trace("hcd_pipe_xfer dev_addr %d, ep_addr 0x%x, total_bytes %d, int_on_complete %d\n",
+        dev_addr, ep_addr, total_bytes, int_on_complete);
+
+    // Same logic as hcd_edpt_xfer as far as I am concerned
+    hcd_edpt_xfer(0, dev_addr, ep_addr, buffer, total_bytes);
+
+    return true;
+}
+#endif

+ 279 - 0
src/portable/raspberrypi/rp2040/rp2040_usb.c

@@ -0,0 +1,279 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * 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 <stdlib.h>
+#include "rp2040_usb.h"
+#include "hardware/clocks.h"
+#include "tusb_option.h"
+
+// Direction strings for debug
+const char *ep_dir_string[] = {
+        "out",
+        "in",
+};
+
+static inline void _hw_endpoint_lock_update(struct hw_endpoint *ep, int delta) {
+    // todo add critsec as necessary to prevent issues between worker and IRQ...
+    //  note that this is perhaps as simple as disabling IRQs because it would make
+    //  sense to have worker and IRQ on same core, however I think using critsec is about equivalent.
+}
+
+static inline void _hw_endpoint_update_last_buf(struct hw_endpoint *ep)
+{
+    ep->last_buf = ep->len + ep->transfer_size == ep->total_len;
+}
+
+void rp2040_usb_init(void)
+{
+    // Reset usb controller
+    reset_block(RESETS_RESET_USBCTRL_BITS);
+    unreset_block_wait(RESETS_RESET_USBCTRL_BITS);
+
+    // Clear any previous state just in case
+    memset(usb_hw, 0, sizeof(*usb_hw));
+    memset(usb_dpram, 0, sizeof(*usb_dpram));
+
+    // Mux to phy
+    usb_hw->muxing    = USB_USB_MUXING_TO_PHY_BITS    | USB_USB_MUXING_SOFTCON_BITS;
+    usb_hw->pwr       = USB_USB_PWR_VBUS_DETECT_BITS  | USB_USB_PWR_VBUS_DETECT_OVERRIDE_EN_BITS;
+}
+
+void hw_endpoint_reset_transfer(struct hw_endpoint *ep)
+{
+    ep->stalled = false;
+    ep->active = false;
+    ep->sent_setup = false;
+    ep->total_len = 0;
+    ep->len = 0;
+    ep->transfer_size = 0;
+    ep->user_buf = 0;
+}
+
+void _hw_endpoint_buffer_control_update32(struct hw_endpoint *ep, uint32_t and_mask, uint32_t or_mask) {
+    uint32_t value = 0;
+    if (and_mask) {
+        value = *ep->buffer_control & and_mask;
+    }
+    if (or_mask) {
+        value |= or_mask;
+        if (or_mask & USB_BUF_CTRL_AVAIL) {
+            if (*ep->buffer_control & USB_BUF_CTRL_AVAIL) {
+                panic("ep %d %s was already available", ep->num, ep_dir_string[ep->in]);
+            }
+            *ep->buffer_control = value & ~USB_BUF_CTRL_AVAIL;
+            // 12 cycle delay.. (should be good for 48*12Mhz = 576Mhz)
+            // Don't need delay in host mode as host is in charge
+#ifndef RP2040_USB_HOST_MODE
+            __asm volatile (
+                    "b 1f\n"
+                    "1: b 1f\n"
+                    "1: b 1f\n"
+                    "1: b 1f\n"
+                    "1: b 1f\n"
+                    "1: b 1f\n"
+                    "1:\n"
+                    : : : "memory");
+#endif
+        }
+    }
+    *ep->buffer_control = value;
+}
+
+void _hw_endpoint_start_next_buffer(struct hw_endpoint *ep)
+{
+    // Prepare buffer control register value
+    uint32_t val = ep->transfer_size | USB_BUF_CTRL_AVAIL;
+
+    if (!ep->rx)
+    {
+        // Copy data from user buffer to hw buffer
+        memcpy(ep->hw_data_buf, &ep->user_buf[ep->len], ep->transfer_size);
+        // Mark as full
+        val |= USB_BUF_CTRL_FULL;
+    }
+
+    // PID
+    val |= ep->next_pid ? USB_BUF_CTRL_DATA1_PID : USB_BUF_CTRL_DATA0_PID;
+    ep->next_pid ^= 1u;
+
+    // Is this the last buffer? Only really matters for host mode. Will trigger
+    // the trans complete irq but also stop it polling. We only really care about
+    // trans complete for setup packets being sent
+    if (ep->last_buf)
+    {
+        pico_trace("Last buf (%d bytes left)\n", ep->transfer_size);
+        val |= USB_BUF_CTRL_LAST;
+    }
+
+    // Finally, write to buffer_control which will trigger the transfer
+    // the next time the controller polls this dpram address
+    _hw_endpoint_buffer_control_set_value32(ep, val);
+    pico_trace("buffer control (0x%p) <- 0x%x\n", ep->buffer_control, val);
+}
+
+
+void _hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len)
+{
+    _hw_endpoint_lock_update(ep, 1);
+    pico_trace("Start transfer of total len %d on ep %d %s\n", total_len, ep->num, ep_dir_string[ep->in]);
+    if (ep->active)
+    {
+        // TODO: Is this acceptable for interrupt packets?
+        pico_warn("WARN: starting new transfer on already active ep %d %s\n", ep->num, ep_dir_string[ep->in]);
+
+        hw_endpoint_reset_transfer(ep);
+    }
+
+    // Fill in info now that we're kicking off the hw
+    ep->total_len = total_len;
+    ep->len = 0;
+    // FIXME: What if low speed
+    ep->transfer_size = total_len > 64 ? 64 : total_len;
+    ep->active = true;
+    ep->user_buf = buffer;
+    // Recalculate if this is the last buffer
+    _hw_endpoint_update_last_buf(ep);
+    ep->buf_sel = 0;
+
+    _hw_endpoint_start_next_buffer(ep);
+    _hw_endpoint_lock_update(ep, -1);
+}
+
+void _hw_endpoint_xfer_sync(struct hw_endpoint *ep)
+{
+    // Update hw endpoint struct with info from hardware
+    // after a buff status interrupt
+
+    // Get the buffer state and amount of bytes we have
+    // transferred
+    uint32_t buf_ctrl = _hw_endpoint_buffer_control_get_value32(ep);
+    uint transferred_bytes = buf_ctrl & USB_BUF_CTRL_LEN_MASK;
+
+#ifdef RP2040_USB_HOST_MODE
+    // tag::host_buf_sel_fix[]
+    if (ep->buf_sel == 1)
+    {
+        // Host can erroneously write status to top half of buf_ctrl register
+        buf_ctrl = buf_ctrl >> 16;
+    }
+    // Flip buf sel for host
+    ep->buf_sel ^= 1u;
+    // end::host_buf_sel_fix[]
+#endif
+
+    // We are continuing a transfer here. If we are TX, we have successfullly
+    // sent some data can increase the length we have sent
+    if (!ep->rx)
+    {
+        assert(!(buf_ctrl & USB_BUF_CTRL_FULL));
+        pico_trace("tx %d bytes (buf_ctrl 0x%08x)\n", transferred_bytes, buf_ctrl);
+        ep->len += transferred_bytes;
+    }
+    else
+    {
+        // If we are OUT we have recieved some data, so can increase the length
+        // we have recieved AFTER we have copied it to the user buffer at the appropriate
+        // offset
+        pico_trace("rx %d bytes (buf_ctrl 0x%08x)\n", transferred_bytes, buf_ctrl);
+        assert(buf_ctrl & USB_BUF_CTRL_FULL);
+        memcpy(&ep->user_buf[ep->len], ep->hw_data_buf, transferred_bytes);
+        ep->len += transferred_bytes;
+    }
+
+    // Sometimes the host will send less data than we expect...
+    // If this is a short out transfer update the total length of the transfer
+    // to be the current length
+    if ((ep->rx) && (transferred_bytes < ep->transfer_size))
+    {
+        pico_trace("Short rx transfer\n");
+        // Reduce total length as this is last packet
+        ep->total_len = ep->len;
+    }
+}
+
+// Returns true if transfer is complete
+bool _hw_endpoint_xfer_continue(struct hw_endpoint *ep)
+{
+    _hw_endpoint_lock_update(ep, 1);
+    // Part way through a transfer
+    if (!ep->active)
+    {
+        panic("Can't continue xfer on inactive ep %d %s", ep->num, ep_dir_string);
+    }
+
+    // Update EP struct from hardware state
+    _hw_endpoint_xfer_sync(ep);
+
+    // Now we have synced our state with the hardware. Is there more data to transfer?
+    uint remaining_bytes = ep->total_len - ep->len;
+    ep->transfer_size = remaining_bytes > 64 ? 64 : remaining_bytes;
+    _hw_endpoint_update_last_buf(ep);
+
+    // Can happen because of programmer error so check for it
+    if (ep->len > ep->total_len)
+    {
+        panic("Transferred more data than expected");
+    }
+
+    // If we are done then notify tinyusb
+    if (ep->len == ep->total_len)
+    {
+        pico_trace("Completed transfer of %d bytes on ep %d %s\n",
+                   ep->len, ep->num, ep_dir_string[ep->in]);
+        // Notify caller we are done so it can notify the tinyusb
+        // stack
+        _hw_endpoint_lock_update(ep, -1);
+        return true;
+    }
+    else
+    {
+        _hw_endpoint_start_next_buffer(ep);
+    }
+
+    _hw_endpoint_lock_update(ep, -1);
+    // More work to do
+    return false;
+}
+
+void _hw_endpoint_xfer(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len, bool start)
+{
+    // Trace
+    pico_trace("hw_endpoint_xfer ep %d %s", ep->num, ep_dir_string[ep->in]);
+    pico_trace(" total_len %d, start=%d\n", total_len, start);
+
+    assert(ep->configured);
+
+
+    if (start)
+    {
+        _hw_endpoint_xfer_start(ep, buffer, total_len);
+    }
+    else
+    {
+        _hw_endpoint_xfer_continue(ep);
+    }
+}
+

+ 124 - 0
src/portable/raspberrypi/rp2040/rp2040_usb.h

@@ -0,0 +1,124 @@
+#ifndef RP2040_COMMON_H_
+#define RP2040_COMMON_H_
+
+#if defined(RP2040_USB_HOST_MODE) && defined(RP2040_USB_DEVICE_MODE)
+#error TinyUSB device and host mode not supported at the same time
+#endif
+
+#include "common/tusb_common.h"
+
+#include "pico.h"
+#include "hardware/structs/usb.h"
+#include "hardware/irq.h"
+#include "hardware/resets.h"
+
+#if defined(PICO_RP2040_USB_DEVICE_ENUMERATION_FIX) && !defined(TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX)
+#define TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX PICO_RP2040_USB_DEVICE_ENUMERATION_FIX
+#endif
+
+// For memset
+#include <string.h>
+
+#if false && !defined(NDEBUG)
+#define pico_trace(format,args...) printf(format, ## args)
+#else
+#define pico_trace(format,...) ((void)0)
+#endif
+
+#if false && !defined(NDEBUG)
+#define pico_info(format,args...) printf(format, ## args)
+#else
+#define pico_info(format,...) ((void)0)
+#endif
+
+#if false && !defined(NDEBUG)
+#define pico_warn(format,args...) printf(format, ## args)
+#else
+#define pico_warn(format,...) ((void)0)
+#endif
+
+// Hardware information per endpoint
+struct hw_endpoint
+{
+    // Is this a valid struct
+    bool configured;
+    // EP direction
+    bool in;
+    // EP num (not including direction)
+    uint8_t num;
+    
+    // Transfer direction (i.e. IN is rx for host but tx for device)
+    // allows us to common up transfer functions
+    bool rx;
+    
+    uint8_t ep_addr;
+    uint8_t next_pid;
+
+    // Endpoint control register
+    io_rw_32 *endpoint_control;
+    // Buffer control register
+    io_rw_32 *buffer_control;
+
+    // Buffer pointer in usb dpram
+    uint8_t *hw_data_buf;
+
+    // Have we been stalled
+    bool stalled;
+
+    // Current transfer information
+    bool active;
+    uint total_len;
+    uint len;
+    // Amount of data with the hardware
+    uint transfer_size;
+    // Only needed for host mode
+    bool last_buf;
+    // HOST BUG. Host will incorrect write status to top half of buffer
+    // control register when doing transfers > 1 packet
+    uint8_t buf_sel;
+    // User buffer in main memory
+    uint8_t *user_buf;
+
+    // Data needed from EP descriptor
+    uint wMaxPacketSize;
+    // Interrupt, bulk, etc
+    uint8_t transfer_type;
+    
+    // Only needed for host
+    uint8_t dev_addr;
+    bool sent_setup;
+    // If interrupt endpoint
+    uint8_t interrupt_num;
+};
+
+void rp2040_usb_init(void);
+
+void hw_endpoint_reset_transfer(struct hw_endpoint *ep);
+void _hw_endpoint_xfer(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len, bool start);
+void _hw_endpoint_start_next_buffer(struct hw_endpoint *ep);
+void _hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len);
+void _hw_endpoint_xfer_sync(struct hw_endpoint *ep);
+bool _hw_endpoint_xfer_continue(struct hw_endpoint *ep);
+void _hw_endpoint_buffer_control_update32(struct hw_endpoint *ep, uint32_t and_mask, uint32_t or_mask);
+static inline uint32_t _hw_endpoint_buffer_control_get_value32(struct hw_endpoint *ep) {
+    return *ep->buffer_control;
+}
+static inline void _hw_endpoint_buffer_control_set_value32(struct hw_endpoint *ep, uint32_t value) {
+    return _hw_endpoint_buffer_control_update32(ep, 0, value);
+}
+static inline void _hw_endpoint_buffer_control_set_mask32(struct hw_endpoint *ep, uint32_t value) {
+    return _hw_endpoint_buffer_control_update32(ep, ~value, value);
+}
+static inline void _hw_endpoint_buffer_control_clear_mask32(struct hw_endpoint *ep, uint32_t value) {
+    return _hw_endpoint_buffer_control_update32(ep, ~value, 0);
+}
+
+static inline uintptr_t hw_data_offset(uint8_t *buf)
+{
+    // Remove usb base from buffer pointer
+    return (uintptr_t)buf ^ (uintptr_t)usb_dpram;
+}
+
+extern const char *ep_dir_string[];
+
+#endif

+ 1 - 1
src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c

@@ -547,7 +547,7 @@ void dcd_int_handler(uint8_t rhport) {
     // USBRST is start of reset.
     clear_istr_bits(USB_ISTR_RESET);
     dcd_handle_bus_reset();
-    dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
+    dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
     return; // Don't do the rest of the things here; perhaps they've been cleared?
   }
 

+ 124 - 91
src/portable/st/synopsys/dcd_synopsys.c

@@ -45,13 +45,13 @@
 #define STM32L4_SYNOPSYS
 #endif
 
-#if TUSB_OPT_DEVICE_ENABLED && \
+#if TUSB_OPT_DEVICE_ENABLED &&                                          \
     ( (CFG_TUSB_MCU == OPT_MCU_STM32F1 && defined(STM32F1_SYNOPSYS)) || \
-        CFG_TUSB_MCU == OPT_MCU_STM32F2 || \
-        CFG_TUSB_MCU == OPT_MCU_STM32F4 || \
-        CFG_TUSB_MCU == OPT_MCU_STM32F7 || \
-        CFG_TUSB_MCU == OPT_MCU_STM32H7 || \
-        (CFG_TUSB_MCU == OPT_MCU_STM32L4 && defined(STM32L4_SYNOPSYS)) \
+       CFG_TUSB_MCU == OPT_MCU_STM32F2                               || \
+       CFG_TUSB_MCU == OPT_MCU_STM32F4                               || \
+       CFG_TUSB_MCU == OPT_MCU_STM32F7                               || \
+       CFG_TUSB_MCU == OPT_MCU_STM32H7                               || \
+      (CFG_TUSB_MCU == OPT_MCU_STM32L4 && defined(STM32L4_SYNOPSYS))    \
     )
 
 // EP_MAX       : Max number of bi-directional endpoints including EP0
@@ -115,6 +115,7 @@
 #define EP_FIFO_SIZE      EP_FIFO_SIZE_HS
 #define RHPORT_REGS_BASE  USB_OTG_HS_PERIPH_BASE
 #define RHPORT_IRQn       OTG_HS_IRQn
+
 #endif
 
 #define GLOBAL_BASE(_port)     ((USB_OTG_GlobalTypeDef*) RHPORT_REGS_BASE)
@@ -139,25 +140,40 @@ typedef struct {
   uint8_t interval;
 } xfer_ctl_t;
 
-// EP size and transfer type report
-typedef struct TU_ATTR_PACKED {
-  // The following format may look complicated but it is the most elegant way of addressing the required fields: EP number, EP direction, and EP transfer type.
-  // The codes assigned to those fields, according to the USB specification, can be neatly used as indices.
-  uint16_t ep_size[EP_MAX][2];          ///< dim 1: EP number, dim 2: EP direction denoted by TUSB_DIR_OUT (= 0) and TUSB_DIR_IN (= 1)
-  bool ep_transfer_type[EP_MAX][2][4];      ///< dim 1: EP number, dim 2: EP direction, dim 3: transfer type, where 0 = Control, 1 = Isochronous, 2 = Bulk, and 3 = Interrupt
-  ///< I know very well that EP0 can only be used as control EP and we waste space here but for the sake of simplicity we accept that. It is used in a non-persistent way anyway!
-} ep_sz_tt_report_t;
-
 typedef volatile uint32_t * usb_fifo_t;
 
 xfer_ctl_t xfer_status[EP_MAX][2];
 #define XFER_CTL_BASE(_ep, _dir) &xfer_status[_ep][_dir]
 
 // EP0 transfers are limited to 1 packet - larger sizes has to be split
-static uint16_t ep0_pending[2];     // Index determines direction as tusb_dir_t type
+static uint16_t ep0_pending[2];                   // Index determines direction as tusb_dir_t type
+
+// TX FIFO RAM allocation so far in words - RX FIFO size is readily available from usb_otg->GRXFSIZ
+static uint16_t _allocated_fifo_words_tx;         // TX FIFO size in words (IN EPs)
+static bool _out_ep_closed;                       // Flag to check if RX FIFO size needs an update (reduce its size)
 
-// FIFO RAM allocation so far in words
-static uint16_t _allocated_fifo_words;
+// Calculate the RX FIFO size according to recommendations from reference manual
+static inline uint16_t calc_rx_ff_size(uint16_t ep_size)
+{
+  return 15 + 2*(ep_size/4) + 2*EP_MAX;
+}
+
+static void update_grxfsiz(uint8_t rhport)
+{
+  (void) rhport;
+
+  USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport);
+
+  // Determine largest EP size for RX FIFO
+  uint16_t max_epsize = 0;
+  for (uint8_t epnum = 0; epnum < EP_MAX; epnum++)
+  {
+    max_epsize = tu_max16(max_epsize, xfer_status[epnum][TUSB_DIR_OUT].max_size);
+  }
+
+  // Update size of RX FIFO
+  usb_otg->GRXFSIZ = calc_rx_ff_size(max_epsize);
+}
 
 // Setup the control endpoint 0.
 static void bus_reset(uint8_t rhport)
@@ -170,6 +186,7 @@ static void bus_reset(uint8_t rhport)
   USB_OTG_INEndpointTypeDef * in_ep = IN_EP_BASE(rhport);
 
   tu_memclr(xfer_status, sizeof(xfer_status));
+  _out_ep_closed = false;
 
   for(uint8_t n = 0; n < EP_MAX; n++) {
     out_ep[n].DOEPCTL |= USB_OTG_DOEPCTL_SNAK;
@@ -182,16 +199,28 @@ static void bus_reset(uint8_t rhport)
   // "USB Data FIFOs" section in reference manual
   // Peripheral FIFO architecture
   //
+  // The FIFO is split up in a lower part where the RX FIFO is located and an upper part where the TX FIFOs start.
+  // We do this to allow the RX FIFO to grow dynamically which is possible since the free space is located
+  // between the RX and TX FIFOs. This is required by ISO OUT EPs which need a bigger FIFO than the standard
+  // configuration done below.
+  //
+  // Dynamically FIFO sizes are of interest only for ISO EPs since all others are usually not opened and closed.
+  // All EPs other than ISO are opened as soon as the driver starts up i.e. when the host sends a
+  // configure interface command. Hence, all IN EPs other the ISO will be located at the top. IN ISO EPs are usually
+  // opened when the host sends an additional command: setInterface. At this point in time
+  // the ISO EP will be located next to the free space and can change its size. In case more IN EPs change its size
+  // an additional memory
+  //
   // --------------- 320 or 1024 ( 1280 or 4096 bytes )
+  // | IN FIFO 0   |
+  // --------------- (320 or 1024) - 16
+  // | IN FIFO 1   |
+  // --------------- (320 or 1024) - 16 - x
+  // |   . . . .   |
+  // --------------- (320 or 1024) - 16 - x - y - ... - z
   // | IN FIFO MAX |
   // ---------------
-  // |    ...      |
-  // --------------- y + x + 16 + GRXFSIZ
-  // | IN FIFO 2   |
-  // --------------- x + 16 + GRXFSIZ
-  // | IN FIFO 1   |
-  // --------------- 16 + GRXFSIZ
-  // | IN FIFO 0   |
+  // |    FREE     |
   // --------------- GRXFSIZ
   // | OUT FIFO    |
   // | ( Shared )  |
@@ -213,24 +242,16 @@ static void bus_reset(uint8_t rhport)
   //   NOTE: Largest-EPsize & EPOUTnum is actual used endpoints in configuration. Since DCD has no knowledge
   //   of the overall picture yet. We will use the worst scenario: largest possible + EP_MAX
   //
-  //   FIXME: for Isochronous, largest EP size can be 1023/1024 for FS/HS respectively. In addition if multiple ISO
+  //   For Isochronous, largest EP size can be 1023/1024 for FS/HS respectively. In addition if multiple ISO
   //   are enabled at least "2 x (Largest-EPsize/4) + 1" are recommended.  Maybe provide a macro for application to
   //   overwrite this.
 
-#if TUD_OPT_HIGH_SPEED
-  _allocated_fifo_words = 271 + 2*EP_MAX;
-#else
-  _allocated_fifo_words =  47 + 2*EP_MAX;
-#endif
+  usb_otg->GRXFSIZ = calc_rx_ff_size(TUD_OPT_HIGH_SPEED ? 512 : 64);
 
-  usb_otg->GRXFSIZ = _allocated_fifo_words;
+  _allocated_fifo_words_tx = 16;
 
   // Control IN uses FIFO 0 with 64 bytes ( 16 32-bit word )
-  usb_otg->DIEPTXF0_HNPTXFSIZ = (16 << USB_OTG_TX0FD_Pos) | _allocated_fifo_words;
-
-  _allocated_fifo_words += 16;
-
-  // TU_LOG2_INT(_allocated_fifo_words);
+  usb_otg->DIEPTXF0_HNPTXFSIZ = (16 << USB_OTG_TX0FD_Pos) | (EP_FIFO_SIZE/4 - _allocated_fifo_words_tx);
 
   // Fixed control EP0 size to 64 bytes
   in_ep[0].DIEPCTL &= ~(0x03 << USB_OTG_DIEPCTL_MPSIZ_Pos);
@@ -534,6 +555,8 @@ void dcd_disconnect(uint8_t rhport)
 
 bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
 {
+  (void) rhport;
+
   USB_OTG_GlobalTypeDef * usb_otg = GLOBAL_BASE(rhport);
   USB_OTG_DeviceTypeDef * dev = DEVICE_BASE(rhport);
   USB_OTG_OUTEndpointTypeDef * out_ep = OUT_EP_BASE(rhport);
@@ -544,21 +567,26 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
 
   TU_ASSERT(epnum < EP_MAX);
 
-  if (desc_edpt->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS)
-  {
-    TU_ASSERT(desc_edpt->wMaxPacketSize.size <= (get_speed(rhport) == TUSB_SPEED_HIGH ? 1024 : 1023));
-  }
-  else
-  {
-    TU_ASSERT(desc_edpt->wMaxPacketSize.size <= (get_speed(rhport) == TUSB_SPEED_HIGH ? 512 : 64));
-  }
-
   xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
   xfer->max_size = desc_edpt->wMaxPacketSize.size;
   xfer->interval = desc_edpt->bInterval;
 
+  uint16_t const fifo_size = (desc_edpt->wMaxPacketSize.size + 3) / 4; // Round up to next full word
+
   if(dir == TUSB_DIR_OUT)
   {
+    // Calculate required size of RX FIFO
+    uint16_t const sz = calc_rx_ff_size(4*fifo_size);
+
+    // If size_rx needs to be extended check if possible and if so enlarge it
+    if (usb_otg->GRXFSIZ < sz)
+    {
+      TU_ASSERT(sz + _allocated_fifo_words_tx <= EP_FIFO_SIZE/4);
+
+      // Enlarge RX FIFO
+      usb_otg->GRXFSIZ = sz;
+    }
+
     out_ep[epnum].DOEPCTL |= (1 << USB_OTG_DOEPCTL_USBAEP_Pos) |
         (desc_edpt->bmAttributes.xfer << USB_OTG_DOEPCTL_EPTYP_Pos) |
         (desc_edpt->wMaxPacketSize.size << USB_OTG_DOEPCTL_MPSIZ_Pos);
@@ -571,15 +599,15 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
     // Peripheral FIFO architecture
     //
     // --------------- 320 or 1024 ( 1280 or 4096 bytes )
+    // | IN FIFO 0   |
+    // --------------- (320 or 1024) - 16
+    // | IN FIFO 1   |
+    // --------------- (320 or 1024) - 16 - x
+    // |   . . . .   |
+    // --------------- (320 or 1024) - 16 - x - y - ... - z
     // | IN FIFO MAX |
     // ---------------
-    // |    ...      |
-    // --------------- y + x + 16 + GRXFSIZ
-    // | IN FIFO 2   |
-    // --------------- x + 16 + GRXFSIZ
-    // | IN FIFO 1   |
-    // --------------- 16 + GRXFSIZ
-    // | IN FIFO 0   |
+    // |    FREE     |
     // --------------- GRXFSIZ
     // | OUT FIFO    |
     // | ( Shared )  |
@@ -587,34 +615,15 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
     //
     // In FIFO is allocated by following rules:
     // - IN EP 1 gets FIFO 1, IN EP "n" gets FIFO "n".
-    // - Offset: allocated so far
-    // - Size
-    //    - Interrupt is EPSize
-    //    - Bulk/ISO is max(EPSize, remaining-fifo / non-opened-EPIN)
-
-    uint16_t const fifo_remaining = EP_FIFO_SIZE/4 - _allocated_fifo_words;
-    uint16_t fifo_size = (desc_edpt->wMaxPacketSize.size + 3) / 4;  // +3 for rounding up to next full word
-
-    if ( desc_edpt->bmAttributes.xfer != TUSB_XFER_INTERRUPT )
-    {
-      uint8_t opened = 0;
-      for(uint8_t i = 0; i < EP_MAX; i++)
-      {
-        if ( (i != epnum) && (xfer_status[i][TUSB_DIR_IN].max_size > 0) ) opened++;
-      }
 
-      // EP Size or equally divided of remaining whichever is larger
-      fifo_size = tu_max16(fifo_size, fifo_remaining / (EP_MAX - opened));
-    }
+    // Check if free space is available
+    TU_ASSERT(_allocated_fifo_words_tx + fifo_size + usb_otg->GRXFSIZ <= EP_FIFO_SIZE/4);
 
-    // FIFO overflows, we probably need a better allocating scheme
-    TU_ASSERT(fifo_size <= fifo_remaining);
+    _allocated_fifo_words_tx += fifo_size;
 
     // DIEPTXF starts at FIFO #1.
     // Both TXFD and TXSA are in unit of 32-bit words.
-    usb_otg->DIEPTXF[epnum - 1] = (fifo_size << USB_OTG_DIEPTXF_INEPTXFD_Pos) | _allocated_fifo_words;
-
-    _allocated_fifo_words += fifo_size;
+    usb_otg->DIEPTXF[epnum - 1] = (fifo_size << USB_OTG_DIEPTXF_INEPTXFD_Pos) | (EP_FIFO_SIZE/4 - _allocated_fifo_words_tx);
 
     in_ep[epnum].DIEPCTL |= (1 << USB_OTG_DIEPCTL_USBAEP_Pos) |
         (epnum << USB_OTG_DIEPCTL_TXFNUM_Pos) |
@@ -724,13 +733,21 @@ void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr)
   uint8_t const dir   = tu_edpt_dir(ep_addr);
 
   dcd_edpt_disable(rhport, ep_addr, false);
+
+  // Update max_size
+  xfer_status[epnum][dir].max_size = 0;  // max_size = 0 marks a disabled EP - required for changing FIFO allocation
+
   if (dir == TUSB_DIR_IN)
   {
     uint16_t const fifo_size = (usb_otg->DIEPTXF[epnum - 1] & USB_OTG_DIEPTXF_INEPTXFD_Msk) >> USB_OTG_DIEPTXF_INEPTXFD_Pos;
     uint16_t const fifo_start = (usb_otg->DIEPTXF[epnum - 1] & USB_OTG_DIEPTXF_INEPTXSA_Msk) >> USB_OTG_DIEPTXF_INEPTXSA_Pos;
-    // For now only endpoint that has FIFO at the end of FIFO memory can be closed without fuss.
-    TU_ASSERT(fifo_start + fifo_size == _allocated_fifo_words,);
-    _allocated_fifo_words -= fifo_size;
+    // For now only the last opened endpoint can be closed without fuss.
+    TU_ASSERT(fifo_start == EP_FIFO_SIZE/4 - _allocated_fifo_words_tx,);
+    _allocated_fifo_words_tx -= fifo_size;
+  }
+  else
+  {
+    _out_ep_closed = true;     // Set flag such that RX FIFO gets reduced in size once RX FIFO is empty
   }
 }
 
@@ -986,13 +1003,15 @@ void dcd_int_handler(uint8_t rhport)
 
   uint32_t int_status = usb_otg->GINTSTS;
 
-  if(int_status & USB_OTG_GINTSTS_USBRST) {
+  if(int_status & USB_OTG_GINTSTS_USBRST)
+  {
     // USBRST is start of reset.
     usb_otg->GINTSTS = USB_OTG_GINTSTS_USBRST;
     bus_reset(rhport);
   }
 
-  if(int_status & USB_OTG_GINTSTS_ENUMDNE) {
+  if(int_status & USB_OTG_GINTSTS_ENUMDNE)
+  {
     // ENUMDNE is the end of reset where speed of the link is detected
 
     usb_otg->GINTSTS = USB_OTG_GINTSTS_ENUMDNE;
@@ -1029,45 +1048,59 @@ void dcd_int_handler(uint8_t rhport)
   }
 
 #if USE_SOF
-  if(int_status & USB_OTG_GINTSTS_SOF) {
+  if(int_status & USB_OTG_GINTSTS_SOF)
+  {
     usb_otg->GINTSTS = USB_OTG_GINTSTS_SOF;
     dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
   }
 #endif
 
   // RxFIFO non-empty interrupt handling.
-  if(int_status & USB_OTG_GINTSTS_RXFLVL) {
+  if(int_status & USB_OTG_GINTSTS_RXFLVL)
+  {
     // RXFLVL bit is read-only
 
     // Mask out RXFLVL while reading data from FIFO
     usb_otg->GINTMSK &= ~USB_OTG_GINTMSK_RXFLVLM;
 
     // Loop until all available packets were handled
-    do {
+    do
+    {
       handle_rxflvl_ints(rhport, out_ep);
       int_status = usb_otg->GINTSTS;
     } while(int_status & USB_OTG_GINTSTS_RXFLVL);
 
+    // Manage RX FIFO size
+    if (_out_ep_closed)
+    {
+      update_grxfsiz(rhport);
+
+      // Disable flag
+      _out_ep_closed = false;
+    }
+
     usb_otg->GINTMSK |= USB_OTG_GINTMSK_RXFLVLM;
   }
 
   // OUT endpoint interrupt handling.
-  if(int_status & USB_OTG_GINTSTS_OEPINT) {
+  if(int_status & USB_OTG_GINTSTS_OEPINT)
+  {
     // OEPINT is read-only
     handle_epout_ints(rhport, dev, out_ep);
   }
 
   // IN endpoint interrupt handling.
-  if(int_status & USB_OTG_GINTSTS_IEPINT) {
+  if(int_status & USB_OTG_GINTSTS_IEPINT)
+  {
     // IEPINT bit read-only
     handle_epin_ints(rhport, dev, in_ep);
   }
 
-//  // Check for Incomplete isochronous IN transfer
-//  if(int_status & USB_OTG_GINTSTS_IISOIXFR) {
-//    printf("      IISOIXFR!\r\n");
-////    TU_LOG2("      IISOIXFR!\r\n");
-//  }
+  //  // Check for Incomplete isochronous IN transfer
+  //  if(int_status & USB_OTG_GINTSTS_IISOIXFR) {
+  //    printf("      IISOIXFR!\r\n");
+  ////    TU_LOG2("      IISOIXFR!\r\n");
+  //  }
 }
 
 #endif

+ 3 - 3
src/portable/ti/msp430x5xx/dcd_msp430x5xx.c

@@ -232,9 +232,9 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
   uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress);
   uint8_t const dir   = tu_edpt_dir(desc_edpt->bEndpointAddress);
 
-  // Unsupported endpoint numbers/size or type (Iso not supported. Control
+  // Unsupported endpoint numbers or type (Iso not supported. Control
   // not supported on nonzero endpoints).
-  if((desc_edpt->wMaxPacketSize.size > 64) || (epnum > 7) || \
+  if( (epnum > 7) || \
       (desc_edpt->bmAttributes.xfer == 0) || \
       (desc_edpt->bmAttributes.xfer == 1)) {
     return false;
@@ -572,7 +572,7 @@ void dcd_int_handler(uint8_t rhport)
   {
     case USBVECINT_RSTR:
       bus_reset();
-      dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
+      dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
       break;
 
     // Clear the (hardware-enforced) NAK on EP 0 after a SETUP packet

+ 1 - 1
src/portable/valentyusb/eptri/dcd_eptri.c

@@ -332,7 +332,7 @@ static void dcd_reset(void)
   usb_out_ev_enable_write(1);
   usb_setup_ev_enable_write(3);
 
-  dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
+  dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
 }
 
 // Initializes the USB peripheral for device mode and enables it.

+ 13 - 6
src/tusb_option.h

@@ -97,6 +97,12 @@
 // Dialog
 #define OPT_MCU_DA1469X          1000 ///< Dialog Semiconductor DA1469x
 
+// Raspberry Pi
+#define OPT_MCU_RP2040           1100 ///< Raspberry Pi RP2040
+
+// NXP Kinetis
+#define OPT_MCU_MKL25ZXX         1200 ///< NXP MKL25Zxx
+
 /** @} */
 
 /** \defgroup group_supported_os Supported RTOS
@@ -106,6 +112,7 @@
 #define OPT_OS_FREERTOS   2  ///< FreeRTOS
 #define OPT_OS_MYNEWT     3  ///< Mynewt OS
 #define OPT_OS_CUSTOM     4  ///< Custom OS is implemented by application
+#define OPT_OS_PICO       5  ///< Raspberry Pi Pico SDK
 /** @} */
 
 
@@ -147,22 +154,22 @@
   #define CFG_TUSB_RHPORT1_MODE OPT_MODE_NONE
 #endif
 
-#if ((CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST  ) && (CFG_TUSB_RHPORT1_MODE & OPT_MODE_HOST  )) || \
-    ((CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE) && (CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE))
+#if (((CFG_TUSB_RHPORT0_MODE) & OPT_MODE_HOST  ) && ((CFG_TUSB_RHPORT1_MODE) & OPT_MODE_HOST  )) || \
+    (((CFG_TUSB_RHPORT0_MODE) & OPT_MODE_DEVICE) && ((CFG_TUSB_RHPORT1_MODE) & OPT_MODE_DEVICE))
   #error "TinyUSB currently does not support same modes on more than 1 roothub port"
 #endif
 
 // Which roothub port is configured as host
-#define TUH_OPT_RHPORT          ( (CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST) ? 0 : ((CFG_TUSB_RHPORT1_MODE & OPT_MODE_HOST) ? 1 : -1) )
+#define TUH_OPT_RHPORT          ( ((CFG_TUSB_RHPORT0_MODE) & OPT_MODE_HOST) ? 0 : (((CFG_TUSB_RHPORT1_MODE) & OPT_MODE_HOST) ? 1 : -1) )
 #define TUSB_OPT_HOST_ENABLED   ( TUH_OPT_RHPORT >= 0 )
 
 // Which roothub port is configured as device
-#define TUD_OPT_RHPORT          ( (CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE) ? 0 : ((CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE) ? 1 : -1) )
+#define TUD_OPT_RHPORT          ( ((CFG_TUSB_RHPORT0_MODE) & OPT_MODE_DEVICE) ? 0 : (((CFG_TUSB_RHPORT1_MODE) & OPT_MODE_DEVICE) ? 1 : -1) )
 
 #if TUD_OPT_RHPORT == 0
-#define TUD_OPT_HIGH_SPEED      ( CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED )
+#define TUD_OPT_HIGH_SPEED      ( (CFG_TUSB_RHPORT0_MODE) & OPT_MODE_HIGH_SPEED )
 #else
-#define TUD_OPT_HIGH_SPEED      ( CFG_TUSB_RHPORT1_MODE & OPT_MODE_HIGH_SPEED )
+#define TUD_OPT_HIGH_SPEED      ( (CFG_TUSB_RHPORT1_MODE) & OPT_MODE_HIGH_SPEED )
 #endif
 
 #define TUSB_OPT_DEVICE_ENABLED ( TUD_OPT_RHPORT >= 0 )

+ 1 - 1
test/test/device/msc/test_msc_device.c

@@ -202,7 +202,7 @@ void setUp(void)
     tusb_init();
   }
 
-  dcd_event_bus_signal(rhport, DCD_EVENT_BUS_RESET, false);
+  dcd_event_bus_reset(rhport, TUSB_SPEED_HIGH, false);
   tud_task();
 }
 

+ 3 - 3
test/test/support/tusb_config.h

@@ -86,8 +86,8 @@
 //------------- CDC -------------//
 
 // FIFO size of CDC TX and RX
-#define CFG_TUD_CDC_RX_BUFSIZE   64
-#define CFG_TUD_CDC_TX_BUFSIZE   64
+#define CFG_TUD_CDC_RX_BUFSIZE   512
+#define CFG_TUD_CDC_TX_BUFSIZE   512
 
 //------------- MSC -------------//
 
@@ -97,7 +97,7 @@
 //------------- HID -------------//
 
 // Should be sufficient to hold ID (if any) + Data
-#define CFG_TUD_HID_EP_BUFSIZE    16
+#define CFG_TUD_HID_EP_BUFSIZE    64
 
 #ifdef __cplusplus
  }

+ 14 - 1
tools/top.mk

@@ -2,6 +2,12 @@ ifneq ($(lastword a b),b)
 $(error This Makefile require make 3.81 or newer)
 endif
 
+# Detect whether shell style is windows or not
+# https://stackoverflow.com/questions/714100/os-detecting-makefile/52062069#52062069
+ifeq '$(findstring ;,$(PATH))' ';'
+CMDEXE := 1
+endif
+
 # Set TOP to be the path to get from the current directory (where make was
 # invoked) to the top of the tree. $(lastword $(MAKEFILE_LIST)) returns
 # the name of this makefile relative to where make was invoked.
@@ -9,9 +15,16 @@ endif
 THIS_MAKEFILE := $(lastword $(MAKEFILE_LIST))
 TOP := $(patsubst %/tools/top.mk,%,$(THIS_MAKEFILE))
 
+ifeq ($(CMDEXE),1)
+TOP := $(subst \,/,$(shell for %%i in ( $(TOP) ) do echo %%~fi))
+else
 TOP := $(shell realpath $(TOP))
-
+endif
 #$(info Top directory is $(TOP))
 
+ifeq ($(CMDEXE),1)
+CURRENT_PATH := $(subst $(TOP)/,,$(subst \,/,$(shell echo %CD%)))
+else
 CURRENT_PATH := $(shell realpath --relative-to=$(TOP) `pwd`)
+endif
 #$(info Path from top is $(CURRENT_PATH))