fix(keys): 插孔消抖 + 音量 ramp 3s 上限 + 亮度写日志 — 耳机插拔 ADC 幽灵音量键修复
真机实证(meow_20260901_034946.log): 4.4 插入瞬间电气瞬态在 jz_adc_aux(event2)
产生 VOL_UP 幽灵按住, vol_ramp 24%→上限50 (225391 ramp 起→227213 爬满,
紧随 audiod jack changed). 修(限 meow_launcherd/+meow_watchdog/):
① launcher audio.c + watchdog 各自读 /sys/class/switch/{headset,balance}/state
(节流缓存 1s/500ms, 热路径不每圈读文件), 变化后 800ms 内忽略音量键边沿
(vol_ramp_begin 不起/在爬 ramp 中止/wd_vol_adj 单步忽略)
② ramp 总时长上限 VOL_RAMP_MAX_MS=3000 兜底防一次幽灵打满(正常长按 24→50 ~1.4s 不伤)
③ write_brightness(val,src) 全 10 调用点带源+逐写 logmsg, 亮度跳上限下次可定位
(grep 全库: 写 backlight 仅 launcher/watchdog/svc_power/bootmenu, 无确定 255 路径)
宿主: 新 test_vol_ramp_jack 15/15 PASS(单击/长按回归+3s cap+消抖忽略/中止/恢复);
test_wd_fw 17/17 + test_settings_brightness + test_notif_tf PASS; MIPS -Wall -Wextra 0 warning
(顺带修 watchdog 既有 sys/poll.h→poll.h 重定向 warning).
watchdog 已热更设备 /usr/data(baa997ea); launcher 7be1d4eb 待固件. fw_content.conf 已更新.
遗留: SDK meow_ui_input.c 音量键路径需同款消抖(sdk/ 有主未动)
This commit is contained in:
1 parent
44d170413d
commit
4bafdb5d69
13 files changed
+348
-20
No files matched your search
@@ -97,5 +97,6 @@ watchdog 主循环(`for(;;)`)加按键轮询(`read /dev/input/event0/event
|
||||
2. **音量跳步**:watchdog 每按下边沿按 `audio.volume_step` 一步(不做 launcher 的线性渐变);短按与 launcher 菜单一致,长按不连续爬(媒体落地后可补)。launcher 应用返回时 `settings_load()` 同步。
|
||||
3. **左右滑切屏**:**已落地(M2.81 Now Playing 横划页)**——主界面左滑进 Now Playing 大页;左缘返回手势保留。此处旧注「未实现切换逻辑」作废。
|
||||
4. **媒体键**:**已落地(M2.81 起路由最近活跃媒体会话)**——播放/上下曲路由到最近活跃媒体会话(MSG_PLAYCTL),无会话时「抓住」不动作。此处旧注「路由待阶段 D」作废。
|
||||
5. **插孔消抖(2026-09-01,真机日志实证)**:4.4/3.5 插拔的电气瞬态在音量键 ADC(jz_adc_aux,event2)上产生 VOL_UP 幽灵按住,launcher vol_ramp 曾从 24% 一路爬到上限 50(日志 225391 ramp 起 → 227213 爬满 → 紧随 audiod「jack changed」)。修:launcher 与 watchdog 音量键处理统一读 `/sys/class/switch/{headset,balance}/state`(节流读缓存:launcher 1s / watchdog 500ms,绝不在热路径每圈读文件),插孔状态变化后 **800ms 内忽略音量键边沿**(vol_ramp_begin 不起、在爬 ramp 中止、watchdog 单步忽略);另有 **ramp 总时长上限 VOL_RAMP_MAX_MS=3000** 兜底,防任何「持续按住」假象一次打满。SDK 输入层(框架 App 前台时的音量路径)需同款消抖——sdk/ 归其主,另行落地。
|
||||
|
||||
真机验收要点(§7):应用内电源单击/长按菜单/8s 强关、音量±调音量、播放/上下曲无反应(被抓住)、launcher 菜单同样一致、电源菜单任何界面可弹(含应用内)、息屏后任意键唤醒。
|
||||
@@ -153,8 +153,65 @@ static unsigned long long vol_ramp_next_at = 0;
|
||||
static unsigned long long vol_ramp_press_at = 0; /* M2.18 Bug 4:本方向键按下时刻(长按判据) */
|
||||
|
||||
|
||||
/* ---- 插孔消抖(2026-09-01,真机日志实证)----
|
||||
* 4.4/3.5 耳机插拔的电气瞬态在音量键 ADC(jz_adc_aux,event2)上产生 VOL_UP 幽灵按住
|
||||
* (EVIOCGKEY 轮询到持续 down),vol_ramp 一路爬到上限:日志 225391「volume UP <- ramp」
|
||||
* → 227213「ramp end -> 50」,紧接着 audiod 才报「route: jack changed -> 4.4 balance」。
|
||||
* 修法:/sys/class/switch/{headset,balance}/state 任一变化后 JACK_DEBOUNCE_MS 内忽略音量键
|
||||
* 边沿(vol_ramp_begin 不起、vol_ramp_tick 在爬也中止)。插拔耳机时没人按音量,误伤可忽略。
|
||||
* 检测走 1s 节流读缓存——绝不在 10ms 主循环热路径上每圈读文件。 */
|
||||
/* 宿主测试可 -D 覆盖(同 meow_settings.c MEOW_SETTINGS_FILE 惯例) */
|
||||
#ifndef JACK_HS_STATE
|
||||
#define JACK_HS_STATE "/sys/class/switch/headset/state"
|
||||
#endif
|
||||
#ifndef JACK_BAL_STATE
|
||||
#define JACK_BAL_STATE "/sys/class/switch/balance/state"
|
||||
#endif
|
||||
|
||||
static int jack_combo_cached = -1; /* hs*2+bal;-1=未初始化/节点缺失 */
|
||||
|
||||
static unsigned long long jack_poll_at = 0; /* 上次节流读时刻 */
|
||||
|
||||
static unsigned long long jack_changed_at = 0; /* 最近一次检测到插孔变化的时刻(0=无) */
|
||||
|
||||
|
||||
/* 读两个插孔开关节点合成 hs*2+bal;两节点都不可读返回 -1(不更新缓存,不误判变化)。 */
|
||||
static int jack_combo_read(void)
|
||||
{
|
||||
int hs = read_file_int(JACK_HS_STATE);
|
||||
int bal = read_file_int(JACK_BAL_STATE);
|
||||
if (hs < 0 && bal < 0) return -1;
|
||||
return (hs > 0 ? 2 : 0) | (bal > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
|
||||
/* 返回 1=插孔变化后消抖窗口内(音量键边沿应忽略/在爬 ramp 应中止)。
|
||||
* 自节流:每 JACK_POLL_MS 才真正读一次 sysfs。 */
|
||||
static int jack_debounce_active(void)
|
||||
{
|
||||
unsigned long long now = now_ms();
|
||||
if (now - jack_poll_at >= JACK_POLL_MS) {
|
||||
jack_poll_at = now;
|
||||
int c = jack_combo_read();
|
||||
if (c >= 0) {
|
||||
if (jack_combo_cached >= 0 && c != jack_combo_cached) {
|
||||
jack_changed_at = now;
|
||||
logmsg("jack state changed (%d -> %d), vol-key debounce %dms",
|
||||
jack_combo_cached, c, JACK_DEBOUNCE_MS);
|
||||
}
|
||||
jack_combo_cached = c;
|
||||
}
|
||||
}
|
||||
return jack_changed_at && (now - jack_changed_at < (unsigned long long)JACK_DEBOUNCE_MS);
|
||||
}
|
||||
|
||||
|
||||
static void vol_ramp_begin(int delta)
|
||||
{
|
||||
if (jack_debounce_active()) { /* 插孔瞬态期:忽略音量键边沿(幽灵按下) */
|
||||
logmsg("volume adj %+d ignored (jack debounce)", delta);
|
||||
return;
|
||||
}
|
||||
int target = vol_clamp(settings_volume + delta);
|
||||
if (target == settings_volume) return;
|
||||
vol_ramp_dir = (target > settings_volume) ? 1 : -1;
|
||||
@@ -175,6 +232,22 @@ int vol_ramp_tick(int key_held)
|
||||
unsigned long long now = now_ms();
|
||||
if (now < vol_ramp_next_at) return 1;
|
||||
vol_ramp_next_at = now + VOL_RAMP_MS;
|
||||
/* 插孔消抖:插拔瞬态的幽灵按住正在驱动 ramp(真机实证 24%→上限 50)——
|
||||
* 一旦检测到插孔变化(1s 节流轮询),立即中止在爬的 ramp。 */
|
||||
if (jack_debounce_active()) {
|
||||
vol_ramp_dir = 0; vol_ramp_target = -1;
|
||||
meow_settings_save();
|
||||
logmsg("volume ramp aborted (jack debounce) -> %d", settings_volume);
|
||||
return 0;
|
||||
}
|
||||
/* 兜底:ramp 总时长上限 VOL_RAMP_MAX_MS——防任何「持续按住」假象(ADC 噪声卡 key_held=1)
|
||||
* 一次打满/打到底。正常长按 0→上限(通常 ≤2.7s)不受影响;到顶想再调需松手重按。 */
|
||||
if (now - vol_ramp_press_at >= (unsigned long long)VOL_RAMP_MAX_MS) {
|
||||
vol_ramp_dir = 0; vol_ramp_target = -1;
|
||||
meow_settings_save();
|
||||
logmsg("volume ramp force-stop (%dms cap) -> %d", VOL_RAMP_MAX_MS, settings_volume);
|
||||
return 0;
|
||||
}
|
||||
/* 先看是否已到「当前目标值」(settings_volume 等于 target,而非候选 v)——
|
||||
* 因为一旦在 target 再取 v=settings_volume+dir 便越过 target,导致 hold_wait 失效继续爬。 */
|
||||
if (settings_volume == vol_ramp_target) {
|
||||
|
||||
@@ -41,7 +41,7 @@ void apply_user_brightness(void)
|
||||
}
|
||||
int v = meow_settings_brightness_clamped();
|
||||
if (v <= 0) v = 60; /* 防御:clamp 后不可能 ≤0,仅兜底 */
|
||||
write_brightness(v);
|
||||
write_brightness(v, "apply_user_brightness");
|
||||
g_sys.brightness = v;
|
||||
meow_brightness_pending = 0;
|
||||
logmsg("apply user brightness (MeowOS takeover) -> %d", v);
|
||||
@@ -73,13 +73,16 @@ static void write_file(const char *path, const char *val)
|
||||
}
|
||||
|
||||
|
||||
void write_brightness(int val)
|
||||
void write_brightness(int val, const char *src)
|
||||
{
|
||||
/* 亮度幽灵排查(2026-09-01:用户报告插耳机瞬间亮度自己跳到 raw 上限,写者未知):
|
||||
* 每次写背光都留值+调用源线索,下次「亮度自己跳」可直接定位写者。 */
|
||||
char buf[8];
|
||||
int l = snprintf(buf, sizeof buf, "%d\n", val);
|
||||
int fd = open(BACKLIGHT, O_WRONLY);
|
||||
if (fd < 0) { logmsg("backlight open fail: %s", strerror(errno)); return; }
|
||||
if (write(fd, buf, l) < 0) logmsg("backlight write %d: %s", val, strerror(errno));
|
||||
if (fd < 0) { logmsg("backlight <- %d (src=%s) open fail: %s", val, src, strerror(errno)); return; }
|
||||
if (write(fd, buf, l) < 0) logmsg("backlight <- %d (src=%s): %s", val, src, strerror(errno));
|
||||
else logmsg("backlight <- %d (src=%s)", val, src);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
@@ -107,7 +110,7 @@ void ensure_backlight(void)
|
||||
}
|
||||
int target = (saved_brightness > 0) ? saved_brightness : 60;
|
||||
logmsg("backlight off (cur=%d bl_power=%d) -> %d", cur, blp, target);
|
||||
if (cur <= 0) write_brightness(target);
|
||||
if (cur <= 0) write_brightness(target, "ensure_backlight");
|
||||
if (blp > 0) write_file(BACKLIGHT_POWER, "0\n");
|
||||
}
|
||||
|
||||
@@ -126,7 +129,7 @@ void restore_backlight(void)
|
||||
{
|
||||
if (menu_opened_blank) {
|
||||
logmsg("restore: panel was blanked at menu open, re-blank (0 + FBIOBLANK 4)");
|
||||
write_brightness(0);
|
||||
write_brightness(0, "restore_backlight(re-blank)");
|
||||
int fbfd = open(FB_DEV, O_RDWR);
|
||||
if (fbfd >= 0) { ioctl(fbfd, FBIOBLANK, 4); close(fbfd); }
|
||||
menu_opened_blank = 0;
|
||||
@@ -138,7 +141,7 @@ void restore_backlight(void)
|
||||
int target = (saved_brightness > 0) ? saved_brightness : 60;
|
||||
logmsg("restore backlight (cur=%d bl_power=%d) -> %d (saved=%d)",
|
||||
cur, blp, target, saved_brightness);
|
||||
if (cur <= 0) write_brightness(target);
|
||||
if (cur <= 0) write_brightness(target, "restore_backlight");
|
||||
if (blp > 0) write_file(BACKLIGHT_POWER, "0\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ void meow_panel_off(void)
|
||||
{
|
||||
meow_saved_brightness = read_brightness();
|
||||
if (meow_saved_brightness <= 0) meow_saved_brightness = 60;
|
||||
write_brightness(0);
|
||||
write_brightness(0, "meow_panel_off");
|
||||
if (screen_off_mode == 0) {
|
||||
/* panel:面板下电 + FBIOPBLANK(4)(同时挂起 goodix 触摸,无误触) */
|
||||
int f = open(FB_DEV, O_RDWR);
|
||||
@@ -58,7 +58,7 @@ void meow_panel_on(void)
|
||||
{
|
||||
int f = open(FB_DEV, O_RDWR);
|
||||
if (f >= 0) { ioctl(f, FBIOBLANK, 0); close(f); }
|
||||
write_brightness(meow_saved_brightness > 0 ? meow_saved_brightness : 60);
|
||||
write_brightness(meow_saved_brightness > 0 ? meow_saved_brightness : 60, "meow_panel_on");
|
||||
meow_screen_on = 1;
|
||||
/* M2.8 Bug 3-residual:亮屏必须用 fb_pan_buf0_force 无条件重发 pan。fb_norm_buf0 在
|
||||
* 缓存 fb_active_yoffset==0 时提前返回,而 FBIOBLANK(4)->(0) 后 LCDC 扫描地址可能已复位
|
||||
|
||||
@@ -140,6 +140,9 @@ extern "C" {
|
||||
#define MEOW_VOL_MAX_DEFAULT 100 /* 默认最大音量上限(尊重上限;可配 audio.volume_max) */
|
||||
#define VOL_DRAG_DAC_MS 60 /* 滑条拖动期间两次真实 DAC 写入的最短墙钟间隔 */
|
||||
#define VOL_RAMP_MS 15 /* 每 1% 步进的最短墙钟间隔 */
|
||||
#define VOL_RAMP_MAX_MS 3000 /* ramp 总时长上限(防 ADC 噪声假按住一次打满;正常长按到上限 <3s) */
|
||||
#define JACK_POLL_MS 1000 /* 插孔状态节流读取间隔(绝不在 10ms 热路径每圈读文件) */
|
||||
#define JACK_DEBOUNCE_MS 800 /* 插孔变化后忽略音量键边沿的窗口(插拔时没人按音量,误伤可忽略) */
|
||||
#define BT_ROLE_OFF 0 /* 关 */
|
||||
#define BT_ROLE_ON_NO_CONN 1 /* 开未连接 */
|
||||
#define BT_ROLE_MASTER 2 /* 主模式:连了外围(如耳机) */
|
||||
@@ -759,7 +762,7 @@ void vol_restore(struct vol_snap s);
|
||||
struct vol_snap vol_snapshot(void);
|
||||
int wlan_connected(void);
|
||||
int wlan_radio_up(void);
|
||||
void write_brightness(int val);
|
||||
void write_brightness(int val, const char *src); /* src=调用源线索,随值一并 logmsg(亮度幽灵排查) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -386,11 +386,11 @@ static int ncp_chip_apply(int chip)
|
||||
int vmin = 1; meow_settings_get_int("display.brightness_min", &vmin);
|
||||
if (mnp_brightness_clamp(cur, vmin) < 100) {
|
||||
meow_settings_set_int("display.brightness", 100);
|
||||
write_brightness(100);
|
||||
write_brightness(100, "notif_center bright_chip");
|
||||
g_sys.brightness = 100;
|
||||
} else {
|
||||
meow_settings_set_int("display.brightness", vmin);
|
||||
write_brightness(vmin);
|
||||
write_brightness(vmin, "notif_center bright_chip");
|
||||
g_sys.brightness = vmin;
|
||||
}
|
||||
paint_sb_right();
|
||||
@@ -865,7 +865,7 @@ static int ncp_slider_apply(int which, int tx, int save_now)
|
||||
int cur = 0; meow_settings_get_int("display.brightness", &cur);
|
||||
if (!save_now && v == mnp_brightness_clamp(cur, bmin)) return 0; /* 去抖:值未变(含 clamp 后) */
|
||||
meow_settings_set_int("display.brightness", v);
|
||||
write_brightness(v);
|
||||
write_brightness(v, "notif_center bright_slider");
|
||||
g_sys.brightness = v; /* 状态栏数值源=最近写入值(含 clamp 后) */
|
||||
paint_sb_right();
|
||||
if (save_now) meow_settings_save();
|
||||
|
||||
@@ -373,7 +373,7 @@ static void s_commit(const srow_t *r, int save_now)
|
||||
* 修法:写背光后同步 g_sys.brightness 并 paint_sb_right()(≡sb_redraw 整条标脏)。
|
||||
* 一致性:其后 meow_sys_refresh 从 sysfs 读回的也是刚写入的 v,二者一致不打架。 */
|
||||
int v; if (meow_settings_get_int("display.brightness", &v) == 0) {
|
||||
write_brightness(v);
|
||||
write_brightness(v, "settings_page bright_slider");
|
||||
g_sys.brightness = v;
|
||||
paint_sb_right();
|
||||
}
|
||||
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,189 @@
|
||||
/* test_vol_ramp_jack.c — 宿主单测:音量 ramp 的插孔消抖 + 时长上限(2026-09-01)。
|
||||
*
|
||||
* 背景(真机日志实证):4.4 耳机插入瞬间,插孔电气瞬态在音量键 ADC(jz_adc_aux,
|
||||
* event2)上产生 VOL_UP 幽灵按住,launcher vol_ramp 从 24% 一路爬到上限 50
|
||||
* (225391「volume UP <- ramp」→ 227213「ramp end -> 50」,紧随 audiod「jack changed」)。
|
||||
*
|
||||
* 编译运行(在 patch_v2/meow_launcherd 下):
|
||||
* gcc -Wall -Wextra -Os -I../sdk \
|
||||
* -DJACK_HS_STATE='"/tmp/tvrj_hs"' -DJACK_BAL_STATE='"/tmp/tvrj_bal"' \
|
||||
* test_vol_ramp_jack.c -o test_vol_ramp_jack && ./test_vol_ramp_jack
|
||||
* 全 PASS -> exit 0。
|
||||
*
|
||||
* 手法:#include "audio.c" 同 TU 平移(同 test_notif_tf.c 惯例);now_ms 用假时钟
|
||||
* (手动推进,3s 上限与 1s 节流都可瞬时测);meow_settings_* 打内存桩。 */
|
||||
|
||||
/* ---- 桩(先定义,audio.c 经内部头拿到声明) ---- */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
static unsigned long long g_fake_now = 100000; /* 假时钟(ms),手动推进 */
|
||||
|
||||
unsigned long long now_ms(void); /* 前置声明,防与头文件顺序问题 */
|
||||
unsigned long long now_ms(void) { return g_fake_now; }
|
||||
static void advance_ms(unsigned long long d) { g_fake_now += d; }
|
||||
|
||||
void logmsg(const char *fmt, ...);
|
||||
void logmsg(const char *fmt, ...)
|
||||
{
|
||||
va_list ap; va_start(ap, fmt);
|
||||
fprintf(stderr, " [log] "); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n");
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/* meow_settings 内存桩:audio.c 只用 get_int("audio.volume*")/set_int/save */
|
||||
static int g_store_volume = 24;
|
||||
static int g_store_max = 50;
|
||||
static int g_store_step = 1;
|
||||
static int g_save_count = 0;
|
||||
|
||||
extern int settings_volume; /* audio.c 的缓存全局(同 TU #include 后链接可见) */
|
||||
extern int settings_volume_max;
|
||||
extern int settings_volume_step;
|
||||
|
||||
int meow_settings_get_int(const char *key, int *out)
|
||||
{
|
||||
if (!strcmp(key, "audio.volume")) { *out = g_store_volume; return 0; }
|
||||
if (!strcmp(key, "audio.volume_max")) { *out = g_store_max; return 0; }
|
||||
if (!strcmp(key, "audio.volume_step")) { *out = g_store_step; return 0; }
|
||||
return -1;
|
||||
}
|
||||
int meow_settings_get_enum(const char *key, int *out) { (void)key; (void)out; return -1; }
|
||||
int meow_settings_get_bool(const char *key, int *out) { (void)key; (void)out; return -1; }
|
||||
int meow_settings_set_int(const char *key, int v)
|
||||
{
|
||||
/* 真代码经 settings_sync_cache_listener 把 backend 值同步到 audio.c 的 settings_volume
|
||||
* 缓存全局;桩里直接同步,保持同一语义。 */
|
||||
if (!strcmp(key, "audio.volume")) { g_store_volume = v; settings_volume = v; return 0; }
|
||||
return -1;
|
||||
}
|
||||
int meow_settings_save(void) { g_save_count++; return 0; }
|
||||
|
||||
/* util.c 符号桩(audio.c 引用,宿主无 util.c) */
|
||||
int read_file_int(const char *path);
|
||||
int read_file_int(const char *path)
|
||||
{
|
||||
FILE *f = fopen(path, "r");
|
||||
if (!f) return -1;
|
||||
int v = -1;
|
||||
if (fscanf(f, "%d", &v) != 1) v = -1;
|
||||
fclose(f);
|
||||
return v;
|
||||
}
|
||||
|
||||
/* 其它模块全局桩(settings_sync_cache_* 引用;本测试不走该路径,仅满足链接) */
|
||||
int screen_off_timeout = 0;
|
||||
int screen_off_mode = 0;
|
||||
int power_longpress_menu_ms = 0;
|
||||
int power_forceoff_ms = 0;
|
||||
int refresh_rate_cfg = 60;
|
||||
int refresh_always_max = 0;
|
||||
|
||||
#include "audio.c"
|
||||
|
||||
static int g_pass = 0, g_fail = 0;
|
||||
static void check(const char *name, int got, int want)
|
||||
{
|
||||
if (got == want) { g_pass++; printf("PASS %s\n", name); }
|
||||
else { g_fail++; printf("FAIL %s: got %d want %d\n", name, got, want); }
|
||||
}
|
||||
|
||||
static void write_file(const char *path, int v)
|
||||
{
|
||||
FILE *f = fopen(path, "w");
|
||||
if (!f) { perror(path); exit(2); }
|
||||
fprintf(f, "%d\n", v);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
/* 驱动 ramp 直到停止或步数超限;held=key_held 恒值;每步推进 40ms
|
||||
* (模拟真机每步 ~52ms 的 amixer system() 开销:0→100 全程 ~4s,必然越过 3s 上限,
|
||||
* 而常见 24→50 窗口 ~1s 不受影响)。 */
|
||||
static int drive_ramp(int held, int max_steps)
|
||||
{
|
||||
int steps = 0;
|
||||
while (vol_ramp_dir != 0 && steps < max_steps) {
|
||||
advance_ms(40);
|
||||
vol_ramp_tick(held);
|
||||
steps++;
|
||||
}
|
||||
return steps;
|
||||
}
|
||||
|
||||
/* 场景起始音量:backend 桩与 audio.c 缓存全局一起置(真代码由 settings_load 同步)。 */
|
||||
static void set_vol(int v) { g_store_volume = v; settings_volume = v; }
|
||||
|
||||
/* 音量上限/跳步同理:vol_clamp 读的是 audio.c 的 settings_volume_max/settings_volume_step。 */
|
||||
static void set_max(int v) { g_store_max = v; settings_volume_max = v; }
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* T0 基线回归:无插孔节点(文件不存在)→ 单击起 ramp,到 target 即停(只跳 step 格)。 */
|
||||
unlink(JACK_HS_STATE); unlink(JACK_BAL_STATE);
|
||||
set_vol(24); set_max(50); g_store_step = 1; g_save_count = 0;
|
||||
settings_volume_adj(+1);
|
||||
check("T0 ramp started", vol_ramp_dir != 0, 1);
|
||||
drive_ramp(0, 100);
|
||||
check("T0 click stops at target 25", g_store_volume, 25);
|
||||
check("T0 ramp stopped", vol_ramp_dir, 0);
|
||||
|
||||
/* T1 长按续爬到上限 50(无插孔干扰,正常长按体验不受消抖影响)。 */
|
||||
set_vol(24); g_save_count = 0;
|
||||
settings_volume_adj(+1);
|
||||
drive_ramp(1, 5000); /* 24→50=26 步 ×40ms≈1s,远未到 3s 上限 */
|
||||
check("T1 long-press reaches max 50", g_store_volume, 50);
|
||||
check("T1 stopped at max", vol_ramp_dir, 0);
|
||||
|
||||
/* T2 时长上限:max 提到 100(0→100 需 ~100 步 >3s 窗)→ 3s 强制停,不打满。
|
||||
* 假时钟 40ms/步 → 3s=75 步 <100 步(4s);drive 300 步必触发 cap。 */
|
||||
set_vol(0); set_max(100); g_save_count = 0;
|
||||
settings_volume_adj(+1);
|
||||
drive_ramp(1, 300);
|
||||
check("T2 ramp force-stopped by cap", vol_ramp_dir, 0);
|
||||
check("T2 volume below max (not pinned)", g_store_volume < 100, 1);
|
||||
check("T2 volume climbed (>0)", g_store_volume > 0, 1);
|
||||
printf(" (T2 stopped at volume=%d after cap)\n", g_store_volume);
|
||||
|
||||
/* T3 插孔消抖:prime 插孔状态(hs=0,bal=0),随后模拟插入 4.4(bal=1)。
|
||||
* 变化后 800ms 内:vol_ramp_begin 被忽略;在爬的 ramp 被 tick 中止。 */
|
||||
set_max(50); set_vol(24);
|
||||
write_file(JACK_HS_STATE, 0); write_file(JACK_BAL_STATE, 0);
|
||||
advance_ms(JACK_POLL_MS + 10);
|
||||
settings_volume_adj(+1); /* 这次调用顺带 prime 插孔缓存(无变化→不消抖) */
|
||||
drive_ramp(0, 100);
|
||||
check("T3 prime: normal click still works", g_store_volume, 25);
|
||||
|
||||
write_file(JACK_BAL_STATE, 1); /* 模拟 4.4 插入 */
|
||||
advance_ms(JACK_POLL_MS + 10); /* 过节流窗,下次检测即发现变化 */
|
||||
set_vol(24); g_save_count = 0;
|
||||
settings_volume_adj(+1); /* 此调用检测变化 → 边沿被忽略 */
|
||||
check("T3 press ignored in debounce window", vol_ramp_dir, 0);
|
||||
check("T3 volume untouched", g_store_volume, 24);
|
||||
|
||||
/* T3b 在爬的 ramp 被插孔变化中止(幽灵按住场景:ramp 已起,随后检测到插孔变化)。 */
|
||||
/* 先让消抖窗过期:推进 >800ms,再正常起 ramp(长按)。 */
|
||||
advance_ms(JACK_DEBOUNCE_MS + 100);
|
||||
set_vol(24);
|
||||
settings_volume_adj(+1);
|
||||
check("T3b ramp restarted after window", vol_ramp_dir, 1);
|
||||
vol_ramp_tick(1); advance_ms(20); vol_ramp_tick(1); /* 爬了两步 */
|
||||
int before = g_store_volume;
|
||||
write_file(JACK_HS_STATE, 1); /* 又插 3.5(变化) */
|
||||
advance_ms(JACK_POLL_MS + 10); /* 过节流窗 */
|
||||
vol_ramp_tick(1); /* 检测到变化 → 中止 */
|
||||
check("T3b in-flight ramp aborted by jack change", vol_ramp_dir, 0);
|
||||
check("T3b volume froze at abort point", g_store_volume, before);
|
||||
printf(" (T3b aborted at volume=%d)\n", g_store_volume);
|
||||
|
||||
/* T3c 消抖窗过后恢复正常(误伤可忽略验证)。 */
|
||||
advance_ms(JACK_DEBOUNCE_MS + 100);
|
||||
settings_volume_adj(+1);
|
||||
check("T3c press works again after window", vol_ramp_dir, 1);
|
||||
drive_ramp(0, 100);
|
||||
|
||||
unlink(JACK_HS_STATE); unlink(JACK_BAL_STATE);
|
||||
printf("\n%d pass, %d fail\n", g_pass, g_fail);
|
||||
return g_fail ? 1 : 0;
|
||||
}
|
||||
Binary file not shown.
@@ -20,6 +20,8 @@
|
||||
* 让位,绝不与 launcher 抢屏/抢键);launcher 画菜单,本进程不自绘。
|
||||
* 电源长按 ≥8s -> 强制关机兜底(仅当菜单未生效/launcher 未接管)。
|
||||
* 音量±(115/114) -> 写真实 DAC(CS43198 amixer numid=1/2)+ 持久化 settings.conf(同 launcher 同源)。
|
||||
* 插孔消抖:/sys/class/switch/{headset,balance} 变化后 800ms 内忽略音量键边沿
|
||||
* (插拔电气瞬态会在 ADC 键上产生幽灵按下,真机实证 2026-09-01)。
|
||||
* 播放/上下曲(164/165/163/19) -> 捕获 + 日志 + 消费,不路由(媒体会话落地后转活跃会话)。
|
||||
* (8s 强关的「防误触 / pm_interacted」逻辑在 launcher 的 show_power_menu 内,此进程只做兜底。)
|
||||
* - M2.86(226) SDK 框架 App 让位:启动时经 /proc/<pid>/exe 读同目录 app.json 的 framework
|
||||
@@ -46,7 +48,7 @@
|
||||
#include <signal.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/poll.h>
|
||||
#include <poll.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
@@ -379,6 +381,55 @@ static void wd_vol_save(void)
|
||||
wd_log("vol: saved volume=%d(max=%d,step=%d)", wd_vol, wd_vol_max, wd_vol_step);
|
||||
}
|
||||
|
||||
/* ---- 插孔消抖(2026-09-01,与 launcher audio.c 同款,真机日志实证)----
|
||||
* 4.4/3.5 耳机插拔的电气瞬态在音量键 ADC(jz_adc_aux,event2)上产生幽灵按下
|
||||
* (launcher 侧实证:VOL_UP 假按住 → ramp 24%→上限 50,紧随 audiod「jack changed」)。
|
||||
* /sys/class/switch/{headset,balance}/state 任一变化后 WD_JACK_DEBOUNCE_MS 内忽略音量键
|
||||
* 边沿(插拔耳机时没人按音量,误伤可忽略)。读文件走节流缓存,不每圈读。 */
|
||||
#define WD_JACK_HS_STATE "/sys/class/switch/headset/state"
|
||||
#define WD_JACK_BAL_STATE "/sys/class/switch/balance/state"
|
||||
#define WD_JACK_POLL_MS 500 /* 节流读取间隔(主循环 100ms,5 圈一读) */
|
||||
#define WD_JACK_DEBOUNCE_MS 800 /* 插孔变化后忽略音量键边沿的窗口 */
|
||||
|
||||
static int wd_jack_combo = -1; /* hs*2+bal;-1=未初始化/节点缺失 */
|
||||
static unsigned long long wd_jack_poll_at = 0;
|
||||
static unsigned long long wd_jack_changed_at = 0; /* 0=未检测到变化 */
|
||||
|
||||
static int wd_read_int_file(const char *path)
|
||||
{
|
||||
int fd = open(path, O_RDONLY);
|
||||
if (fd < 0) return -1;
|
||||
char buf[16];
|
||||
int n = read(fd, buf, sizeof buf - 1);
|
||||
close(fd);
|
||||
if (n <= 0) return -1;
|
||||
buf[n] = 0;
|
||||
return atoi(buf);
|
||||
}
|
||||
|
||||
static void wd_jack_tick(void)
|
||||
{
|
||||
unsigned long long now = now_ms();
|
||||
if (now - wd_jack_poll_at < WD_JACK_POLL_MS) return;
|
||||
wd_jack_poll_at = now;
|
||||
int hs = wd_read_int_file(WD_JACK_HS_STATE);
|
||||
int bal = wd_read_int_file(WD_JACK_BAL_STATE);
|
||||
if (hs < 0 && bal < 0) return; /* 节点缺失:不更新缓存,不误判变化 */
|
||||
int c = (hs > 0 ? 2 : 0) | (bal > 0 ? 1 : 0);
|
||||
if (wd_jack_combo >= 0 && c != wd_jack_combo) {
|
||||
wd_jack_changed_at = now;
|
||||
wd_log("jack state changed (%d -> %d), vol-key debounce %dms",
|
||||
wd_jack_combo, c, WD_JACK_DEBOUNCE_MS);
|
||||
}
|
||||
wd_jack_combo = c;
|
||||
}
|
||||
|
||||
static int wd_jack_debounce(void)
|
||||
{
|
||||
return wd_jack_changed_at &&
|
||||
(now_ms() - wd_jack_changed_at < (unsigned long long)WD_JACK_DEBOUNCE_MS);
|
||||
}
|
||||
|
||||
/* 音量步进:+1/-1(每按下边沿一次;clamp 到极大/最小)。 */
|
||||
static void wd_vol_adj(int dir)
|
||||
{
|
||||
@@ -612,10 +663,17 @@ int main(int argc, char **argv)
|
||||
} else if (type == EV_KEY && code == KEY_POWER) {
|
||||
handle_power(val); /* M2.22(A)/M2.24:电源单击=息屏/亮屏,长按 tick 里判菜单 */
|
||||
} else if (type == EV_KEY && code == KEY_VOLUMEUP) {
|
||||
/* M2.86(226):SDK 框架 App 前台 -> 音量键让位 SDK(防双步进)。 */
|
||||
if (val == 1 && !g_fw_app) wd_vol_adj(+1); /* M2.24:音量+ = 系统音量 */
|
||||
/* M2.86(226):SDK 框架 App 前台 -> 音量键让位 SDK(防双步进)。
|
||||
* 插孔消抖:插拔瞬态的 ADC 幽灵按下直接忽略。 */
|
||||
if (val == 1 && !g_fw_app) {
|
||||
if (wd_jack_debounce()) wd_log("volume UP ignored (jack debounce)");
|
||||
else wd_vol_adj(+1); /* M2.24:音量+ = 系统音量 */
|
||||
}
|
||||
} else if (type == EV_KEY && code == KEY_VOLUMEDOWN) {
|
||||
if (val == 1 && !g_fw_app) wd_vol_adj(-1); /* M2.24:音量- = 系统音量 */
|
||||
if (val == 1 && !g_fw_app) {
|
||||
if (wd_jack_debounce()) wd_log("volume DOWN ignored (jack debounce)");
|
||||
else wd_vol_adj(-1); /* M2.24:音量- = 系统音量 */
|
||||
}
|
||||
} else if (type == EV_KEY && (code == KEY_PLAYPAUSE || code == KEY_PREVIOUSSONG ||
|
||||
code == KEY_NEXTSONG || code == KEY_PREVIOUS)) {
|
||||
/* M2.86(226):框架 App 前台 -> 媒体键让位 SDK(接媒体会话路由)。 */
|
||||
@@ -626,6 +684,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
wd_power_tick(); /* M2.24:电源长按检测(3s 菜单 / 8s 强关 / 菜单期让位) */
|
||||
wd_jack_tick(); /* 插孔状态节流轮询(消抖音量键幽灵按下) */
|
||||
elapsed += 100;
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
|
||||
# ---- 用户态/服务守护二进制(usr/bin,全部 chmod 755 静态 MIPS)----
|
||||
patch_v2/bootmenu/meow_bootmenu|/usr/bin/meow_bootmenu|4441822d87e76bfce2ce584604ac3830|引导选单 M2.85(sysui让位 agent-216)
|
||||
patch_v2/meow_launcherd/meow_launcherd|/usr/bin/meow_launcherd|2aa6178090837793957b815ff2f108ba|主屏 launcher(+充电态权威源 mp2731-charger/status,修 battery/status 撒谎恒 Discharging)
|
||||
patch_v2/meow_watchdog/meow_watchdog|/usr/bin/meow_watchdog|6373d8e5cac62a34ee468a344b267761|应用看门狗 M2.89(SIGUSR1 仅裸沉浸 App,框架 App 不误杀 agent-236)
|
||||
patch_v2/meow_launcherd/meow_launcherd|/usr/bin/meow_launcherd|7be1d4eb786ff08f4b20f3be775a8151|主屏 launcher(插孔消抖+ramp 3s 上限+亮度写日志;耳机插拔 ADC 幽灵音量键修复)
|
||||
patch_v2/meow_watchdog/meow_watchdog|/usr/bin/meow_watchdog|baa997ea9100b04e1cc96e25c16f7e4b|应用看门狗(插孔消抖:插拔瞬态忽略音量键 800ms;M2.89 SIGUSR1 仅裸沉浸 App)
|
||||
patch_v2/meow_svcd/meow_svcd|/usr/bin/meow_svcd|605646c4cc93f2baf29071ea6bc870ea|服务管理器 M2.77(+崩溃上报 meow_crash agent-187)
|
||||
mips-repo/bins/meow_mdns/bin/meow_mdns|/usr/bin/meow_mdns|d6b0151bda26768f19952dae1342610d|mDNS 应答器 M2.78(启动竞态+换IP重检 agent-194)
|
||||
patch_v2/meow_sysui/meow_sysui|/usr/bin/meow_sysui|173013cc8f0899b3001d0b894ac0d9e6|系统 UI(sysui)
|
||||
|
||||
Reference in new issue
Block a user