再论声卡之control

来源:互联网 发布:常见的几个默认端口号 编辑:程序博客网 时间:2024/05/23 21:41

 

control是提供给用户使用。内核实现一个file_operations结构体,供给用户调用。

问题1、既然是file_operations,那么所有control
会当成字符设备注册,那么内核是如何做的?

答:首先是通过snd_soc_new_pcms函数里:
snd_soc_new_pcms
     ret = snd_card_create(idx, xid, codec->owner, 0, &codec->card);
           err = snd_ctl_create(card);
                snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops); //ops就是
snd_ctl_create的ops
                         list_add(&dev->list, &card->devices);
这里是吧一个类型为SNDRV_DEV_CONTROL的设备放到card->devices连表里。

那么接下来就有个地方要做的事情就是从连表里取出来,然后注册(
并且我们就可以猜到会调用snd_ctl_create的ops的注册函数snd_ctl_dev_register)。
那个地方就在:
 soc_probe(struct platform_device *pdev)
      snd_soc_register_card(card);
           snd_soc_instantiate_cards();
              snd_soc_instantiate_card(struct snd_soc_card *card)
                 ret = snd_card_register(codec->card);
                    err = snd_device_register_all(card)
snd_device_register_all函数顾名思义就知道在这个函数里会注册card->
devicessnd_devices的所有设备,具体如下:
snd_device_register_all(struct snd_card *card)
     list_for_each_entry(dev, &card->devices, list) {
          if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) {
                 if ((err = dev->ops->dev_register(dev)) < 0)
              ...........
调用card->devices每一个dev->ops->dev_register(dev)来注册该设备。
所以对于上面的类型为SNDRV_DEV_CONTROL的设备就是调用注册函数
snd_ctl_dev_register来实现的。如下:
 snd_ctl_dev_register(struct snd_device *device)
      sprintf(name, "controlC%i", cardnum);  //从这个名字就可以知道/dev下的
controlC0i设备节点就是对应这个设备了。
       snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1, &snd_ctl_f_ops
, card, name);  //controlC0i设备操作函数snd_ctl_f_ops。

好了到这里就把类型SNDRV_DEV_CONTROL的设备注册入内核了,并且提供了操作函数
snd_ctl_f_ops供用户系统调用。这个就是上片文件说道的“从用户空间调用角度谈
control”。


现在就看看如何通过操作函数来操作声卡的control了。
static const struct file_operations snd_ctl_f_ops =
{
 .owner = THIS_MODULE,
 .read =  snd_ctl_read,
 .open =  snd_ctl_open,
 .release = snd_ctl_release,
 .llseek = no_llseek,
 .poll =  snd_ctl_poll,
 .unlocked_ioctl = snd_ctl_ioctl,
 .compat_ioctl = snd_ctl_ioctl_compat,
 .fasync = snd_ctl_fasync,
};


在讲解用户操作control之前,得看看control的原型是怎么样的,以音量control为例子:
SOC_DOUBLE_TLV("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1,
out_tlv),


一看就是宏定义,我们展开得到如下东西:

{.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = (("Headphone Playback Volume"),
.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |SNDRV_CTL_ELEM_ACCESS_READWRITE,
.tlv.p = (out_tlv),
.info = snd_soc_info_volsw,
.get = snd_soc_get_volsw,
.put = snd_soc_put_volsw,
.private_value = (unsigned long)&(struct soc_mixer_control){.reg =
AC97_HEADPHONE, .shift = 8, .rshift =0,\
   .max = 31, .platform_max = 31, .invert = 1} }

现在先解释一下一些成员:
其中put设置该control;
get得到该control信息;
reg 是寄存器,该寄存器设置的对象包括左右设置(如音量有左右),shift
确定左设置在该寄存器的开始位数,rshift是右设置在该寄存器的开始位数。
max , invert是产生掩码位。

 

接下来就是用户如何使用control了。

 

 

原创粉丝点击