Przeglądaj źródła

Merge pull request #1047 from hathach/host-hid-controller

Add host hid controller example
Ha Thach 4 lat temu
rodzic
commit
155c186b17

+ 0 - 0
examples/host/hid_controller/.only.MCU_LPC175X_6X


+ 0 - 0
examples/host/hid_controller/.only.MCU_LPC177X_8X


+ 0 - 0
examples/host/hid_controller/.only.MCU_LPC18XX


+ 0 - 0
examples/host/hid_controller/.only.MCU_LPC40XX


+ 0 - 0
examples/host/hid_controller/.only.MCU_LPC43XX


+ 0 - 0
examples/host/hid_controller/.only.MCU_MIMXRT10XX


+ 0 - 0
examples/host/hid_controller/.only.MCU_RP2040


+ 28 - 0
examples/host/hid_controller/CMakeLists.txt

@@ -0,0 +1,28 @@
+cmake_minimum_required(VERSION 3.5)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
+
+# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
+family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
+
+project(${PROJECT})
+
+# Checks this example is valid for the family and initializes the project
+family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+
+add_executable(${PROJECT})
+
+# Example source
+target_sources(${PROJECT} PUBLIC
+        ${CMAKE_CURRENT_SOURCE_DIR}/src/hid_app.c
+        ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
+        )
+
+# Example include
+target_include_directories(${PROJECT} PUBLIC
+        ${CMAKE_CURRENT_SOURCE_DIR}/src
+        )
+
+# 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})

+ 30 - 0
examples/host/hid_controller/Makefile

@@ -0,0 +1,30 @@
+include ../../../tools/top.mk
+include ../../make.mk
+
+INC += \
+	src \
+	$(TOP)/hw \
+
+# Example source
+EXAMPLE_SOURCE += \
+	src/hid_app.c \
+	src/main.c
+	
+SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+
+CFLAGS += -Wno-error=cast-align
+
+# TinyUSB Host Stack source
+SRC_C += \
+	src/class/cdc/cdc_host.c \
+	src/class/hid/hid_host.c \
+	src/class/msc/msc_host.c \
+	src/host/hub.c \
+	src/host/usbh.c \
+	src/host/usbh_control.c \
+	src/portable/ehci/ehci.c \
+	src/portable/ohci/ohci.c \
+	src/portable/nxp/transdimension/hcd_transdimension.c \
+	src/portable/nxp/lpc17_40/hcd_lpc17_40.c
+
+include ../../rules.mk

+ 249 - 0
examples/host/hid_controller/src/hid_app.c

@@ -0,0 +1,249 @@
+/*
+ * 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.
+ *
+ */
+
+#include "bsp/board.h"
+#include "tusb.h"
+
+/* From https://www.kernel.org/doc/html/latest/input/gamepad.html
+          ____________________________              __
+         / [__ZL__]          [__ZR__] \               |
+        / [__ TL __]        [__ TR __] \              | Front Triggers
+     __/________________________________\__         __|
+    /                                  _   \          |
+   /      /\           __             (N)   \         |
+  /       ||      __  |MO|  __     _       _ \        | Main Pad
+ |    <===DP===> |SE|      |ST|   (W) -|- (E) |       |
+  \       ||    ___          ___       _     /        |
+  /\      \/   /   \        /   \     (S)   /\      __|
+ /  \________ | LS  | ____ |  RS | ________/  \       |
+|         /  \ \___/ /    \ \___/ /  \         |      | Control Sticks
+|        /    \_____/      \_____/    \        |    __|
+|       /                              \       |
+ \_____/                                \_____/
+
+     |________|______|    |______|___________|
+       D-Pad    Left       Right   Action Pad
+               Stick       Stick
+
+                 |_____________|
+                    Menu Pad
+
+  Most gamepads have the following features:
+  - Action-Pad 4 buttons in diamonds-shape (on the right side) NORTH, SOUTH, WEST and EAST.
+  - D-Pad (Direction-pad) 4 buttons (on the left side) that point up, down, left and right.
+  - Menu-Pad Different constellations, but most-times 2 buttons: SELECT - START.
+  - Analog-Sticks provide freely moveable sticks to control directions, Analog-sticks may also
+  provide a digital button if you press them.
+  - Triggers are located on the upper-side of the pad in vertical direction. The upper buttons
+  are normally named Left- and Right-Triggers, the lower buttons Z-Left and Z-Right.
+  - Rumble Many devices provide force-feedback features. But are mostly just simple rumble motors.
+ */
+
+// Sony DS4 report layout detail https://www.psdevwiki.com/ps4/DS4-USB
+typedef struct TU_ATTR_PACKED
+{
+  uint8_t x, y, z, rz; // joystick
+
+  struct {
+    uint8_t dpad     : 4; // (hat format, 0x08 is released, 0=N, 1=NE, 2=E, 3=SE, 4=S, 5=SW, 6=W, 7=NW)
+    uint8_t square   : 1; // west
+    uint8_t cross    : 1; // south
+    uint8_t circle   : 1; // east
+    uint8_t triangle : 1; // north
+  };
+
+  struct {
+    uint8_t l1     : 1;
+    uint8_t r1     : 1;
+    uint8_t l2     : 1;
+    uint8_t r2     : 1;
+    uint8_t share  : 1;
+    uint8_t option : 1;
+    uint8_t l3     : 1;
+    uint8_t r3     : 1;
+  };
+
+  struct {
+    uint8_t ps      : 1; // playstation button
+    uint8_t tpad    : 1; // track pad click
+    uint8_t counter : 6; // +1 each report
+  };
+
+  // comment out since not used by this example
+  // uint8_t l2_trigger; // 0 released, 0xff fully pressed
+  // uint8_t r2_trigger; // as above
+
+  //  uint16_t timestamp;
+  //  uint8_t  battery;
+  //
+  //  int16_t gyro[3];  // x, y, z;
+  //  int16_t accel[3]; // x, y, z
+
+  // there is still lots more info
+
+} sony_ds4_report_t;
+
+// check if device is Sony DualShock 4
+static inline bool is_sony_ds4(uint8_t dev_addr)
+{
+  uint16_t vid, pid;
+  tuh_vid_pid_get(dev_addr, &vid, &pid);
+
+  return (vid == 0x054c && pid == 0x09cc);
+}
+
+//--------------------------------------------------------------------+
+// MACRO TYPEDEF CONSTANT ENUM DECLARATION
+//--------------------------------------------------------------------+
+
+void hid_app_task(void)
+{
+  // nothing to do
+}
+
+//--------------------------------------------------------------------+
+// TinyUSB Callbacks
+//--------------------------------------------------------------------+
+
+// Invoked when device with hid interface is mounted
+// Report descriptor is also available for use. tuh_hid_parse_report_descriptor()
+// can be used to parse common/simple enough descriptor.
+// Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE, it will be skipped
+// therefore report_desc = NULL, desc_len = 0
+void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len)
+{
+  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);
+
+  // Sony DualShock 4 [CUH-ZCT2x]
+  if ( is_sony_ds4(dev_addr) )
+  {
+    // request to receive report
+    // tuh_hid_report_received_cb() will be invoked when report is available
+    if ( !tuh_hid_receive_report(dev_addr, instance) )
+    {
+      printf("Error: cannot request to receive 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);
+
+}
+
+// check if different than 2
+bool diff_than_2(uint8_t x, uint8_t y)
+{
+  return (x - y > 2) || (y - x > 2);
+}
+
+// check if 2 reports are different enough
+bool diff_report(sony_ds4_report_t const* rpt1, sony_ds4_report_t const* rpt2)
+{
+  bool result;
+
+  // x, y, z, rz must different than 2 to be counted
+  result = diff_than_2(rpt1->x, rpt2->x) || diff_than_2(rpt1->y , rpt2->y ) ||
+           diff_than_2(rpt1->z, rpt2->z) || diff_than_2(rpt1->rz, rpt2->rz);
+
+  // check the reset with mem compare
+  result |= memcmp(&rpt1->rz + 1, &rpt2->rz + 1, sizeof(sony_ds4_report_t)-4);
+
+  return result;
+}
+
+void process_sony_ds4(uint8_t const* report, uint16_t len)
+{
+  const char* dpad_str[] = { "N", "NE", "E", "SE", "S", "SW", "W", "NW", "none" };
+
+  // previous report used to compare for changes
+  static sony_ds4_report_t prev_report = { 0 };
+
+  uint8_t const report_id = report[0];
+  report++;
+  len--;
+
+  // all buttons state is stored in ID 1
+  if (report_id == 1)
+  {
+    sony_ds4_report_t ds4_report;
+    memcpy(&ds4_report, report, sizeof(ds4_report));
+
+    // counter is +1, assign to make it easier to compare 2 report
+    prev_report.counter = ds4_report.counter;
+
+    // only print if changes since it is polled ~ 5ms
+    // Since count+1 after each report and  x, y, z, rz fluctuate within 1 or 2
+    // We need more than memcmp to check if report is different enough
+    if ( diff_report(&prev_report, &ds4_report) )
+    {
+      printf("(x, y, z, rz) = (%u, %u, %u, %u)\r\n", ds4_report.x, ds4_report.y, ds4_report.z, ds4_report.rz);
+      printf("DPad = %s ", dpad_str[ds4_report.dpad]);
+
+      if (ds4_report.square   ) printf("Square ");
+      if (ds4_report.cross    ) printf("Cross ");
+      if (ds4_report.circle   ) printf("Circle ");
+      if (ds4_report.triangle ) printf("Triangle ");
+
+      if (ds4_report.l1       ) printf("L1 ");
+      if (ds4_report.r1       ) printf("R1 ");
+      if (ds4_report.l2       ) printf("L2 ");
+      if (ds4_report.r2       ) printf("R2 ");
+
+      if (ds4_report.share    ) printf("Share ");
+      if (ds4_report.option   ) printf("Option ");
+      if (ds4_report.l3       ) printf("L3 ");
+      if (ds4_report.r3       ) printf("R3 ");
+
+      if (ds4_report.ps       ) printf("PS ");
+      if (ds4_report.tpad     ) printf("TPad ");
+
+      printf("\r\n");
+    }
+
+    prev_report = ds4_report;
+  }
+}
+
+// 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 ( is_sony_ds4(dev_addr) )
+  {
+    process_sony_ds4(report, len);
+  }
+
+  // continue to request to receive report
+  if ( !tuh_hid_receive_report(dev_addr, instance) )
+  {
+    printf("Error: cannot request to receive report\r\n");
+  }
+}

+ 93 - 0
examples/host/hid_controller/src/main.c

@@ -0,0 +1,93 @@
+/* 
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 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 example current worked and tested with following controller
+ * - Sony DualShock 4 [CUH-ZCT2x] VID = 0x054c, PID = 0x09cc
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "bsp/board.h"
+#include "tusb.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF PROTYPES
+//--------------------------------------------------------------------+
+void led_blinking_task(void);
+
+extern void cdc_task(void);
+extern void hid_app_task(void);
+
+/*------------- MAIN -------------*/
+int main(void)
+{
+  board_init();
+
+  printf("TinyUSB Host HID Controller Example\r\n");
+
+  tusb_init();
+
+  while (1)
+  {
+    // tinyusb host task
+    tuh_task();
+    led_blinking_task();
+
+#if CFG_TUH_CDC
+    cdc_task();
+#endif
+
+#if CFG_TUH_HID
+    hid_app_task();
+#endif
+  }
+
+  return 0;
+}
+
+//--------------------------------------------------------------------+
+// TinyUSB Callbacks
+//--------------------------------------------------------------------+
+
+//--------------------------------------------------------------------+
+// Blinking Task
+//--------------------------------------------------------------------+
+void led_blinking_task(void)
+{
+  const uint32_t interval_ms = 1000;
+  static uint32_t start_ms = 0;
+
+  static bool led_state = false;
+
+  // Blink every interval ms
+  if ( board_millis() - start_ms < interval_ms) return; // not enough time
+  start_ms += interval_ms;
+
+  board_led_write(led_state);
+  led_state = 1 - led_state; // toggle
+}

+ 95 - 0
examples/host/hid_controller/src/tusb_config.h

@@ -0,0 +1,95 @@
+/* 
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 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.
+ *
+ */
+
+#ifndef _TUSB_CONFIG_H_
+#define _TUSB_CONFIG_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------
+// COMMON CONFIGURATION
+//--------------------------------------------------------------------
+
+// defined by compiler flags for flexibility
+#ifndef CFG_TUSB_MCU
+  #error CFG_TUSB_MCU must be defined
+#endif
+
+#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)
+#else
+  #define CFG_TUSB_RHPORT0_MODE       OPT_MODE_HOST
+#endif
+
+#ifndef CFG_TUSB_OS
+#define CFG_TUSB_OS                 OPT_OS_NONE
+#endif
+
+// CFG_TUSB_DEBUG is defined by compiler in DEBUG build
+// #define CFG_TUSB_DEBUG           0
+
+/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
+ * Tinyusb use follows macros to declare transferring memory so that they can be put
+ * into those specific section.
+ * e.g
+ * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
+ * - CFG_TUSB_MEM_ALIGN   : __attribute__ ((aligned(4)))
+ */
+#ifndef CFG_TUSB_MEM_SECTION
+#define CFG_TUSB_MEM_SECTION
+#endif
+
+#ifndef CFG_TUSB_MEM_ALIGN
+#define CFG_TUSB_MEM_ALIGN          __attribute__ ((aligned(4)))
+#endif
+
+//--------------------------------------------------------------------
+// CONFIGURATION
+//--------------------------------------------------------------------
+
+// Size of buffer to hold descriptors and other data used for enumeration
+#define CFG_TUH_ENUMERATION_BUFSIZE 256
+
+#define CFG_TUH_HUB                 0
+#define CFG_TUH_CDC                 0
+#define CFG_TUH_HID                 4 // typical keyboard + mouse device can have 3-4 HID interfaces
+#define CFG_TUH_MSC                 0
+#define CFG_TUH_VENDOR              0
+
+// max device support (excluding hub device)
+// 1 hub typically has 4 ports
+#define CFG_TUH_DEVICE_MAX          (CFG_TUH_HUB ? 4 : 1)
+
+//------------- HID -------------//
+
+#define CFG_TUH_HID_EP_BUFSIZE      64
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_CONFIG_H_ */

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

@@ -228,7 +228,7 @@ bool hidh_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint3
   if ( dir == TUSB_DIR_IN )
   {
     TU_LOG2("  Get Report callback (%u, %u)\r\n", dev_addr, instance);
-    TU_LOG1_MEM(hid_itf->epin_buf, 8, 2);
+    TU_LOG3_MEM(hid_itf->epin_buf, xferred_bytes, 2);
     tuh_hid_report_received_cb(dev_addr, instance, hid_itf->epin_buf, xferred_bytes);
   }else
   {

+ 71 - 49
src/host/usbh.c

@@ -61,9 +61,13 @@ typedef struct {
   uint8_t speed;
 
   //------------- device descriptor -------------//
-  uint16_t vendor_id;
-  uint16_t product_id;
-  uint8_t  ep0_packet_size;
+  uint16_t vid;
+  uint16_t pid;
+
+  uint8_t  ep0_size;
+  uint8_t  i_manufacturer;
+  uint8_t  i_product;
+  uint8_t  i_serial;
 
   //------------- configuration descriptor -------------//
   // uint8_t interface_count; // bNumInterfaces alias
@@ -105,6 +109,8 @@ typedef struct
   uint8_t hub_addr;
   uint8_t hub_port;
   uint8_t speed;
+
+  volatile uint8_t connected;
 } usbh_dev0_t;
 
 
@@ -225,15 +231,28 @@ extern bool usbh_control_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result
 //--------------------------------------------------------------------+
 // PUBLIC API (Parameter Verification is required)
 //--------------------------------------------------------------------+
-bool tuh_device_configured(uint8_t dev_addr)
+bool tuh_mounted(uint8_t dev_addr)
 {
   return get_device(dev_addr)->configured;
 }
 
-tusb_speed_t tuh_device_get_speed (uint8_t const dev_addr)
+bool tuh_vid_pid_get(uint8_t dev_addr, uint16_t* vid, uint16_t* pid)
+{
+  *vid = *pid = 0;
+
+  TU_VERIFY(tuh_mounted(dev_addr));
+
+  usbh_device_t const* dev = get_device(dev_addr);
+
+  *vid = dev->vid;
+  *pid = dev->pid;
+
+  return true;
+}
+
+tusb_speed_t tuh_speed_get (uint8_t dev_addr)
 {
-  TU_ASSERT( dev_addr <= CFG_TUH_DEVICE_MAX + CFG_TUH_HUB, TUSB_SPEED_INVALID);
-  return (tusb_speed_t) get_device(dev_addr)->speed;
+  return (tusb_speed_t) (dev_addr ? get_device(dev_addr)->speed : _dev0.speed);
 }
 
 #if CFG_TUSB_OS == OPT_OS_NONE
@@ -652,45 +671,6 @@ static bool enum_hub_get_status0_complete(uint8_t dev_addr, tusb_control_request
 }
 #endif
 
-static bool enum_request_set_addr(void)
-{
-  uint8_t const addr0 = 0;
-  tusb_desc_device_t const * desc_device = (tusb_desc_device_t const*) _usbh_ctrl_buf;
-
-  // Get new address
-  uint8_t const new_addr = get_new_address(desc_device->bDeviceClass == TUSB_CLASS_HUB);
-  TU_ASSERT(new_addr != ADDR_INVALID);
-
-  TU_LOG2("Set Address = %d\r\n", new_addr);
-
-  usbh_device_t* new_dev = get_device(new_addr);
-
-  new_dev->rhport          = _dev0.rhport;
-  new_dev->hub_addr        = _dev0.hub_addr;
-  new_dev->hub_port        = _dev0.hub_port;
-  new_dev->speed           = _dev0.speed;
-  new_dev->connected       = 1;
-  new_dev->ep0_packet_size = desc_device->bMaxPacketSize0;
-
-  tusb_control_request_t const new_request =
-  {
-    .bmRequestType_bit =
-    {
-      .recipient = TUSB_REQ_RCPT_DEVICE,
-      .type      = TUSB_REQ_TYPE_STANDARD,
-      .direction = TUSB_DIR_OUT
-    },
-    .bRequest = TUSB_REQ_SET_ADDRESS,
-    .wValue   = new_addr,
-    .wIndex   = 0,
-    .wLength  = 0
-  };
-
-  TU_ASSERT( tuh_control_xfer(addr0, &new_request, NULL, enum_set_address_complete) );
-
-  return true;
-}
-
 static bool enum_new_device(hcd_event_t* event)
 {
   _dev0.rhport   = event->rhport; // TODO refractor integrate to device_pool
@@ -795,6 +775,45 @@ static bool enum_get_addr0_device_desc_complete(uint8_t dev_addr, tusb_control_r
   return true;
 }
 
+static bool enum_request_set_addr(void)
+{
+  uint8_t const addr0 = 0;
+  tusb_desc_device_t const * desc_device = (tusb_desc_device_t const*) _usbh_ctrl_buf;
+
+  // Get new address
+  uint8_t const new_addr = get_new_address(desc_device->bDeviceClass == TUSB_CLASS_HUB);
+  TU_ASSERT(new_addr != ADDR_INVALID);
+
+  TU_LOG2("Set Address = %d\r\n", new_addr);
+
+  usbh_device_t* new_dev = get_device(new_addr);
+
+  new_dev->rhport    = _dev0.rhport;
+  new_dev->hub_addr  = _dev0.hub_addr;
+  new_dev->hub_port  = _dev0.hub_port;
+  new_dev->speed     = _dev0.speed;
+  new_dev->connected = 1;
+  new_dev->ep0_size  = desc_device->bMaxPacketSize0;
+
+  tusb_control_request_t const new_request =
+  {
+    .bmRequestType_bit =
+    {
+      .recipient = TUSB_REQ_RCPT_DEVICE,
+      .type      = TUSB_REQ_TYPE_STANDARD,
+      .direction = TUSB_DIR_OUT
+    },
+    .bRequest = TUSB_REQ_SET_ADDRESS,
+    .wValue   = new_addr,
+    .wIndex   = 0,
+    .wLength  = 0
+  };
+
+  TU_ASSERT( tuh_control_xfer(addr0, &new_request, NULL, enum_set_address_complete) );
+
+  return true;
+}
+
 // After SET_ADDRESS is complete
 static bool enum_set_address_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
 {
@@ -810,7 +829,7 @@ static bool enum_set_address_complete(uint8_t dev_addr, tusb_control_request_t c
   hcd_device_close(_dev0.rhport, 0);
 
   // open control pipe for new address
-  TU_ASSERT( usbh_edpt_control_open(new_addr, new_dev->ep0_packet_size) );
+  TU_ASSERT( usbh_edpt_control_open(new_addr, new_dev->ep0_size) );
 
   // Get full device descriptor
   TU_LOG2("Get Device Descriptor\r\n");
@@ -841,8 +860,11 @@ static bool enum_get_device_desc_complete(uint8_t dev_addr, tusb_control_request
   tusb_desc_device_t const * desc_device = (tusb_desc_device_t const*) _usbh_ctrl_buf;
   usbh_device_t* dev = get_device(dev_addr);
 
-  dev->vendor_id  = desc_device->idVendor;
-  dev->product_id = desc_device->idProduct;
+  dev->vid            = desc_device->idVendor;
+  dev->pid            = desc_device->idProduct;
+  dev->i_manufacturer = desc_device->iManufacturer;
+  dev->i_product      = desc_device->iProduct;
+  dev->i_serial       = desc_device->iSerialNumber;
 
 //  if (tuh_attach_cb) tuh_attach_cb((tusb_desc_device_t*) _usbh_ctrl_buf);
 

+ 14 - 5
src/host/usbh.h

@@ -57,16 +57,25 @@ void tuh_task(void);
 extern void hcd_int_handler(uint8_t rhport);
 #define tuh_int_handler   hcd_int_handler
 
-tusb_speed_t tuh_device_get_speed (uint8_t dev_addr);
+bool tuh_vid_pid_get(uint8_t dev_addr, uint16_t* vid, uint16_t* pid);
+tusb_speed_t tuh_speed_get(uint8_t dev_addr);
 
-// Check if device is configured
-bool tuh_device_configured(uint8_t dev_addr);
+// Check if device is connected and configured
+bool tuh_mounted(uint8_t dev_addr);
+
+// Check if device is suspended
+static inline bool tuh_suspended(uint8_t dev_addr)
+{
+  // TODO implement suspend & resume on host
+  (void) dev_addr;
+  return false;
+}
 
 // Check if device is ready to communicate with
 TU_ATTR_ALWAYS_INLINE
-static inline bool tuh_device_ready(uint8_t dev_addr)
+static inline bool tuh_ready(uint8_t dev_addr)
 {
-  return tuh_device_configured(dev_addr);
+  return tuh_mounted(dev_addr) && !tuh_suspended(dev_addr);
 }
 
 // Carry out control transfer

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

@@ -87,7 +87,7 @@ 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);
+    return hcd_port_speed_get(0) != tuh_speed_get(dev_addr);
 }
 
 static void hw_xfer_complete(struct hw_endpoint *ep, xfer_result_t xfer_result)