fix(watchdog): M20 亮屏亮度不再回退固定 60(settings 读失败重试 + 未知快照语义拆分)— 重编出货二进制

真机 0.8.46(2026-11-05 21:09 日志)复现「开机/亮屏背光跳到固定值」:
  76646 [watchdog] power: panel ON (blank 0 + brightness 60)
而 /usr/data/meow/settings.conf 是 display.brightness = 1。根因链:
- 出货 /usr/bin/meow_watchdog(md5 553840cd…,与仓库内二进制逐字节相同)是
  2026-09-01 6a4d330 编出的旧版:不含 M6.23 的 settings 读取(strings 无
  display.brightness_min,日志仍是旧格式 "brightness %d")—— M6.23 只改了源码、
  没重编。旧版 wd_panel_on 无条件 write_brightness(wd_saved_bri>0?wd_saved_bri:60),
  wd_saved_bri 初值 60 且该 session 未熄屏过 → 亮屏写 60,覆盖 launcher 刚设的 1。
- 源码侧同样有 60 兜底:wd_settings_brightness() fopen 失败即 -1 → 回退快照 60。

改动(patch_v2/meow_watchdog/):
- wd_settings_brightness():fopen 失败重试 3×10ms。覆盖 settings.conf「写 tmp+rename」
  原子替换、开机早期 /usr/data 未就绪的瞬时窗口;只在失败时发生(成功路径 0 开销),
  最坏 20ms,远小于亮屏轮询粒度 REFRESH_MS=100ms。
- 新增 wd_bri_decide() 纯函数:settings(clamp 后)→ 本次运行期真实快照 → 不写
  (面板已亮,untouched)→ 安全低亮 10(面板黑且无任何值来源)。任何路径都不再写 60。
- wd_saved_bri 初值 60 → WD_BRI_UNKNOWN(0);wd_panel_off 只在读到正亮度时更新快照,
  读失败/被 launcher 抢先写 0 时保留上一次真实值(不再是 (br>0)?br:60)。
- panel ON/OFF 日志带 src=settings|snapshot|untouched|safe-low 与 user/saved/cur,
  真机一眼看出用的是哪一支。
- 背光路径加 MEOW_BACKLIGHT env 测试钩子(与 MEOW_SETTINGS_FILE 同款,真机无 env)。
- test_wd_fw.c 新增 21 条用例(决策 7 + settings 解析/重试 6 + panel OFF/ON 端到端 8);
  端到端用例真的读写背光文件,复现「settings=1 → 亮屏写 1」而不是 60。
- 重编 meow_watchdog:mipsel 静态 MIPS32r2 ELF,md5 ad8c1f2b…,0 warning。

验证:./build_watchdog.sh host → 38 pass / 0 fail;交叉编译(-Os -Wall -Wextra -static)
0 warning,产物静态 MIPS32r2。
生效条件:meow_watchdog 在 squashfs /usr/bin,必须刷固件(或等价的重打包流程)才生效。
后续(不在本单 scope):scripts/build/fw_content.conf 第 32 行仍 pin 旧 md5 553840cd…,
不更新会让 build_fw_apps.sh 的强校验失败。
This commit is contained in:
AskaEth committed 2026-11-05 21:32:27 +08:00
1 parent bcb11e012f
commit f2551a28a2
4 files changed
+246 -19

No files matched your search

+82 -16
View File
@@ -31,6 +31,9 @@
* (true|1|yes)。判定为 SDK 框架 App -> 音量/媒体键让位 SDK 输入层(meow_ui_input.c 已消化,
* 防同一次按键双步进);电源仍由本进程兜底(SDK 对电源是 no-op)。裸自绘 App(framework=0/
* 无 framework)或 app.json 不可读 -> 音量/媒体键仍由本进程兜底(不造成功能真空)。
* - M20 亮屏亮度:settings.conf 的 display.brightness(clamp 到 brightness_min..100)为唯一
* 权威;settings 瞬时不可读用**真实观测快照**;两者都未知时**不写背光**(不再回退固定 60,
* 那会覆盖 launcher 刚设好的用户值 → 开机/亮屏跳 60)。面板已黑且无任何值来源才写安全低亮。
* Exits when the app process disappears (kill(pid,0) -> ESRCH) or on
* SIGTERM from the parent.
*
@@ -192,16 +195,44 @@ static int g_fw_app = 0;
/* ---- M2.22(A)+ M2.24 电源键接管:息屏/亮屏 + 长按菜单/强关 ---- */
static int wd_fbfd = -1; /* fb0 fd(main 里打开,供 FBIOBLANK/FBIOPAN 用) */
static int wd_saved_bri = 60; /* 熄屏前亮度快照 */
/* M20(远古 bug 回归:开机/亮屏背光跳到固定 60):0 是「未知」哨兵,不是亮度值
* (0 = 面板全黑/息屏,任何情况下都不是有效用户亮度)。旧代码让 60 同时表达
* 「未知快照」和「兜底值」,于是一旦没有可信来源就把 60 写进背光,覆盖 launcher
* 刚按 display.brightness 设好的用户值(真机 0.8.46:用户亮度 1,亮屏仍写 60)。 */
#define WD_BRI_UNKNOWN 0
/* 唯一保留的固定亮度:settings 读不到 + 快照未知 + **当前亮度确实是 0**(面板黑)时才用。
* 这条退路上不存在任何可保留的有效用户值,不写就是永久黑屏;取低亮(≈10% of
* max_brightness 101)而不是 60 —— 宁可暗到用户手动调亮,也不要夜里突然刺眼。
* 注意 60 是固件 S11jpeg_display_shell 画完开机 logo 后写死的值(/etc/init.d,不在本仓库),
* 本进程任何分支都不再写 60。 */
#define WD_BRI_SAFE_LOW 10
/* M20 短期修复 A:settings 读取重试。settings.conf 由 launcher/audiod 以
* 「写 .tmp + rename」原子替换,开机早期 /usr/data(UBIFS) 也可能尚未就绪,
* fopen 存在瞬时失败窗口。3 次 × 10ms 只覆盖该窗口:最坏总阻塞 (3-1)*10 = 20ms,
* 且只在失败时发生(成功路径 0 开销)。亮屏轮询粒度本身是 REFRESH_MS=100ms,
* 20ms 在「按键→亮屏」路径上不可感知(见 M20 报文)。 */
#define WD_SETTINGS_TRIES 3
#define WD_SETTINGS_GAP_MS 10
static int wd_saved_bri = WD_BRI_UNKNOWN; /* 熄屏前亮度快照;未知=WD_BRI_UNKNOWN(不再用 60) */
static int pwr_down = 0; /* 电源当前按住 */
static unsigned long long pwr_down_ms = 0;
static int pwr_woke = 0; /* 按下时屏已黑 → 本次只作唤醒,松开不再 toggle */
static int pwr_menu_notified = 0; /* 已写 /tmp/meow_power_menu 请 launcher 弹菜单 */
static int pwr_force_done = 0; /* 8s 强关已发(防重复) */
/* 背光 sysfs 路径(缺省 BACKLIGHT;env MEOW_BACKLIGHT 覆盖,供宿主测试隔离——与
* MEOW_SETTINGS_FILE 同款测试钩子,真机无 env 走默认,行为不变)。 */
static const char *wd_backlight_path(void)
{
const char *e = getenv("MEOW_BACKLIGHT");
return (e && e[0]) ? e : BACKLIGHT;
}
static int read_brightness(void)
{
int fd = open(BACKLIGHT, O_RDONLY);
int fd = open(wd_backlight_path(), O_RDONLY);
if (fd < 0) return -1;
char buf[16];
int n = read(fd, buf, sizeof buf - 1);
@@ -215,7 +246,7 @@ static void write_brightness(int val)
{
char buf[8];
int l = snprintf(buf, sizeof buf, "%d\n", val);
int fd = open(BACKLIGHT, O_WRONLY);
int fd = open(wd_backlight_path(), O_WRONLY);
if (fd < 0) return;
if (write(fd, buf, l) < 0) wd_log("backlight write %d: %s", val, strerror(errno));
close(fd);
@@ -231,16 +262,24 @@ static const char *wd_settings_path(void)
/* M6.23:亮屏恢复亮度以**用户设置**为准(/usr/data/meow/settings.conf 的 display.brightness),
* 而非 wd_saved_bri 快照——该快照可能被污染:panel OFF 前 read_brightness() 可能读到 launcher
* 先写好的 0(launcher meow_panel_off 已 write_brightness(0)),`wd_saved_bri=(br>0)?br:60` 便落到
* 先写好的 0(launcher meow_panel_off 已 write_brightness(0)),旧代码 `(br>0)?br:60` 便落到
* 默认 60;panel ON 恢复 60 与 launcher 按用户设置恢复 7 竞态 → 亮屏亮度跳变(真机「跳高到 60 /
* 又降回 7」,2026-09-03)。
* 又降回 7」,2026-09-03)。旧公式里的 `:60` 已在 M20 移除。
* 与 launcher meow_settings_brightness_clamped() 同源(同文件、同 clamp:下限 display.brightness_min
* 默认 1=极暗但可见、上限 100=防闪屏);本进程自身 wd_vol_load 早已读该文件,不引入 launcher 全量
* 依赖,保持 watchdog 独立性。返回 clamped 值;settings 文件不可读(S70 早启动 /usr/data 未就绪)
* → 返回 -1,调用方回退 wd_saved_bri(保持旧行为,防开机竞态)。 */
* M20 起:fopen 失败做 WD_SETTINGS_TRIES 次、间隔 WD_SETTINGS_GAP_MS 的短重试,
* 覆盖 settings.conf 原子替换 / 开机早期 /usr/data 未就绪的瞬时窗口;仍失败才返回 -1。
* 只重试 open:rename 是原子的,已打开的 fd 指向旧 inode、内容完整,读内容无需重试。
* 返回 -1 时调用方**不再**回退「默认 60」,取值语义统一由 wd_bri_decide() 决定。 */
static int wd_settings_brightness(void)
{
FILE *f = fopen(wd_settings_path(), "r");
FILE *f = NULL;
for (int i = 0; i < WD_SETTINGS_TRIES; i++) {
f = fopen(wd_settings_path(), "r");
if (f) break;
if (i + 1 < WD_SETTINGS_TRIES) usleep(WD_SETTINGS_GAP_MS * 1000);
}
if (!f) return -1;
int b = 101; /* display.brightness 默认(与 launcher 设置表同值,键缺失时 clamp→100) */
int mn = 1; /* display.brightness_min 默认(极暗但可见;键缺失时保底) */
@@ -276,26 +315,53 @@ static void wd_pan_buf0_force(void)
ioctl(wd_fbfd, FBIOPAN_DISPLAY, v);
}
/* 亮屏亮度决策(纯函数:宿主单测直接断言,见 test_wd_fw.c)。
* 返回 >0 = 要写入的背光值;返回 -1 = **不写背光**(保持当前值)。
* *src 输出取值来源,原样写进日志供真机排查:
* settings settings.conf 的 display.brightness(已 clamp)—— 唯一权威
* snapshot 本次运行期熄屏前读到的真实亮度(>0 才算数)—— settings 瞬时不可读时的过渡值
* untouched settings 与快照都未知、但面板已亮 → 不覆盖现场(M20 核心修复)
* safe-low settings/快照都未知且面板当前为 0(黑)→ 点亮,用低亮而不是 60
* 优先级:settings → 快照(真实近似值)→ 保持现状 → 安全低亮。
* 任何分支都不再写「固定 60」:60 只在固件 S11 画完开机 logo 后写一次(固件侧问题,非本进程)。 */
static int wd_bri_decide(int user, int saved, int cur, const char **src)
{
if (user > 0) { *src = "settings"; return user; } /* 用户设置优先 */
if (saved > 0) { *src = "snapshot"; return saved; } /* 真实观测到的近似值 */
if (cur > 0) { *src = "untouched"; return -1; } /* 已有可见亮度:绝不覆盖 */
*src = "safe-low"; return WD_BRI_SAFE_LOW; /* 面板黑 + 无任何值来源 */
}
static void wd_panel_off(void)
{
int br = read_brightness();
wd_saved_bri = (br > 0) ? br : 60;
/* M20:快照只记「真实观测到的正亮度」。br<=0(读失败,或 launcher meow_panel_off 抢先
* 写成 0)时保留上一次已知真实值,绝不写死 60 —— 否则 60 会在下次亮屏被当成用户值回放
* (远古 bug 的燃料)。真的一次都没观测到正亮度时保持 WD_BRI_UNKNOWN。 */
if (br > 0) wd_saved_bri = br;
write_brightness(0);
if (wd_fbfd >= 0) ioctl(wd_fbfd, FBIOBLANK, 4); /* 面板下电 + 挂起 goodix 触摸 */
wd_log("power: panel OFF (was %d -> 0, blank 4)", br);
wd_log("power: panel OFF (was %d -> 0, blank 4, saved=%d)", br, wd_saved_bri);
}
static void wd_panel_on(void)
{
if (wd_fbfd >= 0) ioctl(wd_fbfd, FBIOBLANK, 0);
/* M6.23:恢复亮度以**用户设置**为准(wd_settings_brightness),settings 不可用时才回退
* wd_saved_bri 快照(再退默认 60)——与 launcher meow_panel_on 同语义,杜绝「亮屏恢复 60
* 与 launcher 恢复 7 竞态 → 跳变」。 */
int b = wd_settings_brightness();
int restore = (b > 0) ? b : (wd_saved_bri > 0 ? wd_saved_bri : 60);
write_brightness(restore);
/* M6.23 + M20:用户设置为唯一权威;settings 瞬时不可读用真实快照;两者都未知时
* **不写背光**(旧代码在这里写死 60,直接覆盖 launcher 刚按 display.brightness 设好的
* 用户值 → 开机/亮屏跳 60,真机 0.8.46 实证)。取值分支见 wd_bri_decide()。 */
int user = wd_settings_brightness(); /* >0=用户设置;-1=不可读 */
int cur = read_brightness(); /* 当前系统背光:判定「不写」是否安全 + 日志 */
const char *src = "?";
int restore = wd_bri_decide(user, wd_saved_bri, cur, &src);
if (restore > 0) write_brightness(restore);
wd_pan_buf0_force(); /* 唤醒后强制回 buffer0 */
wd_log("power: panel ON (blank 0 + brightness %d, user=%d saved=%d)", restore, b, wd_saved_bri);
if (restore > 0)
wd_log("power: panel ON (blank 0 + brightness %d src=%s user=%d saved=%d cur=%d)",
restore, src, user, wd_saved_bri, cur);
else
wd_log("power: panel ON (blank 0 + brightness UNTOUCHED cur=%d src=%s user=%d saved=%d)",
cur, src, user, wd_saved_bri);
}
/* launcher 正在画电源菜单(/tmp/meow_power_menu_active 存在)→ watchdog 完全让位。 */