Compare commits

..

No commits in common. "master" and "fix-esp32-max3421e" have entirely different histories.

64 changed files with 2003 additions and 3223 deletions

View file

@ -46,7 +46,7 @@ body:
- type: input
attributes:
label: TinyUSB Library version
placeholder: "Release version or commit SHA"
placeholder: "Release version or github latest"
validations:
required: true
@ -100,11 +100,3 @@ body:
description: If applicable, add screenshots to help explain your problem.
validations:
required: false
- type: checkboxes
attributes:
label: I have checked existing issues, pr, discussion and documentation
description: You agree to check all the resources above before opening a new issue.
options:
- label: I confirm I have checked existing issues, pr, discussion and documentation.
required: true

View file

@ -38,7 +38,7 @@ jobs:
run: bash ci/doxy_gen_and_deploy.sh
# ---------------------------------------
# build
# Main
# ---------------------------------------
build:
runs-on: ubuntu-latest
@ -83,3 +83,53 @@ jobs:
- name: test platforms
run: python3 ci/build_platform.py ${{ matrix.arduino-platform }}
# ---------------------------------------
# Build ESP32 v2
# ---------------------------------------
build-esp32-v2:
if: false
runs-on: ubuntu-latest
needs: pre-commit
strategy:
fail-fast: false
matrix:
arduino-platform:
- 'feather_esp32s2'
- 'feather_esp32s3'
esp32-version:
- '2.0.17'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Checkout adafruit/ci-arduino
uses: actions/checkout@v4
with:
repository: adafruit/ci-arduino
path: ci
- name: pre-install
run: bash ci/actions_install.sh
- name: Install arduino-esp32 v2 and Libraries
env:
BSP_URLS: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
run: |
arduino-cli core install esp32:esp32@${{ matrix.esp32-version }} --additional-urls $BSP_URLS
arduino-cli lib install ${{ env.ARDUINO_LIBS }}
arduino-cli core list
arduino-cli lib list
- name: Create custom build script
working-directory: ${{ github.workspace }}/ci
run: |
echo 'import build_platform' > build_esp32_v2.py
echo 'build_platform.test_examples_in_folder("'${{ matrix.arduino-platform }}'", build_platform.BUILD_DIR)' >> build_esp32_v2.py
echo 'exit(build_platform.success)' >> build_esp32_v2.py
cat build_esp32_v2.py
- name: test platforms
run: |
python3 ci/build_esp32_v2.py

View file

@ -59,14 +59,14 @@ static void rp2040_configure_pio_usb(void) {
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("Core1 setup to run TinyUSB host with pio-usb");
// Check for CPU frequency, must be multiple of 12 Mhz for bit-banging USB
// Check for CPU frequency, must be multiple of 120Mhz for bit-banging USB
uint32_t cpu_hz = clock_get_hz(clk_sys);
if (cpu_hz % 12000000UL) {
if (cpu_hz != 120000000UL && cpu_hz != 240000000UL) {
while (!Serial) {
delay(10); // wait for native usb
}
Serial.printf("Error: CPU Clock = %lu, PIO USB require CPU clock must be multiple of 12 Mhz\r\n", cpu_hz);
Serial.printf("Change your CPU Clock to 12*n Mhz in Menu->CPU Speed \r\n");
Serial.printf("Error: CPU Clock = %lu, PIO USB require CPU clock must be multiple of 120 Mhz\r\n", cpu_hz);
Serial.printf("Change your CPU Clock to either 120 or 240 Mhz in Menu->CPU Speed \r\n");
while (1) {
delay(1);
}

View file

@ -59,14 +59,14 @@ static void rp2040_configure_pio_usb(void) {
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("Core1 setup to run TinyUSB host with pio-usb");
// Check for CPU frequency, must be multiple of 12 Mhz for bit-banging USB
// Check for CPU frequency, must be multiple of 120Mhz for bit-banging USB
uint32_t cpu_hz = clock_get_hz(clk_sys);
if (cpu_hz % 12000000UL) {
if (cpu_hz != 120000000UL && cpu_hz != 240000000UL) {
while (!Serial) {
delay(10); // wait for native usb
}
Serial.printf("Error: CPU Clock = %lu, PIO USB require CPU clock must be multiple of 12 Mhz\r\n", cpu_hz);
Serial.printf("Change your CPU Clock to 12*n Mhz in Menu->CPU Speed \r\n");
Serial.printf("Error: CPU Clock = %lu, PIO USB require CPU clock must be multiple of 120 Mhz\r\n", cpu_hz);
Serial.printf("Change your CPU Clock to either 120 or 240 Mhz in Menu->CPU Speed \r\n");
while (1) {
delay(1);
}

View file

@ -59,14 +59,14 @@ static void rp2040_configure_pio_usb(void) {
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("Core1 setup to run TinyUSB host with pio-usb");
// Check for CPU frequency, must be multiple of 12 Mhz for bit-banging USB
// Check for CPU frequency, must be multiple of 120Mhz for bit-banging USB
uint32_t cpu_hz = clock_get_hz(clk_sys);
if (cpu_hz % 12000000UL) {
if (cpu_hz != 120000000UL && cpu_hz != 240000000UL) {
while (!Serial) {
delay(10); // wait for native usb
}
Serial.printf("Error: CPU Clock = %lu, PIO USB require CPU clock must be multiple of 12 Mhz\r\n", cpu_hz);
Serial.printf("Change your CPU Clock to 12*n Mhz in Menu->CPU Speed \r\n");
Serial.printf("Error: CPU Clock = %lu, PIO USB require CPU clock must be multiple of 120 Mhz\r\n", cpu_hz);
Serial.printf("Change your CPU Clock to either 120 or 240 Mhz in Menu->CPU Speed \r\n");
while (1) {
delay(1);
}

View file

@ -59,14 +59,14 @@ static void rp2040_configure_pio_usb(void) {
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("Core1 setup to run TinyUSB host with pio-usb");
// Check for CPU frequency, must be multiple of 12 Mhz for bit-banging USB
// Check for CPU frequency, must be multiple of 120Mhz for bit-banging USB
uint32_t cpu_hz = clock_get_hz(clk_sys);
if (cpu_hz % 12000000UL) {
if (cpu_hz != 120000000UL && cpu_hz != 240000000UL) {
while (!Serial) {
delay(10); // wait for native usb
}
Serial.printf("Error: CPU Clock = %lu, PIO USB require CPU clock must be multiple of 12 Mhz\r\n", cpu_hz);
Serial.printf("Change your CPU Clock to 12*n Mhz in Menu->CPU Speed \r\n");
Serial.printf("Error: CPU Clock = %lu, PIO USB require CPU clock must be multiple of 120 Mhz\r\n", cpu_hz);
Serial.printf("Change your CPU Clock to either 120 or 240 Mhz in Menu->CPU Speed \r\n");
while (1) {
delay(1);
}

View file

@ -59,14 +59,14 @@ static void rp2040_configure_pio_usb(void) {
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("Core1 setup to run TinyUSB host with pio-usb");
// Check for CPU frequency, must be multiple of 12 Mhz for bit-banging USB
// Check for CPU frequency, must be multiple of 120Mhz for bit-banging USB
uint32_t cpu_hz = clock_get_hz(clk_sys);
if (cpu_hz % 12000000UL) {
if (cpu_hz != 120000000UL && cpu_hz != 240000000UL) {
while (!Serial) {
delay(10); // wait for native usb
}
Serial.printf("Error: CPU Clock = %lu, PIO USB require CPU clock must be multiple of 12 Mhz\r\n", cpu_hz);
Serial.printf("Change your CPU Clock to 12*n Mhz in Menu->CPU Speed \r\n");
Serial.printf("Error: CPU Clock = %lu, PIO USB require CPU clock must be multiple of 120 Mhz\r\n", cpu_hz);
Serial.printf("Change your CPU Clock to either 120 or 240 Mhz in Menu->CPU Speed \r\n");
while (1) {
delay(1);
}

View file

@ -59,14 +59,14 @@ static void rp2040_configure_pio_usb(void) {
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("Core1 setup to run TinyUSB host with pio-usb");
// Check for CPU frequency, must be multiple of 12 Mhz for bit-banging USB
// Check for CPU frequency, must be multiple of 120Mhz for bit-banging USB
uint32_t cpu_hz = clock_get_hz(clk_sys);
if (cpu_hz % 12000000UL) {
if (cpu_hz != 120000000UL && cpu_hz != 240000000UL) {
while (!Serial) {
delay(10); // wait for native usb
}
Serial.printf("Error: CPU Clock = %lu, PIO USB require CPU clock must be multiple of 12 Mhz\r\n", cpu_hz);
Serial.printf("Change your CPU Clock to 12*n Mhz in Menu->CPU Speed \r\n");
Serial.printf("Error: CPU Clock = %lu, PIO USB require CPU clock must be multiple of 120 Mhz\r\n", cpu_hz);
Serial.printf("Change your CPU Clock to either 120 or 240 Mhz in Menu->CPU Speed \r\n");
while (1) {
delay(1);
}

View file

@ -59,14 +59,14 @@ static void rp2040_configure_pio_usb(void) {
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("Core1 setup to run TinyUSB host with pio-usb");
// Check for CPU frequency, must be multiple of 12 Mhz for bit-banging USB
// Check for CPU frequency, must be multiple of 120Mhz for bit-banging USB
uint32_t cpu_hz = clock_get_hz(clk_sys);
if (cpu_hz % 12000000UL) {
if (cpu_hz != 120000000UL && cpu_hz != 240000000UL) {
while (!Serial) {
delay(10); // wait for native usb
}
Serial.printf("Error: CPU Clock = %lu, PIO USB require CPU clock must be multiple of 12 Mhz\r\n", cpu_hz);
Serial.printf("Change your CPU Clock to 12*n Mhz in Menu->CPU Speed \r\n");
Serial.printf("Error: CPU Clock = %lu, PIO USB require CPU clock must be multiple of 120 Mhz\r\n", cpu_hz);
Serial.printf("Change your CPU Clock to either 120 or 240 Mhz in Menu->CPU Speed \r\n");
while (1) {
delay(1);
}

View file

@ -59,14 +59,14 @@ static void rp2040_configure_pio_usb(void) {
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("Core1 setup to run TinyUSB host with pio-usb");
// Check for CPU frequency, must be multiple of 12 Mhz for bit-banging USB
// Check for CPU frequency, must be multiple of 120Mhz for bit-banging USB
uint32_t cpu_hz = clock_get_hz(clk_sys);
if (cpu_hz % 12000000UL) {
if (cpu_hz != 120000000UL && cpu_hz != 240000000UL) {
while (!Serial) {
delay(10); // wait for native usb
}
Serial.printf("Error: CPU Clock = %lu, PIO USB require CPU clock must be multiple of 12 Mhz\r\n", cpu_hz);
Serial.printf("Change your CPU Clock to 12*n Mhz in Menu->CPU Speed \r\n");
Serial.printf("Error: CPU Clock = %lu, PIO USB require CPU clock must be multiple of 120 Mhz\r\n", cpu_hz);
Serial.printf("Change your CPU Clock to either 120 or 240 Mhz in Menu->CPU Speed \r\n");
while (1) {
delay(1);
}

View file

@ -33,62 +33,38 @@
// for flashTransport definition
#include "flash_config.h"
Adafruit_SPIFlash flash(&flashTransport);
// External Flash File system
FatVolume fatfs;
//--------------------------------------------------------------------+
// SDCard Config
//--------------------------------------------------------------------+
#if defined(ARDUINO_PYPORTAL_M4) || defined(ARDUINO_PYPORTAL_M4_TITANO)
// PyPortal has on-board card reader
#define SDCARD_CS 32
#define SDCARD_DETECT 33
#define SDCARD_DETECT_ACTIVE HIGH
#elif defined(ARDUINO_ADAFRUIT_METRO_RP2040)
#define SDIO_CLK_PIN 18
#define SDIO_CMD_PIN 19 // MOSI
#define SDIO_DAT0_PIN 20 // DAT1: 21, DAT2: 22, DAT3: 23
#define SDCARD_DETECT 15
#define SDCARD_DETECT_ACTIVE LOW
#elif defined(ARDUINO_ADAFRUIT_METRO_RP2350)
// Note: not working yet (need troubleshoot later)
#define SDIO_CLK_PIN 34
#define SDIO_CMD_PIN 35 // MOSI
#define SDIO_DAT0_PIN 36 // DAT1: 37, DAT2: 38, DAT3: 39
#define SDCARD_DETECT 40
#define SDCARD_DETECT_ACTIVE LOW
#elif defined(ARDUINO_ADAFRUIT_FRUITJAM_RP2350)
#define SDIO_CLK_PIN 34
#define SDIO_CMD_PIN 35 // MOSI
#define SDIO_DAT0_PIN 36 // DAT1: 37, DAT2: 38, DAT3: 39
#define SDCARD_DETECT 33
#define SDCARD_DETECT_ACTIVE LOW
#else
// Use SPI, no detect
#define SDCARD_CS 10
// no detect
#endif
#if defined(SDIO_CLK_PIN) && defined(SDIO_CMD_PIN) && defined(SDIO_DAT0_PIN)
#define SD_CONFIG SdioConfig(SDIO_CLK_PIN, SDIO_CMD_PIN, SDIO_DAT0_PIN)
#else
#define SD_CONFIG SdSpiConfig(SDCARD_CS, SHARED_SPI, SD_SCK_MHZ(50))
#endif
// File system on SD Card
// SDCard File system
SdFat sd;
// USB Mass Storage object
Adafruit_USBD_MSC usb_msc;
// Set to true when PC write to flash
bool sd_changed = false;
bool sd_inited = false;
bool flash_formatted = false;
bool flash_changed = false;
// the setup function runs once when you press reset or power the board
void setup() {
#ifdef LED_BUILTIN
@ -117,11 +93,14 @@ void setup() {
//------------- Lun 0 for external flash -------------//
flash.begin();
flash_formatted = fatfs.begin(&flash);
usb_msc.setCapacity(0, flash.size()/512, 512);
usb_msc.setReadWriteCallback(0, external_flash_read_cb, external_flash_write_cb, external_flash_flush_cb);
usb_msc.setUnitReady(0, true);
flash_changed = true; // to print contents initially
//------------- Lun 1 for SD card -------------//
#ifdef SDCARD_DETECT
// DETECT pin is available, detect card present on the fly with test unit ready
@ -139,21 +118,32 @@ void setup() {
delay(1000);
}
bool init_sdcard(void) {
bool init_sdcard(void)
{
Serial.print("Init SDCard ... ");
if (!sd.begin(SD_CONFIG)) {
Serial.println("initialization failed. Things to check:");
Serial.println("- is a card inserted?");
Serial.println("- is your wiring correct?");
Serial.println("- did you change the SDCARD_CS or SDIO pin to match your shield or module?");
if ( !sd.begin(SDCARD_CS, SD_SCK_MHZ(50)) )
{
Serial.print("Failed ");
sd.errorPrint("sd.begin() failed");
return false;
}
uint32_t block_count = sd.card()->sectorCount();
uint32_t block_count;
#if SD_FAT_VERSION >= 20000
block_count = sd.card()->sectorCount();
#else
block_count = sd.card()->cardSize();
#endif
usb_msc.setCapacity(1, block_count, 512);
usb_msc.setReadWriteCallback(1, sdcard_read_cb, sdcard_write_cb, sdcard_flush_cb);
sd_changed = true; // to print contents initially
Serial.print("OK, Card size = ");
Serial.print((block_count / (1024*1024)) * 512);
Serial.println(" MB");
@ -161,17 +151,20 @@ bool init_sdcard(void) {
return true;
}
void print_rootdir(File32* rdir) {
void print_rootdir(File32* rdir)
{
File32 file;
// Open next file in root.
// Warning, openNext starts at the current directory position
// so a rewind of the directory may be required.
while (file.openNext(rdir, O_RDONLY)) {
while ( file.openNext(rdir, O_RDONLY) )
{
file.printFileSize(&Serial);
Serial.write(' ');
file.printName(&Serial);
if (file.isDir()) {
if ( file.isDir() )
{
// Indicate a directory.
Serial.write('/');
}
@ -180,8 +173,46 @@ void print_rootdir(File32* rdir) {
}
}
void loop() {
// nothing to do
void loop()
{
if ( flash_changed )
{
if (!flash_formatted)
{
flash_formatted = fatfs.begin(&flash);
}
// skip if still not formatted
if (flash_formatted)
{
File32 root;
root = fatfs.open("/");
Serial.println("Flash contents:");
print_rootdir(&root);
Serial.println();
root.close();
}
flash_changed = false;
}
if ( sd_changed )
{
File32 root;
root = sd.open("/");
Serial.println("SD contents:");
print_rootdir(&root);
Serial.println();
root.close();
sd_changed = false;
}
delay(1000); // refresh every 1 second
}
@ -189,8 +220,16 @@ void loop() {
// SD Card callbacks
//--------------------------------------------------------------------+
int32_t sdcard_read_cb (uint32_t lba, void* buffer, uint32_t bufsize) {
bool rc = sd.card()->readSectors(lba, (uint8_t*) buffer, bufsize/512);
int32_t sdcard_read_cb (uint32_t lba, void* buffer, uint32_t bufsize)
{
bool rc;
#if SD_FAT_VERSION >= 20000
rc = sd.card()->readSectors(lba, (uint8_t*) buffer, bufsize/512);
#else
rc = sd.card()->readBlocks(lba, (uint8_t*) buffer, bufsize/512);
#endif
return rc ? bufsize : -1;
}
@ -198,18 +237,35 @@ int32_t sdcard_read_cb (uint32_t lba, void* buffer, uint32_t bufsize) {
// Process data in buffer to disk's storage and
// return number of written bytes (must be multiple of block size)
int32_t sdcard_write_cb (uint32_t lba, uint8_t* buffer, uint32_t bufsize) {
bool rc;
#ifdef LED_BUILTIN
digitalWrite(LED_BUILTIN, HIGH);
#endif
bool rc = sd.card()->writeSectors(lba, buffer, bufsize/512);
#if SD_FAT_VERSION >= 20000
rc = sd.card()->writeSectors(lba, buffer, bufsize/512);
#else
rc = sd.card()->writeBlocks(lba, buffer, bufsize/512);
#endif
return rc ? bufsize : -1;
}
// Callback invoked when WRITE10 command is completed (status received and accepted by host).
// used to flush any pending cache.
void sdcard_flush_cb (void) {
void sdcard_flush_cb (void)
{
#if SD_FAT_VERSION >= 20000
sd.card()->syncDevice();
sd.cacheClear(); // clear file system's cache to force refresh
#else
sd.card()->syncBlocks();
#endif
// clear file system's cache to force refresh
sd.cacheClear();
sd_changed = true;
#ifdef LED_BUILTIN
digitalWrite(LED_BUILTIN, LOW);
@ -219,18 +275,24 @@ void sdcard_flush_cb (void) {
#ifdef SDCARD_DETECT
// Invoked when received Test Unit Ready command.
// return true allowing host to read/write this LUN e.g SD card inserted
bool sdcard_ready_callback(void) {
bool sdcard_ready_callback(void)
{
// Card is inserted
if (digitalRead(SDCARD_DETECT) == SDCARD_DETECT_ACTIVE) {
if ( digitalRead(SDCARD_DETECT) == HIGH )
{
// init SD card if not already
if (!sd_inited) {
if ( !sd_inited )
{
sd_inited = init_sdcard();
}
} else {
}else
{
sd_inited = false;
usb_msc.setReadWriteCallback(1, NULL, NULL, NULL);
}
Serial.println(sd_inited);
return sd_inited;
}
#endif
@ -265,6 +327,12 @@ int32_t external_flash_write_cb (uint32_t lba, uint8_t* buffer, uint32_t bufsize
// used to flush any pending cache.
void external_flash_flush_cb (void) {
flash.syncBlocks();
// clear file system's cache to force refresh
fatfs.cacheClear();
flash_changed = true;
#ifdef LED_BUILTIN
digitalWrite(LED_BUILTIN, LOW);
#endif

View file

@ -10,69 +10,34 @@
*********************************************************************/
/* This example expose SD card as mass storage using
* - SdFat https://github.com/adafruit/SdFat
* SdFat Library
*/
#include "SPI.h"
#include "SdFat_Adafruit_Fork.h"
#include "Adafruit_TinyUSB.h"
//--------------------------------------------------------------------+
// SDCard Config
//--------------------------------------------------------------------+
#if defined(ARDUINO_PYPORTAL_M4) || defined(ARDUINO_PYPORTAL_M4_TITANO)
// PyPortal has on-board card reader
#define SDCARD_CS 32
#define SDCARD_DETECT 33
#define SDCARD_DETECT_ACTIVE HIGH
#elif defined(ARDUINO_ADAFRUIT_METRO_RP2040)
#define SDIO_CLK_PIN 18
#define SDIO_CMD_PIN 19 // MOSI
#define SDIO_DAT0_PIN 20 // DAT1: 21, DAT2: 22, DAT3: 23
#define SDCARD_DETECT 15
#define SDCARD_DETECT_ACTIVE LOW
#elif defined(ARDUINO_ADAFRUIT_METRO_RP2350)
#define SDIO_CLK_PIN 34
#define SDIO_CMD_PIN 35 // MOSI
#define SDIO_DAT0_PIN 36 // DAT1: 37, DAT2: 38, DAT3: 39
#define SDCARD_DETECT 40
#define SDCARD_DETECT_ACTIVE LOW
#elif defined(ARDUINO_ADAFRUIT_FRUITJAM_RP2350)
#define SDIO_CLK_PIN 34
#define SDIO_CMD_PIN 35 // MOSI
#define SDIO_DAT0_PIN 36 // DAT1: 37, DAT2: 38, DAT3: 39
#define SDCARD_DETECT 33
#define SDCARD_DETECT_ACTIVE LOW
#else
// Use SPI, no detect
#define SDCARD_CS 10
#endif
#if defined(SDIO_CLK_PIN) && defined(SDIO_CMD_PIN) && defined(SDIO_DAT0_PIN)
#define SD_CONFIG SdioConfig(SDIO_CLK_PIN, SDIO_CMD_PIN, SDIO_DAT0_PIN)
#else
#define SD_CONFIG SdSpiConfig(SDCARD_CS, SHARED_SPI, SD_SCK_MHZ(50))
#endif
const int chipSelect = 10;
// File system on SD Card
SdFat sd;
SdFile root;
SdFile file;
// USB Mass Storage object
Adafruit_USBD_MSC usb_msc;
// Set to true when PC write to flash
bool fs_changed;
// the setup function runs once when you press reset or power the board
void setup() {
Serial.begin(115200);
#ifdef LED_BUILTIN
pinMode(LED_BUILTIN, OUTPUT);
#endif
Serial.begin(115200);
// Set disk vendor id, product id and revision with string up to 8, 16, 4 characters respectively
usb_msc.setID("Adafruit", "SD Card", "1.0");
@ -95,17 +60,23 @@ void setup() {
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("Adafruit TinyUSB Mass Storage SD Card example");
Serial.print("\nInitializing SD card ... ");
Serial.print("CS = "); Serial.println(chipSelect);
if (!sd.begin(SD_CONFIG)) {
if ( !sd.begin(chipSelect, SD_SCK_MHZ(50)) ) {
Serial.println("initialization failed. Things to check:");
Serial.println("- is a card inserted?");
Serial.println("- is your wiring correct?");
Serial.println("- did you change the SDCARD_CS or SDIO pin to match your shield or module?");
Serial.println("* is a card inserted?");
Serial.println("* is your wiring correct?");
Serial.println("* did you change the chipSelect pin to match your shield or module?");
while (1) delay(1);
}
// Size in blocks (512 bytes)
#if SD_FAT_VERSION >= 20000
uint32_t block_count = sd.card()->sectorCount();
#else
uint32_t block_count = sd.card()->cardSize();
#endif
Serial.print("Volume size (MB): ");
Serial.println((block_count/2) / 1024);
@ -114,17 +85,51 @@ void setup() {
// MSC is ready for read/write
usb_msc.setUnitReady(true);
fs_changed = true; // to print contents initially
}
void loop() {
// noting to do
if ( fs_changed ) {
root.open("/");
Serial.println("SD contents:");
// Open next file in root.
// Warning, openNext starts at the current directory position
// so a rewind of the directory may be required.
while ( file.openNext(&root, O_RDONLY) ) {
file.printFileSize(&Serial);
Serial.write(' ');
file.printName(&Serial);
if ( file.isDir() ) {
// Indicate a directory.
Serial.write('/');
}
Serial.println();
file.close();
}
root.close();
Serial.println();
fs_changed = false;
delay(1000); // refresh every 0.5 second
}
}
// Callback invoked when received READ10 command.
// Copy disk's data to buffer (up to bufsize) and
// return number of copied bytes (must be multiple of block size)
int32_t msc_read_cb (uint32_t lba, void* buffer, uint32_t bufsize) {
bool rc = sd.card()->readSectors(lba, (uint8_t*) buffer, bufsize/512);
bool rc;
#if SD_FAT_VERSION >= 20000
rc = sd.card()->readSectors(lba, (uint8_t*) buffer, bufsize/512);
#else
rc = sd.card()->readBlocks(lba, (uint8_t*) buffer, bufsize/512);
#endif
return rc ? bufsize : -1;
}
@ -132,18 +137,34 @@ int32_t msc_read_cb (uint32_t lba, void* buffer, uint32_t bufsize) {
// Process data in buffer to disk's storage and
// return number of written bytes (must be multiple of block size)
int32_t msc_write_cb (uint32_t lba, uint8_t* buffer, uint32_t bufsize) {
bool rc;
#ifdef LED_BUILTIN
digitalWrite(LED_BUILTIN, HIGH);
#endif
bool rc = sd.card()->writeSectors(lba, buffer, bufsize/512);
#if SD_FAT_VERSION >= 20000
rc = sd.card()->writeSectors(lba, buffer, bufsize/512);
#else
rc = sd.card()->writeBlocks(lba, buffer, bufsize/512);
#endif
return rc ? bufsize : -1;
}
// Callback invoked when WRITE10 command is completed (status received and accepted by host).
// used to flush any pending cache.
void msc_flush_cb (void) {
#if SD_FAT_VERSION >= 20000
sd.card()->syncDevice();
sd.cacheClear(); // clear file system's cache to force refresh
#else
sd.card()->syncBlocks();
#endif
// clear file system's cache to force refresh
sd.cacheClear();
fs_changed = true;
#ifdef LED_BUILTIN
digitalWrite(LED_BUILTIN, LOW);

View file

@ -2,7 +2,7 @@
"name": "Adafruit TinyUSB Library",
"keywords": "usb, arduino, tinyusb",
"description": "Arduino library for TinyUSB",
"version": "3.7.1",
"version": "3.4.3",
"repository": {
"type": "git",
"url": "https://github.com/adafruit/Adafruit_TinyUSB_Arduino.git"
@ -10,7 +10,8 @@
"frameworks": "*",
"platforms": "*",
"build": {
"libArchive": false
"libArchive": false,
"flags": "-DUSE_TINYUSB"
},
"authors":
[

View file

@ -1,5 +1,5 @@
name=Adafruit TinyUSB Library
version=3.7.1
version=3.4.3
author=Adafruit
maintainer=Adafruit <info@adafruit.com>
sentence=TinyUSB library for Arduino

View file

@ -186,7 +186,7 @@ void Adafruit_USBD_Device::clearConfiguration(void) {
uint8_t const dev_cfg[sizeof(tusb_desc_configuration_t)] = {
TUD_CONFIG_DESCRIPTOR(1, 0, 0, sizeof(tusb_desc_configuration_t),
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP | TU_BIT(7),
USB_CONFIG_POWER),
100),
};
memcpy(_desc_cfg_buffer, dev_cfg, sizeof(tusb_desc_configuration_t));

View file

@ -80,21 +80,6 @@ public:
// Clear/Reset configuration descriptor
void clearConfiguration(void);
// Set configuration attribute
void setConfigurationAttribute(uint8_t attribute) {
_desc_cfg[offsetof(tusb_desc_configuration_t, bmAttributes)] = attribute;
}
// Set max power consumption in mA (absolute max is 510ma)
bool setConfigurationMaxPower(uint16_t power_ma) {
if (power_ma > 255 * 2u) {
return false;
}
_desc_cfg[offsetof(tusb_desc_configuration_t, bMaxPower)] =
(uint8_t)(power_ma / 2);
return true;
}
// Provide user buffer for configuration descriptor, if total length > 256
void setConfigurationBuffer(uint8_t *buf, uint32_t buflen);

View file

@ -661,7 +661,6 @@ typedef struct TU_ATTR_PACKED
uint16_t wTotalLength ; ///< Total number of bytes returned for the class-specific AudioControl interface descriptor. Includes the combined length of this descriptor header and all Clock Source, Unit and Terminal descriptors.
uint8_t bmControls ; ///< See: audio_cs_ac_interface_control_pos_t.
} audio_desc_cs_ac_interface_t;
TU_VERIFY_STATIC(sizeof(audio_desc_cs_ac_interface_t) == 9, "size is not correct");
/// AUDIO Clock Source Descriptor (4.7.2.1)
typedef struct TU_ATTR_PACKED

View file

@ -208,15 +208,15 @@ tu_static CFG_TUD_MEM_SECTION struct {
#endif// CFG_TUD_AUDIO_ENABLE_EP_OUT && (USE_LINEAR_BUFFER || CFG_TUD_AUDIO_ENABLE_DECODING)
// Control buffers
tu_static CFG_TUD_MEM_SECTION struct {
TUD_EPBUF_DEF(buf1, CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ);
tu_static uint8_t ctrl_buf_1[CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ];
#if CFG_TUD_AUDIO > 1
TUD_EPBUF_DEF(buf2, CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ);
tu_static uint8_t ctrl_buf_2[CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ];
#endif
#if CFG_TUD_AUDIO > 2
TUD_EPBUF_DEF(buf3, CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ);
tu_static uint8_t ctrl_buf_3[CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ];
#endif
} ctrl_buf;
// Active alternate setting of interfaces
tu_static uint8_t alt_setting_1[CFG_TUD_AUDIO_FUNC_1_N_AS_INT];
@ -628,6 +628,10 @@ static uint8_t audiod_get_audio_fct_idx(audiod_function_t *audio);
#if (CFG_TUD_AUDIO_ENABLE_EP_IN && (CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL || CFG_TUD_AUDIO_ENABLE_ENCODING)) || (CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING)
static void audiod_parse_for_AS_params(audiod_function_t *audio, uint8_t const *p_desc, uint8_t const *p_desc_end, uint8_t const as_itf);
static inline uint8_t tu_desc_subtype(void const *desc) {
return ((uint8_t const *) desc)[2];
}
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
@ -1223,18 +1227,18 @@ void audiod_init(void) {
// Initialize control buffers
switch (i) {
case 0:
audio->ctrl_buf = ctrl_buf.buf1;
audio->ctrl_buf = ctrl_buf_1;
audio->ctrl_buf_sz = CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ;
break;
#if CFG_TUD_AUDIO > 1 && CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ > 0
case 1:
audio->ctrl_buf = ctrl_buf.buf2;
audio->ctrl_buf = ctrl_buf_2;
audio->ctrl_buf_sz = CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ;
break;
#endif
#if CFG_TUD_AUDIO > 2 && CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ > 0
case 2:
audio->ctrl_buf = ctrl_buf.buf3;
audio->ctrl_buf = ctrl_buf_3;
audio->ctrl_buf_sz = CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ;
break;
#endif

View file

@ -82,7 +82,7 @@ typedef struct {
static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC];
CFG_TUD_MEM_SECTION static cdcd_epbuf_t _cdcd_epbuf[CFG_TUD_CDC];
static tud_cdc_configure_t _cdcd_cfg = TUD_CDC_CONFIGURE_DEFAULT();
static tud_cdc_configure_fifo_t _cdcd_fifo_cfg;
static bool _prep_out_transaction(uint8_t itf) {
const uint8_t rhport = 0;
@ -119,9 +119,9 @@ static bool _prep_out_transaction(uint8_t itf) {
// APPLICATION API
//--------------------------------------------------------------------+
bool tud_cdc_configure(const tud_cdc_configure_t* driver_cfg) {
TU_VERIFY(driver_cfg);
_cdcd_cfg = *driver_cfg;
bool tud_cdc_configure_fifo(const tud_cdc_configure_fifo_t* cfg) {
TU_VERIFY(cfg);
_cdcd_fifo_cfg = (*cfg);
return true;
}
@ -175,7 +175,7 @@ void tud_cdc_n_read_flush(uint8_t itf) {
//--------------------------------------------------------------------+
uint32_t tud_cdc_n_write(uint8_t itf, const void* buffer, uint32_t bufsize) {
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
uint16_t wr_count = tu_fifo_write_n(&p_cdc->tx_ff, buffer, (uint16_t) TU_MIN(bufsize, UINT16_MAX));
uint16_t ret = tu_fifo_write_n(&p_cdc->tx_ff, buffer, (uint16_t) TU_MIN(bufsize, UINT16_MAX));
// flush if queue more than packet size
if (tu_fifo_count(&p_cdc->tx_ff) >= BULK_PACKET_SIZE
@ -186,7 +186,7 @@ uint32_t tud_cdc_n_write(uint8_t itf, const void* buffer, uint32_t bufsize) {
tud_cdc_n_write_flush(itf);
}
return wr_count;
return ret;
}
uint32_t tud_cdc_n_write_flush(uint8_t itf) {
@ -233,6 +233,8 @@ bool tud_cdc_n_write_clear(uint8_t itf) {
//--------------------------------------------------------------------+
void cdcd_init(void) {
tu_memclr(_cdcd_itf, sizeof(_cdcd_itf));
tu_memclr(&_cdcd_fifo_cfg, sizeof(_cdcd_fifo_cfg));
for (uint8_t i = 0; i < CFG_TUD_CDC; i++) {
cdcd_interface_t* p_cdc = &_cdcd_itf[i];
@ -247,10 +249,10 @@ void cdcd_init(void) {
// Config RX fifo
tu_fifo_config(&p_cdc->rx_ff, p_cdc->rx_ff_buf, TU_ARRAY_SIZE(p_cdc->rx_ff_buf), 1, false);
// TX fifo can be configured to change to overwritable if not connected (DTR bit not set). Without DTR we do not
// know if data is actually polled by terminal. This way the most current data is prioritized.
// Default: is overwritable
tu_fifo_config(&p_cdc->tx_ff, p_cdc->tx_ff_buf, TU_ARRAY_SIZE(p_cdc->tx_ff_buf), 1, _cdcd_cfg.tx_overwritabe_if_not_connected);
// Config TX fifo as overwritable at initialization and will be changed to non-overwritable
// if terminal supports DTR bit. Without DTR we do not know if data is actually polled by terminal.
// In this way, the most current data is prioritized.
tu_fifo_config(&p_cdc->tx_ff, p_cdc->tx_ff_buf, TU_ARRAY_SIZE(p_cdc->tx_ff_buf), 1, true);
#if OSAL_MUTEX_REQUIRED
osal_mutex_t mutex_rd = osal_mutex_create(&p_cdc->rx_ff_mutex);
@ -292,13 +294,13 @@ void cdcd_reset(uint8_t rhport) {
cdcd_interface_t* p_cdc = &_cdcd_itf[i];
tu_memclr(p_cdc, ITF_MEM_RESET_SIZE);
if (!_cdcd_cfg.rx_persistent) {
if (!_cdcd_fifo_cfg.rx_persistent) {
tu_fifo_clear(&p_cdc->rx_ff);
}
if (!_cdcd_cfg.tx_persistent) {
if (!_cdcd_fifo_cfg.tx_persistent) {
tu_fifo_clear(&p_cdc->tx_ff);
}
tu_fifo_set_overwritable(&p_cdc->tx_ff, _cdcd_cfg.tx_overwritabe_if_not_connected);
tu_fifo_set_overwritable(&p_cdc->tx_ff, true);
}
}
@ -412,12 +414,8 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_requ
p_cdc->line_state = (uint8_t) request->wValue;
// If enabled: fifo overwriting is disabled if DTR bit is set and vice versa
if (_cdcd_cfg.tx_overwritabe_if_not_connected) {
// Disable fifo overwriting if DTR bit is set
tu_fifo_set_overwritable(&p_cdc->tx_ff, !dtr);
} else {
tu_fifo_set_overwritable(&p_cdc->tx_ff, false);
}
TU_LOG_DRV(" Set Control Line State: DTR = %d, RTS = %d\r\n", dtr, rts);
@ -498,7 +496,7 @@ bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_
// xferred_bytes is multiple of EP Packet size and not zero
if (!tu_fifo_count(&p_cdc->tx_ff) && xferred_bytes && (0 == (xferred_bytes & (BULK_PACKET_SIZE - 1)))) {
if (usbd_edpt_claim(rhport, p_cdc->ep_in)) {
TU_ASSERT(usbd_edpt_xfer(rhport, p_cdc->ep_in, NULL, 0));
usbd_edpt_xfer(rhport, p_cdc->ep_in, NULL, 0);
}
}
}

View file

@ -48,24 +48,14 @@
//--------------------------------------------------------------------+
// Driver Configuration
//--------------------------------------------------------------------+
typedef struct TU_ATTR_PACKED {
uint8_t rx_persistent : 1; // keep rx fifo data even with bus reset or disconnect
uint8_t tx_persistent : 1; // keep tx fifo data even with reset or disconnect
uint8_t tx_overwritabe_if_not_connected : 1; // if not connected, tx fifo can be overwritten
} tud_cdc_configure_t;
uint8_t rx_persistent : 1; // keep rx fifo on bus reset or disconnect
uint8_t tx_persistent : 1; // keep tx fifo on bus reset or disconnect
} tud_cdc_configure_fifo_t;
#define TUD_CDC_CONFIGURE_DEFAULT() { \
.rx_persistent = 0, \
.tx_persistent = 0, \
.tx_overwritabe_if_not_connected = 1, \
}
// Configure CDC driver behavior
bool tud_cdc_configure(const tud_cdc_configure_t* driver_cfg);
// Backward compatible
#define tud_cdc_configure_fifo_t tud_cdc_configure_t
#define tud_cdc_configure_fifo tud_cdc_configure
// Configure CDC FIFOs behavior
bool tud_cdc_configure_fifo(tud_cdc_configure_fifo_t const* cfg);
//--------------------------------------------------------------------+
// Application API (Multiple Ports) i.e. CFG_TUD_CDC > 1

View file

@ -672,7 +672,7 @@ void cdch_close(uint8_t daddr) {
bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) {
// TODO handle stall response, retry failed transfer ...
TU_VERIFY(event == XFER_RESULT_SUCCESS);
TU_ASSERT(event == XFER_RESULT_SUCCESS);
uint8_t const idx = get_idx_by_ep_addr(daddr, ep_addr);
cdch_interface_t * p_cdc = get_itf(idx);
@ -691,10 +691,10 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t
}
} else if ( ep_addr == p_cdc->stream.rx.ep_addr ) {
#if CFG_TUH_CDC_FTDI
if (p_cdc->serial_drid == SERIAL_DRIVER_FTDI && xferred_bytes > 2) {
if (p_cdc->serial_drid == SERIAL_DRIVER_FTDI) {
// FTDI reserve 2 bytes for status
// uint8_t status[2] = {p_cdc->stream.rx.ep_buf[0], p_cdc->stream.rx.ep_buf[1]};
tu_edpt_stream_read_xfer_complete_with_buf(&p_cdc->stream.rx, p_cdc->stream.rx.ep_buf+2, xferred_bytes-2);
tu_edpt_stream_read_xfer_complete_offset(&p_cdc->stream.rx, xferred_bytes, 2);
}else
#endif
{

View file

@ -49,22 +49,22 @@
// RX FIFO size
#ifndef CFG_TUH_CDC_RX_BUFSIZE
#define CFG_TUH_CDC_RX_BUFSIZE TUH_EPSIZE_BULK_MPS
#define CFG_TUH_CDC_RX_BUFSIZE USBH_EPSIZE_BULK_MAX
#endif
// RX Endpoint size
#ifndef CFG_TUH_CDC_RX_EPSIZE
#define CFG_TUH_CDC_RX_EPSIZE TUH_EPSIZE_BULK_MPS
#define CFG_TUH_CDC_RX_EPSIZE USBH_EPSIZE_BULK_MAX
#endif
// TX FIFO size
#ifndef CFG_TUH_CDC_TX_BUFSIZE
#define CFG_TUH_CDC_TX_BUFSIZE TUH_EPSIZE_BULK_MPS
#define CFG_TUH_CDC_TX_BUFSIZE USBH_EPSIZE_BULK_MAX
#endif
// TX Endpoint size
#ifndef CFG_TUH_CDC_TX_EPSIZE
#define CFG_TUH_CDC_TX_EPSIZE TUH_EPSIZE_BULK_MPS
#define CFG_TUH_CDC_TX_EPSIZE USBH_EPSIZE_BULK_MAX
#endif
//--------------------------------------------------------------------+

View file

@ -444,7 +444,7 @@ bool hidh_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t
hidh_epbuf_t* epbuf = get_hid_epbuf(idx);
if (dir == TUSB_DIR_IN) {
TU_LOG_DRV(" [idx=%u] Get Report callback\r\n", idx);
TU_LOG_DRV(" Get Report callback (%u, %u)\r\n", daddr, idx);
TU_LOG3_MEM(epbuf->epin, xferred_bytes, 2);
tuh_hid_report_received_cb(daddr, idx, epbuf->epin, (uint16_t) xferred_bytes);
} else {
@ -461,9 +461,7 @@ void hidh_close(uint8_t daddr) {
hidh_interface_t* p_hid = &_hidh_itf[i];
if (p_hid->daddr == daddr) {
TU_LOG_DRV(" HIDh close addr = %u index = %u\r\n", daddr, i);
if (tuh_hid_umount_cb) {
tuh_hid_umount_cb(daddr, i);
}
if (tuh_hid_umount_cb) tuh_hid_umount_cb(daddr, i);
tu_memclr(p_hid, sizeof(hidh_interface_t));
}
}

View file

@ -24,8 +24,13 @@
* This file is part of the TinyUSB stack.
*/
#ifndef TUSB_MIDI_H_
#define TUSB_MIDI_H_
/** \ingroup group_class
* \defgroup ClassDriver_CDC Communication Device Class (CDC)
* Currently only Abstract Control Model subclass is supported
* @{ */
#ifndef _TUSB_MIDI_H__
#define _TUSB_MIDI_H__
#include "common/tusb_common.h"
@ -34,31 +39,30 @@
#endif
//--------------------------------------------------------------------+
// Constants
// Class Specific Descriptor
//--------------------------------------------------------------------+
enum {
MIDI_VERSION_1_0 = 0x0100,
MIDI_VERSION_2_0 = 0x0200,
};
typedef enum {
typedef enum
{
MIDI_CS_INTERFACE_HEADER = 0x01,
MIDI_CS_INTERFACE_IN_JACK = 0x02,
MIDI_CS_INTERFACE_OUT_JACK = 0x03,
MIDI_CS_INTERFACE_ELEMENT = 0x04,
} midi_cs_interface_subtype_t;
typedef enum {
MIDI_CS_ENDPOINT_GENERAL = 0x01,
MIDI_CS_ENDPOINT_GENERAL_2_0 = 0x02,
typedef enum
{
MIDI_CS_ENDPOINT_GENERAL = 0x01
} midi_cs_endpoint_subtype_t;
typedef enum {
typedef enum
{
MIDI_JACK_EMBEDDED = 0x01,
MIDI_JACK_EXTERNAL = 0x02
} midi_jack_type_t;
typedef enum {
typedef enum
{
MIDI_CIN_MISC = 0,
MIDI_CIN_CABLE_EVENT = 1,
MIDI_CIN_SYSCOM_2BYTE = 2, // 2 byte system common message e.g MTC, SongSelect
@ -78,7 +82,8 @@ typedef enum {
} midi_code_index_number_t;
// MIDI 1.0 status byte
enum {
enum
{
//------------- System Exclusive -------------//
MIDI_STATUS_SYSEX_START = 0xF0,
MIDI_STATUS_SYSEX_END = 0xF7,
@ -101,26 +106,19 @@ enum {
MIDI_STATUS_SYSREAL_SYSTEM_RESET = 0xFF,
};
enum {
MIDI_MAX_DATA_VAL = 0x7F,
};
//--------------------------------------------------------------------+
// Class Specific Descriptor
//--------------------------------------------------------------------+
/// MIDI Interface Header Descriptor
typedef struct TU_ATTR_PACKED {
typedef struct TU_ATTR_PACKED
{
uint8_t bLength ; ///< Size of this descriptor in bytes.
uint8_t bDescriptorType; ///< must be TUSB_DESC_CS_INTERFACE
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
uint8_t bDescriptorSubType ; ///< Descriptor SubType
uint16_t bcdMSC ; ///< MidiStreaming SubClass release number in Binary-Coded Decimal
uint16_t wTotalLength ;
} midi_desc_header_t;
TU_VERIFY_STATIC(sizeof(midi_desc_header_t) == 7, "size is not correct");
/// MIDI In Jack Descriptor
typedef struct TU_ATTR_PACKED {
typedef struct TU_ATTR_PACKED
{
uint8_t bLength ; ///< Size of this descriptor in bytes.
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
uint8_t bDescriptorSubType ; ///< Descriptor SubType
@ -128,9 +126,25 @@ typedef struct TU_ATTR_PACKED {
uint8_t bJackID ; ///< Unique ID for MIDI IN Jack
uint8_t iJack ; ///< string descriptor
} midi_desc_in_jack_t;
TU_VERIFY_STATIC(sizeof(midi_desc_in_jack_t) == 6, "size is not correct");
/// MIDI Out Jack Descriptor with multiple input pins
/// MIDI Out Jack Descriptor with single pin
typedef struct TU_ATTR_PACKED
{
uint8_t bLength ; ///< Size of this descriptor in bytes.
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
uint8_t bDescriptorSubType ; ///< Descriptor SubType
uint8_t bJackType ; ///< Embedded or External
uint8_t bJackID ; ///< Unique ID for MIDI IN Jack
uint8_t bNrInputPins;
uint8_t baSourceID;
uint8_t baSourcePin;
uint8_t iJack ; ///< string descriptor
} midi_desc_out_jack_t ;
/// MIDI Out Jack Descriptor with multiple pins
#define midi_desc_out_jack_n_t(input_num) \
struct TU_ATTR_PACKED { \
uint8_t bLength ; \
@ -142,13 +156,30 @@ TU_VERIFY_STATIC(sizeof(midi_desc_in_jack_t) == 6, "size is not correct");
struct TU_ATTR_PACKED { \
uint8_t baSourceID; \
uint8_t baSourcePin; \
} input[input_num]; \
} pins[input_num]; \
uint8_t iJack ; \
}
typedef midi_desc_out_jack_n_t(1) midi_desc_out_jack_1in_t; // 1 input
typedef midi_desc_out_jack_1in_t midi_desc_out_jack_t; // backward compatible
TU_VERIFY_STATIC(sizeof(midi_desc_out_jack_1in_t) == 7 + 2 * 1, "size is not correct");
/// MIDI Element Descriptor
typedef struct TU_ATTR_PACKED
{
uint8_t bLength ; ///< Size of this descriptor in bytes.
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
uint8_t bDescriptorSubType ; ///< Descriptor SubType
uint8_t bElementID;
uint8_t bNrInputPins;
uint8_t baSourceID;
uint8_t baSourcePin;
uint8_t bNrOutputPins;
uint8_t bInTerminalLink;
uint8_t bOutTerminalLink;
uint8_t bElCapsSize;
uint16_t bmElementCaps;
uint8_t iElement;
} midi_desc_element_t;
/// MIDI Element Descriptor with multiple pins
#define midi_desc_element_n_t(input_num) \
@ -170,32 +201,12 @@ TU_VERIFY_STATIC(sizeof(midi_desc_out_jack_1in_t) == 7 + 2 * 1, "size is not cor
uint8_t iElement; \
}
// This descriptor follows the standard bulk data endpoint descriptor
#define midi_desc_cs_endpoint_n_t(jack_num) \
struct TU_ATTR_PACKED { \
uint8_t bLength; \
uint8_t bDescriptorType; \
uint8_t bDescriptorSubType; \
uint8_t bNumEmbMIDIJack; \
uint8_t baAssocJackID[jack_num]; \
}
typedef midi_desc_cs_endpoint_n_t() midi_desc_cs_endpoint_t; // empty/flexible jack list
typedef midi_desc_cs_endpoint_n_t(1) midi_desc_cs_endpoint_1jack_t;
TU_VERIFY_STATIC(sizeof(midi_desc_cs_endpoint_1jack_t) == 4+1, "size is not correct");
//--------------------------------------------------------------------+
// For Internal Driver Use
//--------------------------------------------------------------------+
typedef struct {
uint8_t buffer[4];
uint8_t index;
uint8_t total;
} midi_driver_stream_t;
/** @} */
#ifdef __cplusplus
}
#endif
#endif
/** @} */

View file

@ -39,6 +39,13 @@
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
typedef struct {
uint8_t buffer[4];
uint8_t index;
uint8_t total;
} midid_stream_t;
typedef struct {
uint8_t itf_num;
uint8_t ep_in;
@ -47,8 +54,8 @@ typedef struct {
// For Stream read()/write() API
// Messages are always 4 bytes long, queue them for reading and writing so the
// callers can use the Stream interface with single-byte read/write calls.
midi_driver_stream_t stream_write;
midi_driver_stream_t stream_read;
midid_stream_t stream_write;
midid_stream_t stream_read;
/*------------- From this point, data is not cleared by bus reset -------------*/
// FIFO
@ -115,7 +122,7 @@ uint32_t tud_midi_n_available(uint8_t itf, uint8_t cable_num)
(void) cable_num;
midid_interface_t* midi = &_midid_itf[itf];
const midi_driver_stream_t* stream = &midi->stream_read;
const midid_stream_t* stream = &midi->stream_read;
// when using with packet API stream total & index are both zero
return tu_fifo_count(&midi->rx_ff) + (uint8_t) (stream->total - stream->index);
@ -129,7 +136,7 @@ uint32_t tud_midi_n_stream_read(uint8_t itf, uint8_t cable_num, void* buffer, ui
uint8_t* buf8 = (uint8_t*) buffer;
midid_interface_t* midi = &_midid_itf[itf];
midi_driver_stream_t* stream = &midi->stream_read;
midid_stream_t* stream = &midi->stream_read;
uint32_t total_read = 0;
while( bufsize )
@ -234,7 +241,7 @@ uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, const uint8_t*
midid_interface_t* midi = &_midid_itf[itf];
TU_VERIFY(midi->ep_in, 0);
midi_driver_stream_t* stream = &midi->stream_write;
midid_stream_t* stream = &midi->stream_write;
uint32_t i = 0;
while ( (i < bufsize) && (tu_fifo_remaining(&midi->tx_ff) >= 4) )
@ -422,22 +429,22 @@ void midid_reset(uint8_t rhport)
}
}
uint16_t midid_open(uint8_t rhport, const tusb_desc_interface_t* desc_itf, uint16_t max_len) {
uint16_t drv_len = 0;
uint8_t const * p_desc = (uint8_t const *)desc_itf;
// 1st Interface is Audio Control v1 (optional)
if (TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass &&
uint16_t midid_open(uint8_t rhport, const tusb_desc_interface_t* desc_itf, uint16_t max_len)
{
// 1st Interface is Audio Control v1
TU_VERIFY(TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass &&
AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass &&
AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_itf->bInterfaceProtocol) {
drv_len = tu_desc_len(desc_itf);
p_desc = tu_desc_next(desc_itf);
AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_itf->bInterfaceProtocol, 0);
uint16_t drv_len = tu_desc_len(desc_itf);
const uint8_t* p_desc = tu_desc_next(desc_itf);
// Skip Class Specific descriptors
while (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len) {
while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len )
{
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
}
}
// 2nd Interface is MIDI Streaming
TU_VERIFY(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0);

View file

@ -1,622 +0,0 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* This file is part of the TinyUSB stack.
*/
#include "tusb_option.h"
#if (CFG_TUH_ENABLED && CFG_TUH_MIDI)
#include "host/usbh.h"
#include "host/usbh_pvt.h"
#include "midi_host.h"
// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
#ifndef CFG_TUH_MIDI_LOG_LEVEL
#define CFG_TUH_MIDI_LOG_LEVEL CFG_TUH_LOG_LEVEL
#endif
#define TU_LOG_DRV(...) TU_LOG(CFG_TUH_MIDI_LOG_LEVEL, __VA_ARGS__)
//--------------------------------------------------------------------+
// Weak stubs: invoked if no strong implementation is available
//--------------------------------------------------------------------+
TU_ATTR_WEAK void tuh_midi_descriptor_cb(uint8_t idx, const tuh_midi_descriptor_cb_t * desc_cb_data) { (void) idx; (void) desc_cb_data; }
TU_ATTR_WEAK void tuh_midi_mount_cb(uint8_t idx, const tuh_midi_mount_cb_t* mount_cb_data) { (void) idx; (void) mount_cb_data; }
TU_ATTR_WEAK void tuh_midi_umount_cb(uint8_t idx) { (void) idx; }
TU_ATTR_WEAK void tuh_midi_rx_cb(uint8_t idx, uint32_t xferred_bytes) { (void) idx; (void) xferred_bytes; }
TU_ATTR_WEAK void tuh_midi_tx_cb(uint8_t idx, uint32_t xferred_bytes) { (void) idx; (void) xferred_bytes; }
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
typedef struct {
uint8_t daddr;
uint8_t bInterfaceNumber; // interface number of MIDI streaming
uint8_t iInterface;
uint8_t itf_count; // number of interface including Audio Control + MIDI streaming
uint8_t ep_in; // IN endpoint address
uint8_t ep_out; // OUT endpoint address
uint8_t rx_cable_count; // IN endpoint CS descriptor bNumEmbMIDIJack value
uint8_t tx_cable_count; // OUT endpoint CS descriptor bNumEmbMIDIJack value
#if CFG_TUH_MIDI_STREAM_API
// For Stream read()/write() API
// Messages are always 4 bytes long, queue them for reading and writing so the
// callers can use the Stream interface with single-byte read/write calls.
midi_driver_stream_t stream_write;
midi_driver_stream_t stream_read;
#endif
// Endpoint stream
struct {
tu_edpt_stream_t tx;
tu_edpt_stream_t rx;
uint8_t rx_ff_buf[CFG_TUH_MIDI_RX_BUFSIZE];
uint8_t tx_ff_buf[CFG_TUH_MIDI_TX_BUFSIZE];
} ep_stream;
bool mounted;
}midih_interface_t;
typedef struct {
TUH_EPBUF_DEF(tx, TUH_EPSIZE_BULK_MPS);
TUH_EPBUF_DEF(rx, TUH_EPSIZE_BULK_MPS);
} midih_epbuf_t;
static midih_interface_t _midi_host[CFG_TUH_MIDI];
CFG_TUH_MEM_SECTION static midih_epbuf_t _midi_epbuf[CFG_TUH_MIDI];
//--------------------------------------------------------------------+
// Helper
//--------------------------------------------------------------------+
TU_ATTR_ALWAYS_INLINE static inline uint8_t find_new_midi_index(void) {
for (uint8_t idx = 0; idx < CFG_TUH_MIDI; idx++) {
if (_midi_host[idx].daddr == 0) {
return idx;
}
}
return TUSB_INDEX_INVALID_8;
}
static inline uint8_t get_idx_by_ep_addr(uint8_t daddr, uint8_t ep_addr) {
for (uint8_t idx = 0; idx < CFG_TUH_MIDI; idx++) {
const midih_interface_t *p_midi = &_midi_host[idx];
if ((p_midi->daddr == daddr) &&
(ep_addr == p_midi->ep_stream.rx.ep_addr || ep_addr == p_midi->ep_stream.tx.ep_addr)) {
return idx;
}
}
return TUSB_INDEX_INVALID_8;
}
//--------------------------------------------------------------------+
// USBH API
//--------------------------------------------------------------------+
bool midih_init(void) {
tu_memclr(&_midi_host, sizeof(_midi_host));
for (int inst = 0; inst < CFG_TUH_MIDI; inst++) {
midih_interface_t *p_midi_host = &_midi_host[inst];
tu_edpt_stream_init(&p_midi_host->ep_stream.rx, true, false, false,
p_midi_host->ep_stream.rx_ff_buf, CFG_TUH_MIDI_RX_BUFSIZE, _midi_epbuf->rx, TUH_EPSIZE_BULK_MPS);
tu_edpt_stream_init(&p_midi_host->ep_stream.tx, true, true, false,
p_midi_host->ep_stream.tx_ff_buf, CFG_TUH_MIDI_TX_BUFSIZE, _midi_epbuf->tx, TUH_EPSIZE_BULK_MPS);
}
return true;
}
bool midih_deinit(void) {
for (size_t i = 0; i < CFG_TUH_MIDI; i++) {
midih_interface_t* p_midi = &_midi_host[i];
tu_edpt_stream_deinit(&p_midi->ep_stream.rx);
tu_edpt_stream_deinit(&p_midi->ep_stream.tx);
}
return true;
}
void midih_close(uint8_t daddr) {
for (uint8_t idx = 0; idx < CFG_TUH_MIDI; idx++) {
midih_interface_t* p_midi = &_midi_host[idx];
if (p_midi->daddr == daddr) {
TU_LOG_DRV(" MIDI close addr = %u index = %u\r\n", daddr, idx);
tuh_midi_umount_cb(idx);
p_midi->ep_in = 0;
p_midi->ep_out = 0;
p_midi->bInterfaceNumber = 0;
p_midi->rx_cable_count = 0;
p_midi->tx_cable_count = 0;
p_midi->daddr = 0;
p_midi->mounted = false;
#if CFG_TUH_MIDI_STREAM_API
tu_memclr(&p_midi->stream_read, sizeof(p_midi->stream_read));
tu_memclr(&p_midi->stream_write, sizeof(p_midi->stream_write));
#endif
tu_edpt_stream_close(&p_midi->ep_stream.rx);
tu_edpt_stream_close(&p_midi->ep_stream.tx);
}
}
}
bool midih_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
(void) result;
const uint8_t idx = get_idx_by_ep_addr(dev_addr, ep_addr);
TU_VERIFY(idx < CFG_TUH_MIDI);
midih_interface_t *p_midi = &_midi_host[idx];
if (ep_addr == p_midi->ep_stream.rx.ep_addr) {
// receive new data, put it into FIFO and invoke callback if available
// Note: some devices send back all zero packets even if there is no data ready
if (xferred_bytes && !tu_mem_is_zero(p_midi->ep_stream.rx.ep_buf, xferred_bytes)) {
tu_edpt_stream_read_xfer_complete(&p_midi->ep_stream.rx, xferred_bytes);
tuh_midi_rx_cb(idx, xferred_bytes);
}
tu_edpt_stream_read_xfer(dev_addr, &p_midi->ep_stream.rx); // prepare for next transfer
} else if (ep_addr == p_midi->ep_stream.tx.ep_addr) {
tuh_midi_tx_cb(idx, xferred_bytes);
if (0 == tu_edpt_stream_write_xfer(dev_addr, &p_midi->ep_stream.tx)) {
// If there is no data left, a ZLP should be sent if
// xferred_bytes is multiple of EP size and not zero
tu_edpt_stream_write_zlp_if_needed(dev_addr, &p_midi->ep_stream.tx, xferred_bytes);
}
}
return true;
}
//--------------------------------------------------------------------+
// Enumeration
//--------------------------------------------------------------------+
bool midih_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len) {
(void) rhport;
TU_VERIFY(TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass);
const uint8_t *p_end = ((const uint8_t *) desc_itf) + max_len;
const uint8_t *p_desc = (const uint8_t *) desc_itf;
const uint8_t idx = find_new_midi_index();
TU_VERIFY(idx < CFG_TUH_MIDI);
midih_interface_t *p_midi = &_midi_host[idx];
p_midi->itf_count = 0;
tuh_midi_descriptor_cb_t desc_cb = { 0 };
desc_cb.jack_num = 0;
// There can be just a MIDI or an Audio + MIDI interface
// If there is Audio Control Interface + Audio Header descriptor, skip it
if (AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass) {
TU_VERIFY(max_len > 2*sizeof(tusb_desc_interface_t) + sizeof(audio_desc_cs_ac_interface_t));
p_desc = tu_desc_next(p_desc);
TU_VERIFY(tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE &&
tu_desc_subtype(p_desc) == AUDIO_CS_AC_INTERFACE_HEADER);
desc_cb.desc_audio_control = desc_itf;
p_desc = tu_desc_next(p_desc);
desc_itf = (const tusb_desc_interface_t *)p_desc;
TU_VERIFY(TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass);
p_midi->itf_count = 1;
}
TU_VERIFY(AUDIO_SUBCLASS_MIDI_STREAMING == desc_itf->bInterfaceSubClass);
TU_LOG_DRV("MIDI opening Interface %u (addr = %u)\r\n", desc_itf->bInterfaceNumber, dev_addr);
p_midi->bInterfaceNumber = desc_itf->bInterfaceNumber;
p_midi->iInterface = desc_itf->iInterface;
p_midi->itf_count++;
desc_cb.desc_midi = desc_itf;
p_desc = tu_desc_next(p_desc); // next to CS Header
bool found_new_interface = false;
while ((p_desc < p_end) && (tu_desc_next(p_desc) <= p_end) && !found_new_interface) {
switch (tu_desc_type(p_desc)) {
case TUSB_DESC_INTERFACE:
found_new_interface = true;
break;
case TUSB_DESC_CS_INTERFACE:
switch (tu_desc_subtype(p_desc)) {
case MIDI_CS_INTERFACE_HEADER:
TU_LOG_DRV(" Interface Header descriptor\r\n");
desc_cb.desc_header = p_desc;
break;
case MIDI_CS_INTERFACE_IN_JACK:
case MIDI_CS_INTERFACE_OUT_JACK: {
TU_LOG_DRV(" Jack %s %s descriptor \r\n",
tu_desc_subtype(p_desc) == MIDI_CS_INTERFACE_IN_JACK ? "IN" : "OUT",
p_desc[3] == MIDI_JACK_EXTERNAL ? "External" : "Embedded");
if (desc_cb.jack_num < TU_ARRAY_SIZE(desc_cb.desc_jack)) {
desc_cb.desc_jack[desc_cb.jack_num++] = p_desc;
}
break;
}
case MIDI_CS_INTERFACE_ELEMENT:
TU_LOG_DRV(" Element descriptor\r\n");
desc_cb.desc_element = p_desc;
break;
default:
TU_LOG_DRV(" Unknown CS Interface sub-type %u\r\n", tu_desc_subtype(p_desc));
break;
}
break;
case TUSB_DESC_ENDPOINT: {
const tusb_desc_endpoint_t *p_ep = (const tusb_desc_endpoint_t *) p_desc;
p_desc = tu_desc_next(p_desc); // next to CS endpoint
TU_VERIFY(p_desc < p_end && tu_desc_next(p_desc) <= p_end);
const midi_desc_cs_endpoint_t *p_csep = (const midi_desc_cs_endpoint_t *) p_desc;
TU_LOG_DRV(" Endpoint and CS_Endpoint descriptor %02x\r\n", p_ep->bEndpointAddress);
if (tu_edpt_dir(p_ep->bEndpointAddress) == TUSB_DIR_OUT) {
p_midi->ep_out = p_ep->bEndpointAddress;
p_midi->tx_cable_count = p_csep->bNumEmbMIDIJack;
desc_cb.desc_epout = p_ep;
TU_ASSERT(tuh_edpt_open(dev_addr, p_ep));
tu_edpt_stream_open(&p_midi->ep_stream.tx, p_ep);
} else {
p_midi->ep_in = p_ep->bEndpointAddress;
p_midi->rx_cable_count = p_csep->bNumEmbMIDIJack;
desc_cb.desc_epin = p_ep;
TU_ASSERT(tuh_edpt_open(dev_addr, p_ep));
tu_edpt_stream_open(&p_midi->ep_stream.rx, p_ep);
}
break;
}
default: break; // skip unknown descriptor
}
p_desc = tu_desc_next(p_desc);
}
desc_cb.desc_midi_total_len = (uint16_t) ((uintptr_t)p_desc - (uintptr_t) desc_itf);
p_midi->daddr = dev_addr;
tuh_midi_descriptor_cb(idx, &desc_cb);
return true;
}
bool midih_set_config(uint8_t dev_addr, uint8_t itf_num) {
uint8_t idx = tuh_midi_itf_get_index(dev_addr, itf_num);
TU_ASSERT(idx < CFG_TUH_MIDI);
midih_interface_t *p_midi = &_midi_host[idx];
p_midi->mounted = true;
const tuh_midi_mount_cb_t mount_cb_data = {
.daddr = dev_addr,
.bInterfaceNumber = itf_num,
.rx_cable_count = p_midi->rx_cable_count,
.tx_cable_count = p_midi->tx_cable_count,
};
tuh_midi_mount_cb(idx, &mount_cb_data);
tu_edpt_stream_read_xfer(dev_addr, &p_midi->ep_stream.rx); // prepare for incoming data
// No special config things to do for MIDI
usbh_driver_set_config_complete(dev_addr, p_midi->bInterfaceNumber);
return true;
}
//--------------------------------------------------------------------+
// API
//--------------------------------------------------------------------+
bool tuh_midi_mounted(uint8_t idx) {
TU_VERIFY(idx < CFG_TUH_MIDI);
midih_interface_t *p_midi = &_midi_host[idx];
return p_midi->mounted;
}
uint8_t tuh_midi_itf_get_index(uint8_t daddr, uint8_t itf_num) {
for (uint8_t idx = 0; idx < CFG_TUH_MIDI; idx++) {
const midih_interface_t *p_midi = &_midi_host[idx];
if (p_midi->daddr == daddr &&
(p_midi->bInterfaceNumber == itf_num ||
p_midi->bInterfaceNumber == (uint8_t) (itf_num + p_midi->itf_count - 1))) {
return idx;
}
}
return TUSB_INDEX_INVALID_8;
}
bool tuh_midi_itf_get_info(uint8_t idx, tuh_itf_info_t* info) {
midih_interface_t* p_midi = &_midi_host[idx];
TU_VERIFY(p_midi && info);
info->daddr = p_midi->daddr;
// re-construct descriptor
tusb_desc_interface_t* desc = &info->desc;
desc->bLength = sizeof(tusb_desc_interface_t);
desc->bDescriptorType = TUSB_DESC_INTERFACE;
desc->bInterfaceNumber = p_midi->bInterfaceNumber;
desc->bAlternateSetting = 0;
desc->bNumEndpoints = (uint8_t)((p_midi->ep_in != 0 ? 1:0) + (p_midi->ep_out != 0 ? 1:0));
desc->bInterfaceClass = TUSB_CLASS_AUDIO;
desc->bInterfaceSubClass = AUDIO_SUBCLASS_MIDI_STREAMING;
desc->bInterfaceProtocol = 0;
desc->iInterface = p_midi->iInterface;
return true;
}
uint8_t tuh_midi_get_tx_cable_count (uint8_t idx) {
TU_VERIFY(idx < CFG_TUH_MIDI);
midih_interface_t *p_midi = &_midi_host[idx];
TU_VERIFY(p_midi->ep_stream.tx.ep_addr != 0, 0);
return p_midi->tx_cable_count;
}
uint8_t tuh_midi_get_rx_cable_count (uint8_t idx) {
TU_VERIFY(idx < CFG_TUH_MIDI);
midih_interface_t *p_midi = &_midi_host[idx];
TU_VERIFY(p_midi->ep_stream.rx.ep_addr != 0, 0);
return p_midi->rx_cable_count;
}
uint32_t tuh_midi_read_available(uint8_t idx) {
TU_VERIFY(idx < CFG_TUH_MIDI);
midih_interface_t *p_midi = &_midi_host[idx];
return tu_edpt_stream_read_available(&p_midi->ep_stream.rx);
}
uint32_t tuh_midi_write_flush(uint8_t idx) {
TU_VERIFY(idx < CFG_TUH_MIDI);
midih_interface_t *p_midi = &_midi_host[idx];
return tu_edpt_stream_write_xfer(p_midi->daddr, &p_midi->ep_stream.tx);
}
//--------------------------------------------------------------------+
// Packet API
//--------------------------------------------------------------------+
uint32_t tuh_midi_packet_read_n(uint8_t idx, uint8_t* buffer, uint32_t bufsize) {
TU_VERIFY(idx < CFG_TUH_MIDI && buffer && bufsize > 0, 0);
midih_interface_t *p_midi = &_midi_host[idx];
uint32_t count4 = tu_min32(bufsize, tu_edpt_stream_read_available(&p_midi->ep_stream.rx));
count4 = tu_align4(count4); // round down to multiple of 4
TU_VERIFY(count4 > 0, 0);
return tu_edpt_stream_read(p_midi->daddr, &p_midi->ep_stream.rx, buffer, count4);
}
uint32_t tuh_midi_packet_write_n(uint8_t idx, const uint8_t* buffer, uint32_t bufsize) {
TU_VERIFY(idx < CFG_TUH_MIDI && buffer && bufsize > 0, 0);
midih_interface_t *p_midi = &_midi_host[idx];
const uint32_t bufsize4 = tu_align4(bufsize);
TU_VERIFY(bufsize4 > 0, 0);
return tu_edpt_stream_write(p_midi->daddr, &p_midi->ep_stream.tx, buffer, bufsize4);
}
//--------------------------------------------------------------------+
// Stream API
//--------------------------------------------------------------------+
#if CFG_TUH_MIDI_STREAM_API
uint32_t tuh_midi_stream_write(uint8_t idx, uint8_t cable_num, uint8_t const *buffer, uint32_t bufsize) {
TU_VERIFY(idx < CFG_TUH_MIDI && buffer && bufsize > 0);
midih_interface_t *p_midi = &_midi_host[idx];
TU_VERIFY(cable_num < p_midi->tx_cable_count);
midi_driver_stream_t *stream = &p_midi->stream_write;
uint32_t byte_count = 0;
while ((byte_count < bufsize) && (tu_edpt_stream_write_available(p_midi->daddr, &p_midi->ep_stream.tx) >= 4)) {
uint8_t const data = buffer[byte_count];
byte_count++;
if (data >= MIDI_STATUS_SYSREAL_TIMING_CLOCK) {
// real-time messages need to be sent right away
midi_driver_stream_t streamrt;
streamrt.buffer[0] = MIDI_CIN_SYSEX_END_1BYTE;
streamrt.buffer[1] = data;
streamrt.index = 2;
streamrt.total = 2;
uint32_t const count = tu_edpt_stream_write(p_midi->daddr, &p_midi->ep_stream.tx, streamrt.buffer, 4);
TU_ASSERT(count == 4, byte_count); // Check FIFO overflown, since we already check fifo remaining. It is probably race condition
} else if (stream->index == 0) {
//------------- New event packet -------------//
uint8_t const msg = data >> 4;
stream->index = 2;
stream->buffer[1] = data;
// Check to see if we're still in a SysEx transmit.
if (stream->buffer[0] == MIDI_CIN_SYSEX_START) {
if (data == MIDI_STATUS_SYSEX_END) {
stream->buffer[0] = MIDI_CIN_SYSEX_END_1BYTE;
stream->total = 2;
} else {
stream->total = 4;
}
} else if ((msg >= 0x8 && msg <= 0xB) || msg == 0xE) {
// Channel Voice Messages
stream->buffer[0] = (uint8_t) ((cable_num << 4) | msg);
stream->total = 4;
} else if (msg == 0xC || msg == 0xD) {
// Channel Voice Messages, two-byte variants (Program Change and Channel Pressure)
stream->buffer[0] = (uint8_t) ((cable_num << 4) | msg);
stream->total = 3;
} else if (msg == 0xf) {
// System message
if (data == MIDI_STATUS_SYSEX_START) {
stream->buffer[0] = MIDI_CIN_SYSEX_START;
stream->total = 4;
} else if (data == MIDI_STATUS_SYSCOM_TIME_CODE_QUARTER_FRAME || data == MIDI_STATUS_SYSCOM_SONG_SELECT) {
stream->buffer[0] = MIDI_CIN_SYSCOM_2BYTE;
stream->total = 3;
} else if (data == MIDI_STATUS_SYSCOM_SONG_POSITION_POINTER) {
stream->buffer[0] = MIDI_CIN_SYSCOM_3BYTE;
stream->total = 4;
} else {
stream->buffer[0] = MIDI_CIN_SYSEX_END_1BYTE;
stream->total = 2;
}
} else {
// Pack individual bytes if we don't support packing them into words.
stream->buffer[0] = (uint8_t) (cable_num << 4 | 0xf);
stream->buffer[2] = 0;
stream->buffer[3] = 0;
stream->index = 2;
stream->total = 2;
}
} else {
//------------- On-going (buffering) packet -------------//
TU_ASSERT(stream->index < 4, byte_count);
stream->buffer[stream->index] = data;
stream->index++;
// See if this byte ends a SysEx.
if (stream->buffer[0] == MIDI_CIN_SYSEX_START && data == MIDI_STATUS_SYSEX_END) {
stream->buffer[0] = MIDI_CIN_SYSEX_START + (stream->index - 1);
stream->total = stream->index;
}
}
// Send out packet
if (stream->index >= 2 && stream->index == stream->total) {
// zeroes unused bytes
for (uint8_t i = stream->total; i < 4; i++) {
stream->buffer[i] = 0;
}
TU_LOG3_MEM(stream->buffer, 4, 2);
const uint32_t count = tu_edpt_stream_write(p_midi->daddr, &p_midi->ep_stream.tx, stream->buffer, 4);
// complete current event packet, reset stream
stream->index = 0;
stream->total = 0;
// FIFO overflown, since we already check fifo remaining. It is probably race condition
TU_ASSERT(count == 4, byte_count);
}
}
return byte_count;
}
uint32_t tuh_midi_stream_read(uint8_t idx, uint8_t *p_cable_num, uint8_t *p_buffer, uint16_t bufsize) {
TU_VERIFY(idx < CFG_TUH_MIDI && p_cable_num && p_buffer && bufsize > 0);
midih_interface_t *p_midi = &_midi_host[idx];
uint32_t bytes_buffered = 0;
uint8_t one_byte;
if (!tu_edpt_stream_peek(&p_midi->ep_stream.rx, &one_byte)) {
return 0;
}
*p_cable_num = (one_byte >> 4) & 0xf;
uint32_t nread = tu_edpt_stream_read(p_midi->daddr, &p_midi->ep_stream.rx, p_midi->stream_read.buffer, 4);
static uint16_t cable_sysex_in_progress;// bit i is set if received MIDI_STATUS_SYSEX_START but not MIDI_STATUS_SYSEX_END
while (nread == 4 && bytes_buffered < bufsize) {
*p_cable_num = (p_midi->stream_read.buffer[0] >> 4) & 0x0f;
uint8_t bytes_to_add_to_stream = 0;
if (*p_cable_num < p_midi->rx_cable_count) {
// ignore the CIN field; too many devices out there encode this wrong
uint8_t status = p_midi->stream_read.buffer[1];
uint16_t cable_mask = (uint16_t) (1 << *p_cable_num);
if (status <= MIDI_MAX_DATA_VAL || status == MIDI_STATUS_SYSEX_START) {
if (status == MIDI_STATUS_SYSEX_START) {
cable_sysex_in_progress |= cable_mask;
}
// only add the packet if a sysex message is in progress
if (cable_sysex_in_progress & cable_mask) {
++bytes_to_add_to_stream;
for (uint8_t i = 2; i < 4; i++) {
if (p_midi->stream_read.buffer[i] <= MIDI_MAX_DATA_VAL) {
++bytes_to_add_to_stream;
} else if (p_midi->stream_read.buffer[i] == MIDI_STATUS_SYSEX_END) {
++bytes_to_add_to_stream;
cable_sysex_in_progress &= (uint16_t) ~cable_mask;
i = 4;// force the loop to exit; I hate break statements in loops
}
}
}
} else if (status < MIDI_STATUS_SYSEX_START) {
// then it is a channel message either three bytes or two
uint8_t fake_cin = (status & 0xf0) >> 4;
switch (fake_cin) {
case MIDI_CIN_NOTE_OFF:
case MIDI_CIN_NOTE_ON:
case MIDI_CIN_POLY_KEYPRESS:
case MIDI_CIN_CONTROL_CHANGE:
case MIDI_CIN_PITCH_BEND_CHANGE:
bytes_to_add_to_stream = 3;
break;
case MIDI_CIN_PROGRAM_CHANGE:
case MIDI_CIN_CHANNEL_PRESSURE:
bytes_to_add_to_stream = 2;
break;
default:
break;// Should not get this
}
cable_sysex_in_progress &= (uint16_t) ~cable_mask;
} else if (status < MIDI_STATUS_SYSREAL_TIMING_CLOCK) {
switch (status) {
case MIDI_STATUS_SYSCOM_TIME_CODE_QUARTER_FRAME:
case MIDI_STATUS_SYSCOM_SONG_SELECT:
bytes_to_add_to_stream = 2;
break;
case MIDI_STATUS_SYSCOM_SONG_POSITION_POINTER:
bytes_to_add_to_stream = 3;
break;
case MIDI_STATUS_SYSCOM_TUNE_REQUEST:
case MIDI_STATUS_SYSEX_END:
bytes_to_add_to_stream = 1;
break;
default:
break;
}
cable_sysex_in_progress &= (uint16_t) ~cable_mask;
} else {
// Real-time message: can be inserted into a sysex message,
// so do don't clear cable_sysex_in_progress bit
bytes_to_add_to_stream = 1;
}
}
for (uint8_t i = 1; i <= bytes_to_add_to_stream; i++) {
*p_buffer++ = p_midi->stream_read.buffer[i];
}
bytes_buffered += bytes_to_add_to_stream;
nread = 0;
if (tu_edpt_stream_peek(&p_midi->ep_stream.rx, &one_byte)) {
uint8_t new_cable = (one_byte >> 4) & 0xf;
if (new_cable == *p_cable_num) {
// still on the same cable. Continue reading the stream
nread = tu_edpt_stream_read(p_midi->daddr, &p_midi->ep_stream.rx, p_midi->stream_read.buffer, 4);
}
}
}
return bytes_buffered;
}
#endif
#endif

View file

@ -1,193 +0,0 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* This file is part of the TinyUSB stack.
*/
#ifndef TUSB_MIDI_HOST_H_
#define TUSB_MIDI_HOST_H_
#include "class/audio/audio.h"
#include "midi.h"
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------+
// Class Driver Configuration
//--------------------------------------------------------------------+
#ifndef CFG_TUH_MIDI_RX_BUFSIZE
#define CFG_TUH_MIDI_RX_BUFSIZE TUH_EPSIZE_BULK_MPS
#endif
#ifndef CFG_TUH_MIDI_TX_BUFSIZE
#define CFG_TUH_MIDI_TX_BUFSIZE TUH_EPSIZE_BULK_MPS
#endif
#ifndef CFG_TUH_MIDI_EP_BUFSIZE
#define CFG_TUH_MIDI_EP_BUFSIZE TUH_EPSIZE_BULK_MPS
#endif
// Enable the MIDI stream read/write API. Some library can work with raw USB MIDI packet
// Disable this can save driver footprint.
#ifndef CFG_TUH_MIDI_STREAM_API
#define CFG_TUH_MIDI_STREAM_API 1
#endif
//--------------------------------------------------------------------+
// Application Types
//--------------------------------------------------------------------+
typedef struct {
const tusb_desc_interface_t* desc_audio_control;
const tusb_desc_interface_t* desc_midi; // start of whole midi interface descriptor
uint16_t desc_midi_total_len;
const uint8_t* desc_header;
const uint8_t* desc_element;
const tusb_desc_endpoint_t* desc_epin; // endpoint IN descriptor, CS_ENDPOINT is right after
const tusb_desc_endpoint_t* desc_epout; // endpoint OUT descriptor, CS_ENDPOINT is right after
uint8_t jack_num;
const uint8_t* desc_jack[32]; // list of jack descriptors (embedded + external)
} tuh_midi_descriptor_cb_t;
typedef struct {
uint8_t daddr;
uint8_t bInterfaceNumber; // interface number of MIDI streaming
uint8_t rx_cable_count;
uint8_t tx_cable_count;
} tuh_midi_mount_cb_t;
//--------------------------------------------------------------------+
// Application API
//--------------------------------------------------------------------+
// Check if MIDI interface is mounted
bool tuh_midi_mounted(uint8_t idx);
// Get Interface index from device address + interface number
// return TUSB_INDEX_INVALID_8 (0xFF) if not found
uint8_t tuh_midi_itf_get_index(uint8_t daddr, uint8_t itf_num);
// Get Interface information
// return true if index is correct and interface is currently mounted
bool tuh_midi_itf_get_info(uint8_t idx, tuh_itf_info_t* info);
// return the number of virtual midi cables on the device's IN endpoint
uint8_t tuh_midi_get_rx_cable_count(uint8_t idx);
// return the number of virtual midi cables on the device's OUT endpoint
uint8_t tuh_midi_get_tx_cable_count(uint8_t idx);
// return the raw number of bytes available.
// Note: this is related but not the same as number of stream bytes available.
uint32_t tuh_midi_read_available(uint8_t idx);
// Send any queued packets to the device if the host hardware is able to do it
// Returns the number of bytes flushed to the host hardware or 0 if
// the host hardware is busy or there is nothing in queue to send.
uint32_t tuh_midi_write_flush(uint8_t idx);
//--------------------------------------------------------------------+
// Packet API
//--------------------------------------------------------------------+
// Read all available MIDI packets from the connected device
// Return number of bytes read (always multiple of 4)
uint32_t tuh_midi_packet_read_n(uint8_t idx, uint8_t* buffer, uint32_t bufsize);
// Read a raw MIDI packet from the connected device
// Return true if a packet was returned
TU_ATTR_ALWAYS_INLINE static inline
bool tuh_midi_packet_read (uint8_t idx, uint8_t packet[4]) {
return 4 == tuh_midi_packet_read_n(idx, packet, 4);
}
// Write all 4-byte packets, data is locally buffered and only transferred when buffered bytes
// reach the endpoint packet size or tuh_midi_write_flush() is called
uint32_t tuh_midi_packet_write_n(uint8_t idx, const uint8_t* buffer, uint32_t bufsize);
// Write a 4-bytes packet to the device.
// Returns true if the packet was successfully queued.
TU_ATTR_ALWAYS_INLINE static inline
bool tuh_midi_packet_write (uint8_t idx, uint8_t const packet[4]) {
return 4 == tuh_midi_packet_write_n(idx, packet, 4);
}
//--------------------------------------------------------------------+
// Stream API
//--------------------------------------------------------------------+
#if CFG_TUH_MIDI_STREAM_API
// Queue a message to the device using stream API. data is locally buffered and only transferred when buffered bytes
// reach the endpoint packet size or tuh_midi_write_flush() is called
// Returns number of bytes was successfully queued.
uint32_t tuh_midi_stream_write(uint8_t idx, uint8_t cable_num, uint8_t const *p_buffer, uint32_t bufsize);
// Get the MIDI stream from the device. Set the value pointed
// to by p_cable_num to the MIDI cable number intended to receive it.
// The MIDI stream will be stored in the buffer pointed to by p_buffer.
// Return the number of bytes added to the buffer.
// Note that this function ignores the CIN field of the MIDI packet
// because a number of commercial devices out there do not encode
// it properly.
uint32_t tuh_midi_stream_read(uint8_t idx, uint8_t *p_cable_num, uint8_t *p_buffer, uint16_t bufsize);
#endif
//--------------------------------------------------------------------+
// Callbacks (Weak is optional)
//--------------------------------------------------------------------+
// Invoked when MIDI interface is detected in enumeration. Application can copy/parse descriptor if needed.
// Note: may be fired before tuh_midi_mount_cb(), therefore midi interface is not mounted/ready.
void tuh_midi_descriptor_cb(uint8_t idx, const tuh_midi_descriptor_cb_t * desc_cb_data);
// Invoked when device with MIDI interface is mounted.
void tuh_midi_mount_cb(uint8_t idx, const tuh_midi_mount_cb_t* mount_cb_data);
// Invoked when device with MIDI interface is un-mounted
void tuh_midi_umount_cb(uint8_t idx);
// Invoked when received new data
void tuh_midi_rx_cb(uint8_t idx, uint32_t xferred_bytes);
// Invoked when a TX is complete and therefore space becomes available in TX buffer
void tuh_midi_tx_cb(uint8_t idx, uint32_t xferred_bytes);
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
bool midih_init (void);
bool midih_deinit (void);
bool midih_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len);
bool midih_set_config (uint8_t dev_addr, uint8_t itf_num);
bool midih_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
void midih_close (uint8_t daddr);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -344,7 +344,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
msc_csw_t * p_csw = &p_msc->csw;
switch (p_msc->stage) {
case MSC_STAGE_CMD: {
case MSC_STAGE_CMD:
//------------- new CBW received -------------//
// Complete IN while waiting for CMD is usually Status of previous SCSI op, ignore it
if (ep_addr != p_msc->ep_out) {
@ -442,7 +442,6 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
}
}
break;
}
case MSC_STAGE_DATA:
TU_LOG_DRV(" SCSI Data [Lun%u]\r\n", p_cbw->lun);

View file

@ -196,8 +196,8 @@ void vendord_reset(uint8_t rhport) {
uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t* desc_itf, uint16_t max_len) {
TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == desc_itf->bInterfaceClass, 0);
const uint8_t* desc_end = (const uint8_t*)desc_itf + max_len;
const uint8_t* p_desc = tu_desc_next(desc_itf);
const uint8_t* desc_end = p_desc + max_len;
// Find available interface
vendord_interface_t* p_vendor = NULL;
@ -210,27 +210,27 @@ uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t* desc_itf, uin
TU_VERIFY(p_vendor, 0);
p_vendor->itf_num = desc_itf->bInterfaceNumber;
while (tu_desc_is_valid(p_desc, desc_end)) {
const uint8_t desc_type = tu_desc_type(p_desc);
if (desc_type == TUSB_DESC_INTERFACE || desc_type == TUSB_DESC_INTERFACE_ASSOCIATION) {
break; // end of this interface
} else if (desc_type == TUSB_DESC_ENDPOINT) {
uint8_t found_ep = 0;
while (found_ep < desc_itf->bNumEndpoints) {
// skip non-endpoint descriptors
while ( (TUSB_DESC_ENDPOINT != tu_desc_type(p_desc)) && (p_desc < desc_end) ) {
p_desc = tu_desc_next(p_desc);
}
if (p_desc >= desc_end) {
break;
}
const tusb_desc_endpoint_t* desc_ep = (const tusb_desc_endpoint_t*) p_desc;
TU_ASSERT(usbd_edpt_open(rhport, desc_ep));
found_ep++;
// open endpoint stream, skip if already opened
if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
if (p_vendor->tx.stream.ep_addr == 0) {
tu_edpt_stream_open(&p_vendor->tx.stream, desc_ep);
tud_vendor_n_write_flush((uint8_t)(p_vendor - _vendord_itf));
}
} else {
if (p_vendor->rx.stream.ep_addr == 0) {
tu_edpt_stream_open(&p_vendor->rx.stream, desc_ep);
TU_ASSERT(tu_edpt_stream_read_xfer(rhport, &p_vendor->rx.stream) > 0, 0); // prepare for incoming data
}
}
}
p_desc = tu_desc_next(p_desc);
}

View file

@ -120,22 +120,6 @@ TU_ATTR_ALWAYS_INLINE static inline int tu_memcpy_s(void *dest, size_t destsz, c
return 0;
}
TU_ATTR_ALWAYS_INLINE static inline bool tu_mem_is_zero(const void *buffer, size_t size) {
const uint8_t* buf8 = (const uint8_t*) buffer;
for (size_t i = 0; i < size; i++) {
if (buf8[i] != 0) { return false; }
}
return true;
}
TU_ATTR_ALWAYS_INLINE static inline bool tu_mem_is_ff(const void *buffer, size_t size) {
const uint8_t* buf8 = (const uint8_t*) buffer;
for (size_t i = 0; i < size; i++) {
if (buf8[i] != 0xff) { return false; }
}
return true;
}
//------------- Bytes -------------//
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_u32(uint8_t b3, uint8_t b2, uint8_t b1, uint8_t b0) {
@ -197,7 +181,8 @@ TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_round_up(uint32_t v, uint32_t f)
// log2 of a value is its MSB's position
// TODO use clz TODO remove
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_log2(uint32_t value) {
static inline uint8_t tu_log2(uint32_t value)
{
uint8_t result = 0;
while (value >>= 1) { result++; }
return result;
@ -208,7 +193,8 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_log2(uint32_t value) {
// return sizeof(uint32_t) * CHAR_BIT - __builtin_clz(x) - 1;
//}
TU_ATTR_ALWAYS_INLINE static inline bool tu_is_power_of_two(uint32_t value) {
static inline bool tu_is_power_of_two(uint32_t value)
{
return (value != 0) && ((value & (value - 1)) == 0);
}
@ -219,22 +205,26 @@ TU_ATTR_ALWAYS_INLINE static inline bool tu_is_power_of_two(uint32_t value) {
typedef struct { uint16_t val; } TU_ATTR_PACKED tu_unaligned_uint16_t;
typedef struct { uint32_t val; } TU_ATTR_PACKED tu_unaligned_uint32_t;
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_unaligned_read32(const void *mem) {
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_unaligned_read32(const void* mem)
{
tu_unaligned_uint32_t const* ua32 = (tu_unaligned_uint32_t const*) mem;
return ua32->val;
}
TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write32(void *mem, uint32_t value) {
TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write32(void* mem, uint32_t value)
{
tu_unaligned_uint32_t* ua32 = (tu_unaligned_uint32_t*) mem;
ua32->val = value;
}
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_unaligned_read16(const void *mem) {
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_unaligned_read16(const void* mem)
{
tu_unaligned_uint16_t const* ua16 = (tu_unaligned_uint16_t const*) mem;
return ua16->val;
}
TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write16(void *mem, uint16_t value) {
TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write16(void* mem, uint16_t value)
{
tu_unaligned_uint16_t* ua16 = (tu_unaligned_uint16_t*) mem;
ua16->val = value;
}

View file

@ -108,13 +108,15 @@ typedef struct {
} tu_lookup_table_t;
static inline const char* tu_lookup_find(tu_lookup_table_t const* p_table, uint32_t key) {
tu_static char not_found[11];
for(uint16_t i=0; i<p_table->count; i++) {
if (p_table->items[i].key == key) { return p_table->items[i].data; }
if (p_table->items[i].key == key) return p_table->items[i].data;
}
// not found return the key value in hex
static char not_found[11];
snprintf(not_found, sizeof(not_found), "0x%08lX", (unsigned long) key);
return not_found;
}

View file

@ -916,11 +916,8 @@ bool tu_fifo_clear(tu_fifo_t *f)
Overwritable mode the fifo is set to
*/
/******************************************************************************/
bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable) {
if (f->overwritable == overwritable) {
return true;
}
bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable)
{
_ff_lock(f->mutex_wr);
_ff_lock(f->mutex_rd);

View file

@ -369,10 +369,6 @@
#define TUP_DCD_ENDPOINT_MAX 7 // only 5 TX FIFO for endpoint IN
#define CFG_TUSB_OS_INC_PATH_DEFAULT freertos/
#if CFG_TUSB_MCU == OPT_MCU_ESP32S3
#define TUP_MCU_MULTIPLE_CORE 1
#endif
// Disable slave if DMA is enabled
#define CFG_TUD_DWC2_SLAVE_ENABLE_DEFAULT !CFG_TUD_DWC2_DMA_ENABLE
#define CFG_TUH_DWC2_SLAVE_ENABLE_DEFAULT !CFG_TUH_DWC2_DMA_ENABLE
@ -385,8 +381,6 @@
#define CFG_TUSB_OS_INC_PATH_DEFAULT freertos/
#define TUP_MCU_MULTIPLE_CORE 1
// Disable slave if DMA is enabled
#define CFG_TUD_DWC2_SLAVE_ENABLE_DEFAULT !CFG_TUD_DWC2_DMA_ENABLE
#define CFG_TUH_DWC2_SLAVE_ENABLE_DEFAULT !CFG_TUH_DWC2_DMA_ENABLE
@ -416,7 +410,6 @@
#elif TU_CHECK_MCU(OPT_MCU_RP2040)
#define TUP_DCD_EDPT_ISO_ALLOC
#define TUP_DCD_ENDPOINT_MAX 16
#define TUP_MCU_MULTIPLE_CORE 1
#define TU_ATTR_FAST_FUNC __attribute__((section(".time_critical.tinyusb")))

View file

@ -67,7 +67,7 @@ typedef struct {
//--------------------------------------------------------------------+
// Check if endpoint descriptor is valid per USB specs
bool tu_edpt_validate(tusb_desc_endpoint_t const * desc_ep, tusb_speed_t speed, bool is_host);
bool tu_edpt_validate(tusb_desc_endpoint_t const * desc_ep, tusb_speed_t speed);
// Bind all endpoint of a interface descriptor to class driver
void tu_edpt_bind_driver(uint8_t ep2drv[][2], tusb_desc_interface_t const* p_desc, uint16_t desc_len, uint8_t driver_id);
@ -138,7 +138,7 @@ uint32_t tu_edpt_stream_read(uint8_t hwid, tu_edpt_stream_t* s, void* buffer, ui
// Start an usb transfer if endpoint is not busy
uint32_t tu_edpt_stream_read_xfer(uint8_t hwid, tu_edpt_stream_t* s);
// Complete read transfer by writing EP -> FIFO. Must be called in the transfer complete callback
// Must be called in the transfer complete callback
TU_ATTR_ALWAYS_INLINE static inline
void tu_edpt_stream_read_xfer_complete(tu_edpt_stream_t* s, uint32_t xferred_bytes) {
if (tu_fifo_depth(&s->ff)) {
@ -146,11 +146,11 @@ void tu_edpt_stream_read_xfer_complete(tu_edpt_stream_t* s, uint32_t xferred_byt
}
}
// Complete read transfer with provided buffer
// Same as tu_edpt_stream_read_xfer_complete but skip the first n bytes
TU_ATTR_ALWAYS_INLINE static inline
void tu_edpt_stream_read_xfer_complete_with_buf(tu_edpt_stream_t* s, const void * buf, uint32_t xferred_bytes) {
if (tu_fifo_depth(&s->ff)) {
tu_fifo_write_n(&s->ff, buf, (uint16_t) xferred_bytes);
void tu_edpt_stream_read_xfer_complete_offset(tu_edpt_stream_t* s, uint32_t xferred_bytes, uint32_t skip_offset) {
if (tu_fifo_depth(&s->ff) && (skip_offset < xferred_bytes)) {
tu_fifo_write_n(&s->ff, s->ep_buf + skip_offset, (uint16_t) (xferred_bytes - skip_offset));
}
}

View file

@ -281,8 +281,7 @@ typedef enum {
// TODO remove
enum {
DESC_OFFSET_LEN = 0,
DESC_OFFSET_TYPE = 1,
DESC_OFFSET_SUBTYPE = 2
DESC_OFFSET_TYPE = 1
};
enum {
@ -571,27 +570,16 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t const * tu_desc_next(void const* des
return desc8 + desc8[DESC_OFFSET_LEN];
}
// get descriptor length
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_len(void const* desc) {
return ((uint8_t const*) desc)[DESC_OFFSET_LEN];
}
// get descriptor type
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_type(void const* desc) {
return ((uint8_t const*) desc)[DESC_OFFSET_TYPE];
}
// get descriptor subtype
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_subtype(void const* desc) {
return ((uint8_t const*) desc)[DESC_OFFSET_SUBTYPE];
// get descriptor length
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_len(void const* desc) {
return ((uint8_t const*) desc)[DESC_OFFSET_LEN];
}
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_is_valid(void const* desc, uint8_t const* desc_end) {
const uint8_t* desc8 = (uint8_t const*) desc;
return (desc8 < desc_end) && (tu_desc_next(desc) <= desc_end);
}
// find descriptor that match byte1 (type)
uint8_t const * tu_desc_find(uint8_t const* desc, uint8_t const* end, uint8_t byte1);

View file

@ -342,16 +342,15 @@ TU_ATTR_ALWAYS_INLINE static inline usbd_class_driver_t const * get_driver(uint8
enum { RHPORT_INVALID = 0xFFu };
tu_static uint8_t _usbd_rhport = RHPORT_INVALID;
static OSAL_SPINLOCK_DEF(_usbd_spin, usbd_int_set);
// Event queue: usbd_int_set() is used as mutex in OS NONE config
// Event queue
// usbd_int_set() is used as mutex in OS NONE config
OSAL_QUEUE_DEF(usbd_int_set, _usbd_qdef, CFG_TUD_TASK_QUEUE_SZ, dcd_event_t);
static osal_queue_t _usbd_q;
tu_static osal_queue_t _usbd_q;
// Mutex for claiming endpoint
#if OSAL_MUTEX_REQUIRED
static osal_mutex_def_t _ubsd_mutexdef;
static osal_mutex_t _usbd_mutex;
tu_static osal_mutex_def_t _ubsd_mutexdef;
tu_static osal_mutex_t _usbd_mutex;
#else
#define _usbd_mutex NULL
#endif
@ -478,8 +477,6 @@ bool tud_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
tu_varclr(&_usbd_dev);
_usbd_queued_setup = 0;
osal_spin_init(&_usbd_spin);
#if OSAL_MUTEX_REQUIRED
// Init device mutex
_usbd_mutex = osal_mutex_create(&_ubsd_mutexdef);
@ -566,7 +563,8 @@ static void usbd_reset(uint8_t rhport) {
}
bool tud_task_event_ready(void) {
TU_VERIFY(tud_inited()); // Skip if stack is not initialized
// Skip if stack is not initialized
if (!tud_inited()) return false;
return !osal_queue_empty(_usbd_q);
}
@ -688,9 +686,7 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) {
case USBD_EVENT_FUNC_CALL:
TU_LOG_USBD("\r\n");
if (event.func_call.func) {
event.func_call.func(event.func_call.param);
}
if (event.func_call.func) event.func_call.func(event.func_call.param);
break;
case DCD_EVENT_SOF:
@ -707,7 +703,7 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) {
#if CFG_TUSB_OS != OPT_OS_NONE && CFG_TUSB_OS != OPT_OS_PICO
// return if there is no more events, for application to run other background
if (osal_queue_empty(_usbd_q)) { return; }
if (osal_queue_empty(_usbd_q)) return;
#endif
}
}
@ -1039,14 +1035,7 @@ static bool process_set_config(uint8_t rhport, uint8_t cfg_num)
#endif
#if CFG_TUD_MIDI
if (driver->open == midid_open) {
// If there is a class-compliant Audio Control Class, then 2 interfaces. Otherwise, only one
if (TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass &&
AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass &&
AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_itf->bInterfaceProtocol) {
assoc_itf_count = 2;
}
}
if ( driver->open == midid_open ) assoc_itf_count = 2;
#endif
#if CFG_TUD_BTH && CFG_TUD_BTH_ISO_ALT_COUNT
@ -1251,21 +1240,17 @@ TU_ATTR_FAST_FUNC void dcd_event_handler(dcd_event_t const* event, bool in_isr)
// USBD API For Class Driver
//--------------------------------------------------------------------+
void usbd_int_set(bool enabled) {
if (enabled) {
void usbd_int_set(bool enabled)
{
if (enabled)
{
dcd_int_enable(_usbd_rhport);
} else {
}else
{
dcd_int_disable(_usbd_rhport);
}
}
void usbd_spin_lock(bool in_isr) {
osal_spin_lock(&_usbd_spin, in_isr);
}
void usbd_spin_unlock(bool in_isr) {
osal_spin_unlock(&_usbd_spin, in_isr);
}
// 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)
{
@ -1310,7 +1295,7 @@ bool usbd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_ep) {
rhport = _usbd_rhport;
TU_ASSERT(tu_edpt_number(desc_ep->bEndpointAddress) < CFG_TUD_ENDPPOINT_MAX);
TU_ASSERT(tu_edpt_validate(desc_ep, (tusb_speed_t) _usbd_dev.speed, false));
TU_ASSERT(tu_edpt_validate(desc_ep, (tusb_speed_t) _usbd_dev.speed));
return dcd_edpt_open(rhport, desc_ep);
}
@ -1511,7 +1496,7 @@ bool usbd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const* desc_ep)
uint8_t const dir = tu_edpt_dir(desc_ep->bEndpointAddress);
TU_ASSERT(epnum < CFG_TUD_ENDPPOINT_MAX);
TU_ASSERT(tu_edpt_validate(desc_ep, (tusb_speed_t) _usbd_dev.speed, false));
TU_ASSERT(tu_edpt_validate(desc_ep, (tusb_speed_t) _usbd_dev.speed));
_usbd_dev.ep_status[epnum][dir].stalled = 0;
_usbd_dev.ep_status[epnum][dir].busy = 0;

View file

@ -68,8 +68,6 @@ usbd_class_driver_t const* usbd_app_driver_get_cb(uint8_t* driver_count) TU_ATTR
typedef bool (*usbd_control_xfer_cb_t)(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
void usbd_int_set(bool enabled);
void usbd_spin_lock(bool in_isr);
void usbd_spin_unlock(bool in_isr);
//--------------------------------------------------------------------+
// USBD Endpoint API

View file

@ -90,6 +90,13 @@ typedef struct {
};
} hcd_event_t;
typedef struct {
uint8_t rhport;
uint8_t hub_addr;
uint8_t hub_port;
uint8_t speed;
} hcd_devtree_info_t;
//--------------------------------------------------------------------+
// Memory API
//--------------------------------------------------------------------+
@ -156,12 +163,8 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr);
//--------------------------------------------------------------------+
// Open an endpoint
// return true if successfully opened or endpoint is currently opened
bool hcd_edpt_open(uint8_t rhport, uint8_t daddr, tusb_desc_endpoint_t const * ep_desc);
// Close an endpoint
bool hcd_edpt_close(uint8_t rhport, uint8_t daddr, uint8_t ep_addr);
// Submit a transfer, when complete hcd_event_xfer_complete() must be invoked
bool hcd_edpt_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen);
@ -179,6 +182,13 @@ bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr);
// USBH implemented API
//--------------------------------------------------------------------+
// Get device tree information of a device
// USB device tree can be complicated and manged by USBH, this help HCD to retrieve
// needed topology info to carry out its work
extern void hcd_devtree_get_info(uint8_t dev_addr, hcd_devtree_info_t* devtree_info);
//------------- Event API -------------//
// Called by HCD to notify stack
extern void hcd_event_handler(hcd_event_t const* event, bool in_isr);
@ -225,4 +235,4 @@ void hcd_event_xfer_complete(uint8_t dev_addr, uint8_t ep_addr, uint32_t xferred
}
#endif
#endif
#endif /* _TUSB_HCD_H_ */

View file

@ -57,11 +57,9 @@ typedef struct {
TUH_EPBUF_DEF(ctrl_buf, CFG_TUH_HUB_BUFSIZE);
} hub_epbuf_t;
static tuh_xfer_cb_t user_complete_cb = NULL;
static hub_interface_t hub_itfs[CFG_TUH_HUB];
CFG_TUH_MEM_SECTION static hub_epbuf_t hub_epbufs[CFG_TUH_HUB];
TU_ATTR_ALWAYS_INLINE static inline hub_interface_t* get_hub_itf(uint8_t daddr) {
return &hub_itfs[daddr-1-CFG_TUH_DEVICE_MAX];
}
@ -148,19 +146,6 @@ bool hub_port_set_feature(uint8_t hub_addr, uint8_t hub_port, uint8_t feature,
return true;
}
static void port_get_status_complete (tuh_xfer_t* xfer) {
if (xfer->result == XFER_RESULT_SUCCESS) {
hub_interface_t* p_hub = get_hub_itf(xfer->daddr);
p_hub->port_status = *((const hub_port_status_response_t *) (uintptr_t) xfer->buffer);
}
xfer->complete_cb = user_complete_cb;
user_complete_cb = NULL;
if (xfer->complete_cb) {
xfer->complete_cb(xfer);
}
}
bool hub_port_get_status(uint8_t hub_addr, uint8_t hub_port, void* resp,
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
tusb_control_request_t const request = {
@ -184,28 +169,11 @@ bool hub_port_get_status(uint8_t hub_addr, uint8_t hub_port, void* resp,
.user_data = user_data
};
if (hub_port != 0) {
// intercept complete callback to save port status, ignore resp
hub_epbuf_t* p_epbuf = get_hub_epbuf(hub_addr);
xfer.complete_cb = port_get_status_complete;
xfer.buffer = p_epbuf->ctrl_buf;
user_complete_cb = complete_cb;
} else {
user_complete_cb = NULL;
}
TU_LOG_DRV("HUB Get Port Status: addr = %u port = %u\r\n", hub_addr, hub_port);
TU_VERIFY( tuh_control_xfer(&xfer) );
return true;
}
bool hub_port_get_status_local(uint8_t hub_addr, uint8_t hub_port, hub_port_status_response_t* resp) {
(void) hub_port;
hub_interface_t* p_hub = get_hub_itf(hub_addr);
*resp = p_hub->port_status;
return true;
}
//--------------------------------------------------------------------+
// CLASS-USBH API (don't require to verify parameters)
//--------------------------------------------------------------------+
@ -270,10 +238,10 @@ bool hub_edpt_status_xfer(uint8_t daddr) {
static void config_set_port_power (tuh_xfer_t* xfer);
static void config_port_power_complete (tuh_xfer_t* xfer);
bool hub_set_config(uint8_t daddr, uint8_t itf_num) {
hub_interface_t* p_hub = get_hub_itf(daddr);
bool hub_set_config(uint8_t dev_addr, uint8_t itf_num) {
hub_interface_t* p_hub = get_hub_itf(dev_addr);
TU_ASSERT(itf_num == p_hub->itf_num);
hub_epbuf_t* p_epbuf = get_hub_epbuf(daddr);
hub_epbuf_t* p_epbuf = get_hub_epbuf(dev_addr);
// Get Hub Descriptor
tusb_control_request_t const request = {
@ -289,7 +257,7 @@ bool hub_set_config(uint8_t daddr, uint8_t itf_num) {
};
tuh_xfer_t xfer = {
.daddr = daddr,
.daddr = dev_addr,
.ep_addr = 0,
.setup = &request,
.buffer = p_epbuf->ctrl_buf,
@ -344,15 +312,11 @@ static void config_port_power_complete (tuh_xfer_t* xfer) {
//--------------------------------------------------------------------+
// Connection Changes
//--------------------------------------------------------------------+
enum {
STATE_IDLE = 0,
STATE_HUB_STATUS,
STATE_CLEAR_CHANGE,
STATE_CHECK_CONN,
STATE_COMPLETE
};
static void process_new_status(tuh_xfer_t* xfer);
static void get_status_complete (tuh_xfer_t* xfer);
static void port_get_status_complete (tuh_xfer_t* xfer);
static void port_clear_feature_complete_stub(tuh_xfer_t* xfer);
static void connection_clear_conn_change_complete (tuh_xfer_t* xfer);
static void connection_port_reset_complete (tuh_xfer_t* xfer);
// callback as response of interrupt endpoint polling
bool hub_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
@ -373,12 +337,12 @@ bool hub_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t
processed = false;
} else if (tu_bit_test(status_change, 0)) {
// Hub bit 0 is for the hub device events
processed = hub_get_status(daddr, p_epbuf->ctrl_buf, process_new_status, STATE_HUB_STATUS);
processed = hub_get_status(daddr, p_epbuf->ctrl_buf, get_status_complete, 0);
} else {
// Hub bits 1 to n are hub port events
for (uint8_t port=1; port <= p_hub->bNbrPorts; port++) {
if (tu_bit_test(status_change, port)) {
processed = hub_port_get_status(daddr, port, NULL, process_new_status, STATE_CLEAR_CHANGE);
processed = hub_port_get_status(daddr, port, p_epbuf->ctrl_buf, port_get_status_complete, 0);
break; // after completely processed one port, we will re-queue the status poll and handle next one
}
}
@ -394,80 +358,26 @@ bool hub_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t
return true;
}
static void process_new_status(tuh_xfer_t* xfer) {
static void port_clear_feature_complete_stub(tuh_xfer_t* xfer) {
hub_edpt_status_xfer(xfer->daddr);
}
static void get_status_complete(tuh_xfer_t *xfer) {
const uint8_t daddr = xfer->daddr;
if (xfer->result != XFER_RESULT_SUCCESS) {
TU_ASSERT(hub_edpt_status_xfer(daddr),);
return;
}
const uint8_t port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
hub_interface_t *p_hub = get_hub_itf(daddr);
const uintptr_t state = xfer->user_data;
bool processed = false; // true if new status is processed
switch (state) {
case STATE_HUB_STATUS: {
if (xfer->result == XFER_RESULT_SUCCESS) {
hub_status_response_t hub_status = *((const hub_status_response_t *) (uintptr_t) xfer->buffer);
TU_LOG_DRV("HUB Got hub status, addr = %u, status = %04x\r\n", daddr, hub_status.change.value);
if (hub_status.change.local_power_source) {
TU_LOG_DRV(" Local Power Change\r\n");
processed = hub_clear_feature(daddr, HUB_FEATURE_HUB_LOCAL_POWER_CHANGE,
process_new_status, STATE_COMPLETE);
processed = hub_clear_feature(daddr, HUB_FEATURE_HUB_LOCAL_POWER_CHANGE, port_clear_feature_complete_stub, 0);
} else if (hub_status.change.over_current) {
TU_LOG_DRV(" Over Current\r\n");
processed = hub_clear_feature(daddr, HUB_FEATURE_HUB_OVER_CURRENT_CHANGE,
process_new_status, STATE_COMPLETE);
processed = hub_clear_feature(daddr, HUB_FEATURE_HUB_OVER_CURRENT_CHANGE, port_clear_feature_complete_stub, 0);
}
break;
}
case STATE_CLEAR_CHANGE:
// Get port status complete --> clear change
if (p_hub->port_status.change.connection) {
// Connection change
// Port is powered and enabled
//TU_VERIFY(port_status.status_current.port_power && port_status.status_current.port_enable, );
// Acknowledge Port Connection Change
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_CONNECTION_CHANGE,
process_new_status, STATE_CHECK_CONN);
} else if (p_hub->port_status.change.port_enable) {
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_ENABLE_CHANGE,
process_new_status, STATE_COMPLETE);
} else if (p_hub->port_status.change.suspend) {
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_SUSPEND_CHANGE,
process_new_status, STATE_COMPLETE);
} else if (p_hub->port_status.change.over_current) {
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_OVER_CURRENT_CHANGE,
process_new_status, STATE_COMPLETE);
} else if (p_hub->port_status.change.reset) {
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_RESET_CHANGE,
process_new_status, STATE_COMPLETE);
}
break;
case STATE_CHECK_CONN: {
const hcd_event_t event = {
.rhport = usbh_get_rhport(daddr),
.event_id = p_hub->port_status.status.connection ? HCD_EVENT_DEVICE_ATTACH : HCD_EVENT_DEVICE_REMOVE,
.connection = {
.hub_addr = daddr,
.hub_port = port_num
}
};
hcd_event_handler(&event, false);
// skip status for attach event, usbh will do it after handled this enumeration
processed = (event.event_id == HCD_EVENT_DEVICE_ATTACH);
break;
}
case STATE_COMPLETE:
default:
processed = false; // complete this status, queue next status
break;
}
if (!processed) {
@ -475,4 +385,90 @@ static void process_new_status(tuh_xfer_t* xfer) {
}
}
static void port_get_status_complete(tuh_xfer_t *xfer) {
const uint8_t daddr = xfer->daddr;
bool processed = false; // true if new status is processed
if (xfer->result == XFER_RESULT_SUCCESS) {
const uint8_t port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
hub_interface_t *p_hub = get_hub_itf(daddr);
p_hub->port_status = *((const hub_port_status_response_t *) (uintptr_t) xfer->buffer);
// Clear port status change interrupts
if (p_hub->port_status.change.connection) {
// Connection change
// Port is powered and enabled
//TU_VERIFY(port_status.status_current.port_power && port_status.status_current.port_enable, );
// Acknowledge Port Connection Change
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_CONNECTION_CHANGE, connection_clear_conn_change_complete, 0);
} else if (p_hub->port_status.change.port_enable) {
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_ENABLE_CHANGE, port_clear_feature_complete_stub, 0);
} else if (p_hub->port_status.change.suspend) {
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_SUSPEND_CHANGE, port_clear_feature_complete_stub, 0);
} else if (p_hub->port_status.change.over_current) {
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_OVER_CURRENT_CHANGE, port_clear_feature_complete_stub, 0);
} else if (p_hub->port_status.change.reset) {
processed = hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_RESET_CHANGE, port_clear_feature_complete_stub, 0);
}
}
if (!processed) {
TU_ASSERT(hub_edpt_status_xfer(daddr), );
}
}
static void connection_clear_conn_change_complete (tuh_xfer_t* xfer) {
const uint8_t daddr = xfer->daddr;
if (xfer->result != XFER_RESULT_SUCCESS) {
TU_ASSERT(hub_edpt_status_xfer(daddr), );
return;
}
hub_interface_t *p_hub = get_hub_itf(daddr);
const uint8_t port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
if (p_hub->port_status.status.connection) {
// Reset port if attach event
hub_port_reset(daddr, port_num, connection_port_reset_complete, 0);
} else {
// submit detach event
const hcd_event_t event = {
.rhport = usbh_get_rhport(daddr),
.event_id = HCD_EVENT_DEVICE_REMOVE,
.connection = {
.hub_addr = daddr,
.hub_port = port_num
}
};
hcd_event_handler(&event, false);
}
}
static void connection_port_reset_complete (tuh_xfer_t* xfer) {
const uint8_t daddr = xfer->daddr;
if (xfer->result != XFER_RESULT_SUCCESS) {
// retry port reset if failed
if (!tuh_control_xfer(xfer)) {
TU_ASSERT(hub_edpt_status_xfer(daddr), ); // back to status poll if failed to queue request
}
return;
}
const uint8_t port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
// submit attach event
hcd_event_t event = {
.rhport = usbh_get_rhport(daddr),
.event_id = HCD_EVENT_DEVICE_ATTACH,
.connection = {
.hub_addr = daddr,
.hub_port = port_num
}
};
hcd_event_handler(&event, false);
}
#endif

View file

@ -170,13 +170,9 @@ bool hub_port_set_feature(uint8_t hub_addr, uint8_t hub_port, uint8_t feature,
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
// Get port status
// If hub_port != 0, resp is ignored. hub_port_get_status_local() can be used to retrieve the status
bool hub_port_get_status(uint8_t hub_addr, uint8_t hub_port, void *resp,
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
// Get port status from local cache. This does not send a request to the device
bool hub_port_get_status_local(uint8_t hub_addr, uint8_t hub_port, hub_port_status_response_t* resp);
// Get status from Interrupt endpoint
bool hub_edpt_status_xfer(uint8_t daddr);
@ -192,7 +188,7 @@ bool hub_port_clear_reset_change(uint8_t hub_addr, uint8_t hub_port, tuh_xfer_cb
return hub_port_clear_feature(hub_addr, hub_port, HUB_FEATURE_PORT_RESET_CHANGE, complete_cb, user_data);
}
// Get Hub status (port = 0)
// Get Hub status
TU_ATTR_ALWAYS_INLINE static inline
bool hub_get_status(uint8_t hub_addr, void* resp, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
return hub_port_get_status(hub_addr, 0, resp, complete_cb, user_data);
@ -209,7 +205,7 @@ bool hub_clear_feature(uint8_t hub_addr, uint8_t feature, tuh_xfer_cb_t complete
bool hub_init (void);
bool hub_deinit (void);
bool hub_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
bool hub_set_config (uint8_t daddr, uint8_t itf_num);
bool hub_set_config (uint8_t dev_addr, uint8_t itf_num);
bool hub_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
void hub_close (uint8_t dev_addr);

File diff suppressed because it is too large Load diff

View file

@ -33,20 +33,14 @@
#include "common/tusb_common.h"
#if CFG_TUH_MAX3421
#include "portable/analog/max3421/hcd_max3421.h"
#endif
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
// Endpoint Bulk size depending on host mx speed
#define TUH_EPSIZE_BULK_MPS (TUD_OPT_HIGH_SPEED ? TUSB_EPSIZE_BULK_HS : TUSB_EPSIZE_BULK_FS)
// forward declaration
struct tuh_xfer_s;
typedef struct tuh_xfer_s tuh_xfer_t;
typedef void (*tuh_xfer_cb_t)(tuh_xfer_t* xfer);
// Note1: layout and order of this will be changed in near future
@ -79,17 +73,6 @@ typedef struct {
tusb_desc_interface_t desc;
} tuh_itf_info_t;
typedef struct {
uint8_t rhport;
uint8_t hub_addr;
uint8_t hub_port;
uint8_t speed;
} tuh_bus_info_t;
// backward compatibility for hcd_devtree_info_t, maybe removed in the future
#define hcd_devtree_info_t tuh_bus_info_t
#define hcd_devtree_get_info(_daddr, _bus_info) tuh_bus_info_get(_daddr, _bus_info)
// ConfigID for tuh_configure()
enum {
TUH_CFGID_INVALID = 0,
@ -113,15 +96,6 @@ typedef union {
// APPLICATION CALLBACK
//--------------------------------------------------------------------+
// Invoked when enumeration get device descriptor
// Device is not ready to communicate yet, application can copy the descriptor if needed
void tuh_enum_descriptor_device_cb(uint8_t daddr, const tusb_desc_device_t *desc_device);
// Invoked when enumeration get configuration descriptor
// For multi-configuration device return false to skip, true to proceed with this configuration (may not be implemented yet)
// Device is not ready to communicate yet, application can copy the descriptor if needed
bool tuh_enum_descriptor_configuration_cb(uint8_t daddr, uint8_t cfg_index, const tusb_desc_configuration_t *desc_config);
// Invoked when a device is mounted (configured)
TU_ATTR_WEAK void tuh_mount_cb (uint8_t daddr);
@ -172,7 +146,8 @@ bool tuh_inited(void);
void tuh_task_ext(uint32_t timeout_ms, bool in_isr);
// Task function should be called in main/rtos loop
TU_ATTR_ALWAYS_INLINE static inline void tuh_task(void) {
TU_ATTR_ALWAYS_INLINE static inline
void tuh_task(void) {
tuh_task_ext(UINT32_MAX, false);
}
@ -187,8 +162,6 @@ extern void hcd_int_handler(uint8_t rhport, bool in_isr);
#define _tuh_int_handler_arg0() TU_VERIFY_STATIC(false, "tuh_int_handler() must have 1 or 2 arguments")
#define _tuh_int_handler_arg1(_rhport) hcd_int_handler(_rhport, true)
#define _tuh_int_handler_arg2(_rhport, _in_isr) hcd_int_handler(_rhport, _in_isr)
// 1st argument is rhport (mandatory), 2nd argument in_isr (optional)
#define tuh_int_handler(...) TU_FUNC_OPTIONAL_ARG(_tuh_int_handler, __VA_ARGS__)
// Check if roothub port is initialized and active as a host
@ -215,20 +188,19 @@ bool tuh_mounted(uint8_t daddr);
bool tuh_connected(uint8_t daddr);
// Check if device is suspended
TU_ATTR_ALWAYS_INLINE static inline bool tuh_suspended(uint8_t daddr) {
TU_ATTR_ALWAYS_INLINE static inline
bool tuh_suspended(uint8_t daddr) {
// TODO implement suspend & resume on host
(void) daddr;
return false;
}
// Check if device is ready to communicate with
TU_ATTR_ALWAYS_INLINE static inline bool tuh_ready(uint8_t daddr) {
TU_ATTR_ALWAYS_INLINE static inline
bool tuh_ready(uint8_t daddr) {
return tuh_mounted(daddr) && !tuh_suspended(daddr);
}
// Get bus information of device
bool tuh_bus_info_get(uint8_t daddr, tuh_bus_info_t* bus_info);
//--------------------------------------------------------------------+
// Transfer API
//--------------------------------------------------------------------+
@ -246,17 +218,10 @@ bool tuh_edpt_xfer(tuh_xfer_t* xfer);
// Open a non-control endpoint
bool tuh_edpt_open(uint8_t daddr, tusb_desc_endpoint_t const * desc_ep);
// Close a non-control endpoint, it will abort any pending transfer
bool tuh_edpt_close(uint8_t daddr, uint8_t ep_addr);
// Abort a queued transfer. Note: it can only abort transfer that has not been started
// Return true if a queued transfer is aborted, false if there is no transfer to abort
bool tuh_edpt_abort_xfer(uint8_t daddr, uint8_t ep_addr);
// Set Address (control transfer)
bool tuh_address_set(uint8_t daddr, uint8_t new_addr,
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
// Set Configuration (control transfer)
// config_num = 0 will un-configure device. Note: config_num = config_descriptor_index + 1
// true on success, false if there is on-going control transfer or incorrect parameters

View file

@ -41,6 +41,10 @@
#define TU_LOG_INT_USBH(...) TU_LOG_INT(CFG_TUH_LOG_LEVEL, __VA_ARGS__)
#define TU_LOG_HEX_USBH(...) TU_LOG_HEX(CFG_TUH_LOG_LEVEL, __VA_ARGS__)
enum {
USBH_EPSIZE_BULK_MAX = (TUH_OPT_HIGH_SPEED ? TUSB_EPSIZE_BULK_HS : TUSB_EPSIZE_BULK_FS)
};
//--------------------------------------------------------------------+
// Class Driver API
//--------------------------------------------------------------------+
@ -63,7 +67,7 @@ usbh_class_driver_t const* usbh_app_driver_get_cb(uint8_t* driver_count) TU_ATTR
// Call by class driver to tell USBH that it has complete the enumeration
void usbh_driver_set_config_complete(uint8_t dev_addr, uint8_t itf_num);
uint8_t usbh_get_rhport(uint8_t daddr);
uint8_t usbh_get_rhport(uint8_t dev_addr);
uint8_t* usbh_get_enum_buf(void);
@ -71,9 +75,6 @@ void usbh_int_set(bool enabled);
void usbh_defer_func(osal_task_func_t func, void *param, bool in_isr);
void usbh_spin_lock(bool in_isr);
void usbh_spin_unlock(bool in_isr);
//--------------------------------------------------------------------+
// USBH Endpoint API
//--------------------------------------------------------------------+

View file

@ -75,10 +75,6 @@ typedef void (*osal_task_func_t)( void * );
// OSAL Porting API
// Should be implemented as static inline function in osal_port.h header
/*
void osal_spin_init(osal_spinlock_t *ctx);
void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr)
void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr);
osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef);
bool osal_semaphore_delete(osal_semaphore_t semd_hdl);
bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr);

View file

@ -45,8 +45,7 @@ extern "C" {
typedef StaticSemaphore_t osal_semaphore_def_t;
typedef StaticSemaphore_t osal_mutex_def_t;
#else
// not used therefore defined to the smallest possible type to save space
// not used therefore defined to smallest possible type to save space
typedef uint8_t osal_semaphore_def_t;
typedef uint8_t osal_mutex_def_t;
#endif
@ -55,7 +54,8 @@ typedef SemaphoreHandle_t osal_semaphore_t;
typedef SemaphoreHandle_t osal_mutex_t;
typedef QueueHandle_t osal_queue_t;
typedef struct {
typedef struct
{
uint16_t depth;
uint16_t item_sz;
void* buf;
@ -83,14 +83,16 @@ typedef struct {
//--------------------------------------------------------------------+
// TASK API
//--------------------------------------------------------------------+
TU_ATTR_ALWAYS_INLINE static inline uint32_t _osal_ms2tick(uint32_t msec) {
if (msec == OSAL_TIMEOUT_WAIT_FOREVER) { return portMAX_DELAY; }
if (msec == 0) { return 0; }
if ( msec == OSAL_TIMEOUT_WAIT_FOREVER ) return portMAX_DELAY;
if ( msec == 0 ) return 0;
uint32_t ticks = pdMS_TO_TICKS(msec);
// If configTICK_RATE_HZ is less than 1000 and 1 tick > 1 ms, we still need to delay at least 1 tick
if (ticks == 0) { ticks = 1; }
// configTICK_RATE_HZ is less than 1000 and 1 tick > 1 ms
// we still need to delay at least 1 tick
if ( ticks == 0 ) ticks = 1;
return ticks;
}
@ -99,71 +101,10 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
vTaskDelay(pdMS_TO_TICKS(msec));
}
//--------------------------------------------------------------------+
// Spinlock API
//--------------------------------------------------------------------+
#define OSAL_SPINLOCK_DEF(_name, _int_set) \
osal_spinlock_t _name
#if TUSB_MCU_VENDOR_ESPRESSIF
// Espressif critical take spinlock as argument and does not use in_isr
typedef portMUX_TYPE osal_spinlock_t;
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_init(osal_spinlock_t *ctx) {
spinlock_initialize(ctx);
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr) {
if (!TUP_MCU_MULTIPLE_CORE && in_isr) {
return; // single core MCU does not need to lock in ISR
}
portENTER_CRITICAL(ctx);
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr) {
if (!TUP_MCU_MULTIPLE_CORE && in_isr) {
return; // single core MCU does not need to lock in ISR
}
portEXIT_CRITICAL(ctx);
}
#else
typedef UBaseType_t osal_spinlock_t;
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_init(osal_spinlock_t *ctx) {
(void) ctx;
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr) {
if (in_isr) {
if (!TUP_MCU_MULTIPLE_CORE) {
(void) ctx;
return; // single core MCU does not need to lock in ISR
}
*ctx = taskENTER_CRITICAL_FROM_ISR();
} else {
taskENTER_CRITICAL();
}
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr) {
if (in_isr) {
if (!TUP_MCU_MULTIPLE_CORE) {
(void) ctx;
return; // single core MCU does not need to lock in ISR
}
taskEXIT_CRITICAL_FROM_ISR(*ctx);
} else {
taskEXIT_CRITICAL();
}
}
#endif
//--------------------------------------------------------------------+
// Semaphore API
//--------------------------------------------------------------------+
TU_ATTR_ALWAYS_INLINE static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t *semdef) {
#if configSUPPORT_STATIC_ALLOCATION
return xSemaphoreCreateBinaryStatic(semdef);
@ -184,7 +125,14 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t se
} else {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
BaseType_t res = xSemaphoreGiveFromISR(sem_hdl, &xHigherPriorityTaskWoken);
#if CFG_TUSB_MCU == OPT_MCU_ESP32S2 || CFG_TUSB_MCU == OPT_MCU_ESP32S3
// not needed after https://github.com/espressif/esp-idf/commit/c5fd79547ac9b7bae06fa660e9f814d18d3390b7
if ( xHigherPriorityTaskWoken ) portYIELD_FROM_ISR();
#else
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
#endif
return res != 0;
}
}
@ -200,6 +148,7 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t c
//--------------------------------------------------------------------+
// MUTEX API (priority inheritance)
//--------------------------------------------------------------------+
TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t *mdef) {
#if configSUPPORT_STATIC_ALLOCATION
return xSemaphoreCreateMutexStatic(mdef);
@ -225,6 +174,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hd
//--------------------------------------------------------------------+
// QUEUE API
//--------------------------------------------------------------------+
TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef) {
osal_queue_t q;
@ -256,7 +206,14 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void
} else {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
BaseType_t res = xQueueSendToBackFromISR(qhdl, data, &xHigherPriorityTaskWoken);
#if CFG_TUSB_MCU == OPT_MCU_ESP32S2 || CFG_TUSB_MCU == OPT_MCU_ESP32S3
// not needed after https://github.com/espressif/esp-idf/commit/c5fd79547ac9b7bae06fa660e9f814d18d3390b7 (IDF v5)
if ( xHigherPriorityTaskWoken ) portYIELD_FROM_ISR();
#else
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
#endif
return res != 0;
}
}

View file

@ -40,32 +40,6 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
os_time_delay( os_time_ms_to_ticks32(msec) );
}
//--------------------------------------------------------------------+
// Spinlock API
//--------------------------------------------------------------------+
typedef os_sr_t osal_spinlock_t;
#define OSAL_SPINLOCK_DEF(_name, _int_set) \
osal_spinlock_t _name
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_init(osal_spinlock_t *ctx) {
(void) ctx;
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr) {
if (!TUP_MCU_MULTIPLE_CORE && in_isr) {
return; // single core MCU does not need to lock in ISR
}
OS_ENTER_CRITICAL(*ctx);
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr) {
if (!TUP_MCU_MULTIPLE_CORE && in_isr) {
return; // single core MCU does not need to lock in ISR
}
OS_ENTER_CRITICAL(*ctx);
}
//--------------------------------------------------------------------+
// Semaphore API
//--------------------------------------------------------------------+

View file

@ -40,33 +40,6 @@ extern "C" {
TU_ATTR_WEAK void osal_task_delay(uint32_t msec);
#endif
//--------------------------------------------------------------------+
// Spinlock API
//--------------------------------------------------------------------+
typedef struct {
void (* interrupt_set)(bool);
} osal_spinlock_t;
// For SMP, spinlock must be locked by hardware, cannot just use interrupt
#define OSAL_SPINLOCK_DEF(_name, _int_set) \
osal_spinlock_t _name = { .interrupt_set = _int_set }
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_init(osal_spinlock_t *ctx) {
(void) ctx;
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr) {
if (!in_isr) {
ctx->interrupt_set(false);
}
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr) {
if (!in_isr) {
ctx->interrupt_set(true);
}
}
//--------------------------------------------------------------------+
// Binary Semaphore API
//--------------------------------------------------------------------+

View file

@ -43,27 +43,6 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
sleep_ms(msec);
}
//--------------------------------------------------------------------+
// Spinlock API
//--------------------------------------------------------------------+
typedef critical_section_t osal_spinlock_t; // pico implement critical section with spinlock
#define OSAL_SPINLOCK_DEF(_name, _int_set) \
osal_spinlock_t _name
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_init(osal_spinlock_t *ctx) {
critical_section_init(ctx);
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr) {
(void) in_isr;
critical_section_enter_blocking(ctx);
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr) {
(void) in_isr;
critical_section_exit(ctx);
}
//--------------------------------------------------------------------+
// Binary Semaphore API
//--------------------------------------------------------------------+

View file

@ -42,32 +42,6 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
rt_thread_mdelay(msec);
}
//--------------------------------------------------------------------+
// Spinlock API
//--------------------------------------------------------------------+
typedef struct rt_spinlock osal_spinlock_t;
#define OSAL_SPINLOCK_DEF(_name, _int_set) \
osal_spinlock_t _name
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_init(osal_spinlock_t *ctx) {
rt_spin_lock_init(ctx);
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr) {
if (!TUP_MCU_MULTIPLE_CORE && in_isr) {
return; // single core MCU does not need to lock in ISR
}
rt_spin_lock(ctx);
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr) {
if (!TUP_MCU_MULTIPLE_CORE && in_isr) {
return; // single core MCU does not need to lock in ISR
}
rt_spin_unlock(ctx);
}
//--------------------------------------------------------------------+
// Semaphore API
//--------------------------------------------------------------------+

View file

@ -56,25 +56,6 @@ TU_ATTR_ALWAYS_INLINE static inline uint16_t msec2wait(uint32_t msec) {
}
}
//--------------------------------------------------------------------+
// Spinlock API, stub not implemented
//--------------------------------------------------------------------+
typedef uint8_t osal_spinlock_t;
#define OSAL_SPINLOCK_DEF(_name, _int_set) \
osal_spinlock_t _name
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_init(osal_spinlock_t *ctx) {
(void) ctx;
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr) {
(void) ctx; (void) in_isr;
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr) {
(void) ctx; (void) in_isr;
}
//--------------------------------------------------------------------+
// Semaphore API
//--------------------------------------------------------------------+

View file

@ -35,35 +35,6 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
k_msleep(msec);
}
//--------------------------------------------------------------------+
// Spinlock API
//--------------------------------------------------------------------+
typedef struct {
struct k_spinlock lock;
k_spinlock_key_t key;
} osal_spinlock_t;
#define OSAL_SPINLOCK_DEF(_name, _int_set) \
osal_spinlock_t _name
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_init(osal_spinlock_t *ctx) {
(void) ctx;
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr) {
if (!TUP_MCU_MULTIPLE_CORE && in_isr) {
return; // single core MCU does not need to lock in ISR
}
ctx->key = k_spin_lock(&ctx->lock);
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr) {
if (!TUP_MCU_MULTIPLE_CORE && in_isr) {
return; // single core MCU does not need to lock in ISR
}
k_spin_unlock(&ctx->lock, ctx->key);
}
//--------------------------------------------------------------------+
// Binary Semaphore API
//--------------------------------------------------------------------+

View file

@ -28,9 +28,9 @@
#if CFG_TUH_ENABLED && defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421
#include <stdatomic.h>
#include "host/hcd.h"
#include "host/usbh.h"
#include "host/usbh_pvt.h"
//--------------------------------------------------------------------+
//
@ -233,7 +233,13 @@ typedef struct {
uint8_t hxfr;
}sndfifo_owner;
bool busy_lock; // busy transferring
#if CFG_TUSB_MCU == OPT_MCU_RP2040
// currently has undefined reference to `__atomic_test_and_set' with rp2040 on Arduino with gcc 14.2
// temporarily use native semaphore instead. TODO rework osal semaphore/mutex later on
semaphore_t busy; // busy transferring
#else
atomic_flag busy; // busy transferring
#endif
#if OSAL_MUTEX_REQUIRED
OSAL_MUTEX_DEF(spi_mutexdef);
@ -252,6 +258,47 @@ static tuh_configure_max3421_t _tuh_cfg = {
.pinctl = 0, // default: negative edge interrupt
};
#if CFG_TUSB_MCU == OPT_MCU_RP2040
TU_ATTR_ALWAYS_INLINE static inline bool usb_xfer_test_and_set(void) {
return !sem_try_acquire(&_hcd_data.busy);
}
TU_ATTR_ALWAYS_INLINE static inline void usb_xfer_clear(void) {
sem_release(&_hcd_data.busy);
}
#else
TU_ATTR_ALWAYS_INLINE static inline bool usb_xfer_test_and_set(void) {
return atomic_flag_test_and_set(&_hcd_data.busy);
}
TU_ATTR_ALWAYS_INLINE static inline void usb_xfer_clear(void) {
atomic_flag_clear(&_hcd_data.busy);
}
#endif
//--------------------------------------------------------------------+
// API: SPI transfer with MAX3421E
// - spi_cs_api(), spi_xfer_api(), int_api(): must be implemented by application
// - reg_read(), reg_write(): is implemented by this driver, can be used by application
//--------------------------------------------------------------------+
// API to control MAX3421 SPI CS
extern void tuh_max3421_spi_cs_api(uint8_t rhport, bool active);
// API to transfer data with MAX3421 SPI
// Either tx_buf or rx_buf can be NULL, which means transfer is write or read only
extern bool tuh_max3421_spi_xfer_api(uint8_t rhport, uint8_t const* tx_buf, uint8_t* rx_buf, size_t xfer_bytes);
// API to enable/disable MAX3421 INTR pin interrupt
extern void tuh_max3421_int_api(uint8_t rhport, bool enabled);
// API to read MAX3421's register. Implemented by TinyUSB
uint8_t tuh_max3421_reg_read(uint8_t rhport, uint8_t reg, bool in_isr);
// API to write MAX3421's register. Implemented by TinyUSB
bool tuh_max3421_reg_write(uint8_t rhport, uint8_t reg, uint8_t data, bool in_isr);
//--------------------------------------------------------------------+
// SPI Commands and Helper
//--------------------------------------------------------------------+
@ -327,9 +374,7 @@ TU_ATTR_ALWAYS_INLINE static inline void mode_write(uint8_t rhport, uint8_t data
}
TU_ATTR_ALWAYS_INLINE static inline void peraddr_write(uint8_t rhport, uint8_t data, bool in_isr) {
if (_hcd_data.peraddr == data) {
return; // no need to change address
}
if ( _hcd_data.peraddr == data ) return; // no need to change address
_hcd_data.peraddr = data;
reg_write(rhport, PERADDR_ADDR, data, in_isr);
@ -375,7 +420,7 @@ TU_ATTR_ALWAYS_INLINE static inline void hwfifo_setup(uint8_t rhport, const uint
static void hwfifo_receive(uint8_t rhport, uint8_t * buffer, uint16_t len, bool in_isr) {
uint8_t hirq;
const uint8_t reg = RCVVFIFO_ADDR;
uint8_t const reg = RCVVFIFO_ADDR;
max3421_spi_lock(rhport, in_isr);
@ -391,7 +436,7 @@ static void hwfifo_receive(uint8_t rhport, uint8_t * buffer, uint16_t len, bool
//--------------------------------------------------------------------+
static max3421_ep_t* find_ep_not_addr0(uint8_t daddr, uint8_t ep_num, uint8_t ep_dir) {
const uint8_t is_out = 1-ep_dir;
uint8_t const is_out = 1-ep_dir;
for(size_t i=1; i<CFG_TUH_MAX3421_ENDPOINT_TOTAL; i++) {
max3421_ep_t* ep = &_hcd_data.ep[i];
// control endpoint is bi-direction (skip check)
@ -486,6 +531,10 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
tu_memclr(&_hcd_data, sizeof(_hcd_data));
_hcd_data.peraddr = 0xff; // invalid
#if CFG_TUSB_MCU == OPT_MCU_RP2040
sem_init(&_hcd_data.busy, 1, 1);
#endif
#if OSAL_MUTEX_REQUIRED
_hcd_data.spi_mutex = osal_mutex_create(&_hcd_data.spi_mutexdef);
#endif
@ -543,6 +592,10 @@ bool hcd_deinit(uint8_t rhport) {
_hcd_data.spi_mutex = NULL;
#endif
#if CFG_TUSB_MCU == OPT_MCU_RP2040
sem_reset(&_hcd_data.busy, 1);
#endif
return true;
}
@ -612,9 +665,6 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t daddr, tusb_desc_endpoint_t const * e
if (daddr == 0 && ep_num == 0) {
ep = &_hcd_data.ep[0];
}else {
if (NULL != find_ep_not_addr0(daddr, ep_num, ep_dir)) {
return true; // already opened
}
ep = allocate_ep();
TU_ASSERT(ep);
ep->daddr = daddr;
@ -628,21 +678,6 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t daddr, tusb_desc_endpoint_t const * e
return true;
}
bool hcd_edpt_close(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) {
(void) rhport;
uint8_t const ep_num = tu_edpt_number(ep_addr);
tusb_dir_t const ep_dir = tu_edpt_dir(ep_addr);
max3421_ep_t * ep = find_ep_not_addr0(daddr, ep_num, ep_dir);
if (!ep) {
return false; // not opened
}
tu_memclr(ep, sizeof(max3421_ep_t));
return true;
}
/* The microcontroller repeatedly writes the SNDFIFO register R2 to load the FIFO with up to 64 data bytes.
* Then the microcontroller writes the SNDBC register, which this does three things:
* 1. Tells the MAX3421E SIE (Serial Interface Engine) how many bytes in the FIFO to send.
@ -729,8 +764,8 @@ static void xact_generic(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool
// Submit a transfer, when complete hcd_event_xfer_complete() must be invoked
bool hcd_edpt_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) {
const uint8_t ep_num = tu_edpt_number(ep_addr);
const uint8_t ep_dir = (uint8_t) tu_edpt_dir(ep_addr);
uint8_t const ep_num = tu_edpt_number(ep_addr);
uint8_t const ep_dir = (uint8_t) tu_edpt_dir(ep_addr);
max3421_ep_t* ep = find_opened_ep(daddr, ep_num, ep_dir);
TU_VERIFY(ep);
@ -746,17 +781,8 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr, uint8_t * buf
ep->xferred_len = 0;
ep->state = EP_STATE_ATTEMPT_1;
bool has_xfer = false;
usbh_spin_lock(false);
if (!_hcd_data.busy_lock) {
_hcd_data.busy_lock = true;
has_xfer = true;
}
usbh_spin_unlock(false);
// carry out transfer if not busy
if (has_xfer) {
if (!usb_xfer_test_and_set()) {
xact_generic(rhport, ep, true, false);
}
@ -792,17 +818,8 @@ bool hcd_setup_send(uint8_t rhport, uint8_t daddr, uint8_t const setup_packet[8]
ep->xferred_len = 0;
ep->state = EP_STATE_ATTEMPT_1;
bool has_xfer = false;
usbh_spin_lock(false);
if (!_hcd_data.busy_lock) {
_hcd_data.busy_lock = true;
has_xfer = true;
}
usbh_spin_unlock(false);
// carry out transfer if not busy
if (has_xfer) {
if (!usb_xfer_test_and_set()) {
xact_setup(rhport, ep, false);
}
@ -868,8 +885,8 @@ static void handle_connect_irq(uint8_t rhport, bool in_isr) {
}
static void xfer_complete_isr(uint8_t rhport, max3421_ep_t *ep, xfer_result_t result, uint8_t hrsl, bool in_isr) {
const uint8_t ep_dir = 1 - ep->hxfr_bm.is_out;
const uint8_t ep_addr = tu_edpt_addr(ep->hxfr_bm.ep_num, ep_dir);
uint8_t const ep_dir = 1-ep->hxfr_bm.is_out;
uint8_t const ep_addr = tu_edpt_addr(ep->hxfr_bm.ep_num, ep_dir);
// save data toggle
if (ep_dir) {
@ -887,9 +904,7 @@ static void xfer_complete_isr(uint8_t rhport, max3421_ep_t *ep, xfer_result_t re
xact_generic(rhport, next_ep, true, in_isr);
}else {
// no more pending
usbh_spin_lock(in_isr);
_hcd_data.busy_lock = false;
usbh_spin_unlock(in_isr);
usb_xfer_clear();
}
}
@ -928,9 +943,7 @@ static void handle_xfer_done(uint8_t rhport, bool in_isr) {
xact_generic(rhport, next_ep, true, in_isr);
} else {
// no more pending in this frame -> clear busy
usbh_spin_lock(in_isr);
_hcd_data.busy_lock = false;
usbh_spin_unlock(in_isr);
usb_xfer_clear();
}
return;
@ -1021,7 +1034,7 @@ void print_hirq(uint8_t hirq) {
// Interrupt handler
void hcd_int_handler(uint8_t rhport, bool in_isr) {
uint8_t hirq = reg_read(rhport, HIRQ_ADDR, in_isr) & _hcd_data.hien;
if (!hirq) { return; }
if (!hirq) return;
// print_hirq(hirq);
if (hirq & HIRQ_FRAME_IRQ) {
@ -1041,21 +1054,10 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
}
// start usb transfer if not busy
if (ep_retry != NULL) {
bool has_xfer = false;
usbh_spin_lock(in_isr);
if (!_hcd_data.busy_lock) {
_hcd_data.busy_lock = true;
has_xfer = true;
}
usbh_spin_unlock(in_isr);
if (has_xfer) {
if (ep_retry != NULL && !usb_xfer_test_and_set()) {
xact_generic(rhport, ep_retry, true, in_isr);
}
}
}
if (hirq & HIRQ_CONDET_IRQ) {
handle_connect_irq(rhport, in_isr);

View file

@ -1,63 +0,0 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2025 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* This file is part of the TinyUSB stack.
*/
#ifndef TUSB_HCD_MAX3421_H
#define TUSB_HCD_MAX3421_H
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------+
// SPI transfer API with MAX3421E are implemented by application
// - spi_cs_api(), spi_xfer_api(), int_api()
//--------------------------------------------------------------------+
// API to control MAX3421 SPI CS
extern void tuh_max3421_spi_cs_api(uint8_t rhport, bool active);
// API to transfer data with MAX3421 SPI
// Either tx_buf or rx_buf can be NULL, which means transfer is write or read only
extern bool tuh_max3421_spi_xfer_api(uint8_t rhport, uint8_t const* tx_buf, uint8_t* rx_buf, size_t xfer_bytes);
// API to enable/disable MAX3421 INTR pin interrupt
extern void tuh_max3421_int_api(uint8_t rhport, bool enabled);
//--------------------------------------------------------------------+
// API for read/write MAX3421 registers
// are implemented by this driver, can be used by application
//--------------------------------------------------------------------+
// API to read MAX3421's register. Implemented by TinyUSB
uint8_t tuh_max3421_reg_read(uint8_t rhport, uint8_t reg, bool in_isr);
// API to write MAX3421's register. Implemented by TinyUSB
bool tuh_max3421_reg_write(uint8_t rhport, uint8_t reg, uint8_t data, bool in_isr);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -114,19 +114,14 @@ void hcd_int_disable(uint8_t rhport) {
//--------------------------------------------------------------------+
bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const *desc_ep) {
tuh_bus_info_t bus_info;
tuh_bus_info_get(dev_addr, &bus_info);
bool const need_pre = (bus_info.hub_addr && bus_info.speed == TUSB_SPEED_LOW);
hcd_devtree_info_t dev_tree;
hcd_devtree_get_info(dev_addr, &dev_tree);
bool const need_pre = (dev_tree.hub_addr && dev_tree.speed == TUSB_SPEED_LOW);
uint8_t const pio_rhport = RHPORT_PIO(rhport);
return pio_usb_host_endpoint_open(pio_rhport, dev_addr, (uint8_t const *) desc_ep, need_pre);
}
bool hcd_edpt_close(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) {
uint8_t const pio_rhport = RHPORT_PIO(rhport);
return pio_usb_host_endpoint_close(pio_rhport, daddr, ep_addr);
}
bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen) {
uint8_t const pio_rhport = RHPORT_PIO(rhport);
return pio_usb_host_endpoint_transfer(pio_rhport, dev_addr, ep_addr, buffer, buflen);

View file

@ -514,6 +514,7 @@ void hcd_int_disable(uint8_t rhport)
//--------------------------------------------------------------------+
// Endpoint API
//--------------------------------------------------------------------+
bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc)
{
(void) rhport;
@ -534,11 +535,6 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const
return true;
}
bool hcd_edpt_close(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) {
(void) rhport; (void) daddr; (void) ep_addr;
return false; // TODO not implemented yet
}
bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen)
{
(void) rhport;

View file

@ -39,9 +39,14 @@
#define DWC2_DEBUG 2
#include "device/dcd.h"
#include "device/usbd_pvt.h"
#include "dwc2_common.h"
#if TU_CHECK_MCU(OPT_MCU_GD32VF103)
#define DWC2_EP_COUNT(_dwc2) DWC2_EP_MAX
#else
#define DWC2_EP_COUNT(_dwc2) ((_dwc2)->ghwcfg2_bm.num_dev_ep + 1)
#endif
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM
//--------------------------------------------------------------------+
@ -53,7 +58,6 @@ typedef struct {
uint8_t interval;
} xfer_ctl_t;
// This variable is modified from ISR context, so it must be protected by critical section
static xfer_ctl_t xfer_status[DWC2_EP_MAX][2];
#define XFER_CTL_BASE(_ep, _dir) (&xfer_status[_ep][_dir])
@ -75,16 +79,6 @@ CFG_TUD_MEM_SECTION static struct {
TUD_EPBUF_DEF(setup_packet, 8);
} _dcd_usbbuf;
TU_ATTR_ALWAYS_INLINE static inline uint8_t dwc2_ep_count(const dwc2_regs_t* dwc2) {
#if TU_CHECK_MCU(OPT_MCU_GD32VF103)
return DWC2_EP_MAX;
#else
const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2};
return ghwcfg2.num_dev_ep + 1;
#endif
}
//--------------------------------------------------------------------
// DMA
//--------------------------------------------------------------------
@ -108,8 +102,7 @@ bool dcd_dcache_clean_invalidate(const void* addr, uint32_t data_size) {
TU_ATTR_ALWAYS_INLINE static inline bool dma_device_enabled(const dwc2_regs_t* dwc2) {
(void) dwc2;
// Internal DMA only
const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2};
return CFG_TUD_DWC2_DMA_ENABLE && ghwcfg2.arch == GHWCFG2_ARCH_INTERNAL_DMA;
return CFG_TUD_DWC2_DMA_ENABLE && dwc2->ghwcfg2_bm.arch == GHWCFG2_ARCH_INTERNAL_DMA;
}
static void dma_setup_prepare(uint8_t rhport) {
@ -257,15 +250,20 @@ static void edpt_activate(uint8_t rhport, const tusb_desc_endpoint_t* p_endpoint
xfer->interval = p_endpoint_desc->bInterval;
// Endpoint control
dwc2_depctl_t depctl = {.value = 0};
depctl.mps = xfer->max_size;
depctl.active = 1;
depctl.type = p_endpoint_desc->bmAttributes.xfer;
union {
uint32_t value;
dwc2_depctl_t bm;
} depctl;
depctl.value = 0;
depctl.bm.mps = xfer->max_size;
depctl.bm.active = 1;
depctl.bm.type = p_endpoint_desc->bmAttributes.xfer;
if (p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS) {
depctl.set_data0_iso_even = 1;
depctl.bm.set_data0_iso_even = 1;
}
if (dir == TUSB_DIR_IN) {
depctl.tx_fifo_num = epnum;
depctl.bm.tx_fifo_num = epnum;
}
dwc2_dep_t* dep = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][epnum];
@ -323,9 +321,6 @@ static void edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) {
}
}
// Since this function returns void, it is not possible to return a boolean success message
// We must make sure that this function is not called when the EP is disabled
// Must be called from critical section
static void edpt_schedule_packets(uint8_t rhport, const uint8_t epnum, const uint8_t dir) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
xfer_ctl_t* const xfer = XFER_CTL_BASE(epnum, dir);
@ -348,22 +343,31 @@ static void edpt_schedule_packets(uint8_t rhport, const uint8_t epnum, const uin
}
// transfer size: A full OUT transfer (multiple packets, possibly) triggers XFRC.
dwc2_ep_tsize_t deptsiz = {.value = 0};
deptsiz.xfer_size = total_bytes;
deptsiz.packet_count = num_packets;
union {
uint32_t value;
dwc2_ep_tsize_t bm;
} deptsiz;
deptsiz.value = 0;
deptsiz.bm.xfer_size = total_bytes;
deptsiz.bm.packet_count = num_packets;
dep->tsiz = deptsiz.value;
// control
dwc2_depctl_t depctl = {.value = dep->ctl};
depctl.clear_nak = 1;
depctl.enable = 1;
if (depctl.type == DEPCTL_EPTYPE_ISOCHRONOUS && xfer->interval == 1) {
const dwc2_dsts_t dsts = {.value = dwc2->dsts};
const uint32_t odd_now = dsts.frame_number & 1u;
union {
dwc2_depctl_t bm;
uint32_t value;
} depctl;
depctl.value = dep->ctl;
depctl.bm.clear_nak = 1;
depctl.bm.enable = 1;
if (depctl.bm.type == DEPCTL_EPTYPE_ISOCHRONOUS && xfer->interval == 1) {
const uint32_t odd_now = (dwc2->dsts_bm.frame_number & 1u);
if (odd_now) {
depctl.set_data0_iso_even = 1;
depctl.bm.set_data0_iso_even = 1;
} else {
depctl.set_data1_iso_odd = 1;
depctl.bm.set_data1_iso_odd = 1;
}
}
@ -406,8 +410,7 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
// XCVRDLY: transceiver delay between xcvr_sel and txvalid during device chirp is required
// when using with some PHYs such as USB334x (USB3341, USB3343, USB3346, USB3347)
const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2};
if (ghwcfg2.hs_phy_type == GHWCFG2_HSPHY_ULPI) {
if (dwc2->ghwcfg2_bm.hs_phy_type == GHWCFG2_HSPHY_ULPI) {
dcfg |= DCFG_XCVRDLY;
}
} else {
@ -536,8 +539,6 @@ void dcd_edpt_close_all(uint8_t rhport) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
usbd_spin_lock(false);
_dcd_data.allocated_epin_count = 0;
// Disable non-control interrupt
@ -555,9 +556,8 @@ void dcd_edpt_close_all(uint8_t rhport) {
dfifo_flush_tx(dwc2, 0x10); // all tx fifo
dfifo_flush_rx(dwc2);
dfifo_device_init(rhport); // re-init dfifo
usbd_spin_unlock(false);
dfifo_device_init(rhport); // re-init dfifo
}
bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
@ -575,14 +575,8 @@ bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpo
bool dcd_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);
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, dir);
bool ret;
usbd_spin_lock(false);
if (xfer->max_size == 0) {
ret = false; // Endpoint is closed
} else {
xfer->buffer = buffer;
xfer->ff = NULL;
xfer->total_len = total_bytes;
@ -594,12 +588,8 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t to
// Schedule packets to be sent within interrupt
edpt_schedule_packets(rhport, epnum, dir);
ret = true;
}
usbd_spin_unlock(false);
return ret;
return true;
}
// The number of bytes has to be given explicitly to allow more flexible control of how many
@ -612,14 +602,8 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_t
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);
bool ret;
usbd_spin_lock(false);
if (xfer->max_size == 0) {
ret = false; // Endpoint is closed
} else {
xfer->buffer = NULL;
xfer->ff = ff;
xfer->total_len = total_bytes;
@ -627,12 +611,8 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_t
// Schedule packets to be sent within interrupt
// TODO xfer fifo may only available for slave mode
edpt_schedule_packets(rhport, epnum, dir);
ret = true;
}
usbd_spin_unlock(false);
return ret;
return true;
}
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
@ -659,10 +639,9 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
//--------------------------------------------------------------------
// 7.4.1 Initialization on USB Reset
// Must be called from critical section
static void handle_bus_reset(uint8_t rhport) {
dwc2_regs_t *dwc2 = DWC2_REG(rhport);
const uint8_t ep_count = dwc2_ep_count(dwc2);
const uint8_t ep_count = DWC2_EP_COUNT(dwc2);
tu_memclr(xfer_status, sizeof(xfer_status));
@ -692,9 +671,7 @@ static void handle_bus_reset(uint8_t rhport) {
dfifo_device_init(rhport);
// 5. Reset device address
dwc2_dcfg_t dcfg = {.value = dwc2->dcfg};
dcfg.address = 0;
dwc2->dcfg = dcfg.value;
dwc2->dcfg_bm.address = 0;
// Fixed both control EP0 size to 64 bytes
dwc2->epin[0].ctl &= ~(0x03 << DIEPCTL_MPSIZ_Pos);
@ -714,9 +691,8 @@ static void handle_bus_reset(uint8_t rhport) {
static void handle_enum_done(uint8_t rhport) {
dwc2_regs_t *dwc2 = DWC2_REG(rhport);
const dwc2_dsts_t dsts = {.value = dwc2->dsts};
tusb_speed_t speed;
switch (dsts.enum_speed) {
switch (dwc2->dsts_bm.enum_speed) {
case DCFG_SPEED_HIGH:
speed = TUSB_SPEED_HIGH;
break;
@ -761,12 +737,12 @@ static void handle_rxflvl_irq(uint8_t rhport) {
const volatile uint32_t* rx_fifo = dwc2->fifo[0];
// Pop control word off FIFO
const dwc2_grxstsp_t grxstsp = {.value = dwc2->grxstsp};
const uint8_t epnum = grxstsp.ep_ch_num;
const dwc2_grxstsp_t grxstsp_bm = dwc2->grxstsp_bm;
const uint8_t epnum = grxstsp_bm.ep_ch_num;
dwc2_dep_t* epout = &dwc2->epout[epnum];
switch (grxstsp.packet_status) {
switch (grxstsp_bm.packet_status) {
case GRXSTS_PKTSTS_GLOBAL_OUT_NAK:
// Global OUT NAK: do nothing
break;
@ -788,7 +764,7 @@ static void handle_rxflvl_irq(uint8_t rhport) {
case GRXSTS_PKTSTS_RX_DATA: {
// Out packet received
const uint16_t byte_count = grxstsp.byte_count;
const uint16_t byte_count = grxstsp_bm.byte_count;
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
if (byte_count) {
@ -802,8 +778,7 @@ static void handle_rxflvl_irq(uint8_t rhport) {
// short packet, minus remaining bytes (xfer_size)
if (byte_count < xfer->max_size) {
const dwc2_ep_tsize_t tsiz = {.value = epout->tsiz};
xfer->total_len -= tsiz.xfer_size;
xfer->total_len -= epout->tsiz_bm.xfer_size;
if (epnum == 0) {
xfer->total_len -= _dcd_data.ep0_pending[TUSB_DIR_OUT];
_dcd_data.ep0_pending[TUSB_DIR_OUT] = 0;
@ -865,13 +840,11 @@ static void handle_epin_slave(uint8_t rhport, uint8_t epnum, dwc2_diepint_t diep
// - 64 bytes or
// - Half/Empty of TX FIFO size (configured by GAHBCFG.TXFELVL)
if (diepint_bm.txfifo_empty && (dwc2->diepempmsk & (1 << epnum))) {
dwc2_ep_tsize_t tsiz = {.value = epin->tsiz};
const uint16_t remain_packets = tsiz.packet_count;
const uint16_t remain_packets = epin->tsiz_bm.packet_count;
// Process every single packet (only whole packets can be written to fifo)
for (uint16_t i = 0; i < remain_packets; i++) {
tsiz.value = epin->tsiz;
const uint16_t remain_bytes = (uint16_t) tsiz.xfer_size;
const uint16_t remain_bytes = (uint16_t) epin->tsiz_bm.xfer_size;
const uint16_t xact_bytes = tu_min16(remain_bytes, xfer->max_size);
// Check if dtxfsts has enough space available
@ -890,8 +863,7 @@ static void handle_epin_slave(uint8_t rhport, uint8_t epnum, dwc2_diepint_t diep
}
// Turn off TXFE if all bytes are written.
tsiz.value = epin->tsiz;
if (tsiz.xfer_size == 0) {
if (epin->tsiz_bm.xfer_size == 0) {
dwc2->diepempmsk &= ~(1 << epnum);
}
}
@ -922,8 +894,7 @@ static void handle_epout_dma(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doepi
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
// determine actual received bytes
const dwc2_ep_tsize_t tsiz = {.value = epout->tsiz};
const uint16_t remain = tsiz.xfer_size;
const uint16_t remain = epout->tsiz_bm.xfer_size;
xfer->total_len -= remain;
// this is ZLP, so prepare EP0 for next setup
@ -959,7 +930,7 @@ static void handle_epin_dma(uint8_t rhport, uint8_t epnum, dwc2_diepint_t diepin
static void handle_ep_irq(uint8_t rhport, uint8_t dir) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
const bool is_dma = dma_device_enabled(dwc2);
const uint8_t ep_count = dwc2_ep_count(dwc2);
const uint8_t ep_count = DWC2_EP_COUNT(dwc2);
const uint8_t daint_offset = (dir == TUSB_DIR_IN) ? DAINT_IEPINT_Pos : DAINT_OEPINT_Pos;
dwc2_dep_t* ep_base = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][0];
@ -1012,16 +983,14 @@ static void handle_ep_irq(uint8_t rhport, uint8_t dir) {
*/
void dcd_int_handler(uint8_t rhport) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
const uint32_t gintmask = dwc2->gintmsk;
const uint32_t gintsts = dwc2->gintsts & gintmask;
if (gintsts & GINTSTS_USBRST) {
// USBRST is start of reset.
dwc2->gintsts = GINTSTS_USBRST;
usbd_spin_lock(true);
handle_bus_reset(rhport);
usbd_spin_unlock(true);
}
if (gintsts & GINTSTS_ENUMDNE) {

View file

@ -36,7 +36,6 @@
#if CFG_TUH_ENABLED
#include "host/hcd.h"
#include "host/usbh.h"
#endif
#include "dwc2_common.h"
@ -89,13 +88,11 @@ static void phy_fs_init(dwc2_regs_t* dwc2) {
static void phy_hs_init(dwc2_regs_t* dwc2) {
uint32_t gusbcfg = dwc2->gusbcfg;
const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2};
const dwc2_ghwcfg4_t ghwcfg4 = {.value = dwc2->ghwcfg4};
// De-select FS PHY
gusbcfg &= ~GUSBCFG_PHYSEL;
if (ghwcfg2.hs_phy_type == GHWCFG2_HSPHY_ULPI) {
if (dwc2->ghwcfg2_bm.hs_phy_type == GHWCFG2_HSPHY_ULPI) {
TU_LOG(DWC2_COMMON_DEBUG, "Highspeed ULPI PHY init\r\n");
// Select ULPI PHY (external)
@ -119,7 +116,7 @@ static void phy_hs_init(dwc2_regs_t* dwc2) {
gusbcfg &= ~GUSBCFG_ULPI_UTMI_SEL;
// Set 16-bit interface if supported
if (ghwcfg4.phy_data_width) {
if (dwc2->ghwcfg4_bm.phy_data_width) {
gusbcfg |= GUSBCFG_PHYIF16; // 16 bit
} else {
gusbcfg &= ~GUSBCFG_PHYIF16; // 8 bit
@ -130,7 +127,7 @@ static void phy_hs_init(dwc2_regs_t* dwc2) {
dwc2->gusbcfg = gusbcfg;
// mcu specific phy init
dwc2_phy_init(dwc2, ghwcfg2.hs_phy_type);
dwc2_phy_init(dwc2, dwc2->ghwcfg2_bm.hs_phy_type);
// Reset core after selecting PHY
reset_core(dwc2);
@ -139,11 +136,11 @@ static void phy_hs_init(dwc2_regs_t* dwc2) {
// - 9 if using 8-bit PHY interface
// - 5 if using 16-bit PHY interface
gusbcfg &= ~GUSBCFG_TRDT_Msk;
gusbcfg |= (ghwcfg4.phy_data_width ? 5u : 9u) << GUSBCFG_TRDT_Pos;
gusbcfg |= (dwc2->ghwcfg4_bm.phy_data_width ? 5u : 9u) << GUSBCFG_TRDT_Pos;
dwc2->gusbcfg = gusbcfg;
// MCU specific PHY update post reset
dwc2_phy_update(dwc2, ghwcfg2.hs_phy_type);
dwc2_phy_update(dwc2, dwc2->ghwcfg2_bm.hs_phy_type);
}
static bool check_dwc2(dwc2_regs_t* dwc2) {
@ -174,6 +171,7 @@ static bool check_dwc2(dwc2_regs_t* dwc2) {
//--------------------------------------------------------------------
bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, tusb_role_t role) {
(void)dwc2;
#if CFG_TUD_ENABLED
if (role == TUSB_ROLE_DEVICE && !TUD_OPT_HIGH_SPEED) {
return false;
@ -185,8 +183,7 @@ bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, tusb_role_t role) {
}
#endif
const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2};
return ghwcfg2.hs_phy_type != GHWCFG2_HSPHY_NOT_SUPPORTED;
return dwc2->ghwcfg2_bm.hs_phy_type != GHWCFG2_HSPHY_NOT_SUPPORTED;
}
/* dwc2 has several PHYs option

View file

@ -184,9 +184,7 @@ enum {
//--------------------------------------------------------------------
// Common Register Bitfield
//--------------------------------------------------------------------
typedef union {
uint32_t value;
struct TU_ATTR_PACKED {
typedef struct TU_ATTR_PACKED {
uint32_t ses_req_scs : 1; // 0 Session request success
uint32_t ses_req : 1; // 1 Session request
uint32_t vbval_ov_en : 1; // 2 VBUS valid override enable
@ -212,13 +210,10 @@ typedef union {
uint32_t chirp_en : 1; // 27 Chirp detection enable
uint32_t rsv28_30 : 3; // 28.30: Reserved
uint32_t test_mode_corr_eusb2 : 1; // 31 Test mode control for eUSB2 PHY
};
} dwc2_gotgctl_t;
TU_VERIFY_STATIC(sizeof(dwc2_gotgctl_t) == 4, "incorrect size");
typedef union {
uint32_t value;
struct TU_ATTR_PACKED {
typedef struct TU_ATTR_PACKED {
uint32_t rsv0_1 : 2; // 0..1 Reserved
uint32_t ses_end_det : 1; // 2 Session end detected
uint32_t rsv3_7 : 5; // 3..7 Reserved
@ -230,13 +225,10 @@ typedef union {
uint32_t dbnc_done : 1; // 19 Debounce done
uint32_t mult_val_lp_change : 1; // 20 Multi-valued input pin change
uint32_t rsv21_31 :11; // 21..31 Reserved
};
} dwc2_gotgint_t;
TU_VERIFY_STATIC(sizeof(dwc2_gotgint_t) == 4, "incorrect size");
typedef union {
uint32_t value;
struct TU_ATTR_PACKED {
typedef struct TU_ATTR_PACKED {
uint32_t gintmask : 1; // 0 Global interrupt mask
uint32_t hbst_len : 4; // 1..4 Burst length/type
uint32_t dma_en : 1; // 5 DMA enable
@ -249,13 +241,10 @@ typedef union {
uint32_t ahb_single : 1; // 23 AHB single
uint32_t inv_desc_endian : 1; // 24 Inverse descriptor endian
uint32_t rsv25_31 : 7; // 25..31 Reserved
};
} dwc2_gahbcfg_t;
TU_VERIFY_STATIC(sizeof(dwc2_gahbcfg_t) == 4, "incorrect size");
typedef union {
uint32_t value;
struct TU_ATTR_PACKED {
typedef struct TU_ATTR_PACKED {
uint32_t timeout_cal : 3; /* 0..2 Timeout calibration.
The USB standard timeout value for high-speed operation is 736 to 816 (inclusive) bit times. The USB standard
timeout value for full- speed operation is 16 to 18 (inclusive) bit times. The application must program this field
@ -292,13 +281,10 @@ typedef union {
uint32_t force_host_mode : 1; // 29 Force host mode
uint32_t force_dev_mode : 1; // 30 Force device mode
uint32_t corrupt_tx_pkt : 1; // 31 Corrupt Tx packet. 0: normal, 1: debug
};
} dwc2_gusbcfg_t;
TU_VERIFY_STATIC(sizeof(dwc2_gusbcfg_t) == 4, "incorrect size");
typedef union {
uint32_t value;
struct TU_ATTR_PACKED {
typedef struct TU_ATTR_PACKED {
uint32_t core_soft_rst : 1; // 0 Core Soft Reset
uint32_t piufs_soft_rst : 1; // 1 PIU FS Dedicated Controller Soft Reset
uint32_t frame_counter_rst : 1; // 2 Frame Counter Reset (host)
@ -310,26 +296,21 @@ typedef union {
uint32_t core_soft_rst_done : 1; // 29 Core Soft Reset Done, from v4.20a
uint32_t dma_req : 1; // 30 DMA Request
uint32_t ahb_idle : 1; // 31 AHB Idle
};
} dwc2_grstctl_t;
TU_VERIFY_STATIC(sizeof(dwc2_grstctl_t) == 4, "incorrect size");
typedef union {
uint32_t value;
struct TU_ATTR_PACKED {
typedef struct TU_ATTR_PACKED {
uint32_t ep_ch_num : 4; // 0..3 Endpoint/Channel Number
uint32_t byte_count :11; // 4..14 Byte Count
uint32_t dpid : 2; // 15..16 Data PID
uint32_t packet_status : 4; // 17..20 Packet Status
uint32_t frame_number : 4; // 21..24 Frame Number
uint32_t rsv25_31 : 7; // 25..31 Reserved
};
} dwc2_grxstsp_t;
TU_VERIFY_STATIC(sizeof(dwc2_grxstsp_t) == 4, "incorrect size");
typedef union {
uint32_t value;
struct TU_ATTR_PACKED {
// Hardware Configuration
typedef struct TU_ATTR_PACKED {
uint32_t op_mode : 3; // 0..2 HNP/SRP Host/Device/OTG mode
uint32_t arch : 2; // 3..4 Slave/External/Internal DMA
uint32_t single_point : 1; // 5 0: support hub and split | 1: no hub, no split
@ -345,13 +326,10 @@ typedef union {
uint32_t ptx_q_depth : 2; // 24..25 Host periodic request queue depth: 0 = 2. 1 = 4, 2 = 8
uint32_t token_q_depth : 5; // 26..30 Device IN token sequence learning queue depth: 0-30
uint32_t otg_enable_ic_usb : 1; // 31 IC_USB mode specified for mode of operation
};
} dwc2_ghwcfg2_t;
TU_VERIFY_STATIC(sizeof(dwc2_ghwcfg2_t) == 4, "incorrect size");
typedef union {
uint32_t value;
struct TU_ATTR_PACKED {
typedef struct TU_ATTR_PACKED {
uint32_t xfer_size_width : 4; // 0..3 Transfer size counter in bits = 11 + n (max 19 bits)
uint32_t packet_size_width : 3; // 4..6 Packet size counter in bits = 4 + n (max 10 bits)
uint32_t otg_enable : 1; // 7 OTG capable
@ -364,13 +342,10 @@ typedef union {
uint32_t battery_charger_support : 1; // s14 upport battery charger
uint32_t lpm_mode : 1; // 15 LPM mode
uint32_t dfifo_depth : 16; // DFIFO depth - EP_LOC_CNT in terms of 32-bit words
};
}dwc2_ghwcfg3_t;
TU_VERIFY_STATIC(sizeof(dwc2_ghwcfg3_t) == 4, "incorrect size");
typedef union {
uint32_t value;
struct TU_ATTR_PACKED {
typedef struct TU_ATTR_PACKED {
uint32_t num_dev_period_in_ep : 4; // 0..3 Number of Device Periodic IN Endpoints
uint32_t partial_powerdown : 1; // 4 Partial Power Down Enabled
uint32_t ahb_freq_min : 1; // 5 1: minimum of AHB frequency is less than 60 MHz
@ -393,26 +368,24 @@ typedef union {
uint32_t num_dev_in_eps : 4; // 26..29 Number of Device IN Endpoints including EP0
uint32_t dma_desc_enabled : 1; // scatter/gather DMA configuration enabled
uint32_t dma_desc_dynamic : 1; // Dynamic scatter/gather DMA
};
}dwc2_ghwcfg4_t;
TU_VERIFY_STATIC(sizeof(dwc2_ghwcfg4_t) == 4, "incorrect size");
typedef union {
uint32_t value;
struct TU_ATTR_PACKED {
//--------------------------------------------------------------------
// Host Register Bitfield
//--------------------------------------------------------------------
typedef struct TU_ATTR_PACKED {
uint32_t fifo_available : 16; // 0..15 Number of words available in the Tx FIFO
uint32_t req_queue_available : 8; // 16..23 Number of spaces available in the NPT transmit request queue for both IN and OU
// 24..31 is top entry in the request queue that is currently being processed by the MAC
uint32_t qtop_terminate : 1; // 24 Last entry for selected channel
uint32_t qtop_type : 2; // 25..26 Token (0) In/Out (1) ZLP, (2) Ping/cspit, (3) Channel halt command
uint32_t qtop_ch_num : 4; // 27..30 Channel number
};
} dwc2_hnptxsts_t;
TU_VERIFY_STATIC(sizeof(dwc2_hnptxsts_t) == 4, "incorrect size");
typedef union {
uint32_t value;
struct TU_ATTR_PACKED {
typedef struct TU_ATTR_PACKED {
uint32_t fifo_available : 16; // 0..15 Number of words available in the Tx FIFO
uint32_t req_queue_available : 7; // 16..22 Number of spaces available in the PTX transmit request queue
uint32_t qtop_terminate : 1; // 23 Last entry for selected channel
@ -420,13 +393,10 @@ typedef union {
uint32_t qtop_type : 2; // 25..26 Token (0) In/Out (1) ZLP, (2) Ping/cspit, (3) Channel halt command
uint32_t qtop_ch_num : 4; // 27..30 Channel number
uint32_t qtop_odd_frame : 1; // 31 Send in odd frame
};
} dwc2_hptxsts_t;
TU_VERIFY_STATIC(sizeof(dwc2_hptxsts_t) == 4, "incorrect size");
typedef union {
uint32_t value;
struct TU_ATTR_PACKED {
typedef struct TU_ATTR_PACKED {
uint32_t conn_status : 1; // 0 Port connect status
uint32_t conn_detected : 1; // 1 Port connect detected
uint32_t enable : 1; // 2 Port enable status
@ -442,13 +412,10 @@ typedef union {
uint32_t test_control : 4; // 13..16 Port Test control
uint32_t speed : 2; // 17..18 Port speed
uint32_t rsv19_31 :13; // 19..31 Reserved
};
}dwc2_hprt_t;
TU_VERIFY_STATIC(sizeof(dwc2_hprt_t) == 4, "incorrect size");
typedef union {
uint32_t value;
struct TU_ATTR_PACKED {
typedef struct TU_ATTR_PACKED {
uint32_t ep_size : 11; // 0..10 Maximum packet size
uint32_t ep_num : 4; // 11..14 Endpoint number
uint32_t ep_dir : 1; // 15 Endpoint direction
@ -460,50 +427,49 @@ typedef union {
uint32_t odd_frame : 1; // 29 Odd frame
uint32_t disable : 1; // 30 Channel disable
uint32_t enable : 1; // 31 Channel enable
};
} dwc2_channel_char_t;
TU_VERIFY_STATIC(sizeof(dwc2_channel_char_t) == 4, "incorrect size");
typedef union {
uint32_t value;
struct TU_ATTR_PACKED {
typedef struct TU_ATTR_PACKED {
uint32_t hub_port : 7; // 0..6 Hub port number
uint32_t hub_addr : 7; // 7..13 Hub address
uint32_t xact_pos : 2; // 14..15 Transaction position
uint32_t split_compl : 1; // 16 Split completion
uint32_t rsv17_30 : 14; // 17..30 Reserved
uint32_t split_en : 1; // 31 Split enable
};
} dwc2_channel_split_t;
TU_VERIFY_STATIC(sizeof(dwc2_channel_split_t) == 4, "incorrect size");
typedef union {
uint32_t value;
struct TU_ATTR_PACKED {
typedef struct TU_ATTR_PACKED {
uint32_t xfer_size : 19; // 0..18 Transfer size in bytes
uint32_t packet_count : 10; // 19..28 Number of packets
uint32_t pid : 2; // 29..30 Packet ID
uint32_t do_ping : 1; // 31 Do PING
};
} dwc2_channel_tsize_t;
TU_VERIFY_STATIC(sizeof(dwc2_channel_tsize_t) == 4, "incorrect size");
typedef union {
uint32_t value;
struct TU_ATTR_PACKED {
typedef struct TU_ATTR_PACKED {
uint32_t num : 16; // 0..15 Frame number
uint32_t remainning : 16; // 16..31 Frame remaining
};
} dwc2_hfnum_t;
TU_VERIFY_STATIC(sizeof(dwc2_hfnum_t) == 4, "incorrect size");
// Host Channel
typedef struct {
union {
volatile uint32_t hcchar; // 500 + 20*ch Host Channel Characteristics
volatile dwc2_channel_char_t hcchar_bm;
};
union {
volatile uint32_t hcsplt; // 504 + 20*ch Host Channel Split Control
volatile dwc2_channel_split_t hcsplt_bm;
};
volatile uint32_t hcint; // 508 + 20*ch Host Channel Interrupt
volatile uint32_t hcintmsk; // 50C + 20*ch Host Channel Interrupt Mask
union {
volatile uint32_t hctsiz; // 510 + 20*ch Host Channel Transfer Size
volatile dwc2_channel_tsize_t hctsiz_bm;
};
volatile uint32_t hcdma; // 514 + 20*ch Host Channel DMA Address
uint32_t reserved518; // 518 + 20*ch
volatile uint32_t hcdmab; // 51C + 20*ch Host Channel DMA Address
@ -512,9 +478,7 @@ typedef struct {
//--------------------------------------------------------------------
// Device Register Bitfield
//--------------------------------------------------------------------
typedef union {
uint32_t value;
struct TU_ATTR_PACKED {
typedef struct TU_ATTR_PACKED {
uint32_t speed : 2; // 0..1 Speed
uint32_t nzsts_out_handshake : 1; // 2 Non-zero-length status OUT handshake
uint32_t en_32khz_suspsend : 1; // 3 Enable 32-kHz SUSPEND mode
@ -526,16 +490,13 @@ typedef union {
uint32_t rsv16 : 1; // 16 Reserved
uint32_t ipg_iso_support : 1; // 17 Interpacket gap ISO support
uint32_t epin_mismatch_count : 5; // 18..22 EP IN mismatch count
uint32_t dma_desc : 1; // 23 Enable scatter/gather DMA descriptor
uint32_t period_schedule_interval : 2; // 24..25 Periodic schedule interval for scatter/gather DMA
uint32_t dma_desc : 1; // 23 Enable scatter/gatter DMA descriptor
uint32_t period_schedule_interval : 2; // 24..25 Periodic schedule interval for scatter/gatter DMA
uint32_t resume_valid : 6; // 26..31 Resume valid period
};
} dwc2_dcfg_t;
TU_VERIFY_STATIC(sizeof(dwc2_dcfg_t) == 4, "incorrect size");
typedef union {
uint32_t value;
struct TU_ATTR_PACKED {
typedef struct TU_ATTR_PACKED {
uint32_t remote_wakeup_signal : 1; // 0 Remote wakeup signal
uint32_t soft_disconnet : 1; // 1 Soft disconnect
uint32_t gnp_in_nak_status : 1; // 2 Global non-periodic NAK IN status
@ -554,13 +515,10 @@ typedef union {
uint32_t deep_sleep_besl_reject : 1; // 18 Deep sleep BESL reject
uint32_t service_interval : 1; // 19 Service interval for ISO IN endpoint
uint32_t rsv20_31 :12; // 20..31 Reserved
};
} dwc2_dctl_t;
TU_VERIFY_STATIC(sizeof(dwc2_dctl_t) == 4, "incorrect size");
typedef union {
uint32_t value;
struct TU_ATTR_PACKED {
typedef struct TU_ATTR_PACKED {
uint32_t suspend_status : 1; // 0 Suspend status
uint32_t enum_speed : 2; // 1..2 Enumerated speed
uint32_t erratic_err : 1; // 3 Erratic error
@ -568,13 +526,10 @@ typedef union {
uint32_t frame_number : 14; // 8..21 Frame/MicroFrame number
uint32_t line_status : 2; // 22..23 Line status
uint32_t rsv24_31 : 8; // 24..31 Reserved
};
} dwc2_dsts_t;
TU_VERIFY_STATIC(sizeof(dwc2_dsts_t) == 4, "incorrect size");
typedef union {
uint32_t value;
struct TU_ATTR_PACKED {
typedef struct TU_ATTR_PACKED {
uint32_t xfer_complete : 1; // 0 Transfer complete
uint32_t disabled : 1; // 1 Endpoint disabled
uint32_t ahb_err : 1; // 2 AHB error
@ -591,18 +546,15 @@ typedef union {
uint32_t nak : 1; // 13 NAK
uint32_t nyet : 1; // 14 NYET
uint32_t rsv14_31 :17; // 15..31 Reserved
};
} dwc2_diepint_t;
TU_VERIFY_STATIC(sizeof(dwc2_diepint_t) == 4, "incorrect size");
typedef union {
uint32_t value;
struct TU_ATTR_PACKED {
uint32_t mps : 11; // 0..10 Maximum packet size, EP0 only use 2 bits
typedef struct TU_ATTR_PACKED {
uint32_t mps : 11; // 0..10 Maximum packet size, EP0 only use 2 bit
uint32_t next_ep : 4; // 11..14 Next endpoint number
uint32_t active : 1; // 15 Active
uint32_t dpid_iso_odd : 1; // 16 DATA0/DATA1 for bulk/interrupt, odd frame for isochronous
uint32_t nak_status : 1; // 17 NAK status
const uint32_t dpid_iso_odd : 1; // 16 DATA0/DATA1 for bulk/interrupt, odd frame for isochronous
const uint32_t nak_status : 1; // 17 NAK status
uint32_t type : 2; // 18..19 Endpoint type
uint32_t rsv20 : 1; // 20 Reserved
uint32_t stall : 1; // 21 Stall
@ -613,13 +565,10 @@ typedef union {
uint32_t set_data1_iso_odd : 1; // 29 Set DATA1 if bulk/interrupt, odd frame for isochronous
uint32_t disable : 1; // 30 Disable
uint32_t enable : 1; // 31 Enable
};
} dwc2_depctl_t;
TU_VERIFY_STATIC(sizeof(dwc2_depctl_t) == 4, "incorrect size");
typedef union {
uint32_t value;
struct TU_ATTR_PACKED {
typedef struct TU_ATTR_PACKED {
uint32_t xfer_complete : 1; // 0 Transfer complete
uint32_t disabled : 1; // 1 Endpoint disabled
uint32_t ahb_err : 1; // 2 AHB error
@ -637,17 +586,13 @@ typedef union {
uint32_t nyet : 1; // 14 NYET
uint32_t setup_packet_rx : 1; // 15 Setup packet received (Buffer DMA Mode only)
uint32_t rsv16_31 :16; // 16..31 Reserved
};
} dwc2_doepint_t;
TU_VERIFY_STATIC(sizeof(dwc2_doepint_t) == 4, "incorrect size");
typedef union {
uint32_t value;
struct TU_ATTR_PACKED {
typedef struct TU_ATTR_PACKED {
uint32_t xfer_size : 19; // 0..18 Transfer size in bytes
uint32_t packet_count : 10; // 19..28 Number of packets
uint32_t mc_pid : 2; // 29..30 IN: Multi Count, OUT: PID
};
} dwc2_ep_tsize_t;
TU_VERIFY_STATIC(sizeof(dwc2_ep_tsize_t) == 4, "incorrect size");
@ -656,19 +601,26 @@ typedef struct {
union {
volatile uint32_t diepctl;
volatile uint32_t doepctl;
volatile uint32_t ctl;
volatile dwc2_depctl_t ctl_bm;
};
uint32_t rsv04;
union {
volatile uint32_t intr;
volatile uint32_t diepint;
volatile dwc2_diepint_t diepint_bm;
volatile uint32_t doepint;
volatile dwc2_doepint_t doepint_bm;
};
uint32_t rsv0c;
union {
volatile uint32_t dieptsiz;
volatile uint32_t doeptsiz;
volatile uint32_t tsiz;
volatile dwc2_ep_tsize_t tsiz_bm;
};
union {
volatile uint32_t diepdma;
@ -684,16 +636,34 @@ TU_VERIFY_STATIC(sizeof(dwc2_dep_t) == 0x20, "incorrect size");
// CSR Register Map
//--------------------------------------------------------------------
typedef struct {
//------------- Core Global -------------
//------------- Core Global -------------//
union {
volatile uint32_t gotgctl; // 000 OTG Control and Status
volatile dwc2_gotgctl_t gotgctl_bm;
};
union {
volatile uint32_t gotgint; // 004 OTG Interrupt
volatile dwc2_gotgint_t gotgint_bm;
};
union {
volatile uint32_t gahbcfg; // 008 AHB Configuration
volatile dwc2_gahbcfg_t gahbcfg_bm;
};
union {
volatile uint32_t gusbcfg; // 00c USB Configuration
volatile dwc2_gusbcfg_t gusbcfg_bm;
};
union {
volatile uint32_t grstctl; // 010 Reset
volatile dwc2_grstctl_t grstctl_bm;
};
volatile uint32_t gintsts; // 014 Interrupt
volatile uint32_t gintmsk; // 018 Interrupt Mask
volatile uint32_t grxstsr; // 01c Receive Status Debug Read
union {
volatile uint32_t grxstsp; // 020 Receive Status Read/Pop
volatile dwc2_grxstsp_t grxstsp_bm;
};
volatile uint32_t grxfsiz; // 024 Receive FIFO Size
union {
volatile uint32_t dieptxf0; // 028 EP0 Tx FIFO Size
@ -701,6 +671,7 @@ typedef struct {
};
union {
volatile uint32_t hnptxsts; // 02c Non-periodic Transmit FIFO/Queue Status
volatile dwc2_hnptxsts_t hnptxsts_bm;
volatile uint32_t gnptxsts;
};
volatile uint32_t gi2cctl; // 030 I2C Address
@ -712,8 +683,14 @@ typedef struct {
volatile uint32_t guid; // 03C User (Application programmable) ID
volatile uint32_t gsnpsid; // 040 Synopsys ID + Release version
volatile uint32_t ghwcfg1; // 044 User Hardware Configuration1: endpoint dir (2 bit per ep)
union {
volatile uint32_t ghwcfg2; // 048 User Hardware Configuration2
volatile dwc2_ghwcfg2_t ghwcfg2_bm;
};
union {
volatile uint32_t ghwcfg3; // 04C User Hardware Configuration3
volatile dwc2_ghwcfg3_t ghwcfg3_bm;
};
union {
volatile uint32_t ghwcfg4; // 050 User Hardware Configuration4
volatile dwc2_ghwcfg4_t ghwcfg4_bm;
@ -727,30 +704,54 @@ typedef struct {
volatile uint32_t dieptxf[15]; // 104..13C Device Periodic Transmit FIFO Size
uint32_t reserved140[176]; // 140..3FF
//------------ Host -------------
//------------ Host -------------//
volatile uint32_t hcfg; // 400 Host Configuration
volatile uint32_t hfir; // 404 Host Frame Interval
union {
volatile uint32_t hfnum; // 408 Host Frame Number / Frame Remaining
volatile dwc2_hfnum_t hfnum_bm;
};
uint32_t reserved40c; // 40C
union {
volatile uint32_t hptxsts; // 410 Host Periodic TX FIFO / Queue Status
volatile dwc2_hptxsts_t hptxsts_bm;
};
volatile uint32_t haint; // 414 Host All Channels Interrupt
volatile uint32_t haintmsk; // 418 Host All Channels Interrupt Mask
volatile uint32_t hflbaddr; // 41C Host Frame List Base Address
uint32_t reserved420[8]; // 420..43F
union {
volatile uint32_t hprt; // 440 Host Port Control and Status
volatile dwc2_hprt_t hprt_bm;
};
uint32_t reserved444[47]; // 444..4FF
//------------- Host Channel --------
//------------- Host Channel -------------//
dwc2_channel_t channel[16]; // 500..6FF Host Channels 0-15
uint32_t reserved700[64]; // 700..7FF
//------------- Device -----------
//------------- Device -----------//
union {
volatile uint32_t dcfg; // 800 Device Configuration
volatile dwc2_dcfg_t dcfg_bm;
};
union {
volatile uint32_t dctl; // 804 Device Control
volatile dwc2_dctl_t dctl_bm;
};
union {
volatile uint32_t dsts; // 808 Device Status (RO)
volatile dwc2_dsts_t dsts_bm;
};
uint32_t reserved80c; // 80C
union {
volatile uint32_t diepmsk; // 810 Device IN Endpoint Interrupt Mask
volatile dwc2_diepint_t diepmsk_bm;
};
union {
volatile uint32_t doepmsk; // 814 Device OUT Endpoint Interrupt Mask
volatile dwc2_doepint_t doepmsk_bm;
};
volatile uint32_t daint; // 818 Device All Endpoints Interrupt
volatile uint32_t daintmsk; // 81C Device All Endpoints Interrupt Mask
volatile uint32_t dtknqr1; // 820 Device IN token sequence learning queue read1
@ -760,15 +761,15 @@ typedef struct {
volatile uint32_t dthrctl; // 830 Device threshold Control
volatile uint32_t diepempmsk; // 834 Device IN Endpoint FIFO Empty Interrupt Mask
// Device Each Endpoint (IN/OUT) Interrupt/Mask for generating dedicated EP interrupt line require
// OTG_MULTI_PROC_INTRPT=1
// Device Each Endpoint (IN/OUT) Interrupt/Mask for generating dedicated EP interrupt line
// require OTG_MULTI_PROC_INTRPT=1
volatile uint32_t deachint; // 838 Device Each Endpoint Interrupt
volatile uint32_t deachmsk; // 83C Device Each Endpoint Interrupt mask
volatile uint32_t diepeachmsk[16]; // 840..87C Device Each IN Endpoint mask
volatile uint32_t doepeachmsk[16]; // 880..8BF Device Each OUT Endpoint mask
uint32_t reserved8c0[16]; // 8C0..8FF
//------------- Device Endpoint -----
//------------- Device Endpoint -------------//
union {
dwc2_dep_t ep[2][16]; // 0: IN, 1 OUT
struct {
@ -778,12 +779,12 @@ typedef struct {
};
uint32_t reservedd00[64]; // D00..DFF
//------------- Power Clock ---------
//------------- Power Clock -------------//
volatile uint32_t pcgcctl; // E00 Power and Clock Gating Characteristic Control
volatile uint32_t pcgcctl1; // E04 Power and Clock Gating Characteristic Control 1
uint32_t reservede08[126]; // E08..FFF
//------------- FIFOs -------------
//------------- FIFOs -------------//
// Word-accessed only using first pointer since it auto shift
volatile uint32_t fifo[16][0x400]; // 1000..FFFF Endpoint FIFO
} dwc2_regs_t;

View file

@ -36,7 +36,6 @@
#define DWC2_DEBUG 2
#include "host/hcd.h"
#include "host/usbh.h"
#include "dwc2_common.h"
// Max number of endpoints application can open, can be larger than DWC2_CHANNEL_COUNT_MAX
@ -45,6 +44,8 @@
#endif
#define DWC2_CHANNEL_COUNT_MAX 16 // absolute max channel count
#define DWC2_CHANNEL_COUNT(_dwc2) tu_min8((_dwc2)->ghwcfg2_bm.num_host_ch + 1, DWC2_CHANNEL_COUNT_MAX)
TU_VERIFY_STATIC(CFG_TUH_DWC2_ENDPOINT_MAX <= 255, "currently only use 8-bit for index");
enum {
@ -77,8 +78,8 @@ typedef struct {
struct TU_ATTR_PACKED {
uint32_t uframe_interval : 18; // micro-frame interval
uint32_t speed : 2;
uint32_t next_pid : 2; // PID for next transfer
uint32_t next_do_ping : 1; // Do PING for next transfer if possible (highspeed OUT)
uint32_t next_pid : 2;
uint32_t do_ping : 1;
// uint32_t : 9;
};
@ -96,6 +97,7 @@ typedef struct {
uint8_t err_count : 3;
uint8_t period_split_nyet_count : 3;
uint8_t halted_nyet : 1;
uint8_t halted_sof_schedule : 1;
};
uint8_t result;
@ -114,15 +116,9 @@ hcd_data_t _hcd_data;
//--------------------------------------------------------------------
//
//--------------------------------------------------------------------
TU_ATTR_ALWAYS_INLINE static inline uint8_t dwc2_channel_count(const dwc2_regs_t* dwc2) {
const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2};
return tu_min8(ghwcfg2.num_host_ch + 1, DWC2_CHANNEL_COUNT_MAX);
}
TU_ATTR_ALWAYS_INLINE static inline tusb_speed_t hprt_speed_get(dwc2_regs_t* dwc2) {
tusb_speed_t speed;
const dwc2_hprt_t hprt = {.value = dwc2->hprt};
switch(hprt.speed) {
switch(dwc2->hprt_bm.speed) {
case HPRT_SPEED_HIGH: speed = TUSB_SPEED_HIGH; break;
case HPRT_SPEED_FULL: speed = TUSB_SPEED_FULL; break;
case HPRT_SPEED_LOW : speed = TUSB_SPEED_LOW ; break;
@ -137,8 +133,7 @@ TU_ATTR_ALWAYS_INLINE static inline tusb_speed_t hprt_speed_get(dwc2_regs_t* dwc
TU_ATTR_ALWAYS_INLINE static inline bool dma_host_enabled(const dwc2_regs_t* dwc2) {
(void) dwc2;
// Internal DMA only
const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2};
return CFG_TUH_DWC2_DMA_ENABLE && ghwcfg2.arch == GHWCFG2_ARCH_INTERNAL_DMA;
return CFG_TUH_DWC2_DMA_ENABLE && dwc2->ghwcfg2_bm.arch == GHWCFG2_ARCH_INTERNAL_DMA;
}
#if CFG_TUH_MEM_DCACHE_ENABLE
@ -160,7 +155,7 @@ bool hcd_dcache_clean_invalidate(const void* addr, uint32_t data_size) {
// Allocate a channel for new transfer
TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_alloc(dwc2_regs_t* dwc2) {
const uint8_t max_channel = dwc2_channel_count(dwc2);
const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2);
for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) {
hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
if (!xfer->allocated) {
@ -173,18 +168,15 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_alloc(dwc2_regs_t* dwc2) {
}
// Check if is periodic (interrupt/isochronous)
TU_ATTR_ALWAYS_INLINE static inline bool channel_is_periodic(uint32_t hcchar) {
const dwc2_channel_char_t hcchar_bm = {.value = hcchar};
return hcchar_bm.ep_type == HCCHAR_EPTYPE_INTERRUPT || hcchar_bm.ep_type == HCCHAR_EPTYPE_ISOCHRONOUS;
TU_ATTR_ALWAYS_INLINE static inline bool edpt_is_periodic(uint8_t ep_type) {
return ep_type == HCCHAR_EPTYPE_INTERRUPT || ep_type == HCCHAR_EPTYPE_ISOCHRONOUS;
}
TU_ATTR_ALWAYS_INLINE static inline uint8_t req_queue_avail(const dwc2_regs_t* dwc2, bool is_period) {
if (is_period) {
const dwc2_hptxsts_t hptxsts = {.value = dwc2->hptxsts};
return hptxsts.req_queue_available;
return dwc2->hptxsts_bm.req_queue_available;
} else {
const dwc2_hnptxsts_t hnptxsts = {.value = dwc2->hnptxsts};
return hnptxsts.req_queue_available;
return dwc2->hnptxsts_bm.req_queue_available;
}
}
@ -196,7 +188,7 @@ TU_ATTR_ALWAYS_INLINE static inline void channel_dealloc(dwc2_regs_t* dwc2, uint
TU_ATTR_ALWAYS_INLINE static inline bool channel_disable(const dwc2_regs_t* dwc2, dwc2_channel_t* channel) {
// disable also require request queue
TU_ASSERT(req_queue_avail(dwc2, channel_is_periodic(channel->hcchar)));
TU_ASSERT(req_queue_avail(dwc2, edpt_is_periodic(channel->hcchar_bm.ep_type)));
channel->hcintmsk |= HCINT_HALTED;
channel->hcchar |= HCCHAR_CHDIS | HCCHAR_CHENA; // must set both CHDIS and CHENA
return true;
@ -204,18 +196,18 @@ TU_ATTR_ALWAYS_INLINE static inline bool channel_disable(const dwc2_regs_t* dwc2
// attempt to send IN token to receive data
TU_ATTR_ALWAYS_INLINE static inline bool channel_send_in_token(const dwc2_regs_t* dwc2, dwc2_channel_t* channel) {
TU_ASSERT(req_queue_avail(dwc2, channel_is_periodic(channel->hcchar)));
TU_ASSERT(req_queue_avail(dwc2, edpt_is_periodic(channel->hcchar_bm.ep_type)));
channel->hcchar |= HCCHAR_CHENA;
return true;
}
// Find currently enabled channel. Note: EP0 is bidirectional
TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_find_enabled(dwc2_regs_t* dwc2, uint8_t dev_addr, uint8_t ep_num, uint8_t ep_dir) {
const uint8_t max_channel = dwc2_channel_count(dwc2);
const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2);
for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) {
if (_hcd_data.xfer[ch_id].allocated) {
const dwc2_channel_char_t hcchar = {.value = dwc2->channel[ch_id].hcchar};
if (hcchar.dev_addr == dev_addr && hcchar.ep_num == ep_num && (ep_num == 0 || hcchar.ep_dir == ep_dir)) {
const dwc2_channel_char_t hcchar_bm = dwc2->channel[ch_id].hcchar_bm;
if (hcchar_bm.dev_addr == dev_addr && hcchar_bm.ep_num == ep_num && (ep_num == 0 || hcchar_bm.ep_dir == ep_dir)) {
return ch_id;
}
}
@ -312,13 +304,12 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t cal_next_pid(uint8_t pid, uint8_t pa
static void dfifo_host_init(uint8_t rhport) {
const dwc2_controller_t* dwc2_controller = &_dwc2_controller[rhport];
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2};
// Scatter/Gather DMA mode is not yet supported. Buffer DMA only need 1 words per channel
const bool is_dma = dma_host_enabled(dwc2);
uint16_t dfifo_top = dwc2_controller->ep_fifo_size/4;
if (is_dma) {
dfifo_top -= ghwcfg2.num_host_ch;
dfifo_top -= dwc2->ghwcfg2_bm.num_host_ch;
}
// fixed allocation for now, improve later:
@ -328,7 +319,7 @@ static void dfifo_host_init(uint8_t rhport) {
uint32_t ptx_largest = is_highspeed ? TUSB_EPSIZE_ISO_HS_MAX/4 : 256/4;
uint16_t nptxfsiz = 2 * nptx_largest;
uint16_t rxfsiz = 2 * (ptx_largest + 2) + ghwcfg2.num_host_ch;
uint16_t rxfsiz = 2 * (ptx_largest + 2) + dwc2->ghwcfg2_bm.num_host_ch;
TU_ASSERT(dfifo_top >= (nptxfsiz + rxfsiz),);
uint16_t ptxfsiz = dfifo_top - (nptxfsiz + rxfsiz);
@ -390,7 +381,7 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
dwc2->hprt = HPRT_POWER; // turn on VBUS
// Enable required interrupts
dwc2->gintmsk |= GINTSTS_OTGINT | GINTSTS_CONIDSTSCHNG | GINTSTS_HPRTINT | GINTSTS_HCINT | GINTSTS_DISCINT;
dwc2->gintmsk |= GINTSTS_OTGINT | GINTSTS_CONIDSTSCHNG | GINTSTS_HPRTINT | GINTSTS_HCINT;
// NPTX can hold at least 2 packet, change interrupt level to half-empty
uint32_t gahbcfg = dwc2->gahbcfg & ~GAHBCFG_TX_FIFO_EPMTY_LVL;
@ -470,8 +461,8 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t*
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
const tusb_speed_t rh_speed = hprt_speed_get(dwc2);
tuh_bus_info_t bus_info;
tuh_bus_info_get(dev_addr, &bus_info);
hcd_devtree_info_t devtree_info;
hcd_devtree_get_info(dev_addr, &devtree_info);
// find a free endpoint
const uint8_t ep_id = edpt_alloc();
@ -482,7 +473,7 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t*
hcchar_bm->ep_size = tu_edpt_packet_size(desc_ep);
hcchar_bm->ep_num = tu_edpt_number(desc_ep->bEndpointAddress);
hcchar_bm->ep_dir = tu_edpt_dir(desc_ep->bEndpointAddress);
hcchar_bm->low_speed_dev = (bus_info.speed == TUSB_SPEED_LOW) ? 1 : 0;
hcchar_bm->low_speed_dev = (devtree_info.speed == TUSB_SPEED_LOW) ? 1 : 0;
hcchar_bm->ep_type = desc_ep->bmAttributes.xfer; // ep_type matches TUSB_XFER_*
hcchar_bm->err_multi_count = 0;
hcchar_bm->dev_addr = dev_addr;
@ -491,21 +482,21 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t*
hcchar_bm->enable = 1;
dwc2_channel_split_t* hcsplt_bm = &edpt->hcsplt_bm;
hcsplt_bm->hub_port = bus_info.hub_port;
hcsplt_bm->hub_addr = bus_info.hub_addr;
hcsplt_bm->hub_port = devtree_info.hub_port;
hcsplt_bm->hub_addr = devtree_info.hub_addr;
hcsplt_bm->xact_pos = 0;
hcsplt_bm->split_compl = 0;
hcsplt_bm->split_en = (rh_speed == TUSB_SPEED_HIGH && bus_info.speed != TUSB_SPEED_HIGH) ? 1 : 0;
hcsplt_bm->split_en = (rh_speed == TUSB_SPEED_HIGH && devtree_info.speed != TUSB_SPEED_HIGH) ? 1 : 0;
edpt->speed = bus_info.speed;
edpt->speed = devtree_info.speed;
edpt->next_pid = HCTSIZ_PID_DATA0;
if (desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) {
edpt->uframe_interval = 1 << (desc_ep->bInterval - 1);
if (bus_info.speed == TUSB_SPEED_FULL) {
if (devtree_info.speed == TUSB_SPEED_FULL) {
edpt->uframe_interval <<= 3;
}
} else if (desc_ep->bmAttributes.xfer == TUSB_XFER_INTERRUPT) {
if (bus_info.speed == TUSB_SPEED_HIGH) {
if (devtree_info.speed == TUSB_SPEED_HIGH) {
edpt->uframe_interval = 1 << (desc_ep->bInterval - 1);
} else {
edpt->uframe_interval = desc_ep->bInterval << 3;
@ -515,19 +506,13 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t*
return true;
}
bool hcd_edpt_close(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) {
(void) rhport; (void) daddr; (void) ep_addr;
return false; // TODO not implemented yet
}
// clean up channel after part of transfer is done but the whole urb is not complete
static void channel_xfer_out_wrapup(dwc2_regs_t* dwc2, uint8_t ch_id) {
hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
const dwc2_channel_t* channel = &dwc2->channel[ch_id];
dwc2_channel_t* channel = &dwc2->channel[ch_id];
hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
const dwc2_channel_tsize_t hctsiz = {.value = channel->hctsiz};
edpt->next_pid = hctsiz.pid; // save PID
edpt->next_pid = channel->hctsiz_bm.pid; // save PID
/* Since hctsiz.xfersize field reflects the number of bytes transferred via the AHB, not the USB)
* For IN: we can use hctsiz.xfersize as remaining bytes.
@ -535,10 +520,9 @@ static void channel_xfer_out_wrapup(dwc2_regs_t* dwc2, uint8_t ch_id) {
* number of packets that have been transferred via the USB. This is always an integral number of packets if the
* transfer was halted before its normal completion.
*/
const uint16_t remain_packets = hctsiz.packet_count;
const dwc2_channel_char_t hcchar = {.value = channel->hcchar};
const uint16_t total_packets = cal_packet_count(edpt->buflen, hcchar.ep_size);
const uint16_t actual_bytes = (total_packets - remain_packets) * hcchar.ep_size;
const uint16_t remain_packets = channel->hctsiz_bm.packet_count;
const uint16_t total_packets = cal_packet_count(edpt->buflen, channel->hcchar_bm.ep_size);
const uint16_t actual_bytes = (total_packets - remain_packets) * channel->hcchar_bm.ep_size;
xfer->fifo_bytes = 0;
xfer->xferred_bytes += actual_bytes;
@ -551,7 +535,7 @@ static bool channel_xfer_start(dwc2_regs_t* dwc2, uint8_t ch_id) {
hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
dwc2_channel_char_t* hcchar_bm = &edpt->hcchar_bm;
dwc2_channel_t* channel = &dwc2->channel[ch_id];
bool const is_period = channel_is_periodic(edpt->hcchar);
bool const is_period = edpt_is_periodic(hcchar_bm->ep_type);
// clear previous state
xfer->fifo_bytes = 0;
@ -564,16 +548,13 @@ static bool channel_xfer_start(dwc2_regs_t* dwc2, uint8_t ch_id) {
// hctsiz: zero length packet still count as 1
const uint16_t packet_count = cal_packet_count(edpt->buflen, hcchar_bm->ep_size);
dwc2_channel_tsize_t hctsiz = {.value = 0};
hctsiz.pid = edpt->next_pid; // next PID is set in transfer complete interrupt
hctsiz.packet_count = packet_count;
hctsiz.xfer_size = edpt->buflen;
if (edpt->next_do_ping && edpt->speed == TUSB_SPEED_HIGH &&
uint32_t hctsiz = (edpt->next_pid << HCTSIZ_PID_Pos) | (packet_count << HCTSIZ_PKTCNT_Pos) | edpt->buflen;
if (edpt->do_ping && edpt->speed == TUSB_SPEED_HIGH &&
edpt->next_pid != HCTSIZ_PID_SETUP && hcchar_bm->ep_dir == TUSB_DIR_OUT) {
hctsiz.do_ping = 1;
hctsiz |= HCTSIZ_DOPING;
}
channel->hctsiz = hctsiz.value;
edpt->next_do_ping = 0;
channel->hctsiz = hctsiz;
edpt->do_ping = 0;
// pre-calculate next PID based on packet count, adjusted in transfer complete interrupt if short packet
if (hcchar_bm->ep_num == 0) {
@ -604,7 +585,7 @@ static bool channel_xfer_start(dwc2_regs_t* dwc2, uint8_t ch_id) {
hcintmsk |= HCINT_BABBLE_ERR | HCINT_DATATOGGLE_ERR | HCINT_ACK;
} else {
hcintmsk |= HCINT_NYET;
if (edpt->hcsplt_bm.split_en || hctsiz.do_ping) {
if (edpt->hcsplt_bm.split_en) {
hcintmsk |= HCINT_ACK;
}
}
@ -713,23 +694,18 @@ bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
//--------------------------------------------------------------------
// HCD Event Handler
//--------------------------------------------------------------------
// retry an IN transfer, channel must be halted
static void channel_xfer_in_retry(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hcint) {
hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
dwc2_channel_t* channel = &dwc2->channel[ch_id];
dwc2_channel_char_t hcchar = {.value = channel->hcchar};
hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
if (channel_is_periodic(hcchar.value)){
const dwc2_channel_split_t hcsplt = {.value = channel->hcsplt};
if (edpt_is_periodic(channel->hcchar_bm.ep_type)){
// retry immediately for periodic split NYET if we haven't reach max retry
if (hcsplt.split_en && hcsplt.split_compl && (hcint & HCINT_NYET || xfer->halted_nyet)) {
if (channel->hcsplt_bm.split_en && channel->hcsplt_bm.split_compl && (hcint & HCINT_NYET || xfer->halted_nyet)) {
xfer->period_split_nyet_count++;
xfer->halted_nyet = 0;
if (xfer->period_split_nyet_count < HCD_XFER_PERIOD_SPLIT_NYET_MAX) {
hcchar.odd_frame = 1 - (dwc2->hfnum & 1); // transfer on next frame
channel->hcchar = hcchar.value;
channel->hcchar_bm.odd_frame = 1 - (dwc2->hfnum & 1); // transfer on next frame
channel_send_in_token(dwc2, channel);
return;
} else {
@ -738,24 +714,18 @@ static void channel_xfer_in_retry(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci
}
}
const uint32_t ucount = (hprt_speed_get(dwc2) == TUSB_SPEED_HIGH ? 1 : 8);
if (edpt->uframe_interval == ucount) {
// retry on next frame if bInterval is 1
hcchar.odd_frame = 1 - (dwc2->hfnum & 1);
channel->hcchar = hcchar.value;
channel_send_in_token(dwc2, channel);
} else {
// otherwise, de-allocate channel, enable SOF set frame counter for later transfer
const dwc2_channel_tsize_t hctsiz = {.value = channel->hctsiz};
edpt->next_pid = hctsiz.pid; // save PID
edpt->uframe_countdown = edpt->uframe_interval - ucount;
// enable SOF interrupt if not already enabled
if (!(dwc2->gintmsk & GINTMSK_SOFM)) {
dwc2->gintsts = GINTSTS_SOF;
dwc2->gintmsk |= GINTMSK_SOFM;
}
// for periodic, de-allocate channel, enable SOF set frame counter for later transfer
edpt->next_pid = channel->hctsiz_bm.pid; // save PID
edpt->uframe_countdown = edpt->uframe_interval;
dwc2->gintmsk |= GINTSTS_SOF;
if (hcint & HCINT_HALTED) {
// already halted, de-allocate channel (called from DMA isr)
channel_dealloc(dwc2, ch_id);
} else {
// disable channel first if not halted (called slave isr)
xfer->halted_sof_schedule = 1;
channel_disable(dwc2, channel);
}
} else {
// for control/bulk: retry immediately
@ -786,13 +756,13 @@ static void handle_rxflvl_irq(uint8_t rhport) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
// Pop control word off FIFO
const dwc2_grxstsp_t grxstsp = {.value= dwc2->grxstsp};
const uint8_t ch_id = grxstsp.ep_ch_num;
const dwc2_grxstsp_t grxstsp_bm = dwc2->grxstsp_bm;
const uint8_t ch_id = grxstsp_bm.ep_ch_num;
switch (grxstsp.packet_status) {
switch (grxstsp_bm.packet_status) {
case GRXSTS_PKTSTS_RX_DATA: {
// In packet received, pop this entry --> ACK interrupt
const uint16_t byte_count = grxstsp.byte_count;
const uint16_t byte_count = grxstsp_bm.byte_count;
hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX,);
hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
@ -826,26 +796,25 @@ static void handle_rxflvl_irq(uint8_t rhport) {
// return true if there is still pending data and need more ISR
static bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) {
// Use period txsts for both p/np to get request queue space available (1-bit difference, it is small enough)
const dwc2_hptxsts_t txsts = {.value = (is_periodic ? dwc2->hptxsts : dwc2->hnptxsts)};
volatile dwc2_hptxsts_t* txsts_bm = (volatile dwc2_hptxsts_t*) (is_periodic ? &dwc2->hptxsts : &dwc2->hnptxsts);
const uint8_t max_channel = dwc2_channel_count(dwc2);
const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2);
for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) {
dwc2_channel_t* channel = &dwc2->channel[ch_id];
const dwc2_channel_char_t hcchar = {.value = channel->hcchar};
// skip writing to FIFO if channel is expecting halted.
if (!(channel->hcintmsk & HCINT_HALTED) && (hcchar.ep_dir == TUSB_DIR_OUT)) {
if (!(channel->hcintmsk & HCINT_HALTED) && (channel->hcchar_bm.ep_dir == TUSB_DIR_OUT)) {
hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX);
hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
const dwc2_channel_tsize_t hctsiz = {.value = channel->hctsiz};
const uint16_t remain_packets = hctsiz.packet_count;
const uint16_t remain_packets = channel->hctsiz_bm.packet_count;
for (uint16_t i = 0; i < remain_packets; i++) {
const uint16_t remain_bytes = edpt->buflen - xfer->fifo_bytes;
const uint16_t xact_bytes = tu_min16(remain_bytes, hcchar.ep_size);
const uint16_t xact_bytes = tu_min16(remain_bytes, channel->hcchar_bm.ep_size);
// skip if there is not enough space in FIFO and RequestQueue.
// Packet's last word written to FIFO will trigger a request queue
if ((xact_bytes > (txsts.fifo_available << 2)) || (txsts.req_queue_available == 0)) {
if ((xact_bytes > (txsts_bm->fifo_available << 2)) || (txsts_bm->req_queue_available == 0)) {
return true;
}
@ -862,26 +831,23 @@ static bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t h
hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
dwc2_channel_t* channel = &dwc2->channel[ch_id];
hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
dwc2_channel_split_t hcsplt = {.value = channel->hcsplt};
const dwc2_channel_tsize_t hctsiz = {.value = channel->hctsiz};
bool is_done = false;
// if (hcsplt.split_en) {
// if (channel->hcsplt_bm.split_en) {
// if (edpt->hcchar_bm.ep_num == 1) {
// TU_LOG1("Frame %u, ch %u: ep %u, hcint 0x%04lX ", dwc2->hfnum_bm.num, ch_id, hcsplt.ep_num, hcint);
// TU_LOG1("Frame %u, ch %u: ep %u, hcint 0x%04lX ", dwc2->hfnum_bm.num, ch_id, channel->hcchar_bm.ep_num, hcint);
// print_hcint(hcint);
// }
if (hcint & HCINT_XFER_COMPLETE) {
if (edpt->hcchar_bm.ep_num != 0) {
edpt->next_pid = hctsiz.pid; // save pid (already toggled)
edpt->next_pid = channel->hctsiz_bm.pid; // save pid (already toggled)
}
const uint16_t remain_packets = hctsiz.packet_count;
if (hcsplt.split_en && remain_packets && xfer->fifo_bytes == edpt->hcchar_bm.ep_size) {
const uint16_t remain_packets = channel->hctsiz_bm.packet_count;
if (channel->hcsplt_bm.split_en && remain_packets && xfer->fifo_bytes == edpt->hcchar_bm.ep_size) {
// Split can only complete 1 transaction (up to 1 packet) at a time, schedule more
hcsplt.split_compl = 0;
channel->hcsplt = hcsplt.value;
channel->hcsplt_bm.split_compl = 0;
} else {
xfer->result = XFER_RESULT_SUCCESS;
}
@ -900,44 +866,43 @@ static bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t h
channel_disable(dwc2, channel);
} else if (hcint & HCINT_NYET) {
// restart complete split
hcsplt.split_compl = 1;
channel->hcsplt = hcsplt.value;
channel->hcsplt_bm.split_compl = 1;
xfer->halted_nyet = 1;
channel_disable(dwc2, channel);
} else if (hcint & HCINT_NAK) {
// NAK received, disable channel to flush all posted request and try again
if (hcsplt.split_en) {
hcsplt.split_compl = 0; // restart with start-split
channel->hcsplt = hcsplt.value;
// NAK received, re-enable channel if request queue is available
if (channel->hcsplt_bm.split_en) {
channel->hcsplt_bm.split_compl = 0; // restart with start-split
}
channel_disable(dwc2, channel);
} else if (hcint & HCINT_ACK) {
xfer->err_count = 0;
if (hcsplt.split_en) {
if (!hcsplt.split_compl) {
if (channel->hcsplt_bm.split_en) {
if (!channel->hcsplt_bm.split_compl) {
// start split is ACK --> do complete split
channel->hcintmsk |= HCINT_NYET;
hcsplt.split_compl = 1;
channel->hcsplt = hcsplt.value;
channel->hcsplt_bm.split_compl = 1;
channel_send_in_token(dwc2, channel);
} else {
// do nothing for complete split with DATA, this will trigger XferComplete and handled there
}
} else {
// ACK with data
const uint16_t remain_packets = hctsiz.packet_count;
const uint16_t remain_packets = channel->hctsiz_bm.packet_count;
if (remain_packets) {
// still more packet to receive, also reset to start split
hcsplt.split_compl = 0;
channel->hcsplt = hcsplt.value;
channel->hcsplt_bm.split_compl = 0;
channel_send_in_token(dwc2, channel);
}
}
} else if (hcint & HCINT_HALTED) {
channel->hcintmsk &= ~HCINT_HALTED;
if (xfer->result != XFER_RESULT_INVALID) {
if (xfer->halted_sof_schedule) {
// de-allocate channel but does not complete xfer, we schedule it in the SOF interrupt
channel_dealloc(dwc2, ch_id);
} else if (xfer->result != XFER_RESULT_INVALID) {
is_done = true;
} else if (xfer->err_count == HCD_XFER_ERROR_MAX) {
xfer->result = XFER_RESULT_FAILED;
@ -957,29 +922,23 @@ static bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t
hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
dwc2_channel_t* channel = &dwc2->channel[ch_id];
hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
dwc2_channel_split_t hcsplt = {.value = channel->hcsplt};
bool is_done = false;
if (hcint & HCINT_XFER_COMPLETE) {
is_done = true;
xfer->result = XFER_RESULT_SUCCESS;
channel->hcintmsk &= ~HCINT_ACK;
if (hcint & HCINT_NYET) {
// complete transfer with NYET, do ping next time
edpt->next_do_ping = 1;
}
} else if (hcint & HCINT_STALL) {
xfer->result = XFER_RESULT_STALLED;
channel_disable(dwc2, channel);
} else if (hcint & HCINT_NYET) {
xfer->err_count = 0;
if (hcsplt.split_en) {
if (channel->hcsplt_bm.split_en) {
// retry complete split
hcsplt.split_compl = 1;
channel->hcsplt = hcsplt.value;
channel->hcsplt_bm.split_compl = 1;
channel->hcchar |= HCCHAR_CHENA;
} else {
edpt->next_do_ping = 1;
edpt->do_ping = 1;
channel_xfer_out_wrapup(dwc2, ch_id);
channel_disable(dwc2, channel);
}
@ -992,7 +951,7 @@ static bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t
channel->hcintmsk |= HCINT_ACK;
} else {
// NAK disable channel to flush all posted request and try again
edpt->next_do_ping = 1;
edpt->do_ping = 1;
xfer->err_count = 0;
}
} else if (hcint & HCINT_HALTED) {
@ -1009,17 +968,9 @@ static bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t
} else if (hcint & HCINT_ACK) {
xfer->err_count = 0;
channel->hcintmsk &= ~HCINT_ACK;
if (hcsplt.split_en) {
if (!hcsplt.split_compl) {
// ACK for start split --> do complete split
hcsplt.split_compl = 1;
channel->hcsplt = hcsplt.value;
channel->hcchar |= HCCHAR_CHENA;
}
} else {
// ACK interrupt is only enabled for Split and PING
// ACK for PING, which mean device is ready to receive data
channel->hctsiz &= ~HCTSIZ_DOPING; // HC already cleared PING bit, but we clear anyway
if (channel->hcsplt_bm.split_en && !channel->hcsplt_bm.split_compl) {
// start split is ACK --> do complete split
channel->hcsplt_bm.split_compl = 1;
channel->hcchar |= HCCHAR_CHENA;
}
}
@ -1038,9 +989,6 @@ static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci
hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
dwc2_channel_t* channel = &dwc2->channel[ch_id];
hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
dwc2_channel_char_t hcchar = {.value = channel->hcchar};
dwc2_channel_split_t hcsplt = {.value = channel->hcsplt};
const dwc2_channel_tsize_t hctsiz = {.value = channel->hctsiz};
bool is_done = false;
@ -1048,8 +996,8 @@ static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci
if (hcint & HCINT_HALTED) {
if (hcint & (HCINT_XFER_COMPLETE | HCINT_STALL | HCINT_BABBLE_ERR)) {
const uint16_t remain_bytes = (uint16_t) hctsiz.xfer_size;
const uint16_t remain_packets = hctsiz.packet_count;
const uint16_t remain_bytes = (uint16_t) channel->hctsiz_bm.xfer_size;
const uint16_t remain_packets = channel->hctsiz_bm.packet_count;
const uint16_t actual_len = edpt->buflen - remain_bytes;
xfer->xferred_bytes += actual_len;
@ -1059,14 +1007,13 @@ static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci
xfer->result = XFER_RESULT_STALLED;
} else if (hcint & HCINT_BABBLE_ERR) {
xfer->result = XFER_RESULT_FAILED;
} else if (hcsplt.split_en && remain_packets && actual_len == hcchar.ep_size) {
} else if (channel->hcsplt_bm.split_en && remain_packets && actual_len == edpt->hcchar_bm.ep_size) {
// Split can only complete 1 transaction (up to 1 packet) at a time, schedule more
is_done = false;
edpt->buffer += actual_len;
edpt->buflen -= actual_len;
hcsplt.split_compl = 0;
channel->hcsplt = hcsplt.value;
channel->hcsplt_bm.split_compl = 0;
channel_xfer_in_retry(dwc2, ch_id, hcint);
} else {
xfer->result = XFER_RESULT_SUCCESS;
@ -1081,38 +1028,33 @@ static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci
xfer->result = XFER_RESULT_FAILED;
} else {
channel->hcintmsk |= HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR;
hcsplt.split_compl = 0;
channel->hcsplt = hcsplt.value;
channel->hcsplt_bm.split_compl = 0;
channel_xfer_in_retry(dwc2, ch_id, hcint);
}
} else if (hcint & HCINT_NYET) {
// Must handle nyet before nak or ack. Could get a nyet at the same time as either of those on a BULK/CONTROL
// OUT that started with a PING. The nyet takes precedence.
if (hcsplt.split_en) {
if (channel->hcsplt_bm.split_en) {
// split not yet mean hub has no data, retry complete split
hcsplt.split_compl = 1;
channel->hcsplt = hcsplt.value;
channel->hcsplt_bm.split_compl = 1;
channel_xfer_in_retry(dwc2, ch_id, hcint);
}
} else if (hcint & HCINT_ACK) {
xfer->err_count = 0;
channel->hcintmsk &= ~HCINT_ACK;
if (hcsplt.split_en) {
if (channel->hcsplt_bm.split_en) {
// start split is ACK --> do complete split
// TODO: for ISO must use xact_pos to plan complete split based on microframe (up to 187.5 bytes/uframe)
hcsplt.split_compl = 1;
channel->hcsplt = hcsplt.value;
if (channel_is_periodic(channel->hcchar)) {
hcchar.odd_frame = 1 - (dwc2->hfnum & 1); // transfer on next frame
channel->hcchar = hcchar.value;
channel->hcsplt_bm.split_compl = 1;
if (edpt_is_periodic(channel->hcchar_bm.ep_type)) {
channel->hcchar_bm.odd_frame = 1 - (dwc2->hfnum & 1); // transfer on next frame
}
channel_send_in_token(dwc2, channel);
}
} else if (hcint & (HCINT_NAK | HCINT_DATATOGGLE_ERR)) {
xfer->err_count = 0;
channel->hcintmsk &= ~(HCINT_NAK | HCINT_DATATOGGLE_ERR);
hcsplt.split_compl = 0; // restart with start-split
channel->hcsplt = hcsplt.value;
channel->hcsplt_bm.split_compl = 0; // restart with start-split
channel_xfer_in_retry(dwc2, ch_id, hcint);
} else if (hcint & HCINT_FARME_OVERRUN) {
// retry start-split in next binterval
@ -1127,8 +1069,6 @@ static bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hc
hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
dwc2_channel_t* channel = &dwc2->channel[ch_id];
hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
const dwc2_channel_char_t hcchar = {.value = channel->hcchar};
dwc2_channel_split_t hcsplt = {.value = channel->hcsplt};
bool is_done = false;
@ -1164,18 +1104,16 @@ static bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hc
}
}
} else if (hcint & HCINT_NYET) {
if (hcsplt.split_en && hcsplt.split_compl) {
if (channel->hcsplt_bm.split_en && channel->hcsplt_bm.split_compl) {
// split not yet mean hub has no data, retry complete split
hcsplt.split_compl = 1;
channel->hcsplt = hcsplt.value;
channel->hcsplt_bm.split_compl = 1;
channel->hcchar |= HCCHAR_CHENA;
}
} else if (hcint & HCINT_ACK) {
xfer->err_count = 0;
if (hcsplt.split_en && !hcsplt.split_compl) {
if (channel->hcsplt_bm.split_en && !channel->hcsplt_bm.split_compl) {
// start split is ACK --> do complete split
hcsplt.split_compl = 1;
channel->hcsplt = hcsplt.value;
channel->hcsplt_bm.split_compl = 1;
channel->hcchar |= HCCHAR_CHENA;
}
}
@ -1191,14 +1129,14 @@ static bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hc
static void handle_channel_irq(uint8_t rhport, bool in_isr) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
const bool is_dma = dma_host_enabled(dwc2);
const uint8_t max_channel = dwc2_channel_count(dwc2);
const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2);
for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) {
if (tu_bit_test(dwc2->haint, ch_id)) {
dwc2_channel_t* channel = &dwc2->channel[ch_id];
hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id];
TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX,);
dwc2_channel_char_t hcchar = {.value = channel->hcchar};
dwc2_channel_char_t hcchar_bm = channel->hcchar_bm;
const uint32_t hcint = channel->hcint;
channel->hcint = hcint; // clear interrupt
@ -1206,7 +1144,7 @@ static void handle_channel_irq(uint8_t rhport, bool in_isr) {
bool is_done = false;
if (is_dma) {
#if CFG_TUH_DWC2_DMA_ENABLE
if (hcchar.ep_dir == TUSB_DIR_OUT) {
if (hcchar_bm.ep_dir == TUSB_DIR_OUT) {
is_done = handle_channel_out_dma(dwc2, ch_id, hcint);
} else {
is_done = handle_channel_in_dma(dwc2, ch_id, hcint);
@ -1218,7 +1156,7 @@ static void handle_channel_irq(uint8_t rhport, bool in_isr) {
#endif
} else {
#if CFG_TUH_DWC2_SLAVE_ENABLE
if (hcchar.ep_dir == TUSB_DIR_OUT) {
if (hcchar_bm.ep_dir == TUSB_DIR_OUT) {
is_done = handle_channel_out_slave(dwc2, ch_id, hcint);
} else {
is_done = handle_channel_in_slave(dwc2, ch_id, hcint);
@ -1227,8 +1165,8 @@ static void handle_channel_irq(uint8_t rhport, bool in_isr) {
}
if (is_done) {
const uint8_t ep_addr = tu_edpt_addr(hcchar.ep_num, hcchar.ep_dir);
hcd_event_xfer_complete(hcchar.dev_addr, ep_addr, xfer->xferred_bytes, (xfer_result_t)xfer->result, in_isr);
const uint8_t ep_addr = tu_edpt_addr(hcchar_bm.ep_num, hcchar_bm.ep_dir);
hcd_event_xfer_complete(hcchar_bm.dev_addr, ep_addr, xfer->xferred_bytes, xfer->result, in_isr);
channel_dealloc(dwc2, ch_id);
}
}
@ -1239,7 +1177,6 @@ static void handle_channel_irq(uint8_t rhport, bool in_isr) {
static bool handle_sof_irq(uint8_t rhport, bool in_isr) {
(void) in_isr;
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
dwc2->gintsts = GINTSTS_SOF; // Clear the SOF interrupt flag
bool more_isr = false;
@ -1248,7 +1185,7 @@ static bool handle_sof_irq(uint8_t rhport, bool in_isr) {
for(uint8_t ep_id = 0; ep_id < CFG_TUH_DWC2_ENDPOINT_MAX; ep_id++) {
hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id];
if (edpt->hcchar_bm.enable && channel_is_periodic(edpt->hcchar) && edpt->uframe_countdown > 0) {
if (edpt->hcchar_bm.enable && edpt_is_periodic(edpt->hcchar_bm.ep_type) && edpt->uframe_countdown > 0) {
edpt->uframe_countdown -= tu_min32(ucount, edpt->uframe_countdown);
if (edpt->uframe_countdown == 0) {
if (!edpt_xfer_kickoff(dwc2, ep_id)) {
@ -1267,10 +1204,10 @@ static bool handle_sof_irq(uint8_t rhport, bool in_isr) {
static void port0_enable(dwc2_regs_t* dwc2, tusb_speed_t speed) {
uint32_t hcfg = dwc2->hcfg & ~HCFG_FSLS_PHYCLK_SEL;
const dwc2_gusbcfg_t gusbcfg = {.value = dwc2->gusbcfg};
const dwc2_gusbcfg_t gusbcfg_bm = dwc2->gusbcfg_bm;
uint32_t phy_clock;
if (gusbcfg.phy_sel) {
if (gusbcfg_bm.phy_sel) {
phy_clock = 48; // dedicated FS is 48Mhz
if (speed == TUSB_SPEED_LOW) {
hcfg |= HCFG_FSLS_PHYCLK_SEL_6MHZ;
@ -1278,11 +1215,11 @@ static void port0_enable(dwc2_regs_t* dwc2, tusb_speed_t speed) {
hcfg |= HCFG_FSLS_PHYCLK_SEL_48MHZ;
}
} else {
if (gusbcfg.ulpi_utmi_sel) {
if (gusbcfg_bm.ulpi_utmi_sel) {
phy_clock = 60; // ULPI 8-bit is 60Mhz
} else {
// UTMI+ 16-bit is 30Mhz, 8-bit is 60Mhz
phy_clock = gusbcfg.phy_if16 ? 30 : 60;
phy_clock = gusbcfg_bm.phy_if16 ? 30 : 60;
// Enable UTMI+ low power mode 48Mhz external clock if not highspeed
if (speed == TUSB_SPEED_HIGH) {
@ -1299,9 +1236,9 @@ static void port0_enable(dwc2_regs_t* dwc2, tusb_speed_t speed) {
uint32_t hfir = dwc2->hfir & ~HFIR_FRIVL_Msk;
if (speed == TUSB_SPEED_HIGH) {
hfir |= 125*phy_clock - 1; // The "- 1" is the correct value. The Synopsys databook was corrected in 3.30a
hfir |= 125*phy_clock;
} else {
hfir |= 1000*phy_clock - 1;
hfir |= 1000*phy_clock;
}
dwc2->hfir = hfir;
@ -1314,19 +1251,21 @@ static void port0_enable(dwc2_regs_t* dwc2, tusb_speed_t speed) {
*/
static void handle_hprt_irq(uint8_t rhport, bool in_isr) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
const dwc2_hprt_t hprt_bm = {.value = dwc2->hprt};
uint32_t hprt = hprt_bm.value & ~HPRT_W1_MASK;
uint32_t hprt = dwc2->hprt & ~HPRT_W1_MASK;
const dwc2_hprt_t hprt_bm = dwc2->hprt_bm;
if (hprt_bm.conn_detected) {
if (dwc2->hprt & HPRT_CONN_DETECT) {
// Port Connect Detect
hprt |= HPRT_CONN_DETECT;
if (hprt_bm.conn_status) {
hcd_event_device_attach(rhport, in_isr);
} else {
hcd_event_device_remove(rhport, in_isr);
}
}
if (hprt_bm.enable_change) {
if (dwc2->hprt & HPRT_ENABLE_CHANGE) {
// Port enable change
hprt |= HPRT_ENABLE_CHANGE;
@ -1385,15 +1324,6 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
handle_channel_irq(rhport, in_isr);
}
if (gintsts & GINTSTS_DISCINT) {
// Device disconnected
dwc2->gintsts = GINTSTS_DISCINT;
if (!(dwc2->hprt & HPRT_CONN_STATUS)) {
hcd_event_device_remove(rhport, in_isr);
}
}
#if CFG_TUH_DWC2_SLAVE_ENABLE
// RxFIFO non-empty interrupt handling
if (gintsts & GINTSTS_RXFLVL) {

View file

@ -142,9 +142,7 @@ void tusb_int_handler(uint8_t rhport, bool in_isr) {
uint8_t const* tu_desc_find(uint8_t const* desc, uint8_t const* end, uint8_t byte1) {
while (desc + 1 < end) {
if (desc[1] == byte1) {
return desc;
}
if (desc[1] == byte1) return desc;
desc += desc[DESC_OFFSET_LEN];
}
return NULL;
@ -152,9 +150,7 @@ uint8_t const* tu_desc_find(uint8_t const* desc, uint8_t const* end, uint8_t byt
uint8_t const* tu_desc_find2(uint8_t const* desc, uint8_t const* end, uint8_t byte1, uint8_t byte2) {
while (desc + 2 < end) {
if (desc[1] == byte1 && desc[2] == byte2) {
return desc;
}
if (desc[1] == byte1 && desc[2] == byte2) return desc;
desc += desc[DESC_OFFSET_LEN];
}
return NULL;
@ -162,9 +158,7 @@ uint8_t const* tu_desc_find2(uint8_t const* desc, uint8_t const* end, uint8_t by
uint8_t const* tu_desc_find3(uint8_t const* desc, uint8_t const* end, uint8_t byte1, uint8_t byte2, uint8_t byte3) {
while (desc + 3 < end) {
if (desc[1] == byte1 && desc[2] == byte2 && desc[3] == byte3) {
return desc;
}
if (desc[1] == byte1 && desc[2] == byte2 && desc[3] == byte3) return desc;
desc += desc[DESC_OFFSET_LEN];
}
return NULL;
@ -205,7 +199,7 @@ bool tu_edpt_release(tu_edpt_state_t* ep_state, osal_mutex_t mutex) {
return ret;
}
bool tu_edpt_validate(tusb_desc_endpoint_t const* desc_ep, tusb_speed_t speed, bool is_host) {
bool tu_edpt_validate(tusb_desc_endpoint_t const* desc_ep, tusb_speed_t speed) {
uint16_t const max_packet_size = tu_edpt_packet_size(desc_ep);
TU_LOG2(" Open EP %02X with Size = %u\r\n", desc_ep->bEndpointAddress, max_packet_size);
@ -221,17 +215,8 @@ bool tu_edpt_validate(tusb_desc_endpoint_t const* desc_ep, tusb_speed_t speed, b
// Bulk highspeed must be EXACTLY 512
TU_ASSERT(max_packet_size == 512);
} else {
// Bulk fullspeed can only be 8, 16, 32, 64
if (is_host && max_packet_size == 512) {
// HACK: while in host mode, some device incorrectly always report 512 regardless of link speed
// overwrite descriptor to force 64
TU_LOG1(" WARN: EP max packet size is 512 in fullspeed, force to 64\r\n");
tusb_desc_endpoint_t* hacked_ep = (tusb_desc_endpoint_t*) (uintptr_t) desc_ep;
hacked_ep->wMaxPacketSize = tu_htole16(64);
} else {
TU_ASSERT(max_packet_size == 8 || max_packet_size == 16 ||
max_packet_size == 32 || max_packet_size == 64);
}
// TODO Bulk fullspeed can only be 8, 16, 32, 64
TU_ASSERT(max_packet_size <= 64);
}
break;

View file

@ -59,10 +59,6 @@
#include "class/cdc/cdc_host.h"
#endif
#if CFG_TUH_MIDI
#include "class/midi/midi_host.h"
#endif
#if CFG_TUH_VENDOR
#include "class/vendor/vendor_host.h"
#endif

View file

@ -35,7 +35,7 @@
#define TUSB_VERSION_REVISION 0
#define TUSB_VERSION_NUMBER (TUSB_VERSION_MAJOR * 10000 + TUSB_VERSION_MINOR * 100 + TUSB_VERSION_REVISION)
#define TUSB_VERSION_STRING TU_XSTRING(TUSB_VERSION_MAJOR) "." TU_XSTRING(TUSB_VERSION_MINOR) "." TU_XSTRING(TUSB_VERSION_REVISION)
#define TUSB_VERSION_STRING TU_STRING(TUSB_VERSION_MAJOR) "." TU_STRING(TUSB_VERSION_MINOR) "." TU_STRING(TUSB_VERSION_REVISION)
//--------------------------------------------------------------------+
// Supported MCUs

View file

@ -1,28 +1,27 @@
from pathlib import Path
import argparse
import click
import sys
import shutil
def main():
@click.command()
@click.argument('dir', type=click.Path(), required=True)
def main(dir):
"""
This script takes a mandatory 'dir' argument, which is a path to pivot example to update for all DualRole's examples
"""
parser = argparse.ArgumentParser()
parser.add_argument('dpath', help='path to folder containing usbh_helper.h to copy from')
args = parser.parse_args()
dpath = args.dpath
sample_dir = Path(dpath)
sample_dir = Path(dir)
if not sample_dir.is_dir():
print(f"The specified dir '{dir}' does not exist or is not a valid dir.")
# add examples/DualRoles to the path
sample_dir = Path('examples/DualRole') / sample_dir
if not sample_dir.is_dir():
click.echo(f"The specified dir '{dir}' does not exist or is not a valid dir.")
sys.exit(1)
sample_file = sample_dir / 'usbh_helper.h'
f_list = sorted(Path('examples/DualRole').glob('**/usbh_helper.h'))
for f in f_list:
if f != sample_file:
print(f"Updating {f}")
click.echo(f"Updating {f}")
shutil.copy(sample_file, f)