2011年(14)
分类: LINUX
2011-08-03 00:59:01
Alsa-driver包括很多在开发中的驱动,以及一些2.2,2.4 linux内核版本的支持。当这些驱动稳定后,将移入alsa-kernel中,并最终在linux kernel
的sound目录下.
sound
/core
/oss
/seq
/oss
/instr
/drivers
/mpu401
/opl3
/i2c
/l3
/synth
/emux
/pci
/(cards)
/isa
/(cards)
/arm
/ppc
/sparc
/usb
/pcmcia /(cards)
/oss
core目录是alsa的核心,
core/oss目录主要是为了支持PCM和mixer的OSS模拟,rawmidi OSS模拟放在alsa的rawmidi文件里主要是因为它相当小。这样很多以前支持OSS
的应用程序也可以在Alsa上运行。
core/seq , alsa 音序器.core/seq/oss是alsa对OSS音序器模拟。
driver目录包含各种芯片驱动。
I2C目录,alsa i2c组件。尽管linux 有标准的i2c层,但alsa对于一些声卡有自己的i2c代码,原因是声卡仅仅需要一些简单的操作而标准的
I2C API 对于此目的太复杂。
PCI目录,主要是给PCI总线上的PCI声卡
ISA目录,主要是给ISA总线上的ISA声卡
arm, ppc, and sparc目录,这些架构下的某些声卡驱动,如pxa2xx-pcm.c PXA处理器的
USB目录,USB-audio 驱动
pcmcia,
OSS目录, OSS架构的文件,不属于alsa
第二部分就是创建卡和流,对于alsa驱动来说,是先分成卡0,卡1…,然后对于每一个卡的每一个cpu支持的dai(digit audio interface)也就是pcm接口 或者i2S接口等都要建立对应的流,一个dai有可能包含两个流,一个是录的一个是play的,但在我们的平台上对于i2S的dai是没有录音功能的,所以我们的平台只有一个卡,三个流,pcm的录和play,i2S的play;流的创建还是更多的考虑为上层服务的,它所提供的接口都是soc层的,这里非常重要的地方在于驱动的一个典型做法那就是如何把关键的内核数据结构和export到外部的/dev下的设备节点实现关联,比如:
关键数据结构struct snd_pcm,是根据cpu所固有的dai创建的,而对于每一个struct snd_pcm又可能用到两个substream(它们实现具体的流的播放等),它们之间的链接是通过它的内部数据成员struct snd_pcm_str streams[2];来连接的,而这个snd_pcm类型的指针是在函数snd_device_new里面通过device_data放到设备里面的,这个设备会在snd_device_register_all
的时候注册到/dev下面,并且调用dev_set_drvdata(preg->dev, private_data);来把这个指针放到设备的私有数据里面;而在需要使用的时候通过snd_pcm_playback_open里面的snd_lookup_minor_data函数取得其私有数据并返回的,这样就实现了设备节点和对应的驱动的数据结构的关联,这是一种非常普遍的做法;有了这个数据结构它就可以根据一定的原则取得对应于这个需求的substream,于是一切的操作都可以交给这个substream了;
第三部分就是control的创建,这个函数比较简单,就是把表micco_snd_controls里面已经定义好的controls模板创建controls,然后加入到card的controls列表中去;本身功能很清晰,但是对于我们平台来说,需要非常小心,因为这里决定了各个controls的序号,而这个序号是audio_controller访问硬件的索引,所以千万要小心尽量要维持目前的controls的序号,如果要额外添加新的controls一定要记得要放在micco_add_widgets后面来做,这样可以做到兼容,否则audio_controller的工作量就大了!
第四部分就是门的创建了,这个函数也是很清楚,就是把codec对应的门都加入到codec->dapm_widgets列表中去(这里的门的概念可以简单的理解为水管与水管之间的连接的地方,声音数据像水一样从水管里面流出来,源头可以是CPU了,也可以是modem,然后通过不同的门,流向不同的地方,比如speaker,比如蓝牙耳机等等),然后根据micco_audio_map把所有可能连在一起的门连接起来,这个表micco_audio_map的意思是{目的名字,控制点名字,源头名字},然后函数snd_soc_dapm_connect_input会根据这些名字去查表codec->dapm_widgets(先前已经把所有的门都加入了)找到它们再根据不同的类型做不同的连接,比如是mux之间的连接,mux和pga之间的连接等等,注意这里的连接其实只不过是说找到连接的可能性,它对于不同的门,找到其可能的source和sink,加入到对应的列表中去,具体细节如下:
首先,扫描整个codec所拥有的所有的门,如果它的名字和传入的sink的名字相同,则认为它就是这个路径的sink,如果它的名字和传入的source名字相同,则认为它是这个路径的source,如果源头或者sink没有找到都返回错误;然后分配一个struct snd_soc_dapm_path,这个数据结构的主要成分包括名字,source门,sink门,这条路径的control,这个源头和sink是否已经连接,是否已经走过(用在后面),这个数据结构会被挂在三个链表里面,一个是source的就是这个门会在很多的路径中,把它在这个路径中做source的path都连在一起,一个是sink的就是把这个门在所有这些由它做sink的path都连接在一起,一个是把所有的路径都需要连接在一起的这个是通过codec的dapm_paths来访问的;
list_add(&path->list, &codec->dapm_paths);
list_add(&path->list_sink, &wsink->sources);
list_add(&path->list_source, &wsource->sinks);
需要注意的时候,这里把路径的list_sink加入到了wsink门的sources列表里面,而把路径的list_source加入到wsource的sinks列表里面,所以当访问的时候从wsink门的sources出发就可以找到连接这个门作为sink的所有的路径,而从wsource的sinks列表出发就可以找到所有以这个门作为source的路径;
第三步就是为这个数据结构赋值:source,sink,初始化三个链表;第四步:如果control为空则把这个路径加入到相应的三个链表中去,并且路径设为已经连接,并返回;第五步:否则,根据sink的类型,如果是adc,dac,input,output,micbias,vmid,pre,post,则把路径加入到三个链表,设置已经连接的标志;如果是snd_soc_dapm_mux则调用dapm_connect_mux来处理;如果是mixer和switch则调用dapm_connect_mixer来处理,如果是hp,mic,line,spk,则把path加入到三个链表中去,但是设置成为连接的状态。