diff --git a/.github/workflows/githubci.yml b/.github/workflows/githubci.yml index fb3b5e14..bed122b3 100644 --- a/.github/workflows/githubci.yml +++ b/.github/workflows/githubci.yml @@ -16,13 +16,13 @@ jobs: python-version: '3.x' - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Run pre-commit uses: pre-commit/action@v3.0.0 - name: Checkout adafruit/ci-arduino - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: adafruit/ci-arduino path: ci @@ -46,17 +46,15 @@ jobs: fail-fast: false matrix: arduino-platform: - # ESP32S3 - - 'feather_esp32s3' - # ESP32S2 + # ESP32 - 'feather_esp32s2' + - 'feather_esp32s3' # nRF52 - 'cpb' - 'nrf52840' # RP2040 - 'feather_rp2040_tinyusb' # SAMD - - 'feather_m4_can_tinyusb' - 'metro_m0_tinyusb' - 'metro_m4_tinyusb' @@ -67,10 +65,10 @@ jobs: python-version: '3.x' - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Checkout adafruit/ci-arduino - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: adafruit/ci-arduino path: ci diff --git a/examples/Host/Simple/host_device_info/.pico_rp2040_tinyusb_host.test.only b/examples/Host/Simple/host_device_info/.pico_rp2040_tinyusb_host.test.only new file mode 100644 index 00000000..e69de29b diff --git a/examples/Host/Simple/host_device_info/host_device_info.ino b/examples/Host/Simple/host_device_info/host_device_info.ino new file mode 100644 index 00000000..38a8220b --- /dev/null +++ b/examples/Host/Simple/host_device_info/host_device_info.ino @@ -0,0 +1,223 @@ +/********************************************************************* + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing + products from Adafruit! + + MIT license, check LICENSE for more information + Copyright (c) 2019 Ha Thach for Adafruit Industries + All text above, and the splash screen below must be included in + any redistribution +*********************************************************************/ + +/* This example demonstrates use of native controller as host (TinyUSB Host) + * Note: + * - For most mcu with only 1 native usb, Serial is not available. We will use Serial1 instead + * + * Host example will get device descriptors of attached devices and print it out as follows: + Device 1: ID 046d:c52f + Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 0200 + bDeviceClass 0 + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 8 + idVendor 0x046d + idProduct 0xc52f + bcdDevice 2200 + iManufacturer 1 Logitech + iProduct 2 USB Receiver + iSerialNumber 0 + bNumConfigurations 1 + * + */ + +#include "Adafruit_TinyUSB.h" + +#ifndef USE_TINYUSB_HOST + #error This example requires usb stack configured as host in "Tools -> USB Stack -> Adafruit TinyUSB Host" +#endif + +// Language ID: English +#define LANGUAGE_ID 0x0409 + +typedef struct { + tusb_desc_device_t desc_device; + uint16_t manufacturer[32]; + uint16_t product[48]; + uint16_t serial[16]; + bool mounted; +} dev_info_t; + +// CFG_TUH_DEVICE_MAX is defined by tusb_config header +dev_info_t dev_info[CFG_TUH_DEVICE_MAX] = { 0 }; + +Adafruit_USBH_Host USBHost; + +void setup() { + Serial1.begin(115200); + Serial1.println("TinyUSB Host: Device Info Example"); + + // Init USB Host on native controller roothub port0 + USBHost.begin(0); +} + +//------------- Core0 -------------// +void loop() { + USBHost.task(); + Serial1.flush(); +} + +//--------------------------------------------------------------------+ +// TinyUSB Host callbacks +//--------------------------------------------------------------------+ +void print_device_descriptor(tuh_xfer_t *xfer); + +void utf16_to_utf8(uint16_t *temp_buf, size_t buf_len); + +void print_lsusb(void) { + bool no_device = true; + for (uint8_t daddr = 1; daddr < CFG_TUH_DEVICE_MAX + 1; daddr++) { + // TODO can use tuh_mounted(daddr), but tinyusb has an bug + // use local connected flag instead + dev_info_t *dev = &dev_info[daddr - 1]; + if (dev->mounted) { + Serial1.printf("Device %u: ID %04x:%04x %s %s\r\n", daddr, + dev->desc_device.idVendor, dev->desc_device.idProduct, + (char *) dev->manufacturer, (char *) dev->product); + + no_device = false; + } + } + + if (no_device) { + Serial1.println("No device connected (except hub)"); + } +} + +// Invoked when device is mounted (configured) +void tuh_mount_cb(uint8_t daddr) { + Serial1.printf("Device attached, address = %d\r\n", daddr); + + dev_info_t *dev = &dev_info[daddr - 1]; + dev->mounted = true; + + // Get Device Descriptor + tuh_descriptor_get_device(daddr, &dev->desc_device, 18, print_device_descriptor, 0); +} + +/// Invoked when device is unmounted (bus reset/unplugged) +void tuh_umount_cb(uint8_t daddr) { + Serial1.printf("Device removed, address = %d\r\n", daddr); + dev_info_t *dev = &dev_info[daddr - 1]; + dev->mounted = false; + + // print device summary + print_lsusb(); +} + +void print_device_descriptor(tuh_xfer_t *xfer) { + if (XFER_RESULT_SUCCESS != xfer->result) { + Serial1.printf("Failed to get device descriptor\r\n"); + return; + } + + uint8_t const daddr = xfer->daddr; + dev_info_t *dev = &dev_info[daddr - 1]; + tusb_desc_device_t *desc = &dev->desc_device; + + Serial1.printf("Device %u: ID %04x:%04x\r\n", daddr, desc->idVendor, desc->idProduct); + Serial1.printf("Device Descriptor:\r\n"); + Serial1.printf(" bLength %u\r\n" , desc->bLength); + Serial1.printf(" bDescriptorType %u\r\n" , desc->bDescriptorType); + Serial1.printf(" bcdUSB %04x\r\n" , desc->bcdUSB); + Serial1.printf(" bDeviceClass %u\r\n" , desc->bDeviceClass); + Serial1.printf(" bDeviceSubClass %u\r\n" , desc->bDeviceSubClass); + Serial1.printf(" bDeviceProtocol %u\r\n" , desc->bDeviceProtocol); + Serial1.printf(" bMaxPacketSize0 %u\r\n" , desc->bMaxPacketSize0); + Serial1.printf(" idVendor 0x%04x\r\n" , desc->idVendor); + Serial1.printf(" idProduct 0x%04x\r\n" , desc->idProduct); + Serial1.printf(" bcdDevice %04x\r\n" , desc->bcdDevice); + + // Get String descriptor using Sync API + Serial1.printf(" iManufacturer %u ", desc->iManufacturer); + if (XFER_RESULT_SUCCESS == + tuh_descriptor_get_manufacturer_string_sync(daddr, LANGUAGE_ID, dev->manufacturer, sizeof(dev->manufacturer))) { + utf16_to_utf8(dev->manufacturer, sizeof(dev->manufacturer)); + Serial1.printf((char *) dev->manufacturer); + } + Serial1.printf("\r\n"); + + Serial1.printf(" iProduct %u ", desc->iProduct); + if (XFER_RESULT_SUCCESS == + tuh_descriptor_get_product_string_sync(daddr, LANGUAGE_ID, dev->product, sizeof(dev->product))) { + utf16_to_utf8(dev->product, sizeof(dev->product)); + Serial1.printf((char *) dev->product); + } + Serial1.printf("\r\n"); + + Serial1.printf(" iSerialNumber %u ", desc->iSerialNumber); + if (XFER_RESULT_SUCCESS == + tuh_descriptor_get_serial_string_sync(daddr, LANGUAGE_ID, dev->serial, sizeof(dev->serial))) { + utf16_to_utf8(dev->serial, sizeof(dev->serial)); + Serial1.printf((char *) dev->serial); + } + Serial1.printf("\r\n"); + + Serial1.printf(" bNumConfigurations %u\r\n", desc->bNumConfigurations); + + // print device summary + print_lsusb(); +} + +//--------------------------------------------------------------------+ +// String Descriptor Helper +//--------------------------------------------------------------------+ + +static void _convert_utf16le_to_utf8(const uint16_t *utf16, size_t utf16_len, uint8_t *utf8, size_t utf8_len) { + // TODO: Check for runover. + (void) utf8_len; + // Get the UTF-16 length out of the data itself. + + for (size_t i = 0; i < utf16_len; i++) { + uint16_t chr = utf16[i]; + if (chr < 0x80) { + *utf8++ = chr & 0xff; + } else if (chr < 0x800) { + *utf8++ = (uint8_t) (0xC0 | (chr >> 6 & 0x1F)); + *utf8++ = (uint8_t) (0x80 | (chr >> 0 & 0x3F)); + } else { + // TODO: Verify surrogate. + *utf8++ = (uint8_t) (0xE0 | (chr >> 12 & 0x0F)); + *utf8++ = (uint8_t) (0x80 | (chr >> 6 & 0x3F)); + *utf8++ = (uint8_t) (0x80 | (chr >> 0 & 0x3F)); + } + // TODO: Handle UTF-16 code points that take two entries. + } +} + +// Count how many bytes a utf-16-le encoded string will take in utf-8. +static int _count_utf8_bytes(const uint16_t *buf, size_t len) { + size_t total_bytes = 0; + for (size_t i = 0; i < len; i++) { + uint16_t chr = buf[i]; + if (chr < 0x80) { + total_bytes += 1; + } else if (chr < 0x800) { + total_bytes += 2; + } else { + total_bytes += 3; + } + // TODO: Handle UTF-16 code points that take two entries. + } + return total_bytes; +} + +void utf16_to_utf8(uint16_t *temp_buf, size_t buf_len) { + size_t utf16_len = ((temp_buf[0] & 0xff) - 2) / sizeof(uint16_t); + size_t utf8_len = _count_utf8_bytes(temp_buf + 1, utf16_len); + + _convert_utf16le_to_utf8(temp_buf + 1, utf16_len, (uint8_t *) temp_buf, buf_len); + ((uint8_t *) temp_buf)[utf8_len] = '\0'; +} diff --git a/src/arduino/Adafruit_USBD_CDC.cpp b/src/arduino/Adafruit_USBD_CDC.cpp index f6338962..9dedff43 100644 --- a/src/arduino/Adafruit_USBD_CDC.cpp +++ b/src/arduino/Adafruit_USBD_CDC.cpp @@ -24,7 +24,8 @@ #include "tusb_option.h" -#if CFG_TUD_ENABLED && CFG_TUD_CDC && !defined(ARDUINO_ARCH_ESP32) +// esp32 use built-in core cdc +#if CFG_TUD_CDC && !defined(ARDUINO_ARCH_ESP32) #include "Arduino.h" @@ -37,19 +38,16 @@ #define TINYUSB_API_VERSION 0 #endif -// Starting endpoints; adjusted elsewhere as needed -#define EPOUT 0x00 -#define EPIN 0x80 - // SerialTinyUSB can be macro expanding to "Serial" on supported cores Adafruit_USBD_CDC SerialTinyUSB; -//------------- Static member -------------// uint8_t Adafruit_USBD_CDC::_instance_count = 0; +Adafruit_USBD_CDC::Adafruit_USBD_CDC(void) { _instance = INVALID_INSTANCE; } -uint8_t Adafruit_USBD_CDC::getInstanceCount(void) { return _instance_count; } +#if CFG_TUD_ENABLED -Adafruit_USBD_CDC::Adafruit_USBD_CDC(void) { _instance = INVALID_INSTANCE; } +#define EPOUT 0x00 +#define EPIN 0x80 uint16_t Adafruit_USBD_CDC::getInterfaceDescriptor(uint8_t itfnum, uint8_t *buf, uint16_t bufsize) { @@ -265,4 +263,60 @@ void tud_cdc_line_state_cb(uint8_t instance, bool dtr, bool rts) { } } +#else + +// Device stack is not enabled (probably in host mode) +#warning "NO_USB selected. No output to Serial will occur!" + +uint16_t Adafruit_USBD_CDC::getInterfaceDescriptor(uint8_t itfnum, uint8_t *buf, + uint16_t bufsize) { + (void)itfnum; + (void)buf; + (void)bufsize; + + return 0; +} + +// Baud and config is ignore in CDC +void Adafruit_USBD_CDC::begin(uint32_t baud) { (void)baud; } + +void Adafruit_USBD_CDC::begin(uint32_t baud, uint8_t config) { (void)config; } + +void Adafruit_USBD_CDC::end(void) {} + +uint32_t Adafruit_USBD_CDC::baud(void) { return 0; } + +uint8_t Adafruit_USBD_CDC::stopbits(void) { return 0; } + +uint8_t Adafruit_USBD_CDC::paritytype(void) { return 0; } + +uint8_t Adafruit_USBD_CDC::numbits(void) { return 0; } + +int Adafruit_USBD_CDC::dtr(void) { return 0; } + +Adafruit_USBD_CDC::operator bool() { return false; } + +int Adafruit_USBD_CDC::available(void) { return 0; } + +int Adafruit_USBD_CDC::peek(void) { return -1; } + +int Adafruit_USBD_CDC::read(void) { return -1; } + +size_t Adafruit_USBD_CDC::read(uint8_t *buffer, size_t size) { + (void)buffer; + (void)size; + return 0; +} + +void Adafruit_USBD_CDC::flush(void) {} + +size_t Adafruit_USBD_CDC::write(uint8_t ch) { return -1; } + +size_t Adafruit_USBD_CDC::write(const uint8_t *buffer, size_t size) { + return 0; +} + +int Adafruit_USBD_CDC::availableForWrite(void) { return 0; } + #endif // CFG_TUD_ENABLED +#endif // CDC + ESP32 diff --git a/src/arduino/Adafruit_USBD_CDC.h b/src/arduino/Adafruit_USBD_CDC.h index a3d9d025..d938c004 100644 --- a/src/arduino/Adafruit_USBD_CDC.h +++ b/src/arduino/Adafruit_USBD_CDC.h @@ -43,7 +43,7 @@ class Adafruit_USBD_CDC : public Stream, public Adafruit_USBD_Interface { public: Adafruit_USBD_CDC(void); - static uint8_t getInstanceCount(void); + static uint8_t getInstanceCount(void) { return _instance_count; } // from Adafruit_USBD_Interface virtual uint16_t getInterfaceDescriptor(uint8_t itfnum, uint8_t *buf, diff --git a/src/arduino/Adafruit_USBH_Host.h b/src/arduino/Adafruit_USBH_Host.h index 3eb60db1..8daa8a71 100644 --- a/src/arduino/Adafruit_USBH_Host.h +++ b/src/arduino/Adafruit_USBH_Host.h @@ -33,6 +33,7 @@ #include "esp32-hal-tinyusb.h" #endif +#if defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421 extern "C" { void tuh_max3421_spi_cs_api(uint8_t rhport, bool active); bool tuh_max3421_spi_xfer_api(uint8_t rhport, uint8_t const *tx_buf, @@ -41,8 +42,6 @@ void tuh_max3421_int_api(uint8_t rhport, bool enabled); } class Adafruit_USBH_Host { - -#if defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421 private: SPIClass *_spi; int8_t _cs; @@ -67,6 +66,15 @@ class Adafruit_USBH_Host { enum { IOPINS2_ADDR = 21u << 3 }; // 0xA8 return max3421_writeRegister(IOPINS2_ADDR, data, in_isr); } + +private: + friend void tuh_max3421_spi_cs_api(uint8_t rhport, bool active); + friend bool tuh_max3421_spi_xfer_api(uint8_t rhport, uint8_t const *tx_buf, + uint8_t *rx_buf, size_t xfer_bytes); + friend void tuh_max3421_int_api(uint8_t rhport, bool enabled); +#else + +class Adafruit_USBH_Host { #endif public: @@ -87,11 +95,6 @@ class Adafruit_USBH_Host { private: uint8_t _rhport; - - friend void tuh_max3421_spi_cs_api(uint8_t rhport, bool active); - friend bool tuh_max3421_spi_xfer_api(uint8_t rhport, uint8_t const *tx_buf, - uint8_t *rx_buf, size_t xfer_bytes); - friend void tuh_max3421_int_api(uint8_t rhport, bool enabled); }; #endif diff --git a/src/arduino/ports/rp2040/tusb_config_rp2040.h b/src/arduino/ports/rp2040/tusb_config_rp2040.h index c3c68e76..d8a29df0 100644 --- a/src/arduino/ports/rp2040/tusb_config_rp2040.h +++ b/src/arduino/ports/rp2040/tusb_config_rp2040.h @@ -32,9 +32,14 @@ extern "C" { //-------------------------------------------------------------------- // COMMON CONFIGURATION //-------------------------------------------------------------------- -#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE -// Enable device stack +#ifdef USE_TINYUSB_HOST +// native as host +#define CFG_TUD_ENABLED 0 +#define CFG_TUH_ENABLED 1 +#define CFG_TUH_RPI_PIO_USB 0 +#else +// native as device #define CFG_TUD_ENABLED 1 // Enable host stack with pio-usb if Pico-PIO-USB library is available @@ -42,11 +47,15 @@ extern "C" { #define CFG_TUH_ENABLED 1 #define CFG_TUH_RPI_PIO_USB 1 #endif +#endif #ifndef CFG_TUSB_MCU #define CFG_TUSB_MCU OPT_MCU_RP2040 #endif + +#ifndef CFG_TUSB_OS #define CFG_TUSB_OS OPT_OS_PICO +#endif #ifndef CFG_TUSB_DEBUG #define CFG_TUSB_DEBUG 0 @@ -130,7 +139,7 @@ extern "C" { // bit rate = 115200, 1 stop bit, no parity, 8 bit data width // This need Pico-PIO-USB at least 0.5.1 #define CFG_TUH_CDC_LINE_CODING_ON_ENUM \ - { 115200, CDC_LINE_CONDING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 } + { 115200, CDC_LINE_CODING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 } #ifdef __cplusplus }