/* 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=,PROFILE=a2dp, * CODEC=")`),未声明的参数会被 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 /* 从 `hcitool con` 输出收集**所有** ACL 连接的对端 BD_ADDR(按出现顺序去重;MAC 主来源: * 内核 ACL 是连接权威,list-pcms 只在 profile 配置后的秒级窗口内存在)。 * 多设备是常态,调用方要**逐个试开**而不是只取第一条(M50 r1 评审 P2-3)。 * macs 为 [max][18];返回条数(0=无 ACL)。 */ int bt_dev_acl_macs(const char *hcitool_con, char (*macs)[18], int max); /* 解析 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 里按设备 MAC 精确挑端点路径(换耳机 A→B 后不能拿 A 的路径发 volume/info)。 * 返回 0=找到,-1=该 MAC 无端点。 */ int bt_dev_pick_endpoint(const char *listpcms, const char *mac, char *out, size_t outsz); /* 关闭决策(纯函数):bt_dev=1 → 关 BT PCM;否则 fd>2 才关;0/1/2 一律不动(护 stdin)。 * 返回 0=NONE / 1=BT / 2=FD。 */ int bt_dev_close_kind(int bt_dev, int fd); /* 从 `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=,PROFILE=a2dp,CODEC= * 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 */