Files
MeowOS-For-R3P2/patch_v2/meow_audiod/bt_dev.h
T
AskaEth 6e7c0d00c0 fix(audiod): M50 r2 蓝牙打开时序 — MAC 改从 hcitool con 取(list-pcms 易失)+ 短重试 + 行内匹配修正
真机(2026-11-11 18:2x/18:3x,MOONDROP Pudding 33:DE:2E:21:AD:30)暴露两个时序/解析问题:

① **list-pcms 易失,不能当 MAC 主来源**:bluealsa 的 a2dpsrc PCM 对象只在 A2DP profile
   配置好后的秒级窗口内存在(无人开 PCM 时 1~3s 就消失)。r1 用它取 MAC → 探针一错过窗口
   就 btdev=- / bt=fail 回落。改为 **主来源 `hcitool con` 的 ACL 行**(内核 ACL 是连接权威,
   开销一个 popen),拿到 MAC 直接 snd_pcm_open("bluealsa:DEV=<mac>,...") 让 bluealsa
   在 open 时自己创建 PCM;list-pcms 降级为兜底来源。
② **ACL 刚建立时 profile 可能还没配置好** → open 会瞬时 ENODEV。改 3 次 × 300ms 短重试
   (上限 600ms,不占播放线程太久);失败后才记为 fail#n 进退避(仍只在开流/重配边界)。
③ **端点路径要 D-Bus 全串**(`bluealsa-cli volume|info` 要它,不是 MAC):新增
   bt_dev_endpoint_path();PCM 撑住后再 list-pcms 取路径(此时稳定)→ bt_refresh_endpoint()。
④ **命令换行 bug**:旧 vol_full 的 token 扫描不认 '\n',真机把端点路径连换行一起塞进
   `bluealsa-cli volume <path>\n 127`,第二行成了独立命令 → 音量设置实际没下发。改为
   bt_dev_endpoint_path() 严格按空白/\r/\n 截断。
⑤ **行内匹配 bug**:is_src_line() 对整串 strstr 会借到下一行的 a2dpsrc —— hfphf/source
   行被误判成 A2DP 源端点(count_src 少算/endpoint_path 取错行)。改 line_is_src() 只在
   本行内判定。
⑥ STATE 增 `btacodec=`:读回 `bluealsa-cli info` 的 Selected codec。真机 MOONDROP Pudding
   只有 SBC/AAC,settings 请求 LDAC 时 bluealsa 接受参数但实际选 AAC —— 面板必须看到
   实际值(btcodec= 仍是请求值,btacodec= 是实际值)。
⑦ AVRCP 满量程 127(bluealsa-cli volume 取值范围 [0,127],旧值 100 只到 79%)。

宿主:test_bt_dev 66/66(新增 hcitool con 真机原文 / 端点路径 / info 原文解析用例),
test_audiod_host 109/0,交叉编译 0 warning(67944B,md5 0ec3b9a6…)。
真机:r1 二进制 18:26 那次已实测 BT 出声(bt=ok / played=55296 / bluealsa Running:true /
Delay 17.2ms / Volume 127),18:33 起耳机自动关机,r2 的 hcitool-con 主路径未能在真机复验
(耳机不可达),详见报告。
2026-11-11 19:37:03 +08:00

70 lines
4.0 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* bt_dev.h — 蓝牙 A2DP 端点选择 + PCM 名构造 + 回落退避(纯逻辑,宿主可单测)。
*
* 背景(M50 真机定案):`/usr/data/alsa.conf` **不是** alsa 配置(是 HiBy 播放器的数据文件,
* libasound 从不加载),改它对实际解析零影响。真正生效的是 alsa-lib 自带的
* `/etc/alsa/conf.d/20-bluealsa.conf`:
*
* pcm.bluealsa {
* @args [ DEV PROFILE CODEC VOL SOFTVOL DELAY SRV ] ← 只有这 7 个名字
* ... type plug / slave.pcm { type bluealsa ... }
* }
*
* 其 `@args` 均可从 PCM 名内联传入(`snd_pcm_open("bluealsa:DEV=<mac>,PROFILE=a2dp,
* CODEC=<token>")`),未声明的参数会被 alsa-lib 拒绝(parse_args: Unknown parameter)——
* 所以本模块只生成这 7 个里的 3 个,且**不改任何文件**。
*
* 纯 libc(string/stdlib),无 meow_* / popen / 文件 I/O —— meow_audiod 负责探测与打开。
*/
#ifndef BT_DEV_H
#define BT_DEV_H
#include <stddef.h>
/* 从 `hcitool con` 输出取第一条 ACL 连接的对端 BD_ADDR(**MAC 主来源**:内核 ACL 是连接权威,
* list-pcms 只在 profile 配置后的秒级窗口内存在)。返回 0=找到,-1=无 ACL/参数非法。 */
int bt_dev_acl_mac(const char *hcitool_con, char *mac, size_t macsz);
/* 解析 bluealsa-cli list-pcms 输出,取第一个 a2dpsrc/a2dp_source 端点的设备 MAC
* (**兜底来源**:list-pcms 可见时用)。
* 真实行为 D-Bus 对象路径(bluealsa 4.1.1):
* /org/bluealsa/hci0/dev_33_DE_2E_21_AD_30/a2dpsrc/sink
* 返回 0=找到并写入 mac(17 字符、大写冒号分隔,NUL 结尾),-1=无 a2dpsrc 端点 / 参数非法。
* macsz ≥ 18。 */
int bt_dev_detect_mac(const char *listpcms, char *mac, size_t macsz);
/* 统计 list-pcms 输出里的 a2dpsrc/a2dp_source 端点数(0=无可用 A2DP 源端点)。
* 用于「先探测端点、再决定是否打开」——无端点时省掉一次注定失败的 open。 */
int bt_dev_count_src(const char *listpcms);
/* 取 list-pcms 里第一个 a2dpsrc/a2dp_source 端点的完整 D-Bus 路径
* (/org/bluealsa/hci0/dev_XX_../a2dpsrc/sink)——`bluealsa-cli volume|info` 要的是它,不是 MAC。
* 返回 0=写好(out NUL 结尾),-1=无端点/参数非法。outsz 建议 ≥96。 */
int bt_dev_endpoint_path(const char *listpcms, char *out, size_t outsz);
/* 从 `bluealsa-cli info` 输出取实际协商 codec:行首 "Selected codec: AAC" → "AAC"。
* 返回 0=找到,-1=无该行/参数非法。 */
int bt_dev_info_codec(const char *info_out, char *out, size_t outsz);
/* settings.conf `bt.codec` → bluealsa CODEC 参数 token。
* bluealsa 4.1.1 的 a2dp_codecs_codec_id_from_string() 用 strcasecmp 匹配 canonical 名与别名,
* 故大小写不敏感;本函数把它归一成 canonical 名(日志/STATE 用),未知值/auto/unchanged →
* "unchanged"(不改协商结果,等价 20-bluealsa.conf 的 defaults.bluealsa.codec)。
* 返回 out(调用方给的 buf)。outsz 建议 ≥16。 */
const char *bt_dev_codec_token(const char *setting, char *out, size_t outsz);
/* 组 snd_pcm_open 的 PCM 名:
* bluealsa:DEV=<mac>,PROFILE=a2dp,CODEC=<token>
* mac 非 6 组十六进制 → -1(不拼非法名字);token 为空 → 用 "unchanged"。
* 返回 0=写好(out NUL 结尾)。outsz 建议 ≥96。 */
int bt_dev_pcm_name(const char *mac, const char *codec_token, char *out, size_t outsz);
/* 回落退避:连续失败 fails 次后的重试间隔 ms(2s→4s→8s→16s→32s→60s 封顶;fails=0 → 0)。
* 防重试风暴(M10 教训):失败越多等越久,绝不每次 OPEN 都硬打一次 BT。 */
unsigned bt_dev_backoff_ms(unsigned fails);
/* 纯决策:此刻是否允许再尝试一次 BT。fails=0 → 允许;否则要求距上次失败 ≥ 退避间隔。
* since_ms<0(时钟异常)按「允许」处理,避免永久卡死。 */
int bt_dev_should_try(unsigned fails, long long since_ms);
#endif /* BT_DEV_H */