Explorar o código

Merge pull request #1412 from hathach/pio-host

PIO USB support
Ha Thach %!s(int64=3) %!d(string=hai) anos
pai
achega
c2bcda86e2
Modificáronse 37 ficheiros con 781 adicións e 262 borrados
  1. 1 0
      .github/workflows/build_arm.yml
  2. 3 0
      .gitmodules
  3. 4 0
      examples/device/hid_boot_interface/src/tusb_config.h
  4. 3 1
      examples/dual/host_hid_to_device_cdc/CMakeLists.txt
  5. 0 0
      examples/dual/host_hid_to_device_cdc/Makefile
  6. 1 0
      examples/dual/host_hid_to_device_cdc/only.txt
  7. 98 117
      examples/dual/host_hid_to_device_cdc/src/main.c
  8. 3 9
      examples/dual/host_hid_to_device_cdc/src/tusb_config.h
  9. 0 1
      examples/dual/host_hid_to_device_cdc/src/usb_descriptors.c
  10. 4 1
      examples/host/bare_api/CMakeLists.txt
  11. 4 1
      examples/host/cdc_msc_hid/CMakeLists.txt
  12. 7 0
      examples/host/cdc_msc_hid/src/tusb_config.h
  13. 4 1
      examples/host/hid_controller/CMakeLists.txt
  14. 1 1
      examples/rules.mk
  15. 3 0
      hw/bsp/rp2040/family.c
  16. 54 3
      hw/bsp/rp2040/family.cmake
  17. 1 0
      hw/mcu/raspberry_pi/Pico-PIO-USB
  18. 1 0
      lib/Pico-PIO-USB
  19. 1 1
      src/class/hid/hid_host.c
  20. 6 2
      src/common/tusb_debug.h
  21. 7 0
      src/common/tusb_mcu.h
  22. 28 4
      src/device/dcd.h
  23. 1 33
      src/device/usbd.c
  24. 1 1
      src/device/usbd.h
  25. 47 4
      src/host/hcd.h
  26. 19 59
      src/host/usbh.c
  27. 2 2
      src/portable/ehci/ehci.c
  28. 5 0
      src/portable/mentor/musb/hcd_musb.c
  29. 5 0
      src/portable/ohci/ohci.c
  30. 210 0
      src/portable/raspberrypi/pio_usb/dcd_pio_usb.c
  31. 217 0
      src/portable/raspberrypi/pio_usb/hcd_pio_usb.c
  32. 1 1
      src/portable/raspberrypi/rp2040/dcd_rp2040.c
  33. 10 4
      src/portable/raspberrypi/rp2040/hcd_rp2040.c
  34. 5 0
      src/portable/renesas/usba/hcd_usba.c
  35. 14 4
      src/tusb_option.h
  36. 5 6
      tools/build_board.py
  37. 5 6
      tools/build_family.py

+ 1 - 0
.github/workflows/build_arm.yml

@@ -82,6 +82,7 @@ jobs:
       run: |
         git clone --depth 1 -b develop https://github.com/raspberrypi/pico-sdk ~/pico-sdk
         echo >> $GITHUB_ENV PICO_SDK_PATH=~/pico-sdk
+        git submodule update --init hw/mcu/raspberry_pi/Pico-PIO-USB
 
     - name: Set Toolchain URL
       run: echo >> $GITHUB_ENV TOOLCHAIN_URL=https://github.com/xpack-dev-tools/arm-none-eabi-gcc-xpack/releases/download/v10.2.1-1.1/xpack-arm-none-eabi-gcc-10.2.1-1.1-linux-x64.tar.gz

+ 3 - 0
.gitmodules

@@ -146,3 +146,6 @@
 [submodule "hw/mcu/allwinner"]
 	path = hw/mcu/allwinner
 	url = https://github.com/hathach/allwinner_driver.git
+[submodule "hw/mcu/raspberry_pi/Pico-PIO-USB"]
+	path = hw/mcu/raspberry_pi/Pico-PIO-USB
+	url = https://github.com/sekigon-gonnoc/Pico-PIO-USB.git

+ 4 - 0
examples/device/hid_boot_interface/src/tusb_config.h

@@ -39,6 +39,10 @@
   #error CFG_TUSB_MCU must be defined
 #endif
 
+// Use raspberry pio-usb for device
+// #define CFG_TUD_RPI_PIO_USB     1
+// #define BOARD_DEVICE_RHPORT_NUM 1
+
 // RHPort number used for device can be defined by board.mk, default to port 0
 #ifndef BOARD_DEVICE_RHPORT_NUM
   #define BOARD_DEVICE_RHPORT_NUM     0

+ 3 - 1
examples/host/hid_to_cdc/CMakeLists.txt → examples/dual/host_hid_to_device_cdc/CMakeLists.txt

@@ -25,4 +25,6 @@ target_include_directories(${PROJECT} PUBLIC
 
 # Configure compilation flags and libraries for the example... see the corresponding function
 # in hw/bsp/FAMILY/family.cmake for details.
-family_configure_device_example(${PROJECT})
+family_configure_device_example(${PROJECT})
+family_configure_host_example(${PROJECT})
+family_configure_pico_pio_usb_example(${PROJECT})

+ 0 - 0
examples/host/hid_to_cdc/Makefile → examples/dual/host_hid_to_device_cdc/Makefile


+ 1 - 0
examples/host/hid_to_cdc/only.txt → examples/dual/host_hid_to_device_cdc/only.txt

@@ -1,2 +1,3 @@
 board:mimxrt1060_evk
 board:mimxrt1064_evk
+mcu:RP2040

+ 98 - 117
examples/host/hid_to_cdc/src/main.c → examples/dual/host_hid_to_device_cdc/src/main.c

@@ -23,10 +23,8 @@
  *
  */
 
-// This example runs both host and device concurrently. The USB host looks for
-// any HID device with reports that are 8 bytes long and then assumes they are
-// keyboard reports. It translates the keypresses of the reports to ASCII and
-// transmits it over CDC to the device's host.
+// This example runs both host and device concurrently. The USB host receive
+// reports from HID device and print it out over USB Device CDC interface.
 
 #include <stdlib.h>
 #include <stdio.h>
@@ -73,12 +71,14 @@ enum  {
 static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
 
 void led_blinking_task(void);
-void cdc_task(void);
 
 /*------------- MAIN -------------*/
 int main(void)
 {
   board_init();
+
+  printf("TinyUSB Host HID <-> Device CDC Example\r\n");
+
   tusb_init();
 
   while (1)
@@ -86,15 +86,13 @@ int main(void)
     tud_task(); // tinyusb device task
     tuh_task(); // tinyusb host task
     led_blinking_task();
-
-    cdc_task();
   }
 
   return 0;
 }
 
 //--------------------------------------------------------------------+
-// Device callbacks
+// Device CDC
 //--------------------------------------------------------------------+
 
 // Invoked when device is mounted
@@ -124,8 +122,20 @@ void tud_resume_cb(void)
   blink_interval_ms = BLINK_MOUNTED;
 }
 
+// Invoked when CDC interface received data from host
+void tud_cdc_rx_cb(uint8_t itf)
+{
+  (void) itf;
+
+  char buf[64];
+  uint32_t count = tud_cdc_read(buf, sizeof(buf));
+
+  // TODO control LED on keyboard of host stack
+  (void) count;
+}
+
 //--------------------------------------------------------------------+
-// Host callbacks
+// Host HID
 //--------------------------------------------------------------------+
 
 // Invoked when device with hid interface is mounted
@@ -137,169 +147,140 @@ void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_re
 {
   (void)desc_report;
   (void)desc_len;
+
+  // Interface protocol (hid_interface_protocol_enum_t)
+  const char* protocol_str[] = { "None", "Keyboard", "Mouse" };
+  uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
+
   uint16_t vid, pid;
   tuh_vid_pid_get(dev_addr, &vid, &pid);
 
-  printf("HID device address = %d, instance = %d is mounted\r\n", dev_addr, instance);
-  printf("VID = %04x, PID = %04x\r\n", vid, pid);
+  char tempbuf[256];
+  int count = sprintf(tempbuf, "[%04x:%04x][%u] HID Interface%u, Protocol = %s\r\n", vid, pid, dev_addr, instance, protocol_str[itf_protocol]);
+
+  tud_cdc_write(tempbuf, count);
+  tud_cdc_write_flush();
 
-  // Receive any report and treat it like a keyboard.
+  // Receive report from boot keyboard & mouse only
   // tuh_hid_report_received_cb() will be invoked when report is available
-  if ( !tuh_hid_receive_report(dev_addr, instance) )
+  if (itf_protocol == HID_ITF_PROTOCOL_KEYBOARD || itf_protocol == HID_ITF_PROTOCOL_MOUSE)
   {
-    printf("Error: cannot request to receive report\r\n");
+    if ( !tuh_hid_receive_report(dev_addr, instance) )
+    {
+      tud_cdc_write_str("Error: cannot request report\r\n");
+    }
   }
 }
 
 // Invoked when device with hid interface is un-mounted
 void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance)
 {
-  printf("HID device address = %d, instance = %d is unmounted\r\n", dev_addr, instance);
+  char tempbuf[256];
+  int count = sprintf(tempbuf, "[%u] HID Interface%u is unmounted\r\n", dev_addr, instance);
+  tud_cdc_write(tempbuf, count);
+  tud_cdc_write_flush();
 }
 
-// keycodes from last report to check if key is holding or newly pressed
-uint8_t last_keycodes[6] = {0};
-
 // look up new key in previous keys
-static inline bool key_in_last_report(const uint8_t key_arr[6], uint8_t keycode)
+static inline bool find_key_in_report(hid_keyboard_report_t const *report, uint8_t keycode)
 {
   for(uint8_t i=0; i<6; i++)
   {
-    if (key_arr[i] == keycode) return true;
+    if (report->keycode[i] == keycode)  return true;
   }
 
   return false;
 }
 
-// Invoked when received report from device via interrupt endpoint
-void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len)
-{
-  if (len != 8)
-  {
-    char ch_num;
-
-    tud_cdc_write_str("incorrect report len: ");
-
-    if ( len > 10 )
-    {
-      ch_num = '0' + (len / 10);
-      tud_cdc_write(&ch_num, 1);
-      len = len % 10;
-    }
-
-    ch_num = '0' + len;
-    tud_cdc_write(&ch_num, 1);
-
-    tud_cdc_write_str("\r\n");
-    tud_cdc_write_flush();
 
-    // Don't request a new report for a wrong sized endpoint.
-    return;
-  }
-
-  uint8_t const modifiers = report[0];
+// convert hid keycode to ascii and print via usb device CDC (ignore non-printable)
+static void process_kbd_report(uint8_t dev_addr, hid_keyboard_report_t const *report)
+{
+  (void) dev_addr;
+  static hid_keyboard_report_t prev_report = { 0, 0, {0} }; // previous report to check key released
   bool flush = false;
 
-  for (int i = 2; i < 8; i++)
+  for(uint8_t i=0; i<6; i++)
   {
-    uint8_t keycode = report[i];
-
-    if (keycode)
+    uint8_t keycode = report->keycode[i];
+    if ( keycode )
     {
-      if ( key_in_last_report(last_keycodes, keycode) )
+      if ( find_key_in_report(&prev_report, keycode) )
       {
         // exist in previous report means the current key is holding
-        // do nothing
       }else
       {
         // not existed in previous report means the current key is pressed
-        // Only print keycodes 0 - 128.
-        if (keycode < 128)
+
+        // remap the key code for Colemak layout
+        #ifdef KEYBOARD_COLEMAK
+        uint8_t colemak_key_code = colemak[keycode];
+        if (colemak_key_code != 0) keycode = colemak_key_code;
+        #endif
+
+        bool const is_shift = report->modifier & (KEYBOARD_MODIFIER_LEFTSHIFT | KEYBOARD_MODIFIER_RIGHTSHIFT);
+        uint8_t ch = keycode2ascii[keycode][is_shift ? 1 : 0];
+
+        if (ch)
         {
-          // remap the key code for Colemak layout so @tannewt can type.
-          #ifdef KEYBOARD_COLEMAK
-          uint8_t colemak_key_code = colemak[keycode];
-          if (colemak_key_code != 0) keycode = colemak_key_code;
-          #endif
-
-          bool const is_shift = modifiers & (KEYBOARD_MODIFIER_LEFTSHIFT | KEYBOARD_MODIFIER_RIGHTSHIFT);
-          char c = keycode2ascii[keycode][is_shift ? 1 : 0];
-          if (c)
-          {
-            if (c == '\n') tud_cdc_write("\r", 1);
-            tud_cdc_write(&c, 1);
-            flush = true;
-          }
+          if (ch == '\n') tud_cdc_write("\r", 1);
+          tud_cdc_write(&ch, 1);
+          flush = true;
         }
       }
     }
+    // TODO example skips key released
   }
 
   if (flush) tud_cdc_write_flush();
 
-  // save current report
-  memcpy(last_keycodes, report+2, 6);
-
-  // continue to request to receive report
-  if ( !tuh_hid_receive_report(dev_addr, instance) )
-  {
-    printf("Error: cannot request to receive report\r\n");
-  }
+  prev_report = *report;
 }
 
+// send mouse report to usb device CDC
+static void process_mouse_report(uint8_t dev_addr, hid_mouse_report_t const * report)
+{
+  //------------- button state  -------------//
+  //uint8_t button_changed_mask = report->buttons ^ prev_report.buttons;
+  char l = report->buttons & MOUSE_BUTTON_LEFT   ? 'L' : '-';
+  char m = report->buttons & MOUSE_BUTTON_MIDDLE ? 'M' : '-';
+  char r = report->buttons & MOUSE_BUTTON_RIGHT  ? 'R' : '-';
 
+  char tempbuf[32];
+  int count = sprintf(tempbuf, "[%u] %c%c%c %d %d %d\r\n", dev_addr, l, m, r, report->x, report->y, report->wheel);
 
-//--------------------------------------------------------------------+
-// USB CDC
-//--------------------------------------------------------------------+
-void cdc_task(void)
-{
-  // connected() check for DTR bit
-  // Most but not all terminal client set this when making connection
-  // if ( tud_cdc_connected() )
-  {
-    // connected and there are data available
-    if ( tud_cdc_available() )
-    {
-      // read datas
-      char buf[64];
-      uint32_t count = tud_cdc_read(buf, sizeof(buf));
-      (void) count;
-
-      // Echo back
-      // Note: Skip echo by commenting out write() and write_flush()
-      // for throughput test e.g
-      //    $ dd if=/dev/zero of=/dev/ttyACM0 count=10000
-      tud_cdc_write(buf, count);
-      tud_cdc_write_flush();
-    }
-  }
+  tud_cdc_write(tempbuf, count);
+  tud_cdc_write_flush();
 }
 
-// Invoked when cdc when line state changed e.g connected/disconnected
-void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts)
+// Invoked when received report from device via interrupt endpoint
+void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len)
 {
-  (void) itf;
-  (void) rts;
+  (void) len;
+  uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
 
-  // TODO set some indicator
-  if ( dtr )
+  switch(itf_protocol)
   {
-    // Terminal connected
-  }else
-  {
-    // Terminal disconnected
+    case HID_ITF_PROTOCOL_KEYBOARD:
+      process_kbd_report(dev_addr, (hid_keyboard_report_t const*) report );
+    break;
+
+    case HID_ITF_PROTOCOL_MOUSE:
+      process_mouse_report(dev_addr, (hid_mouse_report_t const*) report );
+    break;
+
+    default: break;
   }
-}
 
-// Invoked when CDC interface received data from host
-void tud_cdc_rx_cb(uint8_t itf)
-{
-  (void) itf;
+  // continue to request to receive report
+  if ( !tuh_hid_receive_report(dev_addr, instance) )
+  {
+    tud_cdc_write_str("Error: cannot request report\r\n");
+  }
 }
 
 //--------------------------------------------------------------------+
-// BLINKING TASK
+// Blinking Task
 //--------------------------------------------------------------------+
 void led_blinking_task(void)
 {

+ 3 - 9
examples/host/hid_to_cdc/src/tusb_config.h → examples/dual/host_hid_to_device_cdc/src/tusb_config.h

@@ -49,6 +49,9 @@
   #define BOARD_HOST_RHPORT_NUM     1
 #endif
 
+// Use raspberry pio-usb for host
+#define CFG_TUH_RPI_PIO_USB            1
+
 // RHPort max operational speed can defined by board.mk
 // Default to Highspeed for MCU with internal HighSpeed PHY (can be port specific), otherwise FullSpeed
 #ifndef BOARD_DEVICE_RHPORT_SPEED
@@ -124,10 +127,6 @@
 
 //------------- CLASS -------------//
 #define CFG_TUD_CDC              1
-#define CFG_TUD_MSC              0
-#define CFG_TUD_HID              0
-#define CFG_TUD_MIDI             0
-#define CFG_TUD_VENDOR           0
 
 // CDC FIFO size of TX and RX
 #define CFG_TUD_CDC_RX_BUFSIZE   (TUD_OPT_HIGH_SPEED ? 512 : 64)
@@ -144,14 +143,9 @@
 #define CFG_TUH_ENUMERATION_BUFSIZE 256
 
 #define CFG_TUH_HUB                 1
-#define CFG_TUH_CDC                 0
-#define CFG_TUH_MSC                 0
-#define CFG_TUH_VENDOR              0
-
 // max device support (excluding hub device)
 #define CFG_TUH_DEVICE_MAX          (CFG_TUH_HUB ? 4 : 1) // hub typically has 4 ports
 
-//------------- HID -------------//
 #define CFG_TUH_HID                  4
 #define CFG_TUH_HID_EPIN_BUFSIZE    64
 #define CFG_TUH_HID_EPOUT_BUFSIZE   64

+ 0 - 1
examples/host/hid_to_cdc/src/usb_descriptors.c → examples/dual/host_hid_to_device_cdc/src/usb_descriptors.c

@@ -81,7 +81,6 @@ enum
 {
   ITF_NUM_CDC = 0,
   ITF_NUM_CDC_DATA,
-  ITF_NUM_MSC,
   ITF_NUM_TOTAL
 };
 

+ 4 - 1
examples/host/bare_api/CMakeLists.txt

@@ -24,4 +24,7 @@ target_include_directories(${PROJECT} PUBLIC
 
 # Configure compilation flags and libraries for the example... see the corresponding function
 # in hw/bsp/FAMILY/family.cmake for details.
-family_configure_host_example(${PROJECT})
+family_configure_host_example(${PROJECT})
+
+# For rp2040, un-comment to enable pico-pio-usb
+# family_configure_pico_pio_usb_example(${PROJECT})

+ 4 - 1
examples/host/cdc_msc_hid/CMakeLists.txt

@@ -26,4 +26,7 @@ target_include_directories(${PROJECT} PUBLIC
 
 # Configure compilation flags and libraries for the example... see the corresponding function
 # in hw/bsp/FAMILY/family.cmake for details.
-family_configure_host_example(${PROJECT})
+family_configure_host_example(${PROJECT})
+
+# For rp2040, un-comment to enable pico-pio-usb
+# family_configure_pico_pio_usb_example(${PROJECT})

+ 7 - 0
examples/host/cdc_msc_hid/src/tusb_config.h

@@ -39,8 +39,15 @@
   #error CFG_TUSB_MCU must be defined
 #endif
 
+// Use raspberry pio-usb for host
+// #define CFG_TUH_RPI_PIO_USB            1
+// #define CFG_TUH_RPI_PIO_USB            1
+
 #if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX
   #define CFG_TUSB_RHPORT0_MODE       (OPT_MODE_HOST | OPT_MODE_HIGH_SPEED)
+#elif defined(CFG_TUH_RPI_PIO_USB) && CFG_TUH_RPI_PIO_USB
+  // rp2040: port0 is native, port 1 for PIO-USB
+  #define CFG_TUSB_RHPORT1_MODE       OPT_MODE_HOST
 #else
   #define CFG_TUSB_RHPORT0_MODE       OPT_MODE_HOST
 #endif

+ 4 - 1
examples/host/hid_controller/CMakeLists.txt

@@ -25,4 +25,7 @@ target_include_directories(${PROJECT} PUBLIC
 
 # Configure compilation flags and libraries for the example... see the corresponding function
 # in hw/bsp/FAMILY/family.cmake for details.
-family_configure_host_example(${PROJECT})
+family_configure_host_example(${PROJECT})
+
+# For rp2040, un-comment to enable pico-pio-usb
+# family_configure_pico_pio_usb_example(${PROJECT})

+ 1 - 1
examples/rules.mk

@@ -199,7 +199,7 @@ flash-xfel: $(BUILD)/$(PROJECT)-sunxi.bin
 PYOCD_OPTION ?=
 flash-pyocd: $(BUILD)/$(PROJECT).hex
 	pyocd flash -t $(PYOCD_TARGET) $(PYOCD_OPTION) $<
-	pyocd reset -t $(PYOCD_TARGET)
+	#pyocd reset -t $(PYOCD_TARGET)
 
 # Flash using openocd
 OPENOCD_OPTION ?=

+ 3 - 0
hw/bsp/rp2040/family.c

@@ -127,6 +127,9 @@ void board_init(void)
 #ifndef BUTTON_BOOTSEL
 #endif
 
+  // Set the system clock to a multiple of 120mhz for bitbanging USB with pico-usb
+  set_sys_clock_khz(120000, true);
+
 #if defined(UART_DEV) && defined(LIB_PICO_STDIO_UART)
   bi_decl(bi_2pins_with_func(UART_TX_PIN, UART_TX_PIN, GPIO_FUNC_UART));
   uart_inst = uart_get_instance(UART_DEV);

+ 54 - 3
hw/bsp/rp2040/family.cmake

@@ -23,9 +23,11 @@ if (NOT TARGET _rp2040_family_inclusion_marker)
 		set(PICO_TINYUSB_PATH ${TOP})
 	endif()
 
+  #------------------------------------
 	# Base config for both device and host; wrapped by SDK's tinyusb_common
+	#------------------------------------
 	add_library(tinyusb_common_base INTERFACE)
-	
+
 	target_sources(tinyusb_common_base INTERFACE
 			${TOP}/src/tusb.c
 			${TOP}/src/common/tusb_fifo.c
@@ -56,11 +58,44 @@ if (NOT TARGET _rp2040_family_inclusion_marker)
 			CFG_TUSB_DEBUG=${TINYUSB_DEBUG_LEVEL}
 	)
 
+  #------------------------------------
+  # PIO USB for both host and device
+  #------------------------------------
+  add_library(pico_pio_usb INTERFACE)
+
+  if (NOT DEFINED PICO_PIO_USB_PATH)
+    set(PICO_PIO_USB_PATH "${TOP}/hw/mcu/raspberry_pi/Pico-PIO-USB")
+  endif()
+
+	target_sources(pico_pio_usb INTERFACE
+			${PICO_PIO_USB_PATH}/src/pio_usb.c
+			${PICO_PIO_USB_PATH}/src/pio_usb_host.c
+			${PICO_PIO_USB_PATH}/src/pio_usb_device.c
+			${PICO_PIO_USB_PATH}/src/usb_crc.c
+			)
+
+	target_include_directories(pico_pio_usb INTERFACE
+			${PICO_PIO_USB_PATH}/src
+			)
+
+	target_link_libraries(pico_pio_usb INTERFACE
+	    hardware_dma
+	    hardware_pio
+	    pico_multicore
+			)
+
+	target_compile_definitions(pico_pio_usb INTERFACE
+			PIO_USB_USE_TINYUSB
+	)
+
+  #------------------------------------
 	# Base config for device mode; wrapped by SDK's tinyusb_device
+	#------------------------------------
 	add_library(tinyusb_device_base INTERFACE)
 	target_sources(tinyusb_device_base INTERFACE
 			${TOP}/src/portable/raspberrypi/rp2040/dcd_rp2040.c
 			${TOP}/src/portable/raspberrypi/rp2040/rp2040_usb.c
+		  ${TOP}/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c
 			${TOP}/src/device/usbd.c
 			${TOP}/src/device/usbd_control.c
 			${TOP}/src/class/audio/audio_device.c
@@ -77,11 +112,14 @@ if (NOT TARGET _rp2040_family_inclusion_marker)
 			${TOP}/src/class/video/video_device.c
 			)
 
+	#------------------------------------
 	# Base config for host mode; wrapped by SDK's tinyusb_host
+	#------------------------------------
 	add_library(tinyusb_host_base INTERFACE)
 	target_sources(tinyusb_host_base INTERFACE
 			${TOP}/src/portable/raspberrypi/rp2040/hcd_rp2040.c
 			${TOP}/src/portable/raspberrypi/rp2040/rp2040_usb.c
+		  ${TOP}/src/portable/raspberrypi/pio_usb/hcd_pio_usb.c
 			${TOP}/src/host/usbh.c
 			${TOP}/src/host/hub.c
 			${TOP}/src/class/cdc/cdc_host.c
@@ -90,12 +128,14 @@ if (NOT TARGET _rp2040_family_inclusion_marker)
 			${TOP}/src/class/vendor/vendor_host.c
 			)
 
-	# Sometimes have to do host specific actions in mostly
-	# common functions
+	# Sometimes have to do host specific actions in mostly common functions
 	target_compile_definitions(tinyusb_host_base INTERFACE
 			RP2040_USB_HOST_MODE=1
 	)
 
+	#------------------------------------
+	# BSP & Additions
+	#------------------------------------
 	add_library(tinyusb_bsp INTERFACE)
 	target_sources(tinyusb_bsp INTERFACE
 			${TOP}/hw/bsp/rp2040/family.c
@@ -129,6 +169,10 @@ if (NOT TARGET _rp2040_family_inclusion_marker)
 	  )
 	endif()
 
+	#------------------------------------
+	# Functions
+	#------------------------------------
+
 	function(family_configure_target TARGET)
 		pico_add_extra_outputs(${TARGET})
 		pico_enable_stdio_uart(${TARGET} 1)
@@ -145,6 +189,13 @@ if (NOT TARGET _rp2040_family_inclusion_marker)
 		target_link_libraries(${TARGET} PUBLIC pico_stdlib tinyusb_host)
 	endfunction()
 
+	function(family_configure_pico_pio_usb_example TARGET)
+		family_configure_target(${TARGET})
+		target_link_libraries(${TARGET} PUBLIC pico_stdlib pico_pio_usb)
+    pico_generate_pio_header(tinyusb_common_base ${PICO_PIO_USB_PATH}/src/usb_tx.pio)
+    pico_generate_pio_header(tinyusb_common_base ${PICO_PIO_USB_PATH}/src/usb_rx.pio)
+	endfunction()
+
 	function(family_initialize_project PROJECT DIR)
 		# call the original version of this function from family_common.cmake
 		_family_initialize_project(${PROJECT} ${DIR})

+ 1 - 0
hw/mcu/raspberry_pi/Pico-PIO-USB

@@ -0,0 +1 @@
+Subproject commit 2a9fefd6ccf42e5d8570ae83fdc54c9c875ebdd1

+ 1 - 0
lib/Pico-PIO-USB

@@ -0,0 +1 @@
+Subproject commit 53ec09168c5a3cd30d8dcb577fa97dc328d769d4

+ 1 - 1
src/class/hid/hid_host.c

@@ -329,7 +329,7 @@ bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *de
 
   TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass);
 
-  TU_LOG2("HID opening Interface %u (addr = %u)\r\n", desc_itf->bInterfaceNumber, dev_addr);
+  TU_LOG2("[%u] HID opening Interface %u\r\n", dev_addr, desc_itf->bInterfaceNumber);
 
   // len = interface + hid + n*endpoints
   uint16_t const drv_len = sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t);

+ 6 - 2
src/common/tusb_debug.h

@@ -57,7 +57,7 @@ void tu_print_mem(void const *buf, uint32_t count, uint8_t indent);
   #define tu_printf    printf
 #endif
 
-static inline void tu_print_var(uint8_t const* buf, uint32_t bufsize)
+static inline void tu_print_arr(uint8_t const* buf, uint32_t bufsize)
 {
   for(uint32_t i=0; i<bufsize; i++) tu_printf("%02X ", buf[i]);
 }
@@ -65,6 +65,7 @@ static inline void tu_print_var(uint8_t const* buf, uint32_t bufsize)
 // Log with Level
 #define TU_LOG(n, ...)        TU_XSTRCAT(TU_LOG, n)(__VA_ARGS__)
 #define TU_LOG_MEM(n, ...)    TU_XSTRCAT3(TU_LOG, n, _MEM)(__VA_ARGS__)
+#define TU_LOG_ARR(n, ...)    TU_XSTRCAT3(TU_LOG, n, _ARR)(__VA_ARGS__)
 #define TU_LOG_VAR(n, ...)    TU_XSTRCAT3(TU_LOG, n, _VAR)(__VA_ARGS__)
 #define TU_LOG_INT(n, ...)    TU_XSTRCAT3(TU_LOG, n, _INT)(__VA_ARGS__)
 #define TU_LOG_HEX(n, ...)    TU_XSTRCAT3(TU_LOG, n, _HEX)(__VA_ARGS__)
@@ -74,7 +75,8 @@ static inline void tu_print_var(uint8_t const* buf, uint32_t bufsize)
 // Log Level 1: Error
 #define TU_LOG1               tu_printf
 #define TU_LOG1_MEM           tu_print_mem
-#define TU_LOG1_VAR(_x)       tu_print_var((uint8_t const*)(_x), sizeof(*(_x)))
+#define TU_LOG1_ARR(_x, _n)   tu_print_arr((uint8_t const*)(_x), _n)
+#define TU_LOG1_VAR(_x)       tu_print_arr((uint8_t const*)(_x), sizeof(*(_x)))
 #define TU_LOG1_INT(_x)       tu_printf(#_x " = %ld\r\n", (unsigned long) (_x) )
 #define TU_LOG1_HEX(_x)       tu_printf(#_x " = %lX\r\n", (unsigned long) (_x) )
 
@@ -82,6 +84,7 @@ static inline void tu_print_var(uint8_t const* buf, uint32_t bufsize)
 #if CFG_TUSB_DEBUG >= 2
   #define TU_LOG2             TU_LOG1
   #define TU_LOG2_MEM         TU_LOG1_MEM
+  #define TU_LOG2_ARR         TU_LOG1_ARR
   #define TU_LOG2_VAR         TU_LOG1_VAR
   #define TU_LOG2_INT         TU_LOG1_INT
   #define TU_LOG2_HEX         TU_LOG1_HEX
@@ -91,6 +94,7 @@ static inline void tu_print_var(uint8_t const* buf, uint32_t bufsize)
 #if CFG_TUSB_DEBUG >= 3
   #define TU_LOG3             TU_LOG1
   #define TU_LOG3_MEM         TU_LOG1_MEM
+  #define TU_LOG3_ARR         TU_LOG1_ARR
   #define TU_LOG3_VAR         TU_LOG1_VAR
   #define TU_LOG3_INT         TU_LOG1_INT
   #define TU_LOG3_HEX         TU_LOG1_HEX

+ 7 - 0
src/common/tusb_mcu.h

@@ -228,6 +228,8 @@
 #elif TU_CHECK_MCU(OPT_MCU_RP2040)
   #define TUP_DCD_ENDPOINT_MAX    16
 
+  #define TU_ATTR_FAST_FUNC   __attribute__((section(".time_critical.tinyusb")))
+
 //------------- Silabs -------------//
 #elif TU_CHECK_MCU(OPT_MCU_EFM32GG)
   #define TUP_USBIP_DWC2
@@ -282,4 +284,9 @@
   #define TUP_RHPORT_HIGHSPEED    0x00
 #endif
 
+// fast function, normally mean placing function in SRAM
+#ifndef TU_ATTR_FAST_FUNC
+  #define TU_ATTR_FAST_FUNC
+#endif
+
 #endif

+ 28 - 4
src/device/dcd.h

@@ -174,16 +174,40 @@ void dcd_edpt_clear_stall     (uint8_t rhport, uint8_t ep_addr);
 extern void dcd_event_handler(dcd_event_t const * event, bool in_isr);
 
 // helper to send bus signal event
-extern void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr);
+TU_ATTR_ALWAYS_INLINE static inline void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr)
+{
+  dcd_event_t event = { .rhport = rhport, .event_id = eid };
+  dcd_event_handler(&event, in_isr);
+}
 
 // helper to send bus reset event
-extern void dcd_event_bus_reset (uint8_t rhport, tusb_speed_t speed, bool in_isr);
+TU_ATTR_ALWAYS_INLINE static inline  void dcd_event_bus_reset (uint8_t rhport, tusb_speed_t speed, bool in_isr)
+{
+  dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_BUS_RESET };
+  event.bus_reset.speed = speed;
+  dcd_event_handler(&event, in_isr);
+}
 
 // helper to send setup received
-extern void dcd_event_setup_received(uint8_t rhport, uint8_t const * setup, bool in_isr);
+TU_ATTR_ALWAYS_INLINE static inline void dcd_event_setup_received(uint8_t rhport, uint8_t const * setup, bool in_isr)
+{
+  dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_SETUP_RECEIVED };
+  memcpy(&event.setup_received, setup, 8);
+
+  dcd_event_handler(&event, in_isr);
+}
 
 // helper to send transfer complete event
-extern void dcd_event_xfer_complete (uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool in_isr);
+TU_ATTR_ALWAYS_INLINE static inline void dcd_event_xfer_complete (uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool in_isr)
+{
+  dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_XFER_COMPLETE };
+
+  event.xfer_complete.ep_addr = ep_addr;
+  event.xfer_complete.len     = xferred_bytes;
+  event.xfer_complete.result  = result;
+
+  dcd_event_handler(&event, in_isr);
+}
 
 #ifdef __cplusplus
  }

+ 1 - 33
src/device/usbd.c

@@ -1071,7 +1071,7 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const
 //--------------------------------------------------------------------+
 // DCD Event Handler
 //--------------------------------------------------------------------+
-void dcd_event_handler(dcd_event_t const * event, bool in_isr)
+TU_ATTR_FAST_FUNC void dcd_event_handler(dcd_event_t const * event, bool in_isr)
 {
   switch (event->event_id)
   {
@@ -1121,38 +1121,6 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr)
   }
 }
 
-void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr)
-{
-  dcd_event_t event = { .rhport = rhport, .event_id = eid };
-  dcd_event_handler(&event, in_isr);
-}
-
-void dcd_event_bus_reset (uint8_t rhport, tusb_speed_t speed, bool in_isr)
-{
-  dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_BUS_RESET };
-  event.bus_reset.speed = speed;
-  dcd_event_handler(&event, in_isr);
-}
-
-void dcd_event_setup_received(uint8_t rhport, uint8_t const * setup, bool in_isr)
-{
-  dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_SETUP_RECEIVED };
-  memcpy(&event.setup_received, setup, 8);
-
-  dcd_event_handler(&event, in_isr);
-}
-
-void dcd_event_xfer_complete (uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool in_isr)
-{
-  dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_XFER_COMPLETE };
-
-  event.xfer_complete.ep_addr = ep_addr;
-  event.xfer_complete.len     = xferred_bytes;
-  event.xfer_complete.result  = result;
-
-  dcd_event_handler(&event, in_isr);
-}
-
 //--------------------------------------------------------------------+
 // USBD API For Class Driver
 //--------------------------------------------------------------------+

+ 1 - 1
src/device/usbd.h

@@ -55,7 +55,7 @@ void tud_task (void)
   tud_task_ext(UINT32_MAX, false);
 }
 
-// Check if there is pending events need proccessing by tud_task()
+// Check if there is pending events need processing by tud_task()
 bool tud_task_event_ready(void);
 
 // Interrupt handler, name alias to DCD

+ 47 - 4
src/host/hcd.h

@@ -144,9 +144,16 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr);
 // Endpoints API
 //--------------------------------------------------------------------+
 
-bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]);
+// Open an endpoint
 bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc);
+
+// Submit a transfer, when complete hcd_event_xfer_complete() must be invoked
 bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen);
+
+// Submit a special transfer to send 8-byte Setup Packet, when complete hcd_event_xfer_complete() must be invoked
+bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]);
+
+// clear stall, data toggle is also reset to DATA0
 bool hcd_edpt_clear_stall(uint8_t dev_addr, uint8_t ep_addr);
 
 //--------------------------------------------------------------------+
@@ -164,13 +171,49 @@ extern void hcd_devtree_get_info(uint8_t dev_addr, hcd_devtree_info_t* devtree_i
 extern void hcd_event_handler(hcd_event_t const* event, bool in_isr);
 
 // Helper to send device attach event
-extern void hcd_event_device_attach(uint8_t rhport, bool in_isr);
+TU_ATTR_ALWAYS_INLINE static inline
+void hcd_event_device_attach(uint8_t rhport, bool in_isr)
+{
+  hcd_event_t event;
+  event.rhport              = rhport;
+  event.event_id            = HCD_EVENT_DEVICE_ATTACH;
+  event.connection.hub_addr = 0;
+  event.connection.hub_port = 0;
+  hcd_event_handler(&event, in_isr);
+}
 
 // Helper to send device removal event
-extern void hcd_event_device_remove(uint8_t rhport, bool in_isr);
+TU_ATTR_ALWAYS_INLINE static inline
+void hcd_event_device_remove(uint8_t rhport, bool in_isr)
+{
+  hcd_event_t event;
+  event.rhport              = rhport;
+  event.event_id            = HCD_EVENT_DEVICE_REMOVE;
+  event.connection.hub_addr = 0;
+  event.connection.hub_port = 0;
+
+  hcd_event_handler(&event, in_isr);
+}
 
 // Helper to send USB transfer event
-extern void hcd_event_xfer_complete(uint8_t dev_addr, uint8_t ep_addr, uint32_t xferred_bytes, xfer_result_t result, bool in_isr);
+TU_ATTR_ALWAYS_INLINE static inline
+void hcd_event_xfer_complete(uint8_t dev_addr, uint8_t ep_addr, uint32_t xferred_bytes, xfer_result_t result, bool in_isr)
+{
+  hcd_event_t event =
+  {
+    .rhport   = 0, // TODO correct rhport
+    .event_id = HCD_EVENT_XFER_COMPLETE,
+    .dev_addr = dev_addr,
+    .xfer_complete =
+    {
+      .ep_addr = ep_addr,
+      .result  = result,
+      .len     = xferred_bytes
+    }
+  };
+
+  hcd_event_handler(&event, in_isr);
+}
 
 #ifdef __cplusplus
  }

+ 19 - 59
src/host/usbh.c

@@ -410,7 +410,7 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr)
       case HCD_EVENT_DEVICE_ATTACH:
         // TODO due to the shared _usbh_ctrl_buf, we must complete enumerating
         // one device before enumerating another one.
-        TU_LOG2("USBH DEVICE ATTACH\r\n");
+        TU_LOG2("[%u:] USBH DEVICE ATTACH\r\n", event.rhport);
         enum_new_device(&event);
       break;
 
@@ -550,12 +550,12 @@ bool tuh_control_xfer (tuh_xfer_t* xfer)
   const uint8_t rhport = usbh_get_rhport(daddr);
 
   TU_LOG2("[%u:%u] %s: ", rhport, daddr, xfer->setup->bRequest <= TUSB_REQ_SYNCH_FRAME ? tu_str_std_request[xfer->setup->bRequest] : "Unknown Request");
-  TU_LOG2_VAR(&xfer->setup);
+  TU_LOG2_VAR(xfer->setup);
   TU_LOG2("\r\n");
 
   if (xfer->complete_cb)
   {
-    TU_ASSERT( hcd_setup_send(rhport, daddr, (uint8_t*) &_ctrl_xfer.request) );
+    TU_ASSERT( hcd_setup_send(rhport, daddr, (uint8_t const*) &_ctrl_xfer.request) );
   }else
   {
     // blocking if complete callback is not provided
@@ -630,7 +630,7 @@ static bool usbh_control_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result
 
   if (XFER_RESULT_SUCCESS != result)
   {
-    TU_LOG2("[%u:%u] Control %s\r\n", rhport, dev_addr, result == XFER_RESULT_STALLED ? "STALLED" : "FAILED");
+    TU_LOG1("[%u:%u] Control %s\r\n", rhport, dev_addr, result == XFER_RESULT_STALLED ? "STALLED" : "FAILED");
 
     // terminate transfer if any stage failed
     _xfer_complete(dev_addr, result);
@@ -643,12 +643,13 @@ static bool usbh_control_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result
         {
           // DATA stage: initial data toggle is always 1
           _set_control_xfer_stage(CONTROL_STAGE_DATA);
-          return hcd_edpt_xfer(rhport, dev_addr, tu_edpt_addr(0, request->bmRequestType_bit.direction), _ctrl_xfer.buffer, request->wLength);
+          TU_ASSERT( hcd_edpt_xfer(rhport, dev_addr, tu_edpt_addr(0, request->bmRequestType_bit.direction), _ctrl_xfer.buffer, request->wLength) );
+          return true;
         }
         __attribute__((fallthrough));
 
       case CONTROL_STAGE_DATA:
-        if (xferred_bytes)
+        if (request->wLength)
         {
           TU_LOG2("[%u:%u] Control data:\r\n", rhport, dev_addr);
           TU_LOG2_MEM(_ctrl_xfer.buffer, xferred_bytes, 2);
@@ -658,7 +659,7 @@ static bool usbh_control_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result
 
         // ACK stage: toggle is always 1
         _set_control_xfer_stage(CONTROL_STAGE_ACK);
-        hcd_edpt_xfer(rhport, dev_addr, tu_edpt_addr(0, 1-request->bmRequestType_bit.direction), NULL, 0);
+        TU_ASSERT( hcd_edpt_xfer(rhport, dev_addr, tu_edpt_addr(0, 1-request->bmRequestType_bit.direction), NULL, 0) );
       break;
 
       case CONTROL_STAGE_ACK:
@@ -798,7 +799,7 @@ bool usbh_edpt_xfer_with_callback(uint8_t dev_addr, uint8_t ep_addr, uint8_t * b
 
 static bool usbh_edpt_control_open(uint8_t dev_addr, uint8_t max_packet_size)
 {
-  TU_LOG2("Open EP0 with Size = %u (addr = %u)\r\n", max_packet_size, dev_addr);
+  TU_LOG2("[%u:%u] Open EP0 with Size = %u\r\n", usbh_get_rhport(dev_addr), dev_addr, max_packet_size);
 
   tusb_desc_endpoint_t ep0_desc =
   {
@@ -854,7 +855,7 @@ void hcd_devtree_get_info(uint8_t dev_addr, hcd_devtree_info_t* devtree_info)
   }
 }
 
-void hcd_event_handler(hcd_event_t const* event, bool in_isr)
+TU_ATTR_FAST_FUNC void hcd_event_handler(hcd_event_t const* event, bool in_isr)
 {
   switch (event->event_id)
   {
@@ -864,52 +865,6 @@ void hcd_event_handler(hcd_event_t const* event, bool in_isr)
   }
 }
 
-void hcd_event_xfer_complete(uint8_t dev_addr, uint8_t ep_addr, uint32_t xferred_bytes, xfer_result_t result, bool in_isr)
-{
-  hcd_event_t event =
-  {
-    .rhport   = 0, // TODO correct rhport
-    .event_id = HCD_EVENT_XFER_COMPLETE,
-    .dev_addr = dev_addr,
-    .xfer_complete =
-    {
-      .ep_addr = ep_addr,
-      .result  = result,
-      .len     = xferred_bytes
-    }
-  };
-
-  hcd_event_handler(&event, in_isr);
-}
-
-void hcd_event_device_attach(uint8_t rhport, bool in_isr)
-{
-  hcd_event_t event =
-  {
-    .rhport   = rhport,
-    .event_id = HCD_EVENT_DEVICE_ATTACH
-  };
-
-  event.connection.hub_addr = 0;
-  event.connection.hub_port = 0;
-
-  hcd_event_handler(&event, in_isr);
-}
-
-void hcd_event_device_remove(uint8_t hostid, bool in_isr)
-{
-  hcd_event_t event =
-  {
-    .rhport   = hostid,
-    .event_id = HCD_EVENT_DEVICE_REMOVE
-  };
-
-  event.connection.hub_addr = 0;
-  event.connection.hub_port = 0;
-
-  hcd_event_handler(&event, in_isr);
-}
-
 //--------------------------------------------------------------------+
 // Descriptors Async
 //--------------------------------------------------------------------+
@@ -1184,7 +1139,7 @@ enum {
   //ENUM_HUB_GET_STATUS_1,
   ENUM_HUB_CLEAR_RESET_1,
   ENUM_ADDR0_DEVICE_DESC,
-  ENUM_RESET_2,         // 2nd reset before set address
+  ENUM_RESET_2,         // 2nd reset before set address (not used)
   ENUM_HUB_GET_STATUS_2,
   ENUM_HUB_CLEAR_RESET_2,
   ENUM_SET_ADDR,
@@ -1272,15 +1227,17 @@ static void process_enumeration(tuh_xfer_t* xfer)
     }
     break;
 
+#if 0
     case ENUM_RESET_2:
+      // XXX note used by now, but may be needed for some devices !?
       // Reset device again before Set Address
-      TU_LOG2("Port reset \r\n");
+      TU_LOG2("Port reset2 \r\n");
       if (_dev0.hub_addr == 0)
       {
         // connected directly to roothub
         hcd_port_reset( _dev0.rhport );
         osal_task_delay(RESET_DELAY);
-
+        hcd_port_reset_end(_dev0.rhport);
         // TODO: fall through to SET ADDRESS, refactor later
       }
       #if CFG_TUH_HUB
@@ -1292,6 +1249,7 @@ static void process_enumeration(tuh_xfer_t* xfer)
       }
       #endif
       __attribute__((fallthrough));
+#endif
 
     case ENUM_SET_ADDR:
       enum_request_set_addr();
@@ -1305,7 +1263,7 @@ static void process_enumeration(tuh_xfer_t* xfer)
       TU_ASSERT(new_dev, );
       new_dev->addressed = 1;
 
-      // TODO close device 0, may not be needed
+      // Close device 0
       hcd_device_close(_dev0.rhport, 0);
 
       // open control pipe for new address
@@ -1396,7 +1354,9 @@ static bool enum_new_device(hcd_event_t* event)
   {
     // connected/disconnected directly with roothub
     // wait until device is stable TODO non blocking
+    hcd_port_reset(_dev0.rhport);
     osal_task_delay(RESET_DELAY);
+    hcd_port_reset_end( _dev0.rhport);
 
     // device unplugged while delaying
     if ( !hcd_port_connect_status(_dev0.rhport) ) return true;

+ 2 - 2
src/portable/ehci/ehci.c

@@ -163,15 +163,15 @@ void hcd_port_reset(uint8_t rhport)
   regs->portsc = portsc;
 }
 
-#if 0
 void hcd_port_reset_end(uint8_t rhport)
 {
   (void) rhport;
 
+#if 0
   ehci_registers_t* regs = ehci_data.regs;
   regs->portsc_bm.port_reset = 0;
-}
 #endif
+}
 
 bool hcd_port_connect_status(uint8_t rhport)
 {

+ 5 - 0
src/portable/mentor/musb/hcd_musb.c

@@ -617,6 +617,11 @@ void hcd_port_reset(uint8_t rhport)
   _hcd.need_reset = false;
 }
 
+void hcd_port_reset_end(uint8_t rhport)
+{
+  (void) rhport;
+}
+
 tusb_speed_t hcd_port_speed_get(uint8_t rhport)
 {
   (void)rhport;

+ 5 - 0
src/portable/ohci/ohci.c

@@ -216,6 +216,11 @@ void hcd_port_reset(uint8_t hostid)
   OHCI_REG->rhport_status[0] = RHPORT_PORT_RESET_STATUS_MASK;
 }
 
+void hcd_port_reset_end(uint8_t rhport)
+{
+  (void) rhport;
+}
+
 bool hcd_port_connect_status(uint8_t hostid)
 {
   (void) hostid;

+ 210 - 0
src/portable/raspberrypi/pio_usb/dcd_pio_usb.c

@@ -0,0 +1,210 @@
+/* 
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018, hathach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if CFG_TUD_ENABLED && (CFG_TUSB_MCU == OPT_MCU_RP2040) && CFG_TUD_RPI_PIO_USB
+
+#include "pico.h"
+#include "pio_usb.h"
+#include "pio_usb_ll.h"
+
+#include "device/dcd.h"
+
+//--------------------------------------------------------------------+
+// MACRO TYPEDEF CONSTANT ENUM DECLARATION
+//--------------------------------------------------------------------+
+
+#define RHPORT_OFFSET     1
+#define RHPORT_PIO(_x)    ((_x)-RHPORT_OFFSET)
+
+//-------------  -------------//
+static usb_device_t *usb_device = NULL;
+static usb_descriptor_buffers_t desc;
+
+/*------------------------------------------------------------------*/
+/* Device API
+ *------------------------------------------------------------------*/
+
+// Initialize controller to device mode
+void dcd_init (uint8_t rhport)
+{
+  (void) rhport;
+
+  static pio_usb_configuration_t config = PIO_USB_DEFAULT_CONFIG;
+  usb_device = pio_usb_device_init(&config, &desc);
+}
+
+// Enable device interrupt
+void dcd_int_enable (uint8_t rhport)
+{
+  (void) rhport;
+}
+
+// Disable device interrupt
+void dcd_int_disable (uint8_t rhport)
+{
+  (void) rhport;
+}
+
+// Receive Set Address request, mcu port must also include status IN response
+void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
+{
+  // must be called before queuing status
+  pio_usb_device_set_address(dev_addr);
+  dcd_edpt_xfer(rhport, 0x80, NULL, 0);
+}
+
+// Wake up host
+void dcd_remote_wakeup (uint8_t rhport)
+{
+  (void) rhport;
+}
+
+// Connect by enabling internal pull-up resistor on D+/D-
+void dcd_connect(uint8_t rhport)
+{
+  (void) rhport;
+}
+
+// Disconnect by disabling internal pull-up resistor on D+/D-
+void dcd_disconnect(uint8_t rhport)
+{
+  (void) rhport;
+}
+
+//--------------------------------------------------------------------+
+// Endpoint API
+//--------------------------------------------------------------------+
+
+// Configure endpoint's registers according to descriptor
+bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_ep)
+{
+  (void) rhport;
+  return pio_usb_device_endpoint_open((uint8_t const*) desc_ep);
+}
+
+void dcd_edpt_close_all (uint8_t rhport)
+{
+  (void) rhport;
+}
+
+// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack
+bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
+{
+  (void) rhport;
+  endpoint_t *ep = pio_usb_device_get_endpoint_by_address(ep_addr);
+  return pio_usb_ll_transfer_start(ep, buffer, total_bytes);
+}
+
+// Submit a transfer where is managed by FIFO, When complete dcd_event_xfer_complete() is invoked to notify the stack - optional, however, must be listed in usbd.c
+//bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
+//{
+//  (void) rhport;
+//  (void) ep_addr;
+//  (void) ff;
+//  (void) total_bytes;
+//  return false;
+//}
+
+// Stall endpoint
+void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
+{
+  (void) rhport;
+  endpoint_t *ep = pio_usb_device_get_endpoint_by_address(ep_addr);
+  ep->has_transfer = false;
+  ep->stalled = true;
+}
+
+// clear stall, data toggle is also reset to DATA0
+void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
+{
+  (void) rhport;
+  endpoint_t *ep = pio_usb_device_get_endpoint_by_address(ep_addr);
+  ep->data_id = 0;
+  ep->stalled = false;
+}
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
+static void __no_inline_not_in_flash_func(handle_endpoint_irq)(uint8_t tu_rhport, xfer_result_t result, volatile uint32_t* ep_reg)
+{
+  const uint32_t ep_all = *ep_reg;
+
+  for(uint8_t ep_idx = 0; ep_idx < PIO_USB_EP_POOL_CNT; ep_idx++)
+  {
+    uint32_t const mask = (1u << ep_idx);
+
+    if (ep_all & mask)
+    {
+      endpoint_t* ep = PIO_USB_ENDPOINT(ep_idx);
+      dcd_event_xfer_complete(tu_rhport, ep->ep_num, ep->actual_len, result, true);
+    }
+  }
+
+  // clear all
+  (*ep_reg) &= ~ep_all;
+}
+
+// IRQ Handler
+void __no_inline_not_in_flash_func(pio_usb_device_irq_handler)(uint8_t root_id)
+{
+  uint8_t const tu_rhport = root_id + 1;
+  root_port_t* rport = PIO_USB_ROOT_PORT(root_id);
+  uint32_t const ints = rport->ints;
+
+  if (ints & PIO_USB_INTS_RESET_END_BITS)
+  {
+    dcd_event_bus_reset(tu_rhport, TUSB_SPEED_FULL, true);
+  }
+
+  if (ints & PIO_USB_INTS_SETUP_REQ_BITS)
+  {
+    dcd_event_setup_received(tu_rhport, rport->setup_packet, true);
+  }
+
+  if ( ints & PIO_USB_INTS_ENDPOINT_COMPLETE_BITS )
+  {
+    handle_endpoint_irq(tu_rhport, XFER_RESULT_SUCCESS, &rport->ep_complete);
+  }
+
+  if ( ints & PIO_USB_INTS_ENDPOINT_STALLED_BITS )
+  {
+    handle_endpoint_irq(tu_rhport, XFER_RESULT_STALLED, &rport->ep_stalled);
+  }
+
+  if ( ints & PIO_USB_INTS_ENDPOINT_ERROR_BITS )
+  {
+    handle_endpoint_irq(tu_rhport, XFER_RESULT_FAILED, &rport->ep_error);
+  }
+
+  // clear all
+  rport->ints &= ~ints;
+}
+
+#endif

+ 217 - 0
src/portable/raspberrypi/pio_usb/hcd_pio_usb.c

@@ -0,0 +1,217 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if CFG_TUH_ENABLED && (CFG_TUSB_MCU == OPT_MCU_RP2040) && CFG_TUH_RPI_PIO_USB
+
+#include "pico.h"
+#include "pio_usb.h"
+#include "pio_usb_ll.h"
+
+//--------------------------------------------------------------------+
+// INCLUDE
+//--------------------------------------------------------------------+
+#include "osal/osal.h"
+
+#include "host/hcd.h"
+#include "host/usbh.h"
+
+#define RHPORT_OFFSET     1
+#define RHPORT_PIO(_x)    ((_x)-RHPORT_OFFSET)
+
+static pio_usb_configuration_t pio_host_config = PIO_USB_DEFAULT_CONFIG;
+
+//--------------------------------------------------------------------+
+// HCD API
+//--------------------------------------------------------------------+
+bool hcd_init(uint8_t rhport)
+{
+  (void) rhport;
+
+  // To run USB SOF interrupt in core1, call this init in core1
+  pio_usb_host_init(&pio_host_config);
+
+  return true;
+}
+
+void hcd_port_reset(uint8_t rhport)
+{
+  uint8_t const pio_rhport = RHPORT_PIO(rhport);
+  pio_usb_host_port_reset_start(pio_rhport);
+}
+
+void hcd_port_reset_end(uint8_t rhport)
+{
+  uint8_t const pio_rhport = RHPORT_PIO(rhport);
+  pio_usb_host_port_reset_end(pio_rhport);
+}
+
+bool hcd_port_connect_status(uint8_t rhport)
+{
+  uint8_t const pio_rhport = RHPORT_PIO(rhport);
+
+  root_port_t *root = PIO_USB_ROOT_PORT(pio_rhport);
+  port_pin_status_t line_state = pio_usb_bus_get_line_state(root);
+
+  return line_state != PORT_PIN_SE0;
+}
+
+tusb_speed_t hcd_port_speed_get(uint8_t rhport)
+{
+  // TODO determine link speed
+  uint8_t const pio_rhport = RHPORT_PIO(rhport);
+  return PIO_USB_ROOT_PORT(pio_rhport)->is_fullspeed ? TUSB_SPEED_FULL : TUSB_SPEED_LOW;
+}
+
+// Close all opened endpoint belong to this device
+void hcd_device_close(uint8_t rhport, uint8_t dev_addr)
+{
+  uint8_t const pio_rhport = RHPORT_PIO(rhport);
+  pio_usb_host_close_device(pio_rhport, dev_addr);
+}
+
+uint32_t hcd_frame_number(uint8_t rhport)
+{
+  (void) rhport;
+  return 0;
+}
+
+void hcd_int_enable(uint8_t rhport)
+{
+  (void) rhport;
+}
+
+void hcd_int_disable(uint8_t rhport)
+{
+  (void) rhport;
+}
+
+//--------------------------------------------------------------------+
+// Endpoint API
+//--------------------------------------------------------------------+
+
+bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * desc_ep)
+{
+  hcd_devtree_info_t dev_tree;
+  hcd_devtree_get_info(dev_addr, &dev_tree);
+  bool const need_pre = (dev_tree.hub_addr && dev_tree.speed == TUSB_SPEED_LOW);
+
+  uint8_t const pio_rhport = RHPORT_PIO(rhport);
+  return pio_usb_host_endpoint_open(pio_rhport, dev_addr, (uint8_t const*) desc_ep, need_pre);
+}
+
+bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen)
+{
+  uint8_t const pio_rhport = RHPORT_PIO(rhport);
+  return pio_usb_host_endpoint_transfer(pio_rhport, dev_addr, ep_addr, buffer, buflen);
+}
+
+bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8])
+{
+  uint8_t const pio_rhport = RHPORT_PIO(rhport);
+  return pio_usb_host_send_setup(pio_rhport, dev_addr, setup_packet);
+}
+
+//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_clear_stall(uint8_t dev_addr, uint8_t ep_addr)
+{
+  (void) dev_addr;
+  (void) ep_addr;
+
+  return true;
+}
+
+static void __no_inline_not_in_flash_func(handle_endpoint_irq)(root_port_t* rport, xfer_result_t result, volatile uint32_t* ep_reg)
+{
+  (void) rport;
+  const uint32_t ep_all = *ep_reg;
+
+  for(uint8_t ep_idx = 0; ep_idx < PIO_USB_EP_POOL_CNT; ep_idx++)
+  {
+    uint32_t const mask = (1u << ep_idx);
+
+    if (ep_all & mask)
+    {
+      endpoint_t* ep = PIO_USB_ENDPOINT(ep_idx);
+      hcd_event_xfer_complete(ep->dev_addr, ep->ep_num, ep->actual_len, result, true);
+    }
+  }
+
+  // clear all
+  (*ep_reg) &= ~ep_all;
+}
+
+// IRQ Handler
+void __no_inline_not_in_flash_func(pio_usb_host_irq_handler)(uint8_t root_id)
+{
+  uint8_t const tu_rhport = root_id + 1;
+  root_port_t* rport = PIO_USB_ROOT_PORT(root_id);
+  uint32_t const ints = rport->ints;
+
+  if ( ints & PIO_USB_INTS_CONNECT_BITS )
+  {
+    hcd_event_device_attach(tu_rhport, true);
+  }
+
+  if ( ints & PIO_USB_INTS_DISCONNECT_BITS )
+  {
+    hcd_event_device_remove(tu_rhport, true);
+  }
+
+  if ( ints & PIO_USB_INTS_ENDPOINT_COMPLETE_BITS )
+  {
+    handle_endpoint_irq(rport, XFER_RESULT_SUCCESS, &rport->ep_complete);
+  }
+
+  if ( ints & PIO_USB_INTS_ENDPOINT_STALLED_BITS )
+  {
+    handle_endpoint_irq(rport, XFER_RESULT_STALLED, &rport->ep_stalled);
+  }
+
+  if ( ints & PIO_USB_INTS_ENDPOINT_ERROR_BITS )
+  {
+    handle_endpoint_irq(rport, XFER_RESULT_FAILED, &rport->ep_error);
+  }
+
+  // clear all
+  rport->ints &= ~ints;
+}
+
+#endif

+ 1 - 1
src/portable/raspberrypi/rp2040/dcd_rp2040.c

@@ -26,7 +26,7 @@
 
 #include "tusb_option.h"
 
-#if CFG_TUD_ENABLED && CFG_TUSB_MCU == OPT_MCU_RP2040
+#if CFG_TUD_ENABLED && (CFG_TUSB_MCU == OPT_MCU_RP2040) && !CFG_TUD_RPI_PIO_USB
 
 #include "pico.h"
 #include "rp2040_usb.h"

+ 10 - 4
src/portable/raspberrypi/rp2040/hcd_rp2040.c

@@ -27,7 +27,7 @@
 
 #include "tusb_option.h"
 
-#if CFG_TUH_ENABLED && CFG_TUSB_MCU == OPT_MCU_RP2040
+#if CFG_TUH_ENABLED && (CFG_TUSB_MCU == OPT_MCU_RP2040) && !CFG_TUH_RPI_PIO_USB
 
 #include "pico.h"
 #include "rp2040_usb.h"
@@ -40,7 +40,8 @@
 #include "host/hcd.h"
 #include "host/usbh.h"
 
-#define ROOT_PORT 0
+// port 0 is native USB port, other is counted as software PIO
+#define RHPORT_NATIVE 0
 
 //--------------------------------------------------------------------+
 // Low level rp2040 controller functions
@@ -185,11 +186,11 @@ static void hcd_rp2040_irq(void)
         
         if (dev_speed())
         {
-            hcd_event_device_attach(ROOT_PORT, true);
+            hcd_event_device_attach(RHPORT_NATIVE, true);
         }
         else
         {
-            hcd_event_device_remove(ROOT_PORT, true);
+            hcd_event_device_remove(RHPORT_NATIVE, true);
         }
 
         // Clear speed change interrupt
@@ -388,6 +389,11 @@ void hcd_port_reset(uint8_t rhport)
     // TODO: Nothing to do here yet. Perhaps need to reset some state?
 }
 
+void hcd_port_reset_end(uint8_t rhport)
+{
+  (void) rhport;
+}
+
 bool hcd_port_connect_status(uint8_t rhport)
 {
     pico_trace("hcd_port_connect_status\n");

+ 5 - 0
src/portable/renesas/usba/hcd_usba.c

@@ -620,6 +620,11 @@ void hcd_port_reset(uint8_t rhport)
   _hcd.need_reset = false;
 }
 
+void hcd_port_reset_end(uint8_t rhport)
+{
+  (void) rhport;
+}
+
 tusb_speed_t hcd_port_speed_get(uint8_t rhport)
 {
   (void)rhport;

+ 14 - 4
src/tusb_option.h

@@ -239,7 +239,7 @@
   #define TUH_OPT_RHPORT   -1
 #endif
 
-#define CFG_TUH_ENABLED     ( TUH_RHPORT_MODE & OPT_MODE_HOST )
+#define CFG_TUH_ENABLED     (TUH_RHPORT_MODE & OPT_MODE_HOST)
 
 // For backward compatible
 #define TUSB_OPT_DEVICE_ENABLED CFG_TUD_ENABLED
@@ -258,7 +258,7 @@
 
 
 //--------------------------------------------------------------------+
-// COMMON OPTIONS
+// Common Options (Default)
 //--------------------------------------------------------------------+
 
 // Debug enable to print out error message
@@ -289,7 +289,7 @@
 #define TUSB_OPT_MUTEX      (CFG_TUSB_OS != OPT_OS_NONE)
 
 //--------------------------------------------------------------------
-// DEVICE OPTIONS
+// Device Options (Default)
 //--------------------------------------------------------------------
 
 #ifndef CFG_TUD_ENDPOINT0_SIZE
@@ -354,7 +354,7 @@
 #endif
 
 //--------------------------------------------------------------------
-// HOST OPTIONS
+// Host Options (Default)
 //--------------------------------------------------------------------
 #if CFG_TUH_ENABLED
   #ifndef CFG_TUH_DEVICE_MAX
@@ -396,6 +396,16 @@
 #define CFG_TUH_API_EDPT_XFER 0
 #endif
 
+// Enable PIO-USB software host controller
+#ifndef CFG_TUH_RPI_PIO_USB
+#define CFG_TUH_RPI_PIO_USB 0
+#endif
+
+#ifndef CFG_TUD_RPI_PIO_USB
+#define CFG_TUD_RPI_PIO_USB 0
+#endif
+
+
 //------------------------------------------------------------------
 // Configuration Validation
 //------------------------------------------------------------------

+ 5 - 6
tools/build_board.py

@@ -28,12 +28,11 @@ def filter_with_input(mylist):
 
 # If examples are not specified in arguments, build all
 all_examples = []
-for entry in os.scandir("examples/device"):
-    if entry.is_dir():
-        all_examples.append("device/" + entry.name)
-for entry in os.scandir("examples/host"):
-    if entry.is_dir():
-        all_examples.append("host/" + entry.name)
+for dir1 in os.scandir("examples"):
+    if dir1.is_dir():
+        for entry in os.scandir(dir1.path):
+            if entry.is_dir():
+                all_examples.append(dir1.name + '/' + entry.name)
 filter_with_input(all_examples)
 all_examples.sort()
 

+ 5 - 6
tools/build_family.py

@@ -28,12 +28,11 @@ def filter_with_input(mylist):
 
 # If examples are not specified in arguments, build all
 all_examples = []
-for entry in os.scandir("examples/device"):
-    if entry.is_dir():
-        all_examples.append("device/" + entry.name)
-for entry in os.scandir("examples/host"):
-    if entry.is_dir():
-        all_examples.append("host/" + entry.name)
+for dir1 in os.scandir("examples"):
+    if dir1.is_dir():
+        for entry in os.scandir(dir1.path):
+            if entry.is_dir():
+                all_examples.append(dir1.name + '/' + entry.name)
 filter_with_input(all_examples)
 all_examples.sort()