下面就看看上面提到的两个链表platform和dai_list。
先看platform
搜索platform,发现在函数snd_soc_register_platform中,将snd_soc_platform类型的变量加入到了这个链表,现在就要看是谁调用了snd_soc_register_platform这个函数。之后,在s3c24xx_soc_platform_init中找到了调用这个函数的代码,s3c24xx_soc_platform_init是一个用module_init修饰的模块初始化函数。
在这里,向内核注册的snd_soc_platform类型的变量为:
struct snd_soc_platform s3c24xx_soc_platform = {
.name = "s3c24xx-audio",
.pcm_ops = &s3c24xx_pcm_ops,
.pcm_new = s3c24xx_pcm_new,
.pcm_free = s3c24xx_pcm_free_dma_buffers,
};
下面分析函数snd_soc_register_platform,看看是怎么注册的。
int snd_soc_register_platform(struct snd_soc_platform *platform)
{
if (!platform->name)
return -EINVAL;
INIT_LIST_HEAD(&platform->list);
mutex_lock(&client_mutex);
list_add(&platform->list, &platform_list);
snd_soc_instantiate_cards();
mutex_unlock(&client_mutex);
pr_debug("Registered platform '%s'\n", platform->name);
return 0;
}
看了这个函数之后,发现和snd_soc_register_card一样,也调用了snd_soc_instantiate_cards这个函数,也就是又执行了一遍snd_soc_instantiate_card中匹配的过程。
再看dai_list这个链表。链表dai_list中的节点是在函数snd_soc_register_dai中添加的。模块初始化函数uda134x_init调用了snd_soc_register_dai,向内核注册一个snd_soc_dai类型的变量uda134x_dai。
struct snd_soc_dai uda134x_dai = {
.name = "UDA134X",
/* playback capabilities */
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = UDA134X_RATES,
.formats = UDA134X_FORMATS,
},
/* capture capabilities */
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = UDA134X_RATES,
.formats = UDA134X_FORMATS,
},
/* pcm operations */
.ops = &uda134x_dai_ops,
};
注册dai的函数定义如下。
int snd_soc_register_dai(struct snd_soc_dai *dai)
{
if (!dai->name)
return -EINVAL;
/* The device should become mandatory over time */
if (!dai->dev)
printk(KERN_WARNING "No device for DAI %s\n", dai->name);
if (!dai->ops)
dai->ops = &null_dai_ops;
INIT_LIST_HEAD(&dai->list);
mutex_lock(&client_mutex);
list_add(&dai->list, &dai_list);
snd_soc_instantiate_cards();
mutex_unlock(&client_mutex);
pr_debug("Registered DAI '%s'\n", dai->name);
return 0;
}
看到这里,发现这个函数同样调用了snd_soc_instantiate_cards函数。
看到这里,我们发现不管是调用了snd_soc_register_card、snd_soc_register_dai和snd_soc_register_platform的哪个一个,都会调用snd_soc_instantiate_card函数,只有上面那三个register函数都调用了,snd_soc_instantiate_card才会真正的执行成功。
static void snd_soc_instantiate_card(struct snd_soc_card *card)
{
/* 见上面的分析,但是这里有个疑问,card->dev到底是什么东西呢?这里的card-dev在soc_probe中赋值,pdev = s3c24xx_uda134x_snd_device */
struct platform_device *pdev = container_of(card->dev,
struct platform_device,
dev);
/* 在前面soc_probe函数中执行过card->socdev = socdev,那么这里codec_dev = soc_codec_dev_uda134x */
struct snd_soc_codec_device *codec_dev = card->socdev->codec_dev;
struct snd_soc_platform *platform;
struct snd_soc_dai *dai;
int i, found, ret, ac97;
/* 判断是否已经instantiate(实例化),若已经instantiate,这直接返回 */
if (card->instantiated)
return;
found = 0;
/* 遍历list链表,找到card->platform == platform时,则找到了与之匹配的platform,因此我们需要查找加入platform_list链表中的节点,搜索发现,这个链表的节点,是在snd_soc_register_platform 函数中加入的,我们将在稍后分析这个函数*/
list_for_each_entry(platform, &platform_list, list)
/* 如果snd_soc_s3c24xx_uda134x->platform即s3c24xx_soc_platform 在 platform_list 中,则found = 1,说明card找到与之匹配的platform */
if (card->platform == platform) {
found = 1;
break;
}
if (!found) {
dev_dbg(card->dev, "Platform %s not registered\n",
card->platform->name);
return;
}
ac97 = 0;
/* card = snd_soc_s3c24xx_uda134x,他的num_link = 1*/
for (i = 0; i < card->num_links; i++) {
found = 0;
/* 再遍历dai_list这个链表,这个链表的节点是在snd_soc_register_dai函数中加入的 */
list_for_each_entry(dai, &dai_list, list)
/* card->dai_link[i].cpu_dai = s3c24xx_i2s_dai,如果在dai_list中有s3c24xx_i2s_dai,则found = 1,找到card与之匹配的cpu_dai(digital audio interface) */
if (card->dai_link[i].cpu_dai == dai) {
found = 1;
break;
}
if (!found) {
dev_dbg(card->dev, "DAI %s not registered\n",
card->dai_link[i].cpu_dai->name);
return;
}
/* 判断是否有ac97控制 */
if (card->dai_link[i].cpu_dai->ac97_control)
ac97 = 1;
}
for (i = 0; i < card->num_links; i++) {
/* card->dai_link[i].codec_dai = uda134x_dai,如果uda134x_dai没有ops,则将null_dai_ops赋给ops */
if (!card->dai_link[i].codec_dai->ops)
card->dai_link[i].codec_dai->ops = &null_dai_ops;
}
/* If we have AC97 in the system then don't wait for the
* codec. This will need revisiting if we have to handle
* systems with mixed AC97 and non-AC97 parts. Only check for
* DAIs currently; we can't do this per link since some AC97
* codecs have non-AC97 DAIs.
*/
if (!ac97)
for (i = 0; i < card->num_links; i++) {
found = 0;
list_for_each_entry(dai, &dai_list, list)
if (card->dai_link[i].codec_dai == dai) {
found = 1;
break;
}
if (!found) {
dev_dbg(card->dev, "DAI %s not registered\n",
card->dai_link[i].codec_dai->name);
return;
}
}
/* Note that we do not current check for codec components */
dev_dbg(card->dev, "All components present, instantiating\n");
/* Found everything, bring it up */
/* 如果card->probe函数,则调用,snd_soc_s3c24xx_uda134x没有定义probe函数,不执行 */
if (card->probe) {
ret = card->probe(pdev);
if (ret < 0)
return;
}
for (i = 0; i < card->num_links; i++) {
/* 实际上cpu_dai = s3c24xx_i2s_dai */
struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
/* 如果s3c24xx_i2s_dai->probe函数,则调用,我们这有有probe函数 */
if (cpu_dai->probe) {
/* 实际上调用的是s3c24xx_i2s_probe */
ret = cpu_dai->probe(pdev, cpu_dai);
if (ret < 0)
goto cpu_dai_err;
}
}
/* codec_dev分析了,是soc_codec_dev_uda134x,存在probe函数 */
if (codec_dev->probe) {
/* 实际调用的是uda134x_soc_probe */
ret = codec_dev->probe(pdev);
if (ret < 0)
goto cpu_dai_err;
}
/* 这个就是我们前面在platform_list中找到的那个platform,实际上就是s3c24xx_soc_platform ,它没有probe函数 */
if (platform->probe) {
ret = platform->probe(pdev);
if (ret < 0)
goto platform_err;
}
/* 电源管理相关工作队列的初始化 */
/* DAPM stream work */
INIT_DELAYED_WORK(&card->delayed_work, close_delayed_work);
#ifdef CONFIG_PM
/* deferred resume work */
INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
#endif
/* instantiate完成 */
card->instantiated = 1;
return;
/* 以下为错误处理 */
platform_err:
if (codec_dev->remove)
codec_dev->remove(pdev);
cpu_dai_err:
for (i--; i >= 0; i--) {
struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
if (cpu_dai->remove)
cpu_dai->remove(pdev, cpu_dai);
}
if (card->remove)
card->remove(pdev);
}
当第二次看这个函数的时候,我们可以更加清晰地了解alsa驱动初始化过程。用一个图来说明: