首先接着soc_probe函数看,它里面调用了snd_soc_register_card这个函数。
static int snd_soc_register_card(struct snd_soc_card *card)
{
if (!card->name || !card->dev)
return -EINVAL;
INIT_LIST_HEAD(&card->list);
card->instantiated = 0;
mutex_lock(&client_mutex);
/* 将soc_snd_card = snd_soc_s3c24xx_uda134x加入链表card_list */
list_add(&card->list, &card_list);
snd_soc_instantiate_cards();
mutex_unlock(&client_mutex);
dev_dbg(card->dev, "Registered card '%s'\n", card->name);
return 0;
}
这个函数将card添加到链表card_list中,即将card注册到内核中,即将socdev->card = snd_soc_s3c24xx_uda134x 添加到链表card_list中。
之后调用函数snd_soc_instantiate_cards。
/*
* Attempt to initialise any uninitalised cards. Must be called with
* client_mutex.
*/
static void snd_soc_instantiate_cards(void)
{
struct snd_soc_card *card;
list_for_each_entry(card, &card_list, list)
snd_soc_instantiate_card(card);
}
这个函数遍历card_list这个链表,然后对于每个节点、即每个card都会调用snd_soc_instantiate_card函数,这个函数将以card即snd_soc_s3c24xx_uda134x为参数,执行一系列操作。这个card_list中,就是调用snd_soc_inistantiate_cards的函数snd_soc_register_card的参数指向的那个card,这里就是snd_soc_s3c24xx_uda134x。下面让我们看看这个函数的实现。这个函数很长,一句一句分析。
分析这个函数前,先分析一个在linux内核代码中比较重要的函数,这个函数能够使我们通过结构体内的一个变量,还获取这个指向这个结构体的一个指针。这个函数定义如下:
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
这是个宏函数。首先分析第一行代码:
const typeof( ((type *)0)->member )*__mptr = (ptr);
其中typeof操作符返回的是变量的类型,也就是返回member的数据类型。这行代码定义了一个指向type结构体中member变量的指针__mptr,并将ptr赋给它。
第二行代码(type *)((char *)__mptr - offsetof(type,member) );。这行代码中用到了另外一个宏函数:offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
这个函数计算TYPE结构体中MEMBER成员相对于这个结构体起始地址的偏移。
最后,用__mptr –offsetof 就得到一个指向platform_device类型的指针,这个platform_device中的dev成员为card->dev。
我们先看一个例子,这个例子将在snd_soc_inistantiate_cards函数中用到:
struct platform_device *pdev =container_of(card->dev,
struct platform_device,
dev);
查看前面的代码,card->dev= s3c24xx_uda134x_snd_devdata->card->dev= s3c24xx_uda134x_snd_device->dev,根据前面的分析,card->dev里存放的是s3c24xx_uda134x_snd_device->dev在内存中的地址,这个地址减基platform_device的首地址与dev成员偏移,则得到s3c24xx_uda134x_snd_device的首地址。所以pdev = s3c24xx_uda134x_snd_device。
继续分析代码。
static void snd_soc_instantiate_card(struct snd_soc_card *card)
{
/* 见上面的分析,但是这里有个疑问,card->dev到底是什么东西呢?card->dev是在soc_probe里面设置的 */
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;
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);
}
简单地回顾一下上面的流程。
(1) 首先创建了一个名为"s3c24xx_uda134x"的platform device driver,在"s3c24xx_uda134x"中,主要完成了将s3c24xx_uda134x_snd_devdata这个设备资源给s3c24xx_uda134x_snd_device这个platform_device。
(2) 之后创建s3c24xx_uda134x_snd_device这个platform_device,名为"soc-audio",创建成功之后会调用响应的probe函数,这个probe函数就是soc_probe。
(3) 然后在soe_probe中注册一个SOC声卡(soc_snd_card),这个SOC声卡就是s3c24xx_uda134x_snd_devdata中的card成员变量。
(4) 最后根据这个声卡找到与之匹配的platform和dai,如果匹配成功,就会调用用了platform或者dai的probe函数。