Linux音频子系统(3) -代码分析(以YMU836为例)

0. 前言

        数据流通过I2S的接口来传输,而I2c/Spi接口主要完成控制接口,例如控制声音的大小,功放的增益等操作。

        platform 部分对应的代码在 kernel_3.10/sound/soc/fsl/fsl_ssi.c;

        codec 部分对应的代码在 kernel_3.10/sound/soc/codecs/ymu836.c;

        machine 部分对应的代码在 kernel_3.10/sound/soc/fsl/imx-ymu836.c。

1. 硬件框图

        YMU836(codec)和SOC(platform)之间通过 I2S 搬运音频流数据,SOC和内存之间通过DMA搬运音频流数据。

        当需要发出声音信号的时候,数据从内存通过DMA搬运到soc的I2S模块,I2S模块再把数据发送到YMU836,然后通过扬声器或者耳机输出。

        当需要接受声音信号的时候,数据从外界通过声音采集设备(麦克风),进入YMU836,然后I2S模块,最后通过DMA搬运到内存。

        

 

2. platform部分

        前面有介绍过,platform主要负责DMA的控制和I2S的控制,即PCM dma和cpu dai两个部分。

module_platform_driver(fsl_ssi_driver);
	fsl_ssi_probe
		snd_soc_register_component
			snd_soc_register_dai			
				list_add(&dai->list, &dai_list);
			imx_pcm_dma_init
				snd_dmaengine_pcm_register
					snd_soc_add_platform
						list_add(&platform->list, &platform_list);

        module_platform_driver是一个宏,最终调用的是platform_driver_register这个接口,注册一个平台驱动,所以直接从fsl_ssi_driver的probe回调函数开始分析。

static struct platform_driver fsl_ssi_driver = {
	.driver = {
		.name = "fsl-ssi-dai",
		.owner = THIS_MODULE,
		.of_match_table = fsl_ssi_ids,
		.pm = &fsl_ssi_pm,
	},
	.probe = fsl_ssi_probe,
	.remove = fsl_ssi_remove,
};

static int fsl_ssi_probe(struct platform_device *pdev)
{
	······
	pm_runtime_enable(&pdev->dev);

	/* Register with ASoC */
	dev_set_drvdata(&pdev->dev, ssi_private);
	ret = snd_soc_register_component(&pdev->dev, &fsl_ssi_component, &ssi_private->cpu_dai_drv, 1);  // cpu dai
	if (ret) {
		dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
		goto error_dev;
	}

	if (ssi_private->ssi_on_imx) {
		ret = imx_pcm_dma_init(pdev, SND_DMAENGINE_PCM_FLAG_NO_RESIDUE, IMX_SSI_DMABUF_SIZE);	// pcm dma
	}
 
	return ret;
}

        cpu dai的注册过程如下:

// sound/soc/soc-core.c
int snd_soc_register_component(struct device *dev, const struct snd_soc_component_driver *cmpnt_drv,
			 struct snd_soc_dai_driver *dai_drv, int num_dai)
{
	if (1 == num_dai)
		ret = snd_soc_register_dai(dev, dai_drv); //注册 cpu dai
	else
		ret = snd_soc_register_dais(dev, dai_drv, num_dai);
	if (ret < 0) {
		dev_err(dev, "ASoC: Failed to regster DAIs: %d\n", ret);
		goto error_component_name;
	}

	list_add(&cmpnt->list, &component_list);
	return ret;
}

// sound/soc/soc-core.c
static int snd_soc_register_dai(struct device *dev, struct snd_soc_dai_driver *dai_drv)
{
	struct snd_soc_codec *codec;
	struct snd_soc_dai *dai;
	dev_dbg(dev, "ASoC: dai register %s\n", dev_name(dev));
	dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);
	dai->dev = dev;
	dai->driver = dai_drv;
	dai->dapm.dev = dev;

	list_for_each_entry(codec, &codec_list, list) {
		if (codec->dev == dev) {
			dev_dbg(dev, "ASoC: Mapped DAI %s to CODEC %s\n",
				dai->name, codec->name);
			dai->codec = codec;
			break;
		}
	}
	list_add(&dai->list, &dai_list);  //将cpu dai挂接到静态全局链表dai_list

	dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name);
	return 0;
}

        pcm dma的注册过程如下:

// sound/soc/fsl/imx-pcm-dma.c
int imx_pcm_dma_init(struct platform_device *pdev, unsigned int flags, size_t size)
{
    ······
	return snd_dmaengine_pcm_register(&pdev->dev, config, flags);	//--->
}

// sound/soc/soc-generic-dmaengine-pcm.c
int snd_dmaengine_pcm_register(struct device *dev,
	const struct snd_dmaengine_pcm_config *config, unsigned int flags)
{
	struct dmaengine_pcm *pcm;
	pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
	dmaengine_pcm_request_chan_of(pcm, dev);
	return snd_soc_add_platform(dev, &pcm->platform, &dmaengine_pcm_platform); // --->
}

// sound/soc/soc-core.c
int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform,
		const struct snd_soc_platform_driver *platform_drv)
{
	/* create platform component name */
	platform->name = fmt_single_name(dev, &platform->id);
	platform->dev = dev;
	platform->driver = platform_drv;
	platform->dapm.dev = dev;
	platform->dapm.platform = platform;
	platform->dapm.stream_event = platform_drv->stream_event;
 
	list_add(&platform->list, &platform_list);  //将pcm dma挂接到静态全局链表platform_list
	
	dev_dbg(dev, "ASoC: Registered platform '%s'\n", platform->name);
	return 0;
}

3. codec部分

        因为Codec驱动的代码要做到平台无关性,要使得Machine驱动能够使用该Codec,Codec驱动的首要任务就是确定snd_soc_codec(音频的控制)和snd_soc_dai(数据处理)的实例,并把它们注册到系统中。

module_spi_driver(ymu836_spi_driver)
	ymu836_spi_probe
		snd_soc_register_codec(&spi->dev,&soc_codec_dev_ymu836, &ymu836_dai, 1);		
			list_add(&codec->list, &codec_list)
			snd_soc_register_dais
				list_add(&dai->list, &dai_list);
			soc_codec_dev_ymu836
				ymu836_probe		//注册snd_soc_codec(音频的控制),在machine端会通过链表将snd_soc_codec结构体取出,并调用里面的初始化回调函数

        module_spi_driver宏和module_platform_driver宏类似,就不做赘述了

static struct spi_driver ymu836_spi_driver = {
	.driver = {
		.name = "ymu836",
		.owner = THIS_MODULE,
		.of_match_table = ymu836_of_match,
	},
	.probe =    ymu836_spi_probe,
	.remove =   ymu836_spi_remove,
	.shutdown = ymu836_spi_shutdown,
	.id_table = ymu836_spi_id,
};

static int ymu836_spi_probe(struct spi_device *spi)
{	
	int ret;
	ymu836 = devm_kzalloc(&spi->dev, sizeof(struct ymu836_priv), GFP_KERNEL);
	······
   spi->mode=0;
	ret = spi_setup(spi);
	spi_set_drvdata(spi, ymu836);
	spidev=spi;
	ret = snd_soc_register_codec(&spi->dev,&soc_codec_dev_ymu836, &ymu836_dai, 1);  //		

	return ret;
}

// sound/soc/soc-core.c
int snd_soc_register_codec(struct device *dev, const struct snd_soc_codec_driver *codec_drv,
			   struct snd_soc_dai_driver *dai_drv, int num_dai)
{
	size_t reg_size;
	struct snd_soc_codec *codec;
	int ret, i;

	dev_dbg(dev, "codec register %s\n", dev_name(dev));
	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);

	/* create CODEC component name */
	codec->name = fmt_single_name(dev, &codec->id);
	codec->write = codec_drv->write;
	codec->read = codec_drv->read;
	codec->volatile_register = codec_drv->volatile_register;
	codec->readable_register = codec_drv->readable_register;
	codec->writable_register = codec_drv->writable_register;
	codec->ignore_pmdown_time = codec_drv->ignore_pmdown_time;
	codec->dapm.bias_level = SND_SOC_BIAS_OFF;
	codec->dapm.dev = dev;
	codec->dapm.codec = codec;
	codec->dapm.seq_notifier = codec_drv->seq_notifier;
	codec->dapm.stream_event = codec_drv->stream_event;
	codec->dev = dev;
	codec->driver = codec_drv;  //传进来的soc_codec_dev_ymu836赋值给了codec
	codec->num_dai = num_dai;
	mutex_init(&codec->mutex);

	list_add(&codec->list, &codec_list);  //将ymu836 这款codec相关的信息挂接到静态全局链表codec_list

	/* register any DAIs */
	ret = snd_soc_register_dais(dev, dai_drv, num_dai);  // 注册codec dai

	dev_dbg(codec->dev, "ASoC: Registered codec '%s'\n", codec->name);
	return 0;
}

// sound/soc/soc-core.c
static int snd_soc_register_dais(struct device *dev, struct snd_soc_dai_driver *dai_drv, size_t count)
{
	struct snd_soc_codec *codec;
	struct snd_soc_dai *dai;
	int i, ret = 0;

	dev_dbg(dev, "ASoC: dai register %s #%Zu\n", dev_name(dev), count);
	list_for_each_entry(codec, &codec_list, list) {
		if (codec->dev == dev) {
			dev_dbg(dev, "ASoC: Mapped DAI %s to CODEC %s\n", dai->name, codec->name);
				dai->codec = codec;
				break;
			}
		}
		list_add(&dai->list, &dai_list);  // 将codec dai挂接到静态全局链表dai_list
		dev_dbg(dai->dev, "ASoC: Registered DAI '%s'\n", dai->name);
	}

	return 0;
}

4. machine部分

        通常有两种方法注册一个card:

        第一种是在machine驱动中注册一个名为 “soc-audio” 的 platform device,并将 struct snd_soc_card 结构变量作为这个device的私有数据,在 soc-core.c 调用 名为 “soc-audio” 的 platform drv的probe函数,probe中调用 snd_soc_register_card 注册这个card;

        第二种是直接在machine驱动中调用 snd_soc_register_card 函数注册card。

imx_ymu836_probe
	snd_soc_register_card
		snd_soc_instantiate_card		//关键接口
			soc_bind_dai_link				//绑定dais
				rtd->cpu_dai = cpu_dai;			//从链表dai_list中找到cpu_dai并存储在rtd中
				rtd->codec = codec;			//从链表codec_list中找到codec并存储在rtd中
				rtd->codec_dai = codec_dai;		//从链表dai_list中找到codec_dai并存储在rtd中
				rtd->platform = platform;		//从链表platform_list中找到platform并存储在rtd中
			codec->cache_init				//调用回调函数cache_init进行初始化			
			snd_card_create				
				snd_ctl_create			//.dev_register = snd_ctl_dev_register	===》疑问1			
					snd_ctl_dev_register
						snd_register_device    //生成dev/controlC%i设备文件,sound/core/control.c
							snd_register_device_for_dev
								preg->f_ops = f_ops;
								snd_minors[minor] = preg;	//将controlC设备对应的f_ops放到全局数组snd_minors[]中
								
			card->probe															//回调card的probe函数
			soc_probe_link_components					//针对rtd下的每个component,回调其初始化函数--->
				soc_probe_codec(card, cpu_dai->codec)			//调用platform侧codec层注册的driver中的probe函数	===>该codec在platform部分注册
				soc_probe_codec(card, codec_dai->codec)			//调用codec侧codec层注册的driver中的probe函数	===>该codec在codec部分注册
				soc_probe_platform(card, platform)			//调用platform侧dma层注册的driver中的probe函数	===>该codec在platform部分注册
								
			soc_probe_link_dais			
				soc_new_pcm
					snd_pcm_new
						_snd_pcm_new		//.dev_register = snd_pcm_dev_register  ===》疑问2
							snd_pcm_dev_register		//生成dev/pcmC0D0c或pcmC0D0p设备文件,sound/core/pcm.c	
								snd_register_device_for_dev
									preg->f_ops = f_ops;
									snd_minors[minor] = preg;	//将pcmC0D0c设备或pcmC0D0p设备对应的f_ops放到全局数组snd_minors[]中
					snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops);	//底层驱动需要实现rtd->ops这个结构体
					snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops);	//用户空间打开pcm设备文件以后,会间接调用rtd->ops里的底层驱动回调函数
					platform->driver->pcm_new(rtd)					//此回调函数中会分配环形缓冲区
					
			snd_soc_add_card_controls
			snd_soc_dapm_add_routes
			card->late_probe(card)
			snd_soc_dapm_new_widgets
								
			snd_card_register
				snd_device_register_all
					dev->ops->dev_register(dev) //调用疑问1和疑问2处赋值的dev_register函数指针

        其实呢,machine部分才是关键部分,platform和codec部分的很多内容都可以通过分析machine部分来得到结论,所以machine部分的解析也会杂乱一些,详解如下:

static struct platform_driver imx_ymu836_driver = {
	.driver = {
		.name = "imx-ymu836",
		.owner = THIS_MODULE,
		.pm = &snd_soc_pm_ops,
		.of_match_table = imx_ymu836_dt_ids,
	},
	.probe = imx_ymu836_probe,
	.remove = imx_ymu836_remove,
};
module_platform_driver(imx_ymu836_driver);

static int imx_ymu836_probe(struct platform_device *pdev)
{
	······
	ret = snd_soc_register_card(&data->card);	//---> 
	if (ret) {
		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
		goto fail;
	}
	priv->snd_card = data->card.snd_card;

	return ret;
}

// sound/soc/soc-core.c
int snd_soc_register_card(struct snd_soc_card *card)
{
	int i, ret;
    ······
	ret = snd_soc_instantiate_card(card);	//--->
	return ret;
}

// sound/soc/soc-core.c
static int snd_soc_instantiate_card(struct snd_soc_card *card)
{
	struct snd_soc_codec *codec;
	struct snd_soc_codec_conf *codec_conf;
	enum snd_soc_compress_type compress_type;
	struct snd_soc_dai_link *dai_link;
	int ret, i, order, dai_fmt;

	/* bind DAIs */
	for (i = 0; i < card->num_links; i++) {
		ret = soc_bind_dai_link(card, i);	//--->4-1代码
		if (ret != 0)
			goto base_error;
	}

	/* initialize the register cache for each available codec */
	list_for_each_entry(codec, &codec_list, list) {
		if (codec->cache_init)			//调用回调函数cache_init进行初始化
			continue;
           ······
	}

	/* card bind complete so register a sound card */
	ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,	//创建声卡--->4-2代码
			card->owner, 0, &card->snd_card);
	if (ret < 0) {
		dev_err(card->dev, "ASoC: can't create sound card for card %s: %d\n", card->name, ret);
		goto base_error;
	}
	card->snd_card->dev = card->dev;
	card->dapm.bias_level = SND_SOC_BIAS_OFF;
	card->dapm.dev = card->dev;
	card->dapm.card = card;
	list_add(&card->dapm.list, &card->dapm_list);


	/* initialise the sound card only once */
	if (card->probe) {				//回调card的probe函数
		ret = card->probe(card);
		if (ret < 0)
			goto card_probe_error;
	}

	/* probe all components used by DAI links on this card */
	for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; order++) {
		for (i = 0; i < card->num_links; i++) {
			ret = soc_probe_link_components(card, i, order);  //针对rtd下的每个component,回调其初始化函数--->4-3代码
			if (ret < 0) {
				dev_err(card->dev, "ASoC: failed to instantiate card %d\n", ret);
				goto probe_dai_err;
			}
		}
	}

	/* probe all DAI links on this card */
	for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; order++) {
		for (i = 0; i < card->num_links; i++) {
			ret = soc_probe_link_dais(card, i, order);	//针对rtd下的每个dai,回调其初始化函数--->4-4代码
			if (ret < 0) {
				dev_err(card->dev, "ASoC: failed to instantiate card %d\n", ret);
				goto probe_dai_err;
			}
		}
	}

	snd_soc_dapm_link_dai_widgets(card);
	if (card->controls)
		snd_soc_add_card_controls(card, card->controls, card->num_controls);  
	if (card->dapm_routes)
		snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,	card->num_dapm_routes);
	if (card->late_probe) 
		ret = card->late_probe(card);	
	snd_soc_dapm_new_widgets(&card->dapm);	

	if (card->fully_routed)
		list_for_each_entry(codec, &card->codec_dev_list, card_list)
			snd_soc_dapm_auto_nc_codec_pins(codec);

	ret = snd_card_register(card->snd_card);	//注册声卡,此后用户空间出现设备节点,用户程序可与底层交互了--->4-5代码
	if (ret < 0) {
		dev_err(card->dev, "ASoC: failed to register soundcard %d\n", ret);
		goto probe_aux_dev_err;
	}
	return 0;
}

4-1代码详解

// sound/soc/soc-core.c
static int soc_bind_dai_link(struct snd_soc_card *card, int num)
{
	struct snd_soc_dai_link *dai_link = &card->dai_link[num];
	struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
	struct snd_soc_codec *codec;
	struct snd_soc_platform *platform;
	struct snd_soc_dai *codec_dai, *cpu_dai;
	const char *platform_name;

	dev_dbg(card->dev, "ASoC: binding %s at idx %d\n", dai_link->name, num);

	/* Find CPU DAI from registered DAIs*/
	list_for_each_entry(cpu_dai, &dai_list, list) {
		if (dai_link->cpu_of_node &&
		    (cpu_dai->dev->of_node != dai_link->cpu_of_node))
			continue;
		if (dai_link->cpu_name &&
		    strcmp(dev_name(cpu_dai->dev), dai_link->cpu_name))
			continue;
		if (dai_link->cpu_dai_name &&
		    strcmp(cpu_dai->name, dai_link->cpu_dai_name))
			continue;

		rtd->cpu_dai = cpu_dai;		//找到cpu_dai并存储在rtd中
	}

	/* Find CODEC from registered CODECs */
	list_for_each_entry(codec, &codec_list, list) {
		if (dai_link->codec_of_node) {
			if (codec->dev->of_node != dai_link->codec_of_node)
				continue;
		} else {
			if (strcmp(codec->name, dai_link->codec_name))
				continue;
		}

		rtd->codec = codec;		//找到codec并存储在rtd中

		/*
		 * CODEC found, so find CODEC DAI from registered DAIs from
		 * this CODEC
		 */
		list_for_each_entry(codec_dai, &dai_list, list) {
			if (codec->dev == codec_dai->dev &&
				!strcmp(codec_dai->name,
					dai_link->codec_dai_name)) {

				rtd->codec_dai = codec_dai;		//找到codec_dai并存储在rtd中
			}
		}
	}

	/* if there's no platform we match on the empty platform */
	platform_name = dai_link->platform_name; //目前为止,并没有看到有任何地方给platform_name赋值,所以此时的platform_name应该是空的
	if (!platform_name && !dai_link->platform_of_node)
		platform_name = "snd-soc-dummy";

	/* find one from the set of registered platforms */
	list_for_each_entry(platform, &platform_list, list) {
		if (dai_link->platform_of_node) {
			if (platform->dev->of_node !=
			    dai_link->platform_of_node)
				continue;
		} else {
			if (strcmp(platform->name, platform_name))
				continue;
		}

		rtd->platform = platform;		//找到platform并存储在rtd中
	}
	card->num_rtd++;
	return 0;
}

4-2 代码详解

// sound/core/init.c
int snd_card_create(int idx, const char *xid, struct module *module, int extra_size, struct snd_card **card_ret)
{
	struct snd_card *card;
	int err, idx2;
    ······
	/* the control interface cannot be accessed from the user space until */
	/* snd_cards_bitmask and snd_cards are set with snd_card_register */
	err = snd_ctl_create(card);
	if (err < 0) {
		snd_printk(KERN_ERR "unable to register control minors\n");
		goto __error;
	}
	return 0;
}

// sound/core/control.c
int snd_ctl_create(struct snd_card *card)
{
	static struct snd_device_ops ops = {
		.dev_free = snd_ctl_dev_free,
		.dev_register =	snd_ctl_dev_register,   // ===》疑问1
		.dev_disconnect = snd_ctl_dev_disconnect,
	};

	return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
}

static int snd_ctl_dev_register(struct snd_device *device)
{
	struct snd_card *card = device->device_data;
	int err, cardnum;
	char name[16];

	cardnum = card->number;
	sprintf(name, "controlC%i", cardnum);
   //生成dev/controlC%i设备文件
	if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1, &snd_ctl_f_ops, card, name)) < 0)
		return err;
	return 0;
}

static inline int snd_register_device(int type, struct snd_card *card, int dev,
				      const struct file_operations *f_ops,
				      void *private_data,
				      const char *name)
{
	return snd_register_device_for_dev(type, card, dev, f_ops,
					   private_data, name,
					   snd_card_get_device_link(card));
}

int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
				const struct file_operations *f_ops,
				void *private_data,
				const char *name, struct device *device)
{
	int minor;
	struct snd_minor *preg;

	preg = kmalloc(sizeof *preg, GFP_KERNEL);
	preg->type = type;
	preg->card = card ? card->number : -1;
	preg->device = dev;
	preg->f_ops = f_ops;       //重点操作之一
	preg->private_data = private_data;
	preg->card_ptr = card;

 //将controlC设备对应的f_ops放到全局数组snd_minors[]中,用户空间打开设备文件时会从snd_minors[]数组中取出对应的ops操作集
	snd_minors[minor] = preg;     
	preg->dev = device_create(sound_class, device, MKDEV(major, minor),
				  private_data, "%s", name);
	return 0;
}

4-3 代码详解

static int soc_probe_link_components(struct snd_soc_card *card, int num,
				     int order)
{
	struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
    // 4-1代码中对cpu_dai、codec_dai、platform进行了赋值
	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;  
	struct snd_soc_dai *codec_dai = rtd->codec_dai;
	struct snd_soc_platform *platform = rtd->platform;
	int ret;

	/* probe the CPU-side component, if it is a CODEC */
	if (cpu_dai->codec &&
	    !cpu_dai->codec->probed &&
	    cpu_dai->codec->driver->probe_order == order) {
         //调用platform侧codec层注册的driver中的probe函数 ===>该codec在platform部分注册
		ret = soc_probe_codec(card, cpu_dai->codec);	
		if (ret < 0)
			return ret;
	}

	/* probe the CODEC-side component */
	if (!codec_dai->codec->probed &&
	    codec_dai->codec->driver->probe_order == order) {
         //调用codec侧codec层注册的driver中的probe函数 ===>该codec在codec部分注册
		ret = soc_probe_codec(card, codec_dai->codec);
		if (ret < 0)
			return ret;
	}

	/* probe the platform */
	if (!platform->probed &&
	    platform->driver->probe_order == order) {
         //调用platform侧dma层注册的driver中的probe函数 ===>该codec在platform部分注册
		ret = soc_probe_platform(card, platform);
		if (ret < 0)
			return ret;
	}

	return 0;
}

static int soc_probe_codec(struct snd_soc_card *card, struct snd_soc_codec *codec)
{
	int ret = 0;
	const struct snd_soc_codec_driver *driver = codec->driver;
	struct snd_soc_dai *dai;

	codec->card = card;
	codec->dapm.card = card;
	soc_set_name_prefix(card, codec);
	/* Create DAPM widgets for each DAI stream */
	list_for_each_entry(dai, &dai_list, list) {
		if (dai->dev != codec->dev)
			continue;
		snd_soc_dapm_new_dai_widgets(&codec->dapm, dai);
	}
	if (driver->probe) {	// 调用driver里对应的probe回调函数
		ret = driver->probe(codec);
		if (ret < 0) {
			dev_err(codec->dev, "ASoC: failed to probe CODEC %d\n", ret);
			goto err_probe;
		}
	}
	/* mark codec as probed and add to card codec list */
	codec->probed = 1;
	list_add(&codec->card_list, &card->codec_dev_list);
	list_add(&codec->dapm.list, &card->dapm_list);
	return 0;
}

4-4代码详解

static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
{
	int ret;
	
	if (!dai_link->params) {
		/* create the pcm */
		ret = soc_new_pcm(rtd, num);	//针对每个rtd(也就是每个dai_link),创建一个PCM逻辑设备--->
		if (ret < 0) {
			dev_err(card->dev, "ASoC: can't create pcm %s :%d\n",
		        dai_link->stream_name, ret);
			return ret;
    	}
	}	
	return 0;
}

// sound/soc/soc-pcm.c
int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
{
	struct snd_soc_platform *platform = rtd->platform;
	struct snd_soc_dai *codec_dai = rtd->codec_dai;
	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
	struct snd_pcm *pcm;
	char new_name[64];
	int ret = 0, playback = 0, capture = 0;

		if (rtd->dai_link->dynamic)
			snprintf(new_name, sizeof(new_name), "%s (*)",
				rtd->dai_link->stream_name);
		else
			snprintf(new_name, sizeof(new_name), "%s %s-%d",
				rtd->dai_link->stream_name, codec_dai->name, num);
            //创建PCM逻辑设备 --->
		ret = snd_pcm_new(rtd->card->snd_card, new_name, num, playback,	capture, &pcm);
	}
	if (ret < 0) {
		dev_err(rtd->card->dev, "ASoC: can't create pcm for %s\n",
			rtd->dai_link->name);
		return ret;
	}
	dev_dbg(rtd->card->dev, "ASoC: registered pcm #%d %s\n",num, new_name);

	/* DAPM dai link stream work */
	INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);

	if (playback)
           //底层驱动需要实现rtd->ops这个结构体
		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops);	

	if (capture)
            //用户空间打开pcm设备文件以后,会间接调用rtd->ops里的底层驱动回调函数
		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops);	

	if (platform->driver->pcm_new) {	//此回调函数中会分配环形缓冲区
		ret = platform->driver->pcm_new(rtd);
		if (ret < 0) {
			dev_err(platform->dev, "ASoC: pcm constructor failed: %d\n", ret);
			return ret;
		}
	}
	return ret;
} 

int snd_pcm_new(struct snd_card *card, const char *id, int device,
		int playback_count, int capture_count, struct snd_pcm **rpcm)
{
	return _snd_pcm_new(card, id, device, playback_count, capture_count, false, rpcm);
}

// sound/core/pcm.c
static int _snd_pcm_new(struct snd_card *card, const char *id, int device,
		int playback_count, int capture_count, bool internal,
		struct snd_pcm **rpcm)
{
	struct snd_pcm *pcm;
	int err;
	static struct snd_device_ops ops = {
		.dev_free = snd_pcm_dev_free,
		.dev_register =	snd_pcm_dev_register,     // ===》疑问2
		.dev_disconnect = snd_pcm_dev_disconnect,
	};
   ······
	if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) {
		snd_pcm_free(pcm);
		return err;
	}
	if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)) < 0) {
		snd_pcm_free(pcm);
		return err;
	}
	return 0;
}

static int snd_pcm_dev_register(struct snd_device *device)
{
	int cidx, err;
	struct snd_pcm_substream *substream;
	struct snd_pcm_notify *notify;
	char str[16];
	struct snd_pcm *pcm;
	struct device *dev;

	/* register pcm 生成dev/pcmC0D0c或pcmC0D0p设备文件*/
	err = snd_register_device_for_dev(devtype, pcm->card,
					  pcm->device,
					  &snd_pcm_f_ops[cidx],
					  pcm, str, dev);
	return 0;
}

int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
				const struct file_operations *f_ops,
				void *private_data,
				const char *name, struct device *device)
{
	int minor;
	struct snd_minor *preg;

	preg = kmalloc(sizeof *preg, GFP_KERNEL);
	preg->type = type;
	preg->card = card ? card->number : -1;
	preg->device = dev;
	preg->f_ops = f_ops;       //重点操作之一
	preg->private_data = private_data;
	preg->card_ptr = card;

 //将pcmC0D0c设备或pcmC0D0p设备对应的f_ops放到全局数组snd_minors[]中,用户空间打开设备文件时会从snd_minors[]数组中取出对应的ops操作集
	snd_minors[minor] = preg;     
	preg->dev = device_create(sound_class, device, MKDEV(major, minor),
				  private_data, "%s", name);
	return 0;
}

4-5代码详解

// sound/core/init.c
int snd_card_register(struct snd_card *card)
{
	int err;
	if (!card->card_dev) {
		card->card_dev = device_create(sound_class, card->dev,
					       MKDEV(0, 0), card,
					       "card%i", card->number);
	}

	if ((err = snd_device_register_all(card)) < 0)  // --->
		return err;
	snd_cards[card->number] = card;
	mutex_unlock(&snd_card_mutex);
	init_info_for_card(card);

	return 0;
}

// sound/core/device.c
int snd_device_register_all(struct snd_card *card)
{
	struct snd_device *dev;
	int err;
	
	list_for_each_entry(dev, &card->devices, list) {
		if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) {
                    //调用疑问1和疑问2处赋值的dev_register函数指针
			if ((err = dev->ops->dev_register(dev)) < 0)
				return err;
			dev->state = SNDRV_DEV_REGISTERED;
		}
	}
	return 0;
}

5. 与用户交互的接口

        在snd_open中, 会根据次设备号(每个逻辑设备都有自己的次设备号), 选择对应的逻辑设备的ops函数, 然后替换file->f_op, 此后用户空间的任何操作都是直接与逻辑设备的ops函数交互的。

        ALSA系统中有一个全局数组snd_minors[], 数组的下标就是次设备号, 每个元素对应一个逻辑设备, snd_minor[i]-> f_ops存储的就是逻辑设备自己的ops函数,用次设备号作为下标, 从snd_minors[]中找到对应的元素, 然后用元素的f_ops替换file->f_op即可。

//sound/core/sound.c
#define CONFIG_SND_MAJOR	116	/* standard configuration */
static int major = CONFIG_SND_MAJOR;

static int __init alsa_sound_init(void)
{
	snd_major = major;
	snd_ecards_limit = cards_limit;
	if (register_chrdev(major, "alsa", &snd_fops)) {  //注册字符设备,snd_fops是关键
		snd_printk(KERN_ERR "unable to register native major device number %d\n", major);
		return -EIO;
	}
	snd_info_minor_register();
	return 0;
}
subsys_initcall(alsa_sound_init);

static const struct file_operations snd_fops =
{
	.owner =	THIS_MODULE,
	.open =		snd_open,
	.llseek =	noop_llseek,
};

static int snd_open(struct inode *inode, struct file *file)
{
	unsigned int minor = iminor(inode);
	struct snd_minor *mptr = NULL;
	const struct file_operations *old_fops;
	int err = 0;

	if (minor >= ARRAY_SIZE(snd_minors))
		return -ENODEV;
 
//根据次设备号取出设备对应的file_operations结构体
	mptr = snd_minors[minor];
	old_fops = file->f_op;
 
//将file->f_op 函数操作集替换成对应逻辑设备的file_operations结构体,譬如dev/controlC0设备
	file->f_op = fops_get(mptr->f_ops);
	if (file->f_op == NULL) {
		file->f_op = old_fops;
		err = -ENODEV;
	}
	
	if (file->f_op->open) {
		err = file->f_op->open(inode, file);
		if (err) {
			fops_put(file->f_op);
			file->f_op = fops_get(old_fops);
		}
	}
	fops_put(old_fops);
	return err;
}

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值