Kobo: corrige le suspend (EPDC/VEE) qui vidait la batterie

Cause racine de l'autonomie médiocre : la liseuse ne suspendait JAMAIS.
Juste après un refresh e-ink, le pilote EPDC (20f4000.epdc) refuse de
suspendre tant que la haute tension VEE n'est pas redescendue
("waiting for VEE stable ... please retry suspend later", error -2) :
le noyau avorte tout le suspend, on tombait dans le repli `sleep` et le
CPU tournait 24h/24 (0 suspend réussi sur 261 itérations dans les logs).

Correctif (suspend_for) : on laisse l'EPDC décharger VEE (~8 s) puis on
RÉESSAIE le suspend jusqu'à ce qu'il prenne, comme le suggère le noyau.
Résultat : rtcwake elapsed ~= REFRESH (vrai sommeil entre les refresh),
duty cycle éveillé ~100% -> ~3%.

Aussi :
- intervalle 5 min -> 15 min (moins de réveils).
- suspend_diag() one-shot conservé comme filet (ne se déclenche qu'en
  cas d'échec total après les retries) — à retirer après validation 24 h.
This commit is contained in:
jerem
2026-06-16 01:07:44 +02:00
parent 85edb5d14c
commit 15754fb05c
2 changed files with 70 additions and 25 deletions

View File

@@ -15,8 +15,9 @@ cd "$BASE" || exit 1
# --- Configuration ---
export MONITORINK_URL="http://192.168.0.43:8899/image.png"
export MONITORINK_REFRESH=300 # PROD: refresh partiel 5 min
# Cadence du full refresh : côté SERVEUR via MONITORINK_FULL_EVERY (PROD=12 ~1 h à 5 min/cycle).
export MONITORINK_REFRESH=900 # PROD: refresh partiel 15 min (moins de réveils = batterie)
# Cadence du full refresh : côté SERVEUR via MONITORINK_FULL_EVERY. À 15 min/cycle, FULL_EVERY=4
# -> full refresh ~1 h (ajuster côté backend si besoin pour limiter le ghosting).
echo "===== monitorink start $(date) =====" >> "$LOG"; sync

View File

@@ -121,32 +121,76 @@ frontlight_off() {
done
}
DIAG_DONE=0
suspend_diag() {
# DIAGNOSTIC (one-shot) : quand un suspend échoue sur batterie, capture la raison
# du veto noyau dans le log. À retirer une fois la cause identifiée.
[ "$DIAG_DONE" = 1 ] && return
DIAG_DONE=1
log "===== DIAG suspend (one-shot) ====="
for d in /sys/class/power_supply/*/; do
n=$(basename "$d")
log " $n: status=$(cat "${d}status" 2>/dev/null) online=$(cat "${d}online" 2>/dev/null) present=$(cat "${d}present" 2>/dev/null)"
done
log " wakeup_count=$(cat /sys/power/wakeup_count 2>/dev/null) state-extended=$(cat /sys/power/state-extended 2>/dev/null)"
# IRQ responsable du DERNIER réveil — la clé du mystère.
log " pm_wakeup_irq=$(cat /sys/power/pm_wakeup_irq 2>/dev/null) last_resume_reason=$(cat /sys/kernel/debug/wakeup/last_resume_reason 2>/dev/null)"
# Quels devices d'entrée sont armés comme source de réveil (1=oui).
log " -- input devices (name | wakeup) --"
for inp in /sys/class/input/input*/; do
nm=$(cat "${inp}name" 2>/dev/null)
wk=$(cat "${inp}power/wakeup" 2>/dev/null)
[ -n "$nm" ] && log " $(basename "$inp"): \"$nm\" wakeup=$wk"
done
# debugfs (souvent non monté) -> wakeup_sources triés par nb d'évènements.
mount -t debugfs none /sys/kernel/debug 2>/dev/null
if [ -r /sys/kernel/debug/wakeup_sources ]; then
log " -- top wakeup_sources (event_count) --"
awk 'NR>1 && ($3+0)>0 {print $3"\t"$1}' /sys/kernel/debug/wakeup_sources 2>/dev/null \
| sort -rn | head -8 | while IFS= read -r l; do log " $l"; done
fi
# Les IRQ qui montent = source matérielle qui spamme (touch elan, gpio, etc.).
log " -- /proc/interrupts (lignes non nulles) --"
awk 'NR>1 && ($2+0)>0 {print $0}' /proc/interrupts 2>/dev/null \
| sort -t: -k2 -rn 2>/dev/null | head -12 | while IFS= read -r l; do log " $l"; done
log " -- dmesg : lignes PM/wakeup/elan/gpio (40 dernières) --"
dmesg 2>/dev/null | grep -iE 'PM:|wakeup|abort|suspend|elan|gpio|irq' | tail -40 \
| while IFS= read -r l; do log " $l"; done
log "===== /DIAG ====="
}
suspend_for() {
# Séquence Kobo éprouvée (trmnl-kobo) : state-extended=1 AVANT rtcwake, puis
# suspend manuel (echo mem) si rtcwake n'a pas vraiment suspendu.
# Suspend rtcwake (-m mem), réveil RTC après "secs".
#
# PIÈGE EPDC : juste après un refresh e-ink, la haute tension VEE du panneau n'est pas
# encore redescendue ; le pilote EPDC (20f4000.epdc) refuse alors de suspendre
# ("waiting for VEE stable ... please retry suspend later", error -2) et le noyau avorte
# TOUT le suspend. Sans gestion, on tombait dans un sleep CPU-allumé -> batterie vidée.
# Parade (recommandée par le noyau lui-même) : laisser VEE se décharger, puis RÉESSAYER
# le suspend jusqu'à ce qu'il prenne.
secs="$1"
sync
sleep 8 # laisse l'EPDC couper ses rails (VEE) ~10s après le refresh
attempt=0
while [ "$attempt" -lt 6 ]; do
attempt=$((attempt + 1))
sync
echo 1 > /sys/power/state-extended 2>/dev/null
start=$(date +%s)
"$BUSYBOX" rtcwake -a -s "$secs" -m mem 2>/dev/null
elapsed=$(( $(date +%s) - start ))
log "rtcwake elapsed=${elapsed}s"
if [ "$elapsed" -le 10 ]; then
log "suspend manuel via echo mem"
sleep 1; sync; sleep 2
m_start=$(date +%s)
echo mem > /sys/power/state 2>/dev/null
log "echo mem elapsed=$(( $(date +%s) - m_start ))s (≈${secs}=OK, ≈0=USB branché/suspend bloqué)"
fi
echo 0 > /sys/power/state-extended 2>/dev/null
# Repli : si rien n'a réellement suspendu (USB branché, etc.), on attend le temps
# restant en sleep pour ne pas boucler en continu (martèlement backend + batterie).
total=$(( $(date +%s) - start ))
if [ "$total" -lt "$secs" ]; then
rem=$(( secs - total ))
log "suspend incomplet (${total}s) -> sleep ${rem}s"
sleep "$rem"
fi
log "rtcwake tentative=$attempt elapsed=${elapsed}s"
# elapsed grand = on a réellement dormi (réveil RTC ou bouton) -> terminé.
[ "$elapsed" -ge 15 ] && return
# Échec immédiat (EPDC/VEE pas prêt) : on attend un peu et on retente.
sleep 3
done
# Toujours pas suspendu après les retries (cas anormal) -> diag + repli sleep pour ne pas
# marteler le backend, sans laisser le CPU tourner inutilement plus que "secs".
log "suspend impossible après $attempt tentatives -> diag + sleep"
suspend_diag
sleep "$secs"
}
has_ip() { ip addr show 2>/dev/null | grep -o 'inet [0-9.]*' | grep -qv '127.0'; }