Files
esphome/usb_callback_contexts.md
J. Nick Koston 32797534a7 propsals
2025-11-07 22:04:58 -06:00

91 lines
3.3 KiB
Markdown

# USB Host Component - Callback Execution Contexts
## Overview
After the refactoring to use a dedicated USB task, all USB callbacks now execute in the USB task context, NOT in the main loop. This prevents race conditions and data corruption.
## USB Task Architecture
```cpp
// USB Task (runs on Core 1, priority 5)
void USBClient::usb_task_loop() {
while (usb_task_running_) {
// This handles ALL USB events and triggers callbacks
usb_host_client_handle_events(handle_, pdMS_TO_TICKS(10));
}
}
```
## Callback Execution Contexts
### 1. **client_event_cb** (Device Connect/Disconnect)
- **Context**: USB task
- **Triggered by**: USB device connection/disconnection events
- **What it does**: Queues events to main loop via FreeRTOS queue
- **Data flow**: USB hardware → USB task → Queue → Main loop
### 2. **control_callback** (Control Transfers)
- **Context**: USB task
- **Triggered by**: Completion of USB control transfers
- **What it does**: Queues callback execution to main loop
- **Data flow**: USB hardware → USB task → Queue → Main loop
### 3. **transfer_callback** (Bulk Transfers)
- **Context**: USB task
- **Triggered by**: Completion of bulk IN/OUT transfers
- **What it does**: Queues callback execution to main loop
- **Data flow**: USB hardware → USB task → Queue → Main loop
### 4. **USBUartComponent Input Callback** (Data Reception)
- **Context**: USB task (lambda passed to transfer_in)
- **Called from**: transfer_callback in USB task
- **What it does**:
- Copies received data to temporary buffer
- Queues data processing to main loop via defer()
- Main loop then safely pushes to ring buffer
- **Data flow**: USB hardware → USB task → Copy data → Queue → Main loop → Ring buffer
### 5. **USBUartComponent Output Callback** (Data Transmission)
- **Context**: USB task (lambda passed to transfer_out)
- **Called from**: transfer_callback in USB task
- **What it does**:
- Marks output as not started
- Queues next transfer start to main loop via defer()
- **Data flow**: Main loop reads ring buffer → Start transfer → USB task → Queue restart → Main loop
## Thread Safety Summary
### Safe Operations (Main Loop Only):
- Ring buffer push (incoming data)
- Ring buffer pop (outgoing data)
- Component state changes
- Transfer initiation
### USB Task Operations:
- USB event handling
- Transfer completion detection
- Event queueing to main loop
### Communication Mechanism:
- **FreeRTOS Queue**: For USB events and callbacks
- **defer()**: For component-specific operations
## Why This Architecture?
1. **Prevents data loss**: USB events are handled promptly even when main loop is busy
2. **Thread safety**: Ring buffer is only accessed from main loop
3. **No race conditions**: Data structures aren't shared between tasks
4. **Maintains responsiveness**: USB hardware FIFOs don't overflow
## Key Changes from Original Implementation
### Before (Problematic):
- `usb_host_client_handle_events()` called in main loop
- Callbacks executed in main loop context
- USB events dropped when main loop was busy
- Ring buffer corruption when data arrived during slow processing
### After (Fixed):
- `usb_host_client_handle_events()` runs in dedicated task
- Callbacks execute in USB task, queue work to main loop
- USB events always handled promptly
- Ring buffer only accessed from main loop (thread-safe)