翻译:creator
第三章 管理card和组件
card实例
对于每个声卡,都要分配一个card记录。
一个card记录相当于一个声卡的总部。它管理着声卡中的所有设备(组件),例如PCM,mixers,MIDI,音序器等等。同时,card记录还保持着卡的ID和name字符串,管理proc文件,控制电源管理状态和热拔插。Card的组件列表用来在合适的时候释放资源。
如上,为了创建一个card实例,需要调用snd_card_new().
Struct snd_card *card;
card = snd_card_new(index, id, module, extra_size);
这个函数需要4个参数,card-index号,id字符串,module指针(通常是THIS_MODULE),extra-data空间的大小。最后一个参数是用来分配chip-specific数据card->private_data的。注意这个数据是通过snd_card_new()进行分配。
组件
card被创建之后,你可以把组件(设备)附加到card实例上面。在ALSA驱动 程序中,一个组件用一个snd_devie结构体表示。可以是PCM,控制接口或raw MIDI接口等等。每个组件都有个入口函数。
通过snd_device_new()函数来创建组件。
snd_device_new(card, SNDRV_DEV_XXX, chip, &ops);
它需要card指针,device_level(SNDRV_DEV_XXX),数据指针和回调函数(&ops)。device-level定义了组件类型和注册和卸载的顺序。对于大部分的组件来说, device_level已经定义好了。对于用户自定义的组件,你可以用SNDRV_DEV_LOWLEVEL.
这个函数自己并不分配数据空间。数据必须提前分配,同时把分配好的数据指针传递给这个函数作为参数。这个指针可以作为设备实例的标识符(上述代码的chip)。
每个ALSA预定义组件,如ac97或pcm都是通过在各自的构造函数中调用snd_device_new().在一些回调函数中定义了每个组件的析构函数。因此,你不需要关心这种组件的析构函数的调用。
假如你要创建一个自己的组件,你需要在dev_free中的回调函数ops中设定析构函数。
以便它可以通过snd_card_free()自动被释放。下面的例子就会显示chip-specific数据的实现。
Chip-Specific Data
chip-specific信息,如:i/o口,资源,或者中断号就会保持在 chip-specific记录里面。
Struct mychip{
....
};
通常来说,有两种方式来分配chip记录。
1.通过snd_card_new()分配。
如上面所述,你可以通过传递extra-data-length到snd_card_new()的第四个参数。
card = snd_card_new(index[dev], id[dev], THIS_MODULE, sizeof(struct mychip));
无论struct mychip是否是chip记录类型。
分配之后,可以通过如下方式引用:
struct mychip *chip = char->private_data;
通过这种方法,你不必分配两次。这个记录将会和card实例一起被释放。
2.分配一个extra device。
当你通过snd_card_new()(第四个参数要设定为NULL)分配之后,通过调用kzalloc().
Struct snd_card *card;
struct mychip *chip;
card = snd_card_new(index[dev], id[dev], THIS_MODULE, NULL);
....
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
chip记录应该至少拥有一个snd_card指针的成员变量。
Struct mychip {
struct snd_card *card;
....
};
然后,设定chip的card为snd_card_new返回的card指针。
chip->card = card;
下一步,初始化各个成员变量,使用一个含有特殊的ops的low-level设备注册chip记录。
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->private_data);
}
snd_mychip_free是真正的设备析构函数。
注册和释放
当所有的组件都被分配之后,就可以通过snd_card_register()来注册card实例了。对于设备文件的操作也会使能。那是因为,在调用snd_card_register()调用之前,组件是不能被外界调用的。假如注册失败,必须要调用snd_card_free()来释放card。
对于释放card,你可以简单的通过snd_card_free().如前所述,所有的组件都可以通过它来自动释放。
要特别注意,这些析构函数(包括snd_mychip_dev_free和snd_mychip_free)不能被定义加入__devexit前缀,因为它们也有可能被构造函数调用(当构造失败的时候调用)。
对于一个运行热拔插的设备,你可以用snd_card_free_when_closed。 它将推迟析构直到所有的设备都关闭。