基于mini2440的Linux音频驱动完全解读(二)——ASoc层的初始化(1)

本文深入剖析Linux音频驱动中的ASoc层初始化过程,重点讲解snd_soc_register_card如何将card注册到内核,以及snd_soc_instantiate_cards如何遍历card_list并调用snd_soc_instantiate_card函数。通过对宏函数和offsetof的分析,揭示了设备驱动中的平台设备与声卡之间的关联和初始化流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先接着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函数。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值