1、系统调用过程:
snd_pcm_ioctl
snd_pcm_common_ioctl
snd_pcm_hw_params_user
snd_pcm_hw_params
//hw_params指向soc_pcm_hw_params函数
substream->ops->hw_params(substream, params);
soc_pcm_hw_params
soc_dai_hw_params
dai->driver->ops->hw_params(substream, params, dai);
2、dapm context:
3、is_connected_output_ep函数和is_connected_input_ep函数分析:
函数返回值是指定widget分别到输入、输出端点的完整路径的条数。
static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
struct list_head *list,
bool (*custom_stop_condition)(struct snd_soc_dapm_widget *i,enum snd_soc_dapm_direction))
{
return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_OUT,
is_connected_output_ep, custom_stop_condition);
}
static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
struct list_head *list,
bool (*custom_stop_condition)(struct snd_soc_dapm_widget *i,enum snd_soc_dapm_direction))
{
return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_IN,
is_connected_input_ep, custom_stop_condition);
}
从中看出无论是is_connected_output_ep函数还是is_connected_input_ep函数都调用了is_connected_ep函数;其中,is_connected_ep函数的第四个参数传入的是一个父函数,用于函数的递归调用,第五个参数是一个可选的用于判断什么时候停止的一个条件函数,既然是可选的,这里不分析它。
is_connected_ep函数:
int is_connected_ep(struct snd_soc_dapm_widget *widget,struct list_head *list,
enum snd_soc_dapm_direction dir,
int (*fn)(struct snd_soc_dapm_widget *, struct list_head *,
bool (*custom_stop_condition)(struct snd_soc_dapm_widget *,
enum snd_soc_dapm_direction)),
bool (*custom_stop_condition)(struct snd_soc_dapm_widget *,
enum snd_soc_dapm_direction))
{
//SND_SOC_DAPM_DIR_REVERSE:用于方向的取反,如果输入就是输出,输出就是输入
enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir);
struct snd_soc_dapm_path *path;
int con = 0;
/* do we need to add this widget to the list ? */
/*work_list用于widget加入的完整路径链表*/
if (list)
list_add_tail(&widget->work_list, list);
......
//判断该widget是否是端点等
if ((widget->is_ep & SND_SOC_DAPM_DIR_TO_EP(dir)) && widget->connected) {
widget->endpoints[dir] = snd_soc_dapm_suspend_check(widget);
return widget->endpoints[dir];
}
//注意:这里是rdir,和dir值相反
//以is_connected_output_ep函数功能为例,dir是SND_SOC_DAPM_DIR_OUT,
//rdir是SND_SOC_DAPM_DIR_IN,path连接着源点widget输出和目的widget输入,
//相当于导线(path)连接着两个器件(widget);另外,这里的所有的方向都是从path
//的角度出发的,源点widget为输入,目的widget为输出,源点widget到path为输入,path到
//目的widget为输出,现在以某个widget为起点遍历其上方向为输入的所有路径,即从该widget
//发出的路径,然后再以这些路径上的输出widget为起点进行递归遍历
snd_soc_dapm_widget_for_each_path(widget, rdir, path) {
......
if (path->connect) {
//从前往后遍历,把每个path的walking值都置1
path->walking = 1;
//fn函数回调,也是函数递归调用
//注意:这里是dir,和rdir值相反
con += fn(path->node[dir], list, custom_stop_condition);
//从后往前遍历,把每个path的walking值都置0
path->walking = 0;
}
}
//从该widget出发到输入或输出端点有几个,也就是有几条路径
widget->endpoints[dir] = con;
return con;
}
4、audio path路径添加
snd_soc_instantiate_card(struct snd_soc_card *card)
soc_probe_link_components(card, i, order);
soc_probe_component(card, component);
snd_soc_dapm_add_routes(dapm, component->dapm_routes,component->num_dapm_routes);
snd_soc_dapm_add_routes函数:
int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_route *route, int num)
{
int i= 0;
for (i = 0; i < num; i++) {
//route指针指向一个snd_soc_dapm_route结构的数组,每次访问一个数组元素
snd_soc_dapm_add_route(dapm, route);
route++;
}
}
snd_soc_dapm_add_route函数:
static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_route *route)
{
struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
struct snd_soc_dapm_widget *wtsource = NULL, *wtsink = NULL;
const char *sink;
const char *source;
int ret;
sink = route->sink;
source = route->source;
list_for_each_entry(w, &dapm->card->widgets, list) {
if (!wsink && !(strcmp(w->name, sink))) {
wtsink = w;
if (w->dapm == dapm) {
wsink = w;
if (wsource)
break;
}
continue;
}
if (!wsource && !(strcmp(w->name, source))) {
wtsource = w;
if (w->dapm == dapm) {
wsource = w;
if (wsink)
break;
}
}
}
/* use widget from another DAPM context if not found from this */
if (!wsink)
wsink = wtsink;
if (!wsource)
wsource = wtsource;
ret = snd_soc_dapm_add_path(dapm, wsource, wsink, route->control,route->connected);
return 0;
}
struct snd_soc_dapm_route结构体定义:
struct snd_soc_dapm_route {
const char *sink;
const char *control;
const char *source;
int (*connected)(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink);
};
snd_soc_dapm_add_path函数:根据传进来的route创建对应的path,每个path都是唯一的(widget A和B直接相连的路径只有一条,如果两条,A和B之间直接划"=".),普通的path都是没有path名,包括有明确控件名的,只有mux和mixer有。
static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink,
const char *control,
int (*connected)(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink))
{
struct snd_soc_dapm_widget *widgets[2];
enum snd_soc_dapm_direction dir;
struct snd_soc_dapm_path *path;
path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);
//path连接着源点widget输出和目的widget输入,相当于导线(path)连接着两个器件(widget);
//另外,这里的所有的方向都是从path的角度出发的,源点widget为输入,目的widget为输出,
//源点widget到path为输入,path到目的widget为输出
path->node[SND_SOC_DAPM_DIR_IN] = wsource;
path->node[SND_SOC_DAPM_DIR_OUT] = wsink;
widgets[SND_SOC_DAPM_DIR_IN] = wsource;
widgets[SND_SOC_DAPM_DIR_OUT] = wsink;
path->connected = connected;
INIT_LIST_HEAD(&path->list);
//根据route中的control值和sink widget的类型等条件赋值path->connect变量
if