fix(bt): 蓝牙P2 mipsel libasound链接 — 改glibc-dynamic(设备实为glibc2.22硬浮点,pcm.bluealsa=独立glibc插件需dlopen,musl-static死路)直链设备libasound.so.2,BT层脱离dlopen(内联9个libasound原型);qemu装载动态库验证+宿主67pass/0warning;audiod改glibc-dynamic(57KB)M6.30后需重验DAC出声

This commit is contained in:
AskaEth committed 2026-09-03 21:18:19 +08:00
1 parent 71b2dafecc
commit 4ecddb5ea3
2 files changed
+74 -76

No files matched your search

+36 -63
View File
@@ -57,7 +57,6 @@
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <dlfcn.h>
#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 "<g_bt_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;