linux音频设备节点,Linux音频驱动之三:PCM设备的创建

3. 新建一个pcm

alsa-driver的中间层已经为我们提供了新建pcm的api:

int snd_pcm_new(struct snd_card *card, const char *id, int device, int playback_count, int capture_count,

struct snd_pcm ** rpcm);

参数device 表示目前创建的是该声卡下的第几个pcm,第一个pcm设备从0开始。

参数playback_count 表示该pcm将会有几个playback substream。

参数capture_count 表示该pcm将会有几个capture substream。

另一个用于设置pcm操作函数接口的api:

void snd_pcm_set_ops(struct snd_pcm *pcm, int direction, struct snd_pcm_ops *ops);

新建一个pcm可以用下面一张新建pcm的调用的序列图进行描述:

linux音频设备节点,Linux音频驱动之三:PCM设备的创建_第1张图片

图3.1 新建pcm的序列图

snd_card_create    pcm是声卡下的一个设备(部件),所以第一步是要创建一个声卡

snd_pcm_new    调用该api创建一个pcm,才该api中会做以下事情

如果有,建立playback stream,相应的substream也同时建立

如果有,建立capture stream,相应的substream也同时建立

调用snd_device_new()把该pcm挂到声卡中,参数ops中的dev_register字段指向了函数snd_pcm_dev_register,这个回调函数会在声卡的注册阶段被调用。

snd_pcm_set_ops    设置操作该pcm的控制/操作接口函数,参数中的snd_pcm_ops结构中的函数通常就是我们驱动要实现的函数

snd_card_register    注册声卡,在这个阶段会遍历声卡下的所有逻辑设备,并且调用各设备的注册回调函数,对于pcm,就是第二步提到的snd_pcm_dev_register函数,该回调函数建立了和用户空间应用程序(alsa-lib)通信所用的设备文件节点:/dev/snd/pcmCxxDxxp和/dev/snd/pcmCxxDxxc

4. 设备文件节点的建立(dev/snd/pcmCxxDxxp、pcmCxxDxxc)

4.1 struct snd_minor

每个snd_minor结构体保存了声卡下某个逻辑设备的上下文信息,他在逻辑设备建立阶段被填充,在逻辑设备被使用时就可以从该结构体中得到相应的信息。pcm设备也不例外,也需要使用该结构体。该结构体在include/sound/core.h中定义。

structsnd_minor {

inttype;/* SNDRV_DEVICE_TYPE_XXX */

intcard;/* card number */

intdevice;/* device number */

conststructfile_operations *f_ops;/* file operations */

void*private_data;/* private data for f_ops->open */

structdevice *dev;/* device for sysfs */

};

在sound/sound.c中定义了一个snd_minor指针的全局数组:

staticstructsnd_minor *snd_minors[256];

前面说过,在声卡的注册阶段(snd_card_register),会调用pcm的回调函数snd_pcm_dev_register(),这个函数里会调用函数snd_register_device_for_dev():

staticintsnd_pcm_dev_register(structsnd_device *device)

{

......

/* register pcm */

err = snd_register_device_for_dev(devtype, pcm->card,

pcm->device,

&snd_pcm_f_ops[cidx],

pcm, str, dev);

......

}

我们再进入snd_register_device_for_dev():

intsnd_register_device_for_dev(inttype,structsnd_card *card,intdev,

conststructfile_operations *f_ops,

void*private_data,

constchar*name,structdevice *device)

{

intminor;

structsnd_minor *preg;

if(snd_BUG_ON(!name))

return-EINVAL;

preg = kmalloc(sizeof*preg, GFP_KERNEL);

if(preg == NULL)

return-ENOMEM;

preg->type = type;

preg->card = card ? card->number : -1;

preg->device = dev;

preg->f_ops = f_ops;

preg->private_data = private_data;

mutex_lock(&sound_mutex);

#ifdef CONFIG_SND_DYNAMIC_MINORS

minor = snd_find_free_minor();

#else

minor = snd_kernel_minor(type, card, dev);

if(minor >= 0 && snd_minors[minor])

minor = -EBUSY;

#endif

if(minor 

mutex_unlock(&sound_mutex);

kfree(preg);

returnminor;

}

snd_minors[minor] = preg;

preg->dev = device_create(sound_class, device, MKDEV(major, minor),

private_data,"%s", name);

if(IS_ERR(preg->dev)) {

snd_minors[minor] = NULL;

mutex_unlock(&sound_mutex);

minor = PTR_ERR(preg->dev);

kfree(preg);

returnminor;

}

mutex_unlock(&sound_mutex);

return0;

}

首先,分配并初始化一个snd_minor结构中的各字段

type:SNDRV_DEVICE_TYPE_PCM_PLAYBACK/SNDRV_DEVICE_TYPE_PCM_CAPTURE

card: card的编号

device:pcm实例的编号,大多数情况为0

f_ops:snd_pcm_f_ops

private_data:指向该pcm的实例

根据type,card和pcm的编号,确定数组的索引值minor,minor也作为pcm设备的此设备号

把该snd_minor结构的地址放入全局数组snd_minors[minor]中

最后,调用device_create创建设备节点0b1331709591d260c1c78e86d0c51c18.png

你可能感兴趣的:(linux音频设备节点)