fix(audiod): M50 蓝牙输出端到端 — 内联 DEV/CODEC 打开 + 失败必回落 DAC + MAC 解析修正
① ALSA 正确用法(核心):不再写 /usr/data/alsa.conf(HiBy 播放器数据文件、语法非法、
libasound 从不加载,改它对解析零影响)。改为 snd_pcm_open("bluealsa:DEV=<mac>,
PROFILE=a2dp,CODEC=<token>"),参数名严格取 /etc/alsa/conf.d/20-bluealsa.conf 的
pcm.bluealsa @args[DEV PROFILE CODEC VOL SOFTVOL DELAY SRV](实测未知参数会被
alsa-lib 拒绝);删掉 bt_apply_device_conf/bt_apply_codec_conf 两条改写分支。
② 硬 bug:bt_dev_detect_mac() 旧逻辑取端点 token「最后一个 '/' 之后」——真机
bluealsa-cli list-pcms 输出是 D-Bus 路径 /org/bluealsa/hci0/dev_33_DE_2E_21_AD_30/
a2dpsrc/source,旧代码会把 "source" 当 MAC(旧单测喂的是人造输入所以一直是绿的)。
改按 dev_XX_XX_XX_XX_XX_XX 段解析,兼容老式 a2dpsrc/<MAC>。
③ codec:settings bt.codec 经 bt_dev_codec_token() 归一成 bluealsa canonical 名
(v4.1.1 a2dp-codecs.c 别名表,strcasecmp);auto/未知 → unchanged(不干预协商)。
④ 失败必回落(硬验收):新增 BT_ST_IDLE/OK/FAIL 状态机 —— 无端点/未连接/set_params
失败/写路径致命错(ENODEV 等)都回落 DAC 继续出声,绝不整机静音;回落前同步武装
numid=9(hbc3000 未烧+enable 开 PCM 会楔死整机);STATE 增 bt=/btdev=/btcodec=
三键供面板显示「输出=蓝牙(失败)」,volctl 回落时报 dac。
⑤ 防重试风暴:只在开流/重配边界重试,指数退避 2s→60s(bt_dev_should_try),写循环内
绝不重试;用户显式切目标才清退避。
⑥ 音量:BT 真走 A2DP 时仍不下发 DAC 反转(回归确认),回落 DAC 时按 DAC 音量控;
AVRCP 一键置最大从 100 改 127(bluealsa-cli volume 满量程 [0,127],旧值只到 79%)。
⑦ build_audiod.sh:ROOT/REPO 自动推导,ZIG/CC/LIBBT/ASOUND_SRC 可环境变量覆盖(旧
hiby_os_crack 路径在多数 checkout 里不存在),宿主链接 -l:libasound.so.2 失败退
-lasound;保留 M43 的 meow_audio time32 cli 目标。
宿主:test_bt_dev 51/51(真机 D-Bus 路径格式/别名表/退避),test_audiod_host 109/0,
0 warning。真机:新二进制 glibc-dynamic 上机正常,DAC 出声回归通过,无端点回落 DAC
出声(played/rms 实证),/usr/data/alsa.conf 全流程零写入;耳机出声一项待耳机上电。
不提交二进制、不改 fw_content.conf 钉值(按塔交付形态政策,由 M51 统一重编)。
This commit is contained in:
1 parent
ca042aa79c
commit
33bef36d16
6 files changed
+605
-339
No files matched your search
@@ -1,10 +1,17 @@
|
||||
/* test_bt_dev.c — 蓝牙 DEV 自识别宿主验证(纯逻辑,bt_dev.c)。
|
||||
/* test_bt_dev.c — 蓝牙端点选择 / PCM 名构造 / 回落退避 宿主验证(纯逻辑,bt_dev.c)。
|
||||
*
|
||||
* 覆盖矩阵(对齐任务B 宿主测试要求):
|
||||
* D1 bt_dev_detect_mac:a2dpsrc/<MAC> 端点 → 提取 MAC(含 a2dp_source、多空行、多设备取第一个、
|
||||
* 无 a2dpsrc / 空输出 / NULL → -1)。
|
||||
* D2 bt_dev_rewrite:真实 alsa.conf 文本 → DEV=、device 两处 MAC 换成目标,保留 codec/其余 token;
|
||||
* 同值 → 1(无需改动);无 pcm.bluealsa / 无 DEV= → -1;带引号风格保留。
|
||||
* 输入全部取自**真机实测格式**(bluealsa v4.1.1,设备 /usr/bin/bluealsa 的
|
||||
* `dev_%.2X_%.2X_%.2X_%.2X_%.2X_%.2X` + src/ba-transport.c 的 `<dev>/a2dpsrc/source`):
|
||||
* /org/bluealsa/hci0/dev_33_DE_2E_21_AD_30/a2dpsrc/source
|
||||
*
|
||||
* 覆盖矩阵:
|
||||
* D1 bt_dev_detect_mac:真实 D-Bus 路径 → MAC;多端点/多设备取第一个 a2dpsrc;
|
||||
* hfphf/a2dpsnk 不算;简化 `a2dpsrc/<MAC>` 兼容;空/无源端点/NULL/坏 dev_ 段 → -1。
|
||||
* D2 bt_dev_count_src:0/1/2 个源端点。
|
||||
* D3 bt_dev_codec_token:settings bt.codec → bluealsa canonical(4.1.1 别名表 strcasecmp);
|
||||
* auto/空/unchanged/垃圾 → unchanged。
|
||||
* D4 bt_dev_pcm_name:内联 DEV/PROFILE/CODEC;坏 MAC/小缓冲 → -1。
|
||||
* D5 bt_dev_backoff_ms / should_try:2s→60s 封顶 + 门控(防重试风暴)。
|
||||
*
|
||||
* 编译(在 patch_v2/meow_audiod 下):
|
||||
* cc -Wall -Wextra -o test_bt_dev test_bt_dev.c bt_dev.c
|
||||
@@ -26,91 +33,149 @@ int main(void)
|
||||
{
|
||||
setbuf(stdout, NULL);
|
||||
|
||||
/* ---- D1 设备列表 → 选中 a2dpsrc 连接设备 ---- */
|
||||
/* ==== D1 设备列表 → 选中 a2dpsrc 连接设备(真实 D-Bus 路径格式) ==== */
|
||||
{
|
||||
char mac[32] = "";
|
||||
int rc = bt_dev_detect_mac(
|
||||
"a2dpsrc/E0:42:6D:79:C5:98\na2dpsnk/E0:42:6D:79:C5:98\n", mac, sizeof mac);
|
||||
check(rc == 0 && strcmp(mac, "E0:42:6D:79:C5:98") == 0, "D1a a2dpsrc/<MAC> -> extract MAC");
|
||||
"/org/bluealsa/hci0/dev_33_DE_2E_21_AD_30/a2dpsrc/source\n", mac, sizeof mac);
|
||||
check(rc == 0 && strcmp(mac, "33:DE:2E:21:AD:30") == 0,
|
||||
"D1a real D-Bus path -> MAC (dev_ underscores -> colons)");
|
||||
}
|
||||
{
|
||||
char mac[32] = "";
|
||||
int rc = bt_dev_detect_mac(
|
||||
"profile/aptx\n\na2dp_source/33:DE:2E:21:AD:30\n", mac, sizeof mac);
|
||||
check(rc == 0 && strcmp(mac, "33:DE:2E:21:AD:30") == 0, "D1b a2dp_source/<MAC> -> extract MAC");
|
||||
"/org/bluealsa/hci0/dev_E0_42_6D_79_C5_98/hfphf/source\n"
|
||||
"/org/bluealsa/hci0/dev_33_DE_2E_21_AD_30/a2dpsnk/sink\n"
|
||||
"/org/bluealsa/hci0/dev_33_DE_2E_21_AD_30/a2dpsrc/source\n", mac, sizeof mac);
|
||||
check(rc == 0 && strcmp(mac, "33:DE:2E:21:AD:30") == 0,
|
||||
"D1b hfphf/a2dpsnk skipped -> first a2dpsrc");
|
||||
}
|
||||
{
|
||||
char mac[32] = "";
|
||||
int rc = bt_dev_detect_mac(
|
||||
"a2dpsrc/AA:BB:CC:DD:EE:FF 0\na2dpsrc/11:22:33:44:55:66 1\n", mac, sizeof mac);
|
||||
"/org/bluealsa/hci0/dev_AA_BB_CC_DD_EE_FF/a2dpsrc/source\n"
|
||||
"/org/bluealsa/hci0/dev_11_22_33_44_55_66/a2dpsrc/source\n", mac, sizeof mac);
|
||||
check(rc == 0 && strcmp(mac, "AA:BB:CC:DD:EE:FF") == 0, "D1c multi-device -> first a2dpsrc");
|
||||
}
|
||||
{
|
||||
char mac[32] = "";
|
||||
int rc = bt_dev_detect_mac("\n", mac, sizeof mac);
|
||||
check(rc == -1, "D1d empty output -> -1");
|
||||
int rc = bt_dev_detect_mac("a2dpsrc/E0:42:6D:79:C5:98\n", mac, sizeof mac);
|
||||
check(rc == 0 && strcmp(mac, "E0:42:6D:79:C5:98") == 0, "D1d legacy a2dpsrc/<MAC> still parsed");
|
||||
}
|
||||
{
|
||||
char mac[32] = "";
|
||||
int rc = bt_dev_detect_mac("a2dpsnk/11:22:33:44:55:66\n", mac, sizeof mac);
|
||||
check(rc == -1, "D1e only sink (no a2dpsrc) -> -1");
|
||||
int rc = bt_dev_detect_mac("", mac, sizeof mac);
|
||||
check(rc == -1, "D1e empty output -> -1");
|
||||
}
|
||||
{
|
||||
char mac[32] = "";
|
||||
int rc = bt_dev_detect_mac(
|
||||
"/org/bluealsa/hci0/dev_11_22_33_44_55_66/a2dpsnk/sink\n", mac, sizeof mac);
|
||||
check(rc == -1, "D1f only sink (no a2dpsrc) -> -1");
|
||||
}
|
||||
{
|
||||
char mac[32] = "";
|
||||
int rc = bt_dev_detect_mac(NULL, mac, sizeof mac);
|
||||
check(rc == -1, "D1f NULL input -> -1");
|
||||
check(rc == -1, "D1g NULL input -> -1");
|
||||
}
|
||||
{
|
||||
char mac[32] = "";
|
||||
int rc = bt_dev_detect_mac(
|
||||
"/org/bluealsa/hci0/dev_ZZ_42_6D_79_C5_98/a2dpsrc/source\n", mac, sizeof mac);
|
||||
check(rc == -1 && mac[0] == 0, "D1h malformed dev_ seg -> -1 (no garbage MAC)");
|
||||
}
|
||||
{
|
||||
char mac[32] = "";
|
||||
int rc = bt_dev_detect_mac(
|
||||
"/org/bluealsa/hci0/dev_33_DE_2E_21_AD_30/a2dpsrc/source 1\n", mac, sizeof mac);
|
||||
check(rc == 0 && strcmp(mac, "33:DE:2E:21:AD:30") == 0, "D1i trailing token tolerated");
|
||||
}
|
||||
|
||||
/* ---- D2 DEV 串入 / 保留其它 token ---- */
|
||||
/* ==== D2 源端点计数(有无 A2DP 端点的快速判定) ==== */
|
||||
check(bt_dev_count_src("") == 0, "D2a empty -> 0");
|
||||
check(bt_dev_count_src(
|
||||
"/org/bluealsa/hci0/dev_33_DE_2E_21_AD_30/a2dpsnk/sink\n") == 0,
|
||||
"D2b only sink -> 0");
|
||||
check(bt_dev_count_src(
|
||||
"/org/bluealsa/hci0/dev_33_DE_2E_21_AD_30/a2dpsrc/source\n") == 1,
|
||||
"D2c one a2dpsrc -> 1");
|
||||
check(bt_dev_count_src(
|
||||
"/org/bluealsa/hci0/dev_AA_BB_CC_DD_EE_FF/a2dpsrc/source\n"
|
||||
"/org/bluealsa/hci0/dev_11_22_33_44_55_66/a2dpsrc/source\n") == 2,
|
||||
"D2d two a2dpsrc -> 2");
|
||||
|
||||
/* ==== D3 settings bt.codec → bluealsa canonical token ==== */
|
||||
{
|
||||
const char *conf =
|
||||
"pcm.bluealsa:DEV=E0:42:6D:79:C5:98 {\n"
|
||||
" type plug\n"
|
||||
" slave {\n"
|
||||
" pcm {\n"
|
||||
" type bluealsa\n"
|
||||
" device E0:42:6D:79:C5:98\n"
|
||||
" profile \"a2dp\"\n"
|
||||
" codec \"LDAC\"\n"
|
||||
" ldac_eqmid \"LDAC_ABR\"\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
"}\n";
|
||||
char *out = NULL; size_t olen = 0;
|
||||
int rc = bt_dev_rewrite(conf, strlen(conf), "33:DE:2E:21:AD:30", &out, &olen);
|
||||
check(rc == 0 && out != NULL, "D2a rewrite returns changed");
|
||||
check(out && strstr(out, ":DEV=33:DE:2E:21:AD:30 {"), "D2b DEV= updated");
|
||||
check(out && strstr(out, "device 33:DE:2E:21:AD:30\n"), "D2c device updated");
|
||||
check(out && strstr(out, "codec \"LDAC\"") && strstr(out, "ldac_eqmid \"LDAC_ABR\""),
|
||||
"D2d codec/other tokens preserved");
|
||||
/* 旧 MAC 不再出现 */
|
||||
check(out && !strstr(out, "E0:42:6D:79:C5:98"), "D2e old MAC gone");
|
||||
free(out);
|
||||
}
|
||||
{
|
||||
const char *conf =
|
||||
"pcm.bluealsa:DEV=33:DE:2E:21:AD:30 {\n"
|
||||
" type plug\n slave {\n pcm {\n"
|
||||
" type bluealsa\n device 33:DE:2E:21:AD:30\n"
|
||||
" profile \"a2dp\"\n codec \"LDAC\"\n }\n }\n}\n";
|
||||
char *out = NULL; size_t olen = 0;
|
||||
int rc = bt_dev_rewrite(conf, strlen(conf), "33:DE:2E:21:AD:30", &out, &olen);
|
||||
check(rc == 1 && out == NULL, "D2f same MAC -> unchanged (idempotent)");
|
||||
}
|
||||
{
|
||||
const char *conf = "pcm.bluealsa {\n type bluealsa\n device \"E0:42:6D:79:C5:98\"\n}\n";
|
||||
char *out = NULL; size_t olen = 0;
|
||||
int rc = bt_dev_rewrite(conf, strlen(conf), "33:DE:2E:21:AD:30", &out, &olen);
|
||||
check(rc == 0 && out && strstr(out, "device \"33:DE:2E:21:AD:30\""),
|
||||
"D2g quoted device preserved (no DEV= token)");
|
||||
free(out);
|
||||
}
|
||||
{
|
||||
char *out = NULL; size_t olen = 0;
|
||||
int rc = bt_dev_rewrite("pcm.hw {\n type hw\n}\n", 21, "33:DE:2E:21:AD:30", &out, &olen);
|
||||
check(rc == -1 && out == NULL, "D2h no pcm.bluealsa -> -1");
|
||||
struct { const char *in; const char *want; const char *name; } t[] = {
|
||||
{ "LDAC", "LDAC", "D3a LDAC passthrough" },
|
||||
{ "ldac", "LDAC", "D3b lowercase ldac -> canonical" },
|
||||
{ "SBC", "SBC", "D3c SBC" },
|
||||
{ "AAC", "AAC", "D3d AAC" },
|
||||
{ "MPEG24", "AAC", "D3e MPEG24 alias -> AAC" },
|
||||
{ "aptX", "aptX", "D3f aptX" },
|
||||
{ "apt-X", "aptX", "D3g apt-X alias -> aptX" },
|
||||
{ "aptx-hd", "aptX-HD", "D3h aptx-hd -> aptX-HD" },
|
||||
{ "MPEG", "MP3", "D3i MPEG alias -> MP3" },
|
||||
{ "MP3", "MP3", "D3j MP3" },
|
||||
{ "FastStream", "FastStream", "D3k FastStream" },
|
||||
{ "FS", "FastStream", "D3l FS alias -> FastStream" },
|
||||
{ "auto", "unchanged", "D3m auto -> unchanged (do not touch negotiation)" },
|
||||
{ "", "unchanged", "D3n empty -> unchanged" },
|
||||
{ "unchanged", "unchanged", "D3o unchanged -> unchanged" },
|
||||
{ "BOGUS", "unchanged", "D3p unknown -> unchanged (never inject junk)" },
|
||||
};
|
||||
for (size_t i = 0; i < sizeof t / sizeof t[0]; i++) {
|
||||
char tok[16] = "";
|
||||
const char *r = bt_dev_codec_token(t[i].in, tok, sizeof tok);
|
||||
check(r == tok && strcmp(tok, t[i].want) == 0, t[i].name);
|
||||
}
|
||||
char tok[16] = "";
|
||||
check(bt_dev_codec_token(NULL, tok, sizeof tok) == tok && !strcmp(tok, "unchanged"),
|
||||
"D3q NULL setting -> unchanged");
|
||||
}
|
||||
|
||||
printf(g_fail ? "\nRESULT: %d FAIL(S)\n" : "\nRESULT: ALL PASS\n", g_fail);
|
||||
return g_fail;
|
||||
/* ==== D4 PCM 名构造(20-bluealsa.conf @args 只认 DEV/PROFILE/CODEC 这套名字) ==== */
|
||||
{
|
||||
char n[96] = "";
|
||||
int rc = bt_dev_pcm_name("33:DE:2E:21:AD:30", "LDAC", n, sizeof n);
|
||||
check(rc == 0 && strcmp(n, "bluealsa:DEV=33:DE:2E:21:AD:30,PROFILE=a2dp,CODEC=LDAC") == 0,
|
||||
"D4a name = bluealsa:DEV=<mac>,PROFILE=a2dp,CODEC=<token>");
|
||||
}
|
||||
{
|
||||
char n[96] = "";
|
||||
int rc = bt_dev_pcm_name("33:DE:2E:21:AD:30", "", n, sizeof n);
|
||||
check(rc == 0 && strstr(n, "CODEC=unchanged") != NULL, "D4b empty token -> CODEC=unchanged");
|
||||
}
|
||||
{
|
||||
char n[96] = "";
|
||||
int rc = bt_dev_pcm_name("33:DE:2E:21:AD:30", NULL, n, sizeof n);
|
||||
check(rc == 0 && strstr(n, "CODEC=unchanged") != NULL, "D4c NULL token -> CODEC=unchanged");
|
||||
}
|
||||
{
|
||||
char n[96] = "";
|
||||
check(bt_dev_pcm_name("33:DE:2E:21:AD", "LDAC", n, sizeof n) == -1, "D4d short MAC -> -1");
|
||||
check(bt_dev_pcm_name("GG:DE:2E:21:AD:30", "LDAC", n, sizeof n) == -1, "D4e non-hex MAC -> -1");
|
||||
check(bt_dev_pcm_name(NULL, "LDAC", n, sizeof n) == -1, "D4f NULL MAC -> -1");
|
||||
check(bt_dev_pcm_name("33:DE:2E:21:AD:30", "LDAC", n, 16) == -1, "D4g tiny buffer -> -1");
|
||||
}
|
||||
|
||||
/* ==== D5 回落退避(防重试风暴:M10 教训) ==== */
|
||||
check(bt_dev_backoff_ms(0) == 0, "D5a fails=0 -> 0ms (immediate)");
|
||||
check(bt_dev_backoff_ms(1) == 2000, "D5b fails=1 -> 2s");
|
||||
check(bt_dev_backoff_ms(2) == 4000, "D5c fails=2 -> 4s");
|
||||
check(bt_dev_backoff_ms(3) == 8000, "D5d fails=3 -> 8s");
|
||||
check(bt_dev_backoff_ms(4) == 16000, "D5e fails=4 -> 16s");
|
||||
check(bt_dev_backoff_ms(5) == 32000, "D5f fails=5 -> 32s");
|
||||
check(bt_dev_backoff_ms(6) == 60000, "D5g fails=6 -> 60s (capped)");
|
||||
check(bt_dev_backoff_ms(99) == 60000, "D5h fails=99 -> 60s (capped)");
|
||||
check(bt_dev_should_try(0, 0) == 1, "D5i never failed -> try now");
|
||||
check(bt_dev_should_try(1, 1999) == 0, "D5j fail#1 @1999ms -> gated");
|
||||
check(bt_dev_should_try(1, 2000) == 1, "D5k fail#1 @2000ms -> allowed");
|
||||
check(bt_dev_should_try(3, 7999) == 0, "D5l fail#3 @7999ms -> gated");
|
||||
check(bt_dev_should_try(3, 8000) == 1, "D5m fail#3 @8000ms -> allowed");
|
||||
check(bt_dev_should_try(2, -1) == 1, "D5n clock anomaly (negative) -> allowed (no deadlock)");
|
||||
|
||||
printf(g_fail ? "\nRESULT: %d FAIL(S)\n" : "\nRESULT: ALL PASS (%d checks)\n",
|
||||
g_fail ? g_fail : g_pass);
|
||||
return g_fail ? 1 : 0;
|
||||
}
|
||||
Reference in new issue
Block a user