diff --git a/patch_v2/meow_audiod/build_audiod.sh b/patch_v2/meow_audiod/build_audiod.sh index 6eaf344..9743c13 100755 --- a/patch_v2/meow_audiod/build_audiod.sh +++ b/patch_v2/meow_audiod/build_audiod.sh @@ -1,29 +1,41 @@ #!/bin/bash -# build_audiod.sh — meow_audiod 交叉编译(mipsel 静态)+ 宿主多目标可选。 +# build_audiod.sh — meow_audiod 交叉编译(mipsel glibc-dynamic)+ 宿主多目标可选。 # -# 目标:R3 Pro II / MeowOS(mipsel-linux-muslsf,静态)。audiod 复用 meow_ipc.h(共享协议头, +# 目标:R3 Pro II / MeowOS(MIPS32 o32,真实 ABI)。audiod 复用 meow_ipc.h(共享协议头, # -I../meow_launcherd)。audiod 为 svcd 托管的独立守护;meow_audio 为用户侧 CLI。 # +# 【P2 蓝牙链接定案 2026-09-03】设备为 glibc-2.22(ld-2.22.so / libc-2.22.so,非 musl),且 +# libasound 会在运行时 dlopen 单独的 bluealsa pcm 插件(/usr/lib/alsa-lib/libasound_module_pcm_bluealsa.so) +# ——故 audiod 必须以**动态 glibc** 形态链接设备现有 libasound.so.2: +# - 用 zig --target=mipsel-linux-gnueabihf.2.22(硬浮点 o32,glibc-2.22 版本引用,设备 libs 齐); +# libasound 原型在 meow_audiod.c 内联声明(不依赖 asoundlib.h)。 +# - 删 -static:musl-static 无 dlopen(实测 "Dynamic loading not supported",无法加载 bluealsa 插件); +# musl-dynamic 需设备不存在的 musl ld.so。libasound 链接库来自 libbt/(设备 libasound.so.2 副本)。 +# - meow_audio CLI 无 libasound 依赖,保持 musl-static(zig-mipsel-cc)。 +# # 用法: -# ./build_audiod.sh # 交叉编译 → meow_audiod(mipsel 静态) -# ./build_audiod.sh cli # 交叉编译 meow_audio CLI → meow_audio(mipsel 静态) -# ./build_audiod.sh host # 宿主编译 meow_audiod_host + meow_audio_host(集成测试) +# ./build_audiod.sh # 交叉编译 → meow_audiod(mipsel glibc-dynamic,BT 直链 libasound) +# ./build_audiod.sh cli # 交叉编译 meow_audio CLI → meow_audio(mipsel musl-static) +# ./build_audiod.sh host # 宿主编译 *_host 并跑 test_audiod_host 回归(FILE sink) set -e cd "$(dirname "$0")" ROOT="$HOME/Project/R3ProII" CC="$ROOT/.tools/zig-mipsel-cc" +ZIG="$ROOT/.tools/zig-env/bin/zig" INC="-I$ROOT/patch_v2/meow_launcherd" if [ "$1" = "host" ]; then HOST_CC="${CC_HOST:-cc}" - # -ldl:BT 输出目标用 dlopen 运行时解析 libasound(pcm.bluealsa 是 alsa-lib 插件,非内核节点; - # 主机/交叉工具链无 asoundlib.h/libasound,故不静态链接)。mipsel 静态 musl 不支持 dlopen,见 - # meow_audiod.c bt 段注释(真机 mipsel 需主会话提供 musl-libasound 或改动态,本任务以源码+宿主为准)。 - "$HOST_CC" $INC -Os -Wall -Wextra meow_audiod.c -o meow_audiod_host -lpthread -ldl + # BT 目标直链宿主 libasound.so.2(无 asoundlib.h,原型内联于 meow_audiod.c;-l: 精确到版本, + # 不依赖 unversioned dev 链接)。删 -ldl:BT 不再用 dlopen。 + "$HOST_CC" $INC -Os -Wall -Wextra meow_audiod.c -o meow_audiod_host -lpthread -l:libasound.so.2 "$HOST_CC" $INC -Os -Wall -Wextra meow_audio.c -o meow_audio_host -lm + "$HOST_CC" $INC -Os -Wall -Wextra test_audiod_host.c -o test_audiod_host -lm echo "== host builds ==" - ls -la meow_audiod_host meow_audio_host - file meow_audiod_host meow_audio_host + ls -la meow_audiod_host meow_audio_host test_audiod_host + file meow_audiod_host meow_audio_host test_audiod_host + echo "== host regression (test_audiod_host) ==" + ./test_audiod_host || { echo "!! host regression FAILED"; exit 1; } exit 0 fi @@ -36,8 +48,21 @@ if [ "$1" = "cli" ]; then exit 0 fi -"$CC" $INC -Os -Wall -Wextra -static meow_audiod.c -o meow_audiod -lpthread -echo "== meow_audiod (mipsel static) ==" +# meow_audiod:mipsel glibc-dynamic,BT 直链 libasound(-Llibbt -lasound)。-lpthread 供播放线程。 +# 链接源 libbt/libasound.so.2.0.0(设备固件 libasound.so.2 副本);缺失时从固件镜像自拷贝(idempotent)。 +LIBBT="libbt" +ASOUND_SRC="$ROOT/hiby_os_crack/r3proii/squashfs-root-example/usr/lib/libasound.so.2.0.0" +mkdir -p "$LIBBT" +if [ ! -f "$LIBBT/libasound.so.2.0.0" ]; then cp "$ASOUND_SRC" "$LIBBT/libasound.so.2.0.0"; fi +ln -sf libasound.so.2.0.0 "$LIBBT/libasound.so" +ln -sf libasound.so.2.0.0 "$LIBBT/libasound.so.2" +"$ZIG" cc --target=mipsel-linux-gnueabihf.2.22 $INC -Os -Wall -Wextra meow_audiod.c -o meow_audiod \ + -lpthread -Llibbt -lasound +echo "== meow_audiod (mipsel glibc-dynamic, BT links libasound.so.2) ==" ls -la meow_audiod md5sum meow_audiod file meow_audiod +echo "-- dynamic NEEDED --" +readelf -d meow_audiod 2>/dev/null | grep -iE "needed" || true +echo "-- glibc version refs (max must be <= 2.22) --" +readelf --version-info meow_audiod 2>/dev/null | grep -oE "GLIBC_2\.[0-9]+" | sort -uV | tail -3 || true diff --git a/patch_v2/meow_audiod/meow_audiod.c b/patch_v2/meow_audiod/meow_audiod.c index 90bb96f..c12797d 100644 --- a/patch_v2/meow_audiod/meow_audiod.c +++ b/patch_v2/meow_audiod/meow_audiod.c @@ -57,7 +57,6 @@ #include #include #include -#include #include "meow_ipc.h" #ifdef __linux__ @@ -78,7 +77,6 @@ /* ---- 蓝牙 A2DP source(P2 输出目标 OUT_BT) ---- */ #define BT_PCM_DEFAULT "pcm.bluealsa" /* alsa.conf `pcm.bluealsa`(plug→bluealsa);aplay -D bluealsa 等价 */ #define BT_PCM_FALLBACK "bluealsa" /* 部分配置只定义别名节点,防打开失败再试一次 */ -#define BT_SO "libasound.so.2" /* 运行时 dlopen(不静态链接:设备为 musl 静态勿 dlopen,见 main 注释) */ #define BT_CODEC_AUTO "auto" #define BT_VOL_FULL_PCM "/usr/bin/bluealsa-cli" /* 一键设 AVRCP 100%(best-effort,不存在则跳过) */ @@ -187,12 +185,10 @@ static uint32_t g_dev_rate = 0, g_dev_ch = 0, g_dev_fmt = 0; static int g_reconfig_pending = 0; /* ---- 蓝牙 A2DP source 运行时状态(g_mu 保护;仅 g_target==OUT_BT && g_sink==SINK_ALSA 时用) ---- - * 直接写 libasound 插件 PCM(pcm.bluealsa). 不静态链接 libasound:主机/工具链均无 asoundlib, - * 用 dlopen/dlsym 运行时解析(设备 libasound.so.2 在 /usr/lib;见 dl 注释)。 */ + * 直接调用 libasound 插件 PCM(pcm.bluealsa)。audiod 以动态 glibc 链接设备 libasound.so.2 + * (见 bt_* 段注释),此处仅存 snd_pcm_t 不透明句柄。 */ static void *g_bt_hdl = NULL; /* snd_pcm_t *(libasound 不透明句柄) */ static int g_bt_open = 0; /* BT PCM 已打开 */ -static void *g_bt_so = NULL; /* dlopen 句柄 */ -static int g_bt_loaded = 0; /* dlopen 尝试过(失败不再重试风暴) */ static int g_bt_vol_full_done = 0; /* 已对 AVRCP 置 100%(幂等,一次即可) */ /* ---- 数字域电平表(DAC 实时监控;CS43198 无模拟电平回读,播放线程看 PCM 样本) ---- @@ -824,61 +820,39 @@ static int settings_get_volume(void) /* ============================================================================ * 蓝牙 A2DP source(输出目标 OUT_BT,PLAN §三/§五) * ---------------------------------------------------------------------------- - * audiod 用 libasound **运行时 dlopen** 直连 `pcm.bluealsa`(plug→bluealsa,播放向,A2DP), + * audiod **直接调用 libasound** 连 `pcm.bluealsa`(plug→bluealsa,播放向,A2DP), * 把播放器解码输出透传过去。BT 是**独立目标**: * - 不开 hw:0,0(不 period=1024×4、不 FPGA numid=9 武装、不 DAC 音量反转); * - 保留音频流透传 + 格式归一(grant_fmt=S16,plug 层转 codec 协商率); * - 音量走 AVRCP/耳机侧(音量隔离:绝不下发 DAC numid=1/2 反转,见 vol_apply)。 - * dlopen 说明:主机/交叉工具链均无 asoundlib.h/libasound,静态链接不可行;且设备 musl-静态 - * 二进制本身不支持 dlopen("Dynamic loading not supported")——因此 mipsel 真机若要生效, - * 需主会话提供 musl-mipsel libasound 并用 `-lasound` 直接链接(或改动态 musl-PIE)。本源码以 - * dlopen 路径编译到宿主(0 warning),真机 mipsel 链接此点列为本任务风险清单。禁止旁路:不跑 - * bluealsa-aplay/bluealsa-cli open(sink/D-Bus 或新建 daemon),audiod 内换目标 PCM 是唯一最简。 + * 链接说明(2026-09-03 真机定案):`pcm.bluealsa` 的 bluealsa 是**独立 pcm 插件**,libasound + * 在运行时以 dlopen 加载 /usr/lib/alsa-lib/libasound_module_pcm_bluealsa.so(glibc,依赖 + * libdbus-1.so.3)——故 audiod 必须以**动态 glibc** 形态链接设备现有 libasound.so.2 + * (build_audiod.sh:zig --target=mipsel-linux-gnueabihf.2.22,删 -static)。musl-static 不可行 + * (实测 "Dynamic loading not supported",无 dlopen 无法加载插件);musl-dynamic 亦不可行(设备 + * 无 musl ld.so)。libasound 原型在此内联声明(避免依赖 asoundlib.h;工具链不提供)。 + * 禁止旁路:不跑 bluealsa-aplay/bluealsa-cli open(D-Bus/新建 daemon),audiod 内换目标 PCM 唯一最简。 * ========================================================================== */ #define SND_PCM_STREAM_PLAYBACK 0 #define SND_PCM_ACCESS_RW_INTERLEAVED 3 #define SND_PCM_FORMAT_S16_LE 2 #define SND_PCM_NONBLOCK 1 -static int (*g_fn_open)(void **, const char *, int, int); -static int (*g_fn_set_params)(void *, int, int, unsigned, unsigned, int, unsigned); -static int (*g_fn_nonblock)(void *, int); -static long (*g_fn_writei)(void *, const void *, unsigned long); -static int (*g_fn_wait)(void *, int); -static int (*g_fn_recover)(void *, int, int); -static int (*g_fn_drain)(void *); -static int (*g_fn_close)(void *); -static const char *(*g_fn_strerror)(int); -/* dlsym→函数指针:用 memcpy 转(POSIX 可移植惯例;直接 `(void*)&fn` 或 union 会触发 - * -Wstrict-aliasing / 函数指针类型告警,memcpy 无此问题)。 */ -#define BT_LOAD_SYM(field, name) do { \ - void *p_ = dlsym(g_bt_so, name); \ - if (!p_) { logmsg("bt: dlsym %s failed", name); dlclose(g_bt_so); g_bt_so = NULL; return -1; } \ - memcpy(&g_##field, &p_, sizeof g_##field); \ -} while (0) +/* libasound 相关函数原型(内联声明,避免依赖 asoundlib.h;snd_pcm_t 不透明 → void*) */ +int snd_pcm_open(void **pcm, const char *name, int stream, int mode); +int snd_pcm_set_params(void *pcm, int format, int access, unsigned channels, + unsigned rate, int soft_resample, unsigned latency_us); +int snd_pcm_nonblock(void *pcm, int nonblock); +long snd_pcm_writei(void *pcm, const void *buffer, unsigned long size); +int snd_pcm_wait(void *pcm, int timeout); +int snd_pcm_recover(void *pcm, int err, int silent); +int snd_pcm_drain(void *pcm); +int snd_pcm_close(void *pcm); +const char *snd_strerror(int errnum); + static void bt_close(void); /* 前向:bt_write 判死路径要用 */ static void bt_vol_full(void); /* 前向:bt_open 成功时促成 AVRCP 100% */ -static int bt_load(void) -{ - if (g_bt_so) return 0; - if (g_bt_loaded) return -1; /* 已试过且失败:不再重试(防 dlopen 风暴) */ - g_bt_loaded = 1; - g_bt_so = dlopen(BT_SO, RTLD_NOW); - if (!g_bt_so) { logmsg("bt: dlopen %s failed: %s", BT_SO, dlerror()); return -1; } - BT_LOAD_SYM(fn_open, "snd_pcm_open"); - BT_LOAD_SYM(fn_set_params,"snd_pcm_set_params"); - BT_LOAD_SYM(fn_nonblock, "snd_pcm_nonblock"); - BT_LOAD_SYM(fn_writei, "snd_pcm_writei"); - BT_LOAD_SYM(fn_wait, "snd_pcm_wait"); - BT_LOAD_SYM(fn_recover, "snd_pcm_recover"); - BT_LOAD_SYM(fn_drain, "snd_pcm_drain"); - BT_LOAD_SYM(fn_close, "snd_pcm_close"); - BT_LOAD_SYM(fn_strerror, "snd_strerror"); - logmsg("bt: libasound loaded via dlopen"); - return 0; -} - /* bt.codec 生效:bluealsa 的 codec 是 config-time 参数(/usr/data/alsa.conf `pcm.bluealsa` 块的 * `codec "X"`)。audiod 在 BT 打开前做一次**幂等且外科式**的 token 替换:只把 bluealsa 块内的 * `codec "..."` 改成 `codec ""`(其余字符不动)。仅真机且文件可写时执行; @@ -936,30 +910,29 @@ static void bt_apply_codec_conf(void) free(out); } -/* BT 打开:dlopen libasound → snd_pcm_open(pcm.bluealsa/bluealsa, PLAYBACK, NONBLOCK) → +/* BT 打开:libasound → snd_pcm_open(pcm.bluealsa/bluealsa, PLAYBACK, NONBLOCK) → * set_params(S16_LE, RW, ch, rate, soft_resample, 100ms)。返回 0=ok。失败置 g_bt_open=0。 */ static int bt_open(void) { - if (bt_load() != 0) return -1; void *pcm = NULL; const char *names[2] = { BT_PCM_DEFAULT, BT_PCM_FALLBACK }; int r = -1; for (int i = 0; i < 2; i++) { - r = g_fn_open(&pcm, names[i], SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); + r = snd_pcm_open(&pcm, names[i], SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); if (r == 0) { logmsg("bt: opened %s", names[i]); break; } - logmsg("bt: snd_pcm_open %s: %s", names[i], g_fn_strerror ? g_fn_strerror(r) : "?"); + logmsg("bt: snd_pcm_open %s: %s", names[i], snd_strerror(r)); pcm = NULL; } if (r != 0) return -1; g_bt_hdl = pcm; - g_fn_nonblock(pcm, 1); + snd_pcm_nonblock(pcm, 1); unsigned rate = g_st.rate ? g_st.rate : 44100; unsigned ch = g_st.ch ? g_st.ch : 2; - r = g_fn_set_params(pcm, SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_INTERLEAVED, - ch, rate, 1, 100000); + r = snd_pcm_set_params(pcm, SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_INTERLEAVED, + ch, rate, 1, 100000); if (r < 0) { - logmsg("bt: set_params %u/%u/S16: %s", rate, ch, g_fn_strerror ? g_fn_strerror(r) : "?"); - g_fn_close(pcm); g_bt_hdl = NULL; return -1; + logmsg("bt: set_params %u/%u/S16: %s", rate, ch, snd_strerror(r)); + snd_pcm_close(pcm); g_bt_hdl = NULL; return -1; } g_bt_open = 1; logmsg("bt: PCM ready rate=%u ch=%u fmt=S16 codec=%s", rate, ch, g_bt_codec); @@ -985,12 +958,12 @@ static void bt_write(uint8_t *buf, size_t n) const uint8_t *off = buf; int stalls = 0; while (left > 0 && !g_stop) { - long w = g_fn_writei(g_bt_hdl, off, left); + long w = snd_pcm_writei(g_bt_hdl, off, left); if (w > 0) { off += (size_t)w * fs; left -= (unsigned long)w; stalls = 0; continue; } if (w < 0) { int err = (int)(-w); if (err == EAGAIN || err == EWOULDBLOCK) { - int pr = g_fn_wait(g_bt_hdl, ALSA_POLL_MS); + int pr = snd_pcm_wait(g_bt_hdl, ALSA_POLL_MS); if (pr == 0) { /* 无空间超时 → 停摆计数(镜像 DAC stall) */ g_stall++; stalls++; if (stalls >= ALSA_STALL_MAX) { @@ -1006,10 +979,10 @@ static void bt_write(uint8_t *buf, size_t n) if (err == EPIPE || err == ESTRPIPE) { /* xrun:recover 后继续 */ g_underrun++; logmsg("bt xrun #%u -> recover", (unsigned)g_underrun); - g_fn_recover(g_bt_hdl, err, 1); + snd_pcm_recover(g_bt_hdl, err, 1); continue; } - logmsg("bt writei err: %s", g_fn_strerror ? g_fn_strerror(err) : "?"); + logmsg("bt writei err: %s", snd_strerror(err)); break; } if (w == 0) break; @@ -1021,8 +994,8 @@ static void bt_write(uint8_t *buf, size_t n) static void bt_close(void) { if (g_bt_hdl) { - g_fn_drain(g_bt_hdl); - g_fn_close(g_bt_hdl); + snd_pcm_drain(g_bt_hdl); + snd_pcm_close(g_bt_hdl); g_bt_hdl = NULL; } g_bt_open = 0;