17#include "platforms/init_channel_driver.h"
18#include "platforms/is_platform.h"
27 static bool sInitialized =
false;
30 platforms::initChannelDrivers();
38 FL_DBG(
"ChannelManager: Initializing");
45 FL_DBG(
"ChannelManager: Destructor called");
64 if (context ==
nullptr) {
75 constexpr u32 kPollNeededFallbackSliceMs = 1;
77 return kPollNeededFallbackSliceMs;
79 const u32 elapsed =
millis() - startTime;
80 if (elapsed >= timeoutMs) {
83 const u32 remaining = timeoutMs - elapsed;
84 return remaining < kPollNeededFallbackSliceMs ? remaining : kPollNeededFallbackSliceMs;
89 FL_WARN(
"ChannelManager::addDriver() - Null driver provided");
97 if (engineName.
empty()) {
98 FL_WARN(
"ChannelManager::addDriver() - Engine has empty name (driver->getName() returned empty string)");
103 bool replacing =
false;
104 for (
const auto& entry :
mDrivers) {
105 if (entry.name == engineName) {
111 if (entry.driver == driver && entry.priority == priority) {
112 FL_DBG(
"ChannelManager::addDriver() - '" << engineName.
c_str()
113 <<
"' already registered at priority " << priority
114 <<
" (idempotent no-op)");
118 FL_WARN(
"ChannelManager::addDriver() - Replacing existing driver '" << engineName.
c_str() <<
"'");
125 FL_DBG(
"ChannelManager: Waiting for all drivers to become READY before replacement");
129 for (
size_t i = 0; i <
mDrivers.size(); ++i) {
130 if (
mDrivers[i].name == engineName) {
131 FL_DBG(
"ChannelManager: Removing old driver '" << engineName.
c_str() <<
"' (shared_ptr may delete)");
147 mDrivers.push_back({priority, driver, engineName, enabled});
161 capStr +=
"CLOCKLESS";
164 if (!capStr.
empty()) capStr +=
"|";
167 if (capStr.
empty()) {
171 FL_DBG(
"ChannelManager: Added driver '" << engineName.
c_str() <<
"' (priority " << priority <<
", caps: " << capStr.
c_str() <<
")");
183 FL_WARN(
"ChannelManager::removeDriver() - Null driver provided");
188 for (
size_t i = 0; i <
mDrivers.size(); ++i) {
190 FL_DBG(
"ChannelManager: Removing driver '" <<
mDrivers[i].name <<
"'");
201 FL_WARN(
"ChannelManager::removeDriver() - Engine " << driver.
get() <<
" not found in registry");
206 FL_DBG(
"ChannelManager: Waiting for all drivers to become READY before clearing");
212 FL_DBG(
"ChannelManager: Clearing " <<
mDrivers.size() <<
" drivers");
226 FL_ERROR(
"ChannelManager::setDriverEnabled() - Null driver name provided");
232 if (entry.name == name) {
233 entry.enabled = enabled;
235 FL_DBG(
"ChannelManager: Driver '" << name <<
"' " << (enabled ?
"enabled" :
"disabled"));
240 FL_ERROR(
"ChannelManager::setDriverEnabled() - Driver '" << name <<
"' not found in registry");
250 if (!name || !name[0]) {
251 FL_ERROR(
"ChannelManager::setExclusiveDriverByName() - Null or empty driver name provided");
254 entry.enabled =
false;
266 entry.enabled = (entry.name == name);
267 found = found || entry.enabled;
271 FL_ERROR(
"ChannelManager::setExclusiveDriverByName() - Driver '" << name <<
"' not found in registry");
278 FL_ERROR(
"ChannelManager::setDriverPriority() - Empty driver name provided");
285 if (entry.name == name) {
286 entry.priority = priority;
288 FL_DBG(
"ChannelManager: Driver '" << name <<
"' priority changed to " << priority);
294 FL_ERROR(
"ChannelManager::setDriverPriority() - Driver '" << name <<
"' not found in registry");
302 FL_DBG(
"ChannelManager: Engine list re-sorted after priority change");
308 FL_ERROR(
"ChannelManager::isDriverEnabled() - Null driver name provided");
312 for (
const auto& entry :
mDrivers) {
313 if (entry.name == name) {
314 return entry.enabled;
318 FL_ERROR(
"ChannelManager::isDriverEnabled() - Driver '" << name <<
"' not found in registry");
326 for (
const auto& entry :
mDrivers) {
327 if (entry.name == name) {
344 for (
const auto& entry :
mDrivers) {
360 for (
const auto& entry :
mDrivers) {
361 if (entry.enabled && entry.name == name) {
370 FL_ERROR(
"ChannelManager::getDriverByName() - Empty driver name provided");
375 FL_ERROR(
"ChannelManager::getDriverByName() - Driver '" << name.
c_str() <<
"' not found or not enabled");
382 FL_ERROR(
"ChannelManager::selectDriverForChannel() - Null channel data");
393 if (affinity.
empty()) {
400 if (!driver->canHandle(data)) {
401 FL_WARN_ONCE(
"ChannelManager: Affinity driver '" << affinity
402 <<
"' cannot handle channel data (chipset/bus mismatch). "
403 <<
"Falling back to AUTO/priority dispatch.");
411 for (
const auto& entry :
mDrivers) {
412 if (!entry.enabled)
continue;
413 if (entry.driver->canHandle(data)) {
418 FL_ERROR(
"ChannelManager: No compatible driver found for channel data");
423template<
typename Condition>
425 const u32 startTime = timeoutMs > 0 ?
millis() : 0;
439 if (spinBudget > 0) {
441 while ((
fl::micros() - spinStart) < spinBudget) {
445 if (timeoutMs > 0 && (
millis() - startTime) >= timeoutMs) {
446 FL_ERROR(
"ChannelManager: Timeout occurred while waiting for condition");
453 while (!condition()) {
455 if (timeoutMs > 0 && (
millis() - startTime >= timeoutMs)) {
456 FL_ERROR(
"ChannelManager: Timeout occurred while waiting for condition");
496 bool anyBusy =
false;
497 bool anyDraining =
false;
509 firstError =
result.error;
514 if (!firstError.
empty()) {
532 FL_ERROR(
"ChannelManager: Timeout occurred while waiting for READY state");
540 bool draining_or_done = (
544 return draining_or_done;
547 FL_ERROR(
"ChannelManager: Timeout occurred while waiting for READY or DRAINING state");
563 entry.driver->show();
572 FL_DBG(
"ChannelManager: reset() - all drivers ready");
FastLED chrono implementation - duration types for time measurements.
bool isDriverEnabled(const char *name) const FL_NOEXCEPT
Check if a driver is enabled by name.
void setDriverEnabled(const char *name, bool enabled) FL_NOEXCEPT
Enable or disable a driver by name at runtime.
bool removeDriver(fl::shared_ptr< IChannelDriver > driver) FL_NOEXCEPT
Remove a driver from the manager.
fl::shared_ptr< IChannelDriver > selectDriverForChannel(const ChannelDataPtr &data, const fl::string &affinity) FL_NOEXCEPT
Select best driver for channel data (used by Channel::showPixels)
void setExclusiveDriver() FL_NOEXCEPT
Register a single driver at a priority above the platform default and disable all others (compile-tim...
bool waitForPollNeededSignal(u32 timeoutMs) FL_NOEXCEPT
~ChannelManager() FL_NOEXCEPT override
Destructor - cleanup shared drivers (automatic via shared_ptr)
void reset() FL_NOEXCEPT
Reset bus manager state, clearing all enqueued and transmitting channels.
fl::shared_ptr< IChannelDriver > getDriverByName(const fl::string &name) const FL_NOEXCEPT
Get driver by name for affinity binding.
u32 pollNeededWaitSliceMs(u32 startTime, u32 timeoutMs) const FL_NOEXCEPT
IChannelDriver::PollNeededCallback mPollNeededCallback
Shared callback installed on drivers that can signal poll-needed events.
static void notifyPollNeededThunk(void *context) FL_NOEXCEPT
fl::shared_ptr< IChannelDriver > findDriverByName(const fl::string &name) const FL_NOEXCEPT
Silent counterpart to getDriverByName().
bool setDriverPriority(const fl::string &name, int priority) FL_NOEXCEPT
Change the priority of a registered driver.
fl::span< const DriverInfo > getDriverInfos() const FL_NOEXCEPT
Get full state of all registered drivers.
fl::size getDriverCount() const FL_NOEXCEPT
Get count of registered drivers (including unnamed ones)
void addDriver(int priority, fl::shared_ptr< IChannelDriver > driver) FL_NOEXCEPT
Add a driver with priority (higher priority = preferred)
bool waitForReady(u32 timeoutMs=1000) FL_NOEXCEPT
Wait for all drivers to become READY.
platforms::ChannelPollSignal mPollNeededSignal
Platform wait primitive owned by the manager.
void clearAllDrivers() FL_NOEXCEPT
Remove all drivers from the manager.
IChannelDriver::DriverState poll() FL_NOEXCEPT
Poll all registered drivers and return aggregate state.
fl::vector< DriverInfo > mCachedDriverInfo
Cached driver info for getDriverInfos() to avoid allocations.
DriverStatus
Registration status of a driver by name (silent lookup)
@ NOT_REGISTERED
No driver with that name is registered.
@ STATUS_ENABLED
Driver is registered and enabled.
@ STATUS_DISABLED
Driver is registered but disabled.
void onEndFrame() FL_NOEXCEPT override
Trigger transmission of batched channel data.
bool waitForReadyOrDraining(u32 timeoutMs=1000) FL_NOEXCEPT
fl::vector< EngineEntry > mDrivers
Shared drivers sorted by priority descending (higher values first)
bool waitForCondition(Condition condition, u32 timeoutMs=1000) FL_NOEXCEPT
Wait until a condition is met, with check-pump-delay logic.
fl::string mExclusiveDriver
Exclusive driver name (empty if no exclusive mode)
void onBeginFrame() FL_NOEXCEPT override
Poll drivers before frame starts to clear previous frame state.
ChannelManager() FL_NOEXCEPT
Constructor.
bool setExclusiveDriverByName(const char *name) FL_NOEXCEPT
Enable only one driver exclusively (disables all others) — by-name escape hatch.
void notifyPollNeeded() FL_NOEXCEPT
static ChannelManager & instance() FL_NOEXCEPT
Get the global singleton instance.
DriverStatus driverStatus(const fl::string &name) const FL_NOEXCEPT
Look up a driver's registration status without logging on miss.
Unified channel manager with priority-based driver selection.
static void removeListener(Listener *listener) FL_NOEXCEPT
static void addListener(Listener *listener, int priority=0) FL_NOEXCEPT
static T & instance() FL_NOEXCEPT
bool empty() const FL_NOEXCEPT
const char * c_str() const FL_NOEXCEPT
static bool isAnyNetworkActive() FL_NOEXCEPT
T * get() const FL_NOEXCEPT
Task executor — runs registered task runners and manages the run loop.
Centralized logging categories for FastLED hardware interfaces and subsystems.
Unified manager for channel drivers with priority-based fallback.
fl::u32 getWaitSpinBudgetUs() FL_NOEXCEPT
Get the current tiered-wait spin budget (microseconds).
void run(fl::u32 microseconds, ExecFlags flags)
Run selected task subsystems.
fl::u32 millis()
Universal millisecond timer - returns milliseconds since system startup.
const char * busName(Bus b) FL_NOEXCEPT
Canonical driver-name string for a Bus value.
Bus
Driver identifier for compile-time bus selection.
ChannelManager & channelManager()
Get the global ChannelManager singleton instance.
expected< T, E > result
Alias for expected (Rust-style naming)
fl::u32 micros()
Universal microsecond timer - returns microseconds since system startup.
void sort_small(Iterator first, Iterator last, Compare comp) FL_NOEXCEPT
Base definition for an LED controller.
Cross-platform facade for runtime network activity detection.
bool supportsClockless
Supports clockless protocols (WS2812, SK6812, etc.)
bool supportsSpi
Supports SPI protocols (APA102, SK9822, etc.)
Value state
Current driver state.
@ READY
Hardware idle; ready to accept new transmissions.
@ DRAINING
All channels submitted; still transmitting.
@ ERROR
Driver encountered an error.
@ BUSY
Active: channels transmitting or queued.
Driver state with optional error message.
ISR-safe callback handle invoked when the manager should poll again.
Runtime-tunable microsecond spin budget for the channel-manager and driver wait loops (Phase 1 of #28...