Rockchip RK3399 - ALSA Proc Info
----------------------------------------------------------------------------------------------------------------------------
开发板 :NanoPC-T4开发板
eMMC :16GB
LPDDR3 :4GB
显示屏 :15.6英寸HDMI接口显示屏
u-boot :2023.04
linux :6.3
----------------------------------------------------------------------------------------------------------------------------
Linux系统上的/proc目录是一种文件系统,即proc文件系统。与其它常见的文件系统不同的是,/proc是一种伪文件系统(也即虚拟文件系统),存储的是当前内核运行状态的一系列特殊文件,用户可以通过这些文件查看有关系统硬件及当前正在运行进程的信息,甚至可以通过更改其中某些文件来改变内核的运行状态。
基于/proc文件系统如上所述的特殊性,其内的文件也常被称作虚拟文件,并具有一些独特的特点。例如,其中有些文件虽然使用查看命令查看时会返回大量信息,但文件本身的大小却会显示为0字节。此外,这些特殊文件中大多数文件的时间及日期属性通常为当前系统时间和日期,这跟它们随时会被刷新(存储于RAM中)有关。
一、/proc/asound
ALSA有自己的proc tree,在/proc/asound这个目录下可以找到许多关于snd card 的详细信息,Proc Files of ALSA Drivers。
root@rk3399:/# ll /proc/asound dr-xr-xr-x 5 root root 0 Aug 6 16:43 card0/ dr-xr-xr-x 5 root root 0 Aug 6 16:55 card1/ -r--r--r-- 1 root root 0 Aug 6 15:10 cards -r--r--r-- 1 root root 0 Aug 6 16:55 devices lrwxrwxrwx 1 root root 5 Aug 6 16:55 hdmisound -> card0/ -r--r--r-- 1 root root 0 Aug 6 16:55 pcm lrwxrwxrwx 1 root root 5 Aug 6 16:55 realtekrt5651co -> card1/ dr-xr-xr-x 6 root root 0 Aug 6 16:55 seq/ -r--r--r-- 1 root root 0 Aug 6 16:55 timers -r--r--r-- 1 root root 0 Aug 6 16:55 version
其中主要的节点如下:
- cardx:表示注册的sound card;
- cards:显示当前配置的Alsa Drivers,index,the id string,short and long descriptions;
- version:显示版本字符串;
- devices:列举本机设备映射;
- pcm:列举当前可用的 pcm devides,格式如下:<card>-<device>: <id>: <name> : <sub-streams>;
该节主要用于讲解在alsa core 中创建的/proc/asound目录树以及如何实现的。
在 alsa_sound_init函数中会调用snd_info_init函数创建 /proc/asound目录,并将该entry保存在全局变量 snd_proc_root(即作为 sound proc root entry),代码如下:
int __init snd_info_init(void) { //1、创建 alsa proc root entry; snd_proc_root = snd_info_create_entry("asound", NULL); if (!snd_proc_root) return -ENOMEM; snd_proc_root->mode = S_IFDIR | 0555; //2、创建 dir: /proc/asound snd_proc_root->p = proc_mkdir("asound", NULL); if (!snd_proc_root->p) goto error; #ifdef CONFIG_SND_OSSEMUL snd_oss_root = create_subdir(THIS_MODULE, "oss"); if (!snd_oss_root) goto error; #endif #if IS_ENABLED(CONFIG_SND_SEQUENCER) snd_seq_root = create_subdir(THIS_MODULE, "seq"); if (!snd_seq_root) goto error; #endif if (snd_info_version_init() < 0 || //3、创建 file: /proc/asound/version snd_minor_info_init() < 0 || //4、创建 file: /proc/asound/devices snd_minor_info_oss_init() < 0 || snd_card_info_init() < 0 || //5、创建 file: /proc/asound/cards snd_info_minor_register() < 0) goto error; return 0; error: snd_info_free_entry(snd_proc_root); return -ENOMEM; }
1.1 version文件
如上代码所示,在 snd_info_init函数中除了创建/proc/asound ,紧接着在 snd_proc_root entry下(即以 snd_proc_root enter 作为 parent entry) 创建 “version” 文件(即 /proc/asound/version),并提供了read方法,代码详解略,cat /proc/asound/version如下:
root@rk3399:/# cat /proc/asound/version Advanced Linux Sound Architecture Driver Version k6.3.0.
1.2 devices文件
接着在snd_proc_root entry下创建 “devices” 文件(即 /proc/asound/devices),提供了read方法,cat /proc/asound/devices如下:
root@rk3399:/# cat /proc/asound/devices 1: : sequencer 2: [ 0- 0]: digital audio playback 3: [ 0- 0]: digital audio capture 4: [ 0] : control 5: [ 1- 0]: digital audio playback 6: [ 1- 0]: digital audio capture 7: [ 1] : control 33: : timer
对于devices的print格式如下:
- Control设备: “minor: [card_id] : control”;
- PCM设备:“minor: [card_id- device_id]: digital audio playback/capture”;
- timer设备: “minor: : timer”;
1.3 cards文件
接着在snd_proc_root entry下创建 “cards” 文件(即 /proc/asound/cards),提供了read方法,cat /proc/asound/cards如下:
root@rk3399:/# cat /proc/asound/cards 0 [hdmisound ]: simple-card - hdmi-sound hdmi-sound 1 [realtekrt5651co]: simple-card - realtek,rt5651-codec realtek,rt5651-codec
对于cards的print格式如下:
card_id [card_id_string ]: card_driver - card_shortname
card_longname
1.4 cardx目录
我们前面已经分析了创建声卡函数snd_card_new的执行流程,其内部会调用snd_info_card_create在snd_proc_root下创建 "cardx" 目录(即/proc/asound/cardx),并将对应的entry保存在card->proc_root,以便后面基于该声卡的snd device等节点均以其作为 parent entry;
root@rk3399:/# ll /proc/asound/card0 -r--r--r-- 1 root root 0 Aug 6 16:43 id dr-xr-xr-x 5 root root 0 Aug 6 16:43 pcm0c/ dr-xr-xr-x 5 root root 0 Aug 6 16:43 pcm0p/
在cardx目录下主要会对每个PCM设备创建对应的pcmxp/c目录,创建方式如下:
- 我们都知道在创建PCM设备时都调用snd_pcm_new函数,在该函数中也分别会对Playback&Capture调用snd_pcm_new_stream(PLAYBACK/CAPTURE)定义playback&capture设备名称;
- 然后则会调用snd_pcm_stream_proc_init函数,在该函数中会在card->proc_root entry下创建pcmxp/c目录(即 /proc/asound/cardx/pcmxp | pcmxc),并将对应的 entry 保存在 pcm->streams[PLAYBACK/CAPTURE]->proc_root;同时也会在 pcm->streams[]->proc_root下创建info文件(即/proc/asound/cardx/pcmxp|c/info),并提供read方法;
经过上述创建目录以及cat /proc/asound/cardx/pcmxp|c/info 如下所示:
root@rk3399:/# cat /proc/asound/card0/pcm0p/info card: 0 device: 0 subdevice: 0 stream: PLAYBACK id: ff8a0000.i2s-i2s-hifi i2s-hifi-0 name: ff8a0000.i2s-i2s-hifi i2s-hifi-0 subname: subdevice #0 class: 0 subclass: 0 subdevices_count: 1 subdevices_avail: 1 root@rk3399:/# cat /proc/asound/card1/pcm0p/info card: 1 device: 0 subdevice: 0 stream: PLAYBACK id: ff880000.i2s-rt5651-aif1 rt5651-aif1-0 name: ff880000.i2s-rt5651-aif1 rt5651-aif1-0 subname: subdevice #0 class: 0 subclass: 0 subdevices_count: 1 subdevices_avail: 1
对于pcm播放的最小单元是substream,故接着在snd_pcm_new_stream会对该pcm playback/capture stream下所有的substreams(substream_count一般都是1)调用snd_pcm_substream_proc_init函数,即在pcm->streams[]->proc root下创建subx目录(即/proc/asound/cardx/pcmxp|c/sub0),并将对应的entry保存在 pcm->streams[]->substream[0]->proc_root;
同时会在pcm->streams[]->substream[0]->proc_root下创建 info,hw_params,sw_params,status 文件(即 /proc/asound/cardx/pcmxp|c/pcmxp|c/sub0/info | hw_params |sw_params | status),并提供 read方法,分别执行cat后如下(在音频播放状态下执行):
root@rk3399:/# cd /proc/asound/card0/pcm0p/sub0 root@rk3399:/proc/asound/card0/pcm0p/sub0# ll -r--r--r-- 1 root root 0 Aug 6 17:04 hw_params -r--r--r-- 1 root root 0 Aug 6 17:04 info -rw-r--r-- 1 root root 0 Aug 6 17:04 prealloc -r--r--r-- 1 root root 0 Aug 6 17:04 prealloc_max -r--r--r-- 1 root root 0 Aug 6 17:04 status -r--r--r-- 1 root root 0 Aug 6 17:04 sw_params --w------- 1 root root 0 Aug 6 17:04 xrun_injection root@rk3399:/proc/asound/card0/pcm0p/sub0# cat info card: 0 device: 0 subdevice: 0 stream: PLAYBACK id: ff8a0000.i2s-i2s-hifi i2s-hifi-0 name: ff8a0000.i2s-i2s-hifi i2s-hifi-0 subname: subdevice #0 class: 0 subclass: 0 subdevices_count: 1 subdevices_avail: 0 root@rk3399:/proc/asound/card0/pcm0p/sub0# cat hw_params access: RW_INTERLEAVED format: S16_LE subformat: STD channels: 2 rate: 44100 (44100/1) period_size: 5513 buffer_size: 22052 root@rk3399:/proc/asound/card0/pcm0p/sub0# cat sw_params tstamp_mode: NONE period_step: 1 avail_min: 5513 start_threshold: 22052 stop_threshold: 22052 silence_threshold: 0 silence_size: 0 boundary: 6207086186423386112 root@rk3399:/proc/asound/card0/pcm0p/sub0# cat status state: RUNNING owner_pid : 5313 trigger_time: 7044.021765728 tstamp : 0.000000000 delay : 20524 avail : 1528 avail_max : 16539 ----- hw_ptr : 2697409 appl_ptr : 2717933
如上所示,对于hw_params&sw_params& status由于均用到了runtime,故只有在播放的时候才能print info。
特别地,在alsa驱动中当需要为pcm dma分配内存allocate pages时(即有调用 snd_pcm_lib_preallocate_pages_for_all(size,max))时,则会在pcm->streams[]->substream[0]->proc_root下创建 prealloc、prealloc_max文件(即 /proc/asound/cardx/pcmxp|c/sub0/prealloc | prealloc_max),并提供read&write方法,对该节点执行cat&echo如下:
root@rk3399:/proc/asound/card0/pcm0p/sub0# cat prealloc 512 root@rk3399:/proc/asound/card0/pcm0p/sub0# cat prealloc_max 18014398509481983 root@rk3399:/proc/asound/card0/pcm0p/sub0# echo 512 > prealloc root@rk3399:/proc/asound/card0/pcm0p/sub0# cat prealloc 512
1.5 pcm
在sound/core/pcm.c下alsa_pcm_info中会调用snd_pcm_proc_init函数,即会在snd_proc_root下创建pcm文件(即 /proc/asound/pcm),并提供read方法,cat /proc/asound/pcm 后如下:
root@rk3399:/proc/asound/card0/pcm0p/sub0# cat /proc/asound/pcm 00-00: ff8a0000.i2s-i2s-hifi i2s-hifi-0 : ff8a0000.i2s-i2s-hifi i2s-hifi-0 : playback 1 : capture 1 01-00: ff880000.i2s-rt5651-aif1 rt5651-aif1-0 : ff880000.i2s-rt5651-aif1 rt5651-aif1-0 : playback 1 : capture 1
参考文章
[3] Linux音频子系统