usb: added a tinyusb component; ci; soc.

ci: ignore s2-only projects from the cmake-make test
soc: refactored the usb part
This commit is contained in:
Andrei Gramakov 2020-03-06 15:04:06 +01:00
parent f02399948d
commit 9d059be165
24 changed files with 2779 additions and 3753 deletions

3
.gitmodules vendored
View file

@ -79,3 +79,6 @@
path = components/esp_wifi/lib
url = ../../espressif/esp32-wifi-lib.git
[submodule "components/tinyusb/tinyusb"]
path = components/tinyusb/tinyusb
url = ../../espressif/tinyusb.git

View file

@ -20,6 +20,7 @@
#include "soc/gpio_sig_map.h"
#include "soc/usb_reg.h"
#include "soc/usb_types.h"
#include "soc/usb_struct.h"
/**
* @brief A pin decriptor for init

File diff suppressed because it is too large Load diff

View file

@ -68,87 +68,6 @@ typedef struct usb_out_ep_reg {
uint32_t reserved3;
} usb_out_endpoint_t;
typedef struct usb_reg {
volatile uint32_t gotgctl; /*!< 0x0 */
volatile uint32_t gotgint;
volatile uint32_t gahbcfg;
volatile uint32_t gusbcfg;
volatile uint32_t grstctl; /*!< 0x10 */
volatile uint32_t gintsts;
volatile uint32_t gintmsk;
volatile uint32_t grxstsr;
volatile uint32_t grxstsp; /*!< 0x20 */
volatile uint32_t grxfsiz;
volatile uint32_t gnptxfsiz;
volatile uint32_t gnptxsts;
volatile uint32_t gi2cctl;
volatile uint32_t gpvndctl; /*!< 0x30 */
volatile uint32_t ggpio;
volatile uint32_t guid;
volatile uint32_t gsnpsid;
volatile uint32_t ghwcfg1; /*!< 0x40 */
volatile uint32_t ghwcfg2;
volatile uint32_t ghwcfg3;
volatile uint32_t ghwcfg4; /*!< 0x50 */
volatile uint32_t glpmcfg; /*!< 0x54 */
volatile uint32_t gpwrdn; /*!< 0x58 */
volatile uint32_t gdfifocfg; /*!< 0x5c */
volatile uint32_t gadpctl; /*!< 0x60 */
uint32_t reserved1[39];
volatile uint32_t hptxfsiz; /*!< 0x100 */
volatile uint32_t dieptxf[15]; /*!< 0x104 */
uint32_t reserved2[176]; /*!< 0x140 */
/**
* The Host Global Registers structure defines the size and relative
* field offsets for the Host Mode Global Registers. Host Global
* Registers offsets 400h-7FFh.
*/
volatile uint32_t hcfg; /*!< Host Configuration Register. <i>Offset: 400h</i> */
volatile uint32_t hfir; /*!< Host Frame Interval Register. <i>Offset: 404h</i> */
volatile uint32_t hfnum; /*!< Host Frame Number / Frame Remaining Register. <i>Offset: 408h</i> */
uint32_t reserved40C; /*!< Reserved. <i>Offset: 40Ch</i> */
volatile uint32_t hptxsts; /*!< Host Periodic Transmit FIFO/ Queue Status Register. <i>Offset: 410h</i> */
volatile uint32_t haint; /*!< Host All Channels Interrupt Register. <i>Offset: 414h</i> */
volatile uint32_t haintmsk; /*!< Host All Channels Interrupt Mask Register. <i>Offset: 418h</i> */
volatile uint32_t hflbaddr; /*!< Host Frame List Base Address Register . <i>Offset: 41Ch</i> */
uint32_t reserved0x420[7];
volatile uint32_t hprt; //0x440
uint32_t reserved0x444[240];
volatile uint32_t dcfg; /*!< Device Configuration Register. <i>Offset 800h</i> */
volatile uint32_t dctl; /*!< Device Control Register. <i>Offset: 804h</i> */
volatile uint32_t dsts; /*!< Device Status Register (Read Only). <i>Offset: 808h</i> */
uint32_t reserved0x80c; /*!< Reserved. <i>Offset: 80Ch</i> */
volatile uint32_t diepmsk; /*!< Device IN Endpoint Common Interrupt Mask Register. <i>Offset: 810h</i> */
volatile uint32_t doepmsk; /*!< Device OUT Endpoint Common Interrupt Mask Register. <i>Offset: 814h</i> */
volatile uint32_t daint; /*!< Device All Endpoints Interrupt Register. <i>Offset: 818h</i> */
volatile uint32_t daintmsk; /*!< Device All Endpoints Interrupt Mask Register. <i>Offset: 81Ch</i> */
volatile uint32_t dtknqr1; /*!< Device IN Token Queue Read Register-1 (Read Only). <i>Offset: 820h</i> */
volatile uint32_t dtknqr2; /*!< Device IN Token Queue Read Register-2 (Read Only). <i>Offset: 824h</i> */
volatile uint32_t dvbusdis; /*!< Device VBUS discharge Register. <i>Offset: 828h</i> */
volatile uint32_t dvbuspulse; /*!< Device VBUS Pulse Register. <i>Offset: 82Ch</i> */
volatile uint32_t dtknqr3_dthrctl; /*!< Device IN Token Queue Read Register-3 (Read Only). Device Thresholding control register (Read/Write) <i>Offset: 830h</i> */
volatile uint32_t dtknqr4_fifoemptymsk; /*!< Device IN Token Queue Read Register-4 (Read Only). Device IN EPs empty Inr. Mask Register (Read/Write)<i>Offset: 834h</i> */
volatile uint32_t deachint; /*!< Device Each Endpoint Interrupt Register (Read Only). <i>Offset: 838h</i> */
volatile uint32_t deachintmsk; /*!< Device Each Endpoint Interrupt mask Register (Read/Write). <i>Offset: 83Ch</i> */
volatile uint32_t diepeachintmsk[16]; /*!< Device Each In Endpoint Interrupt mask Register (Read/Write). <i>Offset: 840h</i> */
volatile uint32_t doepeachintmsk[16]; /*!< Device Each Out Endpoint Interrupt mask Register (Read/Write). <i>Offset: 880h</i> */
uint32_t reserved0x8c0[16];
/* Input Endpoints*/
usb_in_endpoint_t in_ep_reg[USB_IN_EP_NUM]; /*!< 0x900*/
uint32_t reserved6[72];
/* Output Endpoints */
usb_out_endpoint_t out_ep_reg[USB_OUT_EP_NUM];
uint32_t reserved7[136];
uint32_t pcgctrl; /*!<0xe00*/
uint32_t pcgctrl1;
uint8_t reserved8[0x1000 - 0xe08]; /*!<0xd00*/
uint32_t fifo[16][0x400]; /*!<0x1000*/
uint8_t reserved0x11000[0x20000 - 0x11000];
uint32_t dbg_fifo[0x20000]; /*!< 0x20000*/
} usb_dev_t;
extern usb_dev_t USB0;
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,37 @@
idf_component_register( REQUIRES esp_rom freertos soc)
if(CONFIG_USB_ENABLED)
idf_component_get_property( FREERTOS_ORIG_INCLUDE_PATH freertos ORIG_INCLUDE_PATH)
target_compile_options(${COMPONENT_TARGET} INTERFACE
"-DCFG_TUSB_MCU=OPT_MCU_ESP32_S2"
)
target_include_directories(${COMPONENT_TARGET} INTERFACE
"${FREERTOS_ORIG_INCLUDE_PATH}"
# espressif:
"${COMPONENT_DIR}/port/esp32s2/include/"
"${COMPONENT_DIR}/port/common/include"
# tusb:
"${COMPONENT_DIR}/tinyusb/hw/bsp/"
"${COMPONENT_DIR}/tinyusb/src/"
"${COMPONENT_DIR}/tinyusb/src/device"
)
target_sources(${COMPONENT_TARGET} INTERFACE
# espressif:
"${COMPONENT_DIR}/port/common/src/descriptors_control.c"
"${COMPONENT_DIR}/port/common/src/usb_descriptors.c"
"${COMPONENT_DIR}/port/common/src/usbd.c"
"${COMPONENT_DIR}/port/esp32s2/src/device_controller_driver.c"
"${COMPONENT_DIR}/port/esp32s2/src/tinyusb.c"
# tusb:
"${COMPONENT_DIR}/tinyusb/src/common/tusb_fifo.c"
"${COMPONENT_DIR}/tinyusb/src/device/usbd_control.c"
"${COMPONENT_DIR}/tinyusb/src/class/msc/msc_device.c"
"${COMPONENT_DIR}/tinyusb/src/class/cdc/cdc_device.c"
"${COMPONENT_DIR}/tinyusb/src/class/hid/hid_device.c"
"${COMPONENT_DIR}/tinyusb/src/class/midi/midi_device.c"
"${COMPONENT_DIR}/tinyusb/src/tusb.c"
)
endif()

169
components/tinyusb/Kconfig Normal file
View file

@ -0,0 +1,169 @@
menu "TinyUSB"
config USB_ENABLED
bool "Enable TinyUSB driver"
default n
depends on IDF_TARGET_ESP32S2
select FREERTOS_SUPPORT_STATIC_ALLOCATION
select FREERTOS_USE_AUTHENTIC_INCLUDE_PATHS
help
Adds support for TinyUSB
config USB_CDC_ENABLED
bool "Enable USB Serial (CDC) TinyUSB driver"
default n
depends on USB_ENABLED
help
Enable USB Serial (CDC) TinyUSB driver.
config USB_CDC_RX_BUFSIZE
int "CDC FIFO size of RX"
default 64
depends on USB_CDC_ENABLED
help
CDC FIFO size of RX
config USB_CDC_TX_BUFSIZE
int "CDC FIFO size of TX"
default 64
depends on USB_CDC_ENABLED
help
CDC FIFO size of TX
config USB_MSC_ENABLED
bool "Enable Mass Storage (MSC) TinyUSB driver"
default n
depends on USB_ENABLED
help
Enable MSC TinyUSB driver. It is recomended to use Menuconfig-driven descriptor (.descriptor = NULL and
.string_descriptor = NULL in the tinyusb_config_t structure).
config USB_MSC_BUFSIZE
int "MSC Buffer size of Device Mass storage"
default 512
depends on USB_MSC_ENABLED
help
MSC Buffer size of Device Mass storage
config USB_HID_ENABLED
bool "Enable HID TinyUSB driver"
default n
depends on USB_ENABLED
help
Enable HID TinyUSB driver. It is recomended to use Menuconfig-driven descriptor (.descriptor = NULL and
.string_descriptor = NULL in the tinyusb_config_t structure).
config USB_HID_BUFSIZE
int "HID buffer size Should be sufficient to hold ID (if any) + Data"
default 16
depends on USB_HID_ENABLED
help
HID buffer size Should be sufficient to hold ID (if any) + Data
config USB_MIDI_ENABLED
bool "Enable MIDI TinyUSB driver"
default n
depends on USB_ENABLED
help
Enable MIDI TinyUSB driver. It is recomended to use Menuconfig-driven descriptor (.descriptor = NULL
and .string_descriptor = NULL in the tinyusb_config_t structure).
config USB_CUSTOM_CLASS_ENABLED
bool "Enable a custom TinyUSB class"
default n
depends on USB_ENABLED
help
Enable a custom TinyUSB class. It is recomended to use Menuconfig-driven descriptor (.descriptor = NULL
and .string_descriptor = NULL in the tinyusb_config_t structure).
config USB_DEBUG
bool "Debug mode"
default n
depends on USB_ENABLED
help
Debug mode
menu "Descriptor configuration"
config USB_DESC_USE_ESPRESSIF_VID
bool "VID: Use an Espressif's default value"
default y
depends on USB_ENABLED
help
Long description
config USB_DESC_CUSTOM_VID
hex "Custom VID value"
default 0x1234
depends on !USB_DESC_USE_ESPRESSIF_VID
help
Custom Vendor ID
config USB_DESC_USE_DEFAULT_PID
bool "PID: Use a default PID assigning"
default y
depends on USB_ENABLED
help
Default TinyUSB PID assigning uses values 0x4000...0x4007
config USB_DESC_CUSTOM_PID
hex "Custom PID value"
default 0x5678
depends on !USB_DESC_USE_DEFAULT_PID
help
Custom Product ID
config USB_DESC_BCDDEVICE
hex "bcdDevice"
default 0x0100
depends on USB_ENABLED
help
Version of the firmware of the USB device
config USB_DESC_MANUFACTURER_STRING
string "Manufacturer"
default "Espressif Systems"
depends on USB_ENABLED
help
Name of the manufacturer of the USB device
config USB_DESC_PRODUCT_STRING
string "Product"
default "Espressif Device"
depends on USB_ENABLED
help
Name of the USB device
config USB_DESC_SERIAL_STRING
string "Serial string"
default "123456"
depends on USB_ENABLED
help
Specify serial number of the USB device
config USB_DESC_CDC_STRING
string "CDC Device String"
default "Espressif CDC Device"
depends on USB_CDC_ENABLED
help
Specify name of the CDC device
config USB_DESC_MSC_STRING
string "MSC Device String"
default "Espressif MSC Device"
depends on USB_MSC_ENABLED
help
Specify name of the MSC device
config USB_DESC_HID_STRING
string "HID Device String"
default "Espressif HID Device"
depends on USB_HID_ENABLED
help
Specify name of the HID device
endmenu
endmenu

View file

@ -0,0 +1,71 @@
// Copyright 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 <string.h>
#include "usb_descriptors.h"
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
*
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
#define EPNUM_MSC 0x03
#ifdef __cplusplus
extern "C" {
#endif
//------------- HID Report Descriptor -------------//
#if CFG_TUD_HID
enum {
REPORT_ID_KEYBOARD = 1,
REPORT_ID_MOUSE
};
#endif
//------------- Configuration Descriptor -------------//
enum {
# if CFG_TUD_CDC
ITF_NUM_CDC = 0,
ITF_NUM_CDC_DATA,
# endif
# if CFG_TUD_MSC
ITF_NUM_MSC,
# endif
# if CFG_TUD_HID
ITF_NUM_HID,
# endif
ITF_NUM_TOTAL
};
enum {
CONFIG_TOTAL_LEN = TUD_CONFIG_DESC_LEN + CFG_TUD_CDC * TUD_CDC_DESC_LEN + CFG_TUD_MSC * TUD_MSC_DESC_LEN +
CFG_TUD_HID * TUD_HID_DESC_LEN
};
bool tusb_desc_set;
void tusb_set_descriptor(tusb_desc_device_t *desc, char **str_desc);
tusb_desc_device_t *tusb_get_active_desc(void);
char **tusb_get_active_str_desc(void);
void tusb_clear_descriptor(void);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,30 @@
// Copyright 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 "tusb.h"
#define _PID_MAP(itf, n) ((CFG_TUD_##itf) << (n))
#define USB_ESPRESSIF_VID 0x303A
#define USB_STRING_DESCRIPTOR_ARRAY_SIZE 7
typedef char *tusb_desc_strarray_device_t[USB_STRING_DESCRIPTOR_ARRAY_SIZE];
tusb_desc_device_t descriptor_tinyusb;
tusb_desc_strarray_device_t descriptor_str_tinyusb;
tusb_desc_device_t descriptor_kconfig;
tusb_desc_strarray_device_t descriptor_str_kconfig;

View file

@ -0,0 +1,177 @@
// Copyright 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 "descriptors_control.h"
#include "esp_log.h"
static const char *TAG = "TUSB:descriptors_control";
static tusb_desc_device_t s_descriptor;
static char *s_str_descriptor[USB_STRING_DESCRIPTOR_ARRAY_SIZE];
#if CFG_TUD_HID //HID Report Descriptor
uint8_t const desc_hid_report[] = {
TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(REPORT_ID_KEYBOARD), ),
TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(REPORT_ID_MOUSE), )
};
#endif
uint8_t const desc_configuration[] = {
// interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
# if CFG_TUD_CDC
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, 0x81, 8, 0x02, 0x82, 64),
# endif
# if CFG_TUD_MSC
// Interface number, string index, EP Out & EP In address, EP size
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC, 0x80 | EPNUM_MSC, 64), // highspeed 512
# endif
# if CFG_TUD_HID
// Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
TUD_HID_DESCRIPTOR(ITF_NUM_HID, 6, HID_PROTOCOL_NONE, sizeof(desc_hid_report), 0x84, 16, 10)
# endif
};
// =============================================================================
// CALLBACKS
// =============================================================================
/**
* @brief Invoked when received GET DEVICE DESCRIPTOR.
* Application returns pointer to descriptor
*
* @return uint8_t const*
*/
uint8_t const *tud_descriptor_device_cb(void)
{
return (uint8_t const *)&s_descriptor;
}
/**
* @brief Invoked when received GET CONFIGURATION DESCRIPTOR.
* Descriptor contents must exist long enough for transfer to complete
*
* @param index
* @return uint8_t const* Application return pointer to descriptor
*/
uint8_t const *tud_descriptor_configuration_cb(uint8_t index)
{
(void)index; // for multiple configurations
return desc_configuration;
}
static uint16_t _desc_str[32];
/**
* @brief Invoked when received GET STRING DESCRIPTOR request.
* Application returns pointer to descriptor, whose contents must exist long
* enough for transfer to complete
*
* @param index
* @return uint16_t const*
*/
uint16_t const *tud_descriptor_string_cb(uint8_t index)
{
uint8_t chr_count;
if (index == 0) {
memcpy(&_desc_str[1], s_str_descriptor[0], 2);
chr_count = 1;
} else {
// Convert ASCII string into UTF-16
if (index >= sizeof(s_str_descriptor) /
sizeof(s_str_descriptor[0])) {
return NULL;
}
const char *str = s_str_descriptor[index];
// Cap at max char
chr_count = strlen(str);
if (chr_count > 31) {
chr_count = 31;
}
for (uint8_t i = 0; i < chr_count; i++) {
_desc_str[1 + i] = str[i];
}
}
// first byte is len, second byte is string type
_desc_str[0] = TUD_DESC_STR_HEADER(chr_count);
return _desc_str;
}
/**
* @brief Invoked when received GET HID REPORT DESCRIPTOR
* Application returns pointer to descriptor. Descriptor contents must exist
* long enough for transfer to complete
*
* @return uint8_t const*
*/
#if CFG_TUD_HID
uint8_t const *tud_hid_descriptor_report_cb(void)
{
return desc_hid_report;
}
#endif
// =============================================================================
// Driver functions
// =============================================================================
void tusb_set_descriptor(tusb_desc_device_t *desc, char **str_desc)
{
ESP_LOGI(TAG, "Setting of a descriptor: \n"
".bDeviceClass = %u\n"
".bDeviceSubClass = %u,\n"
".bDeviceProtocol = %u,\n"
".bMaxPacketSize0 = %u,\n"
".idVendor = 0x%08x,\n"
".idProduct = 0x%08x,\n"
".bcdDevice = 0x%08x,\n"
".iManufacturer = 0x%02x,\n"
".iProduct = 0x%02x,\n"
".iSerialNumber = 0x%02x,\n"
".bNumConfigurations = 0x%02x\n",
desc->bDeviceClass, desc->bDeviceSubClass,
desc->bDeviceProtocol, desc->bMaxPacketSize0,
desc->idVendor, desc->idProduct, desc->bcdDevice,
desc->iManufacturer, desc->iProduct, desc->iSerialNumber,
desc->bNumConfigurations);
s_descriptor = *desc;
if (str_desc != NULL) {
memcpy(s_str_descriptor, str_desc,
sizeof(s_str_descriptor[0])*USB_STRING_DESCRIPTOR_ARRAY_SIZE);
}
tusb_desc_set = true;
}
tusb_desc_device_t *tusb_get_active_desc(void)
{
return &s_descriptor;
}
char **tusb_get_active_str_desc(void)
{
return s_str_descriptor;
}
void tusb_clear_descriptor(void)
{
memset(&s_descriptor, 0, sizeof(s_descriptor));
memset(&s_str_descriptor, 0, sizeof(s_str_descriptor));
tusb_desc_set = false;
}

View file

@ -0,0 +1,128 @@
// Copyright 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 "sdkconfig.h"
#include "usb_descriptors.h"
#define USB_TUSB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | _PID_MAP(MIDI, 3))
/**** TinyUSB default ****/
tusb_desc_device_t descriptor_tinyusb = {
.bLength = sizeof(descriptor_tinyusb),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
#if CFG_TUD_CDC
// Use Interface Association Descriptor (IAD) for CDC
// As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
#else
.bDeviceClass = 0x00,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
#endif
.bMaxPacketSize0 = CFG_TUD_ENDOINT0_SIZE,
.idVendor = 0xCafe,
.idProduct = USB_TUSB_PID,
.bcdDevice = 0x0100,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01};
tusb_desc_strarray_device_t descriptor_str_tinyusb = {
// array of pointer to string descriptors
(char[]){0x09, 0x04}, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
"TinyUSB Device", // 2: Product
"123456", // 3: Serials, should use chip ID
"TinyUSB CDC", // 4: CDC Interface
"TinyUSB MSC", // 5: MSC Interface
"TinyUSB HID" // 6: HID
};
/* End of TinyUSB default */
/**** Kconfig driven Descriptor ****/
tusb_desc_device_t descriptor_kconfig = {
.bLength = sizeof(descriptor_kconfig),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
#if CFG_TUD_CDC
// Use Interface Association Descriptor (IAD) for CDC
// As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
#else
.bDeviceClass = 0x00,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
#endif
.bMaxPacketSize0 = CFG_TUD_ENDOINT0_SIZE,
#if CONFIG_USB_DESC_USE_ESPRESSIF_VID
.idVendor = USB_ESPRESSIF_VID,
#else
.idVendor = CONFIG_USB_DESC_CUSTOM_VID,
#endif
#if CONFIG_USB_DESC_USE_DEFAULT_PID
.idProduct = USB_TUSB_PID,
#else
.idProduct = CONFIG_USB_DESC_CUSTOM_PID,
#endif
.bcdDevice = CONFIG_USB_DESC_BCDDEVICE,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01};
tusb_desc_strarray_device_t descriptor_str_kconfig = {
// array of pointer to string descriptors
(char[]){0x09, 0x04}, // 0: is supported language is English (0x0409)
CONFIG_USB_DESC_MANUFACTURER_STRING, // 1: Manufacturer
CONFIG_USB_DESC_PRODUCT_STRING, // 2: Product
CONFIG_USB_DESC_SERIAL_STRING, // 3: Serials, should use chip ID
#if CONFIG_USB_CDC_ENABLED
CONFIG_USB_DESC_CDC_STRING, // 4: CDC Interface
#else
"",
#endif
#if CONFIG_USB_MSC_ENABLED
CONFIG_USB_DESC_MSC_STRING, // 5: MSC Interface
#else
"",
#endif
#if CONFIG_USB_HID_ENABLED
CONFIG_USB_DESC_HID_STRING // 6: HIDs
#else
"",
#endif
};
/* End of Kconfig driven Descriptor */

View file

@ -0,0 +1,830 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org),
* 2020 Espressif Systems (Shanghai) Co. Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* This file is part of the TinyUSB stack.
*/
#include "tusb_option.h"
#if TUSB_OPT_DEVICE_ENABLED
#include "tusb.h"
#include "usbd.h"
#include "device/usbd_pvt.h"
#include "dcd.h"
#include "esp_log.h"
static const char *TAG = "TUSB:device";
#ifndef CFG_TUD_TASK_QUEUE_SZ
#define CFG_TUD_TASK_QUEUE_SZ 16
#endif
//--------------------------------------------------------------------+
// Device Data
//--------------------------------------------------------------------+
typedef struct {
struct TU_ATTR_PACKED {
volatile uint8_t connected : 1;
volatile uint8_t configured : 1;
volatile uint8_t suspended : 1;
uint8_t remote_wakeup_en : 1; // enable/disable by host
uint8_t remote_wakeup_support : 1; // configuration descriptor's attribute
uint8_t self_powered : 1; // configuration descriptor's attribute
};
uint8_t ep_busy_map[2]; // bit mask for busy endpoint
uint8_t ep_stall_map[2]; // bit map for stalled endpoint
uint8_t itf2drv[16]; // map interface number to driver (0xff is invalid)
uint8_t ep2drv[8][2]; // map endpoint to driver ( 0xff is invalid )
} usbd_device_t;
static usbd_device_t _usbd_dev = {0};
//--------------------------------------------------------------------+
// Class Driver
//--------------------------------------------------------------------+
typedef struct {
uint8_t class_code;
void (*init)(void);
bool (*open)(uint8_t rhport, tusb_desc_interface_t const *desc_intf, uint16_t *p_length);
bool (*control_request)(uint8_t rhport, tusb_control_request_t const *request);
bool (*control_request_complete)(uint8_t rhport, tusb_control_request_t const *request);
bool (*xfer_cb)(uint8_t rhport, uint8_t ep_addr, xfer_result_t, uint32_t);
void (*sof)(uint8_t rhport);
void (*reset)(uint8_t);
} usbd_class_driver_t;
static usbd_class_driver_t const usbd_class_drivers[] = {
#if CFG_TUD_CDC
{
.class_code = TUSB_CLASS_CDC,
.init = cdcd_init,
.open = cdcd_open,
.control_request = cdcd_control_request,
.control_request_complete = cdcd_control_request_complete,
.xfer_cb = cdcd_xfer_cb,
.sof = NULL,
.reset = cdcd_reset
},
#endif
#if CFG_TUD_MSC
{
.class_code = TUSB_CLASS_MSC,
.init = mscd_init,
.open = mscd_open,
.control_request = mscd_control_request,
.control_request_complete = mscd_control_request_complete,
.xfer_cb = mscd_xfer_cb,
.sof = NULL,
.reset = mscd_reset
},
#endif
#if CFG_TUD_HID
{
.class_code = TUSB_CLASS_HID,
.init = hidd_init,
.open = hidd_open,
.control_request = hidd_control_request,
.control_request_complete = hidd_control_request_complete,
.xfer_cb = hidd_xfer_cb,
.sof = NULL,
.reset = hidd_reset
},
#endif
#if CFG_TUD_MIDI
{
.class_code = TUSB_CLASS_AUDIO,
.init = midid_init,
.open = midid_open,
.control_request = midid_control_request,
.control_request_complete = midid_control_request_complete,
.xfer_cb = midid_xfer_cb,
.sof = NULL,
.reset = midid_reset
},
#endif
#if CFG_TUD_CUSTOM_CLASS
{
.class_code = TUSB_CLASS_VENDOR_SPECIFIC,
.init = cusd_init,
.open = cusd_open,
.control_request = cusd_control_request,
.control_request_complete = cusd_control_request_complete,
.xfer_cb = cusd_xfer_cb,
.sof = NULL,
.reset = cusd_reset
},
#endif
};
enum {
USBD_CLASS_DRIVER_COUNT = TU_ARRAY_SZIE(usbd_class_drivers)
};
//--------------------------------------------------------------------+
// DCD Event
//--------------------------------------------------------------------+
// Event queue
// OPT_MODE_DEVICE is used by OS NONE for mutex (disable usb isr)
OSAL_QUEUE_DEF(OPT_MODE_DEVICE, _usbd_qdef, CFG_TUD_TASK_QUEUE_SZ, dcd_event_t);
static osal_queue_t _usbd_q;
//--------------------------------------------------------------------+
// Prototypes
//--------------------------------------------------------------------+
static void mark_interface_endpoint(uint8_t ep2drv[8][2], uint8_t const *p_desc, uint16_t desc_len, uint8_t driver_id);
static bool process_control_request(uint8_t rhport, tusb_control_request_t const *p_request);
static bool process_set_config(uint8_t rhport, uint8_t cfg_num);
static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const *p_request);
void usbd_control_reset(uint8_t rhport);
bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
void usbd_control_set_complete_callback(bool (*fp)(uint8_t, tusb_control_request_t const *));
//--------------------------------------------------------------------+
// Application API
//--------------------------------------------------------------------+
bool tud_mounted(void)
{
return _usbd_dev.configured;
}
bool tud_suspended(void)
{
return _usbd_dev.suspended;
}
bool tud_remote_wakeup(void)
{
// only wake up host if this feature is supported and enabled and we are suspended
TU_VERIFY(_usbd_dev.suspended && _usbd_dev.remote_wakeup_support && _usbd_dev.remote_wakeup_en);
dcd_remote_wakeup(TUD_OPT_RHPORT);
return true;
}
//--------------------------------------------------------------------+
// USBD Task
//--------------------------------------------------------------------+
bool usbd_init(void)
{
tu_varclr(&_usbd_dev);
// Init device queue & task
ESP_LOGV(TAG, "Init device queue & task...");
_usbd_q = osal_queue_create(&_usbd_qdef);
TU_ASSERT(_usbd_q != NULL);
ESP_LOGV(TAG, "Init device queue & task: Done");
// Init class drivers
# if USBD_CLASS_DRIVER_COUNT
for (uint8_t i = 0; i < USBD_CLASS_DRIVER_COUNT; i++) {
usbd_class_drivers[i].init();
}
# endif
// Init device controller driver
ESP_LOGV(TAG, "dcd_init...");
dcd_init(TUD_OPT_RHPORT);
ESP_LOGV(TAG, "dcd_init: Done");
ESP_LOGV(TAG, "dcd_int_enable...");
dcd_int_enable(TUD_OPT_RHPORT);
ESP_LOGV(TAG, "dcd_int_enable: Done");
return true;
}
static void usbd_reset(uint8_t rhport)
{
tu_varclr(&_usbd_dev);
memset(_usbd_dev.itf2drv, 0xff, sizeof(_usbd_dev.itf2drv)); // invalid mapping
memset(_usbd_dev.ep2drv, 0xff, sizeof(_usbd_dev.ep2drv)); // invalid mapping
usbd_control_reset(rhport);
# if USBD_CLASS_DRIVER_COUNT
for (uint8_t i = 0; i < USBD_CLASS_DRIVER_COUNT; i++) {
if (usbd_class_drivers[i].reset) {
usbd_class_drivers[i].reset(rhport);
}
}
# endif
}
/* USB Device Driver task
* This top level thread manages all device controller event and delegates events to class-specific drivers.
* This should be called periodically within the mainloop or rtos thread.
*
@code
int main(void)
{
application_init();
tusb_init();
while(1) // the mainloop
{
application_code();
tud_task(); // tinyusb device task
}
}
@endcode
*/
void tud_task(void)
{
// Skip if stack is not initialized
bool tusb_ready = tusb_inited();
if (!tusb_ready) {
ESP_LOGV(TAG, "is not ready");
return;
}
ESP_LOGV(TAG, "started");
// Loop until there is no more events in the queue
while (1) {
dcd_event_t event;
volatile bool ev = osal_queue_receive(_usbd_q, &event);
if (!ev) {
ESP_LOGV(TAG, "USB EVENT ...empty...");
return;
}
ESP_LOGV(TAG, "USB EVENT: %u", event.event_id);
switch (event.event_id) {
case DCD_EVENT_BUS_RESET:
ESP_LOGV(TAG, "USB EVENT bus_reset");
usbd_reset(event.rhport);
break;
case DCD_EVENT_UNPLUGGED:
ESP_LOGV(TAG, "USB EVENT unplugged");
usbd_reset(event.rhport);
// invoke callback
if (tud_umount_cb) {
tud_umount_cb();
}
break;
case DCD_EVENT_SETUP_RECEIVED:
ESP_LOGV(TAG, "USB EVENT setup_received");
// Mark as connected after receiving 1st setup packet.
// But it is easier to set it every time instead of wasting time to check then set
_usbd_dev.connected = 1;
// Process control request
if (!process_control_request(event.rhport, &event.setup_received)) {
// Failed -> stall both control endpoint IN and OUT
dcd_edpt_stall(event.rhport, 0);
dcd_edpt_stall(event.rhport, 0 | TUSB_DIR_IN_MASK);
}
break;
case DCD_EVENT_XFER_COMPLETE:
// Only handle xfer callback in ready state
// if (_usbd_dev.connected && !_usbd_dev.suspended)
ESP_LOGV(TAG, "USB EVENT xfer_complete");
{
// Invoke the class callback associated with the endpoint address
uint8_t const ep_addr = event.xfer_complete.ep_addr;
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
_usbd_dev.ep_busy_map[dir] = (uint8_t)tu_bit_clear(_usbd_dev.ep_busy_map[dir], epnum);
if (0 == tu_edpt_number(ep_addr)) {
// control transfer DATA stage callback
usbd_control_xfer_cb(event.rhport, ep_addr, event.xfer_complete.result, event.xfer_complete.len);
} else {
uint8_t const drv_id = _usbd_dev.ep2drv[tu_edpt_number(ep_addr)][tu_edpt_dir(ep_addr)];
# if USBD_CLASS_DRIVER_COUNT
TU_ASSERT(drv_id < USBD_CLASS_DRIVER_COUNT, );
# endif
usbd_class_drivers[drv_id].xfer_cb(event.rhport, ep_addr, event.xfer_complete.result, event.xfer_complete.len);
}
}
break;
case DCD_EVENT_SUSPEND:
ESP_LOGV(TAG, "USB EVENT suspend");
if (tud_suspend_cb) {
tud_suspend_cb(_usbd_dev.remote_wakeup_en);
}
break;
case DCD_EVENT_RESUME:
ESP_LOGV(TAG, "USB EVENT resume");
if (tud_resume_cb) {
tud_resume_cb();
}
break;
case DCD_EVENT_SOF:
ESP_LOGV(TAG, "USB EVENT sof");
# if USBD_CLASS_DRIVER_COUNT
for (uint8_t i = 0; i < USBD_CLASS_DRIVER_COUNT; i++) {
if (usbd_class_drivers[i].sof) {
usbd_class_drivers[i].sof(event.rhport);
}
}
# endif
break;
case USBD_EVENT_FUNC_CALL:
ESP_LOGV(TAG, "USB EVENT func_call");
if (event.func_call.func) {
event.func_call.func(event.func_call.param);
}
break;
default:
ESP_LOGV(TAG, "USB EVENT unknown");
TU_BREAKPOINT();
break;
}
}
}
//--------------------------------------------------------------------+
// Control Request Parser & Handling
//--------------------------------------------------------------------+
// This handles the actual request and its response.
// return false will cause its caller to stall control endpoint
static bool process_control_request(uint8_t rhport, tusb_control_request_t const *p_request)
{
usbd_control_set_complete_callback(NULL);
switch (p_request->bmRequestType_bit.recipient) {
//------------- Device Requests e.g in enumeration -------------//
case TUSB_REQ_RCPT_DEVICE:
if (TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type) {
// Non standard request is not supported
TU_BREAKPOINT();
return false;
}
switch (p_request->bRequest) {
case TUSB_REQ_SET_ADDRESS:
ESP_LOGV(TAG, "TUSB_REQ_SET_ADDRESS");
// Depending on mcu, status phase could be sent either before or after changing device address
// Therefore DCD must include zero-length status response
dcd_set_address(rhport, (uint8_t)p_request->wValue);
return true; // skip status
break;
case TUSB_REQ_GET_CONFIGURATION: {
ESP_LOGV(TAG, "TUSB_REQ_GET_CONFIGURATION");
uint8_t cfgnum = _usbd_dev.configured ? 1 : 0;
usbd_control_xfer(rhport, p_request, &cfgnum, 1);
}
break;
case TUSB_REQ_SET_CONFIGURATION: {
ESP_LOGV(TAG, "TUSB_REQ_SET_CONFIGURATION");
uint8_t const cfg_num = (uint8_t)p_request->wValue;
dcd_set_config(rhport, cfg_num);
_usbd_dev.configured = cfg_num ? 1 : 0;
if (cfg_num) {
TU_ASSERT(process_set_config(rhport, cfg_num));
}
usbd_control_status(rhport, p_request);
}
break;
case TUSB_REQ_GET_DESCRIPTOR:
ESP_LOGV(TAG, "TUSB_REQ_GET_DESCRIPTOR");
TU_VERIFY(process_get_descriptor(rhport, p_request));
break;
case TUSB_REQ_SET_FEATURE:
ESP_LOGV(TAG, "TUSB_REQ_SET_FEATURE");
// Only support remote wakeup for device feature
TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue);
// Host may enable remote wake up before suspending especially HID device
_usbd_dev.remote_wakeup_en = true;
usbd_control_status(rhport, p_request);
break;
case TUSB_REQ_CLEAR_FEATURE:
ESP_LOGV(TAG, "TUSB_REQ_CLEAR_FEATURE");
// Only support remote wakeup for device feature
TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue);
// Host may disable remote wake up after resuming
_usbd_dev.remote_wakeup_en = false;
usbd_control_status(rhport, p_request);
break;
case TUSB_REQ_GET_STATUS: {
ESP_LOGV(TAG, "TUSB_REQ_GET_STATUS");
// Device status bit mask
// - Bit 0: Self Powered
// - Bit 1: Remote Wakeup enabled
uint16_t status = (_usbd_dev.self_powered ? 1 : 0) | (_usbd_dev.remote_wakeup_en ? 2 : 0);
usbd_control_xfer(rhport, p_request, &status, 2);
}
break;
// Unknown/Unsupported request
default:
TU_BREAKPOINT();
return false;
}
break;
//------------- Class/Interface Specific Request -------------//
case TUSB_REQ_RCPT_INTERFACE: {
uint8_t const itf = tu_u16_low(p_request->wIndex);
uint8_t const drvid = _usbd_dev.itf2drv[itf];
# if USBD_CLASS_DRIVER_COUNT
TU_VERIFY(drvid < USBD_CLASS_DRIVER_COUNT);
# endif
usbd_control_set_complete_callback(usbd_class_drivers[drvid].control_request_complete);
// stall control endpoint if driver return false
return usbd_class_drivers[drvid].control_request(rhport, p_request);
}
break;
//------------- Endpoint Request -------------//
case TUSB_REQ_RCPT_ENDPOINT:
// Non standard request is not supported
TU_VERIFY(TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type);
switch (p_request->bRequest) {
case TUSB_REQ_GET_STATUS: {
uint16_t status = usbd_edpt_stalled(rhport, tu_u16_low(p_request->wIndex)) ? 0x0001 : 0x0000;
usbd_control_xfer(rhport, p_request, &status, 2);
}
break;
case TUSB_REQ_CLEAR_FEATURE:
if (TUSB_REQ_FEATURE_EDPT_HALT == p_request->wValue) {
usbd_edpt_clear_stall(rhport, tu_u16_low(p_request->wIndex));
}
usbd_control_status(rhport, p_request);
break;
case TUSB_REQ_SET_FEATURE:
if (TUSB_REQ_FEATURE_EDPT_HALT == p_request->wValue) {
usbd_edpt_stall(rhport, tu_u16_low(p_request->wIndex));
}
usbd_control_status(rhport, p_request);
break;
// Unknown/Unsupported request
default:
TU_BREAKPOINT();
return false;
}
break;
// Unknown recipient
default:
TU_BREAKPOINT();
return false;
}
return true;
}
// Process Set Configure Request
// This function parse configuration descriptor & open drivers accordingly
static bool process_set_config(uint8_t rhport, uint8_t cfg_num)
{
tusb_desc_configuration_t const *desc_cfg = (tusb_desc_configuration_t const *)tud_descriptor_configuration_cb(cfg_num - 1); // index is cfg_num-1
TU_ASSERT(desc_cfg != NULL && desc_cfg->bDescriptorType == TUSB_DESC_CONFIGURATION);
// Parse configuration descriptor
_usbd_dev.remote_wakeup_support = (desc_cfg->bmAttributes & TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP) ? 1 : 0;
_usbd_dev.self_powered = (desc_cfg->bmAttributes & TUSB_DESC_CONFIG_ATT_SELF_POWERED) ? 1 : 0;
// Parse interface descriptor
uint8_t const *p_desc = ((uint8_t const *)desc_cfg) + sizeof(tusb_desc_configuration_t);
uint8_t const *desc_end = ((uint8_t const *)desc_cfg) + desc_cfg->wTotalLength;
while (p_desc < desc_end) {
// Each interface always starts with Interface or Association descriptor
if (TUSB_DESC_INTERFACE_ASSOCIATION == tu_desc_type(p_desc)) {
p_desc = tu_desc_next(p_desc); // ignore Interface Association
} else {
TU_ASSERT(TUSB_DESC_INTERFACE == tu_desc_type(p_desc));
tusb_desc_interface_t *desc_itf = (tusb_desc_interface_t *)p_desc;
// Check if class is supported
uint8_t drv_id = 0;
# if USBD_CLASS_DRIVER_COUNT
for (; drv_id < USBD_CLASS_DRIVER_COUNT; drv_id++) {
if (usbd_class_drivers[drv_id].class_code == desc_itf->bInterfaceClass) {
break;
}
}
# endif
// Interface number must not be used already TODO alternate interface
TU_ASSERT(0xff == _usbd_dev.itf2drv[desc_itf->bInterfaceNumber]);
_usbd_dev.itf2drv[desc_itf->bInterfaceNumber] = drv_id;
uint16_t itf_len = 0;
TU_ASSERT(usbd_class_drivers[drv_id].open(rhport, desc_itf, &itf_len));
TU_ASSERT(itf_len >= sizeof(tusb_desc_interface_t));
mark_interface_endpoint(_usbd_dev.ep2drv, p_desc, itf_len, drv_id);
p_desc += itf_len; // next interface
}
}
// invoke callback
if (tud_mount_cb) {
tud_mount_cb();
}
return true;
}
// Helper marking endpoint of interface belongs to class driver
static void mark_interface_endpoint(uint8_t ep2drv[8][2], uint8_t const *p_desc, uint16_t desc_len, uint8_t driver_id)
{
uint16_t len = 0;
while (len < desc_len) {
if (TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)) {
uint8_t const ep_addr = ((tusb_desc_endpoint_t const *)p_desc)->bEndpointAddress;
ep2drv[tu_edpt_number(ep_addr)][tu_edpt_dir(ep_addr)] = driver_id;
}
len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
}
}
// return descriptor's buffer and update desc_len
static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const *p_request)
{
tusb_desc_type_t const desc_type = (tusb_desc_type_t)tu_u16_high(p_request->wValue);
uint8_t const desc_index = tu_u16_low(p_request->wValue);
switch (desc_type) {
case TUSB_DESC_DEVICE:
return usbd_control_xfer(rhport, p_request, (void *)tud_descriptor_device_cb(), sizeof(tusb_desc_device_t));
break;
case TUSB_DESC_CONFIGURATION: {
tusb_desc_configuration_t const *desc_config = (tusb_desc_configuration_t const *)tud_descriptor_configuration_cb(desc_index);
return usbd_control_xfer(rhport, p_request, (void *)desc_config, desc_config->wTotalLength);
}
break;
case TUSB_DESC_STRING:
// String Descriptor always uses the desc set from user
if (desc_index == 0xEE) {
// The 0xEE index string is a Microsoft USB extension.
// It can be used to tell Windows what driver it should use for the device !!!
return false;
} else {
uint8_t const *desc_str = (uint8_t const *)tud_descriptor_string_cb(desc_index);
TU_ASSERT(desc_str);
// first byte of descriptor is its size
return usbd_control_xfer(rhport, p_request, (void *)desc_str, desc_str[0]);
}
break;
case TUSB_DESC_DEVICE_QUALIFIER:
return false;
break;
default:
return false;
}
return true;
}
//--------------------------------------------------------------------+
// DCD Event Handler
//--------------------------------------------------------------------+
void dcd_event_handler(dcd_event_t const *event, bool in_isr)
{
switch (event->event_id) {
case DCD_EVENT_BUS_RESET:
osal_queue_send(_usbd_q, event, in_isr);
break;
case DCD_EVENT_UNPLUGGED:
_usbd_dev.connected = 0;
_usbd_dev.configured = 0;
_usbd_dev.suspended = 0;
osal_queue_send(_usbd_q, event, in_isr);
break;
case DCD_EVENT_SOF:
// nothing to do now
break;
case DCD_EVENT_SUSPEND:
// NOTE: When plugging/unplugging device, the D+/D- state are unstable and can accidentally meet the
// SUSPEND condition ( Idle for 3ms ). Some MCUs such as SAMD doesn't distinguish suspend vs disconnect as well.
// We will skip handling SUSPEND/RESUME event if not currently connected
if (_usbd_dev.connected) {
_usbd_dev.suspended = 1;
osal_queue_send(_usbd_q, event, in_isr);
}
break;
case DCD_EVENT_RESUME:
if (_usbd_dev.connected) {
_usbd_dev.suspended = 0;
osal_queue_send(_usbd_q, event, in_isr);
}
break;
case DCD_EVENT_SETUP_RECEIVED:
osal_queue_send(_usbd_q, event, in_isr);
break;
case DCD_EVENT_XFER_COMPLETE:
// skip zero-length control status complete event, should DCD notify us.
if ((0 == tu_edpt_number(event->xfer_complete.ep_addr)) && (event->xfer_complete.len == 0)) {
break;
}
osal_queue_send(_usbd_q, event, in_isr);
TU_ASSERT(event->xfer_complete.result == XFER_RESULT_SUCCESS, );
break;
// Not an DCD event, just a convenient way to defer ISR function should we need to
case USBD_EVENT_FUNC_CALL:
osal_queue_send(_usbd_q, event, in_isr);
break;
default:
break;
}
}
// helper to send bus signal event
void dcd_event_bus_signal(uint8_t rhport, dcd_eventid_t eid, bool in_isr)
{
dcd_event_t event = {
.rhport = rhport,
.event_id = eid,
};
dcd_event_handler(&event, in_isr);
}
// helper to send setup received
void dcd_event_setup_received(uint8_t rhport, uint8_t const *setup, bool in_isr)
{
dcd_event_t event = {.rhport = rhport, .event_id = DCD_EVENT_SETUP_RECEIVED};
memcpy(&event.setup_received, setup, 8);
dcd_event_handler(&event, in_isr);
}
// helper to send transfer complete event
void dcd_event_xfer_complete(uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool in_isr)
{
dcd_event_t event = {.rhport = rhport, .event_id = DCD_EVENT_XFER_COMPLETE};
event.xfer_complete.ep_addr = ep_addr;
event.xfer_complete.len = xferred_bytes;
event.xfer_complete.result = result;
dcd_event_handler(&event, in_isr);
}
//--------------------------------------------------------------------+
// Helper
//--------------------------------------------------------------------+
// Parse consecutive endpoint descriptors (IN & OUT)
bool usbd_open_edpt_pair(uint8_t rhport, uint8_t const *p_desc, uint8_t ep_count, uint8_t xfer_type, uint8_t *ep_out, uint8_t *ep_in)
{
for (int i = 0; i < ep_count; i++) {
tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *)p_desc;
TU_VERIFY(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && xfer_type == desc_ep->bmAttributes.xfer);
TU_ASSERT(dcd_edpt_open(rhport, desc_ep));
if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
(*ep_in) = desc_ep->bEndpointAddress;
} else {
(*ep_out) = desc_ep->bEndpointAddress;
}
p_desc = tu_desc_next(p_desc);
}
return true;
}
// Helper to defer an isr function
void usbd_defer_func(osal_task_func_t func, void *param, bool in_isr)
{
dcd_event_t event = {
.rhport = 0,
.event_id = USBD_EVENT_FUNC_CALL,
};
event.func_call.func = func;
event.func_call.param = param;
dcd_event_handler(&event, in_isr);
}
//--------------------------------------------------------------------+
// USBD Endpoint API
//--------------------------------------------------------------------+
bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
{
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
TU_VERIFY(dcd_edpt_xfer(rhport, ep_addr, buffer, total_bytes));
_usbd_dev.ep_busy_map[dir] = (uint8_t)tu_bit_set(_usbd_dev.ep_busy_map[dir], epnum);
return true;
}
bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr)
{
(void)rhport;
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
return tu_bit_test(_usbd_dev.ep_busy_map[dir], epnum);
}
void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
{
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
dcd_edpt_stall(rhport, ep_addr);
_usbd_dev.ep_stall_map[dir] = (uint8_t)tu_bit_set(_usbd_dev.ep_stall_map[dir], epnum);
}
void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
{
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
dcd_edpt_clear_stall(rhport, ep_addr);
_usbd_dev.ep_stall_map[dir] = (uint8_t)tu_bit_clear(_usbd_dev.ep_stall_map[dir], epnum);
}
bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr)
{
(void)rhport;
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
return tu_bit_test(_usbd_dev.ep_stall_map[dir], epnum);
}
#endif

View file

@ -0,0 +1,51 @@
// Copyright 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
#ifdef __cplusplus
extern "C" {
#endif
// Espressif
#include "driver/periph_ctrl.h"
#include "freertos/xtensa_api.h"
#include "esp_intr_alloc.h"
#include "esp_log.h"
#include "esp32s2/rom/gpio.h"
#include "soc/dport_reg.h"
#include "soc/gpio_sig_map.h"
#include "soc/usb_periph.h"
#include "tusb_config.h"
// TinyUSB
#include "tusb_option.h"
#include "descriptors_control.h"
#include "device/dcd.h"
#define USB_EP_DIRECTIONS 2
#define USB_MAX_EP_NUM 16
typedef struct {
uint8_t *buffer;
uint16_t total_len;
uint16_t queued_len;
uint16_t max_size;
bool short_packet;
} xfer_ctl_t;
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,80 @@
// Copyright 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 <stdbool.h>
#include "descriptors_control.h"
#include "board.h"
#include "tusb.h"
#include "tusb_config.h"
#ifdef __cplusplus
extern "C" {
#endif
/* tinyusb uses buffers with type of uint8_t[] but in our driver we are reading them as a 32-bit word */
#if (CFG_TUD_ENDOINT0_SIZE < 4)
# define CFG_TUD_ENDOINT0_SIZE 4
# warning "CFG_TUD_ENDOINT0_SIZE was too low and was set to 4"
#endif
#if TUSB_OPT_DEVICE_ENABLED
# if CFG_TUD_HID
# if (CFG_TUD_HID_BUFSIZE < 4)
# define CFG_TUD_HID_BUFSIZE 4
# warning "CFG_TUD_HID_BUFSIZE was too low and was set to 4"
# endif
# endif
# if CFG_TUD_CDC
# if (CFG_TUD_CDC_EPSIZE < 4)
# define CFG_TUD_CDC_EPSIZE 4
# warning "CFG_TUD_CDC_EPSIZE was too low and was set to 4"
# endif
# endif
# if CFG_TUD_MSC
# if (CFG_TUD_MSC_BUFSIZE < 4)
# define CFG_TUD_MSC_BUFSIZE 4
# warning "CFG_TUD_MSC_BUFSIZE was too low and was set to 4"
# endif
# endif
# if CFG_TUD_MIDI
# if (CFG_TUD_MIDI_EPSIZE < 4)
# define CFG_TUD_MIDI_EPSIZE 4
# warning "CFG_TUD_MIDI_EPSIZE was too low and was set to 4"
# endif
# endif
# if CFG_TUD_CUSTOM_CLASS
# warning "Please check that the buffer is more then 4 bytes"
# endif
#endif
typedef struct {
tusb_desc_device_t *descriptor;
char **string_descriptor;
bool external_phy;
} tinyusb_config_t;
esp_err_t tinyusb_driver_install(const tinyusb_config_t *config);
// TODO esp_err_t tinyusb_driver_uninstall(void); (IDF-1474)
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,114 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org),
* Additions Copyright (c) 2020, Espressif Systems (Shanghai) PTE LTD
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#pragma once
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------
// COMMON CONFIGURATION
//--------------------------------------------------------------------
#define OPT_MCU_ESP32_S2 900 // TODO remove after rebase to the last TUSB (IDF-1473)
#define CFG_TUSB_MCU OPT_MCU_ESP32_S2
#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE
#define CFG_TUSB_OS OPT_OS_FREERTOS
// CFG_TUSB_DEBUG is defined by compiler in DEBUG build
#define CFG_TUSB_DEBUG CONFIG_USB_DEBUG
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
* Tinyusb use follows macros to declare transferring memory so that they can be put
* into those specific section.
* e.g
* - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
* - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
*/
#ifndef CFG_TUSB_MEM_SECTION
# define CFG_TUSB_MEM_SECTION
#endif
#ifndef CFG_TUSB_MEM_ALIGN
# define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4)
#endif
//--------------------------------------------------------------------
// DEVICE CONFIGURATION
//--------------------------------------------------------------------
#define CFG_TUD_ENDOINT0_SIZE 64
//------kconfig adaptor part -------//
#ifndef CONFIG_USB_CDC_ENABLED
# define CONFIG_USB_CDC_ENABLED 0
#endif
#ifndef CONFIG_USB_MSC_ENABLED
# define CONFIG_USB_MSC_ENABLED 0
#endif
#ifndef CONFIG_USB_HID_ENABLED
# define CONFIG_USB_HID_ENABLED 0
#endif
#ifndef CONFIG_USB_MIDI_ENABLED
# define CONFIG_USB_MIDI_ENABLED 0
#endif
#ifndef CONFIG_USB_CUSTOM_CLASS_ENABLED
# define CONFIG_USB_CUSTOM_CLASS_ENABLED 0
#endif
//------------- CLASS -------------//
#define CFG_TUD_CDC CONFIG_USB_CDC_ENABLED
#define CFG_TUD_MSC CONFIG_USB_MSC_ENABLED
#define CFG_TUD_HID CONFIG_USB_HID_ENABLED
#define CFG_TUD_MIDI CONFIG_USB_MIDI_ENABLED
#define CFG_TUD_CUSTOM_CLASS CONFIG_USB_CUSTOM_CLASS_ENABLED
// CDC FIFO size of TX and RX
#define CFG_TUD_CDC_RX_BUFSIZE CONFIG_USB_CDC_RX_BUFSIZE
#define CFG_TUD_CDC_TX_BUFSIZE CONFIG_USB_CDC_TX_BUFSIZE
// MSC Buffer size of Device Mass storage:
#define CFG_TUD_MSC_BUFSIZE CONFIG_USB_MSC_BUFSIZE
// HID buffer size Should be sufficient to hold ID (if any) + Data
#define CFG_TUD_HID_BUFSIZE CONFIG_USB_HID_BUFSIZE
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,710 @@
// Copyright 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 "device_controller_driver.h"
static const char *TAG = "TUSB:DCD";
static intr_handle_t usb_ih;
static volatile TU_ATTR_ALIGNED(4) uint32_t _setup_packet[6];
static uint8_t s_setup_phase = 0; /* 00 - got setup,
01 - got done setup,
02 - setup cmd sent*/
#define XFER_CTL_BASE(_ep, _dir) &xfer_status[_ep][_dir]
static xfer_ctl_t xfer_status[USB_MAX_EP_NUM][USB_EP_DIRECTIONS];
static inline void readyfor1setup_pkg(int ep_num)
{
USB0.out_ep_reg[ep_num].doeptsiz |= (1 << USB_SUPCNT0_S); // doeptsiz 29:30 will decremented on every setup received
}
// Setup the control endpoint 0.
static void bus_reset(void)
{
for (int ep_num = 0; ep_num < USB_OUT_EP_NUM; ep_num++) {
USB0.out_ep_reg[ep_num].doepctl |= USB_DO_SNAK0_M; // DOEPCTL0_SNAK
}
USB0.dcfg &= ~USB_DEVADDR_M; // reset address
// Peripheral FIFO architecture
//
// --------------- 320 ( 1280 bytes )
// | IN FIFO 3 |
// --------------- y + x + 16 + GRXFSIZ
// | IN FIFO 2 |
// --------------- x + 16 + GRXFSIZ
// | IN FIFO 1 |
// --------------- 16 + GRXFSIZ
// | IN FIFO 0 |
// --------------- GRXFSIZ
// | OUT FIFO |
// | ( Shared ) |
// --------------- 0
//
// FIFO sizes are set up by the following rules (each word 32-bits):
// All EP OUT shared a unique OUT FIFO which uses (based on page 1354 of Rev 17 of reference manual):
// * 10 locations in hardware for setup packets + setup control words
// (up to 3 setup packets).
// * 2 locations for OUT endpoint control words.
// * 16 for largest packet size of 64 bytes. ( TODO Highspeed is 512 bytes)
// * 1 location for global NAK (not required/used here).
//
// It is recommended to allocate 2 times the largest packet size, therefore
// Recommended value = 10 + 1 + 2 x (16+2) = 47 --> Let's make it 50
USB0.grstctl |= 0x10 << USB_TXFNUM_S; // fifo 0x10,
USB0.grstctl |= USB_TXFFLSH_M; // Flush fifo
USB0.grxfsiz = 50;
USB0.gintmsk = USB_MODEMISMSK_M |
USB_SOFMSK_M |
USB_RXFLVIMSK_M |
USB_ERLYSUSPMSK_M |
USB_USBSUSPMSK_M |
USB_USBRSTMSK_M |
USB_ENUMDONEMSK_M |
USB_IEPINTMSK_M |
USB_OEPINTMSK_M |
USB_RESETDETMSK_M |
USB_DISCONNINTMSK_M;
USB0.daintmsk |= USB_OUTEPMSK0_M | USB_INEPMSK0_M;
USB0.doepmsk |= USB_SETUPMSK_M | USB_XFERCOMPLMSK;
USB0.diepmsk |= USB_TIMEOUTMSK_M | USB_DI_XFERCOMPLMSK_M;
USB0.gnptxfsiz = 16 << USB_NPTXFDEP_S; // Control IN uses FIFO 0 with 64 bytes ( 16 32-bit word )
readyfor1setup_pkg(0);
}
static void enum_done_processing(void)
{
ESP_EARLY_LOGV(TAG, "dcd_int_handler - Speed enumeration done! Sending DCD_EVENT_BUS_RESET then");
// On current silicon on the Full Speed core, speed is fixed to Full Speed.
// However, keep for debugging and in case Low Speed is ever supported.
uint32_t enum_spd = (USB0.dsts >> USB_ENUMSPD_S) & (USB_ENUMSPD_V);
// Maximum packet size for EP 0 is set for both directions by writing DIEPCTL
if (enum_spd == 0x03) { // Full-Speed (PHY on 48 MHz)
USB0.in_ep_reg[0].diepctl &= ~USB_D_MPS0_V; // 64 bytes
USB0.in_ep_reg[0].diepctl &= ~USB_D_STALL0_M; // clear Stall
xfer_status[0][TUSB_DIR_OUT].max_size = 64;
xfer_status[0][TUSB_DIR_IN].max_size = 64;
} else {
USB0.in_ep_reg[0].diepctl |= USB_D_MPS0_V; // 8 bytes
USB0.in_ep_reg[0].diepctl &= ~USB_D_STALL0_M; // clear Stall
xfer_status[0][TUSB_DIR_OUT].max_size = 8;
xfer_status[0][TUSB_DIR_IN].max_size = 8;
}
USB0.gintmsk |= USB_SOFMSK_M; // SOF unmask
}
/*------------------------------------------------------------------*/
/* Controller API
*------------------------------------------------------------------*/
void dcd_init(uint8_t rhport)
{
ESP_LOGV(TAG, "DCD init - Start");
// A. Disconnect
ESP_LOGV(TAG, "DCD init - Soft DISCONNECT and Setting up");
USB0.dctl |= USB_SFTDISCON_M; // Soft disconnect
// B. Programming DCFG
/* If USB host misbehaves during status portion of control xfer
(non zero-length packet), send STALL back and discard. Full speed. */
USB0.dcfg |= USB_NZSTSOUTHSHK_M | // NonZero .... STALL
(3 << 0); // dev speed: fullspeed 1.1 on 48 mhz // TODO no value in usb_reg.h (IDF-1476)
USB0.gahbcfg |= USB_NPTXFEMPLVL_M | USB_GLBLLNTRMSK_M; // Global interruptions ON
USB0.gusbcfg |= USB_FORCEDEVMODE_M; // force devmode
USB0.gotgctl &= ~(USB_BVALIDOVVAL_M | USB_BVALIDOVEN_M | USB_VBVALIDOVVAL_M); //no overrides
#ifdef CONFIG_IDF_TARGET_ESP32S2BETA // needed for beta chip only
//C. chip 7.2.2 hack
ESP_LOGV(TAG, "DCD init - chip ESP32-S2 beta hack");
USB0.gotgctl = (0 << USB_BVALIDOVVAL_S); //B override value
ets_delay_us(20);
USB0.gotgctl = (0 << USB_BVALIDOVVAL_S) | (1 << USB_BVALIDOVEN_S); //B override value & enable
ets_delay_us(20);
#endif
// C. Setting SNAKs, then connect
for (int n = 0; n < USB_OUT_EP_NUM; n++) {
USB0.out_ep_reg[n].doepctl |= USB_DO_SNAK0_M; // DOEPCTL0_SNAK
}
ESP_LOGV(TAG, "DCD init - Soft CONNECT");
USB0.dctl &= ~USB_SFTDISCON_M; // Connect
// D. Interruption masking
USB0.gintmsk = 0; //mask all
USB0.gotgint = ~0U; //clear OTG ints
USB0.gintsts = ~0U; //clear pending ints
USB0.gintmsk = USB_MODEMISMSK_M |
USB_SOFMSK_M |
USB_RXFLVIMSK_M |
USB_ERLYSUSPMSK_M |
USB_USBSUSPMSK_M |
USB_USBRSTMSK_M |
USB_ENUMDONEMSK_M |
USB_RESETDETMSK_M |
USB_DISCONNINTMSK_M;
ets_delay_us(100);
}
void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
{
(void)rhport;
ESP_LOGV(TAG, "DCD init - Set address : %u", dev_addr);
USB0.dcfg |= ((dev_addr & USB_DEVADDR_V) << USB_DEVADDR_S);
// Response with status after changing device address
dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
}
void dcd_set_config(uint8_t rhport, uint8_t config_num)
{
(void)rhport;
(void)config_num;
// Nothing to do
}
void dcd_remote_wakeup(uint8_t rhport)
{
(void)rhport;
}
/*------------------------------------------------------------------*/
/* DCD Endpoint port
*------------------------------------------------------------------*/
bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_edpt)
{
ESP_LOGV(TAG, "DCD endpoint opened");
(void)rhport;
usb_out_endpoint_t *out_ep = &(USB0.out_ep_reg[0]);
usb_in_endpoint_t *in_ep = &(USB0.in_ep_reg[0]);
uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress);
uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress);
// Unsupported endpoint numbers/size.
if ((desc_edpt->wMaxPacketSize.size > 64) || (epnum > 3)) {
return false;
}
xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, dir);
xfer->max_size = desc_edpt->wMaxPacketSize.size;
if (dir == TUSB_DIR_OUT) {
out_ep[epnum].doepctl |= USB_USBACTEP0_M |
desc_edpt->bmAttributes.xfer << USB_EPTYPE0_S |
desc_edpt->wMaxPacketSize.size << USB_MPS0_S;
USB0.daintmsk |= (1 << (16 + epnum));
} else {
// Peripheral FIFO architecture (Rev18 RM 29.11)
//
// --------------- 320 ( 1280 bytes )
// | IN FIFO 3 |
// --------------- y + x + 16 + GRXFSIZ
// | IN FIFO 2 |
// --------------- x + 16 + GRXFSIZ
// | IN FIFO 1 |
// --------------- 16 + GRXFSIZ
// | IN FIFO 0 |
// --------------- GRXFSIZ
// | OUT FIFO |
// | ( Shared ) |
// --------------- 0
//
// Since OUT FIFO = 50, FIFO 0 = 16, average of FIFOx = (312-50-16) / 3 = 82 ~ 80
in_ep[epnum].diepctl |= USB_D_USBACTEP1_M |
(epnum - 1) << USB_D_TXFNUM1_S |
desc_edpt->bmAttributes.xfer << USB_D_EPTYPE1_S |
(desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? (1 << USB_DI_SETD0PID1_S) : 0) |
desc_edpt->wMaxPacketSize.size << 0;
USB0.daintmsk |= (1 << (0 + epnum));
// Both TXFD and TXSA are in unit of 32-bit words
uint16_t const fifo_size = 80;
uint32_t const fifo_offset = (USB0.grxfsiz & USB_NPTXFDEP_V) + 16 + fifo_size * (epnum - 1);
USB0.dieptxf[epnum - 1] = (80 << USB_NPTXFDEP_S) | fifo_offset;
}
return true;
}
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
{
(void)rhport;
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, dir);
xfer->buffer = buffer;
xfer->total_len = total_bytes;
xfer->queued_len = 0;
xfer->short_packet = false;
uint16_t num_packets = (total_bytes / xfer->max_size);
uint8_t short_packet_size = total_bytes % xfer->max_size;
// Zero-size packet is special case.
if (short_packet_size > 0 || (total_bytes == 0)) {
num_packets++;
}
ESP_LOGV(TAG, "Transfer <-> EP%i, %s, pkgs: %i, bytes: %i",
epnum, ((dir == TUSB_DIR_IN) ? "USB0.HOST (in)" : "HOST->DEV (out)"),
num_packets, total_bytes);
// IN and OUT endpoint xfers are interrupt-driven, we just schedule them
// here.
if (dir == TUSB_DIR_IN) {
// A full IN transfer (multiple packets, possibly) triggers XFRC.
int bytes2fifo_left = total_bytes;
uint32_t val; // 32 bit val from 4 buff addresses
USB0.in_ep_reg[epnum].diepint = ~0U; // clear all ints
USB0.in_ep_reg[epnum].dieptsiz = (num_packets << USB_D_PKTCNT0_S) | total_bytes;
USB0.in_ep_reg[epnum].diepctl |= USB_D_EPENA1_M | USB_D_CNAK1_M; // Enable | CNAK
while (bytes2fifo_left > 0) { // TODO move it to ep_in_handle (IDF-1475)
/* ATTENTION! In cases when CFG_TUD_ENDOINT0_SIZE, CFG_TUD_CDC_EPSIZE, CFG_TUD_MIDI_EPSIZE or
CFG_TUD_MSC_BUFSIZE < 4 next line can be a cause of an error.*/
val = (*(buffer + 3) << 24) |
(*(buffer + 2) << 16) |
(*(buffer + 1) << 8) |
(*(buffer + 0) << 0);
ESP_LOGV(TAG, "Transfer 0x%08x -> FIFO%d", val, epnum);
USB0.fifo[epnum][0] = val; //copy and next buffer address
buffer += 4;
bytes2fifo_left -= 4;
}
// USB0.dtknqr4_fifoemptymsk |= (1 << epnum);
} else {
// Each complete packet for OUT xfers triggers XFRC.
USB0.out_ep_reg[epnum].doeptsiz = USB_PKTCNT0_M |
((xfer->max_size & USB_XFERSIZE0_V) << USB_XFERSIZE0_S);
USB0.out_ep_reg[epnum].doepctl |= USB_EPENA0_M | USB_CNAK0_M;
}
return true;
}
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
{
(void)rhport;
usb_out_endpoint_t *out_ep = &(USB0.out_ep_reg[0]);
usb_in_endpoint_t *in_ep = &(USB0.in_ep_reg[0]);
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
if (dir == TUSB_DIR_IN) {
// Only disable currently enabled non-control endpoint
if ((epnum == 0) || !(in_ep[epnum].diepctl & USB_D_EPENA1_M)) {
in_ep[epnum].diepctl |= (USB_DI_SNAK1_M | USB_D_STALL1_M);
} else {
// Stop transmitting packets and NAK IN xfers.
in_ep[epnum].diepctl |= USB_DI_SNAK1_M;
while ((in_ep[epnum].diepint & USB_DI_SNAK1_M) == 0)
;
// Disable the endpoint. Note that both SNAK and STALL are set here.
in_ep[epnum].diepctl |= (USB_DI_SNAK1_M | USB_D_STALL1_M |
USB_D_EPDIS1_M);
while ((in_ep[epnum].diepint & USB_D_EPDISBLD0_M) == 0)
;
in_ep[epnum].diepint = USB_D_EPDISBLD0_M;
}
// Flush the FIFO, and wait until we have confirmed it cleared.
USB0.grstctl |= ((epnum - 1) << USB_TXFNUM_S);
USB0.grstctl |= USB_TXFFLSH_M;
while ((USB0.grstctl & USB_TXFFLSH_M) != 0)
;
} else {
// Only disable currently enabled non-control endpoint
if ((epnum == 0) || !(out_ep[epnum].doepctl & USB_EPENA0_M)) {
out_ep[epnum].doepctl |= USB_STALL0_M;
} else {
// Asserting GONAK is required to STALL an OUT endpoint.
// Simpler to use polling here, we don't use the "B"OUTNAKEFF interrupt
// anyway, and it can't be cleared by user code. If this while loop never
// finishes, we have bigger problems than just the stack.
USB0.dctl |= USB_SGOUTNAK_M;
while ((USB0.gintsts & USB_GOUTNAKEFF_M) == 0)
;
// Ditto here- disable the endpoint. Note that only STALL and not SNAK
// is set here.
out_ep[epnum].doepctl |= (USB_STALL0_M | USB_EPDIS0_M);
while ((out_ep[epnum].doepint & USB_EPDISBLD0_M) == 0)
;
out_ep[epnum].doepint = USB_EPDISBLD0_M;
// Allow other OUT endpoints to keep receiving.
USB0.dctl |= USB_CGOUTNAK_M;
}
}
}
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
{
(void)rhport;
usb_out_endpoint_t *out_ep = &(USB0.out_ep_reg[0]);
usb_in_endpoint_t *in_ep = &(USB0.in_ep_reg[0]);
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
if (dir == TUSB_DIR_IN) {
in_ep[epnum].diepctl &= ~USB_D_STALL1_M;
uint8_t eptype = (in_ep[epnum].diepctl & USB_D_EPTYPE1_M) >> USB_D_EPTYPE1_S;
// Required by USB spec to reset DATA toggle bit to DATA0 on interrupt
// and bulk endpoints.
if (eptype == 2 || eptype == 3) {
in_ep[epnum].diepctl |= USB_DI_SETD0PID1_M;
}
} else {
out_ep[epnum].doepctl &= ~USB_STALL1_M;
uint8_t eptype = (out_ep[epnum].doepctl & USB_EPTYPE1_M) >> USB_EPTYPE1_S;
// Required by USB spec to reset DATA toggle bit to DATA0 on interrupt
// and bulk endpoints.
if (eptype == 2 || eptype == 3) {
out_ep[epnum].doepctl |= USB_DO_SETD0PID1_M;
}
}
}
/*------------------------------------------------------------------*/
static void receive_packet(xfer_ctl_t *xfer, /* usb_out_endpoint_t * out_ep, */ uint16_t xfer_size)
{
ESP_EARLY_LOGV(TAG, "USB - receive_packet");
uint32_t *rx_fifo = USB0.fifo[0];
// See above TODO
// uint16_t remaining = (out_ep->DOEPTSIZ & UsbDOEPTSIZ_XFRSIZ_Msk) >> UsbDOEPTSIZ_XFRSIZ_Pos;
// xfer->queued_len = xfer->total_len - remaining;
uint16_t remaining = xfer->total_len - xfer->queued_len;
uint16_t to_recv_size;
if (remaining <= xfer->max_size) {
// Avoid buffer overflow.
to_recv_size = (xfer_size > remaining) ? remaining : xfer_size;
} else {
// Room for full packet, choose recv_size based on what the microcontroller
// claims.
to_recv_size = (xfer_size > xfer->max_size) ? xfer->max_size : xfer_size;
}
uint8_t to_recv_rem = to_recv_size % 4;
uint16_t to_recv_size_aligned = to_recv_size - to_recv_rem;
// Do not assume xfer buffer is aligned.
uint8_t *base = (xfer->buffer + xfer->queued_len);
// This for loop always runs at least once- skip if less than 4 bytes
// to collect.
if (to_recv_size >= 4) {
for (uint16_t i = 0; i < to_recv_size_aligned; i += 4) {
uint32_t tmp = (*rx_fifo);
base[i] = tmp & 0x000000FF;
base[i + 1] = (tmp & 0x0000FF00) >> 8;
base[i + 2] = (tmp & 0x00FF0000) >> 16;
base[i + 3] = (tmp & 0xFF000000) >> 24;
}
}
// Do not read invalid bytes from RX FIFO.
if (to_recv_rem != 0) {
uint32_t tmp = (*rx_fifo);
uint8_t *last_32b_bound = base + to_recv_size_aligned;
last_32b_bound[0] = tmp & 0x000000FF;
if (to_recv_rem > 1) {
last_32b_bound[1] = (tmp & 0x0000FF00) >> 8;
}
if (to_recv_rem > 2) {
last_32b_bound[2] = (tmp & 0x00FF0000) >> 16;
}
}
xfer->queued_len += xfer_size;
// Per USB spec, a short OUT packet (including length 0) is always
// indicative of the end of a transfer (at least for ctl, bulk, int).
xfer->short_packet = (xfer_size < xfer->max_size);
}
static void transmit_packet(xfer_ctl_t *xfer, volatile usb_in_endpoint_t *in_ep, uint8_t fifo_num)
{
ESP_EARLY_LOGV(TAG, "USB - transmit_packet");
uint32_t *tx_fifo = USB0.fifo[0];
uint16_t remaining = (in_ep->dieptsiz & 0x7FFFFU) >> USB_D_XFERSIZE0_S;
xfer->queued_len = xfer->total_len - remaining;
uint16_t to_xfer_size = (remaining > xfer->max_size) ? xfer->max_size : remaining;
uint8_t to_xfer_rem = to_xfer_size % 4;
uint16_t to_xfer_size_aligned = to_xfer_size - to_xfer_rem;
// Buffer might not be aligned to 32b, so we need to force alignment
// by copying to a temp var.
uint8_t *base = (xfer->buffer + xfer->queued_len);
// This for loop always runs at least once- skip if less than 4 bytes
// to send off.
if (to_xfer_size >= 4) {
for (uint16_t i = 0; i < to_xfer_size_aligned; i += 4) {
uint32_t tmp = base[i] | (base[i + 1] << 8) |
(base[i + 2] << 16) | (base[i + 3] << 24);
(*tx_fifo) = tmp;
}
}
// Do not read beyond end of buffer if not divisible by 4.
if (to_xfer_rem != 0) {
uint32_t tmp = 0;
uint8_t *last_32b_bound = base + to_xfer_size_aligned;
tmp |= last_32b_bound[0];
if (to_xfer_rem > 1) {
tmp |= (last_32b_bound[1] << 8);
}
if (to_xfer_rem > 2) {
tmp |= (last_32b_bound[2] << 16);
}
(*tx_fifo) = tmp;
}
}
static void read_rx_fifo(void)
{
// Pop control word off FIFO (completed xfers will have 2 control words,
// we only pop one ctl word each interrupt).
volatile uint32_t ctl_word = USB0.grxstsp;
uint8_t pktsts = (ctl_word & USB_PKTSTS_M) >> USB_PKTSTS_S;
uint8_t epnum = (ctl_word & USB_CHNUM_M) >> USB_CHNUM_S;
uint16_t bcnt = (ctl_word & USB_BCNT_M) >> USB_BCNT_S;
switch (pktsts) {
case 0x01: // Global OUT NAK (Interrupt)
ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX type : Global OUT NAK");
break;
case 0x02: { // Out packet recvd
ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX type : Out packet");
xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
receive_packet(xfer, bcnt);
}
break;
case 0x03: // Out packet done (Interrupt)
ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX type : Out packet done");
break;
case 0x04: // Setup packet done (Interrupt)
if (s_setup_phase == 0) { // only if setup is started
s_setup_phase = 1;
ESP_EARLY_LOGV(TAG, "TUSB IRQ - setup_phase 1"); //finished
ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX : Setup packet done");
}
break;
case 0x06: { // Setup packet recvd
s_setup_phase = 0;
ESP_EARLY_LOGV(TAG, "TUSB IRQ - setup_phase 0"); // new setup process
// For some reason, it's possible to get a mismatch between
// how many setup packets were received versus the location
// of the Setup packet done word. This leads to situations
// where stale setup packets are in the RX FIFO that were received
// after the core loaded the Setup packet done word. Workaround by
// only accepting one setup packet at a time for now.
_setup_packet[0] = (USB0.grxstsp);
_setup_packet[1] = (USB0.grxstsp);
ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX : Setup packet : 0x%08x 0x%08x",
_setup_packet[0], _setup_packet[1]);
}
break;
default: // Invalid, do something here, like breakpoint?
break;
}
}
static void handle_epout_ints(void)
{
// GINTSTS will be cleared with DAINT == 0
// DAINT for a given EP clears when DOEPINTx is cleared.
// DOEPINT will be cleared when DAINT's out bits are cleared.
for (int n = 0; n < USB_OUT_EP_NUM; n++) {
xfer_ctl_t *xfer = XFER_CTL_BASE(n, TUSB_DIR_OUT);
if (USB0.daint & (1 << (16 + n))) {
// SETUP packet Setup Phase done.
if ((USB0.out_ep_reg[n].doepint & USB_SETUP0_M)) {
USB0.out_ep_reg[n].doepint |= USB_STUPPKTRCVD0_M | USB_SETUP0_M; // clear
if (s_setup_phase == 1) { // only if setup is done, but not handled
s_setup_phase = 2;
ESP_EARLY_LOGV(TAG, "TUSB IRQ - setup_phase 2"); // sending to a handling queue
ESP_EARLY_LOGV(TAG, "TUSB IRQ - EP OUT - Setup Phase done (irq-s 0x%08x)", USB0.out_ep_reg[n].doepint);
dcd_event_setup_received(0, (uint8_t *)&_setup_packet[0], true);
}
readyfor1setup_pkg(0);
}
// OUT XFER complete (single packet).q
if (USB0.out_ep_reg[n].doepint & USB_XFERCOMPL0_M) {
ESP_EARLY_LOGV(TAG, "TUSB IRQ - EP OUT - XFER complete (single packet)");
USB0.out_ep_reg[n].doepint = USB_XFERCOMPL0_M;
// Transfer complete if short packet or total len is transferred
if (xfer->short_packet || (xfer->queued_len == xfer->total_len)) {
xfer->short_packet = false;
dcd_event_xfer_complete(0, n, xfer->queued_len, XFER_RESULT_SUCCESS, true);
} else {
// Schedule another packet to be received.
USB0.out_ep_reg[n].doeptsiz = USB_PKTCNT0_M |
((xfer->max_size & USB_XFERSIZE0_V) << USB_XFERSIZE0_S);
USB0.out_ep_reg[n].doepctl |= USB_EPENA0_M | USB_CNAK0_M;
}
}
}
}
}
static void handle_epin_ints(void)
{
// GINTSTS will be cleared with DAINT == 0
// DAINT for a given EP clears when DIEPINTx is cleared.
// IEPINT will be cleared when DAINT's out bits are cleared.
for (uint32_t n = 0; n < USB_IN_EP_NUM; n++) {
xfer_ctl_t *xfer = &xfer_status[n][TUSB_DIR_IN];
if (USB0.daint & (1 << (0 + n))) {
ESP_EARLY_LOGV(TAG, "TUSB IRQ - EP IN %u", n);
// IN XFER complete (entire xfer).
if (USB0.in_ep_reg[n].diepint & USB_D_XFERCOMPL0_M) {
ESP_EARLY_LOGV(TAG, "TUSB IRQ - IN XFER complete!");
USB0.in_ep_reg[n].diepint = USB_D_XFERCOMPL0_M;
// USB0.dtknqr4_fifoemptymsk &= ~(1 << n); // Turn off TXFE b/c xfer inactive.
dcd_event_xfer_complete(0, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true);
}
// XFER FIFO empty
if (USB0.in_ep_reg[n].diepint & USB_D_XFERCOMPL0_M) {
ESP_EARLY_LOGV(TAG, "TUSB IRQ - IN XFER FIFO empty!");
USB0.in_ep_reg[n].diepint = USB_D_TXFEMP0_M;
transmit_packet(xfer, &USB0.in_ep_reg[n], n);
}
}
}
}
static void dcd_int_handler(void)
{
uint32_t int_status = USB0.gintsts;
uint32_t int_msk = USB0.gintmsk;
if (int_status & USB_DISCONNINT_M) {
ESP_EARLY_LOGV(TAG, "dcd_int_handler - disconnected");
USB0.gintsts = USB_DISCONNINT_M;
}
if (int_status & USB_USBRST_M) {
ESP_EARLY_LOGV(TAG, "dcd_int_handler - reset");
USB0.gintsts = USB_USBRST_M;
bus_reset();
}
if (int_status & USB_RESETDET_M) {
ESP_EARLY_LOGV(TAG, "dcd_int_handler - reset while suspend");
USB0.gintsts = USB_RESETDET_M;
bus_reset();
}
if (int_status & USB_ENUMDONE_M) {
// ENUMDNE detects speed of the link. For full-speed, we
// always expect the same value. This interrupt is considered
// the end of reset.
USB0.gintsts = USB_ENUMDONE_M;
enum_done_processing();
dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
}
if (int_status & USB_SOF_M) {
USB0.gintsts = USB_SOF_M;
dcd_event_bus_signal(0, DCD_EVENT_SOF, true); // do nothing actually
}
if ((int_status & USB_RXFLVI_M) & (int_msk & USB_RXFLVIMSK_M)) {
ESP_EARLY_LOGV(TAG, "dcd_int_handler - rx!");
read_rx_fifo();
}
// OUT endpoint interrupt handling.
if (int_status & USB_OEPINT_M) {
ESP_EARLY_LOGV(TAG, "dcd_int_handler - OUT endpoint!");
handle_epout_ints();
}
// IN endpoint interrupt handling.
if (int_status & USB_IEPINT_M) {
ESP_EARLY_LOGV(TAG, "dcd_int_handler - IN endpoint!");
handle_epin_ints();
}
// Without handling
USB0.gintsts |= USB_CURMOD_INT_M |
USB_MODEMIS_M |
USB_OTGINT_M |
USB_NPTXFEMP_M |
USB_GINNAKEFF_M |
USB_GOUTNAKEFF |
USB_ERLYSUSP_M |
USB_USBSUSP_M |
USB_ISOOUTDROP_M |
USB_EOPF_M |
USB_EPMIS_M |
USB_INCOMPISOIN_M |
USB_INCOMPIP_M |
USB_FETSUSP_M |
USB_PTXFEMP_M;
}
void dcd_int_enable(uint8_t rhport)
{
(void)rhport;
esp_intr_alloc(ETS_USB_INTR_SOURCE, ESP_INTR_FLAG_LOWMED, (intr_handler_t)dcd_int_handler, NULL, &usb_ih);
}
void dcd_int_disable(uint8_t rhport)
{
(void)rhport;
esp_intr_free(usb_ih);
}

View file

@ -0,0 +1,58 @@
// Copyright 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 "tinyusb.h"
#include "hal/usb_hal.h"
/**
* @brief Initializes the tinyUSB driver.
*
* Note: Do not change any Custom descriptor, but
* if it used it is recomended to define: bDeviceClass = TUSB_CLASS_MISC,
* bDeviceSubClass = MISC_SUBCLASS_COMMON and bDeviceClass = TUSB_CLASS_MISC
* to match with Interface Association Descriptor (IAD) for CDC
*
* @param config if equal to NULL the default descriptor will be used
* @return esp_err_t Errors during the initialization
*/
esp_err_t tinyusb_driver_install(const tinyusb_config_t *config)
{
tusb_desc_device_t *descriptor;
char **string_descriptor;
// Hal init
usb_hal_context_t hal = {
.use_external_phy = config->external_phy
};
usb_hal_init(&hal);
if (config->descriptor == NULL) {
descriptor = &descriptor_kconfig;
} else {
descriptor = config->descriptor;
}
if (config->string_descriptor == NULL) {
string_descriptor = descriptor_str_kconfig;
} else {
string_descriptor = config->string_descriptor;
}
tusb_set_descriptor(descriptor,
string_descriptor);
ESP_ERROR_CHECK(tusb_init());
return ESP_OK;
}

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

View file

@ -0,0 +1,7 @@
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32s2)
project(tusb_sample_descriptor)

View file

@ -0,0 +1,70 @@
# TinyUSB Sample Descriptor
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This example is demonstrating how to set up ESP32-S2 chip to work as a Generic USB Device with a user-defined descriptor. You can specify a manufacturer, device's name, ID and other USB-devices parameters responsible for identification by host.
As a USB stack, a TinyUSB component is used.
## How to use example
### Hardware Required
- Any board with the ESP32-S2 chip
### Configure the project
There are two ways to set up a descriptor - using Menuconfig tool and in-code
#### In-code setting up
For the manual descriptor's configuration use the default example's settings and modify `tusb_sample_descriptor.c` according to your needs
#### Menuconfig
If you want to set up the desctiptor using Menuconfig UI:
1. Execute in the terminal from the example's directory: `idf.py menuconfig`
2. Turn off `Set up a USB descriptor manually in code` parameter at `Example Configuration`
3. Folow to `Component config -> TinyUSB -> Descriptor configuration` for all available configurations.
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```bash
idf.py -p PORT flash monitor
```
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
After the flashing you should see the output:
```
I (314) example: USB initialization
I (314) TUSB:descriptors_control: Setting of a descriptor:
.bDeviceClass = 0
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
.bMaxPacketSize0 = 64,
.idVendor = 0x0000303a,
.idProduct = 0x00004000,
.bcdDevice = 0x00000100,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01
I (344) example: USB initialization DONE
I (354) example: USB task started
```

View file

@ -0,0 +1,2 @@
idf_component_register(SRCS "tusb_sample_descriptor.c"
INCLUDE_DIRS . ${COMPONENT_DIR})

View file

@ -0,0 +1,10 @@
menu "Example Configuration"
config EXAMPLE_MANUAL_DESC
bool "Set up a USB descriptor manually in code"
default y
help
You can set up a descriptor using Menuconfig or independently of
your project configuration - manually in code
endmenu

View file

@ -0,0 +1,84 @@
/* USB Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdlib.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "sdkconfig.h"
#include "tinyusb.h"
static const char *TAG = "example";
// USB Device Driver task
// This top level thread processes all usb events and invokes callbacks
static void usb_device_task(void *param) {
(void)param;
ESP_LOGI(TAG, "USB task started");
while (1) {
tud_task(); // RTOS forever loop
}
}
void app_main(void) {
ESP_LOGI(TAG, "USB initialization");
#if CONFIG_EXAMPLE_MANUAL_DESC
// Setting of descriptor. You can use descriptor_tinyusb and
// descriptor_str_tinyusb as a reference
tusb_desc_device_t my_descriptor = {
.bLength = sizeof(my_descriptor),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200, // USB version. 0x0200 means version 2.0
.bDeviceClass = TUSB_CLASS_UNSPECIFIED,
.bMaxPacketSize0 = CFG_TUD_ENDOINT0_SIZE,
.idVendor = 0x303A,
.idProduct = 0x3000,
.bcdDevice = 0x0101, // Device FW version
.iManufacturer = 0x01, // see string_descriptor[1] bellow
.iProduct = 0x02, // see string_descriptor[2] bellow
.iSerialNumber = 0x03, // see string_descriptor[3] bellow
.bNumConfigurations = 0x01};
tusb_desc_strarray_device_t my_string_descriptor = {
// array of pointer to string descriptors
(char[]){0x09, 0x04}, // 0: is supported language is English (0x0409)
"I", // 1: Manufacturer
"My Custom Device", // 2: Product
"012-345", // 3: Serials, should use chip ID
};
tinyusb_config_t tusb_cfg = {
.descriptor = &my_descriptor,
.string_descriptor = my_string_descriptor,
.external_phy = false // In the most cases you need to use a `false` value
};
#else
tinyusb_config_t tusb_cfg = {
.descriptor = NULL,
.string_descriptor = NULL,
.external_phy = false // In the most cases you need to use a `false` value
};
#endif
ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));
ESP_LOGI(TAG, "USB initialization DONE");
// Create a task for tinyusb device stack:
xTaskCreate(usb_device_task, "usbd", 4096, NULL, 5, NULL);
return;
}

View file

@ -0,0 +1,5 @@
CONFIG_USB_ENABLED=y
CONFIG_USB_DESC_USE_ESPRESSIF_VID=n
CONFIG_USB_DESC_CUSTOM_VID=0x303A
CONFIG_USB_DESC_USE_DEFAULT_PID=n
CONFIG_USB_DESC_CUSTOM_PID=0x3000

View file

@ -1,20 +1,71 @@
#!/bin/bash
# While we support GNU Make & CMake together, check the same examples are present for both
# While we support GNU Make & CMake together, check the same examples are present for both. But only for ESP32
CMAKE_EXAMPLE_PATHS=$( find ${IDF_PATH}/examples/ -type f -name CMakeLists.txt | grep -v "/components/" | grep -v "/common_components/" | grep -v "/cxx/experimental/experimental_cpp_component/" | grep -v "/main/" | grep -v "/build_system/cmake/" | grep -v "/mb_example_common/")
MAKE_EXAMPLE_PATHS=$( find ${IDF_PATH}/examples/ -type f -name Makefile | grep -v "/build_system/cmake/")
CMAKE_EXAMPLE_PATHS="$(/usr/bin/dirname $CMAKE_EXAMPLE_PATHS | sort -n)"
MAKE_EXAMPLE_PATHS="$(/usr/bin/dirname $MAKE_EXAMPLE_PATHS | sort -n)"
echo "- Getting paths of CMakeLists and Makefiles"
IFS=
CMAKELISTS=$( find ${IDF_PATH}/examples/ -type f -name CMakeLists.txt | grep -v "/components/" | grep -v "/common_components/" | grep -v "/cxx/experimental/experimental_cpp_component/" | grep -v "/main/" | grep -v "/build_system/cmake/" | grep -v "/mb_example_common/" | sort -n)
MAKEFILES=$( find ${IDF_PATH}/examples/ -type f -name Makefile | grep -v "/build_system/cmake/" | sort -n)
echo " [DONE]"
echo "- Building the ignore list"
IGNORE_LIST=
while read line
do
STL=$(grep "set[(]SUPPORTED_TARGETS" $line)
if test -n $STL # if specified SUPPORTED_TARGETS
then
WO_ESP32=$(grep -v -w "esp32" <<< $STL)
if test -n "$WO_ESP32" # if not specified esp32 at SUPPORTED_TARGETS
then # then consider that the example should not have a Makefile
PATH2IGNORE=$(/usr/bin/dirname $line)
echo " Adding to ignore $PATH2IGNORE"
IGNORE_LIST="$IGNORE_LIST$PATH2IGNORE
"
fi
fi
done <<< $CMAKELISTS
IGNORE_LIST=$(grep -v -e '^$' <<< $IGNORE_LIST) # remove empty lines
echo -e " [DONE] - Ignore list:\n$IGNORE_LIST\n"
echo "- Applying the Ignore list"
while read line
do
echo $line
CMAKELISTS=$(echo $CMAKELISTS | grep -v $line)
MAKEFILES=$(echo $MAKEFILES | grep -v $line)
done <<< $IGNORE_LIST
echo " [DONE]"
echo "- Getting paths of examples"
while read line
do
new_path="$(/usr/bin/dirname $line)"
CMAKE_EXAMPLE_PATHS="$CMAKE_EXAMPLE_PATHS$new_path
"
done <<< $CMAKELISTS
CMAKE_EXAMPLE_PATHS=$(grep -v -e '^$' <<< $CMAKE_EXAMPLE_PATHS) # remove empty lines
while read line
do
new_path="$(/usr/bin/dirname $line)"
MAKE_EXAMPLE_PATHS="$MAKE_EXAMPLE_PATHS$new_path
"
done <<< $MAKEFILES
MAKE_EXAMPLE_PATHS=$(grep -v -e '^$' <<< $MAKE_EXAMPLE_PATHS) # remove empty lines
echo " [DONE]"
echo "- Analysing matches"
MISMATCH=$(comm -3 <(echo "$MAKE_EXAMPLE_PATHS") <(echo "$CMAKE_EXAMPLE_PATHS"))
if [ -n "$MISMATCH" ]; then
echo "Some examples are not in both CMake and GNU Make:"
echo " [ERROR] Some examples are not in both CMake and GNU Make:"
echo "$MISMATCH"
exit 1
fi
echo "Example lists match"
echo " [DONE] Example lists match"
exit 0