mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-10-19 11:55:06 +00:00
Refactor usb host stack
This commit is contained in:
parent
d64eac64f0
commit
6988a22f65
@ -107,20 +107,8 @@
|
|||||||
|
|
||||||
usbhost0 {
|
usbhost0 {
|
||||||
compatible = "espressif,esp32-usbhost";
|
compatible = "espressif,esp32-usbhost";
|
||||||
|
hid;
|
||||||
usbhosthid0 {
|
midi;
|
||||||
compatible = "espressif,esp32-usbhost-hid";
|
msc;
|
||||||
//status = "disabled";
|
|
||||||
};
|
|
||||||
|
|
||||||
usbhostmidi0 {
|
|
||||||
compatible = "espressif,esp32-usbhost-midi";
|
|
||||||
//status = "disabled";
|
|
||||||
};
|
|
||||||
|
|
||||||
usbhostmsc0 {
|
|
||||||
compatible = "espressif,esp32-usbhost-msc";
|
|
||||||
//status = "disabled";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -198,26 +198,10 @@
|
|||||||
usbhost0 {
|
usbhost0 {
|
||||||
compatible = "espressif,esp32-usbhost";
|
compatible = "espressif,esp32-usbhost";
|
||||||
peripheral-map = <0>;
|
peripheral-map = <0>;
|
||||||
|
hid;
|
||||||
usbhosthid0 {
|
midi;
|
||||||
compatible = "espressif,esp32-usbhost-hid";
|
msc;
|
||||||
};
|
audio;
|
||||||
|
|
||||||
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";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
usbdevice0 {
|
usbdevice0 {
|
||||||
|
|||||||
@ -177,17 +177,8 @@
|
|||||||
|
|
||||||
usbhost0 {
|
usbhost0 {
|
||||||
compatible = "espressif,esp32-usbhost";
|
compatible = "espressif,esp32-usbhost";
|
||||||
|
hid;
|
||||||
usbhosthid0 {
|
midi;
|
||||||
compatible = "espressif,esp32-usbhost-hid";
|
msc;
|
||||||
};
|
|
||||||
|
|
||||||
usbhostmidi0 {
|
|
||||||
compatible = "espressif,esp32-usbhost-midi";
|
|
||||||
};
|
|
||||||
|
|
||||||
usbhostmsc0 {
|
|
||||||
compatible = "espressif,esp32-usbhost-msc";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -7,12 +7,10 @@
|
|||||||
|
|
||||||
## Higher Priority
|
## 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.
|
- Improve Setup: Add keyboard/keypad/encoder navigation explanation.
|
||||||
e.g. encoder long press on t-lora pager to get out of textarea
|
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.
|
- 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)
|
- Improve SPI kernel driver (implement read, write, transactions)
|
||||||
- TCA9534 keyboards should use interrupts
|
- TCA9534 keyboards should use interrupts
|
||||||
@ -42,7 +40,6 @@
|
|||||||
- Consider moving certain drivers into separate modules: audio, bt, wifi, etc
|
- Consider moving certain drivers into separate modules: audio, bt, wifi, etc
|
||||||
- Consider using https://github.com/Graphify-Labs/graphify
|
- 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
|
- 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)
|
- TactilityTool: Make API compatibility table (and check for compatibility in the tool itself)
|
||||||
- Refactor HttpServer into C code and move implementation to http-module
|
- Refactor HttpServer into C code and move implementation to http-module
|
||||||
- Use GPS time to set/update the current time
|
- Use GPS time to set/update the current time
|
||||||
|
|||||||
@ -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.
|
|
||||||
@ -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.
|
|
||||||
@ -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.
|
|
||||||
@ -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.
|
|
||||||
@ -13,3 +13,19 @@ properties:
|
|||||||
Default 0 selects the default peripheral (P4 default: controller 0, HS/UTMI).
|
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(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)
|
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)
|
||||||
|
|||||||
@ -9,10 +9,6 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
DEFINE_DEVICETREE(esp32_usbhost, struct Esp32UsbHostConfig)
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@ -9,10 +10,10 @@ extern "C" {
|
|||||||
|
|
||||||
struct Esp32UsbHostConfig {
|
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) */
|
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) */
|
||||||
};
|
bool hid;
|
||||||
|
bool midi;
|
||||||
struct Esp32UsbHostChildConfig {
|
bool msc;
|
||||||
int _unused;
|
bool audio;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sdkconfig.h>
|
||||||
|
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/idf_additions.h>
|
||||||
|
#include <esp_heap_caps.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
@ -4,6 +4,8 @@
|
|||||||
#include <tactility/device.h>
|
#include <tactility/device.h>
|
||||||
#include <tactility/driver.h>
|
#include <tactility/driver.h>
|
||||||
#include <tactility/drivers/esp32_usbhost.h>
|
#include <tactility/drivers/esp32_usbhost.h>
|
||||||
|
#include <tactility/drivers/esp32_usbhost_task.h>
|
||||||
|
#include <tactility/drivers/usb_host.h>
|
||||||
#include <tactility/log.h>
|
#include <tactility/log.h>
|
||||||
|
|
||||||
#include <freertos/FreeRTOS.h>
|
#include <freertos/FreeRTOS.h>
|
||||||
@ -16,17 +18,38 @@
|
|||||||
|
|
||||||
#define GET_CONFIG(device) ((const Esp32UsbHostConfig*)(device)->config)
|
#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_STACK = 4096;
|
||||||
constexpr auto USB_LIB_TASK_PRIORITY = 10;
|
constexpr auto USB_LIB_TASK_PRIORITY = 10;
|
||||||
constexpr auto USB_LIB_EVENT_TIMEOUT_MS = 500;
|
constexpr auto USB_LIB_EVENT_TIMEOUT_MS = 500;
|
||||||
constexpr auto USB_HOST_STOP_TIMEOUT_MS = 3000;
|
constexpr auto USB_HOST_STOP_TIMEOUT_MS = 3000;
|
||||||
constexpr auto USB_HOST_STOP_RETRY_MS = 1000;
|
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 {
|
struct UsbHostContext {
|
||||||
TaskHandle_t lib_task = nullptr;
|
TaskHandle_t lib_task = nullptr;
|
||||||
SemaphoreHandle_t lib_task_done = 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) {
|
static void usbLibTask(void* arg) {
|
||||||
auto* ctx = static_cast<UsbHostContext*>(arg);
|
auto* ctx = static_cast<UsbHostContext*>(arg);
|
||||||
LOG_I(TAG, "lib task started");
|
LOG_I(TAG, "lib task started");
|
||||||
@ -53,18 +76,99 @@ static void usbLibTask(void* arg) {
|
|||||||
|
|
||||||
LOG_I(TAG, "lib task stopping");
|
LOG_I(TAG, "lib task stopping");
|
||||||
xSemaphoreGive(ctx->lib_task_done);
|
xSemaphoreGive(ctx->lib_task_done);
|
||||||
vTaskDelete(nullptr);
|
vTaskDeleteWithCaps(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
// region Children
|
||||||
|
|
||||||
static error_t start_device(struct Device* device) {
|
struct UsbHostChildSpec {
|
||||||
auto* cfg = GET_CONFIG(device);
|
const char* name;
|
||||||
if (!cfg) {
|
Driver* driver;
|
||||||
LOG_E(TAG, "device config is null");
|
};
|
||||||
return ERROR_INVALID_ARGUMENT;
|
|
||||||
|
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 = {
|
usb_host_config_t host_cfg = {
|
||||||
.skip_phy_setup = false,
|
.skip_phy_setup = false,
|
||||||
.root_port_unpowered = false,
|
.root_port_unpowered = false,
|
||||||
@ -80,35 +184,27 @@ static error_t start_device(struct Device* device) {
|
|||||||
return ERROR_RESOURCE;
|
return ERROR_RESOURCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* ctx = new UsbHostContext();
|
|
||||||
|
|
||||||
ctx->lib_task_done = xSemaphoreCreateBinary();
|
ctx->lib_task_done = xSemaphoreCreateBinary();
|
||||||
if (!ctx->lib_task_done) {
|
if (!ctx->lib_task_done) {
|
||||||
LOG_E(TAG, "failed to create lib_task_done semaphore");
|
LOG_E(TAG, "failed to create lib_task_done semaphore");
|
||||||
delete ctx;
|
|
||||||
usb_host_uninstall();
|
usb_host_uninstall();
|
||||||
return ERROR_RESOURCE;
|
return ERROR_RESOURCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseType_t result = xTaskCreate(usbLibTask, "usb_lib", USB_LIB_TASK_STACK,
|
BaseType_t result = esp32_usbhost_task_create_psram(usbLibTask, "usb_lib", USB_LIB_TASK_STACK,
|
||||||
ctx, USB_LIB_TASK_PRIORITY, &ctx->lib_task);
|
ctx, USB_LIB_TASK_PRIORITY, &ctx->lib_task);
|
||||||
if (result != pdPASS) {
|
if (result != pdPASS) {
|
||||||
LOG_E(TAG, "failed to create usb_lib task");
|
LOG_E(TAG, "failed to create usb_lib task");
|
||||||
vSemaphoreDelete(ctx->lib_task_done);
|
vSemaphoreDelete(ctx->lib_task_done);
|
||||||
delete ctx;
|
ctx->lib_task_done = nullptr;
|
||||||
usb_host_uninstall();
|
usb_host_uninstall();
|
||||||
return ERROR_RESOURCE;
|
return ERROR_RESOURCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
device_set_driver_data(device, ctx);
|
|
||||||
LOG_I(TAG, "started (peripheral_map=0x%02x)", cfg->peripheral_map);
|
|
||||||
return ERROR_NONE;
|
return ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static error_t stop_device(struct Device* device) {
|
static error_t uninstall_lib(UsbHostContext* ctx) {
|
||||||
auto* ctx = static_cast<UsbHostContext*>(device_get_driver_data(device));
|
|
||||||
if (!ctx) return ERROR_NONE;
|
|
||||||
|
|
||||||
xTaskNotifyGive(ctx->lib_task);
|
xTaskNotifyGive(ctx->lib_task);
|
||||||
|
|
||||||
bool exited = (xSemaphoreTake(ctx->lib_task_done, pdMS_TO_TICKS(USB_HOST_STOP_TIMEOUT_MS)) == pdTRUE);
|
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) {
|
if (!exited) {
|
||||||
LOG_E(TAG, "lib task stop timed out after %dms, force terminating — USB host restart required",
|
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);
|
USB_HOST_STOP_TIMEOUT_MS + USB_HOST_STOP_RETRY_MS);
|
||||||
vTaskDelete(ctx->lib_task);
|
vTaskDeleteWithCaps(ctx->lib_task);
|
||||||
vTaskDelay(pdMS_TO_TICKS(50));
|
vTaskDelay(pdMS_TO_TICKS(50));
|
||||||
// Skip usb_host_uninstall — USB stack is in undefined state.
|
// Skip usb_host_uninstall — USB stack is in undefined state.
|
||||||
ctx->lib_task = nullptr;
|
ctx->lib_task = nullptr;
|
||||||
vSemaphoreDelete(ctx->lib_task_done);
|
vSemaphoreDelete(ctx->lib_task_done);
|
||||||
device_set_driver_data(device, nullptr);
|
ctx->lib_task_done = nullptr;
|
||||||
delete ctx;
|
|
||||||
return ERROR_RESOURCE;
|
return ERROR_RESOURCE;
|
||||||
}
|
}
|
||||||
ctx->lib_task = nullptr;
|
ctx->lib_task = nullptr;
|
||||||
vSemaphoreDelete(ctx->lib_task_done);
|
vSemaphoreDelete(ctx->lib_task_done);
|
||||||
|
ctx->lib_task_done = nullptr;
|
||||||
|
|
||||||
esp_err_t uninstall_err = usb_host_uninstall();
|
esp_err_t uninstall_err = usb_host_uninstall();
|
||||||
if (uninstall_err != ESP_OK) {
|
if (uninstall_err != ESP_OK) {
|
||||||
LOG_W(TAG, "usb_host_uninstall: %s", esp_err_to_name(uninstall_err));
|
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<UsbHostContext*>(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<UsbClass>(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<UsbHostContext*>(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);
|
device_set_driver_data(device, nullptr);
|
||||||
delete ctx;
|
delete ctx;
|
||||||
LOG_I(TAG, "stopped");
|
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 = {
|
Driver esp32_usbhost_driver = {
|
||||||
.name = "esp32_usbhost",
|
.name = "esp32_usbhost",
|
||||||
.compatible = (const char*[]) { "espressif,esp32-usbhost", nullptr },
|
.compatible = (const char*[]) { "espressif,esp32-usbhost", nullptr },
|
||||||
.start_device = start_device,
|
.start_device = start_device,
|
||||||
.stop_device = stop_device,
|
.stop_device = stop_device,
|
||||||
.api = nullptr,
|
.probe = nullptr,
|
||||||
.device_type = nullptr,
|
.api = &usb_host_api,
|
||||||
|
.device_type = &USB_HOST_TYPE,
|
||||||
.owner = nullptr,
|
.owner = nullptr,
|
||||||
.internal = nullptr,
|
.internal = nullptr,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -673,7 +673,7 @@ static error_t stop_device(struct Device* device) {
|
|||||||
|
|
||||||
Driver esp32_usbhost_hid_driver = {
|
Driver esp32_usbhost_hid_driver = {
|
||||||
.name = "esp32_usbhost_hid",
|
.name = "esp32_usbhost_hid",
|
||||||
.compatible = (const char*[]) { "espressif,esp32-usbhost-hid", nullptr },
|
.compatible = (const char*[]) { nullptr },
|
||||||
.start_device = start_device,
|
.start_device = start_device,
|
||||||
.stop_device = stop_device,
|
.stop_device = stop_device,
|
||||||
.api = &hid_api,
|
.api = &hid_api,
|
||||||
|
|||||||
@ -301,7 +301,7 @@ static error_t stop_device(struct Device* device) {
|
|||||||
|
|
||||||
Driver esp32_usbhost_midi_driver = {
|
Driver esp32_usbhost_midi_driver = {
|
||||||
.name = "esp32_usbhost_midi",
|
.name = "esp32_usbhost_midi",
|
||||||
.compatible = (const char*[]) { "espressif,esp32-usbhost-midi", nullptr },
|
.compatible = (const char*[]) { nullptr },
|
||||||
.start_device = start_device,
|
.start_device = start_device,
|
||||||
.stop_device = stop_device,
|
.stop_device = stop_device,
|
||||||
.api = &midi_api,
|
.api = &midi_api,
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <tactility/device.h>
|
#include <tactility/device.h>
|
||||||
#include <tactility/driver.h>
|
#include <tactility/driver.h>
|
||||||
|
#include <tactility/drivers/esp32_usbhost_task.h>
|
||||||
#include <tactility/drivers/usb_host_msc.h>
|
#include <tactility/drivers/usb_host_msc.h>
|
||||||
#include <tactility/filesystem/file_system.h>
|
#include <tactility/filesystem/file_system.h>
|
||||||
#include <tactility/log.h>
|
#include <tactility/log.h>
|
||||||
@ -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) {
|
static void msc_proc_task(void* arg) {
|
||||||
auto* ctx = static_cast<UsbMscContext*>(arg);
|
auto* ctx = static_cast<UsbMscContext*>(arg);
|
||||||
LOG_I(TAG, "MSC proc task started");
|
LOG_I(TAG, "MSC proc task started");
|
||||||
@ -230,7 +232,7 @@ static void msc_proc_task(void* arg) {
|
|||||||
free_all_msc_devices(ctx);
|
free_all_msc_devices(ctx);
|
||||||
LOG_I(TAG, "MSC proc task stopped");
|
LOG_I(TAG, "MSC proc task stopped");
|
||||||
xSemaphoreGive(ctx->proc_task_done);
|
xSemaphoreGive(ctx->proc_task_done);
|
||||||
vTaskDelete(nullptr);
|
vTaskDeleteWithCaps(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool api_eject(struct Device* device, const char* mount_path) {
|
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;
|
ctx->proc_running = true;
|
||||||
BaseType_t result = xTaskCreate(msc_proc_task, "msc_proc", MSC_PROC_TASK_STACK,
|
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);
|
ctx, MSC_PROC_TASK_PRIORITY, &ctx->proc_task);
|
||||||
if (result != pdPASS) {
|
if (result != pdPASS) {
|
||||||
LOG_E(TAG, "failed to create msc_proc task");
|
LOG_E(TAG, "failed to create msc_proc task");
|
||||||
ctx->proc_running = false;
|
ctx->proc_running = false;
|
||||||
@ -343,7 +345,7 @@ static error_t stop_device(struct Device* device) {
|
|||||||
}
|
}
|
||||||
if (!exited) {
|
if (!exited) {
|
||||||
LOG_W(TAG, "MSC proc task stop timed out, force terminating");
|
LOG_W(TAG, "MSC proc task stop timed out, force terminating");
|
||||||
vTaskDelete(ctx->proc_task);
|
vTaskDeleteWithCaps(ctx->proc_task);
|
||||||
vTaskDelay(pdMS_TO_TICKS(50));
|
vTaskDelay(pdMS_TO_TICKS(50));
|
||||||
// Task was killed mid-cleanup — free devices ourselves as best-effort.
|
// Task was killed mid-cleanup — free devices ourselves as best-effort.
|
||||||
free_all_msc_devices(ctx);
|
free_all_msc_devices(ctx);
|
||||||
@ -363,7 +365,7 @@ static error_t stop_device(struct Device* device) {
|
|||||||
|
|
||||||
Driver esp32_usbhost_msc_driver = {
|
Driver esp32_usbhost_msc_driver = {
|
||||||
.name = "esp32_usbhost_msc",
|
.name = "esp32_usbhost_msc",
|
||||||
.compatible = (const char*[]) { "espressif,esp32-usbhost-msc", nullptr },
|
.compatible = (const char*[]) { nullptr },
|
||||||
.start_device = start_device,
|
.start_device = start_device,
|
||||||
.stop_device = stop_device,
|
.stop_device = stop_device,
|
||||||
.api = &msc_api,
|
.api = &msc_api,
|
||||||
|
|||||||
@ -605,11 +605,6 @@ Driver usb_uac_codec_driver = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static error_t manager_start_device(Device* device) {
|
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();
|
auto* data = new UsbUacData();
|
||||||
data->manager_device = device;
|
data->manager_device = device;
|
||||||
data->mutex = xSemaphoreCreateMutex();
|
data->mutex = xSemaphoreCreateMutex();
|
||||||
@ -705,7 +700,7 @@ static error_t manager_stop_device(Device* device) {
|
|||||||
|
|
||||||
Driver esp32_usbhost_uac_driver = {
|
Driver esp32_usbhost_uac_driver = {
|
||||||
.name = "esp32_usbhost_uac",
|
.name = "esp32_usbhost_uac",
|
||||||
.compatible = (const char*[]) { "espressif,esp32-usbhost-uac", nullptr },
|
.compatible = (const char*[]) { nullptr },
|
||||||
.start_device = manager_start_device,
|
.start_device = manager_start_device,
|
||||||
.stop_device = manager_stop_device,
|
.stop_device = manager_stop_device,
|
||||||
.api = nullptr,
|
.api = nullptr,
|
||||||
|
|||||||
38
TactilityKernel/include/tactility/drivers/usb_host.h
Normal file
38
TactilityKernel/include/tactility/drivers/usb_host.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include <tactility/error.h>
|
||||||
|
|
||||||
|
#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
|
||||||
17
TactilityKernel/source/drivers/usb_host.cpp
Normal file
17
TactilityKernel/source/drivers/usb_host.cpp
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#include <tactility/drivers/usb_host.h>
|
||||||
|
#include <tactility/device.h>
|
||||||
|
#include <tactility/driver.h>
|
||||||
|
|
||||||
|
#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"
|
||||||
@ -39,6 +39,7 @@
|
|||||||
#include <tactility/drivers/uart_controller.h>
|
#include <tactility/drivers/uart_controller.h>
|
||||||
#include <tactility/drivers/usb_device_controller.h>
|
#include <tactility/drivers/usb_device_controller.h>
|
||||||
#include <tactility/drivers/usb_hid_device.h>
|
#include <tactility/drivers/usb_hid_device.h>
|
||||||
|
#include <tactility/drivers/usb_host.h>
|
||||||
#include <tactility/drivers/usb_host_hid.h>
|
#include <tactility/drivers/usb_host_hid.h>
|
||||||
#include <tactility/drivers/usb_host_midi.h>
|
#include <tactility/drivers/usb_host_midi.h>
|
||||||
#include <tactility/drivers/usb_host_msc.h>
|
#include <tactility/drivers/usb_host_msc.h>
|
||||||
@ -449,6 +450,9 @@ const struct ModuleSymbol KERNEL_SYMBOLS[] = {
|
|||||||
DEFINE_MODULE_SYMBOL(lora_tx_event_unsubscribe),
|
DEFINE_MODULE_SYMBOL(lora_tx_event_unsubscribe),
|
||||||
DEFINE_MODULE_SYMBOL(lora_tx_event_poll),
|
DEFINE_MODULE_SYMBOL(lora_tx_event_poll),
|
||||||
DEFINE_MODULE_SYMBOL(LORA_TYPE),
|
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
|
// drivers/usb_host_hid
|
||||||
DEFINE_MODULE_SYMBOL(usb_host_hid_is_connected),
|
DEFINE_MODULE_SYMBOL(usb_host_hid_is_connected),
|
||||||
DEFINE_MODULE_SYMBOL(usb_host_hid_subscribe),
|
DEFINE_MODULE_SYMBOL(usb_host_hid_subscribe),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user