4.3 层层深入,从应用程序到驱动层pcm
4.3.1 字符设备注册
在sound/core/sound.c中有alsa_sound_init()函数,定义如下:
- static
int __init alsa_sound_init(void) - {
-
snd_major = major; -
snd_ecards_limit = cards_limit; -
if (register_chrdev(major, "alsa", &snd_fops)) { -
snd_printk(KERN_ERR "unable to register native major device number %d/n", major); -
return -EIO; -
} -
if (snd_info_init() < 0) { -
unregister_chrdev(major, "alsa"); -
return -ENOMEM; -
} -
snd_info_minor_register(); -
return 0; - }
register_chrdev中的参数major与之前创建pcm设备是device_create时的major是同一个,这样的结果是,当应用程序open设备文件/dev/snd/pcmCxDxp时,会进入snd_fops的open回调函数,我们将在下一节中讲述open的过程。
4.3.2 打开pcm设备
从上一节中我们得知,open一个pcm设备时,将会调用snd_fops的open回调函数,我们先看看snd_fops的定义:
- static
const struct file_operations snd_fops = - {
-
.owner = THIS_MODULE, -
.open = snd_open - };
跟入snd_open函数,它首先从inode中取出此设备号,然后以次设备号为索引,从snd_minors全局数组中取出当初注册pcm设备时填充的snd_minor结构(参看4.1节的内容),然后从snd_minor结构中取出pcm设备的f_ops,并且把file->f_op替换为pcm设备的f_ops,紧接着直接调用pcm设备的f_ops->open(),然后返回。因为file->f_op已经被替换,以后,应用程序的所有read/write/ioctl调用都会进入pcm设备自己的回调函数中,也就是4.2节中提到的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 *old_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; -
} -
} -
old_fops = file->f_op; -
file->f_op = fops_get(mptr->f_ops); -
if (file->f_op == NULL) { -
file->f_op = old_fops; -
err = -ENODEV; -
} -
mutex_unlock(&sound_mutex); -
if (err < 0) -
return err; -
-
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; - }
下面的序列图展示了应用程序如何最终调用到snd_pcm_f_ops结构中的回调函数: