从soc_new_pcm看PCM设备的创建

从int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)看起。

先对snd_soc_pcm_runtime结构判断相关字段,确定playback 和 capture

用来确定是否产生相关SNDRV_PCM_STREAM_PLAYBACK/SNDRV_PCM_STREAM_CAPTURE流

/* 判断dynamic和no_pcm字段 */
if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) {
		playback = rtd->dai_link->dpcm_playback;
		capture = rtd->dai_link->dpcm_capture;
	} else {
        /* 通过num_codecs和codec_dais检查是否存在playback 和 capture  */
		for (i = 0; i < rtd->num_codecs; i++) {
			codec_dai = rtd->codec_dais[i];
			if (codec_dai->driver->playback.channels_min)
				playback = 1;
			if (codec_dai->driver->capture.channels_min)
				capture = 1;
		}

		capture = capture && cpu_dai->driver->capture.channels_min;
		playback = playback && cpu_dai->driver->playback.channels_min;
	}

	if (rtd->dai_link->playback_only) {
		playback = 1;
		capture = 0;
	}

	if (rtd->dai_link->capture_only) {
		playback = 0;
		capture = 1;
	}

然后调用snd_pcm_new创建PCM

snd_pcm_new->_snd_pcm_new

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) 中

1,对struct snd_pcm *pcm 赋值

2,对struct snd_pcm *pcm 分别 snd_pcm_new_stream、snd_device_new

snd_pcm_new_stream - create a new PCM stream

snd_device_new - create an ALSA device component

 

int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)中

1,snd_device_initialize 设备初始化

2,pstr->dev.groups = pcm_dev_attr_groups设备文件pcm_class

3,snd_pcm_stream_proc_init(pstr) // 文件/proc/asound/card0/pcm“xx”/info

4,snd_pcm_substream_proc_init(substream) //同理

5,INIT_LIST_HEAD/list_add_tail

 

int snd_device_new(struct snd_card *card, enum snd_device_type type,  void *device_data, struct snd_device_ops *ops)

看注释!insert the entry in an incrementally sorted list

其实通过成员地址,找到链表对象的地址

找到地址后,加入到链表中dev->list,其实就是将pcm设备挂载了声卡总线上了。

     挂载了的设备又没有看到实际操作啊,总要注册的,在哪里注册呢?

*********************************************

疑问?

关于snd_pcm_new_internal,这个函数干什么用的呢?

 * Creates a new internal PCM instance with no userspace device or procfs
 * entries. This is used by ASoC Back End PCMs in order to create a PCM that
 * will only be used internally by kernel drivers. i.e. it cannot be opened
 * by userspace. It provides existing ASoC components drivers with a substream
 * and access to any private data.

*创建一个新的内部PCM实例,没有用户空间设备或procfs。它被用做ASoC Back End PCMs,这个PCM设备只在内核驱动程序内部使用。例如,它不能被用户空间打开。它为现有的ASoC组件驱动程序提供子流和对任何私有数据的访问。

实际行为和snd_pcm_new是一样的。

*********************************************

注意到ops字段的register函数

在_snd_pcm_new中的snd_device_ops结构体的snd_pcm_dev_register在哪里调用的呢?

	static struct snd_device_ops ops = {
		.dev_free = snd_pcm_dev_free,
		.dev_register =	snd_pcm_dev_register,
		.dev_disconnect = snd_pcm_dev_disconnect,
	};

回溯了一下代码,发现在register card的时候会注册

在:

devm_snd_soc_register_card->snd_soc_register_card->snd_soc_instantiate_card->snd_card_register->snd_device_register_all->__snd_device_register

中进行回调。

	if (dev->state == SNDRV_DEV_BUILD) {
		if (dev->ops->dev_register) {
			int err = dev->ops->dev_register(dev);
			if (err < 0)
				return err;
		}
		dev->state = SNDRV_DEV_REGISTERED;
	}
	return 0;

在这里就调用了snd_pcm_dev_register。

 

static int snd_pcm_dev_register(struct snd_device *device)

1,snd_pcm_add(pcm)

2,snd_register_device(devtype, pcm->card, pcm->device, &snd_pcm_f_ops[cidx], pcm, &pcm->streams[cidx].dev)

3,snd_pcm_timer_init(substream) 创建了timer设备

 

所以可以发现,pcm设备是统一注册的!注册方式是调用ops->dev_register函数指针。函数调用了snd_register_device将设备被分配到一个字符设备上:snd_minors[minor] = preg

此时将preg分配给全局的次设备号为minor的snd_minor结构体。

 

然后重点关注snd_register_device->snd_minors[minor] = preg的起源。

在alsa_sound_init中,存在snd_fops结构。

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

由于这个open函数实际上就仅仅是调用了file->f_op->open(inode, file),也就是某个次设备的open函数(snd_pcm_f_ops)。

 

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

	if (minor >= ARRAY_SIZE(snd_minors))
		return -ENODEV;
	mutex_lock(&sound_mutex);
	mptr = snd_minors[minor];
	if (mptr == NULL) {
		mptr = autoload_device(minor);
		if (!mptr) {
			mutex_unlock(&sound_mutex);
			return -ENODEV;
		}
	}
	new_fops = fops_get(mptr->f_ops);
	mutex_unlock(&sound_mutex);
	if (!new_fops)
		return -ENODEV;
	replace_fops(file, new_fops);

	if (file->f_op->open)
		err = file->f_op->open(inode, file);
	return err;
}

如果这个file_operations是pcm的ops的话,就会调用snd_pcm_f_ops[]的open函数。

如果这个file_operations是ctl的ops的话,就会调用snd_ctl_f_ops的open函数。

 

总结:

    至此,可以发现,alsa会存在一个字符设备,这个字符设备是主设备号为116,次设备号自动分配的设备。并且,在相关的machine驱动注册声卡的时候,总会做这两件事:1,new一个pcm设备,2,注册这个pcm设备。

当注册这个pcm设备的时候,实际上是将产生的设备绑定在alsa字符设备上。所以如果从底层看,想要打开pcm设备进行操作,一定通过从alsa驱动调用open函数,open回调到相应从设备的file_operations的open的函数指针

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值