Skip to content

Status LEDs are not suppressed while an app runs when the app auto-starts at boot (boot race) #542

Description

@karol-karpinski

Status LEDs are not suppressed while an app runs when the app auto-starts at boot (boot race)

Summary

The "disable status LEDs while an app runs" behavior (introduced in feat: disable status leds while app runs, generalized in #354) silently fails for the Wi-Fi status LED when the daemon auto-starts the default app at boot. The Wi-Fi LED keeps blinking on TX for the entire lifetime of the running app, which contradicts the feature's intent. Starting the same app manually after boot works correctly.

Environment

  • Board: UNO Q (arduino,imola)
  • arduino-app-cli 0.12.1 (CLI + daemon)
  • App set as default (auto-runs on boot)

Expected behavior

While an app is running, the LED2 system/status channels (wlan, bt, panic) are handed to the app (trigger=none) and are not driven by their kernel system roles — i.e. no Wi-Fi TX blink while an app runs. (Restored on app stop, per #354.)

Actual behavior

When the default app is auto-started at boot, unoq:wlan-green2 retains the phy0tx trigger and blinks on Wi-Fi TX the whole time the app runs. Live state with the app running:

unoq:user-red1    -> [none]
unoq:user-green1  -> [none]
unoq:user-blue1   -> [none]
unoq:panic-red2   -> [none]
unoq:bt-blue2     -> [none]
unoq:wlan-green2  -> [phy0tx]   <-- still on its system trigger, blinking

Only wlan-green2 is affected; bt-blue2 and panic-red2 are correctly none.

Root cause — boot race with lazy device-tree default-trigger binding

setLedsToUserControlledMode() (internal/orchestrator/helpers.go, called once from StartApp in internal/orchestrator/orchestrator.go) writes trigger=none to each LED once. The Linux LED class applies a channel's device-tree linux,default-trigger (phy0tx for wlan-green2) lazily: it binds when a trigger of that name is registered. A userspace write of none only prevents that binding if the trigger already exists at write time — a none write while the LED's trigger is NULL does not stop the kernel from binding the DT default when the trigger later registers.

At boot the daemon starts the default app before the Wi-Fi driver has loaded, so the seize races the driver:

15:13:39  kernel boot
15:13:51  bluetooth-power trigger registered   (bt-blue2 seize will stick)
15:13:51  app container StartedAt  ->  setLedsToUserControlledMode() writes none to all channels
15:13:54  ath10k firmware/QMI comes up
15:13:58  wcn3990 target ready     ->  phy0* LED triggers register  (AFTER the seize)
15:14:04  wlan0 associated

bluetooth-power registers before the seize, so bt-blue2's none sticks. phy0tx registers after the seize, so wlan-green2's pending DT default is (re)bound → blink. Manual app start after boot is unaffected because by then phy0tx is already registered.

Confirming the mechanism

Once phy0tx is registered, the seize is durable — writing none sticks and does not self-rebind:

$ cat /sys/class/leds/unoq:wlan-green2/trigger    # [phy0tx]
$ echo none > /sys/class/leds/unoq:wlan-green2/trigger
$ cat .../trigger    # [none]   (still [none] after several seconds)

So the seize logic is correct; it just runs too early at boot.

Suggested fix

Make the seize robust to system LED triggers that register after StartApp. Options:

  1. Bounded re-assert: after the initial seize, re-apply trigger=none to the status channels until the read-back is stable, over a window that covers driver load (self-terminating; a no-op when the app is started after boot).
  2. Event-driven: for status channels with a known DT default trigger, wait until that trigger name appears in the channel's available-trigger list, then write none.

Happy to open a PR implementing the bounded re-assert if that direction is acceptable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions