SIGN IN SIGN UP

fix: stop dropping log lines at the low-power clock (#3737)

## Summary

* **What is the goal of this PR?** Stop the firmware from silently
dropping serial log lines while the C3 runs at its 10 MHz low-power
clock.
* **What changes are included?** `logPrintf()` now always calls
`logSerial.print()` instead of writing only `if (logSerial)`.

**Why lines were lost.** After 3 s without input, `HalPowerManager`
drops the C3 to 10 MHz. At that clock, work done with interrupts masked
takes about 16 times longer, so FreeRTOS ticks arrive late. The USB
Serial/JTAG connection monitor checks for a start-of-frame signal on
each tick and allows 3 ticks without one, so it briefly reports the host
as unplugged. The main loop's `Serial` check then makes HWCDC clear its
`connected` flag. The flag stays false until the next TX-complete
interrupt, and `logPrintf()` skipped every line in that window. Losses
cluster in the first ~85 ms after a wake-up press, while rendering
starts before `setPowerSaving(false)` restores the clock.

**Why always writing is safe.** In the not-connected state,
`HWCDC::write()` does not block: it keeps the newest bytes in its
256-byte TX ring and re-arms the interrupt that sets `connected` again.
With a host connected, the write path is unchanged. Sticky's ROM-printf
transport is untouched.

## Scope Check

- [x] I have read SCOPE.md and ROADMAP.md.
- [x] This PR is **not** a new built-in theme (themes are temporarily
closed pending the move to SD-loaded themes).
- [x] This PR is **not** a new external network connector (sync engine,
cloud storage, remote file access, etc.).
- [x] This PR is **not** an interactive app, writing tool,
RSS/news/browser, media playback, or PDF feature.
- [x] The stock firmware does not already handle this well, **and** no
other popular CrossPoint fork already does
      (or, if one does, I explain why CrossPoint still needs it below).
- [x] If this PR touches `freeink-sdk/`, `lib/hal/`, the bootloader,
OTA, or recovery code, I have coordinated with
the relevant maintainer. (Not applicable: only `lib/Logging/` changes.)

## Additional Context

Found while testing the serial device control in #3510, whose `STATE`
replies are ordinary log lines. The measurements used #3510's control
protocol plus temporary logger counters that classified every line the
transport did not fully accept. Each run sent one `STATE` request at a
time and matched every request ID to its reply.

| Firmware on X3 | Replies sent | Lost | Not-connected events |
|---|---:|---:|---:|
| Normal low-power behavior, before this change | 9,138 | 8 | 11 |
| CPU clock pinned, before this change | 3,448 | 0 | 0 |
| Normal low-power behavior, with this change | 6,791 | 0 | 5, all
delivered |

No lock timeouts, full-ring writes or partial writes occurred in any
run. Every loss was a skipped write while HWCDC reported not connected,
and in the refined runs the SOF watchdog reported plugged while the
`connected` flag was false.

Checks:
- `default`, `gh_release`, `slim` and `x4pro` build.
- On an X3, develop plus this change booted and reached Home. The
low-power transition and periodic `MEM` lines appeared, with no `[ERR]`
or panic.
- Flash: −14 bytes on `gh_release`; `slim` unchanged. Static RAM
unchanged.

**Related work:**
- #2034 made serial writes non-blocking with a 0 ms TX timeout, and
#2073 settled it at 1 ms, so logging cannot stall the firmware when no
host drains the port. This change keeps that property: it adds no
waiting and only stops discarding lines the driver can queue.
- #2688 also targets lost serial output, but through truncated large
writes (`CMD:SCREENSHOT`), a different path from the skipped writes
fixed here. Its auto-sleep change relies on
`usb_serial_jtag_is_connected()`, the same SOF watchdog that flaps at 10
MHz.
- #3060 addresses another effect of the idle 10 MHz clock by holding
`HalPowerManager::Lock` during background builds. A clock lock around
serial activity was the alternative here. Fixing the logger covers every
log line instead.

**Please test on hardware:** battery-only use and a charger with no data
host. Both take the not-connected path on every log line, which was
previously skipped. It is non-blocking, but it has not been measured
unplugged.

---

### AI Usage

Did you use AI tools to help write this code? _**YES**_. Claude
diagnosed the loss on device, wrote the change, and ran the builds and
device checks.
S
Sung-jin Brian Hong committed
1f77b83dbc9f3547372a3c3fcc007b87a1b1b7a3
Parent: f1d0af1
Committed by GitHub <noreply@github.com> on 10/3/2026, 2:22:59 AM