ALSA(一)
http://antkillerfarm.github.io/
參考文獻(xiàn)
ALSA(Advanced Linux Sound Architecture)是Linux內(nèi)核默認(rèn)的音頻驅(qū)動框架。以下是一些參考資料:
http://www.alsa-project.org/main/index.php/Tutorials_and_Presentations
包含官方提供的若干教程。
http://blog.sina.com.cn/s/blog_5707eebf0100tjbe.html
這篇文章介紹了ALSA的代碼文件結(jié)構(gòu)。
http://blog.csdn.net/droidphone/article/details/6271122
系列文章共8篇,詳細(xì)介紹了ALSA和ASOC。該博客還有7篇與DAPM有關(guān)的博文。
http://blog.chinaunix.net/uid-20602659-id-2981336.html
系列文章共2篇。主要偏重于ASOC驅(qū)動的具體實(shí)現(xiàn),而不是背后的機(jī)制原理。
關(guān)于ALSA的應(yīng)用層
查找PC本機(jī)驅(qū)動
首先使用lspci -v列出所有的硬件設(shè)備,其中當(dāng)然也包括了音頻設(shè)備。例如我的PC上是這樣的:
Audio device: Intel Corporation 6 Series/C200 Series Chipset Family High Definition Audio Controller (rev 05)Subsystem: ASUSTeK Computer Inc. Device 1b73Flags: bus master, fast devsel, latency 0, IRQ 54Memory at df000000 (64-bit, non-prefetchable) [size=16K]Capabilities: <access denied>Kernel driver in use: snd_hda_intel從驅(qū)動名snd_hda_intel可以很輕松的發(fā)現(xiàn),這個驅(qū)動的源代碼是sound/pci/hda/hda_intel.c。
在/dev/snd下,可以看到聲卡的子設(shè)備。
對于ASoC驅(qū)動來說,因?yàn)樗莗latform驅(qū)動,所以可以在/sys/bus/platform下,找到驅(qū)動的相關(guān)痕跡。
alsa-utils
alsa-utils是ALSA項(xiàng)目提供的工具軟件集,以下是一些測試樣例:
播放wave文件
aplay /mnt/nfs/test.wav
變頻播放(以44 KHz來播放音頻)
aplay -D rate_44k /mnt/nfs/test.wav
錄音,以20秒的間隔(-d 20),立體聲(-c 2),頻率是 8000Hz來錄制Wave格式音頻
arecord -d 20 -c 2 -t wav -r 8000 -f "Signed 16 bit Little Endian" /mnt/nfs/test.wav
測試混音播放(先是播放test1.wav,然后再同時播放test2.wav)
aplay -D plug:dmix_44k /mnt/nfs/test1.wav &
aplay -D plug:dmix_44k /mnt/nfs/test2.wav
設(shè)置放音增益(0 to 3)
amixer set Master 1
設(shè)置錄音音量(0-31)
amixer set Line 10
列出可用的ALSA設(shè)備
aplay -L
ALSA System on Chip (ASoC) layer
這是專為嵌入式設(shè)備設(shè)計的ALSA框架。官網(wǎng)是:
http://www.alsa-project.org/main/index.php/ASoC
上圖是TI某平臺的ASOC架構(gòu)圖。其中的ASoC layer可分為三個不同的部分:
1.Codec driver。這部分是平臺無關(guān)的,包括了音頻接口配置、音頻控制、電源管理和其他IO功能。所謂平臺是若干使用相同SOC的機(jī)型的集合。
2.Platform driver。這部分主要是平臺相關(guān)的音頻接口驅(qū)動和相關(guān)的DMA驅(qū)動。
3.Machine driver。這部分用于前兩部分之間的協(xié)調(diào),起到粘合劑的作用。它通常是與具體使用的機(jī)器相關(guān)的。
以三星設(shè)備為例,Platform driver和Machine driver在sound/soc/samsung下,該文件夾的Makefile節(jié)選如下:
# S3c24XX Platform Support snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o snd-soc-s3c2412-i2s-objs := s3c2412-i2s.oobj-$(CONFIG_SND_S3C24XX_I2S) += snd-soc-s3c24xx-i2s.o obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o# S3C24XX Machine Support snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o snd-soc-s3c24xx-simtec-objs := s3c24xx_simtec.oobj-$(CONFIG_SND_SOC_SAMSUNG_S3C24XX_UDA134X) += snd-soc-s3c24xx-uda134x.o obj-$(CONFIG_SND_SOC_SAMSUNG_SIMTEC) += snd-soc-s3c24xx-simtec.oCodec driver在sound/soc/codecs下,該文件夾的Makefile節(jié)選如下:
snd-soc-uda134x-objs := uda134x.o snd-soc-uda1380-objs := uda1380.oobj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.osnd_soc_register_codec函數(shù)用來注冊Codec driver。
snd_soc_register_component函數(shù)用來注冊外設(shè)接口驅(qū)動(例如I2S)。
snd_soc_register_platform函數(shù)用來注冊Platform driver。
Machine driver通過snd_soc_dai_link結(jié)構(gòu)的codec_name和platform_name成員,指定匹配的Codec driver和Platform driver。
三部分的加載順序:
1.驅(qū)動對象的加載,一般在模塊加載時進(jìn)行。各對象的先后順序無關(guān)緊要。
2.設(shè)備對象的加載,一般在驅(qū)動對象加載之后,而在ALSA加載之前。由于模塊加載的初始化優(yōu)先級是device_initcall,而alsa_sound_last_init函數(shù)的初始化優(yōu)先級是late_initcall_sync,因此設(shè)備對象的初始化優(yōu)先級設(shè)為late_initcall是比較妥當(dāng)?shù)摹?/p>
3.由于Machine driver在Codec driver和Platform driver之間起著橋梁作用,因此需要在加載了Codec和Platform的設(shè)備對象之后,再進(jìn)行Machine設(shè)備對象的加載。
PCM, I2S, DMA
無論何種形式的音頻,最終都要轉(zhuǎn)換成PCM格式的數(shù)據(jù)(有時也被稱作原始音頻數(shù)據(jù)),然后才能發(fā)送給相關(guān)的硬件。CPU和音頻芯片之間的數(shù)據(jù)接口,在PC上主要是AC97接口,而在嵌入式設(shè)備中,則多為I2S接口。
通常使用DMA方式傳輸原始音頻數(shù)據(jù)。在3.X內(nèi)核中,一般將PCM的DMA功能設(shè)置為Platform driver,而將I2S功能設(shè)置為該driver的一個component。
從DMA的實(shí)現(xiàn)方式來看,主要分兩種:
1.Platform driver包含i2s和pcm兩個文件。它的核心是設(shè)置snd_pcm_ops數(shù)據(jù)結(jié)構(gòu)。例如pxa2xx-i2s.c和pxa2xx-pcm.c。
2.Platform driver只包含i2s一個文件。它主要調(diào)用與dmaengine_pcm_platform結(jié)構(gòu)相關(guān)的函數(shù)。例如s3c24xx-i2s.c。
ASoC術(shù)語表
DAI: Digital Audio Interface
DAPM:Dynamic Audio Power Management
ALSA從2.6.X到3.X的變化細(xì)節(jié)
網(wǎng)上的文章有不少都是針對2.6.X的老內(nèi)核的,現(xiàn)將3.X引入的變化,羅列如下:(自己摘錄,僅供備忘,非官方內(nèi)容)
1.snd_card_create改為snd_card_new。
從Gstreamer到ALSA
0.概述
Gstreamer通過alsasink和alsasrc這兩個插件訪問ALSA API。這兩個插件在gst-plugins-base代碼的ext/alsa文件夾下。
gst-plugins-base的git地址是:
git://anongit.freedesktop.org/gstreamer/gst-plugins-base
ALSA API的代碼在alsa-lib中,它的git地址是:
git://git.alsa-project.org/alsa-lib.git
ALSA API會調(diào)用ALSA driver,而driver的代碼肯定在linux內(nèi)核中。
alsa-lib中的很多常用功能,被做成了動態(tài)鏈接庫,例如:alsa-lib/libasound_module_pcm_pulse.so,這些鏈接庫的代碼在alsa-plugins中。alsa-plugins的git地址是:
git://git.alsa-project.org/alsa-plugins.git
aplay等ALSA工具的代碼在alsa-utils中,其地址如下:
git://git.alsa-project.org/alsa-utils.git
1.open操作
gst-plugins-base/ext/alsa/gstalsasink.c: gst_alsasink_open
這一步之后,進(jìn)入alsa-lib作用域。
alsa-lib/src/pcm/pcm.c: snd_pcm_open
alsa-lib/src/pcm/pcm.c: snd_pcm_open_noupdate
alsa-lib/src/pcm/pcm.c: snd_pcm_open_conf
alsa-lib中pcm有很多插件,提供諸如硬件、復(fù)制、線性變換等操作。詳見alsa-lib/include/pcm_plugin.h。
alsa-lib/src/pcm/pcm_hw.c: _snd_pcm_hw_open
alsa-lib/src/pcm/pcm_hw.c: snd_pcm_hw_open
alsa-lib/include/local.h: snd_open_device
這個函數(shù)調(diào)用open系統(tǒng)調(diào)用,進(jìn)入內(nèi)核空間。在內(nèi)核空間中根據(jù)driver模型的不同,進(jìn)入linux-kernel/sound/core/pcm_native.c: snd_pcm_open(ALSA)或linux-kernel/sound/soc/soc-pcm.c: soc_pcm_open(ASOC),注意這里的snd_pcm_open和alsa-lib中的snd_pcm_open名稱相同,參數(shù)卻不同。
2.write操作
gst-plugins-base/ext/alsa/gstalsasink.c: gst_alsasink_write
這一步之后,進(jìn)入alsa-lib作用域。
alsa-lib/src/pcm/pcm.c: snd_pcm_writei
alsa-lib有兩種寫操作:snd_pcm_writei和snd_pcm_writen。snd_pcm_writei表示寫入交織的PCM數(shù)據(jù),而snd_pcm_writen表示寫入非交織的PCM數(shù)據(jù)。
alsa-lib/src/pcm/pcm_local.h: _snd_pcm_writei
alsa-lib/src/pcm/pcm_hw.c: snd_pcm_hw_writei
這個函數(shù)調(diào)用ioctl系統(tǒng)調(diào)用,參數(shù)為SNDRV_PCM_IOCTL_WRITEI_FRAMES,pcm數(shù)據(jù)的buffer,放在snd_xferi結(jié)構(gòu)中,傳入內(nèi)核空間。
linux-kernel/sound/core/pcm_native.c: snd_pcm_playback_ioctl1
linux-kernel/sound/core/pcm_lib.c: snd_pcm_lib_write
linux-kernel/sound/core/pcm_lib.c: snd_pcm_lib_write_transfer
這里會有兩個分支,以下僅討論DMA方式的處理。pcm數(shù)據(jù)放在snd_pcm_substream.runtime->dma_area指向的緩沖區(qū)。open的時候,使用mmap做好snd_pcm_substream.runtime->dma_area和snd_pcm_substream.runtime->dma_addr之間的映射。底層驅(qū)動只須操作dma_addr即可。
3.音量操作
音量操作雖然在Gstreamer和ALSA中都有,但彼此并無調(diào)用關(guān)系。Gstreamer中的音量調(diào)整是用軟件改變PCM數(shù)據(jù)實(shí)現(xiàn)的,可稱為軟音量。與之相對的是音頻硬件功放經(jīng)揚(yáng)聲器所產(chǎn)生的音量,是為硬音量。ALSA的音量可以是軟音量,也可以是硬音量。
Gstreamer的音量操作步驟:
gst-plugins-base/gst/playback/gstplaysink.c: gst_play_sink_set_volume
這個函數(shù)主要是通過設(shè)置playsink的volume屬性來實(shí)現(xiàn)操作。
Gstreamer軟音量的實(shí)現(xiàn)代碼在gst-plugins-base/gst/volume/gstvolume.c中。
除此之外,還有流媒體音量設(shè)置gst_stream_volume_set_volume。詳見gst-plugins-base/gst-libs/gst/audio/streamvolume.c。
ALSA設(shè)置音量的代碼片段如下(注意,該片段沒有異常處理):
void SetAlsaMasterVolume(long volume) {long min, max;snd_mixer_t *handle;snd_mixer_selem_id_t *sid;const char *card = "default";const char *selem_name = "Master";snd_mixer_open(&handle, 0);snd_mixer_attach(handle, card);snd_mixer_selem_register(handle, NULL, NULL);snd_mixer_load(handle);snd_mixer_selem_id_alloca(&sid);snd_mixer_selem_id_set_index(sid, 0);snd_mixer_selem_id_set_name(sid, selem_name);snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid);snd_mixer_selem_get_playback_volume_range(elem, &min, &max);snd_mixer_selem_set_playback_volume_all(elem, volume * max / 100);snd_mixer_close(handle); }ALSA的音量操作步驟:(只討論硬音量)
alsa-lib/src/mixer/simple.c: snd_mixer_selem_set_playback_volume
alsa-lib/src/mixer/simple_none.c: _snd_mixer_selem_set_volume
這一步將volume數(shù)據(jù)放到selem_none_t.str.vol結(jié)構(gòu)中。后面都是針對這個結(jié)構(gòu)的寫操作。
alsa-lib/src/mixer/simple_none.c: selem_write
alsa-lib/src/mixer/simple_none.c: selem_write_main
alsa-lib/src/mixer/simple_none.c: elem_write_volume
alsa-lib/src/control/hcontrol.c: snd_hctl_elem_write
alsa-lib/src/control/control.c: snd_ctl_elem_write
alsa-lib/src/control/control_hw.c: snd_ctl_hw_elem_write
這個函數(shù)調(diào)用ioctl系統(tǒng)調(diào)用,參數(shù)為SNDRV_CTL_IOCTL_ELEM_WRITE,進(jìn)入內(nèi)核空間。
linux-kernel/sound/core/control.c: snd_ctl_elem_write_user
linux-kernel/sound/core/control.c: snd_ctl_elem_write
驅(qū)動上層的數(shù)據(jù)放在snd_card.controls鏈表中。這個鏈表中的元素是snd_kcontrol類型的。而驅(qū)動底層使用snd_kcontrol_new類型的數(shù)據(jù)。
在設(shè)備初始化時,使用snd_soc_cnew或snd_ctl_new1函數(shù)從snd_kcontrol_new類型的模板中,生成相應(yīng)的snd_kcontrol類型的數(shù)據(jù)。snd_kcontrol_new類型的數(shù)據(jù)可以用SOC_SINGLE、SOC_DOUBLE_VALUE之類的宏生成,其默認(rèn)的put函數(shù)是snd_soc_put_volsw。
linux-kernel/sound/soc/soc-ops.c: snd_soc_put_volsw
linux-kernel/sound/soc/soc-io.c: snd_soc_component_update_bits
linux-kernel/drivers/base/regmap/regmap.c: regmap_update_bits_check
使用regmap技術(shù)映射I2C、SPI接口地址之后,即可調(diào)用相關(guān)函數(shù)修改相關(guān)音頻外設(shè)的寄存器值了。
總結(jié)
- 上一篇: linux内核研究(二)
- 下一篇: GStreamer(一)