From 929ed2a0f47af08c28e19e7e812655816a58ddc3 Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Fri, 13 Aug 2021 16:43:32 +0300
Subject: [PATCH 01/17] Implement USB HID Devices

---
 .../USB/examples/CompositeDevice/.skip.esp32  |   0
 .../examples/CompositeDevice/.skip.esp32c3    |   0
 .../CompositeDevice/CompositeDevice.ino       | 212 ++++++++++
 .../USB/examples/ConsumerControl/.skip.esp32  |   0
 .../examples/ConsumerControl/.skip.esp32c3    |   0
 .../ConsumerControl/ConsumerControl.ino       |  20 +
 libraries/USB/examples/Gamepad/.skip.esp32    |   0
 libraries/USB/examples/Gamepad/.skip.esp32c3  |   0
 libraries/USB/examples/Gamepad/Gamepad.ino    |  20 +
 libraries/USB/examples/HIDVendor/.skip.esp32  |   0
 .../USB/examples/HIDVendor/.skip.esp32c3      |   0
 .../USB/examples/HIDVendor/HIDVendor.ino      |  51 +++
 .../Keyboard/KeyboardLogout/.skip.esp32       |   0
 .../Keyboard/KeyboardLogout/.skip.esp32c3     |   0
 .../KeyboardLogout/KeyboardLogout.ino         |  91 +++++
 .../Keyboard/KeyboardMessage/.skip.esp32      |   0
 .../Keyboard/KeyboardMessage/.skip.esp32c3    |   0
 .../KeyboardMessage/KeyboardMessage.ino       |  54 +++
 .../Keyboard/KeyboardReprogram/.skip.esp32    |   0
 .../Keyboard/KeyboardReprogram/.skip.esp32c3  |   0
 .../KeyboardReprogram/KeyboardReprogram.ino   | 105 +++++
 .../Keyboard/KeyboardSerial/.skip.esp32       |   0
 .../Keyboard/KeyboardSerial/.skip.esp32c3     |   0
 .../KeyboardSerial/KeyboardSerial.ino         |  39 ++
 .../KeyboardAndMouseControl/.skip.esp32       |   0
 .../KeyboardAndMouseControl/.skip.esp32c3     |   0
 .../KeyboardAndMouseControl.ino               |  94 +++++
 .../Mouse/ButtonMouseControl/.skip.esp32      |   0
 .../Mouse/ButtonMouseControl/.skip.esp32c3    |   0
 .../ButtonMouseControl/ButtonMouseControl.ino |  85 ++++
 .../USB/examples/SystemControl/.skip.esp32    |   0
 .../USB/examples/SystemControl/.skip.esp32c3  |   0
 .../examples/SystemControl/SystemControl.ino  |  20 +
 libraries/USB/src/USBHID.cpp                  | 257 ++++++++++++
 libraries/USB/src/USBHID.h                    |  72 ++++
 libraries/USB/src/USBHIDConsumerControl.cpp   |  88 +++++
 libraries/USB/src/USBHIDConsumerControl.h     |  86 ++++
 libraries/USB/src/USBHIDGamepad.cpp           | 141 +++++++
 libraries/USB/src/USBHIDGamepad.h             |  88 +++++
 libraries/USB/src/USBHIDKeyboard.cpp          | 373 ++++++++++++++++++
 libraries/USB/src/USBHIDKeyboard.h            | 141 +++++++
 libraries/USB/src/USBHIDMouse.cpp             | 113 ++++++
 libraries/USB/src/USBHIDMouse.h               |  55 +++
 libraries/USB/src/USBHIDSystemControl.cpp     |  90 +++++
 libraries/USB/src/USBHIDSystemControl.h       |  40 ++
 libraries/USB/src/USBHIDVendor.cpp            | 236 +++++++++++
 libraries/USB/src/USBHIDVendor.h              |  64 +++
 libraries/USB/src/USB_NOT.h                   |  15 -
 48 files changed, 2635 insertions(+), 15 deletions(-)
 create mode 100644 libraries/USB/examples/CompositeDevice/.skip.esp32
 create mode 100644 libraries/USB/examples/CompositeDevice/.skip.esp32c3
 create mode 100644 libraries/USB/examples/CompositeDevice/CompositeDevice.ino
 create mode 100644 libraries/USB/examples/ConsumerControl/.skip.esp32
 create mode 100644 libraries/USB/examples/ConsumerControl/.skip.esp32c3
 create mode 100644 libraries/USB/examples/ConsumerControl/ConsumerControl.ino
 create mode 100644 libraries/USB/examples/Gamepad/.skip.esp32
 create mode 100644 libraries/USB/examples/Gamepad/.skip.esp32c3
 create mode 100644 libraries/USB/examples/Gamepad/Gamepad.ino
 create mode 100644 libraries/USB/examples/HIDVendor/.skip.esp32
 create mode 100644 libraries/USB/examples/HIDVendor/.skip.esp32c3
 create mode 100644 libraries/USB/examples/HIDVendor/HIDVendor.ino
 create mode 100644 libraries/USB/examples/Keyboard/KeyboardLogout/.skip.esp32
 create mode 100644 libraries/USB/examples/Keyboard/KeyboardLogout/.skip.esp32c3
 create mode 100644 libraries/USB/examples/Keyboard/KeyboardLogout/KeyboardLogout.ino
 create mode 100644 libraries/USB/examples/Keyboard/KeyboardMessage/.skip.esp32
 create mode 100644 libraries/USB/examples/Keyboard/KeyboardMessage/.skip.esp32c3
 create mode 100644 libraries/USB/examples/Keyboard/KeyboardMessage/KeyboardMessage.ino
 create mode 100644 libraries/USB/examples/Keyboard/KeyboardReprogram/.skip.esp32
 create mode 100644 libraries/USB/examples/Keyboard/KeyboardReprogram/.skip.esp32c3
 create mode 100644 libraries/USB/examples/Keyboard/KeyboardReprogram/KeyboardReprogram.ino
 create mode 100644 libraries/USB/examples/Keyboard/KeyboardSerial/.skip.esp32
 create mode 100644 libraries/USB/examples/Keyboard/KeyboardSerial/.skip.esp32c3
 create mode 100644 libraries/USB/examples/Keyboard/KeyboardSerial/KeyboardSerial.ino
 create mode 100644 libraries/USB/examples/KeyboardAndMouseControl/.skip.esp32
 create mode 100644 libraries/USB/examples/KeyboardAndMouseControl/.skip.esp32c3
 create mode 100644 libraries/USB/examples/KeyboardAndMouseControl/KeyboardAndMouseControl.ino
 create mode 100644 libraries/USB/examples/Mouse/ButtonMouseControl/.skip.esp32
 create mode 100644 libraries/USB/examples/Mouse/ButtonMouseControl/.skip.esp32c3
 create mode 100644 libraries/USB/examples/Mouse/ButtonMouseControl/ButtonMouseControl.ino
 create mode 100644 libraries/USB/examples/SystemControl/.skip.esp32
 create mode 100644 libraries/USB/examples/SystemControl/.skip.esp32c3
 create mode 100644 libraries/USB/examples/SystemControl/SystemControl.ino
 create mode 100644 libraries/USB/src/USBHID.cpp
 create mode 100644 libraries/USB/src/USBHID.h
 create mode 100644 libraries/USB/src/USBHIDConsumerControl.cpp
 create mode 100644 libraries/USB/src/USBHIDConsumerControl.h
 create mode 100644 libraries/USB/src/USBHIDGamepad.cpp
 create mode 100644 libraries/USB/src/USBHIDGamepad.h
 create mode 100644 libraries/USB/src/USBHIDKeyboard.cpp
 create mode 100644 libraries/USB/src/USBHIDKeyboard.h
 create mode 100644 libraries/USB/src/USBHIDMouse.cpp
 create mode 100644 libraries/USB/src/USBHIDMouse.h
 create mode 100644 libraries/USB/src/USBHIDSystemControl.cpp
 create mode 100644 libraries/USB/src/USBHIDSystemControl.h
 create mode 100644 libraries/USB/src/USBHIDVendor.cpp
 create mode 100644 libraries/USB/src/USBHIDVendor.h
 delete mode 100644 libraries/USB/src/USB_NOT.h

diff --git a/libraries/USB/examples/CompositeDevice/.skip.esp32 b/libraries/USB/examples/CompositeDevice/.skip.esp32
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/USB/examples/CompositeDevice/.skip.esp32c3 b/libraries/USB/examples/CompositeDevice/.skip.esp32c3
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/USB/examples/CompositeDevice/CompositeDevice.ino b/libraries/USB/examples/CompositeDevice/CompositeDevice.ino
new file mode 100644
index 00000000000..16d137ed22f
--- /dev/null
+++ b/libraries/USB/examples/CompositeDevice/CompositeDevice.ino
@@ -0,0 +1,212 @@
+#include "USB.h"
+#include "USBHIDMouse.h"
+#include "USBHIDKeyboard.h"
+#include "USBHIDGamepad.h"
+#include "USBHIDConsumerControl.h"
+#include "USBHIDSystemControl.h"
+#include "USBHIDVendor.h"
+#include "FirmwareMSC.h"
+
+#if !ARDUINO_USB_MSC_ON_BOOT
+FirmwareMSC MSC_Update;
+#endif
+#if ARDUINO_USB_CDC_ON_BOOT
+#define HWSerial Serial0
+#define USBSerial Serial
+#else
+#define HWSerial Serial
+USBCDC USBSerial;
+#endif
+
+USBHID HID;
+USBHIDKeyboard Keyboard;
+USBHIDMouse Mouse;
+USBHIDGamepad Gamepad;
+USBHIDConsumerControl ConsumerControl;
+USBHIDSystemControl SystemControl;
+USBHIDVendor Vendor;
+
+const int buttonPin = 0;
+int previousButtonState = HIGH;
+
+static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data){
+  if(event_base == ARDUINO_USB_EVENTS){
+    arduino_usb_event_data_t * data = (arduino_usb_event_data_t*)event_data;
+    switch (event_id){
+      case ARDUINO_USB_STARTED_EVENT:
+        HWSerial.println("USB PLUGGED");
+        break;
+      case ARDUINO_USB_STOPPED_EVENT:
+        HWSerial.println("USB UNPLUGGED");
+        break;
+      case ARDUINO_USB_SUSPEND_EVENT:
+        HWSerial.printf("USB SUSPENDED: remote_wakeup_en: %u\n", data->suspend.remote_wakeup_en);
+        break;
+      case ARDUINO_USB_RESUME_EVENT:
+        HWSerial.println("USB RESUMED");
+        break;
+      
+      default:
+        break;
+    }
+  } else if(event_base == ARDUINO_USB_CDC_EVENTS){
+    arduino_usb_cdc_event_data_t * data = (arduino_usb_cdc_event_data_t*)event_data;
+    switch (event_id){
+      case ARDUINO_USB_CDC_CONNECTED_EVENT:
+        HWSerial.println("CDC CONNECTED");
+        break;
+      case ARDUINO_USB_CDC_DISCONNECTED_EVENT:
+        HWSerial.println("CDC DISCONNECTED");
+        break;
+      case ARDUINO_USB_CDC_LINE_STATE_EVENT:
+        HWSerial.printf("CDC LINE STATE: dtr: %u, rts: %u\n", data->line_state.dtr, data->line_state.rts);
+        break;
+      case ARDUINO_USB_CDC_LINE_CODING_EVENT:
+        HWSerial.printf("CDC LINE CODING: bit_rate: %u, data_bits: %u, stop_bits: %u, parity: %u\n", data->line_coding.bit_rate, data->line_coding.data_bits, data->line_coding.stop_bits, data->line_coding.parity);
+        break;
+      case ARDUINO_USB_CDC_RX_EVENT:
+        HWSerial.printf("CDC RX [%u]:", data->rx.len);
+        {
+            uint8_t buf[data->rx.len];
+            size_t len = USBSerial.read(buf, data->rx.len);
+            HWSerial.write(buf, len);
+        }
+        HWSerial.println();
+        break;
+      
+      default:
+        break;
+    }
+  } else if(event_base == ARDUINO_FIRMWARE_MSC_EVENTS){
+    arduino_firmware_msc_event_data_t * data = (arduino_firmware_msc_event_data_t*)event_data;
+    switch (event_id){
+      case ARDUINO_FIRMWARE_MSC_START_EVENT:
+        HWSerial.println("MSC Update Start");
+        break;
+      case ARDUINO_FIRMWARE_MSC_WRITE_EVENT:
+        //HWSerial.printf("MSC Update Write %u bytes at offset %u\n", data->write.size, data->write.offset);
+        HWSerial.print(".");
+        break;
+      case ARDUINO_FIRMWARE_MSC_END_EVENT:
+        HWSerial.printf("\nMSC Update End: %u bytes\n", data->end.size);
+        break;
+      case ARDUINO_FIRMWARE_MSC_ERROR_EVENT:
+        HWSerial.printf("MSC Update ERROR! Progress: %u bytes\n", data->error.size);
+        break;
+      case ARDUINO_FIRMWARE_MSC_POWER_EVENT:
+        HWSerial.printf("MSC Update Power: power: %u, start: %u, eject: %u", data->power.power_condition, data->power.start, data->power.load_eject);
+        break;
+      
+      default:
+        break;
+    }
+  } else if(event_base == ARDUINO_USB_HID_EVENTS){
+    arduino_usb_hid_event_data_t * data = (arduino_usb_hid_event_data_t*)event_data;
+    switch (event_id){
+      case ARDUINO_USB_HID_SET_PROTOCOL_EVENT:
+        HWSerial.printf("HID SET PROTOCOL: %s\n", data->set_protocol.protocol?"REPORT":"BOOT");
+        break;
+      case ARDUINO_USB_HID_SET_IDLE_EVENT:
+        HWSerial.printf("HID SET IDLE: %u\n", data->set_idle.idle_rate);
+        break;
+      
+      default:
+        break;
+    }
+  } else if(event_base == ARDUINO_USB_HID_KEYBOARD_EVENTS){
+    arduino_usb_hid_keyboard_event_data_t * data = (arduino_usb_hid_keyboard_event_data_t*)event_data;
+    switch (event_id){
+      case ARDUINO_USB_HID_KEYBOARD_LED_EVENT:
+        HWSerial.printf("HID KEYBOARD LED: NumLock:%u, CapsLock:%u, ScrollLock:%u\n", data->numlock, data->capslock, data->scrolllock);
+        break;
+      
+      default:
+        break;
+    }
+  } else if(event_base == ARDUINO_USB_HID_VENDOR_EVENTS){
+    arduino_usb_hid_vendor_event_data_t * data = (arduino_usb_hid_vendor_event_data_t*)event_data;
+    switch (event_id){
+      case ARDUINO_USB_HID_VENDOR_GET_FEATURE_EVENT:
+        HWSerial.printf("HID VENDOR GET FEATURE: len:%u\n", data->len);
+        for(uint16_t i=0; i<data->len; i++){
+          HWSerial.write(data->buffer[i]?data->buffer[i]:'.');
+        }
+        HWSerial.println();
+        break;
+      case ARDUINO_USB_HID_VENDOR_SET_FEATURE_EVENT:
+        HWSerial.printf("HID VENDOR SET FEATURE: len:%u\n", data->len);
+        for(uint16_t i=0; i<data->len; i++){
+          HWSerial.write(data->buffer[i]?data->buffer[i]:'.');
+        }
+        HWSerial.println();
+        break;
+      case ARDUINO_USB_HID_VENDOR_OUTPUT_EVENT:
+        HWSerial.printf("HID VENDOR OUTPUT: len:%u\n", data->len);
+        for(uint16_t i=0; i<data->len; i++){
+          HWSerial.write(Vendor.read());
+        }
+        HWSerial.println();
+        break;
+      
+      default:
+        break;
+    }
+  }
+}
+
+void setup() {
+  HWSerial.begin(115200);
+  HWSerial.setDebugOutput(true);
+  
+  USB.onEvent(usbEventCallback);
+  USBSerial.onEvent(usbEventCallback);
+  MSC_Update.onEvent(usbEventCallback);
+  HID.onEvent(usbEventCallback);
+  Keyboard.onEvent(usbEventCallback);
+  Vendor.onEvent(usbEventCallback);
+  
+  USBSerial.begin();
+  MSC_Update.begin();
+  Vendor.begin();
+  Mouse.begin();
+  Keyboard.begin();
+  Gamepad.begin();
+  ConsumerControl.begin();
+  SystemControl.begin();
+  HID.begin();
+  USB.begin();
+}
+
+void loop() {
+  int buttonState = digitalRead(buttonPin);
+  if (buttonState != previousButtonState) {
+    previousButtonState = buttonState;
+    if (buttonState == LOW) {
+      HWSerial.println("Button Pressed");
+      USBSerial.println("Button Pressed");
+      Vendor.println("Button Pressed");
+      Mouse.move(10,10);
+      Keyboard.pressRaw(HID_KEY_CAPS_LOCK);
+      Gamepad.leftStick(100,100);
+      ConsumerControl.press(CONSUMER_CONTROL_VOLUME_INCREMENT);
+      //SystemControl.press(SYSTEM_CONTROL_POWER_OFF);
+    } else {
+      Keyboard.releaseRaw(HID_KEY_CAPS_LOCK);
+      Gamepad.leftStick(0,0);
+      ConsumerControl.release();
+      //SystemControl.release();
+      Vendor.println("Button Released");
+      USBSerial.println("Button Released");
+      HWSerial.println("Button Released");
+    }
+    delay(100);
+  }
+  
+  while(HWSerial.available()){
+    size_t l = HWSerial.available();
+    uint8_t b[l];
+    l = HWSerial.read(b, l);
+    USBSerial.write(b, l);
+    Vendor.write(b,l);
+  }
+}
diff --git a/libraries/USB/examples/ConsumerControl/.skip.esp32 b/libraries/USB/examples/ConsumerControl/.skip.esp32
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/USB/examples/ConsumerControl/.skip.esp32c3 b/libraries/USB/examples/ConsumerControl/.skip.esp32c3
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/USB/examples/ConsumerControl/ConsumerControl.ino b/libraries/USB/examples/ConsumerControl/ConsumerControl.ino
new file mode 100644
index 00000000000..d3fbe81f85f
--- /dev/null
+++ b/libraries/USB/examples/ConsumerControl/ConsumerControl.ino
@@ -0,0 +1,20 @@
+#include "USBHIDConsumerControl.h"
+USBHIDConsumerControl ConsumerControl;
+
+const int buttonPin = 0;
+int previousButtonState = HIGH;
+
+void setup() {
+  pinMode(buttonPin, INPUT_PULLUP);
+  ConsumerControl.begin();
+  USB.begin();
+}
+
+void loop() {
+  int buttonState = digitalRead(buttonPin);
+  if ((buttonState != previousButtonState) && (buttonState == LOW)) {
+    ConsumerControl.press(CONSUMER_CONTROL_VOLUME_INCREMENT);
+    ConsumerControl.release();
+  }
+  previousButtonState = buttonState;
+}
diff --git a/libraries/USB/examples/Gamepad/.skip.esp32 b/libraries/USB/examples/Gamepad/.skip.esp32
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/USB/examples/Gamepad/.skip.esp32c3 b/libraries/USB/examples/Gamepad/.skip.esp32c3
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/USB/examples/Gamepad/Gamepad.ino b/libraries/USB/examples/Gamepad/Gamepad.ino
new file mode 100644
index 00000000000..e7cd9a4d817
--- /dev/null
+++ b/libraries/USB/examples/Gamepad/Gamepad.ino
@@ -0,0 +1,20 @@
+#include "USBHIDGamepad.h"
+USBHIDGamepad Gamepad;
+
+const int buttonPin = 0;
+int previousButtonState = HIGH;
+
+void setup() {
+  pinMode(buttonPin, INPUT_PULLUP);
+  Gamepad.begin();
+  USB.begin();
+}
+
+void loop() {
+  int buttonState = digitalRead(buttonPin);
+  if ((buttonState != previousButtonState) && (buttonState == LOW)) {
+    Gamepad.pressButton(BUTTON_START);
+    Gamepad.releaseButton(BUTTON_START);
+  }
+  previousButtonState = buttonState;
+}
diff --git a/libraries/USB/examples/HIDVendor/.skip.esp32 b/libraries/USB/examples/HIDVendor/.skip.esp32
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/USB/examples/HIDVendor/.skip.esp32c3 b/libraries/USB/examples/HIDVendor/.skip.esp32c3
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/USB/examples/HIDVendor/HIDVendor.ino b/libraries/USB/examples/HIDVendor/HIDVendor.ino
new file mode 100644
index 00000000000..f4727415761
--- /dev/null
+++ b/libraries/USB/examples/HIDVendor/HIDVendor.ino
@@ -0,0 +1,51 @@
+#include "USBHIDVendor.h"
+USBHIDVendor Vendor;
+
+const int buttonPin = 0;
+int previousButtonState = HIGH;
+
+static void vendorEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data){
+  if(event_base == ARDUINO_USB_HID_VENDOR_EVENTS){
+    arduino_usb_hid_vendor_event_data_t * data = (arduino_usb_hid_vendor_event_data_t*)event_data;
+    switch (event_id){
+      case ARDUINO_USB_HID_VENDOR_GET_FEATURE_EVENT:
+        Serial.printf("HID VENDOR GET FEATURE: len:%u\n", data->len);
+        break;
+      case ARDUINO_USB_HID_VENDOR_SET_FEATURE_EVENT:
+        Serial.printf("HID VENDOR SET FEATURE: len:%u\n", data->len);
+        for(uint16_t i=0; i<data->len; i++){
+          Serial.printf("0x%02X ",data->buffer);
+        }
+        Serial.println();
+        break;
+      case ARDUINO_USB_HID_VENDOR_OUTPUT_EVENT:
+        Serial.printf("HID VENDOR OUTPUT: len:%u\n", data->len);
+        // for(uint16_t i=0; i<data->len; i++){
+        //   Serial.write(Vendor.read());
+        // }
+        break;
+      
+      default:
+        break;
+    }
+  }
+}
+
+void setup() {
+  pinMode(buttonPin, INPUT_PULLUP);
+  Serial.begin(115200);
+  Vendor.onEvent(vendorEventCallback);
+  Vendor.begin();
+  USB.begin();
+}
+
+void loop() {
+  int buttonState = digitalRead(buttonPin);
+  if ((buttonState != previousButtonState) && (buttonState == LOW)) {
+    Vendor.println("Hello World!");
+  }
+  previousButtonState = buttonState;
+  while(Vendor.available()){
+    Serial.write(Vendor.read());
+  }
+}
diff --git a/libraries/USB/examples/Keyboard/KeyboardLogout/.skip.esp32 b/libraries/USB/examples/Keyboard/KeyboardLogout/.skip.esp32
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/USB/examples/Keyboard/KeyboardLogout/.skip.esp32c3 b/libraries/USB/examples/Keyboard/KeyboardLogout/.skip.esp32c3
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/USB/examples/Keyboard/KeyboardLogout/KeyboardLogout.ino b/libraries/USB/examples/Keyboard/KeyboardLogout/KeyboardLogout.ino
new file mode 100644
index 00000000000..90cf6bb4bac
--- /dev/null
+++ b/libraries/USB/examples/Keyboard/KeyboardLogout/KeyboardLogout.ino
@@ -0,0 +1,91 @@
+/*
+  Keyboard logout
+
+  This sketch demonstrates the Keyboard library.
+
+  When you connect pin 2 to ground, it performs a logout.
+  It uses keyboard combinations to do this, as follows:
+
+  On Windows, CTRL-ALT-DEL followed by ALT-l
+  On Ubuntu, CTRL-ALT-DEL, and ENTER
+  On OSX, CMD-SHIFT-q
+
+  To wake: Spacebar.
+
+  Circuit:
+  - Arduino Leonardo or Micro
+  - wire to connect D2 to ground
+
+  created 6 Mar 2012
+  modified 27 Mar 2012
+  by Tom Igoe
+
+  This example is in the public domain.
+
+  http://www.arduino.cc/en/Tutorial/KeyboardLogout
+*/
+
+#define OSX 0
+#define WINDOWS 1
+#define UBUNTU 2
+
+#include "USBHIDKeyboard.h"
+USBHIDKeyboard Keyboard;
+
+// change this to match your platform:
+int platform = OSX;
+
+void setup() {
+  // make pin 0 an input and turn on the pull-up resistor so it goes high unless
+  // connected to ground:
+  pinMode(0, INPUT_PULLUP);
+  Keyboard.begin();
+  USB.begin();
+}
+
+void loop() {
+  while (digitalRead(0) == HIGH) {
+    // do nothing until pin 2 goes low
+    delay(500);
+  }
+  delay(1000);
+
+  switch (platform) {
+    case OSX:
+      Keyboard.press(KEY_LEFT_GUI);
+      // Shift-Q logs out:
+      Keyboard.press(KEY_LEFT_SHIFT);
+      Keyboard.press('Q');
+      delay(100);
+      Keyboard.releaseAll();
+      // enter:
+      Keyboard.write(KEY_RETURN);
+      break;
+    case WINDOWS:
+      // CTRL-ALT-DEL:
+      Keyboard.press(KEY_LEFT_CTRL);
+      Keyboard.press(KEY_LEFT_ALT);
+      Keyboard.press(KEY_DELETE);
+      delay(100);
+      Keyboard.releaseAll();
+      // ALT-l:
+      delay(2000);
+      Keyboard.press(KEY_LEFT_ALT);
+      Keyboard.press('l');
+      Keyboard.releaseAll();
+      break;
+    case UBUNTU:
+      // CTRL-ALT-DEL:
+      Keyboard.press(KEY_LEFT_CTRL);
+      Keyboard.press(KEY_LEFT_ALT);
+      Keyboard.press(KEY_DELETE);
+      delay(1000);
+      Keyboard.releaseAll();
+      // Enter to confirm logout:
+      Keyboard.write(KEY_RETURN);
+      break;
+  }
+
+  // do nothing:
+  while (true) delay(1000);
+}
diff --git a/libraries/USB/examples/Keyboard/KeyboardMessage/.skip.esp32 b/libraries/USB/examples/Keyboard/KeyboardMessage/.skip.esp32
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/USB/examples/Keyboard/KeyboardMessage/.skip.esp32c3 b/libraries/USB/examples/Keyboard/KeyboardMessage/.skip.esp32c3
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/USB/examples/Keyboard/KeyboardMessage/KeyboardMessage.ino b/libraries/USB/examples/Keyboard/KeyboardMessage/KeyboardMessage.ino
new file mode 100644
index 00000000000..93766bad3df
--- /dev/null
+++ b/libraries/USB/examples/Keyboard/KeyboardMessage/KeyboardMessage.ino
@@ -0,0 +1,54 @@
+/*
+  Keyboard Message test
+
+  For the Arduino Leonardo and Micro.
+
+  Sends a text string when a button is pressed.
+
+  The circuit:
+  - pushbutton attached from pin 0 to ground
+  - 10 kilohm resistor attached from pin 0 to +5V
+
+  created 24 Oct 2011
+  modified 27 Mar 2012
+  by Tom Igoe
+  modified 11 Nov 2013
+  by Scott Fitzgerald
+
+  This example code is in the public domain.
+
+  http://www.arduino.cc/en/Tutorial/KeyboardMessage
+*/
+
+#include "USBHIDKeyboard.h"
+USBHIDKeyboard Keyboard;
+
+const int buttonPin = 0;          // input pin for pushbutton
+int previousButtonState = HIGH;   // for checking the state of a pushButton
+int counter = 0;                  // button push counter
+
+void setup() {
+  // make the pushButton pin an input:
+  pinMode(buttonPin, INPUT_PULLUP);
+  // initialize control over the keyboard:
+  Keyboard.begin();
+  USB.begin();
+}
+
+void loop() {
+  // read the pushbutton:
+  int buttonState = digitalRead(buttonPin);
+  // if the button state has changed,
+  if ((buttonState != previousButtonState)
+      // and it's currently pressed:
+      && (buttonState == LOW)) {
+    // increment the button counter
+    counter++;
+    // type out a message
+    Keyboard.print("You pressed the button ");
+    Keyboard.print(counter);
+    Keyboard.println(" times.");
+  }
+  // save the current button state for comparison next time:
+  previousButtonState = buttonState;
+}
diff --git a/libraries/USB/examples/Keyboard/KeyboardReprogram/.skip.esp32 b/libraries/USB/examples/Keyboard/KeyboardReprogram/.skip.esp32
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/USB/examples/Keyboard/KeyboardReprogram/.skip.esp32c3 b/libraries/USB/examples/Keyboard/KeyboardReprogram/.skip.esp32c3
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/USB/examples/Keyboard/KeyboardReprogram/KeyboardReprogram.ino b/libraries/USB/examples/Keyboard/KeyboardReprogram/KeyboardReprogram.ino
new file mode 100644
index 00000000000..192f91f03fb
--- /dev/null
+++ b/libraries/USB/examples/Keyboard/KeyboardReprogram/KeyboardReprogram.ino
@@ -0,0 +1,105 @@
+/*
+  Arduino Programs Blink
+
+  This sketch demonstrates the Keyboard library.
+
+  For Leonardo and Due boards only.
+
+  When you connect pin 2 to ground, it creates a new window with a key
+  combination (CTRL-N), then types in the Blink sketch, then auto-formats the
+  text using another key combination (CTRL-T), then uploads the sketch to the
+  currently selected Arduino using a final key combination (CTRL-U).
+
+  Circuit:
+  - Arduino Leonardo, Micro, Due, LilyPad USB, or Yún
+  - wire to connect D2 to ground
+
+  created 5 Mar 2012
+  modified 29 Mar 2012
+  by Tom Igoe
+  modified 3 May 2014
+  by Scott Fitzgerald
+
+  This example is in the public domain.
+
+  http://www.arduino.cc/en/Tutorial/KeyboardReprogram
+*/
+
+#include "USBHIDKeyboard.h"
+USBHIDKeyboard Keyboard;
+
+// use this option for OSX.
+// Comment it out if using Windows or Linux:
+char ctrlKey = KEY_LEFT_GUI;
+// use this option for Windows and Linux.
+// leave commented out if using OSX:
+//  char ctrlKey = KEY_LEFT_CTRL;
+
+
+void setup() {
+  // make pin 0 an input and turn on the pull-up resistor so it goes high unless
+  // connected to ground:
+  pinMode(0, INPUT_PULLUP);
+  // initialize control over the keyboard:
+  Keyboard.begin();
+  USB.begin();
+}
+
+void loop() {
+  while (digitalRead(0) == HIGH) {
+    // do nothing until pin 0 goes low
+    delay(500);
+  }
+  delay(1000);
+  // new document:
+  Keyboard.press(ctrlKey);
+  Keyboard.press('n');
+  delay(100);
+  Keyboard.releaseAll();
+  // wait for new window to open:
+  delay(1000);
+
+  // versions of the Arduino IDE after 1.5 pre-populate new sketches with
+  // setup() and loop() functions let's clear the window before typing anything new
+  // select all
+  Keyboard.press(ctrlKey);
+  Keyboard.press('a');
+  delay(500);
+  Keyboard.releaseAll();
+  // delete the selected text
+  Keyboard.write(KEY_BACKSPACE);
+  delay(500);
+
+  // Type out "blink":
+  Keyboard.println("void setup() {");
+  Keyboard.println("pinMode(13, OUTPUT);");
+  Keyboard.println("}");
+  Keyboard.println();
+  Keyboard.println("void loop() {");
+  Keyboard.println("digitalWrite(13, HIGH);");
+  Keyboard.print("delay(3000);");
+  // 3000 ms is too long. Delete it:
+  for (int keystrokes = 0; keystrokes < 6; keystrokes++) {
+    delay(500);
+    Keyboard.write(KEY_BACKSPACE);
+  }
+  // make it 1000 instead:
+  Keyboard.println("1000);");
+  Keyboard.println("digitalWrite(13, LOW);");
+  Keyboard.println("delay(1000);");
+  Keyboard.println("}");
+  // tidy up:
+  Keyboard.press(ctrlKey);
+  Keyboard.press('t');
+  delay(100);
+  Keyboard.releaseAll();
+  delay(3000);
+  // upload code:
+  Keyboard.press(ctrlKey);
+  Keyboard.press('u');
+  delay(100);
+  Keyboard.releaseAll();
+
+  // wait for the sweet oblivion of reprogramming:
+  while (true)delay(1000);
+}
diff --git a/libraries/USB/examples/Keyboard/KeyboardSerial/.skip.esp32 b/libraries/USB/examples/Keyboard/KeyboardSerial/.skip.esp32
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/USB/examples/Keyboard/KeyboardSerial/.skip.esp32c3 b/libraries/USB/examples/Keyboard/KeyboardSerial/.skip.esp32c3
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/USB/examples/Keyboard/KeyboardSerial/KeyboardSerial.ino b/libraries/USB/examples/Keyboard/KeyboardSerial/KeyboardSerial.ino
new file mode 100644
index 00000000000..bcfa7542d4d
--- /dev/null
+++ b/libraries/USB/examples/Keyboard/KeyboardSerial/KeyboardSerial.ino
@@ -0,0 +1,39 @@
+/*
+  Keyboard test
+
+  Reads a byte from the serial port, sends a keystroke back.
+  The sent keystroke is one higher than what's received, e.g. if you send a,
+  you get b, send A you get B, and so forth.
+
+  The circuit:
+  - none
+
+  created 21 Oct 2011
+  modified 27 Mar 2012
+  by Tom Igoe
+
+  This example code is in the public domain.
+
+  http://www.arduino.cc/en/Tutorial/KeyboardSerial
+*/
+
+#include "USBHIDKeyboard.h"
+USBHIDKeyboard Keyboard;
+
+void setup() {
+  // open the serial port:
+  Serial.begin(115200);
+  // initialize control over the keyboard:
+  Keyboard.begin();
+  USB.begin();
+}
+
+void loop() {
+  // check for incoming serial data:
+  if (Serial.available() > 0) {
+    // read incoming serial data:
+    char inChar = Serial.read();
+    // Type the next ASCII value from what you received:
+    Keyboard.write(inChar + 1);
+  }
+}
diff --git a/libraries/USB/examples/KeyboardAndMouseControl/.skip.esp32 b/libraries/USB/examples/KeyboardAndMouseControl/.skip.esp32
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/USB/examples/KeyboardAndMouseControl/.skip.esp32c3 b/libraries/USB/examples/KeyboardAndMouseControl/.skip.esp32c3
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/USB/examples/KeyboardAndMouseControl/KeyboardAndMouseControl.ino b/libraries/USB/examples/KeyboardAndMouseControl/KeyboardAndMouseControl.ino
new file mode 100644
index 00000000000..14623929c07
--- /dev/null
+++ b/libraries/USB/examples/KeyboardAndMouseControl/KeyboardAndMouseControl.ino
@@ -0,0 +1,94 @@
+/*
+  KeyboardAndMouseControl
+
+  Hardware:
+  - five pushbuttons attached to D12, D13, D14, D15, D0
+
+  The mouse movement is always relative. This sketch reads four pushbuttons, and
+  uses them to set the movement of the mouse.
+
+  WARNING: When you use the Mouse.move() command, the Arduino takes over your
+  mouse! Make sure you have control before you use the mouse commands.
+
+  created 15 Mar 2012
+  modified 27 Mar 2012
+  by Tom Igoe
+
+  This example code is in the public domain.
+
+  http://www.arduino.cc/en/Tutorial/KeyboardAndMouseControl
+*/
+
+#include "USBHIDMouse.h"
+#include "USBHIDKeyboard.h"
+USBHIDMouse Mouse;
+USBHIDKeyboard Keyboard;
+
+// set pin numbers for the five buttons:
+const int upButton = 12;
+const int downButton = 13;
+const int leftButton = 14;
+const int rightButton = 15;
+const int mouseButton = 0;
+
+void setup() { // initialize the buttons' inputs:
+  pinMode(upButton, INPUT_PULLUP);
+  pinMode(downButton, INPUT_PULLUP);
+  pinMode(leftButton, INPUT_PULLUP);
+  pinMode(rightButton, INPUT_PULLUP);
+  pinMode(mouseButton, INPUT_PULLUP);
+
+  Serial.begin(115200);
+  // initialize mouse control:
+  Mouse.begin();
+  Keyboard.begin();
+  USB.begin();
+}
+
+void loop() {
+  // use serial input to control the mouse:
+  if (Serial.available() > 0) {
+    char inChar = Serial.read();
+
+    switch (inChar) {
+      case 'u':
+        // move mouse up
+        Mouse.move(0, -40);
+        break;
+      case 'd':
+        // move mouse down
+        Mouse.move(0, 40);
+        break;
+      case 'l':
+        // move mouse left
+        Mouse.move(-40, 0);
+        break;
+      case 'r':
+        // move mouse right
+        Mouse.move(40, 0);
+        break;
+      case 'm':
+        // perform mouse left click
+        Mouse.click(MOUSE_LEFT);
+        break;
+    }
+  }
+
+  // use the pushbuttons to control the keyboard:
+  if (digitalRead(upButton) == LOW) {
+    Keyboard.write('u');
+  }
+  if (digitalRead(downButton) == LOW) {
+    Keyboard.write('d');
+  }
+  if (digitalRead(leftButton) == LOW) {
+    Keyboard.write('l');
+  }
+  if (digitalRead(rightButton) == LOW) {
+    Keyboard.write('r');
+  }
+  if (digitalRead(mouseButton) == LOW) {
+    Keyboard.write('m');
+  }
+  delay(5);
+}
diff --git a/libraries/USB/examples/Mouse/ButtonMouseControl/.skip.esp32 b/libraries/USB/examples/Mouse/ButtonMouseControl/.skip.esp32
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/USB/examples/Mouse/ButtonMouseControl/.skip.esp32c3 b/libraries/USB/examples/Mouse/ButtonMouseControl/.skip.esp32c3
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/USB/examples/Mouse/ButtonMouseControl/ButtonMouseControl.ino b/libraries/USB/examples/Mouse/ButtonMouseControl/ButtonMouseControl.ino
new file mode 100644
index 00000000000..8bc1200afcb
--- /dev/null
+++ b/libraries/USB/examples/Mouse/ButtonMouseControl/ButtonMouseControl.ino
@@ -0,0 +1,85 @@
+/*
+  ButtonMouseControl
+
+  Controls the mouse from five pushbuttons on an Arduino Leonardo, Micro or Due.
+
+  Hardware:
+  - five pushbuttons attached to D12, D13, D14, D15, D0
+
+  The mouse movement is always relative. This sketch reads four pushbuttons,
+  and uses them to set the movement of the mouse.
+
+  WARNING: When you use the Mouse.move() command, the Arduino takes over your
+  mouse! Make sure you have control before you use the mouse commands.
+
+  created 15 Mar 2012
+  modified 27 Mar 2012
+  by Tom Igoe
+
+  This example code is in the public domain.
+
+  http://www.arduino.cc/en/Tutorial/ButtonMouseControl
+*/
+
+#include "USBHIDMouse.h"
+USBHIDMouse Mouse;
+
+
+// set pin numbers for the five buttons:
+const int upButton = 12;
+const int downButton = 13;
+const int leftButton = 14;
+const int rightButton = 15;
+const int mouseButton = 0;
+
+int range = 5;              // output range of X or Y movement; affects movement speed
+int responseDelay = 10;     // response delay of the mouse, in ms
+
+
+void setup() {
+  // initialize the buttons' inputs:
+  pinMode(upButton, INPUT_PULLUP);
+  pinMode(downButton, INPUT_PULLUP);
+  pinMode(leftButton, INPUT_PULLUP);
+  pinMode(rightButton, INPUT_PULLUP);
+  pinMode(mouseButton, INPUT_PULLUP);
+  // initialize mouse control:
+  Mouse.begin();
+  USB.begin();
+}
+
+void loop() {
+  // read the buttons:
+  int upState = digitalRead(upButton);
+  int downState = digitalRead(downButton);
+  int rightState = digitalRead(rightButton);
+  int leftState = digitalRead(leftButton);
+  int clickState = digitalRead(mouseButton);
+
+  // calculate the movement distance based on the button states:
+  int  xDistance = (leftState - rightState) * range;
+  int  yDistance = (upState - downState) * range;
+
+  // if X or Y is non-zero, move:
+  if ((xDistance != 0) || (yDistance != 0)) {
+    Mouse.move(xDistance, yDistance, 0);
+  }
+
+  // if the mouse button is pressed:
+  if (clickState == LOW) {
+    // if the mouse is not pressed, press it:
+    if (!Mouse.isPressed(MOUSE_LEFT)) {
+      Mouse.press(MOUSE_LEFT);
+    }
+  }
+  // else the mouse button is not pressed:
+  else {
+    // if the mouse is pressed, release it:
+    if (Mouse.isPressed(MOUSE_LEFT)) {
+      Mouse.release(MOUSE_LEFT);
+    }
+  }
+
+  // a delay so the mouse doesn't move too fast:
+  delay(responseDelay);
+}
diff --git a/libraries/USB/examples/SystemControl/.skip.esp32 b/libraries/USB/examples/SystemControl/.skip.esp32
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/USB/examples/SystemControl/.skip.esp32c3 b/libraries/USB/examples/SystemControl/.skip.esp32c3
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/USB/examples/SystemControl/SystemControl.ino b/libraries/USB/examples/SystemControl/SystemControl.ino
new file mode 100644
index 00000000000..f4a7f5c5843
--- /dev/null
+++ b/libraries/USB/examples/SystemControl/SystemControl.ino
@@ -0,0 +1,20 @@
+#include "USBHIDSystemControl.h"
+USBHIDSystemControl SystemControl;
+
+const int buttonPin = 0;
+int previousButtonState = HIGH;
+
+void setup() {
+  pinMode(buttonPin, INPUT_PULLUP);
+  SystemControl.begin();
+  USB.begin();
+}
+
+void loop() {
+  int buttonState = digitalRead(buttonPin);
+  if ((buttonState != previousButtonState) && (buttonState == LOW)) {
+    SystemControl.press(SYSTEM_CONTROL_POWER_OFF);
+    SystemControl.release();
+  }
+  previousButtonState = buttonState;
+}
diff --git a/libraries/USB/src/USBHID.cpp b/libraries/USB/src/USBHID.cpp
new file mode 100644
index 00000000000..c73b3edc2bb
--- /dev/null
+++ b/libraries/USB/src/USBHID.cpp
@@ -0,0 +1,257 @@
+// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include "esp32-hal.h"
+#include "esp32-hal-tinyusb.h"
+#include "USBHID.h"
+
+#if CFG_TUD_HID
+#define USB_HID_DEVICES_MAX 10
+
+ESP_EVENT_DEFINE_BASE(ARDUINO_USB_HID_EVENTS);
+esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait);
+esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg);
+
+static void log_print_buf_line(const uint8_t *b, size_t len){
+    for(size_t i = 0; i<len; i++){
+        log_printf("%s0x%02x,",i?" ":"", b[i]);
+    }
+    for(size_t i = len; i<16; i++){
+        log_printf("      ");
+    }
+    log_printf("    // ");
+    for(size_t i = 0; i<len; i++){
+        log_printf("%c",((b[i] >= 0x20) && (b[i] < 0x80))?b[i]:'.');
+    }
+    log_printf("\n");
+}
+
+void log_print_buf(const uint8_t *b, size_t len){
+    if(!len || !b){
+        return;
+    }
+    for(size_t i = 0; i<len; i+=16){
+        log_printf("/* 0x%04X */ ", i);
+        log_print_buf_line(b+i, ((len-i)<16)?(len - i):16);
+    }
+}
+
+
+static bool tinyusb_hid_is_initialized = false;
+static tinyusb_hid_device_descriptor_cb_t tinyusb_loaded_hid_devices_callbacks[USB_HID_DEVICES_MAX];
+static uint8_t tinyusb_loaded_hid_devices_num = 0;
+static uint8_t tinyusb_loaded_hid_devices_report_id = 1;
+static uint16_t tinyusb_hid_device_descriptor_len = 0;
+static uint8_t * tinyusb_hid_device_descriptor = NULL;
+static const char * tinyusb_hid_device_report_types[4] = {"INVALID", "INPUT", "OUTPUT", "FEATURE"};
+
+static bool tinyusb_enable_hid_device(uint16_t descriptor_len, tinyusb_hid_device_descriptor_cb_t cb){
+    if(tinyusb_hid_is_initialized){
+        log_e("TinyUSB HID has already started! Device not enabled");
+        return false;
+    }
+    if(tinyusb_loaded_hid_devices_num >= USB_HID_DEVICES_MAX){
+        log_e("Maximum devices already enabled! Device not enabled");
+        return false;
+    }
+    tinyusb_hid_device_descriptor_len += descriptor_len;
+    tinyusb_loaded_hid_devices_callbacks[tinyusb_loaded_hid_devices_num++] = cb;
+    log_d("Device enabled");
+    return true;
+}
+
+static uint16_t tinyusb_load_hid_descriptor(uint8_t interface, uint8_t * dst, uint8_t report_id)
+{
+    if(interface < USB_HID_DEVICES_MAX && tinyusb_loaded_hid_devices_callbacks[interface] != NULL){
+        return tinyusb_loaded_hid_devices_callbacks[interface](dst, report_id);
+    }
+    return 0;
+}
+
+static bool tinyusb_load_enabled_hid_devices(){
+    tinyusb_hid_device_descriptor = (uint8_t *)malloc(tinyusb_hid_device_descriptor_len);
+    if (tinyusb_hid_device_descriptor == NULL) {
+        log_e("HID Descriptor Malloc Failed");
+        return false;
+    }
+    uint8_t * dst = tinyusb_hid_device_descriptor;
+
+    for(uint8_t i=0; i<USB_HID_DEVICES_MAX; i++){
+            uint16_t len = tinyusb_load_hid_descriptor(i, dst, i+1);
+            if (!len) {
+                break;
+            } else {
+                dst += len;
+            }
+    }
+    log_d("Load Done: reports: %u, descr_len: %u", tinyusb_loaded_hid_devices_report_id - 1, tinyusb_hid_device_descriptor_len);
+    return true;
+}
+
+extern "C" uint16_t tusb_hid_load_descriptor(uint8_t * dst, uint8_t * itf)
+{
+    if(tinyusb_hid_is_initialized){
+        return 0;
+    }
+    tinyusb_hid_is_initialized = true;
+    if(!tinyusb_load_enabled_hid_devices()){
+        return 0;
+    }
+
+    uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB HID");
+    uint8_t ep_in = tinyusb_get_free_in_endpoint();
+    TU_VERIFY (ep_in != 0);
+    uint8_t ep_out = tinyusb_get_free_out_endpoint();
+    TU_VERIFY (ep_out != 0);
+    uint8_t descriptor[TUD_HID_INOUT_DESC_LEN] = {
+        // HID Input & Output descriptor
+        // Interface number, string index, protocol, report descriptor len, EP OUT & IN address, size & polling interval
+        TUD_HID_INOUT_DESCRIPTOR(*itf, str_index, HID_ITF_PROTOCOL_NONE, tinyusb_hid_device_descriptor_len, ep_out, (uint8_t)(0x80 | ep_in), 64, 1)
+    };
+    *itf+=1;
+    memcpy(dst, descriptor, TUD_HID_INOUT_DESC_LEN);
+    return TUD_HID_INOUT_DESC_LEN;
+}
+
+
+
+static USBHIDDevice * tinyusb_hid_devices[USB_HID_DEVICES_MAX+1];
+static uint8_t tinyusb_hid_devices_num = 0;
+static bool tinyusb_hid_devices_is_initialized = false;
+
+
+// Invoked when received GET HID REPORT DESCRIPTOR request
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
+uint8_t const * tud_hid_descriptor_report_cb(uint8_t instance){
+    log_d("instance: %u", instance);
+    return tinyusb_hid_device_descriptor;
+}
+
+// Invoked when received SET_PROTOCOL request
+// protocol is either HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1)
+void tud_hid_set_protocol_cb(uint8_t instance, uint8_t protocol){
+    log_d("instance: %u, protocol:%u", instance, protocol);
+    arduino_usb_hid_event_data_t p = {0};
+    p.instance = instance;
+    p.set_protocol.protocol = protocol;
+    arduino_usb_event_post(ARDUINO_USB_HID_EVENTS, ARDUINO_USB_HID_SET_PROTOCOL_EVENT, &p, sizeof(arduino_usb_hid_event_data_t), portMAX_DELAY);
+}
+
+// Invoked when received SET_IDLE request. return false will stall the request
+// - Idle Rate = 0 : only send report if there is changes, i.e skip duplication
+// - Idle Rate > 0 : skip duplication, but send at least 1 report every idle rate (in unit of 4 ms).
+bool tud_hid_set_idle_cb(uint8_t instance, uint8_t idle_rate){
+    log_d("instance: %u, idle_rate:%u", instance, idle_rate);
+    arduino_usb_hid_event_data_t p = {0};
+    p.instance = instance;
+    p.set_idle.idle_rate = idle_rate;
+    arduino_usb_event_post(ARDUINO_USB_HID_EVENTS, ARDUINO_USB_HID_SET_IDLE_EVENT, &p, sizeof(arduino_usb_hid_event_data_t), portMAX_DELAY);
+    return true;
+}
+
+// Invoked when received GET_REPORT control request
+// Application must fill buffer report's content and return its length.
+// Return zero will cause the stack to STALL request
+uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen){
+    if(report_id < USB_HID_DEVICES_MAX && tinyusb_hid_devices[report_id]){
+        return tinyusb_hid_devices[report_id]->_onGetFeature(buffer, reqlen);
+    }
+    log_d("instance: %u, report_id: %u, report_type: %s, reqlen: %u", instance, report_id, tinyusb_hid_device_report_types[report_type], reqlen);
+    return 0;
+}
+
+// Invoked when received SET_REPORT control request or
+// received data on OUT endpoint ( Report ID = 0, Type = 0 )
+void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize){
+    if(!report_id && !report_type){
+        report_id = buffer[0];
+        if(report_id < USB_HID_DEVICES_MAX && tinyusb_hid_devices[report_id]){
+            tinyusb_hid_devices[report_id]->_onOutput(buffer+1, bufsize-1);
+        } else {
+            log_d("instance: %u, report_id: %u, report_type: %s, bufsize: %u", instance, *buffer, tinyusb_hid_device_report_types[HID_REPORT_TYPE_OUTPUT], bufsize-1);
+            log_print_buf(buffer+1, bufsize-1);
+        }
+    } else {
+        if(report_id < USB_HID_DEVICES_MAX && tinyusb_hid_devices[report_id]){
+            tinyusb_hid_devices[report_id]->_onSetFeature(buffer, bufsize);
+        } else {
+            log_d("instance: %u, report_id: %u, report_type: %s, bufsize: %u", instance, report_id, tinyusb_hid_device_report_types[report_type], bufsize);
+            log_print_buf(buffer, bufsize);
+        }
+    }
+}
+
+// Invoked when sent REPORT successfully to host
+// Application can use this to send the next report
+// Note: For composite reports, report[0] is report ID
+void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint8_t len){
+    if(report[0] < USB_HID_DEVICES_MAX && tinyusb_hid_devices[report[0]]){
+        tinyusb_hid_devices[report[0]]->_onInputDone(report+1, len-1);
+    } else {
+        log_i("instance: %u, report_id: %u, report_type: %s, bufsize:%u", instance, report[0], tinyusb_hid_device_report_types[HID_REPORT_TYPE_INPUT], len-1);
+        log_print_buf(report+1, len-1);
+    }
+}
+
+bool tud_hid_n_wait_ready(uint8_t instance, uint32_t timeout_ms){
+    if(tud_hid_n_ready(instance)){
+        return true;
+    }
+    uint32_t start_ms = millis();
+    while(!tud_hid_n_ready(instance)){
+        if((millis() - start_ms) > timeout_ms){
+            return false;
+        }
+        delay(1);
+    }
+    return true;
+}
+
+USBHID::USBHID(){
+    if(!tinyusb_hid_devices_is_initialized){
+        tinyusb_hid_devices_is_initialized = true;
+        memset(tinyusb_hid_devices, 0, sizeof(tinyusb_hid_devices));
+        tinyusb_hid_devices_num = 0;
+        tinyusb_enable_interface(USB_INTERFACE_HID, TUD_HID_INOUT_DESC_LEN, tusb_hid_load_descriptor);
+    }
+
+}
+
+void USBHID::begin(){
+
+}
+
+void USBHID::end(){
+
+}
+
+bool USBHID::addDevice(USBHIDDevice * device, uint16_t descriptor_len, tinyusb_hid_device_descriptor_cb_t cb){
+    if(device && tinyusb_loaded_hid_devices_num < USB_HID_DEVICES_MAX){
+        if(!tinyusb_enable_hid_device(descriptor_len, cb)){
+            return false;
+        }
+        device->id = tinyusb_loaded_hid_devices_num;
+        tinyusb_hid_devices[tinyusb_loaded_hid_devices_num] = device;
+        return true;
+    }
+    return false;
+}
+
+void USBHID::onEvent(esp_event_handler_t callback){
+    onEvent(ARDUINO_USB_HID_ANY_EVENT, callback);
+}
+void USBHID::onEvent(arduino_usb_hid_event_t event, esp_event_handler_t callback){
+    arduino_usb_event_handler_register_with(ARDUINO_USB_HID_EVENTS, event, callback, this);
+}
+
+#endif /* CONFIG_USB_HID_ENABLED */
diff --git a/libraries/USB/src/USBHID.h b/libraries/USB/src/USBHID.h
new file mode 100644
index 00000000000..dba35fb8a4f
--- /dev/null
+++ b/libraries/USB/src/USBHID.h
@@ -0,0 +1,72 @@
+// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+#include <stdint.h>
+#include <stdbool.h>
+#include "esp32-hal.h"
+#include "USB.h"
+#if CONFIG_TINYUSB_HID_ENABLED
+
+#include "class/hid/hid.h"
+#include "esp_event.h"
+
+ESP_EVENT_DECLARE_BASE(ARDUINO_USB_HID_EVENTS);
+
+typedef enum {
+    ARDUINO_USB_HID_ANY_EVENT = ESP_EVENT_ANY_ID,
+    ARDUINO_USB_HID_SET_PROTOCOL_EVENT = 0,
+    ARDUINO_USB_HID_SET_IDLE_EVENT,
+    ARDUINO_USB_HID_MAX_EVENT,
+} arduino_usb_hid_event_t;
+
+typedef struct {
+    uint8_t instance;
+    union {
+        struct {
+                uint8_t protocol;
+        } set_protocol;
+        struct {
+                uint8_t idle_rate;
+        } set_idle;
+    };
+} arduino_usb_hid_event_data_t;
+
+typedef uint16_t (*tinyusb_hid_device_descriptor_cb_t)(uint8_t * dst, uint8_t report_id);
+
+class USBHIDDevice
+{
+public:
+    uint8_t id;
+    USBHIDDevice():id(0){}
+    virtual uint16_t _onGetFeature(uint8_t* buffer, uint16_t len){return 0;}
+    virtual void _onSetFeature(const uint8_t* buffer, uint16_t len){}
+    virtual void _onOutput(const uint8_t* buffer, uint16_t len){}
+    virtual void _onInputDone(const uint8_t* buffer, uint16_t len){}
+};
+
+class USBHID
+{
+public:
+    USBHID(void);
+    void begin(void);
+    void end(void);
+    void onEvent(esp_event_handler_t callback);
+    void onEvent(arduino_usb_hid_event_t event, esp_event_handler_t callback);
+    static bool addDevice(USBHIDDevice * device, uint16_t descriptor_len, tinyusb_hid_device_descriptor_cb_t cb);
+};
+
+bool tud_hid_n_wait_ready(uint8_t instance, uint32_t timeout_ms);
+
+#endif
diff --git a/libraries/USB/src/USBHIDConsumerControl.cpp b/libraries/USB/src/USBHIDConsumerControl.cpp
new file mode 100644
index 00000000000..9cf98d4539a
--- /dev/null
+++ b/libraries/USB/src/USBHIDConsumerControl.cpp
@@ -0,0 +1,88 @@
+// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "esp32-hal.h"
+#include "esp32-hal-tinyusb.h"
+#include "USBHIDConsumerControl.h"
+
+#if CFG_TUD_HID
+
+static uint16_t tinyusb_hid_device_descriptor_cb(uint8_t * dst, uint8_t report_id){
+    uint8_t report_descriptor[] = {
+        TUD_HID_REPORT_DESC_CONSUMER(HID_REPORT_ID(report_id))
+    };
+    memcpy(dst, report_descriptor, sizeof(report_descriptor));
+    return sizeof(report_descriptor);
+}
+
+USBHIDConsumerControl::USBHIDConsumerControl(): hid(), tx_sem(NULL){
+	static bool initialized = false;
+	if(!initialized){
+		initialized = true;
+		uint8_t report_descriptor[] = {
+		    TUD_HID_REPORT_DESC_CONSUMER(HID_REPORT_ID(0))
+		};
+		hid.addDevice(this, sizeof(report_descriptor), tinyusb_hid_device_descriptor_cb);
+	} else {
+		isr_log_e("Only one instance of USBHIDConsumerControl is allowed!");
+		abort();
+	}
+}
+
+void USBHIDConsumerControl::begin(){
+    if(tx_sem == NULL){
+        tx_sem = xSemaphoreCreateBinary();
+        xSemaphoreTake(tx_sem, 0);
+    }
+}
+
+void USBHIDConsumerControl::end(){
+    if (tx_sem != NULL) {
+        vSemaphoreDelete(tx_sem);
+        tx_sem = NULL;
+    }
+}
+
+void USBHIDConsumerControl::_onInputDone(const uint8_t* buffer, uint16_t len){
+    //log_i("len: %u", len);
+    xSemaphoreGive(tx_sem);
+}
+
+bool USBHIDConsumerControl::send(uint16_t value){
+    uint32_t timeout_ms = 100;
+    if(!tud_hid_n_wait_ready(0, timeout_ms)){
+        log_e("not ready");
+        return false;
+    }
+    bool res = tud_hid_n_report(0, id, &value, 2);
+    if(!res){
+        log_e("report failed");
+        return false;
+    } else if(xSemaphoreTake(tx_sem, timeout_ms / portTICK_PERIOD_MS) != pdTRUE){
+        log_e("report wait failed");
+        return false;
+    }
+    return true;
+}
+
+size_t USBHIDConsumerControl::press(uint16_t k){
+    return send(k);
+}
+
+size_t USBHIDConsumerControl::release(){
+    return send(0);
+}
+
+
+#endif /* CFG_TUD_HID */
diff --git a/libraries/USB/src/USBHIDConsumerControl.h b/libraries/USB/src/USBHIDConsumerControl.h
new file mode 100644
index 00000000000..df0b5275b85
--- /dev/null
+++ b/libraries/USB/src/USBHIDConsumerControl.h
@@ -0,0 +1,86 @@
+// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+#include "USBHID.h"
+#if CONFIG_TINYUSB_HID_ENABLED
+
+// Power Control
+#define CONSUMER_CONTROL_POWER                             0x0030
+#define CONSUMER_CONTROL_RESET                             0x0031
+#define CONSUMER_CONTROL_SLEEP                             0x0032
+
+// Screen Brightness
+#define CONSUMER_CONTROL_BRIGHTNESS_INCREMENT              0x006F
+#define CONSUMER_CONTROL_BRIGHTNESS_DECREMENT              0x0070
+
+// These HID usages operate only on mobile systems (battery powered) and
+// require Windows 8 (build 8302 or greater).
+#define CONSUMER_CONTROL_WIRELESS_RADIO_CONTROLS           0x000C
+#define CONSUMER_CONTROL_WIRELESS_RADIO_BUTTONS            0x00C6
+#define CONSUMER_CONTROL_WIRELESS_RADIO_LED                0x00C7
+#define CONSUMER_CONTROL_WIRELESS_RADIO_SLIDER_SWITCH      0x00C8
+
+// Media Control
+#define CONSUMER_CONTROL_PLAY_PAUSE                        0x00CD
+#define CONSUMER_CONTROL_SCAN_NEXT                         0x00B5
+#define CONSUMER_CONTROL_SCAN_PREVIOUS                     0x00B6
+#define CONSUMER_CONTROL_STOP                              0x00B7
+#define CONSUMER_CONTROL_VOLUME                            0x00E0
+#define CONSUMER_CONTROL_MUTE                              0x00E2
+#define CONSUMER_CONTROL_BASS                              0x00E3
+#define CONSUMER_CONTROL_TREBLE                            0x00E4
+#define CONSUMER_CONTROL_BASS_BOOST                        0x00E5
+#define CONSUMER_CONTROL_VOLUME_INCREMENT                  0x00E9
+#define CONSUMER_CONTROL_VOLUME_DECREMENT                  0x00EA
+#define CONSUMER_CONTROL_BASS_INCREMENT                    0x0152
+#define CONSUMER_CONTROL_BASS_DECREMENT                    0x0153
+#define CONSUMER_CONTROL_TREBLE_INCREMENT                  0x0154
+#define CONSUMER_CONTROL_TREBLE_DECREMENT                  0x0155
+
+// Application Launcher
+#define CONSUMER_CONTROL_CONFIGURATION                     0x0183
+#define CONSUMER_CONTROL_EMAIL_READER                      0x018A
+#define CONSUMER_CONTROL_CALCULATOR                        0x0192
+#define CONSUMER_CONTROL_LOCAL_BROWSER                     0x0194
+
+// Browser/Explorer Specific
+#define CONSUMER_CONTROL_SEARCH                            0x0221
+#define CONSUMER_CONTROL_HOME                              0x0223
+#define CONSUMER_CONTROL_BACK                              0x0224
+#define CONSUMER_CONTROL_FORWARD                           0x0225
+#define CONSUMER_CONTROL_BR_STOP                           0x0226
+#define CONSUMER_CONTROL_REFRESH                           0x0227
+#define CONSUMER_CONTROL_BOOKMARKS                         0x022A
+
+// Mouse Horizontal scroll
+#define CONSUMER_CONTROL_PAN                               0x0238
+
+class USBHIDConsumerControl: public USBHIDDevice {
+private:
+    USBHID hid;
+    xSemaphoreHandle tx_sem;
+    bool send(uint16_t value);
+public:
+    USBHIDConsumerControl(void);
+    void begin(void);
+    void end(void);
+    size_t press(uint16_t k);
+    size_t release();
+
+    //internal use
+    void _onInputDone(const uint8_t* buffer, uint16_t len);
+};
+
+#endif
diff --git a/libraries/USB/src/USBHIDGamepad.cpp b/libraries/USB/src/USBHIDGamepad.cpp
new file mode 100644
index 00000000000..0c13e9de516
--- /dev/null
+++ b/libraries/USB/src/USBHIDGamepad.cpp
@@ -0,0 +1,141 @@
+// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "esp32-hal.h"
+#include "esp32-hal-tinyusb.h"
+#include "USBHIDGamepad.h"
+
+#if CFG_TUD_HID
+
+static uint16_t tinyusb_hid_device_descriptor_cb(uint8_t * dst, uint8_t report_id){
+    uint8_t report_descriptor[] = {
+        TUD_HID_REPORT_DESC_GAMEPAD(HID_REPORT_ID(report_id))
+    };
+    memcpy(dst, report_descriptor, sizeof(report_descriptor));
+    return sizeof(report_descriptor);
+}
+
+USBHIDGamepad::USBHIDGamepad(): hid(), tx_sem(NULL), _x(0), _y(0), _z(0), _rz(0), _rx(0), _ry(0), _hat(0), _buttons(0){
+	static bool initialized = false;
+	if(!initialized){
+		initialized = true;
+		uint8_t report_descriptor[] = {
+		    TUD_HID_REPORT_DESC_GAMEPAD(HID_REPORT_ID(0))
+		};
+		hid.addDevice(this, sizeof(report_descriptor), tinyusb_hid_device_descriptor_cb);
+	} else {
+		isr_log_e("Only one instance of USBHIDGamepad is allowed!");
+		abort();
+	}
+}
+
+void USBHIDGamepad::begin(){
+    if(tx_sem == NULL){
+        tx_sem = xSemaphoreCreateBinary();
+        xSemaphoreTake(tx_sem, 0);
+    }
+}
+
+void USBHIDGamepad::end(){
+    if (tx_sem != NULL) {
+        vSemaphoreDelete(tx_sem);
+        tx_sem = NULL;
+    }
+}
+
+void USBHIDGamepad::_onInputDone(const uint8_t* buffer, uint16_t len){
+    //log_i("len: %u", len);
+    xSemaphoreGive(tx_sem);
+}
+
+bool USBHIDGamepad::write(){
+    uint32_t timeout_ms = 100;
+    if(!tud_hid_n_wait_ready(0, timeout_ms)){
+        log_e("not ready");
+        return false;
+    }
+    bool res = tud_hid_n_gamepad_report(0, id, _x, _y, _z, _rz, _rx, _ry, _hat, _buttons);
+    if(!res){
+        log_e("report failed");
+        return false;
+    } else if(xSemaphoreTake(tx_sem, timeout_ms / portTICK_PERIOD_MS) != pdTRUE){
+        log_e("report wait failed");
+        return false;
+    }
+    return true;
+}
+
+bool USBHIDGamepad::leftStick(int8_t x, int8_t y){
+    _x = x;
+    _y = y;
+    return write();
+}
+
+bool USBHIDGamepad::rightStick(int8_t z, int8_t rz){
+    _z = z;
+    _rz = rz;
+    return write();
+}
+
+bool USBHIDGamepad::leftTrigger(int8_t rx){
+    _rx = rx;
+    return write();
+}
+
+bool USBHIDGamepad::rightTrigger(int8_t ry){
+    _ry = ry;
+    return write();
+}
+
+bool USBHIDGamepad::hat(uint8_t hat){
+    if(hat > 9){
+        return false;
+    }
+    _hat = hat;
+    return write();
+}
+
+bool USBHIDGamepad::pressButton(uint8_t button){
+    if(button > 31){
+        return false;
+    }
+    _buttons |= (1 << button);
+    return write();
+}
+
+bool USBHIDGamepad::releaseButton(uint8_t button){
+    if(button > 31){
+        return false;
+    }
+    _buttons &= ~(1 << button);
+    return write();
+}
+
+bool USBHIDGamepad::send(int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons){
+    if(hat > 9){
+        return false;
+    }
+    _x = x;
+    _y = y;
+    _z = z;
+    _rz = rz;
+    _rx = rx;
+    _ry = ry;
+    _hat = hat;
+    _buttons = buttons;
+    return write();
+}
+
+
+#endif /* CFG_TUD_HID */
diff --git a/libraries/USB/src/USBHIDGamepad.h b/libraries/USB/src/USBHIDGamepad.h
new file mode 100644
index 00000000000..bace503c890
--- /dev/null
+++ b/libraries/USB/src/USBHIDGamepad.h
@@ -0,0 +1,88 @@
+// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+#include "USBHID.h"
+#if CONFIG_TINYUSB_HID_ENABLED
+
+/// Standard Gamepad Buttons Naming from Linux input event codes
+/// https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h
+#define BUTTON_A       0
+#define BUTTON_B       1
+#define BUTTON_C       2
+#define BUTTON_X       3
+#define BUTTON_Y       4
+#define BUTTON_Z       5
+#define BUTTON_TL      6
+#define BUTTON_TR      7
+#define BUTTON_TL2     8
+#define BUTTON_TR2     9
+#define BUTTON_SELECT  10
+#define BUTTON_START   11
+#define BUTTON_MODE    12
+#define BUTTON_THUMBL  13
+#define BUTTON_THUMBR  14
+
+#define BUTTON_SOUTH   BUTTON_A
+#define BUTTON_EAST    BUTTON_B
+#define BUTTON_NORTH   BUTTON_X
+#define BUTTON_WEST    BUTTON_Y
+
+/// Standard Gamepad HAT/DPAD Buttons (from Linux input event codes)
+#define HAT_CENTER     0
+#define HAT_UP         1
+#define HAT_UP_RIGHT   2
+#define HAT_RIGHT      3
+#define HAT_DOWN_RIGHT 4
+#define HAT_DOWN       5
+#define HAT_DOWN_LEFT  6
+#define HAT_LEFT       7
+#define HAT_UP_LEFT    8
+
+class USBHIDGamepad: public USBHIDDevice {
+private:
+    USBHID hid;
+    xSemaphoreHandle tx_sem;
+    int8_t  _x;         ///< Delta x  movement of left analog-stick
+    int8_t  _y;         ///< Delta y  movement of left analog-stick
+    int8_t  _z;         ///< Delta z  movement of right analog-joystick
+    int8_t  _rz;        ///< Delta Rz movement of right analog-joystick
+    int8_t  _rx;        ///< Delta Rx movement of analog left trigger
+    int8_t  _ry;        ///< Delta Ry movement of analog right trigger
+    uint8_t _hat;       ///< Buttons mask for currently pressed buttons in the DPad/hat
+    uint32_t _buttons;  ///< Buttons mask for currently pressed buttons
+    bool write();
+public:
+    USBHIDGamepad(void);
+    void begin(void);
+    void end(void);
+
+    bool leftStick(int8_t x, int8_t y);
+    bool rightStick(int8_t z, int8_t rz);
+
+    bool leftTrigger(int8_t rx);
+    bool rightTrigger(int8_t ry);
+
+    bool hat(uint8_t hat);
+
+    bool pressButton(uint8_t button);
+    bool releaseButton(uint8_t button);
+
+    bool send(int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons);
+
+    //internal use
+    void _onInputDone(const uint8_t* buffer, uint16_t len);
+};
+
+#endif
diff --git a/libraries/USB/src/USBHIDKeyboard.cpp b/libraries/USB/src/USBHIDKeyboard.cpp
new file mode 100644
index 00000000000..2bb59243e66
--- /dev/null
+++ b/libraries/USB/src/USBHIDKeyboard.cpp
@@ -0,0 +1,373 @@
+/*
+  Keyboard.cpp
+
+  Copyright (c) 2015, Arduino LLC
+  Original code (pre-library): Copyright (c) 2011, Peter Barrett
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#include "esp32-hal.h"
+#include "esp32-hal-tinyusb.h"
+#include "USBHIDKeyboard.h"
+
+#if CFG_TUD_HID
+
+ESP_EVENT_DEFINE_BASE(ARDUINO_USB_HID_KEYBOARD_EVENTS);
+esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait);
+esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg);
+
+static uint16_t tinyusb_hid_device_descriptor_cb(uint8_t * dst, uint8_t report_id){
+    uint8_t report_descriptor[] = {
+        TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(report_id))
+    };
+    memcpy(dst, report_descriptor, sizeof(report_descriptor));
+    return sizeof(report_descriptor);
+}
+
+USBHIDKeyboard::USBHIDKeyboard(): hid(), tx_sem(NULL){
+    static bool initialized = false;
+    if(!initialized){
+        initialized = true;
+        uint8_t report_descriptor[] = {
+            TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(0))
+        };
+        hid.addDevice(this, sizeof(report_descriptor), tinyusb_hid_device_descriptor_cb);
+    } else {
+        isr_log_e("Only one instance of USBHIDKeyboard is allowed!");
+        abort();
+    }
+}
+
+void USBHIDKeyboard::begin(){
+    if(tx_sem == NULL){
+        tx_sem = xSemaphoreCreateBinary();
+        xSemaphoreTake(tx_sem, 0);
+    }
+}
+
+void USBHIDKeyboard::end(){
+    if (tx_sem != NULL) {
+        vSemaphoreDelete(tx_sem);
+        tx_sem = NULL;
+    }
+}
+
+void USBHIDKeyboard::onEvent(esp_event_handler_t callback){
+    onEvent(ARDUINO_USB_HID_KEYBOARD_ANY_EVENT, callback);
+}
+void USBHIDKeyboard::onEvent(arduino_usb_hid_keyboard_event_t event, esp_event_handler_t callback){
+    arduino_usb_event_handler_register_with(ARDUINO_USB_HID_KEYBOARD_EVENTS, event, callback, this);
+}
+
+void USBHIDKeyboard::_onInputDone(const uint8_t* buffer, uint16_t len){
+    //log_d("len: %u", len);
+    xSemaphoreGive(tx_sem);
+}
+
+void USBHIDKeyboard::_onOutput(const uint8_t* buffer, uint16_t len){
+    //log_d("LEDS: 0x%02x", buffer[0]);
+    arduino_usb_hid_keyboard_event_data_t p = {0};
+    p.leds = buffer[0];
+    arduino_usb_event_post(ARDUINO_USB_HID_KEYBOARD_EVENTS, ARDUINO_USB_HID_KEYBOARD_LED_EVENT, &p, sizeof(arduino_usb_hid_keyboard_event_data_t), portMAX_DELAY);
+}
+
+void USBHIDKeyboard::sendReport(KeyReport* keys)
+{
+    uint32_t timeout_ms = 100;
+    if(!tud_hid_n_wait_ready(0, timeout_ms)){
+        log_e("not ready");
+        return;
+    }
+    bool res = tud_hid_n_keyboard_report(0, id, keys->modifiers, keys->keys);
+    if(!res){
+        log_e("report failed");
+    } else if(xSemaphoreTake(tx_sem, timeout_ms / portTICK_PERIOD_MS) != pdTRUE){
+        log_e("report wait failed");
+    }
+}
+
+#define SHIFT 0x80
+const uint8_t _asciimap[128] =
+{
+    0x00,          // NUL
+    0x00,          // SOH
+    0x00,          // STX
+    0x00,          // ETX
+    0x00,          // EOT
+    0x00,          // ENQ
+    0x00,          // ACK  
+    0x00,          // BEL
+    0x2a,          // BS   Backspace
+    0x2b,          // TAB  Tab
+    0x28,          // LF   Enter
+    0x00,          // VT 
+    0x00,          // FF 
+    0x00,          // CR 
+    0x00,          // SO 
+    0x00,          // SI 
+    0x00,          // DEL
+    0x00,          // DC1
+    0x00,          // DC2
+    0x00,          // DC3
+    0x00,          // DC4
+    0x00,          // NAK
+    0x00,          // SYN
+    0x00,          // ETB
+    0x00,          // CAN
+    0x00,          // EM 
+    0x00,          // SUB
+    0x00,          // ESC
+    0x00,          // FS 
+    0x00,          // GS 
+    0x00,          // RS 
+    0x00,          // US 
+
+    0x2c,          //  ' '
+    0x1e|SHIFT,    // !
+    0x34|SHIFT,    // "
+    0x20|SHIFT,    // #
+    0x21|SHIFT,    // $
+    0x22|SHIFT,    // %
+    0x24|SHIFT,    // &
+    0x34,          // '
+    0x26|SHIFT,    // (
+    0x27|SHIFT,    // )
+    0x25|SHIFT,    // *
+    0x2e|SHIFT,    // +
+    0x36,          // ,
+    0x2d,          // -
+    0x37,          // .
+    0x38,          // /
+    0x27,          // 0
+    0x1e,          // 1
+    0x1f,          // 2
+    0x20,          // 3
+    0x21,          // 4
+    0x22,          // 5
+    0x23,          // 6
+    0x24,          // 7
+    0x25,          // 8
+    0x26,          // 9
+    0x33|SHIFT,    // :
+    0x33,          // ;
+    0x36|SHIFT,    // <
+    0x2e,          // =
+    0x37|SHIFT,    // >
+    0x38|SHIFT,    // ?
+    0x1f|SHIFT,    // @
+    0x04|SHIFT,    // A
+    0x05|SHIFT,    // B
+    0x06|SHIFT,    // C
+    0x07|SHIFT,    // D
+    0x08|SHIFT,    // E
+    0x09|SHIFT,    // F
+    0x0a|SHIFT,    // G
+    0x0b|SHIFT,    // H
+    0x0c|SHIFT,    // I
+    0x0d|SHIFT,    // J
+    0x0e|SHIFT,    // K
+    0x0f|SHIFT,    // L
+    0x10|SHIFT,    // M
+    0x11|SHIFT,    // N
+    0x12|SHIFT,    // O
+    0x13|SHIFT,    // P
+    0x14|SHIFT,    // Q
+    0x15|SHIFT,    // R
+    0x16|SHIFT,    // S
+    0x17|SHIFT,    // T
+    0x18|SHIFT,    // U
+    0x19|SHIFT,    // V
+    0x1a|SHIFT,    // W
+    0x1b|SHIFT,    // X
+    0x1c|SHIFT,    // Y
+    0x1d|SHIFT,    // Z
+    0x2f,          // [
+    0x31,          // bslash
+    0x30,          // ]
+    0x23|SHIFT,    // ^
+    0x2d|SHIFT,    // _
+    0x35,          // `
+    0x04,          // a
+    0x05,          // b
+    0x06,          // c
+    0x07,          // d
+    0x08,          // e
+    0x09,          // f
+    0x0a,          // g
+    0x0b,          // h
+    0x0c,          // i
+    0x0d,          // j
+    0x0e,          // k
+    0x0f,          // l
+    0x10,          // m
+    0x11,          // n
+    0x12,          // o
+    0x13,          // p
+    0x14,          // q
+    0x15,          // r
+    0x16,          // s
+    0x17,          // t
+    0x18,          // u
+    0x19,          // v
+    0x1a,          // w
+    0x1b,          // x
+    0x1c,          // y
+    0x1d,          // z
+    0x2f|SHIFT,    // {
+    0x31|SHIFT,    // |
+    0x30|SHIFT,    // }
+    0x35|SHIFT,    // ~
+    0              // DEL
+};
+
+size_t USBHIDKeyboard::pressRaw(uint8_t k) 
+{
+    uint8_t i;
+    if (k >= 0xE0 && k < 0xE8) {
+        // it's a modifier key
+        _keyReport.modifiers |= (1<<(k-0x80));
+    } else if (k && k < 0xA5) {
+        // Add k to the key report only if it's not already present
+        // and if there is an empty slot.
+        if (_keyReport.keys[0] != k && _keyReport.keys[1] != k && 
+            _keyReport.keys[2] != k && _keyReport.keys[3] != k &&
+            _keyReport.keys[4] != k && _keyReport.keys[5] != k) {
+            
+            for (i=0; i<6; i++) {
+                if (_keyReport.keys[i] == 0x00) {
+                    _keyReport.keys[i] = k;
+                    break;
+                }
+            }
+            if (i == 6) {
+                return 0;
+            }   
+        }
+    } else {
+        //not a modifier and not a key
+        return 0;
+    }
+    sendReport(&_keyReport);
+    return 1;
+}
+
+size_t USBHIDKeyboard::releaseRaw(uint8_t k) 
+{
+    uint8_t i;
+    if (k >= 0xE0 && k < 0xE8) {
+        // it's a modifier key
+        _keyReport.modifiers &= ~(1<<(k-0x80));
+    } else if (k && k < 0xA5) {
+        // Test the key report to see if k is present.  Clear it if it exists.
+        // Check all positions in case the key is present more than once (which it shouldn't be)
+        for (i=0; i<6; i++) {
+            if (0 != k && _keyReport.keys[i] == k) {
+                _keyReport.keys[i] = 0x00;
+            }
+        }
+    } else {
+        //not a modifier and not a key
+        return 0;
+    }
+
+    sendReport(&_keyReport);
+    return 1;
+}
+
+// press() adds the specified key (printing, non-printing, or modifier)
+// to the persistent key report and sends the report.  Because of the way 
+// USB HID works, the host acts like the key remains pressed until we 
+// call release(), releaseAll(), or otherwise clear the report and resend.
+size_t USBHIDKeyboard::press(uint8_t k) 
+{
+    uint8_t i;
+    if (k >= 0x88) {         // it's a non-printing key (not a modifier)
+        k = k - 0x88;
+    } else if (k >= 0x80) {  // it's a modifier key
+        _keyReport.modifiers |= (1<<(k-0x80));
+        k = 0;
+    } else {                // it's a printing key
+        k = _asciimap[k];
+        if (!k) {
+            return 0;
+        }
+        if (k & 0x80) {                     // it's a capital letter or other character reached with shift
+            _keyReport.modifiers |= 0x02;   // the left shift modifier
+            k &= 0x7F;
+        }
+    }
+    return pressRaw(k);
+}
+
+// release() takes the specified key out of the persistent key report and
+// sends the report.  This tells the OS the key is no longer pressed and that
+// it shouldn't be repeated any more.
+size_t USBHIDKeyboard::release(uint8_t k) 
+{
+    uint8_t i;
+    if (k >= 0x88) {         // it's a non-printing key (not a modifier)
+        k = k - 0x88;
+    } else if (k >= 0x80) {  // it's a modifier key
+        _keyReport.modifiers &= ~(1<<(k-0x80));
+        k = 0;
+    } else {                // it's a printing key
+        k = _asciimap[k];
+        if (!k) {
+            return 0;
+        }
+        if (k & 0x80) {                         // it's a capital letter or other character reached with shift
+            _keyReport.modifiers &= ~(0x02);    // the left shift modifier
+            k &= 0x7F;
+        }
+    }
+    return releaseRaw(k);
+}
+
+void USBHIDKeyboard::releaseAll(void)
+{
+    _keyReport.keys[0] = 0;
+    _keyReport.keys[1] = 0; 
+    _keyReport.keys[2] = 0;
+    _keyReport.keys[3] = 0; 
+    _keyReport.keys[4] = 0;
+    _keyReport.keys[5] = 0; 
+    _keyReport.modifiers = 0;
+    sendReport(&_keyReport);
+}
+
+size_t USBHIDKeyboard::write(uint8_t c)
+{
+    uint8_t p = press(c);  // Keydown
+    release(c);            // Keyup
+    return p;              // just return the result of press() since release() almost always returns 1
+}
+
+size_t USBHIDKeyboard::write(const uint8_t *buffer, size_t size) {
+    size_t n = 0;
+    while (size--) {
+        if (*buffer != '\r') {
+            if (write(*buffer)) {
+              n++;
+            } else {
+              break;
+            }
+        }
+        buffer++;
+    }
+    return n;
+}
+
+#endif /* CFG_TUD_HID */
diff --git a/libraries/USB/src/USBHIDKeyboard.h b/libraries/USB/src/USBHIDKeyboard.h
new file mode 100644
index 00000000000..5f4c0e813f4
--- /dev/null
+++ b/libraries/USB/src/USBHIDKeyboard.h
@@ -0,0 +1,141 @@
+/*
+  Keyboard.h
+
+  Copyright (c) 2015, Arduino LLC
+  Original code (pre-library): Copyright (c) 2011, Peter Barrett
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#pragma once
+#include "Print.h"
+#include "USBHID.h"
+#if CONFIG_TINYUSB_HID_ENABLED
+
+#include "esp_event.h"
+
+ESP_EVENT_DECLARE_BASE(ARDUINO_USB_HID_KEYBOARD_EVENTS);
+
+typedef enum {
+    ARDUINO_USB_HID_KEYBOARD_ANY_EVENT = ESP_EVENT_ANY_ID,
+    ARDUINO_USB_HID_KEYBOARD_LED_EVENT = 0,
+    ARDUINO_USB_HID_KEYBOARD_MAX_EVENT,
+} arduino_usb_hid_keyboard_event_t;
+
+typedef union {
+    struct {
+            uint8_t numlock:1;
+            uint8_t capslock:1;
+            uint8_t scrolllock:1;
+            uint8_t compose:1;
+            uint8_t kana:1;
+            uint8_t reserved:3;
+    };
+    uint8_t leds;
+} arduino_usb_hid_keyboard_event_data_t;
+
+#define KEY_LEFT_CTRL   0x80
+#define KEY_LEFT_SHIFT  0x81
+#define KEY_LEFT_ALT    0x82
+#define KEY_LEFT_GUI    0x83
+#define KEY_RIGHT_CTRL  0x84
+#define KEY_RIGHT_SHIFT 0x85
+#define KEY_RIGHT_ALT   0x86
+#define KEY_RIGHT_GUI   0x87
+
+#define KEY_UP_ARROW    0xDA
+#define KEY_DOWN_ARROW  0xD9
+#define KEY_LEFT_ARROW  0xD8
+#define KEY_RIGHT_ARROW 0xD7
+#define KEY_BACKSPACE   0xB2
+#define KEY_TAB         0xB3
+#define KEY_RETURN      0xB0
+#define KEY_ESC         0xB1
+#define KEY_INSERT      0xD1
+#define KEY_DELETE      0xD4
+#define KEY_PAGE_UP     0xD3
+#define KEY_PAGE_DOWN   0xD6
+#define KEY_HOME        0xD2
+#define KEY_END         0xD5
+#define KEY_CAPS_LOCK   0xC1
+#define KEY_F1          0xC2
+#define KEY_F2          0xC3
+#define KEY_F3          0xC4
+#define KEY_F4          0xC5
+#define KEY_F5          0xC6
+#define KEY_F6          0xC7
+#define KEY_F7          0xC8
+#define KEY_F8          0xC9
+#define KEY_F9          0xCA
+#define KEY_F10         0xCB
+#define KEY_F11         0xCC
+#define KEY_F12         0xCD
+#define KEY_F13         0xF0
+#define KEY_F14         0xF1
+#define KEY_F15         0xF2
+#define KEY_F16         0xF3
+#define KEY_F17         0xF4
+#define KEY_F18         0xF5
+#define KEY_F19         0xF6
+#define KEY_F20         0xF7
+#define KEY_F21         0xF8
+#define KEY_F22         0xF9
+#define KEY_F23         0xFA
+#define KEY_F24         0xFB
+
+#define LED_NUMLOCK     0x01
+#define LED_CAPSLOCK    0x02
+#define LED_SCROLLLOCK  0x04
+#define LED_COMPOSE     0x08
+#define LED_KANA        0x10
+
+//  Low level key report: up to 6 keys and shift, ctrl etc at once
+typedef struct
+{
+  uint8_t modifiers;
+  uint8_t reserved;
+  uint8_t keys[6];
+} KeyReport;
+
+class USBHIDKeyboard: public USBHIDDevice, public Print
+{
+private:
+    USBHID hid;
+    xSemaphoreHandle tx_sem;
+    KeyReport _keyReport;
+    void sendReport(KeyReport* keys);
+public:
+    USBHIDKeyboard(void);
+    void begin(void);
+    void end(void);
+    size_t write(uint8_t k);
+    size_t write(const uint8_t *buffer, size_t size);
+    size_t press(uint8_t k);
+    size_t release(uint8_t k);
+    void releaseAll(void);
+
+    //raw functions work with TinyUSB's HID_KEY_* macros
+    size_t pressRaw(uint8_t k);
+    size_t releaseRaw(uint8_t k);
+
+    void onEvent(esp_event_handler_t callback);
+    void onEvent(arduino_usb_hid_keyboard_event_t event, esp_event_handler_t callback);
+
+    //internal use
+    void _onOutput(const uint8_t* buffer, uint16_t len);
+    void _onInputDone(const uint8_t* buffer, uint16_t len);
+};
+
+#endif
diff --git a/libraries/USB/src/USBHIDMouse.cpp b/libraries/USB/src/USBHIDMouse.cpp
new file mode 100644
index 00000000000..0eb2683d4c1
--- /dev/null
+++ b/libraries/USB/src/USBHIDMouse.cpp
@@ -0,0 +1,113 @@
+/*
+  Mouse.cpp
+
+  Copyright (c) 2015, Arduino LLC
+  Original code (pre-library): Copyright (c) 2011, Peter Barrett
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#include "esp32-hal.h"
+#include "esp32-hal-tinyusb.h"
+#include "USBHIDMouse.h"
+
+#if CFG_TUD_HID
+
+static uint16_t tinyusb_hid_device_descriptor_cb(uint8_t * dst, uint8_t report_id){
+    uint8_t report_descriptor[] = {
+        TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(report_id))
+    };
+    memcpy(dst, report_descriptor, sizeof(report_descriptor));
+    return sizeof(report_descriptor);
+}
+
+USBHIDMouse::USBHIDMouse(): hid(), _buttons(0), tx_sem(NULL){
+	static bool initialized = false;
+	if(!initialized){
+		initialized = true;
+		uint8_t report_descriptor[] = {
+		    TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(0))
+		};
+		hid.addDevice(this, sizeof(report_descriptor), tinyusb_hid_device_descriptor_cb);
+	} else {
+		isr_log_e("Only one instance of USBHIDMouse is allowed!");
+		abort();
+	}
+}
+
+void USBHIDMouse::begin(){
+    if(tx_sem == NULL){
+        tx_sem = xSemaphoreCreateBinary();
+        xSemaphoreTake(tx_sem, 0);
+    }
+}
+
+void USBHIDMouse::end(){
+    if (tx_sem != NULL) {
+        vSemaphoreDelete(tx_sem);
+        tx_sem = NULL;
+    }
+}
+
+void USBHIDMouse::_onInputDone(const uint8_t* buffer, uint16_t len){
+    //log_i("len: %u", len);
+    xSemaphoreGive(tx_sem);
+}
+
+void USBHIDMouse::click(uint8_t b){
+    _buttons = b;
+    move(0,0);
+    _buttons = 0;
+    move(0,0);
+}
+
+void USBHIDMouse::move(int8_t x, int8_t y, int8_t wheel, int8_t pan){
+    uint32_t timeout_ms = 100;
+    if(!tud_hid_n_wait_ready(0, timeout_ms)){
+        log_e("not ready");
+        return;
+    }
+    bool res = tud_hid_n_mouse_report(0, id, _buttons, x, y, wheel, pan);
+    if(!res){
+        log_e("report failed");
+    } else if(xSemaphoreTake(tx_sem, timeout_ms / portTICK_PERIOD_MS) != pdTRUE){
+        log_e("report wait failed");
+    }
+}
+
+void USBHIDMouse::buttons(uint8_t b){
+    if (b != _buttons){
+        _buttons = b;
+        move(0,0);
+    }
+}
+
+void USBHIDMouse::press(uint8_t b){
+    buttons(_buttons | b);
+}
+
+void USBHIDMouse::release(uint8_t b){
+    buttons(_buttons & ~b);
+}
+
+bool USBHIDMouse::isPressed(uint8_t b){
+    if ((b & _buttons) > 0) {
+        return true;
+    }
+    return false;
+}
+
+
+#endif /* CFG_TUD_HID */
diff --git a/libraries/USB/src/USBHIDMouse.h b/libraries/USB/src/USBHIDMouse.h
new file mode 100644
index 00000000000..e7ce493208b
--- /dev/null
+++ b/libraries/USB/src/USBHIDMouse.h
@@ -0,0 +1,55 @@
+/*
+  Mouse.h
+
+  Copyright (c) 2015, Arduino LLC
+  Original code (pre-library): Copyright (c) 2011, Peter Barrett
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#pragma once
+#include "USBHID.h"
+#if CONFIG_TINYUSB_HID_ENABLED
+
+#define MOUSE_LEFT      0x01
+#define MOUSE_RIGHT     0x02
+#define MOUSE_MIDDLE    0x04
+#define MOUSE_BACKWARD  0x08
+#define MOUSE_FORWARD   0x10
+#define MOUSE_ALL       0x1F
+
+class USBHIDMouse: public USBHIDDevice {
+private:
+    USBHID hid;
+    uint8_t _buttons;
+    xSemaphoreHandle tx_sem;
+    void buttons(uint8_t b);
+    bool write(int8_t x, int8_t y, int8_t vertical, int8_t horizontal);
+public:
+    USBHIDMouse(void);
+    void begin(void);
+    void end(void);
+
+    void click(uint8_t b = MOUSE_LEFT);
+    void move(int8_t x, int8_t y, int8_t wheel = 0, int8_t pan = 0); 
+    void press(uint8_t b = MOUSE_LEFT);   // press LEFT by default
+    void release(uint8_t b = MOUSE_LEFT); // release LEFT by default
+    bool isPressed(uint8_t b = MOUSE_LEFT); // check LEFT by default
+
+    //internal use
+    void _onInputDone(const uint8_t* buffer, uint16_t len);
+};
+
+#endif
diff --git a/libraries/USB/src/USBHIDSystemControl.cpp b/libraries/USB/src/USBHIDSystemControl.cpp
new file mode 100644
index 00000000000..5b41c78bbcf
--- /dev/null
+++ b/libraries/USB/src/USBHIDSystemControl.cpp
@@ -0,0 +1,90 @@
+// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "esp32-hal.h"
+#include "esp32-hal-tinyusb.h"
+#include "USBHIDSystemControl.h"
+
+#if CFG_TUD_HID
+
+static uint16_t tinyusb_hid_device_descriptor_cb(uint8_t * dst, uint8_t report_id){
+    uint8_t report_descriptor[] = {
+        TUD_HID_REPORT_DESC_SYSTEM_CONTROL(HID_REPORT_ID(report_id))
+    };
+    memcpy(dst, report_descriptor, sizeof(report_descriptor));
+    return sizeof(report_descriptor);
+}
+
+USBHIDSystemControl::USBHIDSystemControl(): hid(), tx_sem(NULL){
+	static bool initialized = false;
+	if(!initialized){
+		initialized = true;
+		uint8_t report_descriptor[] = {
+		    TUD_HID_REPORT_DESC_SYSTEM_CONTROL(HID_REPORT_ID(0))
+		};
+		hid.addDevice(this, sizeof(report_descriptor), tinyusb_hid_device_descriptor_cb);
+	} else {
+		isr_log_e("Only one instance of USBHIDSystemControl is allowed!");
+		abort();
+	}
+}
+
+void USBHIDSystemControl::begin(){
+    if(tx_sem == NULL){
+        tx_sem = xSemaphoreCreateBinary();
+        xSemaphoreTake(tx_sem, 0);
+    }
+}
+
+void USBHIDSystemControl::end(){
+    if (tx_sem != NULL) {
+        vSemaphoreDelete(tx_sem);
+        tx_sem = NULL;
+    }
+}
+
+void USBHIDSystemControl::_onInputDone(const uint8_t* buffer, uint16_t len){
+    //log_i("len: %u", len);
+    xSemaphoreGive(tx_sem);
+}
+
+bool USBHIDSystemControl::send(uint8_t value){
+    uint32_t timeout_ms = 100;
+    if(!tud_hid_n_wait_ready(0, timeout_ms)){
+        log_e("not ready");
+        return false;
+    }
+    bool res = tud_hid_n_report(0, id, &value, 1);
+    if(!res){
+        log_e("report failed");
+        return false;
+    } else if(xSemaphoreTake(tx_sem, timeout_ms / portTICK_PERIOD_MS) != pdTRUE){
+        log_e("report wait failed");
+        return false;
+    }
+    return true;
+}
+
+size_t USBHIDSystemControl::press(uint8_t k){
+    if(k > 3){
+        return 0;
+    }
+    return send(k);
+}
+
+size_t USBHIDSystemControl::release(){
+    return send(0);
+}
+
+#endif /* CFG_TUD_HID */
diff --git a/libraries/USB/src/USBHIDSystemControl.h b/libraries/USB/src/USBHIDSystemControl.h
new file mode 100644
index 00000000000..7a5f65764db
--- /dev/null
+++ b/libraries/USB/src/USBHIDSystemControl.h
@@ -0,0 +1,40 @@
+// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+#include "USBHID.h"
+#if CONFIG_TINYUSB_HID_ENABLED
+
+#define SYSTEM_CONTROL_NONE         0
+#define SYSTEM_CONTROL_POWER_OFF    1
+#define SYSTEM_CONTROL_STANDBY      2
+#define SYSTEM_CONTROL_WAKE_HOST    3
+
+class USBHIDSystemControl: public USBHIDDevice {
+private:
+    USBHID hid;
+    xSemaphoreHandle tx_sem;
+    bool send(uint8_t value);
+public:
+    USBHIDSystemControl(void);
+    void begin(void);
+    void end(void);
+    size_t press(uint8_t k);
+    size_t release();
+
+    //internal use
+    void _onInputDone(const uint8_t* buffer, uint16_t len);
+};
+
+#endif
diff --git a/libraries/USB/src/USBHIDVendor.cpp b/libraries/USB/src/USBHIDVendor.cpp
new file mode 100644
index 00000000000..45497cf73e3
--- /dev/null
+++ b/libraries/USB/src/USBHIDVendor.cpp
@@ -0,0 +1,236 @@
+// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include "esp32-hal.h"
+#include "esp32-hal-tinyusb.h"
+#include "USBHIDVendor.h"
+
+#if CFG_TUD_HID
+
+ESP_EVENT_DEFINE_BASE(ARDUINO_USB_HID_VENDOR_EVENTS);
+esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait);
+esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg);
+
+
+//GENERIC HID
+
+// HID Generic Input, Output & Feature
+// - 1st parameter is report size (mandatory)
+// - 2nd parameter is report id HID_REPORT_ID(n) (optional)
+#define TUD_HID_REPORT_DESC_GENERIC_INOUT_FEATURE(report_size, ...) \
+    HID_USAGE_PAGE_N ( HID_USAGE_PAGE_VENDOR, 2   ),\
+    HID_USAGE        ( 0x01                       ),\
+    HID_COLLECTION   ( HID_COLLECTION_APPLICATION ),\
+      /* Report ID if any */\
+      __VA_ARGS__ \
+      /* Input */ \
+      HID_USAGE       ( 0x02                                   ),\
+      HID_LOGICAL_MIN ( 0x00                                   ),\
+      HID_LOGICAL_MAX ( 0xff                                   ),\
+      HID_REPORT_SIZE ( 8                                      ),\
+      HID_REPORT_COUNT( report_size                            ),\
+      HID_INPUT       ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
+      /* Output */ \
+      HID_USAGE       ( 0x03                                    ),\
+      HID_LOGICAL_MIN ( 0x00                                    ),\
+      HID_LOGICAL_MAX ( 0xff                                    ),\
+      HID_REPORT_SIZE ( 8                                       ),\
+      HID_REPORT_COUNT( report_size                             ),\
+      HID_OUTPUT      ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE  ),\
+      /* Feature */ \
+      HID_USAGE       ( 0x04                                    ),\
+      HID_LOGICAL_MIN ( 0x00                                    ),\
+      HID_LOGICAL_MAX ( 0xff                                    ),\
+      HID_REPORT_SIZE ( 8                                       ),\
+      HID_REPORT_COUNT( report_size                             ),\
+      HID_FEATURE     ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE  ),\
+    HID_COLLECTION_END \
+
+#define TUD_HID_REPORT_DESC_GENERIC_INOUT_FEATURE_LEN 46
+
+static uint16_t tinyusb_hid_device_descriptor_cb(uint8_t * dst, uint8_t report_id){
+    uint8_t report_descriptor[] = {
+        TUD_HID_REPORT_DESC_GENERIC_INOUT_FEATURE(63, HID_REPORT_ID(report_id))
+    };
+    memcpy(dst, report_descriptor, sizeof(report_descriptor));
+    return sizeof(report_descriptor);
+}
+
+USBHIDVendor::USBHIDVendor(): hid(), tx_sem(NULL){
+	static bool initialized = false;
+	if(!initialized){
+		initialized = true;
+		uint8_t report_descriptor[] = {
+		    TUD_HID_REPORT_DESC_GENERIC_INOUT_FEATURE(0, HID_REPORT_ID(0))
+		};
+		hid.addDevice(this, sizeof(report_descriptor), tinyusb_hid_device_descriptor_cb);
+        memset(feature, 0, 63);
+	} else {
+		isr_log_e("Only one instance of USBHIDVendor is allowed!");
+		abort();
+	}
+}
+
+size_t USBHIDVendor::setRxBufferSize(size_t rx_queue_len){
+    if(rx_queue){
+        if(!rx_queue_len){
+            vQueueDelete(rx_queue);
+            rx_queue = NULL;
+        }
+        return 0;
+    }
+    rx_queue = xQueueCreate(rx_queue_len, sizeof(uint8_t));
+    if(!rx_queue){
+        return 0;
+    }
+    return rx_queue_len;
+}
+
+void USBHIDVendor::begin(){
+    if(tx_sem == NULL){
+        tx_sem = xSemaphoreCreateBinary();
+        xSemaphoreTake(tx_sem, 0);
+    }
+    setRxBufferSize(256);//default if not preset
+}
+
+void USBHIDVendor::end(){
+    setRxBufferSize(0);
+    if (tx_sem != NULL) {
+        vSemaphoreDelete(tx_sem);
+        tx_sem = NULL;
+    }
+}
+
+void USBHIDVendor::onEvent(esp_event_handler_t callback){
+    onEvent(ARDUINO_USB_HID_VENDOR_ANY_EVENT, callback);
+}
+void USBHIDVendor::onEvent(arduino_usb_hid_vendor_event_t event, esp_event_handler_t callback){
+    arduino_usb_event_handler_register_with(ARDUINO_USB_HID_VENDOR_EVENTS, event, callback, this);
+}
+
+uint16_t USBHIDVendor::_onGetFeature(uint8_t* buffer, uint16_t len){
+    //log_i("len: %u", len);
+    memcpy(buffer, feature, len);
+    arduino_usb_hid_vendor_event_data_t p = {0};
+    p.buffer = feature;
+    p.len = len;
+    arduino_usb_event_post(ARDUINO_USB_HID_VENDOR_EVENTS, ARDUINO_USB_HID_VENDOR_GET_FEATURE_EVENT, &p, sizeof(arduino_usb_hid_vendor_event_data_t), portMAX_DELAY);
+    return len;
+}
+void USBHIDVendor::_onSetFeature(const uint8_t* buffer, uint16_t len){
+    //log_i("len: %u", len);
+    memcpy(feature, buffer, len);
+    arduino_usb_hid_vendor_event_data_t p = {0};
+    p.buffer = feature;
+    p.len = len;
+    arduino_usb_event_post(ARDUINO_USB_HID_VENDOR_EVENTS, ARDUINO_USB_HID_VENDOR_SET_FEATURE_EVENT, &p, sizeof(arduino_usb_hid_vendor_event_data_t), portMAX_DELAY);
+}
+void USBHIDVendor::_onOutput(const uint8_t* buffer, uint16_t len){
+    //log_i("len: %u", len);
+    for(uint32_t i=0; i<len; i++){
+        if(rx_queue == NULL || !xQueueSend(rx_queue, buffer+i, 0)){
+            len = i+1;
+            log_e("RX Queue Overflow");
+            break;
+        }
+    }
+    arduino_usb_hid_vendor_event_data_t p = {0};
+    p.buffer = buffer;
+    p.len = len;
+    arduino_usb_event_post(ARDUINO_USB_HID_VENDOR_EVENTS, ARDUINO_USB_HID_VENDOR_OUTPUT_EVENT, &p, sizeof(arduino_usb_hid_vendor_event_data_t), portMAX_DELAY);
+}
+void USBHIDVendor::_onInputDone(const uint8_t* buffer, uint16_t len){
+    //log_i("len: %u", buffer[0]);
+    xSemaphoreGive(tx_sem);
+}
+size_t USBHIDVendor::write(const uint8_t* buffer, uint16_t len){
+	uint32_t timeout_ms = 100;
+    uint8_t hid_in[64] = {id, 0x00, };
+    const uint8_t * data = (const uint8_t *)buffer;
+    size_t to_send = len, max_send=64-2, will_send=0;
+    while(to_send){
+        will_send = to_send;
+        if(will_send > max_send){
+            will_send = max_send;
+        }
+        if(!tud_hid_n_wait_ready(0, timeout_ms)){
+            log_e("not ready");
+            return len - to_send;
+        }
+        hid_in[1] = (uint8_t)will_send;
+        memcpy(hid_in+2, data, will_send);
+        memset(hid_in+2+will_send, 0, 64 - (will_send+2));
+        if(!tud_hid_n_report(0, 0, hid_in, 64)){
+            log_e("report failed");
+            return len - to_send;
+        }
+        if(xSemaphoreTake(tx_sem, timeout_ms / portTICK_PERIOD_MS) != pdTRUE){
+            log_e("report wait failed");
+            return len - to_send;
+        }
+        to_send -= will_send;
+        data += will_send;
+    }
+    return len;
+}
+
+size_t USBHIDVendor::write(uint8_t c){
+    return write(&c, 1);
+}
+
+int USBHIDVendor::available(void){
+    if(rx_queue == NULL){
+        return -1;
+    }
+    return uxQueueMessagesWaiting(rx_queue);
+}
+
+int USBHIDVendor::peek(void){
+    if(rx_queue == NULL){
+        return -1;
+    }
+    uint8_t c;
+    if(xQueuePeek(rx_queue, &c, 0)) {
+        return c;
+    }
+    return -1;
+}
+
+int USBHIDVendor::read(void){
+    if(rx_queue == NULL){
+        return -1;
+    }
+    uint8_t c = 0;
+    if(xQueueReceive(rx_queue, &c, 0)) {
+        return c;
+    }
+    return -1;
+}
+
+size_t USBHIDVendor::read(uint8_t *buffer, size_t size){
+    if(rx_queue == NULL){
+        return -1;
+    }
+    uint8_t c = 0;
+    size_t count = 0;
+    while(count < size && xQueueReceive(rx_queue, &c, 0)){
+        buffer[count++] = c;
+    }
+    return count;
+}
+
+void USBHIDVendor::flush(void){}
+
+
+#endif /* CFG_TUD_HID */
diff --git a/libraries/USB/src/USBHIDVendor.h b/libraries/USB/src/USBHIDVendor.h
new file mode 100644
index 00000000000..395529115ba
--- /dev/null
+++ b/libraries/USB/src/USBHIDVendor.h
@@ -0,0 +1,64 @@
+// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+#include "Stream.h"
+#include "USBHID.h"
+#if CONFIG_TINYUSB_HID_ENABLED
+
+ESP_EVENT_DECLARE_BASE(ARDUINO_USB_HID_VENDOR_EVENTS);
+
+typedef enum {
+    ARDUINO_USB_HID_VENDOR_ANY_EVENT = ESP_EVENT_ANY_ID,
+    ARDUINO_USB_HID_VENDOR_SET_FEATURE_EVENT = 0,
+    ARDUINO_USB_HID_VENDOR_GET_FEATURE_EVENT,
+    ARDUINO_USB_HID_VENDOR_OUTPUT_EVENT,
+    ARDUINO_USB_HID_VENDOR_MAX_EVENT,
+} arduino_usb_hid_vendor_event_t;
+
+typedef struct {
+    const uint8_t* buffer;
+    uint16_t len;
+} arduino_usb_hid_vendor_event_data_t;
+
+class USBHIDVendor: public USBHIDDevice, public Stream {
+private:
+    USBHID hid;
+    uint8_t feature[63];
+    xSemaphoreHandle tx_sem;
+    xQueueHandle rx_queue;
+public:
+    USBHIDVendor(void);
+    void begin(void);
+    void end(void);
+    size_t setRxBufferSize(size_t);
+    size_t write(const uint8_t* buffer, uint16_t len);
+    size_t write(uint8_t);
+    int available(void);
+    int peek(void);
+    int read(void);
+    size_t read(uint8_t *buffer, size_t size);
+    void flush(void);
+    
+    void onEvent(esp_event_handler_t callback);
+    void onEvent(arduino_usb_hid_vendor_event_t event, esp_event_handler_t callback);
+
+    //internal use
+    uint16_t _onGetFeature(uint8_t* buffer, uint16_t len);
+    void _onSetFeature(const uint8_t* buffer, uint16_t len);
+    void _onOutput(const uint8_t* buffer, uint16_t len);
+    void _onInputDone(const uint8_t* buffer, uint16_t len);
+};
+
+#endif
diff --git a/libraries/USB/src/USB_NOT.h b/libraries/USB/src/USB_NOT.h
deleted file mode 100644
index f349ad19817..00000000000
--- a/libraries/USB/src/USB_NOT.h
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma once

From a7fab63e8407019f2f6fc268d9f7c46a4ca28ef1 Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Fri, 13 Aug 2021 16:47:33 +0300
Subject: [PATCH 02/17] Update CMakeLists.txt

---
 CMakeLists.txt | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2801ba615ef..a1afff1655c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -76,6 +76,13 @@ set(LIBRARY_SRCS
   libraries/Ticker/src/Ticker.cpp
   libraries/Update/src/Updater.cpp
   libraries/Update/src/HttpsOTAUpdate.cpp
+  libraries/USB/src/USBHID.cpp
+  libraries/USB/src/USBHIDMouse.cpp
+  libraries/USB/src/USBHIDKeyboard.cpp
+  libraries/USB/src/USBHIDGamepad.cpp
+  libraries/USB/src/USBHIDConsumerControl.cpp
+  libraries/USB/src/USBHIDSystemControl.cpp
+  libraries/USB/src/USBHIDVendor.cpp
   libraries/WebServer/src/WebServer.cpp
   libraries/WebServer/src/Parsing.cpp
   libraries/WebServer/src/detail/mimetable.cpp

From 0eeacc87399ada765b7ad814a080917e802620d2 Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Sat, 14 Aug 2021 04:43:36 +0300
Subject: [PATCH 03/17] Provide thread-safe USBHID::SendReport

---
 .../CompositeDevice/CompositeDevice.ino       |  3 +-
 libraries/USB/src/USBHID.cpp                  | 82 ++++++++++++-------
 libraries/USB/src/USBHID.h                    |  4 +-
 libraries/USB/src/USBHIDConsumerControl.cpp   | 31 +------
 libraries/USB/src/USBHIDConsumerControl.h     |  4 -
 libraries/USB/src/USBHIDGamepad.cpp           | 40 +++------
 libraries/USB/src/USBHIDGamepad.h             |  4 -
 libraries/USB/src/USBHIDKeyboard.cpp          | 34 +++-----
 libraries/USB/src/USBHIDKeyboard.h            |  2 -
 libraries/USB/src/USBHIDMouse.cpp             | 35 +++-----
 libraries/USB/src/USBHIDMouse.h               |  4 -
 libraries/USB/src/USBHIDSystemControl.cpp     | 31 +------
 libraries/USB/src/USBHIDSystemControl.h       |  4 -
 libraries/USB/src/USBHIDVendor.cpp            | 61 ++++++--------
 libraries/USB/src/USBHIDVendor.h              |  2 -
 15 files changed, 117 insertions(+), 224 deletions(-)

diff --git a/libraries/USB/examples/CompositeDevice/CompositeDevice.ino b/libraries/USB/examples/CompositeDevice/CompositeDevice.ino
index 16d137ed22f..1f9b9159d2e 100644
--- a/libraries/USB/examples/CompositeDevice/CompositeDevice.ino
+++ b/libraries/USB/examples/CompositeDevice/CompositeDevice.ino
@@ -94,7 +94,7 @@ static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t eve
         HWSerial.printf("MSC Update ERROR! Progress: %u bytes\n", data->error.size);
         break;
       case ARDUINO_FIRMWARE_MSC_POWER_EVENT:
-        HWSerial.printf("MSC Update Power: power: %u, start: %u, eject: %u", data->power.power_condition, data->power.start, data->power.load_eject);
+        HWSerial.printf("MSC Update Power: power: %u, start: %u, eject: %u\n", data->power.power_condition, data->power.start, data->power.load_eject);
         break;
       
       default:
@@ -173,7 +173,6 @@ void setup() {
   Gamepad.begin();
   ConsumerControl.begin();
   SystemControl.begin();
-  HID.begin();
   USB.begin();
 }
 
diff --git a/libraries/USB/src/USBHID.cpp b/libraries/USB/src/USBHID.cpp
index c73b3edc2bb..0fffd1650df 100644
--- a/libraries/USB/src/USBHID.cpp
+++ b/libraries/USB/src/USBHID.cpp
@@ -128,12 +128,14 @@ extern "C" uint16_t tusb_hid_load_descriptor(uint8_t * dst, uint8_t * itf)
 static USBHIDDevice * tinyusb_hid_devices[USB_HID_DEVICES_MAX+1];
 static uint8_t tinyusb_hid_devices_num = 0;
 static bool tinyusb_hid_devices_is_initialized = false;
+static xSemaphoreHandle tinyusb_hid_device_input_sem = NULL;
+static xSemaphoreHandle tinyusb_hid_device_input_mutex = NULL;
 
 
 // Invoked when received GET HID REPORT DESCRIPTOR request
 // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
 uint8_t const * tud_hid_descriptor_report_cb(uint8_t instance){
-    log_d("instance: %u", instance);
+    log_v("instance: %u", instance);
     return tinyusb_hid_device_descriptor;
 }
 
@@ -191,32 +193,6 @@ void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_
     }
 }
 
-// Invoked when sent REPORT successfully to host
-// Application can use this to send the next report
-// Note: For composite reports, report[0] is report ID
-void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint8_t len){
-    if(report[0] < USB_HID_DEVICES_MAX && tinyusb_hid_devices[report[0]]){
-        tinyusb_hid_devices[report[0]]->_onInputDone(report+1, len-1);
-    } else {
-        log_i("instance: %u, report_id: %u, report_type: %s, bufsize:%u", instance, report[0], tinyusb_hid_device_report_types[HID_REPORT_TYPE_INPUT], len-1);
-        log_print_buf(report+1, len-1);
-    }
-}
-
-bool tud_hid_n_wait_ready(uint8_t instance, uint32_t timeout_ms){
-    if(tud_hid_n_ready(instance)){
-        return true;
-    }
-    uint32_t start_ms = millis();
-    while(!tud_hid_n_ready(instance)){
-        if((millis() - start_ms) > timeout_ms){
-            return false;
-        }
-        delay(1);
-    }
-    return true;
-}
-
 USBHID::USBHID(){
     if(!tinyusb_hid_devices_is_initialized){
         tinyusb_hid_devices_is_initialized = true;
@@ -224,15 +200,63 @@ USBHID::USBHID(){
         tinyusb_hid_devices_num = 0;
         tinyusb_enable_interface(USB_INTERFACE_HID, TUD_HID_INOUT_DESC_LEN, tusb_hid_load_descriptor);
     }
-
 }
 
 void USBHID::begin(){
-
+    if(tinyusb_hid_device_input_sem == NULL){
+        tinyusb_hid_device_input_sem = xSemaphoreCreateBinary();
+    }
+    if(tinyusb_hid_device_input_mutex == NULL){
+        tinyusb_hid_device_input_mutex = xSemaphoreCreateMutex();
+    }
 }
 
 void USBHID::end(){
+    if (tinyusb_hid_device_input_sem != NULL) {
+        vSemaphoreDelete(tinyusb_hid_device_input_sem);
+        tinyusb_hid_device_input_sem = NULL;
+    }
+    if (tinyusb_hid_device_input_mutex != NULL) {
+        vSemaphoreDelete(tinyusb_hid_device_input_mutex);
+        tinyusb_hid_device_input_mutex = NULL;
+    }
+}
+
+void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint8_t len){
+    if (tinyusb_hid_device_input_sem) {
+        xSemaphoreGive(tinyusb_hid_device_input_sem);
+    }
+}
+
+bool USBHID::SendReport(uint8_t id, const void* data, size_t len, uint32_t timeout_ms){
+    if(!tinyusb_hid_device_input_sem || !tinyusb_hid_device_input_mutex){
+        log_e("TX Semaphore is NULL. You must call USBHID::begin() before you can send reports");
+        return false;
+    }
+
+    if(xSemaphoreTake(tinyusb_hid_device_input_mutex, timeout_ms / portTICK_PERIOD_MS) != pdTRUE){
+        log_e("report %u mutex failed", id);
+        return false;
+    }
+
+    bool res = tud_hid_n_ready(0);
+    if(!res){
+        log_e("not ready");
+    } else {
+        res = tud_hid_n_report(0, id, data, len);
+        if(!res){
+            log_e("report %u failed", id);
+        } else {
+            xSemaphoreTake(tinyusb_hid_device_input_sem, 0);
+            if(xSemaphoreTake(tinyusb_hid_device_input_sem, timeout_ms / portTICK_PERIOD_MS) != pdTRUE){
+                log_e("report %u wait failed", id);
+                res = false;
+            }
+        }
+    }
 
+    xSemaphoreGive(tinyusb_hid_device_input_mutex);
+    return res;
 }
 
 bool USBHID::addDevice(USBHIDDevice * device, uint16_t descriptor_len, tinyusb_hid_device_descriptor_cb_t cb){
diff --git a/libraries/USB/src/USBHID.h b/libraries/USB/src/USBHID.h
index dba35fb8a4f..defd3349a64 100644
--- a/libraries/USB/src/USBHID.h
+++ b/libraries/USB/src/USBHID.h
@@ -53,7 +53,6 @@ class USBHIDDevice
     virtual uint16_t _onGetFeature(uint8_t* buffer, uint16_t len){return 0;}
     virtual void _onSetFeature(const uint8_t* buffer, uint16_t len){}
     virtual void _onOutput(const uint8_t* buffer, uint16_t len){}
-    virtual void _onInputDone(const uint8_t* buffer, uint16_t len){}
 };
 
 class USBHID
@@ -62,11 +61,10 @@ class USBHID
     USBHID(void);
     void begin(void);
     void end(void);
+    bool SendReport(uint8_t id, const void* data, size_t len, uint32_t timeout_ms = 100);
     void onEvent(esp_event_handler_t callback);
     void onEvent(arduino_usb_hid_event_t event, esp_event_handler_t callback);
     static bool addDevice(USBHIDDevice * device, uint16_t descriptor_len, tinyusb_hid_device_descriptor_cb_t cb);
 };
 
-bool tud_hid_n_wait_ready(uint8_t instance, uint32_t timeout_ms);
-
 #endif
diff --git a/libraries/USB/src/USBHIDConsumerControl.cpp b/libraries/USB/src/USBHIDConsumerControl.cpp
index 9cf98d4539a..9f2a4340c60 100644
--- a/libraries/USB/src/USBHIDConsumerControl.cpp
+++ b/libraries/USB/src/USBHIDConsumerControl.cpp
@@ -26,7 +26,7 @@ static uint16_t tinyusb_hid_device_descriptor_cb(uint8_t * dst, uint8_t report_i
     return sizeof(report_descriptor);
 }
 
-USBHIDConsumerControl::USBHIDConsumerControl(): hid(), tx_sem(NULL){
+USBHIDConsumerControl::USBHIDConsumerControl(): hid(){
 	static bool initialized = false;
 	if(!initialized){
 		initialized = true;
@@ -41,39 +41,14 @@ USBHIDConsumerControl::USBHIDConsumerControl(): hid(), tx_sem(NULL){
 }
 
 void USBHIDConsumerControl::begin(){
-    if(tx_sem == NULL){
-        tx_sem = xSemaphoreCreateBinary();
-        xSemaphoreTake(tx_sem, 0);
-    }
+    hid.begin();
 }
 
 void USBHIDConsumerControl::end(){
-    if (tx_sem != NULL) {
-        vSemaphoreDelete(tx_sem);
-        tx_sem = NULL;
-    }
-}
-
-void USBHIDConsumerControl::_onInputDone(const uint8_t* buffer, uint16_t len){
-    //log_i("len: %u", len);
-    xSemaphoreGive(tx_sem);
 }
 
 bool USBHIDConsumerControl::send(uint16_t value){
-    uint32_t timeout_ms = 100;
-    if(!tud_hid_n_wait_ready(0, timeout_ms)){
-        log_e("not ready");
-        return false;
-    }
-    bool res = tud_hid_n_report(0, id, &value, 2);
-    if(!res){
-        log_e("report failed");
-        return false;
-    } else if(xSemaphoreTake(tx_sem, timeout_ms / portTICK_PERIOD_MS) != pdTRUE){
-        log_e("report wait failed");
-        return false;
-    }
-    return true;
+    return hid.SendReport(id, &value, 2);
 }
 
 size_t USBHIDConsumerControl::press(uint16_t k){
diff --git a/libraries/USB/src/USBHIDConsumerControl.h b/libraries/USB/src/USBHIDConsumerControl.h
index df0b5275b85..579e5704b4f 100644
--- a/libraries/USB/src/USBHIDConsumerControl.h
+++ b/libraries/USB/src/USBHIDConsumerControl.h
@@ -70,7 +70,6 @@
 class USBHIDConsumerControl: public USBHIDDevice {
 private:
     USBHID hid;
-    xSemaphoreHandle tx_sem;
     bool send(uint16_t value);
 public:
     USBHIDConsumerControl(void);
@@ -78,9 +77,6 @@ class USBHIDConsumerControl: public USBHIDDevice {
     void end(void);
     size_t press(uint16_t k);
     size_t release();
-
-    //internal use
-    void _onInputDone(const uint8_t* buffer, uint16_t len);
 };
 
 #endif
diff --git a/libraries/USB/src/USBHIDGamepad.cpp b/libraries/USB/src/USBHIDGamepad.cpp
index 0c13e9de516..44f23a2da8d 100644
--- a/libraries/USB/src/USBHIDGamepad.cpp
+++ b/libraries/USB/src/USBHIDGamepad.cpp
@@ -26,7 +26,7 @@ static uint16_t tinyusb_hid_device_descriptor_cb(uint8_t * dst, uint8_t report_i
     return sizeof(report_descriptor);
 }
 
-USBHIDGamepad::USBHIDGamepad(): hid(), tx_sem(NULL), _x(0), _y(0), _z(0), _rz(0), _rx(0), _ry(0), _hat(0), _buttons(0){
+USBHIDGamepad::USBHIDGamepad(): hid(), _x(0), _y(0), _z(0), _rz(0), _rx(0), _ry(0), _hat(0), _buttons(0){
 	static bool initialized = false;
 	if(!initialized){
 		initialized = true;
@@ -41,39 +41,25 @@ USBHIDGamepad::USBHIDGamepad(): hid(), tx_sem(NULL), _x(0), _y(0), _z(0), _rz(0)
 }
 
 void USBHIDGamepad::begin(){
-    if(tx_sem == NULL){
-        tx_sem = xSemaphoreCreateBinary();
-        xSemaphoreTake(tx_sem, 0);
-    }
+    hid.begin();
 }
 
 void USBHIDGamepad::end(){
-    if (tx_sem != NULL) {
-        vSemaphoreDelete(tx_sem);
-        tx_sem = NULL;
-    }
-}
 
-void USBHIDGamepad::_onInputDone(const uint8_t* buffer, uint16_t len){
-    //log_i("len: %u", len);
-    xSemaphoreGive(tx_sem);
 }
 
 bool USBHIDGamepad::write(){
-    uint32_t timeout_ms = 100;
-    if(!tud_hid_n_wait_ready(0, timeout_ms)){
-        log_e("not ready");
-        return false;
-    }
-    bool res = tud_hid_n_gamepad_report(0, id, _x, _y, _z, _rz, _rx, _ry, _hat, _buttons);
-    if(!res){
-        log_e("report failed");
-        return false;
-    } else if(xSemaphoreTake(tx_sem, timeout_ms / portTICK_PERIOD_MS) != pdTRUE){
-        log_e("report wait failed");
-        return false;
-    }
-    return true;
+    hid_gamepad_report_t report = {
+        .x       = _x,
+        .y       = _y,
+        .z       = _z,
+        .rz      = _rz,
+        .rx      = _rx,
+        .ry      = _ry,
+        .hat     = _hat,
+        .buttons = _buttons
+    };
+    return hid.SendReport(id, &report, sizeof(report));
 }
 
 bool USBHIDGamepad::leftStick(int8_t x, int8_t y){
diff --git a/libraries/USB/src/USBHIDGamepad.h b/libraries/USB/src/USBHIDGamepad.h
index bace503c890..d6b81cc53f4 100644
--- a/libraries/USB/src/USBHIDGamepad.h
+++ b/libraries/USB/src/USBHIDGamepad.h
@@ -53,7 +53,6 @@
 class USBHIDGamepad: public USBHIDDevice {
 private:
     USBHID hid;
-    xSemaphoreHandle tx_sem;
     int8_t  _x;         ///< Delta x  movement of left analog-stick
     int8_t  _y;         ///< Delta y  movement of left analog-stick
     int8_t  _z;         ///< Delta z  movement of right analog-joystick
@@ -80,9 +79,6 @@ class USBHIDGamepad: public USBHIDDevice {
     bool releaseButton(uint8_t button);
 
     bool send(int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons);
-
-    //internal use
-    void _onInputDone(const uint8_t* buffer, uint16_t len);
 };
 
 #endif
diff --git a/libraries/USB/src/USBHIDKeyboard.cpp b/libraries/USB/src/USBHIDKeyboard.cpp
index 2bb59243e66..2d3e69d8fed 100644
--- a/libraries/USB/src/USBHIDKeyboard.cpp
+++ b/libraries/USB/src/USBHIDKeyboard.cpp
@@ -37,7 +37,7 @@ static uint16_t tinyusb_hid_device_descriptor_cb(uint8_t * dst, uint8_t report_i
     return sizeof(report_descriptor);
 }
 
-USBHIDKeyboard::USBHIDKeyboard(): hid(), tx_sem(NULL){
+USBHIDKeyboard::USBHIDKeyboard(): hid(){
     static bool initialized = false;
     if(!initialized){
         initialized = true;
@@ -52,17 +52,10 @@ USBHIDKeyboard::USBHIDKeyboard(): hid(), tx_sem(NULL){
 }
 
 void USBHIDKeyboard::begin(){
-    if(tx_sem == NULL){
-        tx_sem = xSemaphoreCreateBinary();
-        xSemaphoreTake(tx_sem, 0);
-    }
+    hid.begin();
 }
 
 void USBHIDKeyboard::end(){
-    if (tx_sem != NULL) {
-        vSemaphoreDelete(tx_sem);
-        tx_sem = NULL;
-    }
 }
 
 void USBHIDKeyboard::onEvent(esp_event_handler_t callback){
@@ -72,11 +65,6 @@ void USBHIDKeyboard::onEvent(arduino_usb_hid_keyboard_event_t event, esp_event_h
     arduino_usb_event_handler_register_with(ARDUINO_USB_HID_KEYBOARD_EVENTS, event, callback, this);
 }
 
-void USBHIDKeyboard::_onInputDone(const uint8_t* buffer, uint16_t len){
-    //log_d("len: %u", len);
-    xSemaphoreGive(tx_sem);
-}
-
 void USBHIDKeyboard::_onOutput(const uint8_t* buffer, uint16_t len){
     //log_d("LEDS: 0x%02x", buffer[0]);
     arduino_usb_hid_keyboard_event_data_t p = {0};
@@ -86,17 +74,15 @@ void USBHIDKeyboard::_onOutput(const uint8_t* buffer, uint16_t len){
 
 void USBHIDKeyboard::sendReport(KeyReport* keys)
 {
-    uint32_t timeout_ms = 100;
-    if(!tud_hid_n_wait_ready(0, timeout_ms)){
-        log_e("not ready");
-        return;
-    }
-    bool res = tud_hid_n_keyboard_report(0, id, keys->modifiers, keys->keys);
-    if(!res){
-        log_e("report failed");
-    } else if(xSemaphoreTake(tx_sem, timeout_ms / portTICK_PERIOD_MS) != pdTRUE){
-        log_e("report wait failed");
+    hid_keyboard_report_t report;
+    report.reserved = 0;
+    report.modifier = keys->modifiers;
+    if (keys->keys) {
+        memcpy(report.keycode, keys->keys, 6);
+    } else {
+        memset(report.keycode, 0, 6);
     }
+    hid.SendReport(id, &report, sizeof(report));
 }
 
 #define SHIFT 0x80
diff --git a/libraries/USB/src/USBHIDKeyboard.h b/libraries/USB/src/USBHIDKeyboard.h
index 5f4c0e813f4..77833f4ccd2 100644
--- a/libraries/USB/src/USBHIDKeyboard.h
+++ b/libraries/USB/src/USBHIDKeyboard.h
@@ -113,7 +113,6 @@ class USBHIDKeyboard: public USBHIDDevice, public Print
 {
 private:
     USBHID hid;
-    xSemaphoreHandle tx_sem;
     KeyReport _keyReport;
     void sendReport(KeyReport* keys);
 public:
@@ -135,7 +134,6 @@ class USBHIDKeyboard: public USBHIDDevice, public Print
 
     //internal use
     void _onOutput(const uint8_t* buffer, uint16_t len);
-    void _onInputDone(const uint8_t* buffer, uint16_t len);
 };
 
 #endif
diff --git a/libraries/USB/src/USBHIDMouse.cpp b/libraries/USB/src/USBHIDMouse.cpp
index 0eb2683d4c1..2d05b741a22 100644
--- a/libraries/USB/src/USBHIDMouse.cpp
+++ b/libraries/USB/src/USBHIDMouse.cpp
@@ -33,7 +33,7 @@ static uint16_t tinyusb_hid_device_descriptor_cb(uint8_t * dst, uint8_t report_i
     return sizeof(report_descriptor);
 }
 
-USBHIDMouse::USBHIDMouse(): hid(), _buttons(0), tx_sem(NULL){
+USBHIDMouse::USBHIDMouse(): hid(), _buttons(0){
 	static bool initialized = false;
 	if(!initialized){
 		initialized = true;
@@ -48,22 +48,10 @@ USBHIDMouse::USBHIDMouse(): hid(), _buttons(0), tx_sem(NULL){
 }
 
 void USBHIDMouse::begin(){
-    if(tx_sem == NULL){
-        tx_sem = xSemaphoreCreateBinary();
-        xSemaphoreTake(tx_sem, 0);
-    }
+    hid.begin();
 }
 
 void USBHIDMouse::end(){
-    if (tx_sem != NULL) {
-        vSemaphoreDelete(tx_sem);
-        tx_sem = NULL;
-    }
-}
-
-void USBHIDMouse::_onInputDone(const uint8_t* buffer, uint16_t len){
-    //log_i("len: %u", len);
-    xSemaphoreGive(tx_sem);
 }
 
 void USBHIDMouse::click(uint8_t b){
@@ -74,17 +62,14 @@ void USBHIDMouse::click(uint8_t b){
 }
 
 void USBHIDMouse::move(int8_t x, int8_t y, int8_t wheel, int8_t pan){
-    uint32_t timeout_ms = 100;
-    if(!tud_hid_n_wait_ready(0, timeout_ms)){
-        log_e("not ready");
-        return;
-    }
-    bool res = tud_hid_n_mouse_report(0, id, _buttons, x, y, wheel, pan);
-    if(!res){
-        log_e("report failed");
-    } else if(xSemaphoreTake(tx_sem, timeout_ms / portTICK_PERIOD_MS) != pdTRUE){
-        log_e("report wait failed");
-    }
+    hid_mouse_report_t report = {
+        .buttons = _buttons,
+        .x       = x,
+        .y       = y,
+        .wheel   = wheel,
+        .pan     = pan
+    };
+    hid.SendReport(id, &report, sizeof(report));
 }
 
 void USBHIDMouse::buttons(uint8_t b){
diff --git a/libraries/USB/src/USBHIDMouse.h b/libraries/USB/src/USBHIDMouse.h
index e7ce493208b..afa238f43f6 100644
--- a/libraries/USB/src/USBHIDMouse.h
+++ b/libraries/USB/src/USBHIDMouse.h
@@ -34,7 +34,6 @@ class USBHIDMouse: public USBHIDDevice {
 private:
     USBHID hid;
     uint8_t _buttons;
-    xSemaphoreHandle tx_sem;
     void buttons(uint8_t b);
     bool write(int8_t x, int8_t y, int8_t vertical, int8_t horizontal);
 public:
@@ -47,9 +46,6 @@ class USBHIDMouse: public USBHIDDevice {
     void press(uint8_t b = MOUSE_LEFT);   // press LEFT by default
     void release(uint8_t b = MOUSE_LEFT); // release LEFT by default
     bool isPressed(uint8_t b = MOUSE_LEFT); // check LEFT by default
-
-    //internal use
-    void _onInputDone(const uint8_t* buffer, uint16_t len);
 };
 
 #endif
diff --git a/libraries/USB/src/USBHIDSystemControl.cpp b/libraries/USB/src/USBHIDSystemControl.cpp
index 5b41c78bbcf..5c22861af8b 100644
--- a/libraries/USB/src/USBHIDSystemControl.cpp
+++ b/libraries/USB/src/USBHIDSystemControl.cpp
@@ -26,7 +26,7 @@ static uint16_t tinyusb_hid_device_descriptor_cb(uint8_t * dst, uint8_t report_i
     return sizeof(report_descriptor);
 }
 
-USBHIDSystemControl::USBHIDSystemControl(): hid(), tx_sem(NULL){
+USBHIDSystemControl::USBHIDSystemControl(): hid(){
 	static bool initialized = false;
 	if(!initialized){
 		initialized = true;
@@ -41,39 +41,14 @@ USBHIDSystemControl::USBHIDSystemControl(): hid(), tx_sem(NULL){
 }
 
 void USBHIDSystemControl::begin(){
-    if(tx_sem == NULL){
-        tx_sem = xSemaphoreCreateBinary();
-        xSemaphoreTake(tx_sem, 0);
-    }
+    hid.begin();
 }
 
 void USBHIDSystemControl::end(){
-    if (tx_sem != NULL) {
-        vSemaphoreDelete(tx_sem);
-        tx_sem = NULL;
-    }
-}
-
-void USBHIDSystemControl::_onInputDone(const uint8_t* buffer, uint16_t len){
-    //log_i("len: %u", len);
-    xSemaphoreGive(tx_sem);
 }
 
 bool USBHIDSystemControl::send(uint8_t value){
-    uint32_t timeout_ms = 100;
-    if(!tud_hid_n_wait_ready(0, timeout_ms)){
-        log_e("not ready");
-        return false;
-    }
-    bool res = tud_hid_n_report(0, id, &value, 1);
-    if(!res){
-        log_e("report failed");
-        return false;
-    } else if(xSemaphoreTake(tx_sem, timeout_ms / portTICK_PERIOD_MS) != pdTRUE){
-        log_e("report wait failed");
-        return false;
-    }
-    return true;
+    return hid.SendReport(id, &value, 1);
 }
 
 size_t USBHIDSystemControl::press(uint8_t k){
diff --git a/libraries/USB/src/USBHIDSystemControl.h b/libraries/USB/src/USBHIDSystemControl.h
index 7a5f65764db..af83d22ba1c 100644
--- a/libraries/USB/src/USBHIDSystemControl.h
+++ b/libraries/USB/src/USBHIDSystemControl.h
@@ -24,7 +24,6 @@
 class USBHIDSystemControl: public USBHIDDevice {
 private:
     USBHID hid;
-    xSemaphoreHandle tx_sem;
     bool send(uint8_t value);
 public:
     USBHIDSystemControl(void);
@@ -32,9 +31,6 @@ class USBHIDSystemControl: public USBHIDDevice {
     void end(void);
     size_t press(uint8_t k);
     size_t release();
-
-    //internal use
-    void _onInputDone(const uint8_t* buffer, uint16_t len);
 };
 
 #endif
diff --git a/libraries/USB/src/USBHIDVendor.cpp b/libraries/USB/src/USBHIDVendor.cpp
index 45497cf73e3..9336b4d0bfb 100644
--- a/libraries/USB/src/USBHIDVendor.cpp
+++ b/libraries/USB/src/USBHIDVendor.cpp
@@ -21,9 +21,6 @@ ESP_EVENT_DEFINE_BASE(ARDUINO_USB_HID_VENDOR_EVENTS);
 esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait);
 esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg);
 
-
-//GENERIC HID
-
 // HID Generic Input, Output & Feature
 // - 1st parameter is report size (mandatory)
 // - 2nd parameter is report id HID_REPORT_ID(n) (optional)
@@ -58,23 +55,26 @@ esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, i
 
 #define TUD_HID_REPORT_DESC_GENERIC_INOUT_FEATURE_LEN 46
 
+// max size is 64 and we need one byte for the report ID
+static const uint8_t HID_VENDOR_REPORT_SIZE = 63;
+
 static uint16_t tinyusb_hid_device_descriptor_cb(uint8_t * dst, uint8_t report_id){
     uint8_t report_descriptor[] = {
-        TUD_HID_REPORT_DESC_GENERIC_INOUT_FEATURE(63, HID_REPORT_ID(report_id))
+        TUD_HID_REPORT_DESC_GENERIC_INOUT_FEATURE(HID_VENDOR_REPORT_SIZE, HID_REPORT_ID(report_id))
     };
     memcpy(dst, report_descriptor, sizeof(report_descriptor));
     return sizeof(report_descriptor);
 }
 
-USBHIDVendor::USBHIDVendor(): hid(), tx_sem(NULL){
+USBHIDVendor::USBHIDVendor(): hid(){
 	static bool initialized = false;
 	if(!initialized){
 		initialized = true;
 		uint8_t report_descriptor[] = {
-		    TUD_HID_REPORT_DESC_GENERIC_INOUT_FEATURE(0, HID_REPORT_ID(0))
+		    TUD_HID_REPORT_DESC_GENERIC_INOUT_FEATURE(HID_VENDOR_REPORT_SIZE, HID_REPORT_ID(1))
 		};
 		hid.addDevice(this, sizeof(report_descriptor), tinyusb_hid_device_descriptor_cb);
-        memset(feature, 0, 63);
+        memset(feature, 0, HID_VENDOR_REPORT_SIZE);
 	} else {
 		isr_log_e("Only one instance of USBHIDVendor is allowed!");
 		abort();
@@ -97,30 +97,24 @@ size_t USBHIDVendor::setRxBufferSize(size_t rx_queue_len){
 }
 
 void USBHIDVendor::begin(){
-    if(tx_sem == NULL){
-        tx_sem = xSemaphoreCreateBinary();
-        xSemaphoreTake(tx_sem, 0);
-    }
+    hid.begin();
     setRxBufferSize(256);//default if not preset
 }
 
 void USBHIDVendor::end(){
     setRxBufferSize(0);
-    if (tx_sem != NULL) {
-        vSemaphoreDelete(tx_sem);
-        tx_sem = NULL;
-    }
 }
 
 void USBHIDVendor::onEvent(esp_event_handler_t callback){
     onEvent(ARDUINO_USB_HID_VENDOR_ANY_EVENT, callback);
 }
+
 void USBHIDVendor::onEvent(arduino_usb_hid_vendor_event_t event, esp_event_handler_t callback){
     arduino_usb_event_handler_register_with(ARDUINO_USB_HID_VENDOR_EVENTS, event, callback, this);
 }
 
 uint16_t USBHIDVendor::_onGetFeature(uint8_t* buffer, uint16_t len){
-    //log_i("len: %u", len);
+    log_v("len: %u", len);
     memcpy(buffer, feature, len);
     arduino_usb_hid_vendor_event_data_t p = {0};
     p.buffer = feature;
@@ -128,16 +122,18 @@ uint16_t USBHIDVendor::_onGetFeature(uint8_t* buffer, uint16_t len){
     arduino_usb_event_post(ARDUINO_USB_HID_VENDOR_EVENTS, ARDUINO_USB_HID_VENDOR_GET_FEATURE_EVENT, &p, sizeof(arduino_usb_hid_vendor_event_data_t), portMAX_DELAY);
     return len;
 }
+
 void USBHIDVendor::_onSetFeature(const uint8_t* buffer, uint16_t len){
-    //log_i("len: %u", len);
+    log_v("len: %u", len);
     memcpy(feature, buffer, len);
     arduino_usb_hid_vendor_event_data_t p = {0};
     p.buffer = feature;
     p.len = len;
     arduino_usb_event_post(ARDUINO_USB_HID_VENDOR_EVENTS, ARDUINO_USB_HID_VENDOR_SET_FEATURE_EVENT, &p, sizeof(arduino_usb_hid_vendor_event_data_t), portMAX_DELAY);
 }
+
 void USBHIDVendor::_onOutput(const uint8_t* buffer, uint16_t len){
-    //log_i("len: %u", len);
+    log_v("len: %u", len);
     for(uint32_t i=0; i<len; i++){
         if(rx_queue == NULL || !xQueueSend(rx_queue, buffer+i, 0)){
             len = i+1;
@@ -150,33 +146,22 @@ void USBHIDVendor::_onOutput(const uint8_t* buffer, uint16_t len){
     p.len = len;
     arduino_usb_event_post(ARDUINO_USB_HID_VENDOR_EVENTS, ARDUINO_USB_HID_VENDOR_OUTPUT_EVENT, &p, sizeof(arduino_usb_hid_vendor_event_data_t), portMAX_DELAY);
 }
-void USBHIDVendor::_onInputDone(const uint8_t* buffer, uint16_t len){
-    //log_i("len: %u", buffer[0]);
-    xSemaphoreGive(tx_sem);
-}
+
 size_t USBHIDVendor::write(const uint8_t* buffer, uint16_t len){
-	uint32_t timeout_ms = 100;
-    uint8_t hid_in[64] = {id, 0x00, };
     const uint8_t * data = (const uint8_t *)buffer;
-    size_t to_send = len, max_send=64-2, will_send=0;
+    size_t to_send = len, max_send=HID_VENDOR_REPORT_SIZE, will_send=0;
     while(to_send){
         will_send = to_send;
         if(will_send > max_send){
             will_send = max_send;
         }
-        if(!tud_hid_n_wait_ready(0, timeout_ms)){
-            log_e("not ready");
-            return len - to_send;
-        }
-        hid_in[1] = (uint8_t)will_send;
-        memcpy(hid_in+2, data, will_send);
-        memset(hid_in+2+will_send, 0, 64 - (will_send+2));
-        if(!tud_hid_n_report(0, 0, hid_in, 64)){
-            log_e("report failed");
-            return len - to_send;
-        }
-        if(xSemaphoreTake(tx_sem, timeout_ms / portTICK_PERIOD_MS) != pdTRUE){
-            log_e("report wait failed");
+        // On Mac, I can get INPUT only when data length equals the input report size
+        // To be tested on other platforms
+        //uint8_t hid_in[HID_VENDOR_REPORT_SIZE];
+        //memcpy(hid_in, data, will_send);
+        //memset(hid_in + will_send, 0, HID_VENDOR_REPORT_SIZE - will_send);
+        //if(!hid.SendReport(id, hid_in, HID_VENDOR_REPORT_SIZE)){
+        if(!hid.SendReport(id, buffer + (len - to_send), will_send)){
             return len - to_send;
         }
         to_send -= will_send;
diff --git a/libraries/USB/src/USBHIDVendor.h b/libraries/USB/src/USBHIDVendor.h
index 395529115ba..1f88c25ef01 100644
--- a/libraries/USB/src/USBHIDVendor.h
+++ b/libraries/USB/src/USBHIDVendor.h
@@ -36,7 +36,6 @@ class USBHIDVendor: public USBHIDDevice, public Stream {
 private:
     USBHID hid;
     uint8_t feature[63];
-    xSemaphoreHandle tx_sem;
     xQueueHandle rx_queue;
 public:
     USBHIDVendor(void);
@@ -58,7 +57,6 @@ class USBHIDVendor: public USBHIDDevice, public Stream {
     uint16_t _onGetFeature(uint8_t* buffer, uint16_t len);
     void _onSetFeature(const uint8_t* buffer, uint16_t len);
     void _onOutput(const uint8_t* buffer, uint16_t len);
-    void _onInputDone(const uint8_t* buffer, uint16_t len);
 };
 
 #endif

From f36e48361194c672efbde582a6085b0a715b3463 Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Tue, 17 Aug 2021 15:27:17 +0300
Subject: [PATCH 04/17] Parse and assign report IDs to devices

This allows one device to have multiple report IDs, but also requires that different devices do not use the same report ID
---
 libraries/USB/src/USBHID.cpp                | 153 +++++++++++++++-----
 libraries/USB/src/USBHID.h                  |  26 ++--
 libraries/USB/src/USBHIDConsumerControl.cpp |  22 ++-
 libraries/USB/src/USBHIDConsumerControl.h   |   3 +
 libraries/USB/src/USBHIDGamepad.cpp         |  22 ++-
 libraries/USB/src/USBHIDGamepad.h           |   3 +
 libraries/USB/src/USBHIDKeyboard.cpp        |  24 ++-
 libraries/USB/src/USBHIDKeyboard.h          |   5 +-
 libraries/USB/src/USBHIDMouse.cpp           |  36 +++--
 libraries/USB/src/USBHIDMouse.h             |   3 +
 libraries/USB/src/USBHIDSystemControl.cpp   |  22 ++-
 libraries/USB/src/USBHIDSystemControl.h     |   3 +
 libraries/USB/src/USBHIDVendor.cpp          |  36 +++--
 libraries/USB/src/USBHIDVendor.h            |   9 +-
 14 files changed, 230 insertions(+), 137 deletions(-)

diff --git a/libraries/USB/src/USBHID.cpp b/libraries/USB/src/USBHID.cpp
index 0fffd1650df..de241c293dd 100644
--- a/libraries/USB/src/USBHID.cpp
+++ b/libraries/USB/src/USBHID.cpp
@@ -14,6 +14,7 @@
 #include "esp32-hal.h"
 #include "esp32-hal-tinyusb.h"
 #include "USBHID.h"
+#include "esp_hid_common.h"
 
 #if CFG_TUD_HID
 #define USB_HID_DEVICES_MAX 10
@@ -46,16 +47,26 @@ void log_print_buf(const uint8_t *b, size_t len){
     }
 }
 
+typedef struct {
+    USBHIDDevice * device;
+    uint8_t reports_num;
+    uint8_t * report_ids;
+} tinyusb_hid_device_t;
+
+static tinyusb_hid_device_t tinyusb_hid_devices[USB_HID_DEVICES_MAX];
+
+static uint8_t tinyusb_hid_devices_num = 0;
+static bool tinyusb_hid_devices_is_initialized = false;
+static xSemaphoreHandle tinyusb_hid_device_input_sem = NULL;
+static xSemaphoreHandle tinyusb_hid_device_input_mutex = NULL;
 
 static bool tinyusb_hid_is_initialized = false;
-static tinyusb_hid_device_descriptor_cb_t tinyusb_loaded_hid_devices_callbacks[USB_HID_DEVICES_MAX];
 static uint8_t tinyusb_loaded_hid_devices_num = 0;
-static uint8_t tinyusb_loaded_hid_devices_report_id = 1;
 static uint16_t tinyusb_hid_device_descriptor_len = 0;
 static uint8_t * tinyusb_hid_device_descriptor = NULL;
 static const char * tinyusb_hid_device_report_types[4] = {"INVALID", "INPUT", "OUTPUT", "FEATURE"};
 
-static bool tinyusb_enable_hid_device(uint16_t descriptor_len, tinyusb_hid_device_descriptor_cb_t cb){
+static bool tinyusb_enable_hid_device(uint16_t descriptor_len, USBHIDDevice * device){
     if(tinyusb_hid_is_initialized){
         log_e("TinyUSB HID has already started! Device not enabled");
         return false;
@@ -65,20 +76,102 @@ static bool tinyusb_enable_hid_device(uint16_t descriptor_len, tinyusb_hid_devic
         return false;
     }
     tinyusb_hid_device_descriptor_len += descriptor_len;
-    tinyusb_loaded_hid_devices_callbacks[tinyusb_loaded_hid_devices_num++] = cb;
-    log_d("Device enabled");
+    tinyusb_hid_devices[tinyusb_loaded_hid_devices_num++].device = device;
+
+    log_d("Device[%u] len: %u", tinyusb_loaded_hid_devices_num-1, descriptor_len);
     return true;
 }
 
-static uint16_t tinyusb_load_hid_descriptor(uint8_t interface, uint8_t * dst, uint8_t report_id)
-{
-    if(interface < USB_HID_DEVICES_MAX && tinyusb_loaded_hid_devices_callbacks[interface] != NULL){
-        return tinyusb_loaded_hid_devices_callbacks[interface](dst, report_id);
+USBHIDDevice * tinyusb_get_device_by_report_id(uint8_t report_id){
+    for(uint8_t i=0; i<tinyusb_loaded_hid_devices_num; i++){
+        tinyusb_hid_device_t * device = &tinyusb_hid_devices[i];
+        if(device->device && device->reports_num){
+            for(uint8_t r=0; r<device->reports_num; r++){
+                if(report_id == device->report_ids[r]){
+                    return device->device;
+                }
+            }
+        }
+    }
+    return NULL;
+}
+
+static uint16_t tinyusb_on_get_feature(uint8_t report_id, uint8_t* buffer, uint16_t reqlen){
+    USBHIDDevice * device = tinyusb_get_device_by_report_id(report_id);
+    if(device){
+        return device->_onGetFeature(report_id, buffer, reqlen);
     }
     return 0;
 }
 
+static bool tinyusb_on_set_feature(uint8_t report_id, const uint8_t* buffer, uint16_t reqlen){
+    USBHIDDevice * device = tinyusb_get_device_by_report_id(report_id);
+    if(device){
+        device->_onSetFeature(report_id, buffer, reqlen);
+        return true;
+    }
+    return false;
+}
+
+static bool tinyusb_on_set_output(uint8_t report_id, const uint8_t* buffer, uint16_t reqlen){
+    USBHIDDevice * device = tinyusb_get_device_by_report_id(report_id);
+    if(device){
+        device->_onOutput(report_id, buffer, reqlen);
+        return true;
+    }
+    return false;
+}
+
+static uint16_t tinyusb_on_add_descriptor(uint8_t device_index, uint8_t * dst){
+    uint16_t res = 0;
+    uint8_t report_id = 0, reports_num = 0;
+    tinyusb_hid_device_t * device = &tinyusb_hid_devices[device_index];
+    if(device->device){
+        res = device->device->_onGetDescriptor(dst);
+        if(res){
+
+            esp_hid_report_map_t *hid_report_map = esp_hid_parse_report_map(dst, res);
+            if(hid_report_map){
+                if(device->report_ids){
+                    free(device->report_ids);
+                }
+                device->reports_num = hid_report_map->reports_len;
+                device->report_ids = (uint8_t*)malloc(device->reports_num);
+                memset(device->report_ids, 0, device->reports_num);
+                reports_num = device->reports_num;
+
+                for(uint8_t i=0; i<device->reports_num; i++){
+                    if(hid_report_map->reports[i].protocol_mode == ESP_HID_PROTOCOL_MODE_REPORT){
+                        report_id = hid_report_map->reports[i].report_id;
+                        for(uint8_t r=0; r<device->reports_num; r++){
+                            if(report_id == device->report_ids[r]){
+                                //already added
+                                reports_num--;
+                                break;
+                            } else if(!device->report_ids[r]){
+                                //empty slot
+                                device->report_ids[r] = report_id;
+                                break;
+                            }
+                        }
+                    } else {
+                        reports_num--;
+                    }
+                }
+                device->reports_num = reports_num;
+                esp_hid_free_report_map(hid_report_map);
+            }
+
+        }
+    }
+    return res;
+}
+
+
 static bool tinyusb_load_enabled_hid_devices(){
+    if(tinyusb_hid_device_descriptor != NULL){
+        return true;
+    }
     tinyusb_hid_device_descriptor = (uint8_t *)malloc(tinyusb_hid_device_descriptor_len);
     if (tinyusb_hid_device_descriptor == NULL) {
         log_e("HID Descriptor Malloc Failed");
@@ -86,15 +179,15 @@ static bool tinyusb_load_enabled_hid_devices(){
     }
     uint8_t * dst = tinyusb_hid_device_descriptor;
 
-    for(uint8_t i=0; i<USB_HID_DEVICES_MAX; i++){
-            uint16_t len = tinyusb_load_hid_descriptor(i, dst, i+1);
+    for(uint8_t i=0; i<tinyusb_loaded_hid_devices_num; i++){
+            uint16_t len = tinyusb_on_add_descriptor(i, dst);
             if (!len) {
                 break;
             } else {
                 dst += len;
             }
     }
-    log_d("Load Done: reports: %u, descr_len: %u", tinyusb_loaded_hid_devices_report_id - 1, tinyusb_hid_device_descriptor_len);
+
     return true;
 }
 
@@ -125,12 +218,6 @@ extern "C" uint16_t tusb_hid_load_descriptor(uint8_t * dst, uint8_t * itf)
 
 
 
-static USBHIDDevice * tinyusb_hid_devices[USB_HID_DEVICES_MAX+1];
-static uint8_t tinyusb_hid_devices_num = 0;
-static bool tinyusb_hid_devices_is_initialized = false;
-static xSemaphoreHandle tinyusb_hid_device_input_sem = NULL;
-static xSemaphoreHandle tinyusb_hid_device_input_mutex = NULL;
-
 
 // Invoked when received GET HID REPORT DESCRIPTOR request
 // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
@@ -165,11 +252,11 @@ bool tud_hid_set_idle_cb(uint8_t instance, uint8_t idle_rate){
 // Application must fill buffer report's content and return its length.
 // Return zero will cause the stack to STALL request
 uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen){
-    if(report_id < USB_HID_DEVICES_MAX && tinyusb_hid_devices[report_id]){
-        return tinyusb_hid_devices[report_id]->_onGetFeature(buffer, reqlen);
+    uint16_t res = tinyusb_on_get_feature(report_id, buffer, reqlen);
+    if(!res){
+        log_d("instance: %u, report_id: %u, report_type: %s, reqlen: %u", instance, report_id, tinyusb_hid_device_report_types[report_type], reqlen);
     }
-    log_d("instance: %u, report_id: %u, report_type: %s, reqlen: %u", instance, report_id, tinyusb_hid_device_report_types[report_type], reqlen);
-    return 0;
+    return res;
 }
 
 // Invoked when received SET_REPORT control request or
@@ -177,18 +264,14 @@ uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_t
 void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize){
     if(!report_id && !report_type){
         report_id = buffer[0];
-        if(report_id < USB_HID_DEVICES_MAX && tinyusb_hid_devices[report_id]){
-            tinyusb_hid_devices[report_id]->_onOutput(buffer+1, bufsize-1);
-        } else {
+        if(!tinyusb_on_set_output(report_id, buffer+1, bufsize-1)){
             log_d("instance: %u, report_id: %u, report_type: %s, bufsize: %u", instance, *buffer, tinyusb_hid_device_report_types[HID_REPORT_TYPE_OUTPUT], bufsize-1);
-            log_print_buf(buffer+1, bufsize-1);
+            //log_print_buf(buffer+1, bufsize-1);
         }
     } else {
-        if(report_id < USB_HID_DEVICES_MAX && tinyusb_hid_devices[report_id]){
-            tinyusb_hid_devices[report_id]->_onSetFeature(buffer, bufsize);
-        } else {
+        if(!tinyusb_on_set_feature(report_id, buffer, bufsize)){
             log_d("instance: %u, report_id: %u, report_type: %s, bufsize: %u", instance, report_id, tinyusb_hid_device_report_types[report_type], bufsize);
-            log_print_buf(buffer, bufsize);
+            //log_print_buf(buffer, bufsize);
         }
     }
 }
@@ -196,7 +279,9 @@ void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_
 USBHID::USBHID(){
     if(!tinyusb_hid_devices_is_initialized){
         tinyusb_hid_devices_is_initialized = true;
-        memset(tinyusb_hid_devices, 0, sizeof(tinyusb_hid_devices));
+        for(uint8_t i=0; i<USB_HID_DEVICES_MAX; i++){
+            memset(&tinyusb_hid_devices[i], 0, sizeof(tinyusb_hid_device_t));
+        }
         tinyusb_hid_devices_num = 0;
         tinyusb_enable_interface(USB_INTERFACE_HID, TUD_HID_INOUT_DESC_LEN, tusb_hid_load_descriptor);
     }
@@ -259,13 +344,11 @@ bool USBHID::SendReport(uint8_t id, const void* data, size_t len, uint32_t timeo
     return res;
 }
 
-bool USBHID::addDevice(USBHIDDevice * device, uint16_t descriptor_len, tinyusb_hid_device_descriptor_cb_t cb){
+bool USBHID::addDevice(USBHIDDevice * device, uint16_t descriptor_len){
     if(device && tinyusb_loaded_hid_devices_num < USB_HID_DEVICES_MAX){
-        if(!tinyusb_enable_hid_device(descriptor_len, cb)){
+        if(!tinyusb_enable_hid_device(descriptor_len, device)){
             return false;
         }
-        device->id = tinyusb_loaded_hid_devices_num;
-        tinyusb_hid_devices[tinyusb_loaded_hid_devices_num] = device;
         return true;
     }
     return false;
diff --git a/libraries/USB/src/USBHID.h b/libraries/USB/src/USBHID.h
index defd3349a64..426cfd9b28d 100644
--- a/libraries/USB/src/USBHID.h
+++ b/libraries/USB/src/USBHID.h
@@ -22,6 +22,17 @@
 #include "class/hid/hid.h"
 #include "esp_event.h"
 
+// Used by the included TinyUSB drivers
+enum {
+    HID_REPORT_ID_NONE,
+    HID_REPORT_ID_KEYBOARD,
+    HID_REPORT_ID_MOUSE,
+    HID_REPORT_ID_GAMEPAD,
+    HID_REPORT_ID_CONSUMER_CONTROL,
+    HID_REPORT_ID_SYSTEM_CONTROL,
+    HID_REPORT_ID_VENDOR
+};
+
 ESP_EVENT_DECLARE_BASE(ARDUINO_USB_HID_EVENTS);
 
 typedef enum {
@@ -43,16 +54,13 @@ typedef struct {
     };
 } arduino_usb_hid_event_data_t;
 
-typedef uint16_t (*tinyusb_hid_device_descriptor_cb_t)(uint8_t * dst, uint8_t report_id);
-
 class USBHIDDevice
 {
 public:
-    uint8_t id;
-    USBHIDDevice():id(0){}
-    virtual uint16_t _onGetFeature(uint8_t* buffer, uint16_t len){return 0;}
-    virtual void _onSetFeature(const uint8_t* buffer, uint16_t len){}
-    virtual void _onOutput(const uint8_t* buffer, uint16_t len){}
+    virtual uint16_t _onGetDescriptor(uint8_t* buffer){return 0;}
+    virtual uint16_t _onGetFeature(uint8_t report_id, uint8_t* buffer, uint16_t len){return 0;}
+    virtual void _onSetFeature(uint8_t report_id, const uint8_t* buffer, uint16_t len){}
+    virtual void _onOutput(uint8_t report_id, const uint8_t* buffer, uint16_t len){}
 };
 
 class USBHID
@@ -61,10 +69,10 @@ class USBHID
     USBHID(void);
     void begin(void);
     void end(void);
-    bool SendReport(uint8_t id, const void* data, size_t len, uint32_t timeout_ms = 100);
+    bool SendReport(uint8_t report_id, const void* data, size_t len, uint32_t timeout_ms = 100);
     void onEvent(esp_event_handler_t callback);
     void onEvent(arduino_usb_hid_event_t event, esp_event_handler_t callback);
-    static bool addDevice(USBHIDDevice * device, uint16_t descriptor_len, tinyusb_hid_device_descriptor_cb_t cb);
+    static bool addDevice(USBHIDDevice * device, uint16_t descriptor_len);
 };
 
 #endif
diff --git a/libraries/USB/src/USBHIDConsumerControl.cpp b/libraries/USB/src/USBHIDConsumerControl.cpp
index 9f2a4340c60..33c867e9cf1 100644
--- a/libraries/USB/src/USBHIDConsumerControl.cpp
+++ b/libraries/USB/src/USBHIDConsumerControl.cpp
@@ -18,28 +18,26 @@
 
 #if CFG_TUD_HID
 
-static uint16_t tinyusb_hid_device_descriptor_cb(uint8_t * dst, uint8_t report_id){
-    uint8_t report_descriptor[] = {
-        TUD_HID_REPORT_DESC_CONSUMER(HID_REPORT_ID(report_id))
-    };
-    memcpy(dst, report_descriptor, sizeof(report_descriptor));
-    return sizeof(report_descriptor);
-}
+static const uint8_t report_descriptor[] = {
+    TUD_HID_REPORT_DESC_CONSUMER(HID_REPORT_ID(HID_REPORT_ID_CONSUMER_CONTROL))
+};
 
 USBHIDConsumerControl::USBHIDConsumerControl(): hid(){
 	static bool initialized = false;
 	if(!initialized){
 		initialized = true;
-		uint8_t report_descriptor[] = {
-		    TUD_HID_REPORT_DESC_CONSUMER(HID_REPORT_ID(0))
-		};
-		hid.addDevice(this, sizeof(report_descriptor), tinyusb_hid_device_descriptor_cb);
+		hid.addDevice(this, sizeof(report_descriptor));
 	} else {
 		isr_log_e("Only one instance of USBHIDConsumerControl is allowed!");
 		abort();
 	}
 }
 
+uint16_t USBHIDConsumerControl::_onGetDescriptor(uint8_t* dst){
+    memcpy(dst, report_descriptor, sizeof(report_descriptor));
+    return sizeof(report_descriptor);
+}
+
 void USBHIDConsumerControl::begin(){
     hid.begin();
 }
@@ -48,7 +46,7 @@ void USBHIDConsumerControl::end(){
 }
 
 bool USBHIDConsumerControl::send(uint16_t value){
-    return hid.SendReport(id, &value, 2);
+    return hid.SendReport(HID_REPORT_ID_CONSUMER_CONTROL, &value, 2);
 }
 
 size_t USBHIDConsumerControl::press(uint16_t k){
diff --git a/libraries/USB/src/USBHIDConsumerControl.h b/libraries/USB/src/USBHIDConsumerControl.h
index 579e5704b4f..9c60ff69e6f 100644
--- a/libraries/USB/src/USBHIDConsumerControl.h
+++ b/libraries/USB/src/USBHIDConsumerControl.h
@@ -77,6 +77,9 @@ class USBHIDConsumerControl: public USBHIDDevice {
     void end(void);
     size_t press(uint16_t k);
     size_t release();
+
+    // internal use
+    uint16_t _onGetDescriptor(uint8_t* buffer);
 };
 
 #endif
diff --git a/libraries/USB/src/USBHIDGamepad.cpp b/libraries/USB/src/USBHIDGamepad.cpp
index 44f23a2da8d..b77aea3acd6 100644
--- a/libraries/USB/src/USBHIDGamepad.cpp
+++ b/libraries/USB/src/USBHIDGamepad.cpp
@@ -18,28 +18,26 @@
 
 #if CFG_TUD_HID
 
-static uint16_t tinyusb_hid_device_descriptor_cb(uint8_t * dst, uint8_t report_id){
-    uint8_t report_descriptor[] = {
-        TUD_HID_REPORT_DESC_GAMEPAD(HID_REPORT_ID(report_id))
-    };
-    memcpy(dst, report_descriptor, sizeof(report_descriptor));
-    return sizeof(report_descriptor);
-}
+static const uint8_t report_descriptor[] = {
+    TUD_HID_REPORT_DESC_GAMEPAD(HID_REPORT_ID(HID_REPORT_ID_GAMEPAD))
+};
 
 USBHIDGamepad::USBHIDGamepad(): hid(), _x(0), _y(0), _z(0), _rz(0), _rx(0), _ry(0), _hat(0), _buttons(0){
 	static bool initialized = false;
 	if(!initialized){
 		initialized = true;
-		uint8_t report_descriptor[] = {
-		    TUD_HID_REPORT_DESC_GAMEPAD(HID_REPORT_ID(0))
-		};
-		hid.addDevice(this, sizeof(report_descriptor), tinyusb_hid_device_descriptor_cb);
+		hid.addDevice(this, sizeof(report_descriptor));
 	} else {
 		isr_log_e("Only one instance of USBHIDGamepad is allowed!");
 		abort();
 	}
 }
 
+uint16_t USBHIDGamepad::_onGetDescriptor(uint8_t* dst){
+    memcpy(dst, report_descriptor, sizeof(report_descriptor));
+    return sizeof(report_descriptor);
+}
+
 void USBHIDGamepad::begin(){
     hid.begin();
 }
@@ -59,7 +57,7 @@ bool USBHIDGamepad::write(){
         .hat     = _hat,
         .buttons = _buttons
     };
-    return hid.SendReport(id, &report, sizeof(report));
+    return hid.SendReport(HID_REPORT_ID_GAMEPAD, &report, sizeof(report));
 }
 
 bool USBHIDGamepad::leftStick(int8_t x, int8_t y){
diff --git a/libraries/USB/src/USBHIDGamepad.h b/libraries/USB/src/USBHIDGamepad.h
index d6b81cc53f4..72b096db005 100644
--- a/libraries/USB/src/USBHIDGamepad.h
+++ b/libraries/USB/src/USBHIDGamepad.h
@@ -79,6 +79,9 @@ class USBHIDGamepad: public USBHIDDevice {
     bool releaseButton(uint8_t button);
 
     bool send(int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons);
+
+    // internal use
+    uint16_t _onGetDescriptor(uint8_t* buffer);
 };
 
 #endif
diff --git a/libraries/USB/src/USBHIDKeyboard.cpp b/libraries/USB/src/USBHIDKeyboard.cpp
index 2d3e69d8fed..ec3444ba6c6 100644
--- a/libraries/USB/src/USBHIDKeyboard.cpp
+++ b/libraries/USB/src/USBHIDKeyboard.cpp
@@ -29,28 +29,26 @@ ESP_EVENT_DEFINE_BASE(ARDUINO_USB_HID_KEYBOARD_EVENTS);
 esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait);
 esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg);
 
-static uint16_t tinyusb_hid_device_descriptor_cb(uint8_t * dst, uint8_t report_id){
-    uint8_t report_descriptor[] = {
-        TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(report_id))
-    };
-    memcpy(dst, report_descriptor, sizeof(report_descriptor));
-    return sizeof(report_descriptor);
-}
+static const uint8_t report_descriptor[] = {
+    TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(HID_REPORT_ID_KEYBOARD))
+};
 
 USBHIDKeyboard::USBHIDKeyboard(): hid(){
     static bool initialized = false;
     if(!initialized){
         initialized = true;
-        uint8_t report_descriptor[] = {
-            TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(0))
-        };
-        hid.addDevice(this, sizeof(report_descriptor), tinyusb_hid_device_descriptor_cb);
+        hid.addDevice(this, sizeof(report_descriptor));
     } else {
         isr_log_e("Only one instance of USBHIDKeyboard is allowed!");
         abort();
     }
 }
 
+uint16_t USBHIDKeyboard::_onGetDescriptor(uint8_t* dst){
+    memcpy(dst, report_descriptor, sizeof(report_descriptor));
+    return sizeof(report_descriptor);
+}
+
 void USBHIDKeyboard::begin(){
     hid.begin();
 }
@@ -65,7 +63,7 @@ void USBHIDKeyboard::onEvent(arduino_usb_hid_keyboard_event_t event, esp_event_h
     arduino_usb_event_handler_register_with(ARDUINO_USB_HID_KEYBOARD_EVENTS, event, callback, this);
 }
 
-void USBHIDKeyboard::_onOutput(const uint8_t* buffer, uint16_t len){
+void USBHIDKeyboard::_onOutput(uint8_t report_id, const uint8_t* buffer, uint16_t len){
     //log_d("LEDS: 0x%02x", buffer[0]);
     arduino_usb_hid_keyboard_event_data_t p = {0};
     p.leds = buffer[0];
@@ -82,7 +80,7 @@ void USBHIDKeyboard::sendReport(KeyReport* keys)
     } else {
         memset(report.keycode, 0, 6);
     }
-    hid.SendReport(id, &report, sizeof(report));
+    hid.SendReport(HID_REPORT_ID_KEYBOARD, &report, sizeof(report));
 }
 
 #define SHIFT 0x80
diff --git a/libraries/USB/src/USBHIDKeyboard.h b/libraries/USB/src/USBHIDKeyboard.h
index 77833f4ccd2..5f9fdfc4696 100644
--- a/libraries/USB/src/USBHIDKeyboard.h
+++ b/libraries/USB/src/USBHIDKeyboard.h
@@ -132,8 +132,9 @@ class USBHIDKeyboard: public USBHIDDevice, public Print
     void onEvent(esp_event_handler_t callback);
     void onEvent(arduino_usb_hid_keyboard_event_t event, esp_event_handler_t callback);
 
-    //internal use
-    void _onOutput(const uint8_t* buffer, uint16_t len);
+    // internal use
+    uint16_t _onGetDescriptor(uint8_t* buffer);
+    void _onOutput(uint8_t report_id, const uint8_t* buffer, uint16_t len);
 };
 
 #endif
diff --git a/libraries/USB/src/USBHIDMouse.cpp b/libraries/USB/src/USBHIDMouse.cpp
index 2d05b741a22..2592ec24489 100644
--- a/libraries/USB/src/USBHIDMouse.cpp
+++ b/libraries/USB/src/USBHIDMouse.cpp
@@ -25,28 +25,26 @@
 
 #if CFG_TUD_HID
 
-static uint16_t tinyusb_hid_device_descriptor_cb(uint8_t * dst, uint8_t report_id){
-    uint8_t report_descriptor[] = {
-        TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(report_id))
-    };
-    memcpy(dst, report_descriptor, sizeof(report_descriptor));
-    return sizeof(report_descriptor);
-}
+static const uint8_t report_descriptor[] = {
+    TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(HID_REPORT_ID_MOUSE))
+};
 
 USBHIDMouse::USBHIDMouse(): hid(), _buttons(0){
 	static bool initialized = false;
 	if(!initialized){
 		initialized = true;
-		uint8_t report_descriptor[] = {
-		    TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(0))
-		};
-		hid.addDevice(this, sizeof(report_descriptor), tinyusb_hid_device_descriptor_cb);
+		hid.addDevice(this, sizeof(report_descriptor));
 	} else {
 		isr_log_e("Only one instance of USBHIDMouse is allowed!");
 		abort();
 	}
 }
 
+uint16_t USBHIDMouse::_onGetDescriptor(uint8_t* dst){
+    memcpy(dst, report_descriptor, sizeof(report_descriptor));
+    return sizeof(report_descriptor);
+}
+
 void USBHIDMouse::begin(){
     hid.begin();
 }
@@ -54,13 +52,6 @@ void USBHIDMouse::begin(){
 void USBHIDMouse::end(){
 }
 
-void USBHIDMouse::click(uint8_t b){
-    _buttons = b;
-    move(0,0);
-    _buttons = 0;
-    move(0,0);
-}
-
 void USBHIDMouse::move(int8_t x, int8_t y, int8_t wheel, int8_t pan){
     hid_mouse_report_t report = {
         .buttons = _buttons,
@@ -69,7 +60,14 @@ void USBHIDMouse::move(int8_t x, int8_t y, int8_t wheel, int8_t pan){
         .wheel   = wheel,
         .pan     = pan
     };
-    hid.SendReport(id, &report, sizeof(report));
+    hid.SendReport(HID_REPORT_ID_MOUSE, &report, sizeof(report));
+}
+
+void USBHIDMouse::click(uint8_t b){
+    _buttons = b;
+    move(0,0);
+    _buttons = 0;
+    move(0,0);
 }
 
 void USBHIDMouse::buttons(uint8_t b){
diff --git a/libraries/USB/src/USBHIDMouse.h b/libraries/USB/src/USBHIDMouse.h
index afa238f43f6..17adf17b471 100644
--- a/libraries/USB/src/USBHIDMouse.h
+++ b/libraries/USB/src/USBHIDMouse.h
@@ -46,6 +46,9 @@ class USBHIDMouse: public USBHIDDevice {
     void press(uint8_t b = MOUSE_LEFT);   // press LEFT by default
     void release(uint8_t b = MOUSE_LEFT); // release LEFT by default
     bool isPressed(uint8_t b = MOUSE_LEFT); // check LEFT by default
+
+    // internal use
+    uint16_t _onGetDescriptor(uint8_t* buffer);
 };
 
 #endif
diff --git a/libraries/USB/src/USBHIDSystemControl.cpp b/libraries/USB/src/USBHIDSystemControl.cpp
index 5c22861af8b..0a40f48c867 100644
--- a/libraries/USB/src/USBHIDSystemControl.cpp
+++ b/libraries/USB/src/USBHIDSystemControl.cpp
@@ -18,28 +18,26 @@
 
 #if CFG_TUD_HID
 
-static uint16_t tinyusb_hid_device_descriptor_cb(uint8_t * dst, uint8_t report_id){
-    uint8_t report_descriptor[] = {
-        TUD_HID_REPORT_DESC_SYSTEM_CONTROL(HID_REPORT_ID(report_id))
-    };
-    memcpy(dst, report_descriptor, sizeof(report_descriptor));
-    return sizeof(report_descriptor);
-}
+static const uint8_t report_descriptor[] = {
+    TUD_HID_REPORT_DESC_SYSTEM_CONTROL(HID_REPORT_ID(HID_REPORT_ID_SYSTEM_CONTROL))
+};
 
 USBHIDSystemControl::USBHIDSystemControl(): hid(){
 	static bool initialized = false;
 	if(!initialized){
 		initialized = true;
-		uint8_t report_descriptor[] = {
-		    TUD_HID_REPORT_DESC_SYSTEM_CONTROL(HID_REPORT_ID(0))
-		};
-		hid.addDevice(this, sizeof(report_descriptor), tinyusb_hid_device_descriptor_cb);
+		hid.addDevice(this, sizeof(report_descriptor));
 	} else {
 		isr_log_e("Only one instance of USBHIDSystemControl is allowed!");
 		abort();
 	}
 }
 
+uint16_t USBHIDSystemControl::_onGetDescriptor(uint8_t* dst){
+    memcpy(dst, report_descriptor, sizeof(report_descriptor));
+    return sizeof(report_descriptor);
+}
+
 void USBHIDSystemControl::begin(){
     hid.begin();
 }
@@ -48,7 +46,7 @@ void USBHIDSystemControl::end(){
 }
 
 bool USBHIDSystemControl::send(uint8_t value){
-    return hid.SendReport(id, &value, 1);
+    return hid.SendReport(HID_REPORT_ID_SYSTEM_CONTROL, &value, 1);
 }
 
 size_t USBHIDSystemControl::press(uint8_t k){
diff --git a/libraries/USB/src/USBHIDSystemControl.h b/libraries/USB/src/USBHIDSystemControl.h
index af83d22ba1c..05ba573f2b4 100644
--- a/libraries/USB/src/USBHIDSystemControl.h
+++ b/libraries/USB/src/USBHIDSystemControl.h
@@ -31,6 +31,9 @@ class USBHIDSystemControl: public USBHIDDevice {
     void end(void);
     size_t press(uint8_t k);
     size_t release();
+
+    // internal use
+    uint16_t _onGetDescriptor(uint8_t* buffer);
 };
 
 #endif
diff --git a/libraries/USB/src/USBHIDVendor.cpp b/libraries/USB/src/USBHIDVendor.cpp
index 9336b4d0bfb..f775c6a28e7 100644
--- a/libraries/USB/src/USBHIDVendor.cpp
+++ b/libraries/USB/src/USBHIDVendor.cpp
@@ -58,22 +58,15 @@ esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, i
 // max size is 64 and we need one byte for the report ID
 static const uint8_t HID_VENDOR_REPORT_SIZE = 63;
 
-static uint16_t tinyusb_hid_device_descriptor_cb(uint8_t * dst, uint8_t report_id){
-    uint8_t report_descriptor[] = {
-        TUD_HID_REPORT_DESC_GENERIC_INOUT_FEATURE(HID_VENDOR_REPORT_SIZE, HID_REPORT_ID(report_id))
-    };
-    memcpy(dst, report_descriptor, sizeof(report_descriptor));
-    return sizeof(report_descriptor);
-}
+static const uint8_t report_descriptor[] = {
+    TUD_HID_REPORT_DESC_GENERIC_INOUT_FEATURE(HID_VENDOR_REPORT_SIZE, HID_REPORT_ID(HID_REPORT_ID_VENDOR))
+};
 
 USBHIDVendor::USBHIDVendor(): hid(){
 	static bool initialized = false;
 	if(!initialized){
 		initialized = true;
-		uint8_t report_descriptor[] = {
-		    TUD_HID_REPORT_DESC_GENERIC_INOUT_FEATURE(HID_VENDOR_REPORT_SIZE, HID_REPORT_ID(1))
-		};
-		hid.addDevice(this, sizeof(report_descriptor), tinyusb_hid_device_descriptor_cb);
+		hid.addDevice(this, sizeof(report_descriptor));
         memset(feature, 0, HID_VENDOR_REPORT_SIZE);
 	} else {
 		isr_log_e("Only one instance of USBHIDVendor is allowed!");
@@ -81,6 +74,11 @@ USBHIDVendor::USBHIDVendor(): hid(){
 	}
 }
 
+uint16_t USBHIDVendor::_onGetDescriptor(uint8_t* dst){
+    memcpy(dst, report_descriptor, sizeof(report_descriptor));
+    return sizeof(report_descriptor);
+}
+
 size_t USBHIDVendor::setRxBufferSize(size_t rx_queue_len){
     if(rx_queue){
         if(!rx_queue_len){
@@ -113,7 +111,7 @@ void USBHIDVendor::onEvent(arduino_usb_hid_vendor_event_t event, esp_event_handl
     arduino_usb_event_handler_register_with(ARDUINO_USB_HID_VENDOR_EVENTS, event, callback, this);
 }
 
-uint16_t USBHIDVendor::_onGetFeature(uint8_t* buffer, uint16_t len){
+uint16_t USBHIDVendor::_onGetFeature(uint8_t report_id, uint8_t* buffer, uint16_t len){
     log_v("len: %u", len);
     memcpy(buffer, feature, len);
     arduino_usb_hid_vendor_event_data_t p = {0};
@@ -123,7 +121,7 @@ uint16_t USBHIDVendor::_onGetFeature(uint8_t* buffer, uint16_t len){
     return len;
 }
 
-void USBHIDVendor::_onSetFeature(const uint8_t* buffer, uint16_t len){
+void USBHIDVendor::_onSetFeature(uint8_t report_id, const uint8_t* buffer, uint16_t len){
     log_v("len: %u", len);
     memcpy(feature, buffer, len);
     arduino_usb_hid_vendor_event_data_t p = {0};
@@ -132,7 +130,7 @@ void USBHIDVendor::_onSetFeature(const uint8_t* buffer, uint16_t len){
     arduino_usb_event_post(ARDUINO_USB_HID_VENDOR_EVENTS, ARDUINO_USB_HID_VENDOR_SET_FEATURE_EVENT, &p, sizeof(arduino_usb_hid_vendor_event_data_t), portMAX_DELAY);
 }
 
-void USBHIDVendor::_onOutput(const uint8_t* buffer, uint16_t len){
+void USBHIDVendor::_onOutput(uint8_t report_id, const uint8_t* buffer, uint16_t len){
     log_v("len: %u", len);
     for(uint32_t i=0; i<len; i++){
         if(rx_queue == NULL || !xQueueSend(rx_queue, buffer+i, 0)){
@@ -157,11 +155,11 @@ size_t USBHIDVendor::write(const uint8_t* buffer, uint16_t len){
         }
         // On Mac, I can get INPUT only when data length equals the input report size
         // To be tested on other platforms
-        //uint8_t hid_in[HID_VENDOR_REPORT_SIZE];
-        //memcpy(hid_in, data, will_send);
-        //memset(hid_in + will_send, 0, HID_VENDOR_REPORT_SIZE - will_send);
-        //if(!hid.SendReport(id, hid_in, HID_VENDOR_REPORT_SIZE)){
-        if(!hid.SendReport(id, buffer + (len - to_send), will_send)){
+        uint8_t hid_in[HID_VENDOR_REPORT_SIZE];
+        memcpy(hid_in, data, will_send);
+        memset(hid_in + will_send, 0, HID_VENDOR_REPORT_SIZE - will_send);
+        if(!hid.SendReport(HID_REPORT_ID_VENDOR, hid_in, HID_VENDOR_REPORT_SIZE)){
+        //if(!hid.SendReport(HID_REPORT_ID_VENDOR, buffer + (len - to_send), will_send)){
             return len - to_send;
         }
         to_send -= will_send;
diff --git a/libraries/USB/src/USBHIDVendor.h b/libraries/USB/src/USBHIDVendor.h
index 1f88c25ef01..0cabe16c2c0 100644
--- a/libraries/USB/src/USBHIDVendor.h
+++ b/libraries/USB/src/USBHIDVendor.h
@@ -53,10 +53,11 @@ class USBHIDVendor: public USBHIDDevice, public Stream {
     void onEvent(esp_event_handler_t callback);
     void onEvent(arduino_usb_hid_vendor_event_t event, esp_event_handler_t callback);
 
-    //internal use
-    uint16_t _onGetFeature(uint8_t* buffer, uint16_t len);
-    void _onSetFeature(const uint8_t* buffer, uint16_t len);
-    void _onOutput(const uint8_t* buffer, uint16_t len);
+    // internal use
+    uint16_t _onGetDescriptor(uint8_t* buffer);
+    uint16_t _onGetFeature(uint8_t report_id, uint8_t* buffer, uint16_t len);
+    void _onSetFeature(uint8_t report_id, const uint8_t* buffer, uint16_t len);
+    void _onOutput(uint8_t report_id, const uint8_t* buffer, uint16_t len);
 };
 
 #endif

From d1223ddef3a0b1f683d53018a642b263f0d6cc60 Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Tue, 17 Aug 2021 15:47:49 +0300
Subject: [PATCH 05/17] Allow more than one instance of the same device

---
 libraries/USB/src/USBHIDConsumerControl.cpp |  3 ---
 libraries/USB/src/USBHIDGamepad.cpp         |  3 ---
 libraries/USB/src/USBHIDKeyboard.cpp        | 12 +++++-------
 libraries/USB/src/USBHIDMouse.cpp           |  3 ---
 libraries/USB/src/USBHIDSystemControl.cpp   |  3 ---
 libraries/USB/src/USBHIDVendor.cpp          | 18 ++++++++++++------
 libraries/USB/src/USBHIDVendor.h            |  2 --
 7 files changed, 17 insertions(+), 27 deletions(-)

diff --git a/libraries/USB/src/USBHIDConsumerControl.cpp b/libraries/USB/src/USBHIDConsumerControl.cpp
index 33c867e9cf1..35dee39beb7 100644
--- a/libraries/USB/src/USBHIDConsumerControl.cpp
+++ b/libraries/USB/src/USBHIDConsumerControl.cpp
@@ -27,9 +27,6 @@ USBHIDConsumerControl::USBHIDConsumerControl(): hid(){
 	if(!initialized){
 		initialized = true;
 		hid.addDevice(this, sizeof(report_descriptor));
-	} else {
-		isr_log_e("Only one instance of USBHIDConsumerControl is allowed!");
-		abort();
 	}
 }
 
diff --git a/libraries/USB/src/USBHIDGamepad.cpp b/libraries/USB/src/USBHIDGamepad.cpp
index b77aea3acd6..37e65e79ffc 100644
--- a/libraries/USB/src/USBHIDGamepad.cpp
+++ b/libraries/USB/src/USBHIDGamepad.cpp
@@ -27,9 +27,6 @@ USBHIDGamepad::USBHIDGamepad(): hid(), _x(0), _y(0), _z(0), _rz(0), _rx(0), _ry(
 	if(!initialized){
 		initialized = true;
 		hid.addDevice(this, sizeof(report_descriptor));
-	} else {
-		isr_log_e("Only one instance of USBHIDGamepad is allowed!");
-		abort();
 	}
 }
 
diff --git a/libraries/USB/src/USBHIDKeyboard.cpp b/libraries/USB/src/USBHIDKeyboard.cpp
index ec3444ba6c6..15914ceed3c 100644
--- a/libraries/USB/src/USBHIDKeyboard.cpp
+++ b/libraries/USB/src/USBHIDKeyboard.cpp
@@ -38,9 +38,6 @@ USBHIDKeyboard::USBHIDKeyboard(): hid(){
     if(!initialized){
         initialized = true;
         hid.addDevice(this, sizeof(report_descriptor));
-    } else {
-        isr_log_e("Only one instance of USBHIDKeyboard is allowed!");
-        abort();
     }
 }
 
@@ -64,10 +61,11 @@ void USBHIDKeyboard::onEvent(arduino_usb_hid_keyboard_event_t event, esp_event_h
 }
 
 void USBHIDKeyboard::_onOutput(uint8_t report_id, const uint8_t* buffer, uint16_t len){
-    //log_d("LEDS: 0x%02x", buffer[0]);
-    arduino_usb_hid_keyboard_event_data_t p = {0};
-    p.leds = buffer[0];
-    arduino_usb_event_post(ARDUINO_USB_HID_KEYBOARD_EVENTS, ARDUINO_USB_HID_KEYBOARD_LED_EVENT, &p, sizeof(arduino_usb_hid_keyboard_event_data_t), portMAX_DELAY);
+    if(report_id == HID_REPORT_ID_KEYBOARD){
+        arduino_usb_hid_keyboard_event_data_t p = {0};
+        p.leds = buffer[0];
+        arduino_usb_event_post(ARDUINO_USB_HID_KEYBOARD_EVENTS, ARDUINO_USB_HID_KEYBOARD_LED_EVENT, &p, sizeof(arduino_usb_hid_keyboard_event_data_t), portMAX_DELAY);
+    }
 }
 
 void USBHIDKeyboard::sendReport(KeyReport* keys)
diff --git a/libraries/USB/src/USBHIDMouse.cpp b/libraries/USB/src/USBHIDMouse.cpp
index 2592ec24489..dbc84c97505 100644
--- a/libraries/USB/src/USBHIDMouse.cpp
+++ b/libraries/USB/src/USBHIDMouse.cpp
@@ -34,9 +34,6 @@ USBHIDMouse::USBHIDMouse(): hid(), _buttons(0){
 	if(!initialized){
 		initialized = true;
 		hid.addDevice(this, sizeof(report_descriptor));
-	} else {
-		isr_log_e("Only one instance of USBHIDMouse is allowed!");
-		abort();
 	}
 }
 
diff --git a/libraries/USB/src/USBHIDSystemControl.cpp b/libraries/USB/src/USBHIDSystemControl.cpp
index 0a40f48c867..d1dafc30906 100644
--- a/libraries/USB/src/USBHIDSystemControl.cpp
+++ b/libraries/USB/src/USBHIDSystemControl.cpp
@@ -27,9 +27,6 @@ USBHIDSystemControl::USBHIDSystemControl(): hid(){
 	if(!initialized){
 		initialized = true;
 		hid.addDevice(this, sizeof(report_descriptor));
-	} else {
-		isr_log_e("Only one instance of USBHIDSystemControl is allowed!");
-		abort();
 	}
 }
 
diff --git a/libraries/USB/src/USBHIDVendor.cpp b/libraries/USB/src/USBHIDVendor.cpp
index f775c6a28e7..39e35fcd363 100644
--- a/libraries/USB/src/USBHIDVendor.cpp
+++ b/libraries/USB/src/USBHIDVendor.cpp
@@ -58,6 +58,9 @@ esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, i
 // max size is 64 and we need one byte for the report ID
 static const uint8_t HID_VENDOR_REPORT_SIZE = 63;
 
+static uint8_t feature[HID_VENDOR_REPORT_SIZE];
+static xQueueHandle rx_queue = NULL;
+
 static const uint8_t report_descriptor[] = {
     TUD_HID_REPORT_DESC_GENERIC_INOUT_FEATURE(HID_VENDOR_REPORT_SIZE, HID_REPORT_ID(HID_REPORT_ID_VENDOR))
 };
@@ -68,9 +71,6 @@ USBHIDVendor::USBHIDVendor(): hid(){
 		initialized = true;
 		hid.addDevice(this, sizeof(report_descriptor));
         memset(feature, 0, HID_VENDOR_REPORT_SIZE);
-	} else {
-		isr_log_e("Only one instance of USBHIDVendor is allowed!");
-		abort();
 	}
 }
 
@@ -112,7 +112,9 @@ void USBHIDVendor::onEvent(arduino_usb_hid_vendor_event_t event, esp_event_handl
 }
 
 uint16_t USBHIDVendor::_onGetFeature(uint8_t report_id, uint8_t* buffer, uint16_t len){
-    log_v("len: %u", len);
+    if(report_id != HID_REPORT_ID_VENDOR){
+        return 0;
+    }
     memcpy(buffer, feature, len);
     arduino_usb_hid_vendor_event_data_t p = {0};
     p.buffer = feature;
@@ -122,7 +124,9 @@ uint16_t USBHIDVendor::_onGetFeature(uint8_t report_id, uint8_t* buffer, uint16_
 }
 
 void USBHIDVendor::_onSetFeature(uint8_t report_id, const uint8_t* buffer, uint16_t len){
-    log_v("len: %u", len);
+    if(report_id != HID_REPORT_ID_VENDOR){
+        return;
+    }
     memcpy(feature, buffer, len);
     arduino_usb_hid_vendor_event_data_t p = {0};
     p.buffer = feature;
@@ -131,7 +135,9 @@ void USBHIDVendor::_onSetFeature(uint8_t report_id, const uint8_t* buffer, uint1
 }
 
 void USBHIDVendor::_onOutput(uint8_t report_id, const uint8_t* buffer, uint16_t len){
-    log_v("len: %u", len);
+    if(report_id != HID_REPORT_ID_VENDOR){
+        return;
+    }
     for(uint32_t i=0; i<len; i++){
         if(rx_queue == NULL || !xQueueSend(rx_queue, buffer+i, 0)){
             len = i+1;
diff --git a/libraries/USB/src/USBHIDVendor.h b/libraries/USB/src/USBHIDVendor.h
index 0cabe16c2c0..26362d5eeea 100644
--- a/libraries/USB/src/USBHIDVendor.h
+++ b/libraries/USB/src/USBHIDVendor.h
@@ -35,8 +35,6 @@ typedef struct {
 class USBHIDVendor: public USBHIDDevice, public Stream {
 private:
     USBHID hid;
-    uint8_t feature[63];
-    xQueueHandle rx_queue;
 public:
     USBHIDVendor(void);
     void begin(void);

From 34c1d070f2a3fb637dfaa267c7dd33357a3fc784 Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Tue, 17 Aug 2021 16:59:05 +0300
Subject: [PATCH 06/17] Update USBHID.cpp

---
 libraries/USB/src/USBHID.cpp | 26 --------------------------
 1 file changed, 26 deletions(-)

diff --git a/libraries/USB/src/USBHID.cpp b/libraries/USB/src/USBHID.cpp
index de241c293dd..1a897a4d0a4 100644
--- a/libraries/USB/src/USBHID.cpp
+++ b/libraries/USB/src/USBHID.cpp
@@ -23,30 +23,6 @@ ESP_EVENT_DEFINE_BASE(ARDUINO_USB_HID_EVENTS);
 esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait);
 esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg);
 
-static void log_print_buf_line(const uint8_t *b, size_t len){
-    for(size_t i = 0; i<len; i++){
-        log_printf("%s0x%02x,",i?" ":"", b[i]);
-    }
-    for(size_t i = len; i<16; i++){
-        log_printf("      ");
-    }
-    log_printf("    // ");
-    for(size_t i = 0; i<len; i++){
-        log_printf("%c",((b[i] >= 0x20) && (b[i] < 0x80))?b[i]:'.');
-    }
-    log_printf("\n");
-}
-
-void log_print_buf(const uint8_t *b, size_t len){
-    if(!len || !b){
-        return;
-    }
-    for(size_t i = 0; i<len; i+=16){
-        log_printf("/* 0x%04X */ ", i);
-        log_print_buf_line(b+i, ((len-i)<16)?(len - i):16);
-    }
-}
-
 typedef struct {
     USBHIDDevice * device;
     uint8_t reports_num;
@@ -266,12 +242,10 @@ void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_
         report_id = buffer[0];
         if(!tinyusb_on_set_output(report_id, buffer+1, bufsize-1)){
             log_d("instance: %u, report_id: %u, report_type: %s, bufsize: %u", instance, *buffer, tinyusb_hid_device_report_types[HID_REPORT_TYPE_OUTPUT], bufsize-1);
-            //log_print_buf(buffer+1, bufsize-1);
         }
     } else {
         if(!tinyusb_on_set_feature(report_id, buffer, bufsize)){
             log_d("instance: %u, report_id: %u, report_type: %s, bufsize: %u", instance, report_id, tinyusb_hid_device_report_types[report_type], bufsize);
-            //log_print_buf(buffer, bufsize);
         }
     }
 }

From 8ed0d12d44caa2df72ba5f6112d89d9318c79afe Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Tue, 17 Aug 2021 17:18:59 +0300
Subject: [PATCH 07/17] Add bool HID::ready()

---
 .../USB/examples/CompositeDevice/CompositeDevice.ino |  6 ++++--
 libraries/USB/src/USBHID.cpp                         | 12 ++++++++----
 libraries/USB/src/USBHID.h                           |  1 +
 3 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/libraries/USB/examples/CompositeDevice/CompositeDevice.ino b/libraries/USB/examples/CompositeDevice/CompositeDevice.ino
index 1f9b9159d2e..af811420590 100644
--- a/libraries/USB/examples/CompositeDevice/CompositeDevice.ino
+++ b/libraries/USB/examples/CompositeDevice/CompositeDevice.ino
@@ -178,7 +178,7 @@ void setup() {
 
 void loop() {
   int buttonState = digitalRead(buttonPin);
-  if (buttonState != previousButtonState) {
+  if (HID.ready() && buttonState != previousButtonState) {
     previousButtonState = buttonState;
     if (buttonState == LOW) {
       HWSerial.println("Button Pressed");
@@ -206,6 +206,8 @@ void loop() {
     uint8_t b[l];
     l = HWSerial.read(b, l);
     USBSerial.write(b, l);
-    Vendor.write(b,l);
+    if(HID.ready()){
+      Vendor.write(b,l);
+    }
   }
 }
diff --git a/libraries/USB/src/USBHID.cpp b/libraries/USB/src/USBHID.cpp
index 1a897a4d0a4..d24ae310332 100644
--- a/libraries/USB/src/USBHID.cpp
+++ b/libraries/USB/src/USBHID.cpp
@@ -173,9 +173,6 @@ extern "C" uint16_t tusb_hid_load_descriptor(uint8_t * dst, uint8_t * itf)
         return 0;
     }
     tinyusb_hid_is_initialized = true;
-    if(!tinyusb_load_enabled_hid_devices()){
-        return 0;
-    }
 
     uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB HID");
     uint8_t ep_in = tinyusb_get_free_in_endpoint();
@@ -199,6 +196,9 @@ extern "C" uint16_t tusb_hid_load_descriptor(uint8_t * dst, uint8_t * itf)
 // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
 uint8_t const * tud_hid_descriptor_report_cb(uint8_t instance){
     log_v("instance: %u", instance);
+    if(!tinyusb_load_enabled_hid_devices()){
+        return NULL;
+    }
     return tinyusb_hid_device_descriptor;
 }
 
@@ -281,6 +281,10 @@ void USBHID::end(){
     }
 }
 
+bool USBHID::ready(void){
+    return tud_hid_n_ready(0);
+}
+
 void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint8_t len){
     if (tinyusb_hid_device_input_sem) {
         xSemaphoreGive(tinyusb_hid_device_input_sem);
@@ -298,7 +302,7 @@ bool USBHID::SendReport(uint8_t id, const void* data, size_t len, uint32_t timeo
         return false;
     }
 
-    bool res = tud_hid_n_ready(0);
+    bool res = ready();
     if(!res){
         log_e("not ready");
     } else {
diff --git a/libraries/USB/src/USBHID.h b/libraries/USB/src/USBHID.h
index 426cfd9b28d..9c62d018954 100644
--- a/libraries/USB/src/USBHID.h
+++ b/libraries/USB/src/USBHID.h
@@ -69,6 +69,7 @@ class USBHID
     USBHID(void);
     void begin(void);
     void end(void);
+    bool ready(void);
     bool SendReport(uint8_t report_id, const void* data, size_t len, uint32_t timeout_ms = 100);
     void onEvent(esp_event_handler_t callback);
     void onEvent(arduino_usb_hid_event_t event, esp_event_handler_t callback);

From c919886bd3147bf47a9f3b24528e16fa40fa8f1d Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Tue, 17 Aug 2021 18:20:35 +0300
Subject: [PATCH 08/17] Add custom HID device example

---
 .../USB/examples/CustomHIDDevice/.skip.esp32  |  0
 .../examples/CustomHIDDevice/.skip.esp32c3    |  0
 .../CustomHIDDevice/CustomHIDDevice.ino       | 79 +++++++++++++++++++
 3 files changed, 79 insertions(+)
 create mode 100644 libraries/USB/examples/CustomHIDDevice/.skip.esp32
 create mode 100644 libraries/USB/examples/CustomHIDDevice/.skip.esp32c3
 create mode 100644 libraries/USB/examples/CustomHIDDevice/CustomHIDDevice.ino

diff --git a/libraries/USB/examples/CustomHIDDevice/.skip.esp32 b/libraries/USB/examples/CustomHIDDevice/.skip.esp32
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/USB/examples/CustomHIDDevice/.skip.esp32c3 b/libraries/USB/examples/CustomHIDDevice/.skip.esp32c3
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/USB/examples/CustomHIDDevice/CustomHIDDevice.ino b/libraries/USB/examples/CustomHIDDevice/CustomHIDDevice.ino
new file mode 100644
index 00000000000..15d69b6a63b
--- /dev/null
+++ b/libraries/USB/examples/CustomHIDDevice/CustomHIDDevice.ino
@@ -0,0 +1,79 @@
+#include "USB.h"
+#include "USBHID.h"
+USBHID HID;
+
+static const uint8_t report_descriptor[] = { // 8 axis
+    0x05, 0x01,        // Usage Page (Generic Desktop Ctrls)
+    0x09, 0x04,        // Usage (Joystick)
+    0xa1, 0x01,        // Collection (Application)
+    0xa1, 0x00,        //   Collection (Physical)
+    0x05, 0x01,        //   Usage Page (Generic Desktop Ctrls)
+    0x09, 0x30,        //     Usage (X)
+    0x09, 0x31,        //     Usage (Y)
+    0x09, 0x32,        //     Usage (Z)
+    0x09, 0x33,        //     Usage (Rx)
+    0x09, 0x34,        //     Usage (Ry)
+    0x09, 0x35,        //     Usage (Rz)
+    0x09, 0x36,        //     Usage (Slider)
+    0x09, 0x36,        //     Usage (Slider)
+    0x15, 0x81,        //     Logical Minimum (-127)
+    0x25, 0x7f,        //     Logical Maximum (127)
+    0x75, 0x08,        //     Report Size (8)
+    0x95, 0x08,        //     Report Count (8)
+    0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+    0xC0,              //   End Collection
+    0xC0,              // End Collection
+};
+
+class CustomHIDDevice: public USBHIDDevice {
+public:
+  CustomHIDDevice(void){
+    static bool initialized = false;
+    if(!initialized){
+      initialized = true;
+      HID.addDevice(this, sizeof(report_descriptor));
+    }
+  }
+  
+  void begin(void){
+    HID.begin();
+  }
+    
+  uint16_t _onGetDescriptor(uint8_t* buffer){
+    memcpy(buffer, report_descriptor, sizeof(report_descriptor));
+    return sizeof(report_descriptor);
+  }
+
+  bool send(uint8_t * value){
+    return HID.SendReport(0, value, 8);
+  }
+};
+
+CustomHIDDevice Device;
+
+const int buttonPin = 0;
+int previousButtonState = HIGH;
+uint8_t axis[8];
+
+void setup() {
+  Serial.begin(115200);
+  Serial.setDebugOutput(true);
+  pinMode(buttonPin, INPUT_PULLUP);
+  Device.begin();
+  USB.begin();
+}
+
+void loop() {
+  int buttonState = digitalRead(buttonPin);
+  if (HID.ready() && buttonState != previousButtonState) {
+    previousButtonState = buttonState;
+    if (buttonState == LOW) {
+      Serial.println("Button Pressed");
+      axis[0] = random() & 0xFF;
+      Device.send(axis);
+    } else {
+      Serial.println("Button Released");
+    }
+    delay(100);
+  }
+}

From 8559d8b0cb393ee60da13b5c7ddb63364dcc5d09 Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Wed, 18 Aug 2021 03:21:32 +0300
Subject: [PATCH 09/17] Make Vendor class more flexible

---
 libraries/USB/src/USBHIDVendor.cpp | 46 ++++++++++++++++++------------
 libraries/USB/src/USBHIDVendor.h   | 10 +++++--
 2 files changed, 36 insertions(+), 20 deletions(-)

diff --git a/libraries/USB/src/USBHIDVendor.cpp b/libraries/USB/src/USBHIDVendor.cpp
index 39e35fcd363..96e0d6e1661 100644
--- a/libraries/USB/src/USBHIDVendor.cpp
+++ b/libraries/USB/src/USBHIDVendor.cpp
@@ -56,29 +56,36 @@ esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, i
 #define TUD_HID_REPORT_DESC_GENERIC_INOUT_FEATURE_LEN 46
 
 // max size is 64 and we need one byte for the report ID
-static const uint8_t HID_VENDOR_REPORT_SIZE = 63;
-
-static uint8_t feature[HID_VENDOR_REPORT_SIZE];
+static uint8_t HID_VENDOR_REPORT_SIZE = 63;
+static uint8_t feature[64];
 static xQueueHandle rx_queue = NULL;
+static bool prepend_size = false;
 
-static const uint8_t report_descriptor[] = {
-    TUD_HID_REPORT_DESC_GENERIC_INOUT_FEATURE(HID_VENDOR_REPORT_SIZE, HID_REPORT_ID(HID_REPORT_ID_VENDOR))
-};
-
-USBHIDVendor::USBHIDVendor(): hid(){
+USBHIDVendor::USBHIDVendor(uint8_t report_size, bool prepend): hid(){
 	static bool initialized = false;
 	if(!initialized){
 		initialized = true;
-		hid.addDevice(this, sizeof(report_descriptor));
-        memset(feature, 0, HID_VENDOR_REPORT_SIZE);
+		hid.addDevice(this, TUD_HID_REPORT_DESC_GENERIC_INOUT_FEATURE_LEN);
+        memset(feature, 0, 64);
+        if(report_size < 64){
+            HID_VENDOR_REPORT_SIZE = report_size;
+        }
+        prepend_size = prepend;
 	}
 }
 
 uint16_t USBHIDVendor::_onGetDescriptor(uint8_t* dst){
+    uint8_t report_descriptor[] = {
+        TUD_HID_REPORT_DESC_GENERIC_INOUT_FEATURE(HID_VENDOR_REPORT_SIZE, HID_REPORT_ID(HID_REPORT_ID_VENDOR))
+    };
     memcpy(dst, report_descriptor, sizeof(report_descriptor));
     return sizeof(report_descriptor);
 }
 
+void USBHIDVendor::prependInputPacketsWithSize(bool enable){
+    prepend_size = enable;
+}
+
 size_t USBHIDVendor::setRxBufferSize(size_t rx_queue_len){
     if(rx_queue){
         if(!rx_queue_len){
@@ -151,21 +158,24 @@ void USBHIDVendor::_onOutput(uint8_t report_id, const uint8_t* buffer, uint16_t
     arduino_usb_event_post(ARDUINO_USB_HID_VENDOR_EVENTS, ARDUINO_USB_HID_VENDOR_OUTPUT_EVENT, &p, sizeof(arduino_usb_hid_vendor_event_data_t), portMAX_DELAY);
 }
 
-size_t USBHIDVendor::write(const uint8_t* buffer, uint16_t len){
+size_t USBHIDVendor::write(const uint8_t* buffer, size_t len){
+    uint8_t hid_in[HID_VENDOR_REPORT_SIZE];
     const uint8_t * data = (const uint8_t *)buffer;
-    size_t to_send = len, max_send=HID_VENDOR_REPORT_SIZE, will_send=0;
+    uint8_t size_offset = prepend_size?1:0;
+    size_t to_send = len, max_send=HID_VENDOR_REPORT_SIZE - size_offset, will_send=0;
     while(to_send){
         will_send = to_send;
         if(will_send > max_send){
             will_send = max_send;
         }
-        // On Mac, I can get INPUT only when data length equals the input report size
-        // To be tested on other platforms
-        uint8_t hid_in[HID_VENDOR_REPORT_SIZE];
-        memcpy(hid_in, data, will_send);
-        memset(hid_in + will_send, 0, HID_VENDOR_REPORT_SIZE - will_send);
+        if(prepend_size){
+            hid_in[0] = will_send;
+        }
+        // We can get INPUT only when data length equals the input report size
+        memcpy(hid_in + size_offset, data, will_send);
+        // pad with zeroes
+        memset(hid_in + size_offset + will_send, 0, max_send - will_send);
         if(!hid.SendReport(HID_REPORT_ID_VENDOR, hid_in, HID_VENDOR_REPORT_SIZE)){
-        //if(!hid.SendReport(HID_REPORT_ID_VENDOR, buffer + (len - to_send), will_send)){
             return len - to_send;
         }
         to_send -= will_send;
diff --git a/libraries/USB/src/USBHIDVendor.h b/libraries/USB/src/USBHIDVendor.h
index 26362d5eeea..fe11c0b5e11 100644
--- a/libraries/USB/src/USBHIDVendor.h
+++ b/libraries/USB/src/USBHIDVendor.h
@@ -36,11 +36,17 @@ class USBHIDVendor: public USBHIDDevice, public Stream {
 private:
     USBHID hid;
 public:
-    USBHIDVendor(void);
+    // Max report size is 64, but we need one byte for report ID, so in reality max is 63.
+    // Because input packets are always with length equal to the report size
+    // it will not be known how many bytes actually matter. Setting 'prepend_size' to 'true' will
+    // make the first byte of each packet to be the length of data in that packet.
+    // This comes with penalty of one byte, but is very useful when using Vendor for streaming
+    USBHIDVendor(uint8_t report_size=63, bool prepend_size=false);
     void begin(void);
     void end(void);
+    void prependInputPacketsWithSize(bool enable);
     size_t setRxBufferSize(size_t);
-    size_t write(const uint8_t* buffer, uint16_t len);
+    size_t write(const uint8_t* buffer, size_t len);
     size_t write(uint8_t);
     int available(void);
     int peek(void);

From b70fc32e87f966a1bb564f3c06bd8f7ccfaaf92c Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Wed, 18 Aug 2021 14:09:09 +0300
Subject: [PATCH 10/17] Handle better devices that use report ID 0

---
 libraries/USB/src/USBHID.cpp | 43 +++++++++++++++++++++++++++---------
 1 file changed, 32 insertions(+), 11 deletions(-)

diff --git a/libraries/USB/src/USBHID.cpp b/libraries/USB/src/USBHID.cpp
index d24ae310332..d29993021d8 100644
--- a/libraries/USB/src/USBHID.cpp
+++ b/libraries/USB/src/USBHID.cpp
@@ -120,7 +120,10 @@ static uint16_t tinyusb_on_add_descriptor(uint8_t device_index, uint8_t * dst){
                     if(hid_report_map->reports[i].protocol_mode == ESP_HID_PROTOCOL_MODE_REPORT){
                         report_id = hid_report_map->reports[i].report_id;
                         for(uint8_t r=0; r<device->reports_num; r++){
-                            if(report_id == device->report_ids[r]){
+                            if(!report_id){
+                                //todo: handle better when device has no report ID set
+                                break;
+                            } else if(report_id == device->report_ids[r]){
                                 //already added
                                 reports_num--;
                                 break;
@@ -156,12 +159,31 @@ static bool tinyusb_load_enabled_hid_devices(){
     uint8_t * dst = tinyusb_hid_device_descriptor;
 
     for(uint8_t i=0; i<tinyusb_loaded_hid_devices_num; i++){
-            uint16_t len = tinyusb_on_add_descriptor(i, dst);
-            if (!len) {
-                break;
-            } else {
-                dst += len;
+        uint16_t len = tinyusb_on_add_descriptor(i, dst);
+        if (!len) {
+            break;
+        } else {
+            dst += len;
+        }
+    }
+
+    esp_hid_report_map_t *hid_report_map = esp_hid_parse_report_map(tinyusb_hid_device_descriptor, tinyusb_hid_device_descriptor_len);
+    if(hid_report_map){
+        log_d("Loaded HID Desriptor with the following reports:");
+        for(uint8_t i=0; i<hid_report_map->reports_len; i++){
+            if(hid_report_map->reports[i].protocol_mode == ESP_HID_PROTOCOL_MODE_REPORT){
+                log_d("  ID: %3u, Type: %7s, Size: %2u, Usage: %8s",
+                    hid_report_map->reports[i].report_id,
+                    esp_hid_report_type_str(hid_report_map->reports[i].report_type),
+                    hid_report_map->reports[i].value_len,
+                    esp_hid_usage_str(hid_report_map->reports[i].usage)
+                );
             }
+        }
+        esp_hid_free_report_map(hid_report_map);
+    } else {
+        log_e("Failed to parse the hid report descriptor!");
+        return false;
     }
 
     return true;
@@ -205,7 +227,7 @@ uint8_t const * tud_hid_descriptor_report_cb(uint8_t instance){
 // Invoked when received SET_PROTOCOL request
 // protocol is either HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1)
 void tud_hid_set_protocol_cb(uint8_t instance, uint8_t protocol){
-    log_d("instance: %u, protocol:%u", instance, protocol);
+    log_v("instance: %u, protocol:%u", instance, protocol);
     arduino_usb_hid_event_data_t p = {0};
     p.instance = instance;
     p.set_protocol.protocol = protocol;
@@ -216,7 +238,7 @@ void tud_hid_set_protocol_cb(uint8_t instance, uint8_t protocol){
 // - Idle Rate = 0 : only send report if there is changes, i.e skip duplication
 // - Idle Rate > 0 : skip duplication, but send at least 1 report every idle rate (in unit of 4 ms).
 bool tud_hid_set_idle_cb(uint8_t instance, uint8_t idle_rate){
-    log_d("instance: %u, idle_rate:%u", instance, idle_rate);
+    log_v("instance: %u, idle_rate:%u", instance, idle_rate);
     arduino_usb_hid_event_data_t p = {0};
     p.instance = instance;
     p.set_idle.idle_rate = idle_rate;
@@ -239,9 +261,8 @@ uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_t
 // received data on OUT endpoint ( Report ID = 0, Type = 0 )
 void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize){
     if(!report_id && !report_type){
-        report_id = buffer[0];
-        if(!tinyusb_on_set_output(report_id, buffer+1, bufsize-1)){
-            log_d("instance: %u, report_id: %u, report_type: %s, bufsize: %u", instance, *buffer, tinyusb_hid_device_report_types[HID_REPORT_TYPE_OUTPUT], bufsize-1);
+        if(!tinyusb_on_set_output(0, buffer, bufsize) && !tinyusb_on_set_output(buffer[0], buffer+1, bufsize-1)){
+            log_d("instance: %u, report_id: %u, report_type: %s, bufsize: %u", instance, buffer[0], tinyusb_hid_device_report_types[HID_REPORT_TYPE_OUTPUT], bufsize-1);
         }
     } else {
         if(!tinyusb_on_set_feature(report_id, buffer, bufsize)){

From 196ce947789c1d8f3a86047110049ab0edbef5ec Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Thu, 19 Aug 2021 13:21:33 +0300
Subject: [PATCH 11/17] Refactor includes and config keys for USB classes

---
 cores/esp32/FirmwareMSC.cpp                 |  6 +++---
 cores/esp32/FirmwareMSC.h                   |  1 +
 cores/esp32/USB.cpp                         |  7 +++++--
 cores/esp32/USB.h                           |  8 +++-----
 cores/esp32/USBCDC.cpp                      |  9 +++------
 cores/esp32/USBMSC.cpp                      |  8 ++++----
 cores/esp32/USBMSC.h                        |  6 ++++--
 libraries/USB/src/USBHID.cpp                | 10 ++++++----
 libraries/USB/src/USBHID.h                  | 10 +++++-----
 libraries/USB/src/USBHIDConsumerControl.cpp |  9 ++++-----
 libraries/USB/src/USBHIDGamepad.cpp         |  9 ++++-----
 libraries/USB/src/USBHIDKeyboard.cpp        |  9 ++++-----
 libraries/USB/src/USBHIDMouse.cpp           |  9 ++++-----
 libraries/USB/src/USBHIDSystemControl.cpp   |  9 ++++-----
 libraries/USB/src/USBHIDVendor.cpp          | 11 ++++++-----
 15 files changed, 60 insertions(+), 61 deletions(-)

diff --git a/cores/esp32/FirmwareMSC.cpp b/cores/esp32/FirmwareMSC.cpp
index c399a013b25..473e220bd83 100644
--- a/cores/esp32/FirmwareMSC.cpp
+++ b/cores/esp32/FirmwareMSC.cpp
@@ -11,17 +11,17 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
+#include "FirmwareMSC.h"
+
+#if CONFIG_TINYUSB_MSC_ENABLED
 
 #include <cstring>
-#include "FirmwareMSC.h"
 #include "esp_partition.h"
 #include "esp_ota_ops.h"
 #include "esp32-hal.h"
 #include "pins_arduino.h"
 #include "firmware_msc_fat.h"
 
-#if CONFIG_TINYUSB_MSC_ENABLED
-
 #ifndef USB_FW_MSC_VENDOR_ID
 #define USB_FW_MSC_VENDOR_ID "ESP32" //max 8 chars
 #endif
diff --git a/cores/esp32/FirmwareMSC.h b/cores/esp32/FirmwareMSC.h
index 3caaf6a0e64..570feac8e2f 100644
--- a/cores/esp32/FirmwareMSC.h
+++ b/cores/esp32/FirmwareMSC.h
@@ -15,6 +15,7 @@
 #pragma once
 #include <stdbool.h>
 #include "USBMSC.h"
+
 #if CONFIG_TINYUSB_MSC_ENABLED
 
 #include "esp_event.h"
diff --git a/cores/esp32/USB.cpp b/cores/esp32/USB.cpp
index 1221707cb0a..28daa8001a1 100644
--- a/cores/esp32/USB.cpp
+++ b/cores/esp32/USB.cpp
@@ -11,11 +11,14 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-#include "esp32-hal.h"
-#include "esp32-hal-tinyusb.h"
 #include "USB.h"
+
 #if CONFIG_TINYUSB_ENABLED
 
+#include "esp32-hal.h"
+#include "esp32-hal-tinyusb.h"
+#include "common/tusb_common.h"
+
 #ifndef USB_VID
 #define USB_VID USB_ESPRESSIF_VID
 #endif
diff --git a/cores/esp32/USB.h b/cores/esp32/USB.h
index d64962c7fc7..33f3a2c1dfc 100644
--- a/cores/esp32/USB.h
+++ b/cores/esp32/USB.h
@@ -13,13 +13,11 @@
 // limitations under the License.
 #pragma once
 
-#include "sdkconfig.h"
-#if CONFIG_TINYUSB_ENABLED
-
 #include "Arduino.h"
-#include "USBCDC.h"
-#include "common/tusb_common.h"
+
+#if CONFIG_TINYUSB_ENABLED
 #include "esp_event.h"
+#include "USBCDC.h"
 
 #define ARDUINO_USB_ON_BOOT (ARDUINO_USB_CDC_ON_BOOT|ARDUINO_USB_MSC_ON_BOOT|ARDUINO_USB_DFU_ON_BOOT)
 
diff --git a/cores/esp32/USBCDC.cpp b/cores/esp32/USBCDC.cpp
index 7a9cf5e011c..ce2380feebf 100644
--- a/cores/esp32/USBCDC.cpp
+++ b/cores/esp32/USBCDC.cpp
@@ -11,17 +11,16 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-#include "esp32-hal.h"
-#include "esp32-hal-tinyusb.h"
 #include "USB.h"
+#if CONFIG_TINYUSB_CDC_ENABLED
+
 #include "USBCDC.h"
-#if CONFIG_TINYUSB_ENABLED
+#include "esp32-hal-tinyusb.h"
 
 ESP_EVENT_DEFINE_BASE(ARDUINO_USB_CDC_EVENTS);
 esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait);
 esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg);
 
-#if CFG_TUD_CDC
 #define MAX_USB_CDC_DEVICES 2
 USBCDC * devices[MAX_USB_CDC_DEVICES] = {NULL, NULL};
 
@@ -389,5 +388,3 @@ USBCDC Serial(0);
 #endif
 
 #endif /* CONFIG_TINYUSB_CDC_ENABLED */
-
-#endif /* CONFIG_TINYUSB_ENABLED */
diff --git a/cores/esp32/USBMSC.cpp b/cores/esp32/USBMSC.cpp
index c6327ecdee0..479d68e5802 100644
--- a/cores/esp32/USBMSC.cpp
+++ b/cores/esp32/USBMSC.cpp
@@ -11,12 +11,12 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
+#include "USBMSC.h"
+
+#if CONFIG_TINYUSB_MSC_ENABLED
 
-#include "esp32-hal.h"
 #include "esp32-hal-tinyusb.h"
-#include "USBMSC.h"
 
-#if CFG_TUD_MSC
 extern "C" uint16_t tusb_msc_load_descriptor(uint8_t * dst, uint8_t * itf)
 {
     uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB MSC");
@@ -257,4 +257,4 @@ void USBMSC::mediaPresent(bool media_present){
     msc_luns[_lun].media_present = media_present;
 }
 
-#endif /* CONFIG_USB_MSC_ENABLED */
+#endif /* CONFIG_TINYUSB_MSC_ENABLED */
diff --git a/cores/esp32/USBMSC.h b/cores/esp32/USBMSC.h
index 799322a30b5..287a57ceb31 100644
--- a/cores/esp32/USBMSC.h
+++ b/cores/esp32/USBMSC.h
@@ -15,7 +15,8 @@
 #pragma once
 #include <stdint.h>
 #include <stdbool.h>
-#include "esp32-hal.h"
+#include "sdkconfig.h"
+
 #if CONFIG_TINYUSB_MSC_ENABLED
 
 // Invoked when received Start Stop Unit command
@@ -46,4 +47,5 @@ class USBMSC
 private:
 	uint8_t _lun;
 };
-#endif
+
+#endif /* CONFIG_TINYUSB_MSC_ENABLED */
diff --git a/libraries/USB/src/USBHID.cpp b/libraries/USB/src/USBHID.cpp
index d29993021d8..2d497f2266e 100644
--- a/libraries/USB/src/USBHID.cpp
+++ b/libraries/USB/src/USBHID.cpp
@@ -11,12 +11,14 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-#include "esp32-hal.h"
-#include "esp32-hal-tinyusb.h"
 #include "USBHID.h"
+
+#if CONFIG_TINYUSB_HID_ENABLED
+
+#include "esp32-hal-tinyusb.h"
+#include "USB.h"
 #include "esp_hid_common.h"
 
-#if CFG_TUD_HID
 #define USB_HID_DEVICES_MAX 10
 
 ESP_EVENT_DEFINE_BASE(ARDUINO_USB_HID_EVENTS);
@@ -360,4 +362,4 @@ void USBHID::onEvent(arduino_usb_hid_event_t event, esp_event_handler_t callback
     arduino_usb_event_handler_register_with(ARDUINO_USB_HID_EVENTS, event, callback, this);
 }
 
-#endif /* CONFIG_USB_HID_ENABLED */
+#endif /* CONFIG_TINYUSB_HID_ENABLED */
diff --git a/libraries/USB/src/USBHID.h b/libraries/USB/src/USBHID.h
index 9c62d018954..e7eaae10fab 100644
--- a/libraries/USB/src/USBHID.h
+++ b/libraries/USB/src/USBHID.h
@@ -15,12 +15,12 @@
 #pragma once
 #include <stdint.h>
 #include <stdbool.h>
-#include "esp32-hal.h"
-#include "USB.h"
-#if CONFIG_TINYUSB_HID_ENABLED
+#include "sdkconfig.h"
 
-#include "class/hid/hid.h"
+#if CONFIG_TINYUSB_HID_ENABLED
 #include "esp_event.h"
+#include "class/hid/hid.h"
+#include "class/hid/hid_device.h"
 
 // Used by the included TinyUSB drivers
 enum {
@@ -76,4 +76,4 @@ class USBHID
     static bool addDevice(USBHIDDevice * device, uint16_t descriptor_len);
 };
 
-#endif
+#endif /* CONFIG_TINYUSB_HID_ENABLED */
diff --git a/libraries/USB/src/USBHIDConsumerControl.cpp b/libraries/USB/src/USBHIDConsumerControl.cpp
index 35dee39beb7..7977fdd1f24 100644
--- a/libraries/USB/src/USBHIDConsumerControl.cpp
+++ b/libraries/USB/src/USBHIDConsumerControl.cpp
@@ -11,12 +11,11 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
+#include "USBHID.h"
 
-#include "esp32-hal.h"
-#include "esp32-hal-tinyusb.h"
-#include "USBHIDConsumerControl.h"
+#if CONFIG_TINYUSB_HID_ENABLED
 
-#if CFG_TUD_HID
+#include "USBHIDConsumerControl.h"
 
 static const uint8_t report_descriptor[] = {
     TUD_HID_REPORT_DESC_CONSUMER(HID_REPORT_ID(HID_REPORT_ID_CONSUMER_CONTROL))
@@ -55,4 +54,4 @@ size_t USBHIDConsumerControl::release(){
 }
 
 
-#endif /* CFG_TUD_HID */
+#endif /* CONFIG_TINYUSB_HID_ENABLED */
diff --git a/libraries/USB/src/USBHIDGamepad.cpp b/libraries/USB/src/USBHIDGamepad.cpp
index 37e65e79ffc..57a2f028356 100644
--- a/libraries/USB/src/USBHIDGamepad.cpp
+++ b/libraries/USB/src/USBHIDGamepad.cpp
@@ -11,12 +11,11 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
+#include "USBHID.h"
 
-#include "esp32-hal.h"
-#include "esp32-hal-tinyusb.h"
-#include "USBHIDGamepad.h"
+#if CONFIG_TINYUSB_HID_ENABLED
 
-#if CFG_TUD_HID
+#include "USBHIDGamepad.h"
 
 static const uint8_t report_descriptor[] = {
     TUD_HID_REPORT_DESC_GAMEPAD(HID_REPORT_ID(HID_REPORT_ID_GAMEPAD))
@@ -119,4 +118,4 @@ bool USBHIDGamepad::send(int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int
 }
 
 
-#endif /* CFG_TUD_HID */
+#endif /* CONFIG_TINYUSB_HID_ENABLED */
diff --git a/libraries/USB/src/USBHIDKeyboard.cpp b/libraries/USB/src/USBHIDKeyboard.cpp
index 15914ceed3c..9a9445ded89 100644
--- a/libraries/USB/src/USBHIDKeyboard.cpp
+++ b/libraries/USB/src/USBHIDKeyboard.cpp
@@ -18,12 +18,11 @@
   License along with this library; if not, write to the Free Software
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
+#include "USBHID.h"
 
-#include "esp32-hal.h"
-#include "esp32-hal-tinyusb.h"
-#include "USBHIDKeyboard.h"
+#if CONFIG_TINYUSB_HID_ENABLED
 
-#if CFG_TUD_HID
+#include "USBHIDKeyboard.h"
 
 ESP_EVENT_DEFINE_BASE(ARDUINO_USB_HID_KEYBOARD_EVENTS);
 esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait);
@@ -352,4 +351,4 @@ size_t USBHIDKeyboard::write(const uint8_t *buffer, size_t size) {
     return n;
 }
 
-#endif /* CFG_TUD_HID */
+#endif /* CONFIG_TINYUSB_HID_ENABLED */
diff --git a/libraries/USB/src/USBHIDMouse.cpp b/libraries/USB/src/USBHIDMouse.cpp
index dbc84c97505..92efd617661 100644
--- a/libraries/USB/src/USBHIDMouse.cpp
+++ b/libraries/USB/src/USBHIDMouse.cpp
@@ -18,12 +18,11 @@
   License along with this library; if not, write to the Free Software
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
+#include "USBHID.h"
 
-#include "esp32-hal.h"
-#include "esp32-hal-tinyusb.h"
-#include "USBHIDMouse.h"
+#if CONFIG_TINYUSB_HID_ENABLED
 
-#if CFG_TUD_HID
+#include "USBHIDMouse.h"
 
 static const uint8_t report_descriptor[] = {
     TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(HID_REPORT_ID_MOUSE))
@@ -90,4 +89,4 @@ bool USBHIDMouse::isPressed(uint8_t b){
 }
 
 
-#endif /* CFG_TUD_HID */
+#endif /* CONFIG_TINYUSB_HID_ENABLED */
diff --git a/libraries/USB/src/USBHIDSystemControl.cpp b/libraries/USB/src/USBHIDSystemControl.cpp
index d1dafc30906..6bd165619d9 100644
--- a/libraries/USB/src/USBHIDSystemControl.cpp
+++ b/libraries/USB/src/USBHIDSystemControl.cpp
@@ -11,12 +11,11 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
+#include "USBHID.h"
 
-#include "esp32-hal.h"
-#include "esp32-hal-tinyusb.h"
-#include "USBHIDSystemControl.h"
+#if CONFIG_TINYUSB_HID_ENABLED
 
-#if CFG_TUD_HID
+#include "USBHIDSystemControl.h"
 
 static const uint8_t report_descriptor[] = {
     TUD_HID_REPORT_DESC_SYSTEM_CONTROL(HID_REPORT_ID(HID_REPORT_ID_SYSTEM_CONTROL))
@@ -57,4 +56,4 @@ size_t USBHIDSystemControl::release(){
     return send(0);
 }
 
-#endif /* CFG_TUD_HID */
+#endif /* CONFIG_TINYUSB_HID_ENABLED */
diff --git a/libraries/USB/src/USBHIDVendor.cpp b/libraries/USB/src/USBHIDVendor.cpp
index 96e0d6e1661..0a6495abe71 100644
--- a/libraries/USB/src/USBHIDVendor.cpp
+++ b/libraries/USB/src/USBHIDVendor.cpp
@@ -11,11 +11,12 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-#include "esp32-hal.h"
-#include "esp32-hal-tinyusb.h"
-#include "USBHIDVendor.h"
+#include "USBHID.h"
+
+#if CONFIG_TINYUSB_HID_ENABLED
 
-#if CFG_TUD_HID
+#include "esp32-hal-log.h"
+#include "USBHIDVendor.h"
 
 ESP_EVENT_DEFINE_BASE(ARDUINO_USB_HID_VENDOR_EVENTS);
 esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait);
@@ -232,4 +233,4 @@ size_t USBHIDVendor::read(uint8_t *buffer, size_t size){
 void USBHIDVendor::flush(void){}
 
 
-#endif /* CFG_TUD_HID */
+#endif /* CONFIG_TINYUSB_HID_ENABLED */

From d493788a949eb07cccbb768db3d3ee4cdb4050b0 Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Thu, 19 Aug 2021 13:41:39 +0300
Subject: [PATCH 12/17] Fix examples

---
 cores/esp32/USB.h                                          | 3 ++-
 cores/esp32/USBCDC.h                                       | 7 +++----
 libraries/USB/examples/ConsumerControl/ConsumerControl.ino | 1 +
 libraries/USB/examples/Gamepad/Gamepad.ino                 | 1 +
 libraries/USB/examples/HIDVendor/HIDVendor.ino             | 1 +
 .../examples/Keyboard/KeyboardLogout/KeyboardLogout.ino    | 1 +
 .../examples/Keyboard/KeyboardMessage/KeyboardMessage.ino  | 1 +
 .../Keyboard/KeyboardReprogram/KeyboardReprogram.ino       | 1 +
 .../examples/Keyboard/KeyboardSerial/KeyboardSerial.ino    | 1 +
 .../KeyboardAndMouseControl/KeyboardAndMouseControl.ino    | 1 +
 .../Mouse/ButtonMouseControl/ButtonMouseControl.ino        | 1 +
 libraries/USB/examples/SystemControl/SystemControl.ino     | 1 +
 12 files changed, 15 insertions(+), 5 deletions(-)

diff --git a/cores/esp32/USB.h b/cores/esp32/USB.h
index 33f3a2c1dfc..131ff6d4268 100644
--- a/cores/esp32/USB.h
+++ b/cores/esp32/USB.h
@@ -13,9 +13,10 @@
 // limitations under the License.
 #pragma once
 
-#include "Arduino.h"
+#include "sdkconfig.h"
 
 #if CONFIG_TINYUSB_ENABLED
+
 #include "esp_event.h"
 #include "USBCDC.h"
 
diff --git a/cores/esp32/USBCDC.h b/cores/esp32/USBCDC.h
index 26df8a3c81c..ced588f45f2 100644
--- a/cores/esp32/USBCDC.h
+++ b/cores/esp32/USBCDC.h
@@ -13,13 +13,12 @@
 // limitations under the License.
 #pragma once
 
-#include <inttypes.h>
-
-#include "Stream.h"
-#include "esp32-hal.h"
+#include "sdkconfig.h"
 #if CONFIG_TINYUSB_CDC_ENABLED
 
+#include <inttypes.h>
 #include "esp_event.h"
+#include "Stream.h"
 
 ESP_EVENT_DECLARE_BASE(ARDUINO_USB_CDC_EVENTS);
 
diff --git a/libraries/USB/examples/ConsumerControl/ConsumerControl.ino b/libraries/USB/examples/ConsumerControl/ConsumerControl.ino
index d3fbe81f85f..ab6fb19d9ca 100644
--- a/libraries/USB/examples/ConsumerControl/ConsumerControl.ino
+++ b/libraries/USB/examples/ConsumerControl/ConsumerControl.ino
@@ -1,3 +1,4 @@
+#include "USB.h"
 #include "USBHIDConsumerControl.h"
 USBHIDConsumerControl ConsumerControl;
 
diff --git a/libraries/USB/examples/Gamepad/Gamepad.ino b/libraries/USB/examples/Gamepad/Gamepad.ino
index e7cd9a4d817..da9ff69e9dd 100644
--- a/libraries/USB/examples/Gamepad/Gamepad.ino
+++ b/libraries/USB/examples/Gamepad/Gamepad.ino
@@ -1,3 +1,4 @@
+#include "USB.h"
 #include "USBHIDGamepad.h"
 USBHIDGamepad Gamepad;
 
diff --git a/libraries/USB/examples/HIDVendor/HIDVendor.ino b/libraries/USB/examples/HIDVendor/HIDVendor.ino
index f4727415761..223edba1d90 100644
--- a/libraries/USB/examples/HIDVendor/HIDVendor.ino
+++ b/libraries/USB/examples/HIDVendor/HIDVendor.ino
@@ -1,3 +1,4 @@
+#include "USB.h"
 #include "USBHIDVendor.h"
 USBHIDVendor Vendor;
 
diff --git a/libraries/USB/examples/Keyboard/KeyboardLogout/KeyboardLogout.ino b/libraries/USB/examples/Keyboard/KeyboardLogout/KeyboardLogout.ino
index 90cf6bb4bac..a7f8214b78a 100644
--- a/libraries/USB/examples/Keyboard/KeyboardLogout/KeyboardLogout.ino
+++ b/libraries/USB/examples/Keyboard/KeyboardLogout/KeyboardLogout.ino
@@ -29,6 +29,7 @@
 #define WINDOWS 1
 #define UBUNTU 2
 
+#include "USB.h"
 #include "USBHIDKeyboard.h"
 USBHIDKeyboard Keyboard;
 
diff --git a/libraries/USB/examples/Keyboard/KeyboardMessage/KeyboardMessage.ino b/libraries/USB/examples/Keyboard/KeyboardMessage/KeyboardMessage.ino
index 93766bad3df..30f92a47821 100644
--- a/libraries/USB/examples/Keyboard/KeyboardMessage/KeyboardMessage.ino
+++ b/libraries/USB/examples/Keyboard/KeyboardMessage/KeyboardMessage.ino
@@ -20,6 +20,7 @@
   http://www.arduino.cc/en/Tutorial/KeyboardMessage
 */
 
+#include "USB.h"
 #include "USBHIDKeyboard.h"
 USBHIDKeyboard Keyboard;
 
diff --git a/libraries/USB/examples/Keyboard/KeyboardReprogram/KeyboardReprogram.ino b/libraries/USB/examples/Keyboard/KeyboardReprogram/KeyboardReprogram.ino
index 192f91f03fb..3c6520556fe 100644
--- a/libraries/USB/examples/Keyboard/KeyboardReprogram/KeyboardReprogram.ino
+++ b/libraries/USB/examples/Keyboard/KeyboardReprogram/KeyboardReprogram.ino
@@ -25,6 +25,7 @@
   http://www.arduino.cc/en/Tutorial/KeyboardReprogram
 */
 
+#include "USB.h"
 #include "USBHIDKeyboard.h"
 USBHIDKeyboard Keyboard;
 
diff --git a/libraries/USB/examples/Keyboard/KeyboardSerial/KeyboardSerial.ino b/libraries/USB/examples/Keyboard/KeyboardSerial/KeyboardSerial.ino
index bcfa7542d4d..e3bb8769737 100644
--- a/libraries/USB/examples/Keyboard/KeyboardSerial/KeyboardSerial.ino
+++ b/libraries/USB/examples/Keyboard/KeyboardSerial/KeyboardSerial.ino
@@ -17,6 +17,7 @@
   http://www.arduino.cc/en/Tutorial/KeyboardSerial
 */
 
+#include "USB.h"
 #include "USBHIDKeyboard.h"
 USBHIDKeyboard Keyboard;
 
diff --git a/libraries/USB/examples/KeyboardAndMouseControl/KeyboardAndMouseControl.ino b/libraries/USB/examples/KeyboardAndMouseControl/KeyboardAndMouseControl.ino
index 14623929c07..6cf564bbc9f 100644
--- a/libraries/USB/examples/KeyboardAndMouseControl/KeyboardAndMouseControl.ino
+++ b/libraries/USB/examples/KeyboardAndMouseControl/KeyboardAndMouseControl.ino
@@ -19,6 +19,7 @@
   http://www.arduino.cc/en/Tutorial/KeyboardAndMouseControl
 */
 
+#include "USB.h"
 #include "USBHIDMouse.h"
 #include "USBHIDKeyboard.h"
 USBHIDMouse Mouse;
diff --git a/libraries/USB/examples/Mouse/ButtonMouseControl/ButtonMouseControl.ino b/libraries/USB/examples/Mouse/ButtonMouseControl/ButtonMouseControl.ino
index 8bc1200afcb..0a7ee0caa34 100644
--- a/libraries/USB/examples/Mouse/ButtonMouseControl/ButtonMouseControl.ino
+++ b/libraries/USB/examples/Mouse/ButtonMouseControl/ButtonMouseControl.ino
@@ -21,6 +21,7 @@
   http://www.arduino.cc/en/Tutorial/ButtonMouseControl
 */
 
+#include "USB.h"
 #include "USBHIDMouse.h"
 USBHIDMouse Mouse;
 
diff --git a/libraries/USB/examples/SystemControl/SystemControl.ino b/libraries/USB/examples/SystemControl/SystemControl.ino
index f4a7f5c5843..7d644ea9164 100644
--- a/libraries/USB/examples/SystemControl/SystemControl.ino
+++ b/libraries/USB/examples/SystemControl/SystemControl.ino
@@ -1,3 +1,4 @@
+#include "USB.h"
 #include "USBHIDSystemControl.h"
 USBHIDSystemControl SystemControl;
 

From 1cf4559b7be7ef57d53d35d08ee22c07ff476529 Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Thu, 19 Aug 2021 20:56:09 +0300
Subject: [PATCH 13/17] Update esp32-hal-tinyusb.c

---
 cores/esp32/esp32-hal-tinyusb.c | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/cores/esp32/esp32-hal-tinyusb.c b/cores/esp32/esp32-hal-tinyusb.c
index 0da912b0210..a413c75260e 100644
--- a/cores/esp32/esp32-hal-tinyusb.c
+++ b/cores/esp32/esp32-hal-tinyusb.c
@@ -424,12 +424,6 @@ static bool tinyusb_load_enabled_interfaces(){
                 log_e("Descriptor Load Failed");
                 return false;
             } else {
-                if(i == USB_INTERFACE_CDC){
-                    if(!tinyusb_reserve_out_endpoint(3) ||!tinyusb_reserve_in_endpoint(4) || !tinyusb_reserve_in_endpoint(5)){
-                        log_e("CDC Reserve Endpoints Failed");
-                        return false;
-                    }
-                }
                 dst += len;
             }
         }
@@ -505,6 +499,7 @@ static void tinyusb_apply_device_config(tinyusb_device_config_t *config){
         && (config->usb_class != TUSB_CLASS_CDC)
     ){
         config->usb_class = TUSB_CLASS_CDC;
+        config->usb_protocol = 0x00;
     }
 
     WEBUSB_ENABLED            = config->webusb_enabled;
@@ -573,6 +568,12 @@ esp_err_t tinyusb_enable_interface(tinyusb_interface_t interface, uint16_t descr
         log_e("Interface %s invalid or already enabled", (interface >= USB_INTERFACE_MAX)?"":tinyusb_interface_names[interface]);
         return ESP_FAIL;
     }
+    if(interface == USB_INTERFACE_CDC){
+        if(!tinyusb_reserve_out_endpoint(3) ||!tinyusb_reserve_in_endpoint(4) || !tinyusb_reserve_in_endpoint(5)){
+            log_e("CDC Reserve Endpoints Failed");
+            return ESP_FAIL;
+        }
+    }
     tinyusb_loaded_interfaces_mask |= (1U << interface);
     tinyusb_config_descriptor_len += descriptor_len;
     tinyusb_loaded_interfaces_callbacks[interface] = cb;
@@ -586,7 +587,7 @@ esp_err_t tinyusb_init(tinyusb_device_config_t *config) {
     }
     tinyusb_is_initialized = true;
     
-    tinyusb_endpoints.val = 0;
+    //tinyusb_endpoints.val = 0;
     tinyusb_apply_device_config(config);
     if (!tinyusb_load_enabled_interfaces()) {
         tinyusb_is_initialized = false;

From 2918858bfa7b8231d293875f2a16dfc715fd2a12 Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Thu, 19 Aug 2021 22:43:56 +0300
Subject: [PATCH 14/17] Add support and example for USB Vendor

---
 libraries/USB/examples/USBVendor/.skip.esp32  |   0
 .../USB/examples/USBVendor/.skip.esp32c3      |   0
 .../USB/examples/USBVendor/USBVendor.ino      | 165 ++++++++++++++
 libraries/USB/src/USBVendor.cpp               | 209 ++++++++++++++++++
 libraries/USB/src/USBVendor.h                 |  66 ++++++
 5 files changed, 440 insertions(+)
 create mode 100644 libraries/USB/examples/USBVendor/.skip.esp32
 create mode 100644 libraries/USB/examples/USBVendor/.skip.esp32c3
 create mode 100644 libraries/USB/examples/USBVendor/USBVendor.ino
 create mode 100644 libraries/USB/src/USBVendor.cpp
 create mode 100644 libraries/USB/src/USBVendor.h

diff --git a/libraries/USB/examples/USBVendor/.skip.esp32 b/libraries/USB/examples/USBVendor/.skip.esp32
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/USB/examples/USBVendor/.skip.esp32c3 b/libraries/USB/examples/USBVendor/.skip.esp32c3
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/USB/examples/USBVendor/USBVendor.ino b/libraries/USB/examples/USBVendor/USBVendor.ino
new file mode 100644
index 00000000000..d9286043bb5
--- /dev/null
+++ b/libraries/USB/examples/USBVendor/USBVendor.ino
@@ -0,0 +1,165 @@
+#include "USB.h"
+#include "USBVendor.h"
+#include "class/cdc/cdc.h"
+
+#if ARDUINO_USB_CDC_ON_BOOT
+#define HWSerial Serial0
+#else
+#define HWSerial Serial
+#endif
+
+USBVendor Vendor;
+
+const int buttonPin = 0;
+int previousButtonState = HIGH;
+
+static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data){
+  if(event_base == ARDUINO_USB_EVENTS){
+    arduino_usb_event_data_t * data = (arduino_usb_event_data_t*)event_data;
+    switch (event_id){
+      case ARDUINO_USB_STARTED_EVENT:
+        HWSerial.println("USB PLUGGED");
+        break;
+      case ARDUINO_USB_STOPPED_EVENT:
+        HWSerial.println("USB UNPLUGGED");
+        break;
+      case ARDUINO_USB_SUSPEND_EVENT:
+        HWSerial.printf("USB SUSPENDED: remote_wakeup_en: %u\n", data->suspend.remote_wakeup_en);
+        break;
+      case ARDUINO_USB_RESUME_EVENT:
+        HWSerial.println("USB RESUMED");
+        break;
+      
+      default:
+        break;
+    }
+  } else if(event_base == ARDUINO_USB_VENDOR_EVENTS){
+    arduino_usb_vendor_event_data_t * data = (arduino_usb_vendor_event_data_t*)event_data;
+    switch (event_id){
+      case ARDUINO_USB_VENDOR_DATA_EVENT:
+        HWSerial.printf("Vendor RX: len:%u\n", data->data.len);
+        for(uint16_t i=0; i<data->data.len; i++){
+          HWSerial.write(Vendor.read());
+        }
+        HWSerial.println();
+        break;
+        
+      default:
+        break;
+    }
+  }
+}
+
+bool vendorRequestCallback(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request){
+    static const char * vendorRequestTypes[] = {"Standard", "Class", "Vendor"};
+    static const char * vendorRequestRecipients[] = {"Device", "Interface", "Endpoint", "Other"};
+    static const char * vendorStages[] = {"Setup","Data","Ack"};
+    HWSerial.printf("Vendor Request: Stage: %5s, Direction: %3s, Type: %8s, Recipient: %9s, bRequest: 0x%02x, wValue: 0x%04x, wIndex: %u, wLength: %u\n", vendorStages[stage], 
+        request->bmRequestType_bit.direction?"IN":"OUT", 
+        vendorRequestTypes[request->bmRequestType_bit.type], 
+        vendorRequestRecipients[request->bmRequestType_bit.recipient], 
+        request->bRequest, request->wValue, request->wIndex, request->wLength);
+    
+    
+    static uint8_t vendor_line_state = 0;// Bit 0:  DTR (Data Terminal Ready), Bit 1: RTS (Request to Send)
+    static cdc_line_coding_t vendor_line_coding;
+    bool result = false;
+
+    if(request->bmRequestType_bit.direction == TUSB_DIR_OUT && request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE && request->bRequest == 0x0b){
+        if(stage == CONTROL_STAGE_SETUP) {
+            // response with status OK
+            result = Vendor.sendResponse(rhport, request);
+        } else {
+            result = true;
+        }
+    } else if(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS && request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE){
+        //Implement CDC Control Requests
+        switch (request->bRequest) {
+
+            // CDC Set Line Coding
+            case CDC_REQUEST_SET_LINE_CODING: //0x20
+                if(request->wLength != sizeof(cdc_line_coding_t) || request->bmRequestType_bit.direction != TUSB_DIR_OUT){
+                    break;
+                }
+                if(stage == CONTROL_STAGE_SETUP) {
+                    //Send the response in setup stage (it will write the data to vendor_line_coding in the DATA stage)
+                    result = Vendor.sendResponse(rhport, request, (void*) &vendor_line_coding, sizeof(cdc_line_coding_t));
+                } else if(stage == CONTROL_STAGE_ACK){
+                    //In the ACK stage the requst->response is complete
+                    HWSerial.printf("Vendor Line Coding: bit_rate: %u, data_bits: %u, stop_bits: %u, parity: %u\n", vendor_line_coding.bit_rate, vendor_line_coding.data_bits, vendor_line_coding.stop_bits, vendor_line_coding.parity);
+                }
+                result = true;
+            break;
+
+            // CDC Get Line Coding
+            case CDC_REQUEST_GET_LINE_CODING: //0x21
+                if(request->wLength != sizeof(cdc_line_coding_t) || request->bmRequestType_bit.direction != TUSB_DIR_IN){
+                    break;
+                }
+                if(stage == CONTROL_STAGE_SETUP) {
+                    result = Vendor.sendResponse(rhport, request, (void*) &vendor_line_coding, sizeof(cdc_line_coding_t));
+                }
+                result = true;
+            break;
+
+            // CDC Set Line State
+            case CDC_REQUEST_SET_CONTROL_LINE_STATE: //0x22
+                if(request->wLength != 0 || request->bmRequestType_bit.direction != TUSB_DIR_OUT){
+                    break;
+                }
+                if(stage == CONTROL_STAGE_SETUP) {
+                    vendor_line_state = request->wValue;
+                    result = Vendor.sendResponse(rhport, request);
+                } else if(stage == CONTROL_STAGE_ACK){
+                    bool dtr = tu_bit_test(vendor_line_state, 0);
+                    bool rts = tu_bit_test(vendor_line_state, 1);
+                    HWSerial.printf("Vendor Line State: dtr: %u, rts: %u\n", dtr, rts);
+                }
+                result = true;
+            break;
+
+            default:
+                // stall unknown request
+            break;
+        }
+    }
+
+    return result;
+}
+
+void setup() {
+  pinMode(buttonPin, INPUT_PULLUP);
+  HWSerial.begin(115200);
+  HWSerial.setDebugOutput(true);
+  
+  Vendor.onEvent(usbEventCallback);
+  Vendor.onRequest(vendorRequestCallback);
+  Vendor.begin();
+  
+  USB.onEvent(usbEventCallback);
+  USB.webUSB(true);
+  USB.webUSBURL("http://localhost/webusb");
+  USB.begin();
+}
+
+void loop() {
+  int buttonState = digitalRead(buttonPin);
+  if (buttonState != previousButtonState) {
+    previousButtonState = buttonState;
+    if (buttonState == LOW) {
+      HWSerial.println("Button Pressed");
+      Vendor.println("Button Pressed");
+    } else {
+      Vendor.println("Button Released");
+      HWSerial.println("Button Released");
+    }
+    delay(100);
+  }
+  
+  while(HWSerial.available()){
+    size_t l = HWSerial.available();
+    uint8_t b[l];
+    l = HWSerial.read(b, l);
+    Vendor.write(b,l);
+  }
+}
diff --git a/libraries/USB/src/USBVendor.cpp b/libraries/USB/src/USBVendor.cpp
new file mode 100644
index 00000000000..26a75c547fa
--- /dev/null
+++ b/libraries/USB/src/USBVendor.cpp
@@ -0,0 +1,209 @@
+// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include "USBVendor.h"
+
+#if CONFIG_TINYUSB_VENDOR_ENABLED
+
+#include "esp32-hal-tinyusb.h"
+
+ESP_EVENT_DEFINE_BASE(ARDUINO_USB_VENDOR_EVENTS);
+esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait);
+esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg);
+
+uint16_t tusb_vendor_load_descriptor(uint8_t * dst, uint8_t * itf)
+{
+    uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB Vendor");
+    uint8_t ep_num = tinyusb_get_free_duplex_endpoint();
+    TU_VERIFY (ep_num != 0);
+    uint8_t descriptor[TUD_VENDOR_DESC_LEN] = {
+        // Interface number, string index, EP Out & IN address, EP size
+        TUD_VENDOR_DESCRIPTOR(*itf, str_index, ep_num, (uint8_t)(0x80 | ep_num), 64)
+    };
+    *itf+=1;
+    memcpy(dst, descriptor, TUD_VENDOR_DESC_LEN);
+    return TUD_VENDOR_DESC_LEN;
+}
+
+static USBVendor * _Vendor = NULL;
+static xQueueHandle rx_queue = NULL;
+
+void tud_vendor_rx_cb(uint8_t itf){
+    log_v("%u", len);
+    size_t len = tud_vendor_n_available(itf);
+    if(len){
+        uint8_t buffer[len];
+        len = tud_vendor_n_read(itf, buffer, len);
+        log_buf_v(buffer, len);
+        if(_Vendor) {
+            _Vendor->_onRX(buffer, len);
+        }
+    } else {
+        if(_Vendor) {
+            _Vendor->_onRX(NULL, len);
+        }
+    }
+}
+
+extern "C" bool tinyusb_vendor_control_request_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request){
+    log_v("Port: %u, Stage: %u, Direction: %u, Type: %u, Recipient: %u, bRequest: 0x%x, wValue: %u, wIndex: %u, wLength: %u", 
+        rhport, stage, request->bmRequestType_bit.direction, 
+        request->bmRequestType_bit.type, request->bmRequestType_bit.recipient, 
+        request->bRequest, request->wValue, request->wIndex, request->wLength);
+
+    if(_Vendor) {
+        return _Vendor->_onRequest(rhport, stage, request);
+    }
+    return false;
+}
+
+USBVendor::USBVendor():itf(0), cb(NULL){
+    if(!_Vendor){
+        _Vendor = this;
+        tinyusb_enable_interface(USB_INTERFACE_VENDOR, TUD_VENDOR_DESC_LEN, tusb_vendor_load_descriptor);
+    }
+}
+
+size_t USBVendor::setRxBufferSize(size_t rx_queue_len){
+    if(rx_queue){
+        if(!rx_queue_len){
+            vQueueDelete(rx_queue);
+            rx_queue = NULL;
+        }
+        return 0;
+    }
+    rx_queue = xQueueCreate(rx_queue_len, sizeof(uint8_t));
+    if(!rx_queue){
+        return 0;
+    }
+    return rx_queue_len;
+}
+
+void USBVendor::begin(){
+    setRxBufferSize(256);//default if not preset
+}
+
+void USBVendor::end(){
+    setRxBufferSize(0);
+}
+
+void USBVendor::onEvent(esp_event_handler_t callback){
+    onEvent(ARDUINO_USB_VENDOR_ANY_EVENT, callback);
+}
+
+void USBVendor::onEvent(arduino_usb_vendor_event_t event, esp_event_handler_t callback){
+    arduino_usb_event_handler_register_with(ARDUINO_USB_VENDOR_EVENTS, event, callback, this);
+}
+
+bool USBVendor::mounted(){
+    return tud_vendor_n_mounted(itf);
+}
+
+bool USBVendor::sendResponse(uint8_t rhport, tusb_control_request_t const * request, void * data, size_t len){
+    if(!request){
+        return false;
+    }
+    if(!data || !len){
+        return tud_control_status(rhport, request);
+    } else {
+        return tud_control_xfer(rhport, request, data, len);
+    }
+}
+
+void USBVendor::onRequest(arduino_usb_vendor_control_request_handler_t handler){
+    cb = handler;
+}
+
+bool USBVendor::_onRequest(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request){
+    if(cb){
+        return cb(rhport, stage, request);
+    }
+    return false;
+}
+
+void USBVendor::_onRX(const uint8_t* buffer, size_t len){
+    for(uint32_t i=0; i<len; i++){
+        if(rx_queue == NULL || !xQueueSend(rx_queue, buffer+i, 0)){
+            len = i+1;
+            log_e("RX Queue Overflow");
+            break;
+        }
+    }
+    arduino_usb_vendor_event_data_t p = {0};
+    p.data.len = len;
+    arduino_usb_event_post(ARDUINO_USB_VENDOR_EVENTS, ARDUINO_USB_VENDOR_DATA_EVENT, &p, sizeof(arduino_usb_vendor_event_data_t), portMAX_DELAY);
+}
+
+size_t USBVendor::write(const uint8_t* buffer, size_t len){
+    if(!mounted()){
+        log_e("not mounted");
+        return 0;
+    }
+    size_t max_len = tud_vendor_n_write_available(itf);
+    if(len > max_len){
+        len = max_len;
+    }
+    if(len){
+        return tud_vendor_n_write(itf, buffer, len);
+    }
+    return len;
+}
+
+size_t USBVendor::write(uint8_t c){
+    return write(&c, 1);
+}
+
+int USBVendor::available(void){
+    if(rx_queue == NULL){
+        return -1;
+    }
+    return uxQueueMessagesWaiting(rx_queue);
+}
+
+int USBVendor::peek(void){
+    if(rx_queue == NULL){
+        return -1;
+    }
+    uint8_t c;
+    if(xQueuePeek(rx_queue, &c, 0)) {
+        return c;
+    }
+    return -1;
+}
+
+int USBVendor::read(void){
+    if(rx_queue == NULL){
+        return -1;
+    }
+    uint8_t c = 0;
+    if(xQueueReceive(rx_queue, &c, 0)) {
+        return c;
+    }
+    return -1;
+}
+
+size_t USBVendor::read(uint8_t *buffer, size_t size){
+    if(rx_queue == NULL){
+        return -1;
+    }
+    uint8_t c = 0;
+    size_t count = 0;
+    while(count < size && xQueueReceive(rx_queue, &c, 0)){
+        buffer[count++] = c;
+    }
+    return count;
+}
+
+void USBVendor::flush(void){}
+
+#endif /* CONFIG_TINYUSB_VENDOR_ENABLED */
diff --git a/libraries/USB/src/USBVendor.h b/libraries/USB/src/USBVendor.h
new file mode 100644
index 00000000000..b641fd9de31
--- /dev/null
+++ b/libraries/USB/src/USBVendor.h
@@ -0,0 +1,66 @@
+// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+#include "Stream.h"
+#include "sdkconfig.h"
+
+#if CONFIG_TINYUSB_VENDOR_ENABLED
+#include "esp_event.h"
+#include "common/tusb_common.h"
+
+ESP_EVENT_DECLARE_BASE(ARDUINO_USB_VENDOR_EVENTS);
+
+typedef enum {
+    ARDUINO_USB_VENDOR_ANY_EVENT = ESP_EVENT_ANY_ID,
+    ARDUINO_USB_VENDOR_DATA_EVENT,
+    ARDUINO_USB_VENDOR_MAX_EVENT,
+} arduino_usb_vendor_event_t;
+
+typedef union {
+    struct {
+        uint16_t len;
+    } data;
+} arduino_usb_vendor_event_data_t;
+
+typedef bool (*arduino_usb_vendor_control_request_handler_t)(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
+
+class USBVendor: public Stream {
+private:
+    uint8_t itf;
+    arduino_usb_vendor_control_request_handler_t cb;
+public:
+    USBVendor();
+    void begin(void);
+    void end(void);
+    size_t setRxBufferSize(size_t);
+    bool mounted(void);
+    size_t write(const uint8_t* buffer, size_t len);
+    size_t write(uint8_t);
+    int available(void);
+    int peek(void);
+    int read(void);
+    size_t read(uint8_t *buffer, size_t size);
+    void flush(void);
+    
+    void onEvent(esp_event_handler_t callback);
+    void onEvent(arduino_usb_vendor_event_t event, esp_event_handler_t callback);
+    void onRequest(arduino_usb_vendor_control_request_handler_t handler);
+    bool sendResponse(uint8_t rhport, tusb_control_request_t const * request, void * data=NULL, size_t len=0);
+
+    bool _onRequest(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
+    void _onRX(const uint8_t* buffer, size_t len);
+};
+
+#endif /* CONFIG_TINYUSB_VENDOR_ENABLED */

From c0942e8c315050a918ebd3cb5b18cde14d3ac644 Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Thu, 19 Aug 2021 22:46:22 +0300
Subject: [PATCH 15/17] Update CMakeLists.txt

---
 CMakeLists.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index a1afff1655c..cdb1f07952b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -83,6 +83,7 @@ set(LIBRARY_SRCS
   libraries/USB/src/USBHIDConsumerControl.cpp
   libraries/USB/src/USBHIDSystemControl.cpp
   libraries/USB/src/USBHIDVendor.cpp
+  libraries/USB/src/USBVendor.cpp
   libraries/WebServer/src/WebServer.cpp
   libraries/WebServer/src/Parsing.cpp
   libraries/WebServer/src/detail/mimetable.cpp

From a56aab43f4b0816acd5b6f46c29109d092b2da21 Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Fri, 20 Aug 2021 01:45:43 +0300
Subject: [PATCH 16/17] Update USBVendor example

---
 .../USB/examples/USBVendor/USBVendor.ino      | 192 ++++++++++--------
 libraries/USB/src/USBVendor.cpp               |  27 ++-
 libraries/USB/src/USBVendor.h                 |  38 +++-
 3 files changed, 159 insertions(+), 98 deletions(-)

diff --git a/libraries/USB/examples/USBVendor/USBVendor.ino b/libraries/USB/examples/USBVendor/USBVendor.ino
index d9286043bb5..f8976a07e85 100644
--- a/libraries/USB/examples/USBVendor/USBVendor.ino
+++ b/libraries/USB/examples/USBVendor/USBVendor.ino
@@ -1,6 +1,5 @@
 #include "USB.h"
 #include "USBVendor.h"
-#include "class/cdc/cdc.h"
 
 #if ARDUINO_USB_CDC_ON_BOOT
 #define HWSerial Serial0
@@ -9,14 +8,31 @@
 #endif
 
 USBVendor Vendor;
-
 const int buttonPin = 0;
-int previousButtonState = HIGH;
 
-static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data){
-  if(event_base == ARDUINO_USB_EVENTS){
+//CDC Control Requests
+#define REQUEST_SET_LINE_CODING 0x20
+#define REQUEST_GET_LINE_CODING 0x21
+#define REQUEST_SET_CONTROL_LINE_STATE 0x22
+
+//CDC Line Coding Control Request Structure
+typedef struct __attribute__ ((packed)) {
+  uint32_t bit_rate;
+  uint8_t  stop_bits; //0: 1 stop bit, 1: 1.5 stop bits, 2: 2 stop bits
+  uint8_t  parity;    //0: None, 1: Odd, 2: Even, 3: Mark, 4: Space
+  uint8_t  data_bits; //5, 6, 7, 8 or 16
+} request_line_coding_t;
+
+static request_line_coding_t vendor_line_coding = {9600, 0, 0, 8};
+
+// Bit 0:  DTR (Data Terminal Ready), Bit 1: RTS (Request to Send)
+static uint8_t vendor_line_state = 0;
+
+//USB and Vendor events
+static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) {
+  if (event_base == ARDUINO_USB_EVENTS) {
     arduino_usb_event_data_t * data = (arduino_usb_event_data_t*)event_data;
-    switch (event_id){
+    switch (event_id) {
       case ARDUINO_USB_STARTED_EVENT:
         HWSerial.println("USB PLUGGED");
         break;
@@ -29,113 +45,122 @@ static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t eve
       case ARDUINO_USB_RESUME_EVENT:
         HWSerial.println("USB RESUMED");
         break;
-      
+
       default:
         break;
     }
-  } else if(event_base == ARDUINO_USB_VENDOR_EVENTS){
+  } else if (event_base == ARDUINO_USB_VENDOR_EVENTS) {
     arduino_usb_vendor_event_data_t * data = (arduino_usb_vendor_event_data_t*)event_data;
-    switch (event_id){
+    switch (event_id) {
       case ARDUINO_USB_VENDOR_DATA_EVENT:
         HWSerial.printf("Vendor RX: len:%u\n", data->data.len);
-        for(uint16_t i=0; i<data->data.len; i++){
+        for (uint16_t i = 0; i < data->data.len; i++) {
           HWSerial.write(Vendor.read());
         }
         HWSerial.println();
         break;
-        
+
       default:
         break;
     }
   }
 }
 
-bool vendorRequestCallback(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request){
-    static const char * vendorRequestTypes[] = {"Standard", "Class", "Vendor"};
-    static const char * vendorRequestRecipients[] = {"Device", "Interface", "Endpoint", "Other"};
-    static const char * vendorStages[] = {"Setup","Data","Ack"};
-    HWSerial.printf("Vendor Request: Stage: %5s, Direction: %3s, Type: %8s, Recipient: %9s, bRequest: 0x%02x, wValue: 0x%04x, wIndex: %u, wLength: %u\n", vendorStages[stage], 
-        request->bmRequestType_bit.direction?"IN":"OUT", 
-        vendorRequestTypes[request->bmRequestType_bit.type], 
-        vendorRequestRecipients[request->bmRequestType_bit.recipient], 
-        request->bRequest, request->wValue, request->wIndex, request->wLength);
-    
-    
-    static uint8_t vendor_line_state = 0;// Bit 0:  DTR (Data Terminal Ready), Bit 1: RTS (Request to Send)
-    static cdc_line_coding_t vendor_line_coding;
-    bool result = false;
-
-    if(request->bmRequestType_bit.direction == TUSB_DIR_OUT && request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE && request->bRequest == 0x0b){
-        if(stage == CONTROL_STAGE_SETUP) {
-            // response with status OK
-            result = Vendor.sendResponse(rhport, request);
-        } else {
-            result = true;
-        }
-    } else if(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS && request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE){
-        //Implement CDC Control Requests
-        switch (request->bRequest) {
-
-            // CDC Set Line Coding
-            case CDC_REQUEST_SET_LINE_CODING: //0x20
-                if(request->wLength != sizeof(cdc_line_coding_t) || request->bmRequestType_bit.direction != TUSB_DIR_OUT){
-                    break;
-                }
-                if(stage == CONTROL_STAGE_SETUP) {
-                    //Send the response in setup stage (it will write the data to vendor_line_coding in the DATA stage)
-                    result = Vendor.sendResponse(rhport, request, (void*) &vendor_line_coding, sizeof(cdc_line_coding_t));
-                } else if(stage == CONTROL_STAGE_ACK){
-                    //In the ACK stage the requst->response is complete
-                    HWSerial.printf("Vendor Line Coding: bit_rate: %u, data_bits: %u, stop_bits: %u, parity: %u\n", vendor_line_coding.bit_rate, vendor_line_coding.data_bits, vendor_line_coding.stop_bits, vendor_line_coding.parity);
-                }
-                result = true;
-            break;
+static const char * strRequestDirections[] = {"OUT", "IN"};
+static const char * strRequestTypes[] = {"STANDARD", "CLASS", "VENDOR", "INVALID"};
+static const char * strRequestRecipients[] = {"DEVICE", "INTERFACE", "ENDPOINT", "OTHER"};
+static const char * strRequestStages[] = {"SETUP", "DATA", "ACK"};
+
+//Handle USB requests to the vendor interface
+bool vendorRequestCallback(uint8_t rhport, uint8_t requestStage, arduino_usb_control_request_t const * request) {
+  HWSerial.printf("Vendor Request: Stage: %5s, Direction: %3s, Type: %8s, Recipient: %9s, bRequest: 0x%02x, wValue: 0x%04x, wIndex: %u, wLength: %u\n", 
+    strRequestStages[requestStage],
+    strRequestDirections[request->bmRequestDirection],
+    strRequestTypes[request->bmRequestType],
+    strRequestRecipients[request->bmRequestRecipient],
+    request->bRequest, request->wValue, request->wIndex, request->wLength);
 
-            // CDC Get Line Coding
-            case CDC_REQUEST_GET_LINE_CODING: //0x21
-                if(request->wLength != sizeof(cdc_line_coding_t) || request->bmRequestType_bit.direction != TUSB_DIR_IN){
-                    break;
-                }
-                if(stage == CONTROL_STAGE_SETUP) {
-                    result = Vendor.sendResponse(rhport, request, (void*) &vendor_line_coding, sizeof(cdc_line_coding_t));
-                }
-                result = true;
+  bool result = false;
+
+  if (request->bmRequestDirection == REQUEST_DIRECTION_OUT &&
+      request->bmRequestType == REQUEST_TYPE_STANDARD &&
+      request->bmRequestRecipient == REQUEST_RECIPIENT_INTERFACE &&
+      request->bRequest == 0x0b
+     ) {
+    if (requestStage == REQUEST_STAGE_SETUP) {
+      // response with status OK
+      result = Vendor.sendResponse(rhport, request);
+    } else {
+      result = true;
+    }
+  } else
+    //Implement CDC Control Requests
+    if (request->bmRequestType == REQUEST_TYPE_CLASS && request->bmRequestRecipient == REQUEST_RECIPIENT_DEVICE) {
+      switch (request->bRequest) {
+
+        case REQUEST_SET_LINE_CODING: //0x20
+          // Accept only direction OUT with data size 7
+          if (request->wLength != sizeof(request_line_coding_t) || request->bmRequestDirection != REQUEST_DIRECTION_OUT) {
             break;
+          }
+          if (requestStage == REQUEST_STAGE_SETUP) {
+            //Send the response in setup stage (it will write the data to vendor_line_coding in the DATA stage)
+            result = Vendor.sendResponse(rhport, request, (void*) &vendor_line_coding, sizeof(request_line_coding_t));
+          } else if (requestStage == REQUEST_STAGE_ACK) {
+            //In the ACK stage the response is complete
+            HWSerial.printf("Vendor Line Coding: bit_rate: %u, data_bits: %u, stop_bits: %u, parity: %u\n", vendor_line_coding.bit_rate, vendor_line_coding.data_bits, vendor_line_coding.stop_bits, vendor_line_coding.parity);
+          }
+          result = true;
+          break;
 
-            // CDC Set Line State
-            case CDC_REQUEST_SET_CONTROL_LINE_STATE: //0x22
-                if(request->wLength != 0 || request->bmRequestType_bit.direction != TUSB_DIR_OUT){
-                    break;
-                }
-                if(stage == CONTROL_STAGE_SETUP) {
-                    vendor_line_state = request->wValue;
-                    result = Vendor.sendResponse(rhport, request);
-                } else if(stage == CONTROL_STAGE_ACK){
-                    bool dtr = tu_bit_test(vendor_line_state, 0);
-                    bool rts = tu_bit_test(vendor_line_state, 1);
-                    HWSerial.printf("Vendor Line State: dtr: %u, rts: %u\n", dtr, rts);
-                }
-                result = true;
+        case REQUEST_GET_LINE_CODING: //0x21
+          // Accept only direction IN with data size 7
+          if (request->wLength != sizeof(request_line_coding_t) || request->bmRequestDirection != REQUEST_DIRECTION_IN) {
             break;
+          }
+          if (requestStage == REQUEST_STAGE_SETUP) {
+            //Send the response in setup stage (it will write the data to vendor_line_coding in the DATA stage)
+            result = Vendor.sendResponse(rhport, request, (void*) &vendor_line_coding, sizeof(request_line_coding_t));
+          }
+          result = true;
+          break;
 
-            default:
-                // stall unknown request
+        case REQUEST_SET_CONTROL_LINE_STATE: //0x22
+          // Accept only direction OUT with data size 0
+          if (request->wLength != 0 || request->bmRequestDirection != REQUEST_DIRECTION_OUT) {
             break;
-        }
+          }
+          if (requestStage == REQUEST_STAGE_SETUP) {
+            //Send the response in setup stage
+            vendor_line_state = request->wValue;
+            result = Vendor.sendResponse(rhport, request);
+          } else if (requestStage == REQUEST_STAGE_ACK) {
+            //In the ACK stage the response is complete
+            bool dtr = (vendor_line_state & 1) != 0;
+            bool rts = (vendor_line_state & 2) != 0;
+            HWSerial.printf("Vendor Line State: dtr: %u, rts: %u\n", dtr, rts);
+          }
+          result = true;
+          break;
+
+        default:
+          // stall unknown request
+          break;
+      }
     }
 
-    return result;
+  return result;
 }
 
 void setup() {
   pinMode(buttonPin, INPUT_PULLUP);
   HWSerial.begin(115200);
   HWSerial.setDebugOutput(true);
-  
+
   Vendor.onEvent(usbEventCallback);
   Vendor.onRequest(vendorRequestCallback);
   Vendor.begin();
-  
+
   USB.onEvent(usbEventCallback);
   USB.webUSB(true);
   USB.webUSBURL("http://localhost/webusb");
@@ -143,6 +168,7 @@ void setup() {
 }
 
 void loop() {
+  static int previousButtonState = HIGH;
   int buttonState = digitalRead(buttonPin);
   if (buttonState != previousButtonState) {
     previousButtonState = buttonState;
@@ -155,11 +181,11 @@ void loop() {
     }
     delay(100);
   }
-  
-  while(HWSerial.available()){
+
+  while (HWSerial.available()) {
     size_t l = HWSerial.available();
     uint8_t b[l];
     l = HWSerial.read(b, l);
-    Vendor.write(b,l);
+    Vendor.write(b, l);
   }
 }
diff --git a/libraries/USB/src/USBVendor.cpp b/libraries/USB/src/USBVendor.cpp
index 26a75c547fa..0a91fac9354 100644
--- a/libraries/USB/src/USBVendor.cpp
+++ b/libraries/USB/src/USBVendor.cpp
@@ -21,6 +21,10 @@ ESP_EVENT_DEFINE_BASE(ARDUINO_USB_VENDOR_EVENTS);
 esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait);
 esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg);
 
+static USBVendor * _Vendor = NULL;
+static xQueueHandle rx_queue = NULL;
+static uint8_t USB_VENDOR_ENDPOINT_SIZE = 64;
+
 uint16_t tusb_vendor_load_descriptor(uint8_t * dst, uint8_t * itf)
 {
     uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB Vendor");
@@ -28,16 +32,13 @@ uint16_t tusb_vendor_load_descriptor(uint8_t * dst, uint8_t * itf)
     TU_VERIFY (ep_num != 0);
     uint8_t descriptor[TUD_VENDOR_DESC_LEN] = {
         // Interface number, string index, EP Out & IN address, EP size
-        TUD_VENDOR_DESCRIPTOR(*itf, str_index, ep_num, (uint8_t)(0x80 | ep_num), 64)
+        TUD_VENDOR_DESCRIPTOR(*itf, str_index, ep_num, (uint8_t)(0x80 | ep_num), USB_VENDOR_ENDPOINT_SIZE)
     };
     *itf+=1;
     memcpy(dst, descriptor, TUD_VENDOR_DESC_LEN);
     return TUD_VENDOR_DESC_LEN;
 }
 
-static USBVendor * _Vendor = NULL;
-static xQueueHandle rx_queue = NULL;
-
 void tud_vendor_rx_cb(uint8_t itf){
     log_v("%u", len);
     size_t len = tud_vendor_n_available(itf);
@@ -62,15 +63,21 @@ extern "C" bool tinyusb_vendor_control_request_cb(uint8_t rhport, uint8_t stage,
         request->bRequest, request->wValue, request->wIndex, request->wLength);
 
     if(_Vendor) {
-        return _Vendor->_onRequest(rhport, stage, request);
+        return _Vendor->_onRequest(rhport, stage, (arduino_usb_control_request_t const *)request);
     }
     return false;
 }
 
-USBVendor::USBVendor():itf(0), cb(NULL){
+USBVendor::USBVendor(uint8_t endpoint_size):itf(0), cb(NULL){
     if(!_Vendor){
         _Vendor = this;
+        if(endpoint_size <= 64){
+            USB_VENDOR_ENDPOINT_SIZE = endpoint_size;
+        }
         tinyusb_enable_interface(USB_INTERFACE_VENDOR, TUD_VENDOR_DESC_LEN, tusb_vendor_load_descriptor);
+    } else {
+        itf = _Vendor->itf;
+        cb = _Vendor->cb;
     }
 }
 
@@ -109,14 +116,14 @@ bool USBVendor::mounted(){
     return tud_vendor_n_mounted(itf);
 }
 
-bool USBVendor::sendResponse(uint8_t rhport, tusb_control_request_t const * request, void * data, size_t len){
+bool USBVendor::sendResponse(uint8_t rhport, arduino_usb_control_request_t const * request, void * data, size_t len){
     if(!request){
         return false;
     }
     if(!data || !len){
-        return tud_control_status(rhport, request);
+        return tud_control_status(rhport, (tusb_control_request_t const *)request);
     } else {
-        return tud_control_xfer(rhport, request, data, len);
+        return tud_control_xfer(rhport, (tusb_control_request_t const *)request, data, len);
     }
 }
 
@@ -124,7 +131,7 @@ void USBVendor::onRequest(arduino_usb_vendor_control_request_handler_t handler){
     cb = handler;
 }
 
-bool USBVendor::_onRequest(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request){
+bool USBVendor::_onRequest(uint8_t rhport, uint8_t stage, arduino_usb_control_request_t const * request){
     if(cb){
         return cb(rhport, stage, request);
     }
diff --git a/libraries/USB/src/USBVendor.h b/libraries/USB/src/USBVendor.h
index b641fd9de31..2016a76a790 100644
--- a/libraries/USB/src/USBVendor.h
+++ b/libraries/USB/src/USBVendor.h
@@ -18,10 +18,38 @@
 
 #if CONFIG_TINYUSB_VENDOR_ENABLED
 #include "esp_event.h"
-#include "common/tusb_common.h"
 
 ESP_EVENT_DECLARE_BASE(ARDUINO_USB_VENDOR_EVENTS);
 
+#define REQUEST_STAGE_SETUP         0
+#define REQUEST_STAGE_DATA          1
+#define REQUEST_STAGE_ACK           2
+
+#define REQUEST_TYPE_STANDARD       0
+#define REQUEST_TYPE_CLASS          1
+#define REQUEST_TYPE_VENDOR         2
+#define REQUEST_TYPE_INVALID        3
+
+#define REQUEST_RECIPIENT_DEVICE    0
+#define REQUEST_RECIPIENT_INTERFACE 1
+#define REQUEST_RECIPIENT_ENDPOINT  2
+#define REQUEST_RECIPIENT_OTHER     3
+
+#define REQUEST_DIRECTION_OUT       0
+#define REQUEST_DIRECTION_IN        1
+
+typedef struct __attribute__ ((packed)) {
+    struct __attribute__ ((packed)) {
+        uint8_t bmRequestRecipient :  5;
+        uint8_t bmRequestType      :  2;
+        uint8_t bmRequestDirection :  1;
+    };
+    uint8_t  bRequest;
+    uint16_t wValue;
+    uint16_t wIndex;
+    uint16_t wLength;
+} arduino_usb_control_request_t;
+
 typedef enum {
     ARDUINO_USB_VENDOR_ANY_EVENT = ESP_EVENT_ANY_ID,
     ARDUINO_USB_VENDOR_DATA_EVENT,
@@ -34,14 +62,14 @@ typedef union {
     } data;
 } arduino_usb_vendor_event_data_t;
 
-typedef bool (*arduino_usb_vendor_control_request_handler_t)(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
+typedef bool (*arduino_usb_vendor_control_request_handler_t)(uint8_t rhport, uint8_t stage, arduino_usb_control_request_t const * request);
 
 class USBVendor: public Stream {
 private:
     uint8_t itf;
     arduino_usb_vendor_control_request_handler_t cb;
 public:
-    USBVendor();
+    USBVendor(uint8_t endpoint_size=64);
     void begin(void);
     void end(void);
     size_t setRxBufferSize(size_t);
@@ -57,9 +85,9 @@ class USBVendor: public Stream {
     void onEvent(esp_event_handler_t callback);
     void onEvent(arduino_usb_vendor_event_t event, esp_event_handler_t callback);
     void onRequest(arduino_usb_vendor_control_request_handler_t handler);
-    bool sendResponse(uint8_t rhport, tusb_control_request_t const * request, void * data=NULL, size_t len=0);
+    bool sendResponse(uint8_t rhport, arduino_usb_control_request_t const * request, void * data=NULL, size_t len=0);
 
-    bool _onRequest(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
+    bool _onRequest(uint8_t rhport, uint8_t stage, arduino_usb_control_request_t const * request);
     void _onRX(const uint8_t* buffer, size_t len);
 };
 

From bae30affc092a987b7db494096e8cb3a7ab84610 Mon Sep 17 00:00:00 2001
From: me-no-dev <hristo@espressif.com>
Date: Mon, 23 Aug 2021 15:37:01 +0300
Subject: [PATCH 17/17] Convert tabs to spaces

---
 libraries/USB/src/USBHIDConsumerControl.cpp | 10 +++++-----
 libraries/USB/src/USBHIDGamepad.cpp         | 10 +++++-----
 libraries/USB/src/USBHIDMouse.cpp           | 10 +++++-----
 libraries/USB/src/USBHIDSystemControl.cpp   | 10 +++++-----
 libraries/USB/src/USBHIDVendor.cpp          | 10 +++++-----
 5 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/libraries/USB/src/USBHIDConsumerControl.cpp b/libraries/USB/src/USBHIDConsumerControl.cpp
index 7977fdd1f24..b1863dee3f3 100644
--- a/libraries/USB/src/USBHIDConsumerControl.cpp
+++ b/libraries/USB/src/USBHIDConsumerControl.cpp
@@ -22,11 +22,11 @@ static const uint8_t report_descriptor[] = {
 };
 
 USBHIDConsumerControl::USBHIDConsumerControl(): hid(){
-	static bool initialized = false;
-	if(!initialized){
-		initialized = true;
-		hid.addDevice(this, sizeof(report_descriptor));
-	}
+    static bool initialized = false;
+    if(!initialized){
+        initialized = true;
+        hid.addDevice(this, sizeof(report_descriptor));
+    }
 }
 
 uint16_t USBHIDConsumerControl::_onGetDescriptor(uint8_t* dst){
diff --git a/libraries/USB/src/USBHIDGamepad.cpp b/libraries/USB/src/USBHIDGamepad.cpp
index 57a2f028356..9ddf67f4a99 100644
--- a/libraries/USB/src/USBHIDGamepad.cpp
+++ b/libraries/USB/src/USBHIDGamepad.cpp
@@ -22,11 +22,11 @@ static const uint8_t report_descriptor[] = {
 };
 
 USBHIDGamepad::USBHIDGamepad(): hid(), _x(0), _y(0), _z(0), _rz(0), _rx(0), _ry(0), _hat(0), _buttons(0){
-	static bool initialized = false;
-	if(!initialized){
-		initialized = true;
-		hid.addDevice(this, sizeof(report_descriptor));
-	}
+    static bool initialized = false;
+    if(!initialized){
+        initialized = true;
+        hid.addDevice(this, sizeof(report_descriptor));
+    }
 }
 
 uint16_t USBHIDGamepad::_onGetDescriptor(uint8_t* dst){
diff --git a/libraries/USB/src/USBHIDMouse.cpp b/libraries/USB/src/USBHIDMouse.cpp
index 92efd617661..30b802a4607 100644
--- a/libraries/USB/src/USBHIDMouse.cpp
+++ b/libraries/USB/src/USBHIDMouse.cpp
@@ -29,11 +29,11 @@ static const uint8_t report_descriptor[] = {
 };
 
 USBHIDMouse::USBHIDMouse(): hid(), _buttons(0){
-	static bool initialized = false;
-	if(!initialized){
-		initialized = true;
-		hid.addDevice(this, sizeof(report_descriptor));
-	}
+    static bool initialized = false;
+    if(!initialized){
+        initialized = true;
+        hid.addDevice(this, sizeof(report_descriptor));
+    }
 }
 
 uint16_t USBHIDMouse::_onGetDescriptor(uint8_t* dst){
diff --git a/libraries/USB/src/USBHIDSystemControl.cpp b/libraries/USB/src/USBHIDSystemControl.cpp
index 6bd165619d9..93ef897fd69 100644
--- a/libraries/USB/src/USBHIDSystemControl.cpp
+++ b/libraries/USB/src/USBHIDSystemControl.cpp
@@ -22,11 +22,11 @@ static const uint8_t report_descriptor[] = {
 };
 
 USBHIDSystemControl::USBHIDSystemControl(): hid(){
-	static bool initialized = false;
-	if(!initialized){
-		initialized = true;
-		hid.addDevice(this, sizeof(report_descriptor));
-	}
+    static bool initialized = false;
+    if(!initialized){
+        initialized = true;
+        hid.addDevice(this, sizeof(report_descriptor));
+    }
 }
 
 uint16_t USBHIDSystemControl::_onGetDescriptor(uint8_t* dst){
diff --git a/libraries/USB/src/USBHIDVendor.cpp b/libraries/USB/src/USBHIDVendor.cpp
index 0a6495abe71..53b0e999d44 100644
--- a/libraries/USB/src/USBHIDVendor.cpp
+++ b/libraries/USB/src/USBHIDVendor.cpp
@@ -63,16 +63,16 @@ static xQueueHandle rx_queue = NULL;
 static bool prepend_size = false;
 
 USBHIDVendor::USBHIDVendor(uint8_t report_size, bool prepend): hid(){
-	static bool initialized = false;
-	if(!initialized){
-		initialized = true;
-		hid.addDevice(this, TUD_HID_REPORT_DESC_GENERIC_INOUT_FEATURE_LEN);
+    static bool initialized = false;
+    if(!initialized){
+        initialized = true;
+        hid.addDevice(this, TUD_HID_REPORT_DESC_GENERIC_INOUT_FEATURE_LEN);
         memset(feature, 0, 64);
         if(report_size < 64){
             HID_VENDOR_REPORT_SIZE = report_size;
         }
         prepend_size = prepend;
-	}
+    }
 }
 
 uint16_t USBHIDVendor::_onGetDescriptor(uint8_t* dst){