一方面,从内核实现的角度来说,每个声卡的驱动,如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