谈谈amixer controls命令以及widget微件controls数据合成
widget微件
# amixer controls执行命令
应用层
alsa-lib库
amixer controls
controls
snd_hctl_load
snd_ctl_elem_list(hctl->ctl, &list); 第一次hctl->space等于NULL,所以只是返回list.count个数,之后calloc list.count个空间,再次读取实际数据[luther.gliethttp]
ctl->ops->element_list(ctl, list); 也就是snd_ctl_hw_ops.snd_ctl_hw_elem_list
snd_ctl_hw_elem_list
ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_LIST, list);
show_control_id // 打印显示比如:numid=12,iface=MIXER,name='Master Playback Switch'
printf("numid=%u,iface=%s,name='%s'",
snd_ctl_elem_id_get_numid(id),
control_iface(id),
内核层
core/control.c
snd_ctl_f_ops.snd_ctl_ioctl
{
snd_soc_new_pcms
snd_card_create
snd_ctl_create
snd_ctl_dev_register 创建"controlC%i"声卡对应的控制节点,
他的fops为snd_ctl_f_ops
}
snd_ctl_ioctl
case SNDRV_CTL_IOCTL_ELEM_LIST:
return snd_ctl_elem_list(card, argp);
snd_ctl_elem_list
while (space > 0 && plist != &card->controls) { // 遍历card->controls链表上的所有登记数据,当然包括微件widget合成的数据
kctl = snd_kcontrol(plist);
for (jidx = offset; space > 0 && jidx < kctl->count; jidx++) {
snd_ctl_build_ioff(id, kctl, jidx);
id++;
space--;
list.used++;
}
plist = plist->next;
offset = 0;
}
看看codec特有的controls都在什么时候登记到card->controls链表上的
wm8991_codec_probe
wm8991_add_controls // 将wm8991_snd_controls[]数组中的所有control条目添加到card->controls链表上
for (i = 0; i < ARRAY_SIZE(wm8991_snd_controls); i++) {
snd_ctl_add(codec->card, snd_soc_cnew(&wm8991_snd_controls[i],codec, NULL))
// 最终list_add_tail(&kcontrol->list, &card->controls);
}
关于widget微件control数据的添加[luther.gliethttp]
wm8991_add_widgets
for (i = 0; i < ARRAY_SIZE(wm8991_dapm_widgets); i++)
snd_soc_dapm_new_control(codec, &wm8991_dapm_widgets[i]); // 添加自己的widget微件到codec->dapm_widgets链表
/* set up the WM8991 audio map */
====================
/* dapm widget types */
enum snd_soc_dapm_type {
snd_soc_dapm_input = 0, /* input pin */
snd_soc_dapm_output, /* output pin */
snd_soc_dapm_mux, /* selects 1 analog signal from many inputs */
snd_soc_dapm_mixer, /* mixes several analog signals together */
snd_soc_dapm_pga, /* programmable gain/attenuation (volume) */
snd_soc_dapm_adc, /* analog to digital converter */
snd_soc_dapm_dac, /* digital to analog converter */
snd_soc_dapm_micbias, /* microphone bias (power) */
snd_soc_dapm_mic, /* microphone */
snd_soc_dapm_hp, /* headphones */
snd_soc_dapm_spk, /* speaker */
snd_soc_dapm_line, /* line input/output */
snd_soc_dapm_switch, /* analog switch */
snd_soc_dapm_vmid, /* codec bias/vmid - to minimise pops */
snd_soc_dapm_pre, /* machine specific pre widget - exec first */
snd_soc_dapm_post, /* machine specific post widget - exec last */
};
static const char *audio_map[][3] = {
......
{"LOMIX", "LOMIX Left DAC Switch", "Left DAC"},
......
};
====================
// 加入类似link软连接的机制
for (i = 0; audio_map[i][0] != NULL; i++) {
snd_soc_dapm_connect_input(codec, audio_map[i][0],
audio_map[i][1], audio_map[i][2]);
}
/**
* snd_soc_dapm_connect_input - connect dapm widgets
* @codec: audio codec
* @sink: name of target widget
* @control: mixer control name
* @source: name of source name
*
* Connects 2 dapm widgets together via a named audio path. The sink is
* the widget receiving the audio signal, whilst the source is the sender
* of the audio signal.
*
* Returns 0 for success else error.
*/
int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink,
const char * control, const char *source);
wm8991_add_widgets
snd_soc_dapm_new_widgets
dapm_new_mixer // 将上面snd_soc_dapm_connect_input合成的widget微件连接信息添加到card->controls链表上的合成控制通道[luther.gliethttp]
/* create new dapm mixer control */
static int dapm_new_mixer(struct snd_soc_codec *codec,
struct snd_soc_dapm_widget *w)
{
int i, ret = 0;
char name[32];
struct snd_soc_dapm_path *path;
/* add kcontrol */
for (i = 0; i < w->num_kcontrols; i++) {
/* match name */
list_for_each_entry(path, &w->sources, list_sink) {
/* mixer/mux paths name must match control name */
if (path->name != (char*)w->kcontrols[i].name)
continue;
/* add dapm control with long name */
snprintf(name, 32, "%s %s", w->name, w->kcontrols[i].name); // 合成出来的name,不能超过32字节
path->long_name = kstrdup (name, GFP_KERNEL);
if (path->long_name == NULL)
return -ENOMEM;
path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w,
path->long_name);
ret = snd_ctl_add(codec->card, path->kcontrol); // 将该合成出来的control添加到card->controls链表上
// 同时通过snd_ctl_find_hole(card, kcontrol->count);函数[luther.gliethttp]
// 生成kcontrol->id.numid = card->last_numid + 1;这个numid标识号
if (ret < 0) {
printk(KERN_ERR "asoc: failed to add dapm kcontrol %s\n",
path->long_name);
kfree(path->long_name);
path->long_name = NULL;
return ret;
}
}
}
return ret;
}
===下面是2.6.34内核中的实现===========================
/* create new dapm mixer control */
static int dapm_new_mixer(struct snd_soc_codec *codec,
struct snd_soc_dapm_widget *w)
{
int i, ret = 0;
size_t name_len;
struct snd_soc_dapm_path *path;
/* add kcontrol */
for (i = 0; i < w->num_kcontrols; i++) { // 遍历当前widget微件包含的所有w->kcontrols[]
/* match name */
list_for_each_entry(path, &w->sources, list_sink) {
// 因为所有使用本微件w的->kcontrols[i]的地方都都主动的调用list_add(&path->list_sink, &dest->sources);
// 将自己生成的path的list_sink添加到了本微件w的->sources链表上,这样也便于通过path来追踪当前正在使用
// w->kcontrols[i]控制通道的所有应用程序[luther.gliethttp]
/* mixer/mux paths name must match control name */
if (path->name != (char*)w->kcontrols[i].name) // 地址不一致,说明不match
continue;
/* add dapm control with long name.
* for dapm_mixer this is the concatenation of the
* mixer and kcontrol name.
* for dapm_mixer_named_ctl this is simply the
* kcontrol name.
*/
name_len = strlen(w->kcontrols[i].name) + 1;
if (w->id != snd_soc_dapm_mixer_named_ctl) // 是否不进行name合成
name_len += 1 + strlen(w->name);
path->long_name = kmalloc(name_len, GFP_KERNEL);
if (path->long_name == NULL)
return -ENOMEM;
switch (w->id) {
default:
snprintf(path->long_name, name_len, "%s %s",
w->name, w->kcontrols[i].name); // 将widget和kcontrols[i]的名字合成为登记注册时使用的新的kcontrol名字
break;
case snd_soc_dapm_mixer_named_ctl:
snprintf(path->long_name, name_len, "%s",
w->kcontrols[i].name); // 强制使用kcontrols[i]的名字[luther.gliehttp]
break;
}
path->long_name[name_len - 1] = '\0';
path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w,
path->long_name); // 创建一个名为path->long_name的kcontrol.
ret = snd_ctl_add(codec->card, path->kcontrol);
// 将其添加到card->controls链表上list_add_tail(&kcontrol->list, &card->controls);
// 这样path->long_name就是我们执行amixer controls看到的名字了
if (ret < 0) {
printk(KERN_ERR "asoc: failed to add dapm kcontrol %s: %d\n",
path->long_name,
ret);
kfree(path->long_name);
path->long_name = NULL;
return ret;
}
}
}
return ret;
}
阅读(3850) | 评论(1) | 转发(0) |