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