A1: meow_audiod 独占音频服务(agent-244 骨架 + agent-246 真机收尾)

- meow_audiod:启动门控(/tmp/meow_boot_mode 非 meow exit 0)、IO/播放/路由三线程、
  ring ~200ms、ALSA UAPI 独占直开、切率 mute-重配-恢复、抢占(后开赢+PREEMPTED)、
  socket EOF 回收、xrun PREPARE 恢复;meow_route 轮询逻辑并入路由线程
- meow_audio CLI:status/tone/route/vol;services.d/audiod.conf(trigger=manual,on-fail)
- SDK meow_ui_audio.c 编进 meow_ui.a:懒连接/读线程分发 PREEMPTED 回调/EPIPE 标死/
  无 audiod 返回 MEOW_ERR_NOENT 不隐式直开(MEOW_AUDIO_DIRECT=1 仅调试)
- launcher:确认 MeowOS 模式后 meow_svcd start audiod(double-fork best-effort 不阻塞)
- meow_route 退役:S89 预置换 audiod.conf + 清理旧 meow_route.conf(防拉起已删二进制
  重启循环);fw_content.conf 删 meow_route、加 meow_audiod+meow_audio(md5 打包时填)
- 真机定位并修复 ALSA UAPI 两大坑(即 ncm 播放无声根因):
  ① hw_params 须全放行初始化(mask 全 1/interval [0,UINT_MAX]),memset 0 interval
     等价只能取 0 → refine 必空 → HW_PARAMS 恒 EINVAL
  ② access/format mask 先清零再单点(pmask 只 OR),全 1 被 choose 成 MMAP_INTERLEAVED
     → write() 恒 EINVAL
- 附带修复:dev_write fd<0 防日志风暴丢 PCM 限频日志;SDK state 解析 app_id 截空白;
  meow_ui.h 文档负错误码表述修正
- 验证:宿主 FILE sink 全 IPC 链+抢占+tone WAV 20/20,SDK 客户端 13/13;
  qemu-mipsel 同过;真机 tone 44.1k/96k 切率 written=played、underrun=0、日志完整
- meow_ui.a 53a3edd2 → ncm 重链 d2031829(已热更到卡);launcher a2f932a1
This commit is contained in:
MeowOS committed 2026-08-31 22:40:08 +08:00
1 parent 94874107b6
commit ade0a57353
24 files changed
+2795 -11

No files matched your search

+75
View File
@@ -0,0 +1,75 @@
#define _GNU_SOURCE
/* probe_alsa.c — 探测 R3 Pro II CS43198 PCM 设备的 hw_params 约束(找可用的 access/format/rate)。
* mipsel 交叉编译后推到设备运行,输出 kernel 支持哪些组合,诊断「hw_params ioctl 失败」。 */
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sound/asound.h>
static void pmask(struct snd_mask *m, unsigned bit) { m->bits[bit / 32] |= 1u << (bit % 32); }
static void pivset(struct snd_interval *iv, unsigned val)
{
iv->min = iv->max = val; iv->integer = 1; iv->openmin = iv->openmax = 0; iv->empty = 0;
}
static const char *fmt_name(unsigned f)
{
#define N(x) case x: return #x
switch (f) {
N(SNDRV_PCM_FORMAT_S8); N(SNDRV_PCM_FORMAT_U8); N(SNDRV_PCM_FORMAT_S16_LE);
N(SNDRV_PCM_FORMAT_S16_BE); N(SNDRV_PCM_FORMAT_U16_LE); N(SNDRV_PCM_FORMAT_U16_BE);
N(SNDRV_PCM_FORMAT_S24_LE); N(SNDRV_PCM_FORMAT_S24_BE); N(SNDRV_PCM_FORMAT_S32_LE);
N(SNDRV_PCM_FORMAT_S32_BE); N(SNDRV_PCM_FORMAT_FLOAT_LE); N(SNDRV_PCM_FORMAT_FLOAT_BE);
N(SNDRV_PCM_FORMAT_S24_3LE); N(SNDRV_PCM_FORMAT_S24_3BE); N(SNDRV_PCM_FORMAT_U24_LE);
N(SNDRV_PCM_FORMAT_S20_3LE); N(SNDRV_PCM_FORMAT_S20_3BE);
default: return "?";
}
#undef N
}
int main(void)
{
setvbuf(stdout, NULL, _IONBF, 0); /* 每行立即刷出,防 kill 丢输出 */
const char *dev = "/dev/snd/pcmC0D0p";
unsigned access[] = { SNDRV_PCM_ACCESS_MMAP_INTERLEAVED, SNDRV_PCM_ACCESS_RW_INTERLEAVED,
SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED, SNDRV_PCM_ACCESS_RW_NONINTERLEAVED, ~0u };
unsigned fmts[] = { SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_FORMAT_S24_LE, SNDRV_PCM_FORMAT_S32_LE,
SNDRV_PCM_FORMAT_S24_3LE, ~0u };
unsigned rates[] = { 44100, 48000, 96000, 88200, ~0u };
for (unsigned ai = 0; access[ai] != ~0u; ai++) {
for (unsigned fi = 0; fmts[fi] != ~0u; fi++) {
printf("== access=%u fmt=%s ==\n", access[ai], fmt_name(fmts[fi]));
struct snd_pcm_hw_params p;
for (unsigned ri = 0; rates[ri] != ~0u; ri++) {
int fd = open(dev, O_RDWR | O_NONBLOCK);
if (fd < 0) { printf(" open: %s\n", strerror(errno)); break; }
/* 「全放行」初始化:memset 0 的 interval =「只能取 0」→ refine 必空 EINVAL
*(2026-08-31 真机全灭的根因;libasound snd_pcm_hw_params_any 语义) */
memset(&p, 0, sizeof p);
unsigned nm = SNDRV_PCM_HW_PARAM_LAST_MASK - SNDRV_PCM_HW_PARAM_FIRST_MASK + 1;
for (unsigned i = 0; i < nm; i++)
for (unsigned b = 0; b < sizeof p.masks[i].bits / sizeof p.masks[i].bits[0]; b++)
p.masks[i].bits[b] = ~0u;
unsigned ni = SNDRV_PCM_HW_PARAM_LAST_INTERVAL - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1;
for (unsigned i = 0; i < ni; i++) { p.intervals[i].min = 0; p.intervals[i].max = ~0u; }
/* mask 先清零再单点:全 1 会被 choose 取最低 bit(access→MMAP_INTERLEAVED) */
struct snd_mask *am = &p.masks[SNDRV_PCM_HW_PARAM_ACCESS - SNDRV_PCM_HW_PARAM_FIRST_MASK];
memset(am, 0, sizeof *am); pmask(am, access[ai]);
struct snd_mask *fm = &p.masks[SNDRV_PCM_HW_PARAM_FORMAT - SNDRV_PCM_HW_PARAM_FIRST_MASK];
memset(fm, 0, sizeof *fm); pmask(fm, fmts[fi]);
struct snd_mask *sm = &p.masks[SNDRV_PCM_HW_PARAM_SUBFORMAT - SNDRV_PCM_HW_PARAM_FIRST_MASK];
memset(sm, 0, sizeof *sm); pmask(sm, SNDRV_PCM_SUBFORMAT_STD);
pivset(&p.intervals[SNDRV_PCM_HW_PARAM_CHANNELS - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL], 2);
pivset(&p.intervals[SNDRV_PCM_HW_PARAM_RATE - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL], rates[ri]);
p.rmask = ~0u; p.info = 0; p.msbits = 0; p.rate_num = 0; p.rate_den = 0; p.flags = 0;
int rc = ioctl(fd, SNDRV_PCM_IOCTL_HW_PARAMS, &p);
printf(" @%u: %s\n", rates[ri], rc ? strerror(errno) : "OK");
close(fd);
}
}
}
return 0;
}