widget注册细节
它的大致执行顺序是这样的:
int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_widget *widget,int num)
然后跳到
snd_soc_dapm_new_control_unlocked(dapm, widget)
且循环num次,然后跳到
struct snd_soc_dapm_widget *snd_soc_dapm_new_control_unlocked(
struct snd_soc_dapm_context *dapm, onst struct snd_soc_dapm_widget *widget)
在这里会新建一个snd_soc_dapm_widget *w,把传进去的widget的赋值给w,再根据不同的id进行相应的处理
INIT_LIST_HEAD(&w->list); /* 用于链接到声卡的widgets链表 */
INIT_LIST_HEAD(&w->dirty); /* 用于链接到声卡的dapm_dirty链表 */
list_add_tail(&w->list, &dapm->card->widgets); /* 将此widget加入所属的声卡结构体中 */
route注册细节
首先
static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_route *route)
在这里会执行一系列函数:
prefix = soc_dapm_prefix(dapm);
首先会检查该context有没有注册到component,有则返回,无则返回NULL
wsource = dapm_wcache_lookup(&dapm->path_source_cache, source);
wsink = dapm_wcache_lookup(&dapm->path_sink_cache, sink);
这里的wsource和wsink已经是widget了。 这两个函数在context结构体中查找链表中是否存在两个节点。主要是通过list_for_each_entry_from(w, wlist, list)这个循环遍历dapm->path_source_cache->widget链表(是内核链表,通过list连接),意思是:w是内核链表的第一个实体,然后每次都通过w = w->list.next进行循环,直到w==wlist,也就是等于头。若两者存在,则跳到skip_search:
dapm_wcache_update(&dapm->path_sink_cache, wsink);
dapm_wcache_update(&dapm->path_source_cache, wsource);
ret = snd_soc_dapm_add_path(dapm, wsource, wsink, route->control, route->connected);
前两行把path_sink_cache和path_source_cache的->widget更新了,目的是为了之后快速查找下一个widget。然后跳进snd_soc_dapm_add_path()函数:
-
初始化path并填充
-
if (control == NULL) { path->connect = 1;},连接点为NULL则直连,直接到4,否则继续到3
-
根据类型进入连接函数:dapm_connect_mux()、dapm_connect_mixer()等
-
添加到链表list_add(&path->list, &dapm->card->paths);前者加到后者
先来看看2部分的源码
if (control == NULL) {
path->connect = 1;
} else {
switch (wsource->id) {
case snd_soc_dapm_demux:
ret = dapm_connect_mux(dapm, path, control, wsource);
if (ret)
goto err;
break;
default:
break;
}
switch (wsink->id) {
case snd_soc_dapm_mux:
ret = dapm_connect_mux(dapm, path, control, wsink);
if (ret != 0)
goto err;
break;
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
case snd_soc_dapm_mixer_named_ctl:
ret = dapm_connect_mixer(dapm, path, control);
if (ret != 0)
goto err;
break;
default:
break;
}
}
再来看看连接函数: dapm_connect_mux()
static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_path *path, const char *control_name,
struct snd_soc_dapm_widget *w)
{
const struct snd_kcontrol_new *kcontrol = &w->kcontrol_news[0];
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val, item;
int i;
/* 通过判断是否是虚拟节点来设置item值 */
if (e->reg != SND_SOC_NOPM) {
soc_dapm_read(dapm, e->reg, &val);
val = (val >> e->shift_l) & e->mask;
item = snd_soc_enum_val_to_item(e, val);
} else {
item = 0;
}
/* 在texts中查找n次看是否有control_name */
i = match_string(e->texts, e->items, control_name);
if (i < 0)
return -ENODEV;
path->name = e->texts[i];
path->connect = (i == item);
return 0;
}
这里可以发现,path->name是texts中的内容,也就是说可选项就是text
dapm_connect_mixer()
static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_path *path, const char *control_name)
{
int i, nth_path = 0;
/* search for mixer kcontrol */
for (i = 0; i < path->sink->num_kcontrols; i++) {
if (!strcmp(control_name, path->sink->kcontrol_news[i].name)) {
path->name = path->sink->kcontrol_news[i].name;
dapm_set_mixer_path_status(path, i, nth_path++);
return 0;
}
}
return -ENODEV;
}
这里可以发现,path的name是control_name和sink的结合