DAPM控件注册函数源码解析

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()函数:

  1. 初始化path并填充

  2. if (control == NULL) { path->connect = 1;},连接点为NULL则直连,直接到4,否则继续到3

  3. 根据类型进入连接函数:dapm_connect_mux()、dapm_connect_mixer()等

  4. 添加到链表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的结合

猜你喜欢

转载自blog.csdn.net/hhx123456798/article/details/123320104