/* 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; }