diff --git a/patch_v2/tools/sndprobe.c b/patch_v2/tools/sndprobe.c new file mode 100644 index 0000000..6abe5b7 --- /dev/null +++ b/patch_v2/tools/sndprobe.c @@ -0,0 +1,39 @@ +/* sndprobe.c — 探测 /dev/snd/pcmC0D{p,c}* 的 SNDRV_PCM_IOCTL_INFO(id/name/subs), + * 定位 R3 Pro II 耳机输出到底在哪个 PCM 设备。静态 mipsel,只读不写参数。 */ +#include +#include +#include +#include +#include +#include + +int main(void) +{ + static const char *devs[] = { + "/dev/snd/pcmC0D0p", "/dev/snd/pcmC0D1p", "/dev/snd/pcmC0D0c", "/dev/snd/pcmC0D1c", + }; + for (int i = 0; i < 4; i++) { + int fd = open(devs[i], O_RDWR | O_NONBLOCK); + if (fd < 0) { printf("%s: open fail\n", devs[i]); continue; } + struct snd_pcm_info info; + memset(&info, 0, sizeof info); + if (ioctl(fd, SNDRV_PCM_IOCTL_INFO, &info) == 0) + printf("%s: card=%d dev=%d sub=%d id=\"%s\" name=\"%s\" subname=\"%s\" subs=%u/%u\n", + devs[i], info.card, info.device, info.subdevice, + info.id, info.name, info.subname, info.subdevices_avail, info.subdevices_count); + else + printf("%s: INFO ioctl fail\n", devs[i]); + close(fd); + } + /* 控制设备:看卡名 */ + int cf = open("/dev/snd/controlC0", O_RDONLY); + if (cf >= 0) { + struct snd_ctl_card_info ci; + memset(&ci, 0, sizeof ci); + if (ioctl(cf, SNDRV_CTL_IOCTL_CARD_INFO, &ci) == 0) + printf("controlC0: id=\"%s\" driver=\"%s\" name=\"%s\" longname=\"%s\"\n", + ci.id, ci.driver, ci.name, ci.longname); + close(cf); + } + return 0; +} diff --git a/patch_v2/tools/tonepcm.c b/patch_v2/tools/tonepcm.c new file mode 100644 index 0000000..c11bd76 --- /dev/null +++ b/patch_v2/tools/tonepcm.c @@ -0,0 +1,82 @@ +/* tonepcm.c — 直开 /dev/snd/pcmC0D0p 写 N 秒正弦并计时(不需要耳朵的 DAC 时钟验证): + * 若写 5s PCM 的墙钟 ≈5s(扣除缓冲),说明硬件时钟在走、DMA 在消费; + * 若瞬间返回,说明硬件没在走(静音根因在时钟/电源域,不在数据)。 + * 用法: tonepcm [sec] [freq] 静态 mipsel。 */ +#include +#include +#include +#include +#include +#include +#include +#include + +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 long long now_ms(void) +{ + struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); + return (long long)ts.tv_sec * 1000 + ts.tv_nsec / 1000000; +} + +int main(int argc, char **argv) +{ + int sec = argc > 1 ? atoi(argv[1]) : 5; + int freq = argc > 2 ? atoi(argv[2]) : 440; + int rate = 44100, ch = 2; + + int fd = open("/dev/snd/pcmC0D0p", O_RDWR); + if (fd < 0) { printf("open pcmC0D0p fail\n"); return 1; } + + struct snd_pcm_hw_params p; + memset(&p, 0, sizeof p); + pmask(&p.masks[SNDRV_PCM_HW_PARAM_ACCESS - SNDRV_PCM_HW_PARAM_FIRST_MASK], + SNDRV_PCM_ACCESS_RW_INTERLEAVED); + pmask(&p.masks[SNDRV_PCM_HW_PARAM_FORMAT - SNDRV_PCM_HW_PARAM_FIRST_MASK], + SNDRV_PCM_FORMAT_S16_LE); + pmask(&p.masks[SNDRV_PCM_HW_PARAM_SUBFORMAT - SNDRV_PCM_HW_PARAM_FIRST_MASK], + SNDRV_PCM_SUBFORMAT_STD); + pivset(&p.intervals[SNDRV_PCM_HW_PARAM_CHANNELS - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL], ch); + pivset(&p.intervals[SNDRV_PCM_HW_PARAM_RATE - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL], rate); + p.rmask = ~0u; + if (ioctl(fd, SNDRV_PCM_IOCTL_HW_PARAMS, &p) != 0) { printf("HW_PARAMS fail\n"); close(fd); return 1; } + printf("HW_PARAMS ok (rate interval now [%u,%u])\n", + p.intervals[SNDRV_PCM_HW_PARAM_RATE - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL].min, + p.intervals[SNDRV_PCM_HW_PARAM_RATE - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL].max); + if (ioctl(fd, SNDRV_PCM_IOCTL_PREPARE, 0) != 0) { printf("PREPARE fail\n"); close(fd); return 1; } + + /* 生成 1 秒正弦(整数查表近似:简单累加相位,定点幅度 0.3 满幅) */ + int frames1s = rate; + short *buf = malloc((size_t)frames1s * ch * 2); + for (int i = 0; i < frames1s; i++) { + /* 粗略正弦:三角波即可(验证用) */ + int period = rate / freq; + int ph = i % period; + int v = (ph < period / 2) ? (ph * 4 - period) : (3 * period - ph * 4); + short s = (short)(v * 9830 / period); /* ~0.3 * 32767 */ + buf[i * 2] = s; buf[i * 2 + 1] = s; + } + + long long t0 = now_ms(); + long long written = 0; + for (int s2 = 0; s2 < sec; s2++) { + ssize_t w = write(fd, buf, (size_t)frames1s * ch * 2); + if (w <= 0) { printf("write fail at sec %d (ret=%zd)\n", s2, w); break; } + written += w; + } + long long el = now_ms() - t0; + long long bytes = (long long)sec * frames1s * ch * 2; + printf("wrote %lld/%lld bytes in %lld ms (expected ~%d ms if hw clocking; instant = clock dead)\n", + written, bytes, el, sec * 1000); + + struct snd_pcm_status st; + memset(&st, 0, sizeof st); + if (ioctl(fd, SNDRV_PCM_IOCTL_STATUS, &st) == 0) + printf("STATUS: state=%d avail=%lu\n", st.state, (unsigned long)st.avail); + close(fd); + return 0; +} diff --git a/patch_v2/tools/tonepcm2.c b/patch_v2/tools/tonepcm2.c new file mode 100644 index 0000000..84ce4be --- /dev/null +++ b/patch_v2/tools/tonepcm2.c @@ -0,0 +1,126 @@ +/* tonepcm2.c — 先 HW_REFINE 探测 cs43198 真实能力(格式/声道/采样率/缓冲), + * 再按探测结果装参数写 N 秒测试音并计时。 + * 用法: tonepcm2 [sec] 静态 mipsel。 */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void pmask_all(struct snd_pcm_hw_params *p) +{ + for (int i = 0; i < SNDRV_PCM_HW_PARAM_LAST_MASK - SNDRV_PCM_HW_PARAM_FIRST_MASK + 1; i++) + memset(&p->masks[i], 0xff, sizeof(struct snd_mask)); + for (int i = 0; i < SNDRV_PCM_HW_PARAM_LAST_INTERVAL - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1; i++) { + p->intervals[i].min = 0; p->intervals[i].max = ~0u; + } + p->rmask = ~0u; +} +static long long now_ms(void) +{ + struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); + return (long long)ts.tv_sec * 1000 + ts.tv_nsec / 1000000; +} +static const char *fmt_name(unsigned f) +{ + switch (f) { + case SNDRV_PCM_FORMAT_S8: return "S8"; + case SNDRV_PCM_FORMAT_S16_LE: return "S16_LE"; + case SNDRV_PCM_FORMAT_S24_LE: return "S24_LE"; + case SNDRV_PCM_FORMAT_S32_LE: return "S32_LE"; + case SNDRV_PCM_FORMAT_S24_3LE: return "S24_3LE"; + default: return "?"; + } +} + +int main(int argc, char **argv) +{ + int sec = argc > 1 ? atoi(argv[1]) : 5; + int fd = open("/dev/snd/pcmC0D0p", O_RDWR); + if (fd < 0) { printf("open fail\n"); return 1; } + + /* 1) REFINE 探测 */ + struct snd_pcm_hw_params p; + memset(&p, 0, sizeof p); + pmask_all(&p); + if (ioctl(fd, SNDRV_PCM_IOCTL_HW_REFINE, &p) != 0) { printf("REFINE fail\n"); close(fd); return 1; } + struct snd_mask *fm = &p.masks[SNDRV_PCM_HW_PARAM_FORMAT - SNDRV_PCM_HW_PARAM_FIRST_MASK]; + printf("formats:"); + for (unsigned f = 0; f <= 32; f++) + if (fm->bits[f / 32] & (1u << (f % 32))) printf(" %s(%u)", fmt_name(f), f); + printf("\n"); + struct snd_interval *iv; + iv = &p.intervals[SNDRV_PCM_HW_PARAM_CHANNELS - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]; + printf("channels: [%u,%u]\n", iv->min, iv->max); + iv = &p.intervals[SNDRV_PCM_HW_PARAM_RATE - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]; + printf("rate: [%u,%u]\n", iv->min, iv->max); + iv = &p.intervals[SNDRV_PCM_HW_PARAM_BUFFER_SIZE - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]; + printf("buffer_size frames: [%u,%u]\n", iv->min, iv->max); + iv = &p.intervals[SNDRV_PCM_HW_PARAM_PERIOD_SIZE - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]; + printf("period_size frames: [%u,%u]\n", iv->min, iv->max); + + /* 2) 按能力装参数:优先 S16_LE,不支持则 S32_LE */ + unsigned use_fmt = (fm->bits[SNDRV_PCM_FORMAT_S16_LE / 32] & (1u << (SNDRV_PCM_FORMAT_S16_LE % 32))) + ? SNDRV_PCM_FORMAT_S16_LE : SNDRV_PCM_FORMAT_S32_LE; + int bytes_per_smp = (use_fmt == SNDRV_PCM_FORMAT_S16_LE) ? 2 : 4; + printf("using fmt %s\n", fmt_name(use_fmt)); + + memset(&p, 0, sizeof p); + pmask_all(&p); + /* 收窄:access=RW_INTERLEAVED, fmt=use_fmt, ch=2, rate=44100 */ + struct snd_mask *am = &p.masks[SNDRV_PCM_HW_PARAM_ACCESS - SNDRV_PCM_HW_PARAM_FIRST_MASK]; + memset(am, 0, sizeof *am); am->bits[SNDRV_PCM_ACCESS_RW_INTERLEAVED / 32] = 1u << (SNDRV_PCM_ACCESS_RW_INTERLEAVED % 32); + memset(fm, 0, sizeof *fm); /* fm 指针已随 p 重建后失效,重新取 */ + fm = &p.masks[SNDRV_PCM_HW_PARAM_FORMAT - SNDRV_PCM_HW_PARAM_FIRST_MASK]; + fm->bits[use_fmt / 32] = 1u << (use_fmt % 32); + struct snd_mask *sm = &p.masks[SNDRV_PCM_HW_PARAM_SUBFORMAT - SNDRV_PCM_HW_PARAM_FIRST_MASK]; + memset(sm, 0, sizeof *sm); sm->bits[0] = 1; + iv = &p.intervals[SNDRV_PCM_HW_PARAM_CHANNELS - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]; + iv->min = iv->max = 2; iv->integer = 1; + iv = &p.intervals[SNDRV_PCM_HW_PARAM_RATE - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]; + iv->min = iv->max = 44100; iv->integer = 1; + if (ioctl(fd, SNDRV_PCM_IOCTL_HW_REFINE, &p) != 0) { printf("REFINE2 fail\n"); close(fd); return 1; } + if (ioctl(fd, SNDRV_PCM_IOCTL_HW_PARAMS, &p) != 0) { printf("HW_PARAMS fail\n"); close(fd); return 1; } + printf("HW_PARAMS ok\n"); + if (ioctl(fd, SNDRV_PCM_IOCTL_PREPARE, 0) != 0) { printf("PREPARE fail\n"); close(fd); return 1; } + + /* 3) 测试音(三角波 440Hz),按格式写 */ + int frames1s = 44100; + void *buf = malloc((size_t)frames1s * 2 * bytes_per_smp); + for (int i = 0; i < frames1s; i++) { + int period = 44100 / 440; + int ph = i % period; + int v = (ph < period / 2) ? (ph * 4 - period) : (3 * period - ph * 4); + if (use_fmt == SNDRV_PCM_FORMAT_S16_LE) { + short s = (short)(v * 9830 / period); + ((short *)buf)[i * 2] = s; ((short *)buf)[i * 2 + 1] = s; + } else { + int32_t s = (int32_t)((int64_t)v * 644972544 / period); /* ~0.3 * 2^31 */ + ((int32_t *)buf)[i * 2] = s; ((int32_t *)buf)[i * 2 + 1] = s; + } + } + long long t0 = now_ms(); + int ok = 1; + for (int s2 = 0; s2 < sec; s2++) { + ssize_t w = write(fd, buf, (size_t)frames1s * 2 * bytes_per_smp); + if (w <= 0) { printf("write fail at sec %d ret=%zd errno=%d\n", s2, w, errno); ok = 0; break; } + } + long long el = now_ms() - t0; + printf("write %ds done in %lld ms (expect ~%d ms if hw clocking)\n", ok ? sec : -1, el, sec * 1000); + struct snd_pcm_status st; + memset(&st, 0, sizeof st); + if (ioctl(fd, SNDRV_PCM_IOCTL_STATUS, &st) == 0) + printf("STATUS t+0: state=%d(3=running) avail=%lu\n", st.state, (unsigned long)st.avail); + /* 停止写入空等 3s 再读 avail:avail 变大=时钟在走(在消费),不变=时钟死 */ + sleep(3); + memset(&st, 0, sizeof st); + if (ioctl(fd, SNDRV_PCM_IOCTL_STATUS, &st) == 0) + printf("STATUS t+3s: state=%d avail=%lu\n", st.state, (unsigned long)st.avail); + close(fd); + return 0; +} diff --git a/scripts/build/fw_content.conf b/scripts/build/fw_content.conf index 7166384..30389cd 100644 --- a/scripts/build/fw_content.conf +++ b/scripts/build/fw_content.conf @@ -38,8 +38,8 @@ patch_v2/meow_svc_power/meow_svc_power|/usr/bin/meow_svc_power|306df962d319f1616 patch_v2/meow_verify/meow_verify|/usr/bin/meow_verify|6c2e8ed24aebe5caf41e7aed391312d5|II期验签 2B(多公钥+v2签名块 agent-221) patch_v2/meow_crash/meow_crash|/usr/bin/meow_crash|62781084445e82351bcadbdadbe4c7fc|系统级崩溃捕获 (agent-187) patch_v2/meow_svc_perf/meow_svc_perf|/usr/bin/meow_svc_perf|7ea196de9d59ab8cbd1e26edb7762772|性能专项服务 M2.75(net_json 补 network 闭合括号,修 /api/system JSON 解析失败) -patch_v2/meow_audiod/meow_audiod|/usr/bin/meow_audiod||独占音频守护 A1(AUDIOD_DESIGN.md;独占开卡/格式归一/抢占/路由并入;md5 打包时统一填 agent-244) -patch_v2/meow_audiod/meow_audio|/usr/bin/meow_audio||audiod 用户侧 CLI(status/tone/route/vol;md5 打包时统一填 agent-244) +patch_v2/meow_audiod/meow_audiod|/usr/bin/meow_audiod|e07e914466f75bdd8545d2d4ba4f9e01|独占音频守护 A1(AUDIOD_DESIGN.md;独占开卡/格式归一/抢占/路由并入;md5 打包时统一填 agent-244) +patch_v2/meow_audiod/meow_audio|/usr/bin/meow_audio|5cc3c35459409e93672448c99543dc93|audiod 用户侧 CLI(status/tone/route/vol;md5 打包时统一填 agent-244) # ---- 开机 init 脚本(etc/init.d,数字决定启动顺序,按文件名自然序)---- patch_v2/meow_sysui/S70meow_sysui|/etc/init.d/S70meow_sysui|e435affcb56499e5229728c6ae2524b9|sysui 自启 M2.85(launcher.conf enabled=1+迁移)