Skip to content

i.MX6ULL /dev/snd 设备节点 ​

平台:100ask i.MX6ULL Pro 内核:NXP BSP Linux 4.9.88本文:板上 /dev/snd 下面的节点含义、创建路径、以及 open 这些 PCM 节点时如何进到 soc_pcm_open


目录 ​


1. 本文要回答什么 ​

板上 /dev/snd 里每个节点对应什么功能?和 Machine 的三条 dai_link、打开时的 file_operations 如何对应?open(pcm…) 之后 ASoC 做了什么?


2. 板上节点 ​

text
/dev/snd/
  controlC0
  pcmC0D0c  pcmC0D0p
  pcmC0D1c  pcmC0D1p

命名规则:C = card 号,D = device 号,p = playback,c = capture。

与 aplay -l 对应:

text
card 0: wm8960audio [wm8960-audio]
  device 0: HiFi wm8960-hifi-0     → pcmC0D0p / pcmC0D0c
  device 1: HiFi-ASRC-FE (*)       → pcmC0D1p / pcmC0D1c

声卡名来自设备树 model = "wm8960-audio"。device 1 为 ASRC 前端,说明见 §3.1。


Machine 驱动 sound/soc/fsl/imx-wm8960.c 里注册三条 dai_link,再加一个控制节点:

角色名称/dev 节点用途
dai_link[0]HiFipcmC0D0p / pcmC0D0cSAI2 ↔ WM8960 直连;日常播 / 录用 hw:0,0
dai_link[1]HiFi-ASRC-FEpcmC0D1p / pcmC0D1cASRC 前端,见 §3.1
dai_link[2]HiFi-ASRC-BE无(no_pcm)ASRC 后端,见 §3.1
控制节点—controlC0mixer(音量、通路等);amixer / tinymix 等控制使用

播放与录音命令:

节点方向示例
pcmC0D0p播放aplay -D hw:0,0 xxx.wav
pcmC0D0c录音arecord -D hw:0,0 -f cd rec.wav

3.1 ASRC(device 1) ​

本板 DTS 挂了 asrc-controller 时,Machine 会多建两条 dai_link:前端 FE 对应 pcmC0D1*,后端 BE 标 no_pcm,不会出现独立 /dev 节点。本文对这条路径不做赘述。


4. 内核如何创建这些节点 ​

4.1 总流程 ​

text
imx_wm8960_probe
  → snd_soc_register_card / snd_soc_instantiate_card
      → snd_card_new
          → snd_ctl_create()           初始化 control 设备
      → soc_probe_link_dais / soc_new_pcm
          → snd_pcm_new()              为有 PCM 的 link 创建 snd_pcm
      → snd_card_register
          → snd_device_register_all
              → control: snd_ctl_dev_register
              → pcm:     snd_pcm_dev_register
                  → snd_register_device(...)
                      → device_add → 这时用户态才可看见 /dev/snd/xxx

关键文件:

步骤文件
注册到字符设备sound/core/sound.c — snd_register_device()
control 注册sound/core/control.c — snd_ctl_dev_register()
PCM 注册sound/core/pcm.c — snd_pcm_dev_register()
ASoC 创建 PCMsound/soc/soc-pcm.c — soc_new_pcm()
建声卡sound/core/init.c — snd_card_new() / snd_card_register()

snd_card_new() 里会调用 snd_ctl_create() 预备 controlC0,但用户态真正可访问要等 snd_card_register()。

4.2 打开时的统一入口 ​

所有 ALSA 节点先挂在总入口 snd_fops 上(sound/core/sound.c):

text
open("/dev/snd/xxx")
  → snd_fops.open = snd_open
  → 按 minor 查 snd_minors[]
  → replace_fops 换成真正的 file_operations
  → 调用对应 open(如 snd_pcm_playback_open)

5. 对应的 file_operations ​

/dev/snd 节点file_operations定义位置主要接口
controlC0snd_ctl_f_opssound/core/control.copen / unlocked_ioctl / read / poll
pcmC0D0p、pcmC0D1psnd_pcm_f_ops[0]sound/core/pcm_native.cwrite、snd_pcm_playback_ioctl、snd_pcm_playback_open
pcmC0D0c、pcmC0D1csnd_pcm_f_ops[1]sound/core/pcm_native.cread、snd_pcm_capture_ioctl、snd_pcm_capture_open

PCM 节点注册核心代码(sound/core/pcm.c):

c
snd_register_device(devtype, pcm->card, pcm->device,
                    &snd_pcm_f_ops[cidx], pcm,
                    &pcm->streams[cidx].dev);
  • cidx = 0(playback)→ ...p
  • cidx = 1(capture)→ ...c

control 节点注册(sound/core/control.c):

c
snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
                    &snd_ctl_f_ops, card, &card->ctl_dev);

6. PCM 节点 open 与 soc_pcm_open ​

§4.2 / §5 说到首次 open 会换成 snd_pcm_playback_open / snd_pcm_capture_open。对 HiFi(pcmC0D0p / pcmC0D0c) 来说,真正接到 ASoC 各层的是 substream->ops->open → soc_pcm_open(sound/soc/soc-pcm.c)。建 PCM 时在 soc_new_pcm 里写入:rtd->ops.open = soc_pcm_open。

6.1 从节点到 soc_pcm_open ​

以打开 pcmC0D0p 为例(录音把 playback 换成 capture / f_ops[1] 即可):

text
open("/dev/snd/pcmC0D0p")
  → snd_open → replace_fops → snd_pcm_f_ops[0]
  → snd_pcm_playback_open
      → snd_pcm_open(..., PLAYBACK)          // pcm_native.c:卡引用、open_mutex、忙则等待
          → snd_pcm_open_file
              → snd_pcm_open_substream
                  → attach substream、hw 约束初值
                  → substream->ops->open
                      → soc_pcm_open         // 本板 HiFi

snd_pcm_open 负责「这个 PCM 设备能不能被占上」;soc_pcm_open 负责「这条 rtd 上的 CPU DAI / Platform / Codec / Machine 如何 startup」。

6.2 soc_pcm_open 做了什么 ​

substream->private_data 即绑卡得到的 rtd(snd_soc_pcm_runtime),其上已挂好 cpu_dai / platform / codec_dai。函数大致分四段:

text
soc_pcm_open(substream)
  │
  ├─ 1. pinctrl 默认态 + pm_runtime_get(CPU DAI / Codec / Platform 设备)
  ├─ 2. 持 rtd->pcm_mutex,按序 startup / open
  │     → cpu_dai->ops->startup     → fsl_sai_startup
  │     → platform->ops->open       → imx_pcm_open(imx-pcm-dma-v2)
  │     → codec_dai->ops->startup   → WM8960 通常无,跳过
  │     → dai_link->ops->startup    → imx_hifi_startup
  ├─ 3. 非 DPCM:soc_pcm_init_runtime_hw(CPU DAI/Codec 能力求交 → runtime->hw)
  │     检查 rates / formats / channels;对称率等
  └─ 4. snd_soc_runtime_activate;失败则反向 shutdown / close + runtime_put

本板各回调侧重点:

调用文件open 阶段做什么
fsl_sai_startupfsl_sai.c占住该方向流;加速率等 hw 约束
imx_pcm_openimx-pcm-dma-v2.c按 "tx"/"rx" 申请 SDMA 通道,设 PCM hardware
imx_hifi_startupimx-wm8960.c与 SAI 流占用对齐;非 codec-master 时再加板级速率约束

此阶段 一般不 配具体采样率、不开环形 DMA 传数据;那些在后续 hw_params / prepare / trigger(见播/录路径文)。


7. 小结 ​

  • 节点在 snd_card_register → snd_register_device 后出现在 /dev/snd。
  • 首次 open 经 snd_open 再 replace_fops 到 control / PCM 各自的 file_operations。
  • 打开 pcmC0D0p / pcmC0D0c 时,经 snd_pcm_open 进入 soc_pcm_open:依次调用 SAI / Platform / Machine 的 startup(或 open),再把 CPU DAI 与 Codec 能力求交写入 runtime->hw。

基于 VitePress 构建