概述
从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的函数指针
最后
以上就是漂亮小鸽子为你收集整理的从soc_new_pcm看PCM设备的创建的全部内容,希望文章能够帮你解决从soc_new_pcm看PCM设备的创建所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复