探究声卡之controls

本文深入探讨了声卡controls的内核实现,从snd_soc_add_controls函数的使用到snd_ctl_add如何加载control,详细阐述了如何通过snd_kcontrol结构体构建并管理controls,同时介绍了控制的注册和通知过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一方面,从内核实现的角度来说,每个声卡的驱动,如wm9713.c/uda134x.c,都有一句:
snd_soc_add_controls(。。。。)函数,这个函数就是用来添加control的。

 

另一方面,从用户空间来说,controls的实现目的就是提供给用户更好的控制和使用WM9714声卡。既然提供给用户使用,那么少不了系统调用来操作,也就是内核要实现内核操作函数。

在注册control的时候,我们是调用注册函数snd_ctl_dev_register来实现的,在该函数里就有:

if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
           &snd_ctl_f_ops, card, name)) < 0)

其中snd_ctl_f_ops就是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,
};


 

 

现在我们先从第一方面说起,

snd_soc_add_controls(。。。。)函数如何实现control的加载。

snd_soc_add_controls函数调用的核心函数就一个:err = snd_ctl_add(card, snd_soc_cnew(control, codec, NULL));

 

snd_ctl_add包括两部分:

1、为每个control构造snd_kcontrol_new结构体:

由snd_soc_cnew来实现:

        struct snd_kcontrol_new template;

        memcpy(&template, _template, sizeof(template));//_template就是实现好的
control元素,例如wm9714的是wm9713_snd_ac97_controls

        template.index = 0;

        snd_ctl_new1(&template, data);

对于 snd_ctl_new1函数,就是构造snd_kcontrol结构体:

struct snd_kcontrol kctl;

/*用上面构造好的template来设置kctl,ncontrol实际就是上面构造好的template*/

kctl.id.iface = ncontrol->iface;

 access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
   (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
          SNDRV_CTL_ELEM_ACCESS_INACTIVE|
          SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE|
          SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND|
          SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK));
 kctl.info = ncontrol->info;

kctl.count = ncontrol->count ? ncontrol->count : 1;
/*设置kctl.id*/
kctl.id.iface = ncontrol->iface;
kctl.id.device = ncontrol->device;
kctl.id.subdevice = ncontrol->subdevice;
if (ncontrol->name) {
  strlcpy(kctl.id.name, ncontrol->name, sizeof(kctl.id.name));
  if (strcmp(ncontrol->name, kctl.id.name) != 0)
   snd_printk(KERN_WARNING
       "Control name '%s' truncated to '%s'\n",
       ncontrol->name, kctl.id.name);
 }
kctl.id.index = ncontrol->index;

....

return snd_ctl_new(&kctl, access);

在snd_ctl_new函数里是为kctl分配空间kctl = kzalloc(sizeof(*kctl) + sizeof(
struct snd_kcontrol_volatile) * control->count, GFP_KERNEL),然后让kctl
指向上面设置好的kctl。

 

所以snd_soc_cnew函数实际就是把wm9713_snd_ac97_controls的每一个成员元素来实现
snd_kcontrol结构体,这样wm9713_snd_ac97_controls每个元素都有一个snd_kcontrol
结构体记录他们各自的信息。

 

2、通过snd_ctl_add函数使用上面设置好的snd_kcontrol结构体。

snd_ctl_add(card, snd_soc_cnew(control, codec, NULL));
首先通过snd_ctl_find_id(card, &id)(id就是kctl.id)查看是否已经包括该control
,如果没有就通过snd_ctl_find_hole函数来获得card->last_numid(last_numid
就是对链表card->controls里的controls根据count(一个control数量有count
个)数进行编号,这里明显就是得到,最大编号的下一个,就是没用到的编号)。为这个
control编完号之后,理所应当把他也挂到card->controls链表list_add_tail(&kcontrol
->list, &card->controls),然后累加control数目:card->controls_count +=
kcontrol->count;把id号又变为最大编号的下一个card->last_numid += kcontrol->
count;然后调用:

for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++) 
//再次提醒 kcontrol->count:同个control数量有count个
  snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id);

现在看看snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id)函数:
实现结构体struct snd_kctl_event *ev,但是实现的代码是放在看看
list_for_each_entry(ctl, &card->ctl_files, list) {}里。
可是card->ctl_files链表的实现是在snd_ctl_open函数里。我们只有在打开controlC
设备节点才会调用这个函数的list_add_tail(&ctl->list, &card->ctl_files);
所以在wm9714.c里调用snd_soc_add_controls函数的时候,这部分代码是不执行的。

所以snd_ctl_add函数就是把snd_kcontrol_new结构体(wm9714实现的就是
wm9713_snd_ac97_controls),里的control放到链表card->controls,并且更新card->
last_numid。

 

 


最后一个任务,就是往我们之前写的声卡驱动来添加一个control:
在wm9713.c前面添加:
static const struct snd_kcontrol_new wm9713_snd_ac97_controls[] = {
SOC_DOUBLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 7, 1, 1),
};

在sound_common的后面添加:
snd_soc_add_controls(codec, wm9713_snd_ac97_controls,
    ARRAY_SIZE(wm9713_snd_ac97_controls));
然后通过添加打印信息,可以知道,内核在没有加载control的时候card->last_numid=0
,加载了上面这个control的打印结果是:
kcontrol->count=1----card->last_numid=1;
而snd_ctl_notify函数里的list_for_each_entry(ctl, &card->ctl_files, list) {}
代码也确实没有执行。

好了,从内核角度看的control的加载就ok了。

 

转载请标明来源:http://blog.youkuaiyun.com/l_d_d/article/details/8309667


 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值