@brief Cold fallback for the non-pre-bound driver path. Handles dynamic `ChannelManager::selectDriverForChannel` lookup AND the bus-key-miss diagnostic chain. Hoisted out of `showPixels` so the hot legacy `addLeds<>` path stays compact — see #2773 item 2.1. Marked `noinline` (via `FL_NOINLINE`) so the compiler doesn't fold the cold body back into `showPixels`. The whole helper is reachable only w
| 388 | /// LTO can use that across the call site to keep the cold body off the |
| 389 | /// hot icache line. |
| 390 | FL_NO_INLINE |
| 391 | fl::shared_ptr<IChannelDriver> Channel::resolveDynamicDriver() { |
| 392 | #if defined(FASTLED_DISABLE_DYNAMIC_DRIVER) && FASTLED_DISABLE_DYNAMIC_DRIVER |
| 393 | // Body excluded via FASTLED_DISABLE_DYNAMIC_DRIVER (#2926). The |
| 394 | // showPixels call site is also gated so this is unreachable; the |
| 395 | // empty body lets the linker dead-strip ChannelManager::findDriverByName |
| 396 | // and selectDriverForChannel along with this symbol. |
| 397 | return {}; |
| 398 | #else |
| 399 | // Build busKey only when we actually need it (mBus != AUTO). |
| 400 | fl::string busKey; |
| 401 | if (mBus != Bus::AUTO) { |
| 402 | busKey = fl::string::from_literal(busName(mBus)); |
| 403 | } |
| 404 | |
| 405 | fl::shared_ptr<IChannelDriver> driver = |
| 406 | ChannelManager::instance().selectDriverForChannel(mChannelData, busKey); |
| 407 | mDriver = driver; |
| 408 | |
| 409 | // #2455 / #2459: one-shot diagnostic when a typed-Bus miss happens. |
| 410 | // Probe via `findDriverByName` (silent) to distinguish "driver wasn't |
| 411 | // instantiated" from "driver exists but canHandle() rejected this |
| 412 | // chipset" — resolution paths differ. The mBusWarned guard suppresses |
| 413 | // duplicate logs on subsequent shows of the same channel. |
| 414 | #if FASTLED_LOG_RUNTIME_ENABLED |
| 415 | if (mBus != Bus::AUTO && !mBusWarned && |
| 416 | (!driver || driver->getName() != busKey)) { |
| 417 | auto busDriver = ChannelManager::instance().findDriverByName(busKey); |
| 418 | if (!busDriver) { |
| 419 | // Typed Bus miss — emit the actionable hint with the three |
| 420 | // currently-shipping remediations (option 3 added in #2460). |
| 421 | FL_ERROR("Channel '" << mName << "': Driver '" << busKey |
| 422 | << "' wasn't instantiated. Resolve with: " |
| 423 | << "(1) fl::enableDrivers<fl::Bus::" << busKey << ">() " |
| 424 | << "(links only this driver), " |
| 425 | << "(2) FastLED.enableAllDrivers() (links every driver), or " |
| 426 | << "(3) FastLED.addLeds<..., fl::Bus::" << busKey << ">(...) " |
| 427 | << "(legacy API; pins Bus + triggers linker keep-alive). " |
| 428 | << "Defaulting to AUTO/priority dispatch."); |
| 429 | } else { |
| 430 | // Registered, but canHandle() said no — bus/chipset mismatch. |
| 431 | FL_ERROR("Channel '" << mName << "': Driver '" << busKey |
| 432 | << "' is registered but cannot handle this channel's chipset " |
| 433 | << "(bus/chipset mismatch). Defaulting to AUTO/priority dispatch."); |
| 434 | } |
| 435 | mBusWarned = true; |
| 436 | } |
| 437 | if (!driver) { |
| 438 | FL_ERROR("Channel '" << mName << "': No compatible driver found - cannot transmit"); |
| 439 | } |
| 440 | #endif // FASTLED_LOG_RUNTIME_ENABLED — release skips the per-frame |
| 441 | // driver->getName() != busKey compare + the silent |
| 442 | // findDriverByName probe (used only to differentiate the |
| 443 | // FL_ERROR message). The functional return value below is |
| 444 | // preserved in both builds. See #2952. |
| 445 | return driver; |
| 446 | #endif // !FASTLED_DISABLE_DYNAMIC_DRIVER |
| 447 | } |
nothing calls this directly
no test coverage detected