From 6988a22f658c17038e218baae2d61103fbbaa7fd Mon Sep 17 00:00:00 2001 From: Ken Van Hoeylandt Date: Sat, 10 Oct 2026 00:59:46 +0200 Subject: [PATCH] Refactor usb host stack --- .../bigtreetech,panda-touch.dts | 18 +- Devices/m5stack-tab5/m5stack,tab5.dts | 24 +-- Devices/tulip-4r11/tulip-4r11.dts | 15 +- Documentation/ideas.md | 7 +- .../bindings/espressif,esp32-usbhost-hid.yaml | 9 - .../espressif,esp32-usbhost-midi.yaml | 9 - .../bindings/espressif,esp32-usbhost-msc.yaml | 9 - .../bindings/espressif,esp32-usbhost-uac.yaml | 9 - .../bindings/espressif,esp32-usbhost.yaml | 16 ++ .../tactility/bindings/esp32_usbhost.h | 4 - .../include/tactility/drivers/esp32_usbhost.h | 9 +- .../tactility/drivers/esp32_usbhost_task.h | 21 ++ .../source/drivers/usb/esp32_usbhost.cpp | 204 +++++++++++++++--- .../source/drivers/usb/esp32_usbhost_hid.cpp | 2 +- .../source/drivers/usb/esp32_usbhost_midi.cpp | 2 +- .../source/drivers/usb/esp32_usbhost_msc.cpp | 12 +- .../source/drivers/usb/esp32_usbhost_uac.cpp | 7 +- .../include/tactility/drivers/usb_host.h | 38 ++++ TactilityKernel/source/drivers/usb_host.cpp | 17 ++ TactilityKernel/source/symbols.c | 4 + 20 files changed, 302 insertions(+), 134 deletions(-) delete mode 100644 Platforms/platform-esp32/bindings/espressif,esp32-usbhost-hid.yaml delete mode 100644 Platforms/platform-esp32/bindings/espressif,esp32-usbhost-midi.yaml delete mode 100644 Platforms/platform-esp32/bindings/espressif,esp32-usbhost-msc.yaml delete mode 100644 Platforms/platform-esp32/bindings/espressif,esp32-usbhost-uac.yaml create mode 100644 Platforms/platform-esp32/private/tactility/drivers/esp32_usbhost_task.h create mode 100644 TactilityKernel/include/tactility/drivers/usb_host.h create mode 100644 TactilityKernel/source/drivers/usb_host.cpp diff --git a/Devices/btt-panda-touch/bigtreetech,panda-touch.dts b/Devices/btt-panda-touch/bigtreetech,panda-touch.dts index cd1002566..0893c8f9e 100644 --- a/Devices/btt-panda-touch/bigtreetech,panda-touch.dts +++ b/Devices/btt-panda-touch/bigtreetech,panda-touch.dts @@ -107,20 +107,8 @@ usbhost0 { compatible = "espressif,esp32-usbhost"; - - usbhosthid0 { - compatible = "espressif,esp32-usbhost-hid"; - //status = "disabled"; - }; - - usbhostmidi0 { - compatible = "espressif,esp32-usbhost-midi"; - //status = "disabled"; - }; - - usbhostmsc0 { - compatible = "espressif,esp32-usbhost-msc"; - //status = "disabled"; - }; + hid; + midi; + msc; }; }; diff --git a/Devices/m5stack-tab5/m5stack,tab5.dts b/Devices/m5stack-tab5/m5stack,tab5.dts index 470bbd632..ed6f03b40 100644 --- a/Devices/m5stack-tab5/m5stack,tab5.dts +++ b/Devices/m5stack-tab5/m5stack,tab5.dts @@ -198,26 +198,10 @@ usbhost0 { compatible = "espressif,esp32-usbhost"; peripheral-map = <0>; - - usbhosthid0 { - compatible = "espressif,esp32-usbhost-hid"; - }; - - usbhostmidi0 { - compatible = "espressif,esp32-usbhost-midi"; - }; - - usbhostmsc0 { - compatible = "espressif,esp32-usbhost-msc"; - }; - - // USB headphones/headsets (UAC 1.0, 48 kHz / 16-bit / stereo PCM output). - // The manager installs the class client; a dynamic "usb_uac0" codec device - // appears per connected headset and the audio-stream aggregate re-binds - // output to it. - usbhostaudio0 { - compatible = "espressif,esp32-usbhost-uac"; - }; + hid; + midi; + msc; + audio; }; usbdevice0 { diff --git a/Devices/tulip-4r11/tulip-4r11.dts b/Devices/tulip-4r11/tulip-4r11.dts index c5acd59f6..faa5715c8 100644 --- a/Devices/tulip-4r11/tulip-4r11.dts +++ b/Devices/tulip-4r11/tulip-4r11.dts @@ -177,17 +177,8 @@ usbhost0 { compatible = "espressif,esp32-usbhost"; - - usbhosthid0 { - compatible = "espressif,esp32-usbhost-hid"; - }; - - usbhostmidi0 { - compatible = "espressif,esp32-usbhost-midi"; - }; - - usbhostmsc0 { - compatible = "espressif,esp32-usbhost-msc"; - }; + hid; + midi; + msc; }; }; diff --git a/Documentation/ideas.md b/Documentation/ideas.md index 64d4a7740..fdd8aa643 100644 --- a/Documentation/ideas.md +++ b/Documentation/ideas.md @@ -7,12 +7,10 @@ ## Higher Priority -- Update to LVGL 9.6.x (ESP-IDF and POSIX) -- NimBLE looses pairing key after reboot -- Move USB host task stacks to SPIRAM when available: esp32_usbhost*.cpp -- Add bold fonts for e-ink readability improvement - Improve Setup: Add keyboard/keypad/encoder navigation explanation. e.g. encoder long press on t-lora pager to get out of textarea +- NimBLE looses pairing key after reboot +- Pick different font to improve monochrome screen readability (or change the weight). - Drivers/audio-codec-module is not a module. Move it somewhere else. Or make it an actual module. - Improve SPI kernel driver (implement read, write, transactions) - TCA9534 keyboards should use interrupts @@ -42,7 +40,6 @@ - Consider moving certain drivers into separate modules: audio, bt, wifi, etc - Consider using https://github.com/Graphify-Labs/graphify - Consider implementing LVGL gridnav in apps https://lvgl.io/docs/open/9.3/details/auxiliary-modules/gridnav.html -- Make USB host driver disabled by default, so it doesn't consume memory - TactilityTool: Make API compatibility table (and check for compatibility in the tool itself) - Refactor HttpServer into C code and move implementation to http-module - Use GPS time to set/update the current time diff --git a/Platforms/platform-esp32/bindings/espressif,esp32-usbhost-hid.yaml b/Platforms/platform-esp32/bindings/espressif,esp32-usbhost-hid.yaml deleted file mode 100644 index 8b59c903e..000000000 --- a/Platforms/platform-esp32/bindings/espressif,esp32-usbhost-hid.yaml +++ /dev/null @@ -1,9 +0,0 @@ -description: ESP32 USB Host HID class driver - -compatible: "espressif,esp32-usbhost-hid" - -properties: - _unused: - type: int - default: 0 - description: Placeholder required by the binding schema; this driver has no device-tree configuration. diff --git a/Platforms/platform-esp32/bindings/espressif,esp32-usbhost-midi.yaml b/Platforms/platform-esp32/bindings/espressif,esp32-usbhost-midi.yaml deleted file mode 100644 index 878cfbe85..000000000 --- a/Platforms/platform-esp32/bindings/espressif,esp32-usbhost-midi.yaml +++ /dev/null @@ -1,9 +0,0 @@ -description: ESP32 USB Host MIDI class driver - -compatible: "espressif,esp32-usbhost-midi" - -properties: - _unused: - type: int - default: 0 - description: Placeholder required by the binding schema; this driver has no device-tree configuration. diff --git a/Platforms/platform-esp32/bindings/espressif,esp32-usbhost-msc.yaml b/Platforms/platform-esp32/bindings/espressif,esp32-usbhost-msc.yaml deleted file mode 100644 index b2afee470..000000000 --- a/Platforms/platform-esp32/bindings/espressif,esp32-usbhost-msc.yaml +++ /dev/null @@ -1,9 +0,0 @@ -description: ESP32 USB Host MSC class driver (mass storage) - -compatible: "espressif,esp32-usbhost-msc" - -properties: - _unused: - type: int - default: 0 - description: Placeholder required by the binding schema; this driver has no device-tree configuration. diff --git a/Platforms/platform-esp32/bindings/espressif,esp32-usbhost-uac.yaml b/Platforms/platform-esp32/bindings/espressif,esp32-usbhost-uac.yaml deleted file mode 100644 index e69fdaa76..000000000 --- a/Platforms/platform-esp32/bindings/espressif,esp32-usbhost-uac.yaml +++ /dev/null @@ -1,9 +0,0 @@ -description: ESP32 USB Host UAC manager (installs the USB Audio Class client) - -compatible: "espressif,esp32-usbhost-uac" - -properties: - _unused: - type: int - default: 0 - description: Placeholder required by the binding schema; this driver has no device-tree configuration. diff --git a/Platforms/platform-esp32/bindings/espressif,esp32-usbhost.yaml b/Platforms/platform-esp32/bindings/espressif,esp32-usbhost.yaml index 80f20a1ea..d893ff686 100644 --- a/Platforms/platform-esp32/bindings/espressif,esp32-usbhost.yaml +++ b/Platforms/platform-esp32/bindings/espressif,esp32-usbhost.yaml @@ -13,3 +13,19 @@ properties: Default 0 selects the default peripheral (P4 default: controller 0, HS/UTMI). BIT(0) = controller 0 (HS, UTMI PHY - GPIO 49/50 aka USB2_OTG pins, e.g. USB-A on Tab5) BIT(1) = controller 1 (FS, FSLS PHY - GPIO 24/25 aka USB1 pins, e.g. USB-C OTG on Tab5) + hid: + type: boolean + default: false + description: Create the HID class device (keyboard, mouse, consumer control) when the host is enabled + midi: + type: boolean + default: false + description: Create the MIDI class device when the host is enabled + msc: + type: boolean + default: false + description: Create the mass storage class device when the host is enabled + audio: + type: boolean + default: false + description: Create the USB Audio Class device when the host is enabled (ESP32-S3 and ESP32-P4 only) diff --git a/Platforms/platform-esp32/include/tactility/bindings/esp32_usbhost.h b/Platforms/platform-esp32/include/tactility/bindings/esp32_usbhost.h index 6e82a111b..977a10a71 100644 --- a/Platforms/platform-esp32/include/tactility/bindings/esp32_usbhost.h +++ b/Platforms/platform-esp32/include/tactility/bindings/esp32_usbhost.h @@ -9,10 +9,6 @@ extern "C" { #endif DEFINE_DEVICETREE(esp32_usbhost, struct Esp32UsbHostConfig) -DEFINE_DEVICETREE(esp32_usbhost_hid, struct Esp32UsbHostChildConfig) -DEFINE_DEVICETREE(esp32_usbhost_midi, struct Esp32UsbHostChildConfig) -DEFINE_DEVICETREE(esp32_usbhost_msc, struct Esp32UsbHostChildConfig) -DEFINE_DEVICETREE(esp32_usbhost_uac, struct Esp32UsbHostChildConfig) #ifdef __cplusplus } diff --git a/Platforms/platform-esp32/include/tactility/drivers/esp32_usbhost.h b/Platforms/platform-esp32/include/tactility/drivers/esp32_usbhost.h index 0a9fdff75..8b2c3f877 100644 --- a/Platforms/platform-esp32/include/tactility/drivers/esp32_usbhost.h +++ b/Platforms/platform-esp32/include/tactility/drivers/esp32_usbhost.h @@ -1,6 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 #pragma once +#include #include #ifdef __cplusplus @@ -9,10 +10,10 @@ extern "C" { struct Esp32UsbHostConfig { uint8_t peripheral_map; /**< BIT(0)=controller 0 (HS/UTMI, e.g. USB-A on Tab5), BIT(1)=controller 1 (FS/FSLS, e.g. USB-C OTG on Tab5) */ -}; - -struct Esp32UsbHostChildConfig { - int _unused; + bool hid; + bool midi; + bool msc; + bool audio; }; #ifdef __cplusplus diff --git a/Platforms/platform-esp32/private/tactility/drivers/esp32_usbhost_task.h b/Platforms/platform-esp32/private/tactility/drivers/esp32_usbhost_task.h new file mode 100644 index 000000000..04082ffc5 --- /dev/null +++ b/Platforms/platform-esp32/private/tactility/drivers/esp32_usbhost_task.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include +#include +#include + +/** + * Creates a task with its stack in PSRAM when available, otherwise in internal RAM. + * The task must be deleted with vTaskDeleteWithCaps(). + * The task must never perform SPI flash operations, because these crash when the stack is in PSRAM. + */ +inline BaseType_t esp32_usbhost_task_create_psram(TaskFunction_t function, const char* name, uint32_t stack_size, void* arg, UBaseType_t priority, TaskHandle_t* handle) { +#if CONFIG_SPIRAM + if (xTaskCreateWithCaps(function, name, stack_size, arg, priority, handle, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT) == pdPASS) { + return pdPASS; + } +#endif + return xTaskCreateWithCaps(function, name, stack_size, arg, priority, handle, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); +} diff --git a/Platforms/platform-esp32/source/drivers/usb/esp32_usbhost.cpp b/Platforms/platform-esp32/source/drivers/usb/esp32_usbhost.cpp index 5eee7e5d9..0cfcc1295 100644 --- a/Platforms/platform-esp32/source/drivers/usb/esp32_usbhost.cpp +++ b/Platforms/platform-esp32/source/drivers/usb/esp32_usbhost.cpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include @@ -16,17 +18,38 @@ #define GET_CONFIG(device) ((const Esp32UsbHostConfig*)(device)->config) +#define USB_HOST_AUDIO_SUPPORTED (CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32S3) + +extern "C" { +extern Driver esp32_usbhost_hid_driver; +extern Driver esp32_usbhost_midi_driver; +extern Driver esp32_usbhost_msc_driver; +#if USB_HOST_AUDIO_SUPPORTED +extern Driver esp32_usbhost_uac_driver; +#endif +} + constexpr auto USB_LIB_TASK_STACK = 4096; constexpr auto USB_LIB_TASK_PRIORITY = 10; constexpr auto USB_LIB_EVENT_TIMEOUT_MS = 500; constexpr auto USB_HOST_STOP_TIMEOUT_MS = 3000; constexpr auto USB_HOST_STOP_RETRY_MS = 1000; +constexpr auto USB_HOST_CLASS_COUNT = USB_HOST_CLASS_AUDIO + 1; +constexpr auto CHILD_DESTRUCT_RETRIES = 100; + +struct UsbHostChild { + Device device = {}; + bool active = false; +}; struct UsbHostContext { TaskHandle_t lib_task = nullptr; SemaphoreHandle_t lib_task_done = nullptr; + // Indexed by UsbClass + UsbHostChild children[USB_HOST_CLASS_COUNT] = {}; }; +// Stack may be in PSRAM: no flash access allowed from this task. static void usbLibTask(void* arg) { auto* ctx = static_cast(arg); LOG_I(TAG, "lib task started"); @@ -53,18 +76,99 @@ static void usbLibTask(void* arg) { LOG_I(TAG, "lib task stopping"); xSemaphoreGive(ctx->lib_task_done); - vTaskDelete(nullptr); + vTaskDeleteWithCaps(nullptr); } -extern "C" { +// region Children -static error_t start_device(struct Device* device) { - auto* cfg = GET_CONFIG(device); - if (!cfg) { - LOG_E(TAG, "device config is null"); - return ERROR_INVALID_ARGUMENT; +struct UsbHostChildSpec { + const char* name; + Driver* driver; +}; + +static UsbHostChildSpec get_child_spec(UsbClass usb_class) { + switch (usb_class) { + case USB_HOST_CLASS_HID: + return { "usbhosthid0", &esp32_usbhost_hid_driver }; + case USB_HOST_CLASS_MIDI: + return { "usbhostmidi0", &esp32_usbhost_midi_driver }; + case USB_HOST_CLASS_MSC: + return { "usbhostmsc0", &esp32_usbhost_msc_driver }; + case USB_HOST_CLASS_AUDIO: +#if USB_HOST_AUDIO_SUPPORTED + return { "usbhostaudio0", &esp32_usbhost_uac_driver }; +#else + return { "usbhostaudio0", nullptr }; +#endif + } + return { nullptr, nullptr }; +} + +static bool is_class_configured(const Esp32UsbHostConfig* config, UsbClass usb_class) { + switch (usb_class) { + case USB_HOST_CLASS_HID: return config->hid; + case USB_HOST_CLASS_MIDI: return config->midi; + case USB_HOST_CLASS_MSC: return config->msc; + case USB_HOST_CLASS_AUDIO: return config->audio; + } + return false; +} + +static void create_child(Device* host, UsbHostChild* child, UsbClass usb_class) { + const auto spec = get_child_spec(usb_class); + if (spec.driver == nullptr) { + LOG_W(TAG, "%s is not supported on this target", spec.name); + return; } + child->device = Device { + .address = 0, + .name = spec.name, + .config = nullptr, + .parent = nullptr, + .internal = nullptr, + }; + + if (device_construct(&child->device) != ERROR_NONE) { + LOG_E(TAG, "failed to construct %s", spec.name); + return; + } + device_set_parent(&child->device, host); + device_set_driver(&child->device, spec.driver); + if (device_add(&child->device) != ERROR_NONE) { + LOG_E(TAG, "failed to add %s", spec.name); + device_destruct(&child->device); + return; + } + if (device_start(&child->device) != ERROR_NONE) { + LOG_E(TAG, "failed to start %s", spec.name); + device_remove(&child->device); + device_destruct(&child->device); + return; + } + + child->active = true; +} + +static void destroy_child(UsbHostChild* child) { + if (!child->active) { + return; + } + child->active = false; + + device_stop(&child->device); + device_remove(&child->device); + // Short-lived device_get() references block destruction + for (int i = 0; device_destruct(&child->device) == ERROR_RESOURCE_BUSY && i < CHILD_DESTRUCT_RETRIES; i++) { + vTaskDelay(pdMS_TO_TICKS(10)); + } +} + +// endregion + +// region Lib + +static error_t install_lib(UsbHostContext* ctx, const Esp32UsbHostConfig* cfg) { usb_host_config_t host_cfg = { .skip_phy_setup = false, .root_port_unpowered = false, @@ -80,35 +184,27 @@ static error_t start_device(struct Device* device) { return ERROR_RESOURCE; } - auto* ctx = new UsbHostContext(); - ctx->lib_task_done = xSemaphoreCreateBinary(); if (!ctx->lib_task_done) { LOG_E(TAG, "failed to create lib_task_done semaphore"); - delete ctx; usb_host_uninstall(); return ERROR_RESOURCE; } - BaseType_t result = xTaskCreate(usbLibTask, "usb_lib", USB_LIB_TASK_STACK, - ctx, USB_LIB_TASK_PRIORITY, &ctx->lib_task); + BaseType_t result = esp32_usbhost_task_create_psram(usbLibTask, "usb_lib", USB_LIB_TASK_STACK, + ctx, USB_LIB_TASK_PRIORITY, &ctx->lib_task); if (result != pdPASS) { LOG_E(TAG, "failed to create usb_lib task"); vSemaphoreDelete(ctx->lib_task_done); - delete ctx; + ctx->lib_task_done = nullptr; usb_host_uninstall(); return ERROR_RESOURCE; } - device_set_driver_data(device, ctx); - LOG_I(TAG, "started (peripheral_map=0x%02x)", cfg->peripheral_map); return ERROR_NONE; } -static error_t stop_device(struct Device* device) { - auto* ctx = static_cast(device_get_driver_data(device)); - if (!ctx) return ERROR_NONE; - +static error_t uninstall_lib(UsbHostContext* ctx) { xTaskNotifyGive(ctx->lib_task); bool exited = (xSemaphoreTake(ctx->lib_task_done, pdMS_TO_TICKS(USB_HOST_STOP_TIMEOUT_MS)) == pdTRUE); @@ -120,36 +216,94 @@ static error_t stop_device(struct Device* device) { if (!exited) { LOG_E(TAG, "lib task stop timed out after %dms, force terminating — USB host restart required", USB_HOST_STOP_TIMEOUT_MS + USB_HOST_STOP_RETRY_MS); - vTaskDelete(ctx->lib_task); + vTaskDeleteWithCaps(ctx->lib_task); vTaskDelay(pdMS_TO_TICKS(50)); // Skip usb_host_uninstall — USB stack is in undefined state. ctx->lib_task = nullptr; vSemaphoreDelete(ctx->lib_task_done); - device_set_driver_data(device, nullptr); - delete ctx; + ctx->lib_task_done = nullptr; return ERROR_RESOURCE; } ctx->lib_task = nullptr; vSemaphoreDelete(ctx->lib_task_done); + ctx->lib_task_done = nullptr; esp_err_t uninstall_err = usb_host_uninstall(); if (uninstall_err != ESP_OK) { LOG_W(TAG, "usb_host_uninstall: %s", esp_err_to_name(uninstall_err)); } + return ERROR_NONE; +} +// endregion + +// region API + +static error_t is_class_enabled(struct Device* device, enum UsbClass usb_class, bool* enabled) { + if (usb_class < 0 || usb_class >= USB_HOST_CLASS_COUNT) { + return ERROR_INVALID_ARGUMENT; + } + auto* ctx = static_cast(device_get_driver_data(device)); + *enabled = ctx->children[usb_class].active; + return ERROR_NONE; +} + +// endregion + +extern "C" { + +static error_t start_device(struct Device* device) { + auto* cfg = GET_CONFIG(device); + if (!cfg) { + LOG_E(TAG, "device config is null"); + return ERROR_INVALID_ARGUMENT; + } + + auto* ctx = new UsbHostContext(); + if (install_lib(ctx, cfg) != ERROR_NONE) { + delete ctx; + return ERROR_RESOURCE; + } + device_set_driver_data(device, ctx); + + for (int i = 0; i < USB_HOST_CLASS_COUNT; i++) { + const auto usb_class = static_cast(i); + if (is_class_configured(cfg, usb_class)) { + create_child(device, &ctx->children[i], usb_class); + } + } + + LOG_I(TAG, "started (peripheral_map=0x%02x)", cfg->peripheral_map); + return ERROR_NONE; +} + +static error_t stop_device(struct Device* device) { + auto* ctx = static_cast(device_get_driver_data(device)); + if (!ctx) return ERROR_NONE; + + for (int i = USB_HOST_CLASS_COUNT - 1; i >= 0; i--) { + destroy_child(&ctx->children[i]); + } + + error_t result = uninstall_lib(ctx); device_set_driver_data(device, nullptr); delete ctx; LOG_I(TAG, "stopped"); - return ERROR_NONE; + return result; } +static const UsbHostApi usb_host_api = { + .is_class_enabled = is_class_enabled, +}; + Driver esp32_usbhost_driver = { .name = "esp32_usbhost", .compatible = (const char*[]) { "espressif,esp32-usbhost", nullptr }, .start_device = start_device, .stop_device = stop_device, - .api = nullptr, - .device_type = nullptr, + .probe = nullptr, + .api = &usb_host_api, + .device_type = &USB_HOST_TYPE, .owner = nullptr, .internal = nullptr, }; diff --git a/Platforms/platform-esp32/source/drivers/usb/esp32_usbhost_hid.cpp b/Platforms/platform-esp32/source/drivers/usb/esp32_usbhost_hid.cpp index 2ee7eadf9..fb4c3c580 100644 --- a/Platforms/platform-esp32/source/drivers/usb/esp32_usbhost_hid.cpp +++ b/Platforms/platform-esp32/source/drivers/usb/esp32_usbhost_hid.cpp @@ -673,7 +673,7 @@ static error_t stop_device(struct Device* device) { Driver esp32_usbhost_hid_driver = { .name = "esp32_usbhost_hid", - .compatible = (const char*[]) { "espressif,esp32-usbhost-hid", nullptr }, + .compatible = (const char*[]) { nullptr }, .start_device = start_device, .stop_device = stop_device, .api = &hid_api, diff --git a/Platforms/platform-esp32/source/drivers/usb/esp32_usbhost_midi.cpp b/Platforms/platform-esp32/source/drivers/usb/esp32_usbhost_midi.cpp index ac9f57f5a..5a6ea4cf5 100644 --- a/Platforms/platform-esp32/source/drivers/usb/esp32_usbhost_midi.cpp +++ b/Platforms/platform-esp32/source/drivers/usb/esp32_usbhost_midi.cpp @@ -301,7 +301,7 @@ static error_t stop_device(struct Device* device) { Driver esp32_usbhost_midi_driver = { .name = "esp32_usbhost_midi", - .compatible = (const char*[]) { "espressif,esp32-usbhost-midi", nullptr }, + .compatible = (const char*[]) { nullptr }, .start_device = start_device, .stop_device = stop_device, .api = &midi_api, diff --git a/Platforms/platform-esp32/source/drivers/usb/esp32_usbhost_msc.cpp b/Platforms/platform-esp32/source/drivers/usb/esp32_usbhost_msc.cpp index 976cb059f..1f62002e5 100644 --- a/Platforms/platform-esp32/source/drivers/usb/esp32_usbhost_msc.cpp +++ b/Platforms/platform-esp32/source/drivers/usb/esp32_usbhost_msc.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -138,6 +139,7 @@ static void msc_event_cb(const msc_host_event_t* event, void* arg) { } } +// Stack may be in PSRAM: no flash access allowed from this task. static void msc_proc_task(void* arg) { auto* ctx = static_cast(arg); LOG_I(TAG, "MSC proc task started"); @@ -230,7 +232,7 @@ static void msc_proc_task(void* arg) { free_all_msc_devices(ctx); LOG_I(TAG, "MSC proc task stopped"); xSemaphoreGive(ctx->proc_task_done); - vTaskDelete(nullptr); + vTaskDeleteWithCaps(nullptr); } static bool api_eject(struct Device* device, const char* mount_path) { @@ -313,8 +315,8 @@ static error_t start_device(struct Device* device) { } ctx->proc_running = true; - BaseType_t result = xTaskCreate(msc_proc_task, "msc_proc", MSC_PROC_TASK_STACK, - ctx, MSC_PROC_TASK_PRIORITY, &ctx->proc_task); + BaseType_t result = esp32_usbhost_task_create_psram(msc_proc_task, "msc_proc", MSC_PROC_TASK_STACK, + ctx, MSC_PROC_TASK_PRIORITY, &ctx->proc_task); if (result != pdPASS) { LOG_E(TAG, "failed to create msc_proc task"); ctx->proc_running = false; @@ -343,7 +345,7 @@ static error_t stop_device(struct Device* device) { } if (!exited) { LOG_W(TAG, "MSC proc task stop timed out, force terminating"); - vTaskDelete(ctx->proc_task); + vTaskDeleteWithCaps(ctx->proc_task); vTaskDelay(pdMS_TO_TICKS(50)); // Task was killed mid-cleanup — free devices ourselves as best-effort. free_all_msc_devices(ctx); @@ -363,7 +365,7 @@ static error_t stop_device(struct Device* device) { Driver esp32_usbhost_msc_driver = { .name = "esp32_usbhost_msc", - .compatible = (const char*[]) { "espressif,esp32-usbhost-msc", nullptr }, + .compatible = (const char*[]) { nullptr }, .start_device = start_device, .stop_device = stop_device, .api = &msc_api, diff --git a/Platforms/platform-esp32/source/drivers/usb/esp32_usbhost_uac.cpp b/Platforms/platform-esp32/source/drivers/usb/esp32_usbhost_uac.cpp index e2909599b..06e95f286 100644 --- a/Platforms/platform-esp32/source/drivers/usb/esp32_usbhost_uac.cpp +++ b/Platforms/platform-esp32/source/drivers/usb/esp32_usbhost_uac.cpp @@ -605,11 +605,6 @@ Driver usb_uac_codec_driver = { }; static error_t manager_start_device(Device* device) { - if (device_get_parent(device) == nullptr || !device_is_ready(device_get_parent(device))) { - LOG_E(TAG, "USB host parent is not ready"); - return ERROR_RESOURCE; - } - auto* data = new UsbUacData(); data->manager_device = device; data->mutex = xSemaphoreCreateMutex(); @@ -705,7 +700,7 @@ static error_t manager_stop_device(Device* device) { Driver esp32_usbhost_uac_driver = { .name = "esp32_usbhost_uac", - .compatible = (const char*[]) { "espressif,esp32-usbhost-uac", nullptr }, + .compatible = (const char*[]) { nullptr }, .start_device = manager_start_device, .stop_device = manager_stop_device, .api = nullptr, diff --git a/TactilityKernel/include/tactility/drivers/usb_host.h b/TactilityKernel/include/tactility/drivers/usb_host.h new file mode 100644 index 000000000..1206fe198 --- /dev/null +++ b/TactilityKernel/include/tactility/drivers/usb_host.h @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct Device; +struct DeviceType; + +enum UsbClass { + USB_HOST_CLASS_HID, + USB_HOST_CLASS_MIDI, + USB_HOST_CLASS_MSC, + USB_HOST_CLASS_AUDIO, +}; + +struct UsbHostApi { + /** + * @param[in] device the USB host device + * @param[in] usb_class the class to check + * @param[out] enabled true when the class device is active + * @retval ERROR_NONE on success + */ + error_t (*is_class_enabled)(struct Device* device, enum UsbClass usb_class, bool* enabled); +}; + +extern const struct DeviceType USB_HOST_TYPE; + +error_t usb_host_is_class_enabled(struct Device* host, enum UsbClass usb_class, bool* enabled); + +#ifdef __cplusplus +} +#endif diff --git a/TactilityKernel/source/drivers/usb_host.cpp b/TactilityKernel/source/drivers/usb_host.cpp new file mode 100644 index 000000000..5aaca959c --- /dev/null +++ b/TactilityKernel/source/drivers/usb_host.cpp @@ -0,0 +1,17 @@ +#include +#include +#include + +#define USB_HOST_API(driver) ((struct UsbHostApi*)(driver)->api) + +extern "C" { + +const struct DeviceType USB_HOST_TYPE = { + .name = "usb-host", +}; + +error_t usb_host_is_class_enabled(struct Device* host, enum UsbClass usb_class, bool* enabled) { + return USB_HOST_API(device_get_driver(host))->is_class_enabled(host, usb_class, enabled); +} + +} // extern "C" diff --git a/TactilityKernel/source/symbols.c b/TactilityKernel/source/symbols.c index 7481a24f6..1e5d7de64 100644 --- a/TactilityKernel/source/symbols.c +++ b/TactilityKernel/source/symbols.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -449,6 +450,9 @@ const struct ModuleSymbol KERNEL_SYMBOLS[] = { DEFINE_MODULE_SYMBOL(lora_tx_event_unsubscribe), DEFINE_MODULE_SYMBOL(lora_tx_event_poll), DEFINE_MODULE_SYMBOL(LORA_TYPE), + // drivers/usb_host + DEFINE_MODULE_SYMBOL(usb_host_is_class_enabled), + DEFINE_MODULE_SYMBOL(USB_HOST_TYPE), // drivers/usb_host_hid DEFINE_MODULE_SYMBOL(usb_host_hid_is_connected), DEFINE_MODULE_SYMBOL(usb_host_hid_subscribe),