Linux ALSA声卡驱动详细分析(8)

2019-01-27 16:59

.private_value = reg | (shift << 16) | (mask << 24); 然后,get回调函数可以这样实现:

static int snd_sbmixer_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) {

int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 16) & 0xff; int mask = (kcontrol->private_value >> 24) & 0xff; ....

//根据以上的值读取相应寄存器的值并填入value中 }

如果control的count字段大于1,表示control有多个元素单元,get回调函数也应该为value填充多个数值。 put回调函数

put回调函数用于把应用程序的控制值设置到control中。 [c-sharp] view plain copy

1. static int snd_myctl_put(struct snd_kcontrol *kcontrol, 2. struct snd_ctl_elem_value *ucontrol) 3. {

4. struct mychip *chip = snd_kcontrol_chip(kcontrol); 5. int changed = 0;

6. if (chip->current_value !=

7. ucontrol->value.integer.value[0]) { 8. change_current_value(chip,

9. ucontrol->value.integer.value[0]); 10. changed = 1; 11. }

12. return changed; 13. }

36

如上述例子所示,当control的值被改变时,put回调必须要返回1,如果值没有被改变,则返回0。如果发生了错误,则返回一个负数的错误号。

和get回调一样,当control的count大于1时,put回调也要处理多个control中的元素值。 创建Controls

当把以上讨论的内容都准备好了以后,我们就可以创建我们自己的control了。alsa-driver为我们提供了两个用于创建control的API:

? ?

snd_ctl_new1() snd_ctl_add()

我们可以用以下最简单的方式创建control: [c-sharp] view plain copy

1. err = snd_ctl_add(card, snd_ctl_new1(&my_control, chip));

2. if (err < 0) 3. return err;

在这里,my_control是一个之前定义好的snd_kcontrol_new对象,chip对象将会被赋值在kcontrol->private_data字段,该字段可以在回调函数中访问。

snd_ctl_new1()会分配一个新的snd_kcontrol实例,并把my_control中相应的值复制到该实例中,所以,在定义my_control时,通常我们可以加上__devinitdata前缀。snd_ctl_add则把该control绑定到声卡对象card当中。

37

元数据(Metadata)

很多mixer control需要提供以dB为单位的信息,我们可以使用

DECLARE_TLV_xxx宏来定义一些包含这种信息的变量,然后把control的tlv.p字段指向这些变量,最后,在access字段中加上

SNDRV_CTL_ELEM_ACCESS_TLV_READ标志,就像这样:

static DECLARE_TLV_DB_SCALE(db_scale_my_control, -4050, 150, 0);

static struct snd_kcontrol_new my_control __devinitdata = { ...

.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, ...

.tlv.p = db_scale_my_control, };

DECLARE_TLV_DB_SCALE宏定义的mixer control,它所代表的值按一个固定的dB值的步长变化。该宏的第一个参数是要定义变量的名字,第二个参数是最小值,以0.01dB为单位。第三个参数是变化的步长,也是以0.01dB为单位。如果该control处于最小值时会做出mute时,需要把第四个参数设为1。 DECLARE_TLV_DB_LINEAR宏定义的mixer control,它的输出随值的变化而线性变化。 该宏的第一个参数是要定义变量的名字,第二个参数是最小值,以0.01dB为单位。第二个参数是最大值,以0.01dB为单位。如果该control处于最小值时会做出mute时,需要把第二个参数设为TLV_DB_GAIN_MUTE。 这两个宏实际上就是定义一个整形数组,所谓tlv,就是Type-Lenght-Value的意思,数组的第0各元素代表数据的类型,第1个元素代表数据的长度,第三个元素和之后的元素保存该变量的数据。

38

Control设备的建立

Control设备和PCM设备一样,都属于声卡下的逻辑设备。用户空间的应用程序通过alsa-lib访问该Control设备,读取或控制control的控制状态,从而达到控制音频Codec进行各种Mixer等控制操作。

Control设备的创建过程大体上和PCM设备的创建过程相同。详细的创建过程可以参考本博的另一篇文章:Linux音频驱动之三:PCM设备的创建。下面我们只讨论有区别的地方。

我们需要在我们的驱动程序初始化时主动调用snd_pcm_new()函数创建pcm设备,而control设备则在snd_card_create()内被创建,snd_card_create()通过调用snd_ctl_create()函数创建control设备节点。所以我们无需显式地创建control设备,只要建立声卡,control设备被自动地创建。

和pcm设备一样,control设备的名字遵循一定的规则:controlCxx,这里的xx代表声卡的编号。我们也可以通过代码正是这一点,下面的是snd_ctl_dev_register()函数的代码: [c-sharp] view plain copy

1. /*

2. * registration of the control device 3. */

4. static int snd_ctl_dev_register(struct snd_device *device) 5. {

6. struct snd_card *card = device->device_data; 7. int err, cardnum; 8. char name[16]; 9.

10. if (snd_BUG_ON(!card)) 11. return -ENXIO;

12. cardnum = card->number;

13. if (snd_BUG_ON(cardnum < 0 || cardnum >= SNDRV_CARDS))

14. return -ENXIO;

15. /* control设备的名字 */

16. sprintf(name, \, cardnum);

17. if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,

39

18. &snd_ctl_f_ops, card, name)) < 0)

19. return err; 20. return 0; 21. }

snd_ctl_dev_register()函数会在snd_card_register()中,即声卡的注册阶段被调用。注册完成后,control设备的相关信息被保存在snd_minors[]数组中,用control设备的此设备号作索引,即可在snd_minors[]数组中找出相关的信息。注册完成后的数据结构关系可以用下图进行表述:

control设备的操作函数入口

用户程序需要打开control设备时,驱动程序通过snd_minors[]全局数组和此设备号,可以获得snd_ctl_f_ops结构中的各个回调函数,然后通过这些回调函数访问control中的信息和数据(最终会调用control的几个回调函数get,put,info)。详细的代码我就不贴了,大家可以读一下代码:/sound/core/control.c。

5. Linux ALSA声卡驱动之五:移动设备中的ALSA(ASoC)

40


Linux ALSA声卡驱动详细分析(8).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:乡镇2014年上半年工作总结 - 0

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: