目前在一家公司担任软件总监,主要涉及智能手机,笔记本电脑的开发
分类: LINUX
2009-02-26 21:10:02
sound
/core
/oss
/seq
/oss
/instr
/ioctl32
/include
/drivers
/mpu401
/opl3
/i2c
/l3
/synth
/emux
/pci
/(cards)
/isa
/(cards)
/arm
/ppc
/sparc
/usb
/pcmcia /(cards)
/oss
这个目录包含了中间层,它是alsa驱动的核心.在这个目录中保存了原始的alsa模块.它的子目录包含了不同的模块并依赖于内核的配置.
这个目录保存了PCM和混音器的OSS模拟.rawmidi的OSS模拟在alsa的rawmidi代码中,因为它相当小.音序器的代码在core/seq/oss 目录中(见 下面).
这个目录包含了32位 ioctrl封给64位架构比如x86-64, ppc64 和sparc64. 对于32位和 alpha架构,他们不会被编译.
这个目录和它的子目录是为alsa音序器. 这个目录包含了音序器代码和主要的音序器模块比如snd-seq-midi, snd-seq-virmidi,等等.他们只有CONFIG_SND_SEQUENCER在内核配置中设置时才被编译.
这个目录包含了OSS 音序器模拟代码.
这个目录包含了音序器的乐器层.
这是alsa驱动的公公的头文件的地方,他们输出给用户空间,或者在不同目录的几个文件所引用。基本上,私有的头文件不要放在这个目录,但你也许仍然发现私有的头文件在这里,由于历史的原因:)
这个目录包含了代码,他们被在不同的架构下的不同驱动所共享.他们应次假使不是特定架构的。例如,dummy pcm驱动和串行的MIDI驱动在这个目录可以找到.在它的子目录,是与总线和cpu架构无关的模块代码.
MPU401和MPU401-UART 模块放在这里.
OPL3和OPL4 FM-合成器资料放在这里.
这个目录包含了ALSA i2c模块.
尽管在linux中有一个标准的i2c层,ALSA对于一些卡有自己的i2c代码,因为这个声卡之需要一些简单的操作而标准的i2c API对于此目的太复杂了.
这是一个子目录给ARM L3 i2c.
这个目录包含了合成器的中间层模块.
目前,只有Emu8000/Emu10k1合成器去冬在synth/emux子目录下.
这个目录和它的子目录保留了{BANNED}最佳高层的PCI声卡的卡模块,这些代码是为PCI总线特定的.
单个文件编译的驱动直接放在pci目录下,而需要多个源文件编译的驱动放在他们自己的子目录下(例如. emu10k1, ice1712).
这个目录和它的子目录保留了{BANNED}最佳高层的ISA声卡的卡模块.
他们是给特定的这些架构中的一个的{BANNED}最佳高层的声卡模块。
这个目录包含了USB-audio驱动.在{BANNED}最佳新的版本中,USB MIDI驱动集成在usb-audio驱动中.
PCMCIA, 特定的PCCard驱动在这里。 卡总线驱动将在pci目录中,因为他们的API和标准的PCI卡一样.
在linux2.6(或者更新的)目录中,OSS/Lite源文件放在这里.在ALSA去冬压缩包中,这个目录当然是空的:)
第2章.基本的PCI驱动流程
。。。。
第3章 卡和模块的管理
卡实体
对于每一个声卡,一个“卡”记录必须被分配。
一个卡是一个声卡的总部。它管理着声卡上的整个设备(组件)列表,比如PCM,mixer,MIDI,音序器,等等。卡纪录也保持这个声卡的ID和名字字符串,管理proc文件的根,和控制电源管理状态和热插拔断开。卡纪录上的组件列表被用来管理在销毁时正确的资源释放。
如上所述,为了创建一个卡实体,调用snd_card_create().
struct snd_card *card; int err; err = snd_card_create(index, id, module, extra_size, &card);
这个函数有5个参数,卡的索引,id字符串,模块指针(通常是THIS_MODULE),额外的数据空间大小和返回的卡实体的指针. 额外的数据空间大小参数用来分配card->private_data给芯片特定的数据.注意这些数据空间在snd_card_create()中分配.
(注:应该用snd_card_new来创建一个卡实例)
组件
在卡实例创建后,你可以挂载组件(设备)到卡实例上。在ALSA驱动中,一个组件是以一个
结构snd_device对象来表示。一个组件可以是一个PCM实例,一个控制实例,一个原MIDI接口 ,等等。每个这样的实例有一个组件入口 。一个组件可以通过snd_device_new()函数创建。
snd_device_new(card, SNDRV_DEV_XXX, chip, &ops);它以卡指针,设备级别 (SNDRV_DEV_XXX), 数据指针,和回调函数指针(&ops).设备级别定义了组件的类型以及注册,撤销的顺序。对于大多数组件,设备级别已经定义好了。对于一个用户定义的组件,你可以用SNDRV_DEV_LOWLEVEL.这个函数本身不分配数据空间。数据必须之前手动分配,并且其指针作为一个参数传入。指针(在上例中chip指示符)指向实例。每个以前定义的ALSA组件比如ac97和pcm在它的构造函数中调用snd_device_new().每个组件的析构函数定义在回调函数指针中。因此,你不需要关心为这个组件调用析构函数。
如果你想创建自己的组件,你需要设置析构函数指针在ops的dev_free回调函数中,这样它可以自动释放通过snd_card_free().下一个例子将展示一个芯片特定数据的实现.
芯片特定的数据
芯片特定的信息,例如.I/O口的地址,资源指针,或者irq号,存在芯片特定的记录中
struct mychip { .... };
通常,有两种方法分配芯片的记录.
1. 分配通过snd_card_create().
如上所述,你可以传递一个额外数据长度给snd_card_create()函数中的第4个参数,比如 err = snd_card_create(index[dev], id[dev], THIS_MODULE, sizeof(struct mychip), &card);
结构mychip是芯片记录的类型.
反过来,已分配的记录可以这样被访问:
struct mychip *chip = card->private_data; 通过这种方法,你不需要分配两次。记录将和卡实例一起释放。2. 分配一个额外的设备通过snd_card_create分配一个卡实例后(第4个参数为0),调用kzalloc().
struct snd_card *card; struct mychip *chip; err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card); ..... chip = kzalloc(sizeof(*chip), GFP_KERNEL); 芯片记录应当有一项,至少保持有卡指针,struct mychip { struct snd_card *card; .... }; 然后,在返回的芯片实例设置卡指针
chip->card = card; 下一步,初始化芯片记录的项,寄存器 ,作为一个带有特定的ops的low-level设备, static struct snd_device_ops ops = { .dev_free = snd_mychip_dev_free, }; .... snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);snd_mychip_dev_free()是一个设备析构函数,它将调用实际的析构函数.
static int snd_mychip_dev_free(struct snd_device *device) { return snd_mychip_free(device->device_data); } 这里 , snd_mychip_free()是实际的析构函数.
注册和释放
在所有的组件都赋值后,通过调用snd_card_register()来注册卡.在这点开始,可以访问设备文件了.也就是说,在snd_card_register()被调用之前,组件不可以被外界安全访问.如果这个调用失败,在用snd_card_free()释放卡后退出探测函数。
对于释放卡实例,你可以简单地调用snd_card_free().如前面所述,所有的组件通过这个调用自动地释放.
进一步说明,对于析构函数(snd_mychip_dev_free和snd_mychip_free)不能被定义为
__devexit前缀,因为他们也许在构造函数中调用,在false路径.
对于一个允许热插拔的设备,你可以用 snd_card_free_when_closed. 这个函数将推迟析构直到所有的设备关闭后.
第4章.PCI资源管理
。。。。。。
第5章.PCM接口
概要
ALSA PCM中间层非常强大,驱动只需要实现底层的函数以访问硬件。
为了访问PCM层,你需要先include .此外, 如果你访问一些和hw_param相关的函数,也许需要.
每个卡{BANNED}最佳多可以有4个PCM实例。一个pcm实例对应一个pcm设备文件。实例数目的限制来源于 Linux设备数目的可用位大小,一旦当64位设备号码被使用,我们可以得到更多的pcm实例。
pcm实例包括pcm回放和录音流,并且每个pcm流包括一个或者多个pcm子流.一些声卡支持多个回放函数.例如, emu10k1有一个32个立体声子流的PCM回放.在这种情况下,每一次打开 ,一个空闲的子流自动被选择并被打开。同时,当只有一个子流并且该子流已经被打开的时候,接下来的打开要么被阻塞,要么根据文件打开模式返回EAGAIN错误.但是你没必要在你的驱动中关心这些细节。PCM中间层将负责这些工作.
完整代码的例子
下面的例子代码没有包括任何的硬件访问的例程,只是展示它的架构,怎么去建立PCM接口.
例子5.1. PCM例子代码
。。。。。。