Compare commits
36 commits
fix-esp32-
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
737dfb2115 | ||
|
|
766d8cca00 | ||
|
|
d3cc12070a | ||
|
|
86dfc05234 | ||
|
|
8f13cb8bca | ||
|
|
fb9763423c | ||
|
|
7b30ff9e14 | ||
|
|
0e054b598b | ||
|
|
5ab76b7931 | ||
|
|
028629e93e | ||
|
|
8c615b6519 | ||
|
|
d4071f5abe | ||
|
|
4daede3744 | ||
|
|
44053c45df | ||
|
|
eaa9bfdaa5 | ||
|
|
9806375c60 | ||
|
|
e896107698 | ||
|
|
805aa2d41a | ||
|
|
fee8a5e6bc | ||
|
|
59d4393012 | ||
|
|
60fb663caa | ||
|
|
cdc07c1d96 | ||
|
|
794ac7f34b | ||
|
|
9496740134 | ||
|
|
6fa11c4d5e | ||
|
|
f05bd099c3 | ||
|
|
951dba38f6 | ||
|
|
5618d9c054 | ||
|
|
46d2c6cb49 | ||
|
|
23426ed3f6 | ||
|
|
72cd81005b | ||
|
|
fafc824f6b | ||
|
|
6b772c0ac4 | ||
|
|
560a21bdec | ||
|
|
4b9a629a55 | ||
|
|
d522523fd1 |
64 changed files with 3216 additions and 1996 deletions
10
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
10
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
|
@ -46,7 +46,7 @@ body:
|
|||
- type: input
|
||||
attributes:
|
||||
label: TinyUSB Library version
|
||||
placeholder: "Release version or github latest"
|
||||
placeholder: "Release version or commit SHA"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
|
@ -100,3 +100,11 @@ 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
|
||||
|
|
|
|||
52
.github/workflows/githubci.yml
vendored
52
.github/workflows/githubci.yml
vendored
|
|
@ -38,7 +38,7 @@ jobs:
|
|||
run: bash ci/doxy_gen_and_deploy.sh
|
||||
|
||||
# ---------------------------------------
|
||||
# Main
|
||||
# build
|
||||
# ---------------------------------------
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
|
@ -83,53 +83,3 @@ 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
|
||||
|
|
|
|||
|
|
@ -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 120Mhz for bit-banging USB
|
||||
// Check for CPU frequency, must be multiple of 12 Mhz for bit-banging USB
|
||||
uint32_t cpu_hz = clock_get_hz(clk_sys);
|
||||
if (cpu_hz != 120000000UL && cpu_hz != 240000000UL) {
|
||||
if (cpu_hz % 12000000UL) {
|
||||
while (!Serial) {
|
||||
delay(10); // wait for native usb
|
||||
}
|
||||
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");
|
||||
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");
|
||||
while (1) {
|
||||
delay(1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 120Mhz for bit-banging USB
|
||||
// Check for CPU frequency, must be multiple of 12 Mhz for bit-banging USB
|
||||
uint32_t cpu_hz = clock_get_hz(clk_sys);
|
||||
if (cpu_hz != 120000000UL && cpu_hz != 240000000UL) {
|
||||
if (cpu_hz % 12000000UL) {
|
||||
while (!Serial) {
|
||||
delay(10); // wait for native usb
|
||||
}
|
||||
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");
|
||||
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");
|
||||
while (1) {
|
||||
delay(1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 120Mhz for bit-banging USB
|
||||
// Check for CPU frequency, must be multiple of 12 Mhz for bit-banging USB
|
||||
uint32_t cpu_hz = clock_get_hz(clk_sys);
|
||||
if (cpu_hz != 120000000UL && cpu_hz != 240000000UL) {
|
||||
if (cpu_hz % 12000000UL) {
|
||||
while (!Serial) {
|
||||
delay(10); // wait for native usb
|
||||
}
|
||||
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");
|
||||
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");
|
||||
while (1) {
|
||||
delay(1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 120Mhz for bit-banging USB
|
||||
// Check for CPU frequency, must be multiple of 12 Mhz for bit-banging USB
|
||||
uint32_t cpu_hz = clock_get_hz(clk_sys);
|
||||
if (cpu_hz != 120000000UL && cpu_hz != 240000000UL) {
|
||||
if (cpu_hz % 12000000UL) {
|
||||
while (!Serial) {
|
||||
delay(10); // wait for native usb
|
||||
}
|
||||
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");
|
||||
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");
|
||||
while (1) {
|
||||
delay(1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 120Mhz for bit-banging USB
|
||||
// Check for CPU frequency, must be multiple of 12 Mhz for bit-banging USB
|
||||
uint32_t cpu_hz = clock_get_hz(clk_sys);
|
||||
if (cpu_hz != 120000000UL && cpu_hz != 240000000UL) {
|
||||
if (cpu_hz % 12000000UL) {
|
||||
while (!Serial) {
|
||||
delay(10); // wait for native usb
|
||||
}
|
||||
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");
|
||||
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");
|
||||
while (1) {
|
||||
delay(1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 120Mhz for bit-banging USB
|
||||
// Check for CPU frequency, must be multiple of 12 Mhz for bit-banging USB
|
||||
uint32_t cpu_hz = clock_get_hz(clk_sys);
|
||||
if (cpu_hz != 120000000UL && cpu_hz != 240000000UL) {
|
||||
if (cpu_hz % 12000000UL) {
|
||||
while (!Serial) {
|
||||
delay(10); // wait for native usb
|
||||
}
|
||||
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");
|
||||
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");
|
||||
while (1) {
|
||||
delay(1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 120Mhz for bit-banging USB
|
||||
// Check for CPU frequency, must be multiple of 12 Mhz for bit-banging USB
|
||||
uint32_t cpu_hz = clock_get_hz(clk_sys);
|
||||
if (cpu_hz != 120000000UL && cpu_hz != 240000000UL) {
|
||||
if (cpu_hz % 12000000UL) {
|
||||
while (!Serial) {
|
||||
delay(10); // wait for native usb
|
||||
}
|
||||
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");
|
||||
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");
|
||||
while (1) {
|
||||
delay(1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 120Mhz for bit-banging USB
|
||||
// Check for CPU frequency, must be multiple of 12 Mhz for bit-banging USB
|
||||
uint32_t cpu_hz = clock_get_hz(clk_sys);
|
||||
if (cpu_hz != 120000000UL && cpu_hz != 240000000UL) {
|
||||
if (cpu_hz % 12000000UL) {
|
||||
while (!Serial) {
|
||||
delay(10); // wait for native usb
|
||||
}
|
||||
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");
|
||||
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");
|
||||
while (1) {
|
||||
delay(1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,38 +33,62 @@
|
|||
|
||||
// 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_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
|
||||
|
||||
// SDCard File system
|
||||
#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
|
||||
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
|
||||
|
|
@ -93,14 +117,11 @@ 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
|
||||
|
|
@ -113,58 +134,44 @@ void setup() {
|
|||
usb_msc.setUnitReady(1, true);
|
||||
#endif
|
||||
|
||||
// while ( !Serial ) delay(10); // wait for native usb
|
||||
// while ( !Serial ) delay(10); // wait for native usb
|
||||
Serial.println("Adafruit TinyUSB Mass Storage External Flash + SD Card example");
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
bool init_sdcard(void)
|
||||
{
|
||||
bool init_sdcard(void) {
|
||||
Serial.print("Init SDCard ... ");
|
||||
|
||||
if ( !sd.begin(SDCARD_CS, SD_SCK_MHZ(50)) )
|
||||
{
|
||||
Serial.print("Failed ");
|
||||
sd.errorPrint("sd.begin() failed");
|
||||
|
||||
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?");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t block_count;
|
||||
|
||||
#if SD_FAT_VERSION >= 20000
|
||||
block_count = sd.card()->sectorCount();
|
||||
#else
|
||||
block_count = sd.card()->cardSize();
|
||||
#endif
|
||||
|
||||
|
||||
uint32_t block_count = sd.card()->sectorCount();
|
||||
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.print((block_count / (1024 * 1024)) * 512);
|
||||
Serial.println(" MB");
|
||||
|
||||
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('/');
|
||||
}
|
||||
|
|
@ -173,46 +180,8 @@ void print_rootdir(File32* rdir)
|
|||
}
|
||||
}
|
||||
|
||||
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
|
||||
void loop() {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -220,16 +189,8 @@ void loop()
|
|||
// SD Card callbacks
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
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
|
||||
|
||||
int32_t sdcard_read_cb (uint32_t lba, void* buffer, uint32_t bufsize) {
|
||||
bool rc = sd.card()->readSectors(lba, (uint8_t*) buffer, bufsize/512);
|
||||
return rc ? bufsize : -1;
|
||||
}
|
||||
|
||||
|
|
@ -237,35 +198,18 @@ 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
|
||||
|
||||
#if SD_FAT_VERSION >= 20000
|
||||
rc = sd.card()->writeSectors(lba, buffer, bufsize/512);
|
||||
#else
|
||||
rc = sd.card()->writeBlocks(lba, buffer, bufsize/512);
|
||||
#endif
|
||||
|
||||
bool rc = sd.card()->writeSectors(lba, buffer, bufsize/512);
|
||||
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)
|
||||
{
|
||||
#if SD_FAT_VERSION >= 20000
|
||||
void sdcard_flush_cb (void) {
|
||||
sd.card()->syncDevice();
|
||||
#else
|
||||
sd.card()->syncBlocks();
|
||||
#endif
|
||||
|
||||
// clear file system's cache to force refresh
|
||||
sd.cacheClear();
|
||||
|
||||
sd_changed = true;
|
||||
sd.cacheClear(); // clear file system's cache to force refresh
|
||||
|
||||
#ifdef LED_BUILTIN
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
|
|
@ -275,24 +219,18 @@ 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) == HIGH )
|
||||
{
|
||||
if (digitalRead(SDCARD_DETECT) == SDCARD_DETECT_ACTIVE) {
|
||||
// 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
|
||||
|
|
@ -327,12 +265,6 @@ 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
|
||||
|
|
|
|||
|
|
@ -10,34 +10,69 @@
|
|||
*********************************************************************/
|
||||
|
||||
/* This example expose SD card as mass storage using
|
||||
* SdFat Library
|
||||
* - SdFat https://github.com/adafruit/SdFat
|
||||
*/
|
||||
|
||||
#include "SPI.h"
|
||||
#include "SdFat_Adafruit_Fork.h"
|
||||
#include "Adafruit_TinyUSB.h"
|
||||
|
||||
const int chipSelect = 10;
|
||||
//--------------------------------------------------------------------+
|
||||
// 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
|
||||
|
||||
// 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");
|
||||
|
|
@ -60,23 +95,17 @@ 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(chipSelect, SD_SCK_MHZ(50)) ) {
|
||||
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 chipSelect 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 SDCARD_CS or SDIO 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);
|
||||
|
||||
|
|
@ -85,51 +114,17 @@ void setup() {
|
|||
|
||||
// MSC is ready for read/write
|
||||
usb_msc.setUnitReady(true);
|
||||
|
||||
fs_changed = true; // to print contents initially
|
||||
}
|
||||
|
||||
void loop() {
|
||||
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
|
||||
}
|
||||
// noting to do
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
#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
|
||||
|
||||
bool rc = sd.card()->readSectors(lba, (uint8_t*) buffer, bufsize/512);
|
||||
return rc ? bufsize : -1;
|
||||
}
|
||||
|
||||
|
|
@ -137,34 +132,18 @@ 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
|
||||
|
||||
#if SD_FAT_VERSION >= 20000
|
||||
rc = sd.card()->writeSectors(lba, buffer, bufsize/512);
|
||||
#else
|
||||
rc = sd.card()->writeBlocks(lba, buffer, bufsize/512);
|
||||
#endif
|
||||
|
||||
bool rc = sd.card()->writeSectors(lba, buffer, bufsize/512);
|
||||
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();
|
||||
#else
|
||||
sd.card()->syncBlocks();
|
||||
#endif
|
||||
|
||||
// clear file system's cache to force refresh
|
||||
sd.cacheClear();
|
||||
|
||||
fs_changed = true;
|
||||
sd.cacheClear(); // clear file system's cache to force refresh
|
||||
|
||||
#ifdef LED_BUILTIN
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"name": "Adafruit TinyUSB Library",
|
||||
"keywords": "usb, arduino, tinyusb",
|
||||
"description": "Arduino library for TinyUSB",
|
||||
"version": "3.4.3",
|
||||
"version": "3.7.1",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/adafruit/Adafruit_TinyUSB_Arduino.git"
|
||||
|
|
@ -10,8 +10,7 @@
|
|||
"frameworks": "*",
|
||||
"platforms": "*",
|
||||
"build": {
|
||||
"libArchive": false,
|
||||
"flags": "-DUSE_TINYUSB"
|
||||
"libArchive": false
|
||||
},
|
||||
"authors":
|
||||
[
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
name=Adafruit TinyUSB Library
|
||||
version=3.4.3
|
||||
version=3.7.1
|
||||
author=Adafruit
|
||||
maintainer=Adafruit <info@adafruit.com>
|
||||
sentence=TinyUSB library for Arduino
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
100),
|
||||
USB_CONFIG_POWER),
|
||||
};
|
||||
|
||||
memcpy(_desc_cfg_buffer, dev_cfg, sizeof(tusb_desc_configuration_t));
|
||||
|
|
|
|||
|
|
@ -80,6 +80,21 @@ 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);
|
||||
|
||||
|
|
|
|||
|
|
@ -661,6 +661,7 @@ 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
|
||||
|
|
|
|||
|
|
@ -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 uint8_t ctrl_buf_1[CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ];
|
||||
|
||||
#if CFG_TUD_AUDIO > 1
|
||||
tu_static uint8_t ctrl_buf_2[CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ];
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_AUDIO > 2
|
||||
tu_static uint8_t ctrl_buf_3[CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ];
|
||||
#endif
|
||||
tu_static CFG_TUD_MEM_SECTION struct {
|
||||
TUD_EPBUF_DEF(buf1, 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);
|
||||
#endif
|
||||
#if CFG_TUD_AUDIO > 2
|
||||
TUD_EPBUF_DEF(buf3, 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,10 +628,6 @@ 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
|
||||
|
|
@ -1227,18 +1223,18 @@ void audiod_init(void) {
|
|||
// Initialize control buffers
|
||||
switch (i) {
|
||||
case 0:
|
||||
audio->ctrl_buf = ctrl_buf_1;
|
||||
audio->ctrl_buf = ctrl_buf.buf1;
|
||||
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_2;
|
||||
audio->ctrl_buf = ctrl_buf.buf2;
|
||||
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_3;
|
||||
audio->ctrl_buf = ctrl_buf.buf3;
|
||||
audio->ctrl_buf_sz = CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ;
|
||||
break;
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -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_fifo_t _cdcd_fifo_cfg;
|
||||
static tud_cdc_configure_t _cdcd_cfg = TUD_CDC_CONFIGURE_DEFAULT();
|
||||
|
||||
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_fifo(const tud_cdc_configure_fifo_t* cfg) {
|
||||
TU_VERIFY(cfg);
|
||||
_cdcd_fifo_cfg = (*cfg);
|
||||
bool tud_cdc_configure(const tud_cdc_configure_t* driver_cfg) {
|
||||
TU_VERIFY(driver_cfg);
|
||||
_cdcd_cfg = *driver_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 ret = tu_fifo_write_n(&p_cdc->tx_ff, buffer, (uint16_t) TU_MIN(bufsize, UINT16_MAX));
|
||||
uint16_t wr_count = 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 ret;
|
||||
return wr_count;
|
||||
}
|
||||
|
||||
uint32_t tud_cdc_n_write_flush(uint8_t itf) {
|
||||
|
|
@ -233,8 +233,6 @@ 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];
|
||||
|
||||
|
|
@ -249,10 +247,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);
|
||||
|
||||
// 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);
|
||||
// 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);
|
||||
|
||||
#if OSAL_MUTEX_REQUIRED
|
||||
osal_mutex_t mutex_rd = osal_mutex_create(&p_cdc->rx_ff_mutex);
|
||||
|
|
@ -294,13 +292,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_fifo_cfg.rx_persistent) {
|
||||
if (!_cdcd_cfg.rx_persistent) {
|
||||
tu_fifo_clear(&p_cdc->rx_ff);
|
||||
}
|
||||
if (!_cdcd_fifo_cfg.tx_persistent) {
|
||||
if (!_cdcd_cfg.tx_persistent) {
|
||||
tu_fifo_clear(&p_cdc->tx_ff);
|
||||
}
|
||||
tu_fifo_set_overwritable(&p_cdc->tx_ff, true);
|
||||
tu_fifo_set_overwritable(&p_cdc->tx_ff, _cdcd_cfg.tx_overwritabe_if_not_connected);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -414,8 +412,12 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_requ
|
|||
|
||||
p_cdc->line_state = (uint8_t) request->wValue;
|
||||
|
||||
// Disable fifo overwriting if DTR bit is set
|
||||
tu_fifo_set_overwritable(&p_cdc->tx_ff, !dtr);
|
||||
// If enabled: fifo overwriting is disabled if DTR bit is set and vice versa
|
||||
if (_cdcd_cfg.tx_overwritabe_if_not_connected) {
|
||||
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);
|
||||
|
||||
|
|
@ -496,7 +498,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)) {
|
||||
usbd_edpt_xfer(rhport, p_cdc->ep_in, NULL, 0);
|
||||
TU_ASSERT(usbd_edpt_xfer(rhport, p_cdc->ep_in, NULL, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,14 +48,24 @@
|
|||
//--------------------------------------------------------------------+
|
||||
// Driver Configuration
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
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;
|
||||
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;
|
||||
|
||||
// Configure CDC FIFOs behavior
|
||||
bool tud_cdc_configure_fifo(tud_cdc_configure_fifo_t const* cfg);
|
||||
#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
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application API (Multiple Ports) i.e. CFG_TUD_CDC > 1
|
||||
|
|
|
|||
|
|
@ -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_ASSERT(event == XFER_RESULT_SUCCESS);
|
||||
TU_VERIFY(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) {
|
||||
if (p_cdc->serial_drid == SERIAL_DRIVER_FTDI && xferred_bytes > 2) {
|
||||
// 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_offset(&p_cdc->stream.rx, xferred_bytes, 2);
|
||||
tu_edpt_stream_read_xfer_complete_with_buf(&p_cdc->stream.rx, p_cdc->stream.rx.ep_buf+2, xferred_bytes-2);
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
|
|
|
|||
|
|
@ -49,22 +49,22 @@
|
|||
|
||||
// RX FIFO size
|
||||
#ifndef CFG_TUH_CDC_RX_BUFSIZE
|
||||
#define CFG_TUH_CDC_RX_BUFSIZE USBH_EPSIZE_BULK_MAX
|
||||
#define CFG_TUH_CDC_RX_BUFSIZE TUH_EPSIZE_BULK_MPS
|
||||
#endif
|
||||
|
||||
// RX Endpoint size
|
||||
#ifndef CFG_TUH_CDC_RX_EPSIZE
|
||||
#define CFG_TUH_CDC_RX_EPSIZE USBH_EPSIZE_BULK_MAX
|
||||
#define CFG_TUH_CDC_RX_EPSIZE TUH_EPSIZE_BULK_MPS
|
||||
#endif
|
||||
|
||||
// TX FIFO size
|
||||
#ifndef CFG_TUH_CDC_TX_BUFSIZE
|
||||
#define CFG_TUH_CDC_TX_BUFSIZE USBH_EPSIZE_BULK_MAX
|
||||
#define CFG_TUH_CDC_TX_BUFSIZE TUH_EPSIZE_BULK_MPS
|
||||
#endif
|
||||
|
||||
// TX Endpoint size
|
||||
#ifndef CFG_TUH_CDC_TX_EPSIZE
|
||||
#define CFG_TUH_CDC_TX_EPSIZE USBH_EPSIZE_BULK_MAX
|
||||
#define CFG_TUH_CDC_TX_EPSIZE TUH_EPSIZE_BULK_MPS
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
|
|
|
|||
|
|
@ -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(" Get Report callback (%u, %u)\r\n", daddr, idx);
|
||||
TU_LOG_DRV(" [idx=%u] Get Report callback\r\n", idx);
|
||||
TU_LOG3_MEM(epbuf->epin, xferred_bytes, 2);
|
||||
tuh_hid_report_received_cb(daddr, idx, epbuf->epin, (uint16_t) xferred_bytes);
|
||||
} else {
|
||||
|
|
@ -461,7 +461,9 @@ 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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,13 +24,8 @@
|
|||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
/** \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__
|
||||
#ifndef TUSB_MIDI_H_
|
||||
#define TUSB_MIDI_H_
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
|
||||
|
|
@ -39,30 +34,31 @@
|
|||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Class Specific Descriptor
|
||||
// Constants
|
||||
//--------------------------------------------------------------------+
|
||||
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
|
||||
typedef enum {
|
||||
MIDI_CS_ENDPOINT_GENERAL = 0x01,
|
||||
MIDI_CS_ENDPOINT_GENERAL_2_0 = 0x02,
|
||||
} 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
|
||||
|
|
@ -82,8 +78,7 @@ 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,
|
||||
|
|
@ -106,80 +101,54 @@ 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
|
||||
{
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||
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 ;
|
||||
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 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
|
||||
{
|
||||
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 iJack ; ///< string 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 bJackType; ///< Embedded or External
|
||||
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 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
|
||||
/// MIDI Out Jack Descriptor with multiple input pins
|
||||
#define midi_desc_out_jack_n_t(input_num) \
|
||||
struct TU_ATTR_PACKED { \
|
||||
uint8_t bLength ; \
|
||||
uint8_t bDescriptorType ; \
|
||||
uint8_t bDescriptorSubType ; \
|
||||
uint8_t bJackType ; \
|
||||
uint8_t bJackID ; \
|
||||
uint8_t bNrInputPins ; \
|
||||
struct TU_ATTR_PACKED { \
|
||||
uint8_t baSourceID; \
|
||||
uint8_t baSourcePin; \
|
||||
} pins[input_num]; \
|
||||
uint8_t iJack ; \
|
||||
struct TU_ATTR_PACKED { \
|
||||
uint8_t bLength; \
|
||||
uint8_t bDescriptorType; \
|
||||
uint8_t bDescriptorSubType; \
|
||||
uint8_t bJackType; \
|
||||
uint8_t bJackID; \
|
||||
uint8_t bNrInputPins; \
|
||||
struct TU_ATTR_PACKED { \
|
||||
uint8_t baSourceID; \
|
||||
uint8_t baSourcePin; \
|
||||
} input[input_num]; \
|
||||
uint8_t iJack; \
|
||||
}
|
||||
|
||||
/// 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;
|
||||
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 with multiple pins
|
||||
#define midi_desc_element_n_t(input_num) \
|
||||
|
|
@ -201,12 +170,32 @@ typedef struct TU_ATTR_PACKED
|
|||
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
|
||||
|
||||
/** @} */
|
||||
|
|
|
|||
|
|
@ -39,13 +39,6 @@
|
|||
//--------------------------------------------------------------------+
|
||||
// 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;
|
||||
|
|
@ -54,8 +47,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.
|
||||
midid_stream_t stream_write;
|
||||
midid_stream_t stream_read;
|
||||
midi_driver_stream_t stream_write;
|
||||
midi_driver_stream_t stream_read;
|
||||
|
||||
/*------------- From this point, data is not cleared by bus reset -------------*/
|
||||
// FIFO
|
||||
|
|
@ -122,7 +115,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 midid_stream_t* stream = &midi->stream_read;
|
||||
const midi_driver_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);
|
||||
|
|
@ -136,7 +129,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];
|
||||
midid_stream_t* stream = &midi->stream_read;
|
||||
midi_driver_stream_t* stream = &midi->stream_read;
|
||||
|
||||
uint32_t total_read = 0;
|
||||
while( bufsize )
|
||||
|
|
@ -241,7 +234,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);
|
||||
|
||||
midid_stream_t* stream = &midi->stream_write;
|
||||
midi_driver_stream_t* stream = &midi->stream_write;
|
||||
|
||||
uint32_t i = 0;
|
||||
while ( (i < bufsize) && (tu_fifo_remaining(&midi->tx_ff) >= 4) )
|
||||
|
|
@ -429,21 +422,21 @@ void midid_reset(uint8_t rhport)
|
|||
}
|
||||
}
|
||||
|
||||
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, 0);
|
||||
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;
|
||||
|
||||
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 )
|
||||
{
|
||||
drv_len += tu_desc_len(p_desc);
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
// 1st Interface is Audio Control v1 (optional)
|
||||
if (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);
|
||||
// Skip Class Specific descriptors
|
||||
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
|
||||
|
|
|
|||
622
src/class/midi/midi_host.c
Normal file
622
src/class/midi/midi_host.c
Normal file
|
|
@ -0,0 +1,622 @@
|
|||
/*
|
||||
* 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
|
||||
193
src/class/midi/midi_host.h
Normal file
193
src/class/midi/midi_host.h
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* 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
|
||||
|
|
@ -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) {
|
||||
|
|
@ -441,7 +441,8 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
|
|||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
}
|
||||
|
||||
case MSC_STAGE_DATA:
|
||||
TU_LOG_DRV(" SCSI Data [Lun%u]\r\n", p_cbw->lun);
|
||||
|
|
|
|||
40
src/class/vendor/vendor_device.c
vendored
40
src/class/vendor/vendor_device.c
vendored
|
|
@ -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,26 +210,26 @@ 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;
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
const tusb_desc_endpoint_t* desc_ep = (const tusb_desc_endpoint_t*) p_desc;
|
||||
TU_ASSERT(usbd_edpt_open(rhport, desc_ep));
|
||||
|
||||
const tusb_desc_endpoint_t* desc_ep = (const tusb_desc_endpoint_t*) p_desc;
|
||||
TU_ASSERT(usbd_edpt_open(rhport, desc_ep));
|
||||
found_ep++;
|
||||
|
||||
if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
|
||||
tu_edpt_stream_open(&p_vendor->tx.stream, desc_ep);
|
||||
tud_vendor_n_write_flush((uint8_t)(p_vendor - _vendord_itf));
|
||||
} else {
|
||||
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
|
||||
// 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);
|
||||
|
|
|
|||
|
|
@ -120,6 +120,22 @@ 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) {
|
||||
|
|
@ -181,8 +197,7 @@ 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
|
||||
static inline uint8_t tu_log2(uint32_t value)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_log2(uint32_t value) {
|
||||
uint8_t result = 0;
|
||||
while (value >>= 1) { result++; }
|
||||
return result;
|
||||
|
|
@ -193,8 +208,7 @@ static inline uint8_t tu_log2(uint32_t value)
|
|||
// return sizeof(uint32_t) * CHAR_BIT - __builtin_clz(x) - 1;
|
||||
//}
|
||||
|
||||
static inline bool tu_is_power_of_two(uint32_t value)
|
||||
{
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tu_is_power_of_two(uint32_t value) {
|
||||
return (value != 0) && ((value & (value - 1)) == 0);
|
||||
}
|
||||
|
||||
|
|
@ -205,27 +219,23 @@ 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_unaligned_uint32_t const* ua32 = (tu_unaligned_uint32_t const*) 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_unaligned_uint32_t* ua32 = (tu_unaligned_uint32_t*) mem;
|
||||
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_unaligned_uint16_t const* ua16 = (tu_unaligned_uint16_t const*) 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_unaligned_uint16_t* ua16 = (tu_unaligned_uint16_t*) mem;
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -108,15 +108,13 @@ 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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -916,8 +916,11 @@ 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)
|
||||
{
|
||||
bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable) {
|
||||
if (f->overwritable == overwritable) {
|
||||
return true;
|
||||
}
|
||||
|
||||
_ff_lock(f->mutex_wr);
|
||||
_ff_lock(f->mutex_rd);
|
||||
|
||||
|
|
|
|||
|
|
@ -369,6 +369,10 @@
|
|||
#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
|
||||
|
|
@ -381,6 +385,8 @@
|
|||
|
||||
#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
|
||||
|
|
@ -410,6 +416,7 @@
|
|||
#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")))
|
||||
|
||||
|
|
|
|||
|
|
@ -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 tu_edpt_validate(tusb_desc_endpoint_t const * desc_ep, tusb_speed_t speed, bool is_host);
|
||||
|
||||
// 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);
|
||||
|
||||
// Must be called in the transfer complete callback
|
||||
// Complete read transfer by writing EP -> FIFO. 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
|
|||
}
|
||||
}
|
||||
|
||||
// Same as tu_edpt_stream_read_xfer_complete but skip the first n bytes
|
||||
// Complete read transfer with provided buffer
|
||||
TU_ATTR_ALWAYS_INLINE static inline
|
||||
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));
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -281,7 +281,8 @@ typedef enum {
|
|||
// TODO remove
|
||||
enum {
|
||||
DESC_OFFSET_LEN = 0,
|
||||
DESC_OFFSET_TYPE = 1
|
||||
DESC_OFFSET_TYPE = 1,
|
||||
DESC_OFFSET_SUBTYPE = 2
|
||||
};
|
||||
|
||||
enum {
|
||||
|
|
@ -570,16 +571,27 @@ 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 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 subtype
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_subtype(void const* desc) {
|
||||
return ((uint8_t const*) desc)[DESC_OFFSET_SUBTYPE];
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
|||
|
|
@ -342,15 +342,16 @@ 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;
|
||||
|
||||
// Event queue
|
||||
// usbd_int_set() is used as mutex in OS NONE config
|
||||
static OSAL_SPINLOCK_DEF(_usbd_spin, usbd_int_set);
|
||||
|
||||
// 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);
|
||||
tu_static osal_queue_t _usbd_q;
|
||||
static osal_queue_t _usbd_q;
|
||||
|
||||
// Mutex for claiming endpoint
|
||||
#if OSAL_MUTEX_REQUIRED
|
||||
tu_static osal_mutex_def_t _ubsd_mutexdef;
|
||||
tu_static osal_mutex_t _usbd_mutex;
|
||||
static osal_mutex_def_t _ubsd_mutexdef;
|
||||
static osal_mutex_t _usbd_mutex;
|
||||
#else
|
||||
#define _usbd_mutex NULL
|
||||
#endif
|
||||
|
|
@ -468,7 +469,7 @@ bool tud_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
|
|||
TU_ASSERT(rh_init);
|
||||
|
||||
TU_LOG_USBD("USBD init on controller %u, speed = %s\r\n", rhport,
|
||||
rh_init->speed == TUSB_SPEED_HIGH ? "High" : "Full");
|
||||
rh_init->speed == TUSB_SPEED_HIGH ? "High" : "Full");
|
||||
TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(usbd_device_t));
|
||||
TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(dcd_event_t));
|
||||
TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(tu_fifo_t));
|
||||
|
|
@ -477,6 +478,8 @@ 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);
|
||||
|
|
@ -563,8 +566,7 @@ static void usbd_reset(uint8_t rhport) {
|
|||
}
|
||||
|
||||
bool tud_task_event_ready(void) {
|
||||
// Skip if stack is not initialized
|
||||
if (!tud_inited()) return false;
|
||||
TU_VERIFY(tud_inited()); // Skip if stack is not initialized
|
||||
return !osal_queue_empty(_usbd_q);
|
||||
}
|
||||
|
||||
|
|
@ -686,7 +688,9 @@ 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:
|
||||
|
|
@ -703,7 +707,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
|
||||
}
|
||||
}
|
||||
|
|
@ -1035,7 +1039,14 @@ static bool process_set_config(uint8_t rhport, uint8_t cfg_num)
|
|||
#endif
|
||||
|
||||
#if CFG_TUD_MIDI
|
||||
if ( driver->open == midid_open ) assoc_itf_count = 2;
|
||||
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;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_BTH && CFG_TUD_BTH_ISO_ALT_COUNT
|
||||
|
|
@ -1240,17 +1251,21 @@ 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)
|
||||
{
|
||||
|
|
@ -1295,7 +1310,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));
|
||||
TU_ASSERT(tu_edpt_validate(desc_ep, (tusb_speed_t) _usbd_dev.speed, false));
|
||||
|
||||
return dcd_edpt_open(rhport, desc_ep);
|
||||
}
|
||||
|
|
@ -1496,7 +1511,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));
|
||||
TU_ASSERT(tu_edpt_validate(desc_ep, (tusb_speed_t) _usbd_dev.speed, false));
|
||||
|
||||
_usbd_dev.ep_status[epnum][dir].stalled = 0;
|
||||
_usbd_dev.ep_status[epnum][dir].busy = 0;
|
||||
|
|
|
|||
|
|
@ -68,6 +68,8 @@ 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
|
||||
|
|
|
|||
|
|
@ -90,13 +90,6 @@ 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
|
||||
//--------------------------------------------------------------------+
|
||||
|
|
@ -163,8 +156,12 @@ 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);
|
||||
|
||||
|
|
@ -182,13 +179,6 @@ 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);
|
||||
|
||||
|
|
@ -235,4 +225,4 @@ void hcd_event_xfer_complete(uint8_t dev_addr, uint8_t ep_addr, uint32_t xferred
|
|||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_HCD_H_ */
|
||||
#endif
|
||||
|
|
|
|||
230
src/host/hub.c
230
src/host/hub.c
|
|
@ -57,9 +57,11 @@ 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];
|
||||
}
|
||||
|
|
@ -142,10 +144,23 @@ bool hub_port_set_feature(uint8_t hub_addr, uint8_t hub_port, uint8_t feature,
|
|||
};
|
||||
|
||||
TU_LOG_DRV("HUB Set Feature: %s, addr = %u port = %u\r\n", _hub_feature_str[feature], hub_addr, hub_port);
|
||||
TU_ASSERT( tuh_control_xfer(&xfer) );
|
||||
TU_ASSERT(tuh_control_xfer(&xfer));
|
||||
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 = {
|
||||
|
|
@ -169,8 +184,25 @@ 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) );
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -238,10 +270,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 dev_addr, uint8_t itf_num) {
|
||||
hub_interface_t* p_hub = get_hub_itf(dev_addr);
|
||||
bool hub_set_config(uint8_t daddr, uint8_t itf_num) {
|
||||
hub_interface_t* p_hub = get_hub_itf(daddr);
|
||||
TU_ASSERT(itf_num == p_hub->itf_num);
|
||||
hub_epbuf_t* p_epbuf = get_hub_epbuf(dev_addr);
|
||||
hub_epbuf_t* p_epbuf = get_hub_epbuf(daddr);
|
||||
|
||||
// Get Hub Descriptor
|
||||
tusb_control_request_t const request = {
|
||||
|
|
@ -257,7 +289,7 @@ bool hub_set_config(uint8_t dev_addr, uint8_t itf_num) {
|
|||
};
|
||||
|
||||
tuh_xfer_t xfer = {
|
||||
.daddr = dev_addr,
|
||||
.daddr = daddr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = p_epbuf->ctrl_buf,
|
||||
|
|
@ -312,11 +344,15 @@ static void config_port_power_complete (tuh_xfer_t* xfer) {
|
|||
//--------------------------------------------------------------------+
|
||||
// Connection Changes
|
||||
//--------------------------------------------------------------------+
|
||||
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);
|
||||
enum {
|
||||
STATE_IDLE = 0,
|
||||
STATE_HUB_STATUS,
|
||||
STATE_CLEAR_CHANGE,
|
||||
STATE_CHECK_CONN,
|
||||
STATE_COMPLETE
|
||||
};
|
||||
|
||||
static void process_new_status(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) {
|
||||
|
|
@ -337,12 +373,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, get_status_complete, 0);
|
||||
processed = hub_get_status(daddr, p_epbuf->ctrl_buf, process_new_status, STATE_HUB_STATUS);
|
||||
} 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, p_epbuf->ctrl_buf, port_get_status_complete, 0);
|
||||
processed = hub_port_get_status(daddr, port, NULL, process_new_status, STATE_CLEAR_CHANGE);
|
||||
break; // after completely processed one port, we will re-queue the status poll and handle next one
|
||||
}
|
||||
}
|
||||
|
|
@ -358,117 +394,85 @@ bool hub_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t
|
|||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
bool processed = false; // true if new status is processed
|
||||
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, 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, port_clear_feature_complete_stub, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!processed) {
|
||||
TU_ASSERT(hub_edpt_status_xfer(daddr), );
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
static void process_new_status(tuh_xfer_t* xfer) {
|
||||
const uint8_t daddr = xfer->daddr;
|
||||
|
||||
if (xfer->result != XFER_RESULT_SUCCESS) {
|
||||
TU_ASSERT(hub_edpt_status_xfer(daddr), );
|
||||
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 uint8_t port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
|
||||
const uintptr_t state = xfer->user_data;
|
||||
bool processed = false; // true if new status is processed
|
||||
|
||||
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
|
||||
switch (state) {
|
||||
case STATE_HUB_STATUS: {
|
||||
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);
|
||||
} 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);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return;
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
if (!processed) {
|
||||
TU_ASSERT(hub_edpt_status_xfer(daddr),);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -170,9 +170,13 @@ 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);
|
||||
|
||||
|
|
@ -188,7 +192,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
|
||||
// Get Hub status (port = 0)
|
||||
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);
|
||||
|
|
@ -205,7 +209,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 dev_addr, uint8_t itf_num);
|
||||
bool hub_set_config (uint8_t daddr, 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);
|
||||
|
||||
|
|
|
|||
1093
src/host/usbh.c
1093
src/host/usbh.c
File diff suppressed because it is too large
Load diff
|
|
@ -33,14 +33,20 @@
|
|||
|
||||
#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
|
||||
|
|
@ -73,6 +79,17 @@ 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,
|
||||
|
|
@ -96,6 +113,15 @@ 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);
|
||||
|
||||
|
|
@ -146,8 +172,7 @@ 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);
|
||||
}
|
||||
|
||||
|
|
@ -162,6 +187,8 @@ 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
|
||||
|
|
@ -188,19 +215,20 @@ 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
|
||||
//--------------------------------------------------------------------+
|
||||
|
|
@ -218,10 +246,17 @@ 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
|
||||
|
|
|
|||
|
|
@ -41,10 +41,6 @@
|
|||
#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
|
||||
//--------------------------------------------------------------------+
|
||||
|
|
@ -67,7 +63,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 dev_addr);
|
||||
uint8_t usbh_get_rhport(uint8_t daddr);
|
||||
|
||||
uint8_t* usbh_get_enum_buf(void);
|
||||
|
||||
|
|
@ -75,6 +71,9 @@ 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
|
||||
//--------------------------------------------------------------------+
|
||||
|
|
|
|||
|
|
@ -75,6 +75,10 @@ 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);
|
||||
|
|
|
|||
|
|
@ -42,20 +42,20 @@ extern "C" {
|
|||
//--------------------------------------------------------------------+
|
||||
|
||||
#if configSUPPORT_STATIC_ALLOCATION
|
||||
typedef StaticSemaphore_t osal_semaphore_def_t;
|
||||
typedef StaticSemaphore_t osal_mutex_def_t;
|
||||
typedef StaticSemaphore_t osal_semaphore_def_t;
|
||||
typedef StaticSemaphore_t osal_mutex_def_t;
|
||||
#else
|
||||
// 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;
|
||||
|
||||
// not used therefore defined to the smallest possible type to save space
|
||||
typedef uint8_t osal_semaphore_def_t;
|
||||
typedef uint8_t osal_mutex_def_t;
|
||||
#endif
|
||||
|
||||
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,16 +83,14 @@ 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);
|
||||
|
||||
// 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;
|
||||
// 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; }
|
||||
|
||||
return ticks;
|
||||
}
|
||||
|
|
@ -101,10 +99,71 @@ 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);
|
||||
|
|
@ -120,19 +179,12 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_delete(osal_semaphore_t
|
|||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr) {
|
||||
if ( !in_isr ) {
|
||||
if (!in_isr) {
|
||||
return xSemaphoreGive(sem_hdl) != 0;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -148,7 +200,6 @@ 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);
|
||||
|
|
@ -174,7 +225,6 @@ 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;
|
||||
|
||||
|
|
@ -201,19 +251,12 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, v
|
|||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void const *data, bool in_isr) {
|
||||
if ( !in_isr ) {
|
||||
if (!in_isr) {
|
||||
return xQueueSendToBack(qhdl, data, OSAL_TIMEOUT_WAIT_FOREVER) != 0;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,32 @@ 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
|
||||
//--------------------------------------------------------------------+
|
||||
|
|
|
|||
|
|
@ -40,6 +40,33 @@ 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
|
||||
//--------------------------------------------------------------------+
|
||||
|
|
|
|||
|
|
@ -43,6 +43,27 @@ 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
|
||||
//--------------------------------------------------------------------+
|
||||
|
|
|
|||
|
|
@ -42,6 +42,32 @@ 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
|
||||
//--------------------------------------------------------------------+
|
||||
|
|
|
|||
|
|
@ -56,6 +56,25 @@ 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
|
||||
//--------------------------------------------------------------------+
|
||||
|
|
|
|||
|
|
@ -35,6 +35,35 @@ 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
|
||||
//--------------------------------------------------------------------+
|
||||
|
|
|
|||
|
|
@ -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,13 +233,7 @@ typedef struct {
|
|||
uint8_t hxfr;
|
||||
}sndfifo_owner;
|
||||
|
||||
#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
|
||||
bool busy_lock; // busy transferring
|
||||
|
||||
#if OSAL_MUTEX_REQUIRED
|
||||
OSAL_MUTEX_DEF(spi_mutexdef);
|
||||
|
|
@ -258,47 +252,6 @@ 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
|
||||
//--------------------------------------------------------------------+
|
||||
|
|
@ -374,7 +327,9 @@ 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);
|
||||
|
|
@ -420,7 +375,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;
|
||||
uint8_t const reg = RCVVFIFO_ADDR;
|
||||
const uint8_t reg = RCVVFIFO_ADDR;
|
||||
|
||||
max3421_spi_lock(rhport, in_isr);
|
||||
|
||||
|
|
@ -436,7 +391,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) {
|
||||
uint8_t const is_out = 1-ep_dir;
|
||||
const uint8_t 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)
|
||||
|
|
@ -531,10 +486,6 @@ 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
|
||||
|
|
@ -592,10 +543,6 @@ 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;
|
||||
}
|
||||
|
||||
|
|
@ -665,6 +612,9 @@ 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;
|
||||
|
|
@ -678,6 +628,21 @@ 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.
|
||||
|
|
@ -764,8 +729,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) {
|
||||
uint8_t const ep_num = tu_edpt_number(ep_addr);
|
||||
uint8_t const ep_dir = (uint8_t) tu_edpt_dir(ep_addr);
|
||||
const uint8_t ep_num = tu_edpt_number(ep_addr);
|
||||
const uint8_t ep_dir = (uint8_t) tu_edpt_dir(ep_addr);
|
||||
max3421_ep_t* ep = find_opened_ep(daddr, ep_num, ep_dir);
|
||||
TU_VERIFY(ep);
|
||||
|
||||
|
|
@ -781,8 +746,17 @@ 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 (!usb_xfer_test_and_set()) {
|
||||
if (has_xfer) {
|
||||
xact_generic(rhport, ep, true, false);
|
||||
}
|
||||
|
||||
|
|
@ -818,8 +792,17 @@ 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 (!usb_xfer_test_and_set()) {
|
||||
if (has_xfer) {
|
||||
xact_setup(rhport, ep, false);
|
||||
}
|
||||
|
||||
|
|
@ -885,8 +868,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) {
|
||||
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);
|
||||
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);
|
||||
|
||||
// save data toggle
|
||||
if (ep_dir) {
|
||||
|
|
@ -904,7 +887,9 @@ 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
|
||||
usb_xfer_clear();
|
||||
usbh_spin_lock(in_isr);
|
||||
_hcd_data.busy_lock = false;
|
||||
usbh_spin_unlock(in_isr);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -943,7 +928,9 @@ 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
|
||||
usb_xfer_clear();
|
||||
usbh_spin_lock(in_isr);
|
||||
_hcd_data.busy_lock = false;
|
||||
usbh_spin_unlock(in_isr);
|
||||
}
|
||||
return;
|
||||
|
||||
|
|
@ -1034,8 +1021,8 @@ 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;
|
||||
// print_hirq(hirq);
|
||||
if (!hirq) { return; }
|
||||
// print_hirq(hirq);
|
||||
|
||||
if (hirq & HIRQ_FRAME_IRQ) {
|
||||
_hcd_data.frame_count++;
|
||||
|
|
@ -1054,8 +1041,19 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
|
|||
}
|
||||
|
||||
// start usb transfer if not busy
|
||||
if (ep_retry != NULL && !usb_xfer_test_and_set()) {
|
||||
xact_generic(rhport, ep_retry, true, in_isr);
|
||||
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) {
|
||||
xact_generic(rhport, ep_retry, true, in_isr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
63
src/portable/analog/max3421/hcd_max3421.h
Normal file
63
src/portable/analog/max3421/hcd_max3421.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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
|
||||
|
|
@ -114,14 +114,19 @@ 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) {
|
||||
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);
|
||||
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);
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -514,7 +514,6 @@ 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;
|
||||
|
|
@ -535,6 +534,11 @@ 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;
|
||||
|
|
|
|||
|
|
@ -39,14 +39,9 @@
|
|||
#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
|
||||
//--------------------------------------------------------------------+
|
||||
|
|
@ -58,6 +53,7 @@ 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])
|
||||
|
||||
|
|
@ -79,6 +75,16 @@ 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
|
||||
//--------------------------------------------------------------------
|
||||
|
|
@ -102,7 +108,8 @@ 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
|
||||
return CFG_TUD_DWC2_DMA_ENABLE && dwc2->ghwcfg2_bm.arch == GHWCFG2_ARCH_INTERNAL_DMA;
|
||||
const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2};
|
||||
return CFG_TUD_DWC2_DMA_ENABLE && ghwcfg2.arch == GHWCFG2_ARCH_INTERNAL_DMA;
|
||||
}
|
||||
|
||||
static void dma_setup_prepare(uint8_t rhport) {
|
||||
|
|
@ -250,20 +257,15 @@ static void edpt_activate(uint8_t rhport, const tusb_desc_endpoint_t* p_endpoint
|
|||
xfer->interval = p_endpoint_desc->bInterval;
|
||||
|
||||
// Endpoint control
|
||||
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;
|
||||
dwc2_depctl_t depctl = {.value = 0};
|
||||
depctl.mps = xfer->max_size;
|
||||
depctl.active = 1;
|
||||
depctl.type = p_endpoint_desc->bmAttributes.xfer;
|
||||
if (p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS) {
|
||||
depctl.bm.set_data0_iso_even = 1;
|
||||
depctl.set_data0_iso_even = 1;
|
||||
}
|
||||
if (dir == TUSB_DIR_IN) {
|
||||
depctl.bm.tx_fifo_num = epnum;
|
||||
depctl.tx_fifo_num = epnum;
|
||||
}
|
||||
|
||||
dwc2_dep_t* dep = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][epnum];
|
||||
|
|
@ -321,6 +323,9 @@ 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);
|
||||
|
|
@ -343,31 +348,22 @@ 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.
|
||||
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;
|
||||
|
||||
dwc2_ep_tsize_t deptsiz = {.value = 0};
|
||||
deptsiz.xfer_size = total_bytes;
|
||||
deptsiz.packet_count = num_packets;
|
||||
dep->tsiz = deptsiz.value;
|
||||
|
||||
// control
|
||||
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);
|
||||
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;
|
||||
if (odd_now) {
|
||||
depctl.bm.set_data0_iso_even = 1;
|
||||
depctl.set_data0_iso_even = 1;
|
||||
} else {
|
||||
depctl.bm.set_data1_iso_odd = 1;
|
||||
depctl.set_data1_iso_odd = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -410,7 +406,8 @@ 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)
|
||||
if (dwc2->ghwcfg2_bm.hs_phy_type == GHWCFG2_HSPHY_ULPI) {
|
||||
const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2};
|
||||
if (ghwcfg2.hs_phy_type == GHWCFG2_HSPHY_ULPI) {
|
||||
dcfg |= DCFG_XCVRDLY;
|
||||
}
|
||||
} else {
|
||||
|
|
@ -539,6 +536,8 @@ 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
|
||||
|
|
@ -556,8 +555,9 @@ 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);
|
||||
}
|
||||
|
||||
bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
|
||||
|
|
@ -575,21 +575,31 @@ 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);
|
||||
xfer->buffer = buffer;
|
||||
xfer->ff = NULL;
|
||||
xfer->total_len = total_bytes;
|
||||
bool ret;
|
||||
|
||||
// EP0 can only handle one packet
|
||||
if (epnum == 0) {
|
||||
_dcd_data.ep0_pending[dir] = total_bytes;
|
||||
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;
|
||||
|
||||
// EP0 can only handle one packet
|
||||
if (epnum == 0) {
|
||||
_dcd_data.ep0_pending[dir] = total_bytes;
|
||||
}
|
||||
|
||||
// Schedule packets to be sent within interrupt
|
||||
edpt_schedule_packets(rhport, epnum, dir);
|
||||
ret = true;
|
||||
}
|
||||
|
||||
// Schedule packets to be sent within interrupt
|
||||
edpt_schedule_packets(rhport, epnum, dir);
|
||||
usbd_spin_unlock(false);
|
||||
|
||||
return true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// The number of bytes has to be given explicitly to allow more flexible control of how many
|
||||
|
|
@ -602,17 +612,27 @@ 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);
|
||||
xfer->buffer = NULL;
|
||||
xfer->ff = ff;
|
||||
xfer->total_len = total_bytes;
|
||||
bool ret;
|
||||
|
||||
// Schedule packets to be sent within interrupt
|
||||
// TODO xfer fifo may only available for slave mode
|
||||
edpt_schedule_packets(rhport, epnum, dir);
|
||||
usbd_spin_lock(false);
|
||||
|
||||
return true;
|
||||
if (xfer->max_size == 0) {
|
||||
ret = false; // Endpoint is closed
|
||||
} else {
|
||||
xfer->buffer = NULL;
|
||||
xfer->ff = ff;
|
||||
xfer->total_len = total_bytes;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
|
||||
|
|
@ -639,9 +659,10 @@ 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));
|
||||
|
||||
|
|
@ -671,7 +692,9 @@ static void handle_bus_reset(uint8_t rhport) {
|
|||
dfifo_device_init(rhport);
|
||||
|
||||
// 5. Reset device address
|
||||
dwc2->dcfg_bm.address = 0;
|
||||
dwc2_dcfg_t dcfg = {.value = dwc2->dcfg};
|
||||
dcfg.address = 0;
|
||||
dwc2->dcfg = dcfg.value;
|
||||
|
||||
// Fixed both control EP0 size to 64 bytes
|
||||
dwc2->epin[0].ctl &= ~(0x03 << DIEPCTL_MPSIZ_Pos);
|
||||
|
|
@ -691,8 +714,9 @@ 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 (dwc2->dsts_bm.enum_speed) {
|
||||
switch (dsts.enum_speed) {
|
||||
case DCFG_SPEED_HIGH:
|
||||
speed = TUSB_SPEED_HIGH;
|
||||
break;
|
||||
|
|
@ -737,12 +761,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_bm = dwc2->grxstsp_bm;
|
||||
const uint8_t epnum = grxstsp_bm.ep_ch_num;
|
||||
const dwc2_grxstsp_t grxstsp = {.value = dwc2->grxstsp};
|
||||
const uint8_t epnum = grxstsp.ep_ch_num;
|
||||
|
||||
dwc2_dep_t* epout = &dwc2->epout[epnum];
|
||||
|
||||
switch (grxstsp_bm.packet_status) {
|
||||
switch (grxstsp.packet_status) {
|
||||
case GRXSTS_PKTSTS_GLOBAL_OUT_NAK:
|
||||
// Global OUT NAK: do nothing
|
||||
break;
|
||||
|
|
@ -764,7 +788,7 @@ static void handle_rxflvl_irq(uint8_t rhport) {
|
|||
|
||||
case GRXSTS_PKTSTS_RX_DATA: {
|
||||
// Out packet received
|
||||
const uint16_t byte_count = grxstsp_bm.byte_count;
|
||||
const uint16_t byte_count = grxstsp.byte_count;
|
||||
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
|
||||
|
||||
if (byte_count) {
|
||||
|
|
@ -778,7 +802,8 @@ static void handle_rxflvl_irq(uint8_t rhport) {
|
|||
|
||||
// short packet, minus remaining bytes (xfer_size)
|
||||
if (byte_count < xfer->max_size) {
|
||||
xfer->total_len -= epout->tsiz_bm.xfer_size;
|
||||
const dwc2_ep_tsize_t tsiz = {.value = epout->tsiz};
|
||||
xfer->total_len -= tsiz.xfer_size;
|
||||
if (epnum == 0) {
|
||||
xfer->total_len -= _dcd_data.ep0_pending[TUSB_DIR_OUT];
|
||||
_dcd_data.ep0_pending[TUSB_DIR_OUT] = 0;
|
||||
|
|
@ -840,11 +865,13 @@ 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))) {
|
||||
const uint16_t remain_packets = epin->tsiz_bm.packet_count;
|
||||
dwc2_ep_tsize_t tsiz = {.value = epin->tsiz};
|
||||
const uint16_t remain_packets = tsiz.packet_count;
|
||||
|
||||
// Process every single packet (only whole packets can be written to fifo)
|
||||
for (uint16_t i = 0; i < remain_packets; i++) {
|
||||
const uint16_t remain_bytes = (uint16_t) epin->tsiz_bm.xfer_size;
|
||||
tsiz.value = epin->tsiz;
|
||||
const uint16_t remain_bytes = (uint16_t) tsiz.xfer_size;
|
||||
const uint16_t xact_bytes = tu_min16(remain_bytes, xfer->max_size);
|
||||
|
||||
// Check if dtxfsts has enough space available
|
||||
|
|
@ -863,7 +890,8 @@ static void handle_epin_slave(uint8_t rhport, uint8_t epnum, dwc2_diepint_t diep
|
|||
}
|
||||
|
||||
// Turn off TXFE if all bytes are written.
|
||||
if (epin->tsiz_bm.xfer_size == 0) {
|
||||
tsiz.value = epin->tsiz;
|
||||
if (tsiz.xfer_size == 0) {
|
||||
dwc2->diepempmsk &= ~(1 << epnum);
|
||||
}
|
||||
}
|
||||
|
|
@ -894,7 +922,8 @@ 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 uint16_t remain = epout->tsiz_bm.xfer_size;
|
||||
const dwc2_ep_tsize_t tsiz = {.value = epout->tsiz};
|
||||
const uint16_t remain = tsiz.xfer_size;
|
||||
xfer->total_len -= remain;
|
||||
|
||||
// this is ZLP, so prepare EP0 for next setup
|
||||
|
|
@ -930,7 +959,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];
|
||||
|
||||
|
|
@ -983,14 +1012,16 @@ 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) {
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
|
||||
#if CFG_TUH_ENABLED
|
||||
#include "host/hcd.h"
|
||||
#include "host/usbh.h"
|
||||
#endif
|
||||
|
||||
#include "dwc2_common.h"
|
||||
|
|
@ -88,11 +89,13 @@ 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 (dwc2->ghwcfg2_bm.hs_phy_type == GHWCFG2_HSPHY_ULPI) {
|
||||
if (ghwcfg2.hs_phy_type == GHWCFG2_HSPHY_ULPI) {
|
||||
TU_LOG(DWC2_COMMON_DEBUG, "Highspeed ULPI PHY init\r\n");
|
||||
|
||||
// Select ULPI PHY (external)
|
||||
|
|
@ -116,7 +119,7 @@ static void phy_hs_init(dwc2_regs_t* dwc2) {
|
|||
gusbcfg &= ~GUSBCFG_ULPI_UTMI_SEL;
|
||||
|
||||
// Set 16-bit interface if supported
|
||||
if (dwc2->ghwcfg4_bm.phy_data_width) {
|
||||
if (ghwcfg4.phy_data_width) {
|
||||
gusbcfg |= GUSBCFG_PHYIF16; // 16 bit
|
||||
} else {
|
||||
gusbcfg &= ~GUSBCFG_PHYIF16; // 8 bit
|
||||
|
|
@ -127,7 +130,7 @@ static void phy_hs_init(dwc2_regs_t* dwc2) {
|
|||
dwc2->gusbcfg = gusbcfg;
|
||||
|
||||
// mcu specific phy init
|
||||
dwc2_phy_init(dwc2, dwc2->ghwcfg2_bm.hs_phy_type);
|
||||
dwc2_phy_init(dwc2, ghwcfg2.hs_phy_type);
|
||||
|
||||
// Reset core after selecting PHY
|
||||
reset_core(dwc2);
|
||||
|
|
@ -136,11 +139,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 |= (dwc2->ghwcfg4_bm.phy_data_width ? 5u : 9u) << GUSBCFG_TRDT_Pos;
|
||||
gusbcfg |= (ghwcfg4.phy_data_width ? 5u : 9u) << GUSBCFG_TRDT_Pos;
|
||||
dwc2->gusbcfg = gusbcfg;
|
||||
|
||||
// MCU specific PHY update post reset
|
||||
dwc2_phy_update(dwc2, dwc2->ghwcfg2_bm.hs_phy_type);
|
||||
dwc2_phy_update(dwc2, ghwcfg2.hs_phy_type);
|
||||
}
|
||||
|
||||
static bool check_dwc2(dwc2_regs_t* dwc2) {
|
||||
|
|
@ -171,7 +174,6 @@ 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;
|
||||
|
|
@ -183,7 +185,8 @@ bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, tusb_role_t role) {
|
|||
}
|
||||
#endif
|
||||
|
||||
return dwc2->ghwcfg2_bm.hs_phy_type != GHWCFG2_HSPHY_NOT_SUPPORTED;
|
||||
const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2};
|
||||
return ghwcfg2.hs_phy_type != GHWCFG2_HSPHY_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
/* dwc2 has several PHYs option
|
||||
|
|
|
|||
|
|
@ -55,8 +55,8 @@ static const dwc2_controller_t _dwc2_controller[] = {
|
|||
// On ESP32 for consistency we associate
|
||||
// - Port0 to OTG_FS, and Port1 to OTG_HS
|
||||
static const dwc2_controller_t _dwc2_controller[] = {
|
||||
{ .reg_base = DWC2_FS_REG_BASE, .irqnum = ETS_USB_OTG11_CH0_INTR_SOURCE, .ep_count = 7, .ep_in_count = 5, .ep_fifo_size = 1024 },
|
||||
{ .reg_base = DWC2_HS_REG_BASE, .irqnum = ETS_USB_OTG_INTR_SOURCE, .ep_count = 16, .ep_in_count = 8, .ep_fifo_size = 4096 }
|
||||
{ .reg_base = DWC2_FS_REG_BASE, .irqnum = ETS_USB_OTG11_CH0_INTR_SOURCE, .ep_count = 7, .ep_in_count = 5, .ep_fifo_size = 1024 },
|
||||
{ .reg_base = DWC2_HS_REG_BASE, .irqnum = ETS_USB_OTG_INTR_SOURCE, .ep_count = 16, .ep_in_count = 8, .ep_fifo_size = 4096 }
|
||||
};
|
||||
#endif
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -36,6 +36,7 @@
|
|||
#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
|
||||
|
|
@ -44,8 +45,6 @@
|
|||
#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,9 +76,9 @@ typedef struct {
|
|||
|
||||
struct TU_ATTR_PACKED {
|
||||
uint32_t uframe_interval : 18; // micro-frame interval
|
||||
uint32_t speed : 2;
|
||||
uint32_t next_pid : 2;
|
||||
uint32_t do_ping : 1;
|
||||
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 : 9;
|
||||
};
|
||||
|
||||
|
|
@ -97,7 +96,6 @@ 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;
|
||||
|
||||
|
|
@ -116,9 +114,15 @@ 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;
|
||||
switch(dwc2->hprt_bm.speed) {
|
||||
const dwc2_hprt_t hprt = {.value = dwc2->hprt};
|
||||
switch(hprt.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;
|
||||
|
|
@ -133,7 +137,8 @@ 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
|
||||
return CFG_TUH_DWC2_DMA_ENABLE && dwc2->ghwcfg2_bm.arch == GHWCFG2_ARCH_INTERNAL_DMA;
|
||||
const dwc2_ghwcfg2_t ghwcfg2 = {.value = dwc2->ghwcfg2};
|
||||
return CFG_TUH_DWC2_DMA_ENABLE && ghwcfg2.arch == GHWCFG2_ARCH_INTERNAL_DMA;
|
||||
}
|
||||
|
||||
#if CFG_TUH_MEM_DCACHE_ENABLE
|
||||
|
|
@ -155,7 +160,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) {
|
||||
|
|
@ -168,15 +173,18 @@ 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 edpt_is_periodic(uint8_t ep_type) {
|
||||
return ep_type == HCCHAR_EPTYPE_INTERRUPT || ep_type == HCCHAR_EPTYPE_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 uint8_t req_queue_avail(const dwc2_regs_t* dwc2, bool is_period) {
|
||||
if (is_period) {
|
||||
return dwc2->hptxsts_bm.req_queue_available;
|
||||
const dwc2_hptxsts_t hptxsts = {.value = dwc2->hptxsts};
|
||||
return hptxsts.req_queue_available;
|
||||
} else {
|
||||
return dwc2->hnptxsts_bm.req_queue_available;
|
||||
const dwc2_hnptxsts_t hnptxsts = {.value = dwc2->hnptxsts};
|
||||
return hnptxsts.req_queue_available;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -188,7 +196,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, edpt_is_periodic(channel->hcchar_bm.ep_type)));
|
||||
TU_ASSERT(req_queue_avail(dwc2, channel_is_periodic(channel->hcchar)));
|
||||
channel->hcintmsk |= HCINT_HALTED;
|
||||
channel->hcchar |= HCCHAR_CHDIS | HCCHAR_CHENA; // must set both CHDIS and CHENA
|
||||
return true;
|
||||
|
|
@ -196,18 +204,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, edpt_is_periodic(channel->hcchar_bm.ep_type)));
|
||||
TU_ASSERT(req_queue_avail(dwc2, channel_is_periodic(channel->hcchar)));
|
||||
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_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)) {
|
||||
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)) {
|
||||
return ch_id;
|
||||
}
|
||||
}
|
||||
|
|
@ -304,12 +312,13 @@ 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 -= dwc2->ghwcfg2_bm.num_host_ch;
|
||||
dfifo_top -= ghwcfg2.num_host_ch;
|
||||
}
|
||||
|
||||
// fixed allocation for now, improve later:
|
||||
|
|
@ -319,7 +328,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) + dwc2->ghwcfg2_bm.num_host_ch;
|
||||
uint16_t rxfsiz = 2 * (ptx_largest + 2) + ghwcfg2.num_host_ch;
|
||||
TU_ASSERT(dfifo_top >= (nptxfsiz + rxfsiz),);
|
||||
uint16_t ptxfsiz = dfifo_top - (nptxfsiz + rxfsiz);
|
||||
|
||||
|
|
@ -381,7 +390,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;
|
||||
dwc2->gintmsk |= GINTSTS_OTGINT | GINTSTS_CONIDSTSCHNG | GINTSTS_HPRTINT | GINTSTS_HCINT | GINTSTS_DISCINT;
|
||||
|
||||
// NPTX can hold at least 2 packet, change interrupt level to half-empty
|
||||
uint32_t gahbcfg = dwc2->gahbcfg & ~GAHBCFG_TX_FIFO_EPMTY_LVL;
|
||||
|
|
@ -461,8 +470,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);
|
||||
|
||||
hcd_devtree_info_t devtree_info;
|
||||
hcd_devtree_get_info(dev_addr, &devtree_info);
|
||||
tuh_bus_info_t bus_info;
|
||||
tuh_bus_info_get(dev_addr, &bus_info);
|
||||
|
||||
// find a free endpoint
|
||||
const uint8_t ep_id = edpt_alloc();
|
||||
|
|
@ -473,7 +482,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 = (devtree_info.speed == TUSB_SPEED_LOW) ? 1 : 0;
|
||||
hcchar_bm->low_speed_dev = (bus_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;
|
||||
|
|
@ -482,21 +491,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 = devtree_info.hub_port;
|
||||
hcsplt_bm->hub_addr = devtree_info.hub_addr;
|
||||
hcsplt_bm->hub_port = bus_info.hub_port;
|
||||
hcsplt_bm->hub_addr = bus_info.hub_addr;
|
||||
hcsplt_bm->xact_pos = 0;
|
||||
hcsplt_bm->split_compl = 0;
|
||||
hcsplt_bm->split_en = (rh_speed == TUSB_SPEED_HIGH && devtree_info.speed != TUSB_SPEED_HIGH) ? 1 : 0;
|
||||
hcsplt_bm->split_en = (rh_speed == TUSB_SPEED_HIGH && bus_info.speed != TUSB_SPEED_HIGH) ? 1 : 0;
|
||||
|
||||
edpt->speed = devtree_info.speed;
|
||||
edpt->speed = bus_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 (devtree_info.speed == TUSB_SPEED_FULL) {
|
||||
if (bus_info.speed == TUSB_SPEED_FULL) {
|
||||
edpt->uframe_interval <<= 3;
|
||||
}
|
||||
} else if (desc_ep->bmAttributes.xfer == TUSB_XFER_INTERRUPT) {
|
||||
if (devtree_info.speed == TUSB_SPEED_HIGH) {
|
||||
if (bus_info.speed == TUSB_SPEED_HIGH) {
|
||||
edpt->uframe_interval = 1 << (desc_ep->bInterval - 1);
|
||||
} else {
|
||||
edpt->uframe_interval = desc_ep->bInterval << 3;
|
||||
|
|
@ -506,13 +515,19 @@ 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];
|
||||
dwc2_channel_t* channel = &dwc2->channel[ch_id];
|
||||
const dwc2_channel_t* channel = &dwc2->channel[ch_id];
|
||||
hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id];
|
||||
|
||||
edpt->next_pid = channel->hctsiz_bm.pid; // save PID
|
||||
const dwc2_channel_tsize_t hctsiz = {.value = channel->hctsiz};
|
||||
edpt->next_pid = hctsiz.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.
|
||||
|
|
@ -520,9 +535,10 @@ 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 = 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;
|
||||
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;
|
||||
|
||||
xfer->fifo_bytes = 0;
|
||||
xfer->xferred_bytes += actual_bytes;
|
||||
|
|
@ -535,7 +551,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 = edpt_is_periodic(hcchar_bm->ep_type);
|
||||
bool const is_period = channel_is_periodic(edpt->hcchar);
|
||||
|
||||
// clear previous state
|
||||
xfer->fifo_bytes = 0;
|
||||
|
|
@ -548,13 +564,16 @@ 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);
|
||||
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 &&
|
||||
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 &&
|
||||
edpt->next_pid != HCTSIZ_PID_SETUP && hcchar_bm->ep_dir == TUSB_DIR_OUT) {
|
||||
hctsiz |= HCTSIZ_DOPING;
|
||||
hctsiz.do_ping = 1;
|
||||
}
|
||||
channel->hctsiz = hctsiz;
|
||||
edpt->do_ping = 0;
|
||||
channel->hctsiz = hctsiz.value;
|
||||
edpt->next_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) {
|
||||
|
|
@ -585,7 +604,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) {
|
||||
if (edpt->hcsplt_bm.split_en || hctsiz.do_ping) {
|
||||
hcintmsk |= HCINT_ACK;
|
||||
}
|
||||
}
|
||||
|
|
@ -694,18 +713,23 @@ 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];
|
||||
dwc2_channel_t* channel = &dwc2->channel[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};
|
||||
|
||||
if (edpt_is_periodic(channel->hcchar_bm.ep_type)){
|
||||
if (channel_is_periodic(hcchar.value)){
|
||||
const dwc2_channel_split_t hcsplt = {.value = channel->hcsplt};
|
||||
// retry immediately for periodic split NYET if we haven't reach max retry
|
||||
if (channel->hcsplt_bm.split_en && channel->hcsplt_bm.split_compl && (hcint & HCINT_NYET || xfer->halted_nyet)) {
|
||||
if (hcsplt.split_en && hcsplt.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) {
|
||||
channel->hcchar_bm.odd_frame = 1 - (dwc2->hfnum & 1); // transfer on next frame
|
||||
hcchar.odd_frame = 1 - (dwc2->hfnum & 1); // transfer on next frame
|
||||
channel->hcchar = hcchar.value;
|
||||
channel_send_in_token(dwc2, channel);
|
||||
return;
|
||||
} else {
|
||||
|
|
@ -714,18 +738,24 @@ static void channel_xfer_in_retry(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci
|
|||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
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;
|
||||
}
|
||||
// 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
|
||||
|
|
@ -756,13 +786,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_bm = dwc2->grxstsp_bm;
|
||||
const uint8_t ch_id = grxstsp_bm.ep_ch_num;
|
||||
const dwc2_grxstsp_t grxstsp = {.value= dwc2->grxstsp};
|
||||
const uint8_t ch_id = grxstsp.ep_ch_num;
|
||||
|
||||
switch (grxstsp_bm.packet_status) {
|
||||
switch (grxstsp.packet_status) {
|
||||
case GRXSTS_PKTSTS_RX_DATA: {
|
||||
// In packet received, pop this entry --> ACK interrupt
|
||||
const uint16_t byte_count = grxstsp_bm.byte_count;
|
||||
const uint16_t byte_count = grxstsp.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];
|
||||
|
|
@ -796,25 +826,26 @@ 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)
|
||||
volatile dwc2_hptxsts_t* txsts_bm = (volatile dwc2_hptxsts_t*) (is_periodic ? &dwc2->hptxsts : &dwc2->hnptxsts);
|
||||
const dwc2_hptxsts_t txsts = {.value = (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) && (channel->hcchar_bm.ep_dir == TUSB_DIR_OUT)) {
|
||||
if (!(channel->hcintmsk & HCINT_HALTED) && (hcchar.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 uint16_t remain_packets = channel->hctsiz_bm.packet_count;
|
||||
const dwc2_channel_tsize_t hctsiz = {.value = channel->hctsiz};
|
||||
const uint16_t remain_packets = hctsiz.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, channel->hcchar_bm.ep_size);
|
||||
const uint16_t xact_bytes = tu_min16(remain_bytes, hcchar.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_bm->fifo_available << 2)) || (txsts_bm->req_queue_available == 0)) {
|
||||
if ((xact_bytes > (txsts.fifo_available << 2)) || (txsts.req_queue_available == 0)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -831,23 +862,26 @@ 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 (channel->hcsplt_bm.split_en) {
|
||||
// if (hcsplt.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, channel->hcchar_bm.ep_num, hcint);
|
||||
// TU_LOG1("Frame %u, ch %u: ep %u, hcint 0x%04lX ", dwc2->hfnum_bm.num, ch_id, hcsplt.ep_num, hcint);
|
||||
// print_hcint(hcint);
|
||||
// }
|
||||
|
||||
if (hcint & HCINT_XFER_COMPLETE) {
|
||||
if (edpt->hcchar_bm.ep_num != 0) {
|
||||
edpt->next_pid = channel->hctsiz_bm.pid; // save pid (already toggled)
|
||||
edpt->next_pid = hctsiz.pid; // save pid (already toggled)
|
||||
}
|
||||
|
||||
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) {
|
||||
const uint16_t remain_packets = hctsiz.packet_count;
|
||||
if (hcsplt.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
|
||||
channel->hcsplt_bm.split_compl = 0;
|
||||
hcsplt.split_compl = 0;
|
||||
channel->hcsplt = hcsplt.value;
|
||||
} else {
|
||||
xfer->result = XFER_RESULT_SUCCESS;
|
||||
}
|
||||
|
|
@ -866,43 +900,44 @@ 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
|
||||
channel->hcsplt_bm.split_compl = 1;
|
||||
hcsplt.split_compl = 1;
|
||||
channel->hcsplt = hcsplt.value;
|
||||
xfer->halted_nyet = 1;
|
||||
channel_disable(dwc2, channel);
|
||||
} else if (hcint & HCINT_NAK) {
|
||||
// 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
|
||||
// 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;
|
||||
}
|
||||
|
||||
channel_disable(dwc2, channel);
|
||||
} else if (hcint & HCINT_ACK) {
|
||||
xfer->err_count = 0;
|
||||
|
||||
if (channel->hcsplt_bm.split_en) {
|
||||
if (!channel->hcsplt_bm.split_compl) {
|
||||
if (hcsplt.split_en) {
|
||||
if (!hcsplt.split_compl) {
|
||||
// start split is ACK --> do complete split
|
||||
channel->hcintmsk |= HCINT_NYET;
|
||||
channel->hcsplt_bm.split_compl = 1;
|
||||
hcsplt.split_compl = 1;
|
||||
channel->hcsplt = hcsplt.value;
|
||||
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 = channel->hctsiz_bm.packet_count;
|
||||
const uint16_t remain_packets = hctsiz.packet_count;
|
||||
if (remain_packets) {
|
||||
// still more packet to receive, also reset to start split
|
||||
channel->hcsplt_bm.split_compl = 0;
|
||||
hcsplt.split_compl = 0;
|
||||
channel->hcsplt = hcsplt.value;
|
||||
channel_send_in_token(dwc2, channel);
|
||||
}
|
||||
}
|
||||
} else if (hcint & HCINT_HALTED) {
|
||||
channel->hcintmsk &= ~HCINT_HALTED;
|
||||
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) {
|
||||
if (xfer->result != XFER_RESULT_INVALID) {
|
||||
is_done = true;
|
||||
} else if (xfer->err_count == HCD_XFER_ERROR_MAX) {
|
||||
xfer->result = XFER_RESULT_FAILED;
|
||||
|
|
@ -922,23 +957,29 @@ 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 (channel->hcsplt_bm.split_en) {
|
||||
if (hcsplt.split_en) {
|
||||
// retry complete split
|
||||
channel->hcsplt_bm.split_compl = 1;
|
||||
hcsplt.split_compl = 1;
|
||||
channel->hcsplt = hcsplt.value;
|
||||
channel->hcchar |= HCCHAR_CHENA;
|
||||
} else {
|
||||
edpt->do_ping = 1;
|
||||
edpt->next_do_ping = 1;
|
||||
channel_xfer_out_wrapup(dwc2, ch_id);
|
||||
channel_disable(dwc2, channel);
|
||||
}
|
||||
|
|
@ -951,7 +992,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->do_ping = 1;
|
||||
edpt->next_do_ping = 1;
|
||||
xfer->err_count = 0;
|
||||
}
|
||||
} else if (hcint & HCINT_HALTED) {
|
||||
|
|
@ -968,9 +1009,17 @@ 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 (channel->hcsplt_bm.split_en && !channel->hcsplt_bm.split_compl) {
|
||||
// start split is ACK --> do complete split
|
||||
channel->hcsplt_bm.split_compl = 1;
|
||||
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
|
||||
channel->hcchar |= HCCHAR_CHENA;
|
||||
}
|
||||
}
|
||||
|
|
@ -989,6 +1038,9 @@ 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;
|
||||
|
||||
|
|
@ -996,8 +1048,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) channel->hctsiz_bm.xfer_size;
|
||||
const uint16_t remain_packets = channel->hctsiz_bm.packet_count;
|
||||
const uint16_t remain_bytes = (uint16_t) hctsiz.xfer_size;
|
||||
const uint16_t remain_packets = hctsiz.packet_count;
|
||||
const uint16_t actual_len = edpt->buflen - remain_bytes;
|
||||
xfer->xferred_bytes += actual_len;
|
||||
|
||||
|
|
@ -1007,13 +1059,14 @@ 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 (channel->hcsplt_bm.split_en && remain_packets && actual_len == edpt->hcchar_bm.ep_size) {
|
||||
} else if (hcsplt.split_en && remain_packets && actual_len == hcchar.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;
|
||||
|
||||
channel->hcsplt_bm.split_compl = 0;
|
||||
hcsplt.split_compl = 0;
|
||||
channel->hcsplt = hcsplt.value;
|
||||
channel_xfer_in_retry(dwc2, ch_id, hcint);
|
||||
} else {
|
||||
xfer->result = XFER_RESULT_SUCCESS;
|
||||
|
|
@ -1028,33 +1081,38 @@ 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;
|
||||
channel->hcsplt_bm.split_compl = 0;
|
||||
hcsplt.split_compl = 0;
|
||||
channel->hcsplt = hcsplt.value;
|
||||
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 (channel->hcsplt_bm.split_en) {
|
||||
if (hcsplt.split_en) {
|
||||
// split not yet mean hub has no data, retry complete split
|
||||
channel->hcsplt_bm.split_compl = 1;
|
||||
hcsplt.split_compl = 1;
|
||||
channel->hcsplt = hcsplt.value;
|
||||
channel_xfer_in_retry(dwc2, ch_id, hcint);
|
||||
}
|
||||
} else if (hcint & HCINT_ACK) {
|
||||
xfer->err_count = 0;
|
||||
channel->hcintmsk &= ~HCINT_ACK;
|
||||
if (channel->hcsplt_bm.split_en) {
|
||||
if (hcsplt.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)
|
||||
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
|
||||
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_send_in_token(dwc2, channel);
|
||||
}
|
||||
} else if (hcint & (HCINT_NAK | HCINT_DATATOGGLE_ERR)) {
|
||||
xfer->err_count = 0;
|
||||
channel->hcintmsk &= ~(HCINT_NAK | HCINT_DATATOGGLE_ERR);
|
||||
channel->hcsplt_bm.split_compl = 0; // restart with start-split
|
||||
hcsplt.split_compl = 0; // restart with start-split
|
||||
channel->hcsplt = hcsplt.value;
|
||||
channel_xfer_in_retry(dwc2, ch_id, hcint);
|
||||
} else if (hcint & HCINT_FARME_OVERRUN) {
|
||||
// retry start-split in next binterval
|
||||
|
|
@ -1069,6 +1127,8 @@ 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;
|
||||
|
||||
|
|
@ -1104,16 +1164,18 @@ static bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hc
|
|||
}
|
||||
}
|
||||
} else if (hcint & HCINT_NYET) {
|
||||
if (channel->hcsplt_bm.split_en && channel->hcsplt_bm.split_compl) {
|
||||
if (hcsplt.split_en && hcsplt.split_compl) {
|
||||
// split not yet mean hub has no data, retry complete split
|
||||
channel->hcsplt_bm.split_compl = 1;
|
||||
hcsplt.split_compl = 1;
|
||||
channel->hcsplt = hcsplt.value;
|
||||
channel->hcchar |= HCCHAR_CHENA;
|
||||
}
|
||||
} else if (hcint & HCINT_ACK) {
|
||||
xfer->err_count = 0;
|
||||
if (channel->hcsplt_bm.split_en && !channel->hcsplt_bm.split_compl) {
|
||||
if (hcsplt.split_en && !hcsplt.split_compl) {
|
||||
// start split is ACK --> do complete split
|
||||
channel->hcsplt_bm.split_compl = 1;
|
||||
hcsplt.split_compl = 1;
|
||||
channel->hcsplt = hcsplt.value;
|
||||
channel->hcchar |= HCCHAR_CHENA;
|
||||
}
|
||||
}
|
||||
|
|
@ -1129,14 +1191,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_bm = channel->hcchar_bm;
|
||||
dwc2_channel_char_t hcchar = {.value = channel->hcchar};
|
||||
|
||||
const uint32_t hcint = channel->hcint;
|
||||
channel->hcint = hcint; // clear interrupt
|
||||
|
|
@ -1144,7 +1206,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_bm.ep_dir == TUSB_DIR_OUT) {
|
||||
if (hcchar.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);
|
||||
|
|
@ -1156,7 +1218,7 @@ static void handle_channel_irq(uint8_t rhport, bool in_isr) {
|
|||
#endif
|
||||
} else {
|
||||
#if CFG_TUH_DWC2_SLAVE_ENABLE
|
||||
if (hcchar_bm.ep_dir == TUSB_DIR_OUT) {
|
||||
if (hcchar.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);
|
||||
|
|
@ -1165,8 +1227,8 @@ static void handle_channel_irq(uint8_t rhport, bool in_isr) {
|
|||
}
|
||||
|
||||
if (is_done) {
|
||||
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);
|
||||
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);
|
||||
channel_dealloc(dwc2, ch_id);
|
||||
}
|
||||
}
|
||||
|
|
@ -1177,6 +1239,7 @@ 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;
|
||||
|
||||
|
|
@ -1185,7 +1248,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 && edpt_is_periodic(edpt->hcchar_bm.ep_type) && edpt->uframe_countdown > 0) {
|
||||
if (edpt->hcchar_bm.enable && channel_is_periodic(edpt->hcchar) && 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)) {
|
||||
|
|
@ -1204,10 +1267,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_bm = dwc2->gusbcfg_bm;
|
||||
const dwc2_gusbcfg_t gusbcfg = {.value = dwc2->gusbcfg};
|
||||
uint32_t phy_clock;
|
||||
|
||||
if (gusbcfg_bm.phy_sel) {
|
||||
if (gusbcfg.phy_sel) {
|
||||
phy_clock = 48; // dedicated FS is 48Mhz
|
||||
if (speed == TUSB_SPEED_LOW) {
|
||||
hcfg |= HCFG_FSLS_PHYCLK_SEL_6MHZ;
|
||||
|
|
@ -1215,11 +1278,11 @@ static void port0_enable(dwc2_regs_t* dwc2, tusb_speed_t speed) {
|
|||
hcfg |= HCFG_FSLS_PHYCLK_SEL_48MHZ;
|
||||
}
|
||||
} else {
|
||||
if (gusbcfg_bm.ulpi_utmi_sel) {
|
||||
if (gusbcfg.ulpi_utmi_sel) {
|
||||
phy_clock = 60; // ULPI 8-bit is 60Mhz
|
||||
} else {
|
||||
// UTMI+ 16-bit is 30Mhz, 8-bit is 60Mhz
|
||||
phy_clock = gusbcfg_bm.phy_if16 ? 30 : 60;
|
||||
phy_clock = gusbcfg.phy_if16 ? 30 : 60;
|
||||
|
||||
// Enable UTMI+ low power mode 48Mhz external clock if not highspeed
|
||||
if (speed == TUSB_SPEED_HIGH) {
|
||||
|
|
@ -1236,9 +1299,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;
|
||||
hfir |= 125*phy_clock - 1; // The "- 1" is the correct value. The Synopsys databook was corrected in 3.30a
|
||||
} else {
|
||||
hfir |= 1000*phy_clock;
|
||||
hfir |= 1000*phy_clock - 1;
|
||||
}
|
||||
|
||||
dwc2->hfir = hfir;
|
||||
|
|
@ -1251,21 +1314,19 @@ 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);
|
||||
uint32_t hprt = dwc2->hprt & ~HPRT_W1_MASK;
|
||||
const dwc2_hprt_t hprt_bm = dwc2->hprt_bm;
|
||||
const dwc2_hprt_t hprt_bm = {.value = dwc2->hprt};
|
||||
uint32_t hprt = hprt_bm.value & ~HPRT_W1_MASK;
|
||||
|
||||
if (dwc2->hprt & HPRT_CONN_DETECT) {
|
||||
if (hprt_bm.conn_detected) {
|
||||
// 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 (dwc2->hprt & HPRT_ENABLE_CHANGE) {
|
||||
if (hprt_bm.enable_change) {
|
||||
// Port enable change
|
||||
hprt |= HPRT_ENABLE_CHANGE;
|
||||
|
||||
|
|
@ -1324,6 +1385,15 @@ 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) {
|
||||
|
|
|
|||
27
src/tusb.c
27
src/tusb.c
|
|
@ -142,7 +142,9 @@ 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;
|
||||
|
|
@ -150,7 +152,9 @@ 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;
|
||||
|
|
@ -158,7 +162,9 @@ 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;
|
||||
|
|
@ -199,7 +205,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 tu_edpt_validate(tusb_desc_endpoint_t const* desc_ep, tusb_speed_t speed, bool is_host) {
|
||||
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);
|
||||
|
||||
|
|
@ -215,8 +221,17 @@ bool tu_edpt_validate(tusb_desc_endpoint_t const* desc_ep, tusb_speed_t speed) {
|
|||
// Bulk highspeed must be EXACTLY 512
|
||||
TU_ASSERT(max_packet_size == 512);
|
||||
} else {
|
||||
// TODO Bulk fullspeed can only be 8, 16, 32, 64
|
||||
TU_ASSERT(max_packet_size <= 64);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
|||
|
|
@ -59,6 +59,10 @@
|
|||
#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
|
||||
|
|
|
|||
|
|
@ -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_STRING(TUSB_VERSION_MAJOR) "." TU_STRING(TUSB_VERSION_MINOR) "." TU_STRING(TUSB_VERSION_REVISION)
|
||||
#define TUSB_VERSION_STRING TU_XSTRING(TUSB_VERSION_MAJOR) "." TU_XSTRING(TUSB_VERSION_MINOR) "." TU_XSTRING(TUSB_VERSION_REVISION)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Supported MCUs
|
||||
|
|
|
|||
|
|
@ -1,27 +1,28 @@
|
|||
from pathlib import Path
|
||||
import click
|
||||
import argparse
|
||||
import sys
|
||||
import shutil
|
||||
|
||||
@click.command()
|
||||
@click.argument('dir', type=click.Path(), required=True)
|
||||
def main(dir):
|
||||
def main():
|
||||
"""
|
||||
This script takes a mandatory 'dir' argument, which is a path to pivot example to update for all DualRole's examples
|
||||
"""
|
||||
sample_dir = Path(dir)
|
||||
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)
|
||||
if not sample_dir.is_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)
|
||||
print(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:
|
||||
click.echo(f"Updating {f}")
|
||||
print(f"Updating {f}")
|
||||
shutil.copy(sample_file, f)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue