build+tools: fw_content.conf 填 audiod/audio CLI md5; 新增诊断工具 sndprobe(PCM设备探测)/tonepcm2(HW_REFINE能力探测+写计时时钟验证) — 无声定位用。M2.90(0.7.85) 已打包 504bc74b 回验0diff
This commit is contained in:
1 parent
ade0a57353
commit
942bdece78
4 files changed
+249
-2
No files matched your search
@@ -0,0 +1,39 @@
|
||||
/* sndprobe.c — 探测 /dev/snd/pcmC0D{p,c}* 的 SNDRV_PCM_IOCTL_INFO(id/name/subs),
|
||||
* 定位 R3 Pro II 耳机输出到底在哪个 PCM 设备。静态 mipsel,只读不写参数。 */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sound/asound.h>
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/* tonepcm.c — 直开 /dev/snd/pcmC0D0p 写 N 秒正弦并计时(不需要耳朵的 DAC 时钟验证):
|
||||
* 若写 5s PCM 的墙钟 ≈5s(扣除缓冲),说明硬件时钟在走、DMA 在消费;
|
||||
* 若瞬间返回,说明硬件没在走(静音根因在时钟/电源域,不在数据)。
|
||||
* 用法: tonepcm [sec] [freq] 静态 mipsel。 */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <time.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 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;
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/* tonepcm2.c — 先 HW_REFINE 探测 cs43198 真实能力(格式/声道/采样率/缓冲),
|
||||
* 再按探测结果装参数写 N 秒测试音并计时。
|
||||
* 用法: tonepcm2 [sec] 静态 mipsel。 */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sound/asound.h>
|
||||
|
||||
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;
|
||||
}
|
||||
Reference in new issue
Block a user