ALSA系统结构: AUDIO HAL —— —— —— —— —— —— —— —— —— —— —— —— —— —— | | ------------- | | alsa-lib, alsa-util | }----> application layer ------------- | | | —————————————| /dev, /proc, etc. |—————— [user/kernel] — | | | ------------------------- | sound core | | --------------------------------------------------------- | | asoc | ------------------------- | | ———————————— [SW driver /HW driver] ———————————— --------------------------------- | ----------------------------------- | Audio Controller | | | Audio Codec | | on CHip | | | | --------------------------------- | ----------------------------------- — —— —— —— —— —— —— —— —— —— —— —— —— —— —— —— — BUS and HW Audio HAL接到PCM数字音频数据流的处理消息之后,通过alsa-lib调用内核里面的实现,通过vfs进入sound core接口,经过asoc框架,调用DAI(Digital Audio Inte**ce),通过DMA方式,codec硬件处理数据,然后输出。大概就是这样一个工作过程。下面我们分析alsa驱动的代码。 alsa 驱动:应用层通过使用alsa-lib或者直接操作设备文件,来完成对内核中驱动的功能调用。这篇文章主要是是讲的ALSA驱动层的一些设计结构和实现。 Linux的全部的声卡相关驱动代码都在sound目录下,另外在include目录下也有一个sound子目录保存头文件。 sound目录根目录的树形结构如下: . |-- Kconfig |-- Makefile |-- ac97_bus.c |-- aoa |-- arm |-- core |-- drivers |-- i2c |-- isa |-- last.c |-- last.o |-- mips |-- mkuroot |-- modules.order |-- oss |-- parisc |-- pci |-- pcmcia |-- ppc |-- sh |-- soc |-- sound_core.c |-- sound_firmware.c |-- soundcore.o |-- sparc |-- spi |-- synth `-- usb 我们可以看到一个last.c文件,它总是在最后执行,这一点,我们可以参考一下alsa sound的主目录下的Makefile,来证实。 主目录下的Makefile # Makefile for the Linux sound card driver # obj-$(CONFIG_SOUND) += soundcore.o obj-$(CONFIG_SOUND_PRIME) += sound_firmware.o obj-$(CONFIG_SOUND_PRIME) += oss/ obj-$(CONFIG_DMASOUND) += oss/ obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ obj-$(CONFIG_SND_AOA) += aoa/ # This one must be compilable even if sound is configured out obj-$(CONFIG_AC97_BUS) += ac97_bus.o ifeq ($(CONFIG_SND),y) obj-y += last.o endif soundcore-objs := sound_core.o 这里我们可以看到,last.o是在最后一个目标文件被加入的。而在这个文件中只定义了一个函数,这个函数用来遍历所有的声卡对象的全局列表—— snd_cards,它定义在core目录下的init.c文件中。定义如下: struct snd_card *snd_cards[SNDRV_CARDS]; EXPORT_SYMBOL(snd_cards); 在这个init.c文件中,主要定义了一些关于声卡对象的操作和管理的方法。 这些方**随着asoc的初始化过程中被调用,完成对所有的数据结构进行的初始化。 我们首先根据这个Makefile中的声明所提示,从core/目录下的文件结构入手。core目录下有自己的Makefile, 为了简明,我只截取了一部分。如下: # # Makefile for ALSA # Copyright (c) 1999,2001 by Jaroslav Kysela <http://www.360doc.com/mailto:perex@perex.cz> # snd-y := sound.o init.o memory.o info.o control.o misc.o device.o snd-$(CONFIG_ISA_DMA_API) += isadma.o snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o info_oss.o snd-$(CONFIG_SND_VMASTER) += vmaster.o snd-$(CONFIG_SND_JACK) += jack.o snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \ pcm_memory.o snd-page-alloc-y := memalloc.o snd-page-alloc-$(CONFIG_HAS_DMA) += sgbuf.o snd-rawmidi-objs := rawmidi.o snd-timer-objs := timer.o snd-rtctimer-objs := rtctimer.o snd-hwdep-objs := hwdep.o 从上面的主目录下的Makefile, 我们可以看到这里第一个被载入的结构是sound.o文件,我们就从core目录下的这个文件入手。sound.c中,只有一个名为"alsa"的 major为snd_major的字符设备的注册。这个设备只有提供一个snd_open()的方法。这个open方法的实现实际上不是最终调用,而只是通过对参数(inode, file)的解析,在全局设备的列表中找到并调用相应的实现过程。这个驱动接口的执行是在ALSA驱动一开始执行的,所以是整个框架初始化的最初阶段。下面我们可以分析具体的设备的生成过程。另外,这个sound.c文件中还提供Major等变量的全局声明,以及一些通用的设备注册的接口,之后具体的 alsa snd相关设备的生成需要调用这些接口。或者是间接调用。 这里的设备文件,为用户层提供了交互的接口。例如alsa-lib的实现,就是封装了对设备文件的直接操作,而HAL层,则使用alsa-lib提供的接口。这些文件的生成,都是在驱动初始化过程中完成的。 关于alsa声卡设备的文件: 在/dev/snd下,有这样几个设备 timer mixer controlC0 audio dsp pcmC0D0c pcmC0D0p 以及在/proc/asound下的注册文件: imx3stack card0 pcm timers cards devices version oss 接着是init.o文件,我们来看init.c。上面提到,snd_cards这个全局列表在init.c文件中定义,那么init.c之中也一定要提供对于声卡设备管理的方法。如snd_card_new()等。这里没有定义任何驱动接口,而只是提供全局变量的定义,以及全局的接口。 后面的memory.o info.o control.o misc.o device.o这几个,都是提供给init.c中的接口完成相应的设备的功能创建的工作。接下来是pcm的处理。驱动处理从上层的alsa-lib传下来的pcm数字流。pcm.c中主要定义pcm的实例的创建以及初始化,如接口snd_pcm_new()。这个接口是给codec的驱动来调用的。也就是声卡驱动初始化了设备的时候,创建声卡和pcm的实例,供asoc框架的调用。这里的具体工作流程,我们在后面的声卡驱动部分继续讲解。这里暂 snd_pcm_dev_register且提到一笔,方便理解这里的调用关系。 pcm_native.c文件主要是完成全局文件方法变量snd_pcm_f_ops[2]的定义。这个变量会被pcm.c中的方法引用,最终在初始化 pcm实例的时候,完成相应的设备注册的工作。我们可以参考这里的一些代码。定义在pcm.c中的snd_pcm_dev_register()方法,引用snd_pcm_f_ops[2],然后创建playback和capture两个设备文(/dev/snd/下的pcmC0D0c和 pcmC0D0p)件给上层应用。而这个pcm dev注册的方**引入到snd_pcm_new()中,所以,我们可以看到最终还是有codec初始化的时候,完成这里的工作。 info.o提供了/proc entry的创建接口,首先创建了/proc/asound,然后在这里继续创建下面的所有的 而pcm_lib.c封装了一些pcm的通用的处理接口给pcm_native.c使用。到这里,core目录下的Makefile其他的目标文件我们暂且不管,主要的部分我们已经提过了。到这里,core目录下文件的工作以及组织关系,我们已经有了大概的了解了。 回到主目录的Makefile。看过core之后,我们再看soc目录。这个子目录是asoc的主要框架,其中包含了soc的codec的驱动以及相关硬件驱动的操作。我们现来看看这里的Makefile会给我们那些提示。 snd-soc-core-objs := soc-core.o soc-dapm.o obj-$(CONFIG_SND_SOC) += snd-soc-core.o obj-$(CONFIG_SND_SOC) += codecs/ at32/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/ imx/ stmp3**/ obj-$(CONFIG_SND_SOC) += omap/ au1x/ blackfin/ 首先soc-core.o和soc-dapm.o两个文件显然是主要的框架。 soc-core.c文件是用来完成对soc_driver的驱动的注册。关于soc-audio的设备的注册。将会继续深入的去研究 |
联系客服