4.2 设备文件的建立
在4.1节的最后,设备文件已经建立,不过4.1节的重点在于snd_minors数组的赋值过程,在本节中,我们把重点放在设备文件中。 回到pcm的回调函数snd_pcm_dev_register()中: [c-sharp] view plain copy
1. static int snd_pcm_dev_register(struct snd_device *device) 2. {
3. int cidx, err; 4. char str[16];
5. struct snd_pcm *pcm; 6. struct device *dev; 7.
8. pcm = device->device_data; 9. ...... 10. for (cidx = 0; cidx < 2; cidx++) { 11. ...... 12. switch (cidx) { 13. case SNDRV_PCM_STREAM_PLAYBACK: 14. sprintf(str, \, pcm->card->number, pcm->de
vice); 15. devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK; 16. break; 17. case SNDRV_PCM_STREAM_CAPTURE: 18. sprintf(str, \, pcm->card->number, pcm->de
vice); 19. devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE; 20. break; 21. } 22. /* device pointer to use, pcm->dev takes precedence if 23. * it is assigned, otherwise fall back to card's device 24. * if possible */ 25. dev = pcm->dev; 26. if (!dev) 27. dev = snd_card_get_device_link(pcm->card); 28. /* register pcm */ 29. err = snd_register_device_for_dev(devtype, pcm->card, 30. pcm->device, 31. &snd_pcm_f_ops[cidx], 32. pcm, str, dev); 33. ......
26
34. } 35. ...... 36. }
以上代码我们可以看出,对于一个pcm设备,可以生成两个设备文件,一个用于playback,一个用于capture,代码中也确定了他们的命名规则:
?
playback -- pcmCxDxp,通常系统中只有一各声卡和一个pcm,它就是pcmC0D0p
capture -- pcmCxDxc,通常系统中只有一各声卡和一个pcm,它就是pcmC0D0c
?
snd_pcm_f_ops
snd_pcm_f_ops是一个标准的文件系统file_operations结构数组,它的定义在sound/core/pcm_native.c中: [c-sharp] view plain copy
1. const struct file_operations snd_pcm_f_ops[2] = { 2. {
3. .owner = THIS_MODULE, 4. .write = snd_pcm_write,
5. .aio_write = snd_pcm_aio_write, 6. .open = snd_pcm_playback_open, 7. .release = snd_pcm_release, 8. .llseek = no_llseek,
9. .poll = snd_pcm_playback_poll,
10. .unlocked_ioctl = snd_pcm_playback_ioctl, 11. .compat_ioctl = snd_pcm_ioctl_compat, 12. .mmap = snd_pcm_mmap, 13. .fasync = snd_pcm_fasync,
14. .get_unmapped_area = snd_pcm_get_unmapped_area,
15. }, 16. {
17. .owner = THIS_MODULE, 18. .read = snd_pcm_read,
19. .aio_read = snd_pcm_aio_read,
20. .open = snd_pcm_capture_open, 21. .release = snd_pcm_release,
27
22. .llseek = no_llseek,
23. .poll = snd_pcm_capture_poll,
24. .unlocked_ioctl = snd_pcm_capture_ioctl, 25. .compat_ioctl = snd_pcm_ioctl_compat, 26. .mmap = snd_pcm_mmap, 27. .fasync = snd_pcm_fasync,
28. .get_unmapped_area = snd_pcm_get_unmapped_area,
29. } 30. };
snd_pcm_f_ops作为snd_register_device_for_dev的参数被传入,并被记录在snd_minors[minor]中的字段f_ops中。最后,在snd_register_device_for_dev中创建设备节点:
[c-sharp] view plain copy
1. snd_minors[minor] = preg;
2. preg->dev = device_create(sound_class, device, MKDEV(major, minor),
3. private_data, \, name); 4.3 层层深入,从应用程序到驱动层pcm 4.3.1 字符设备注册
在sound/core/sound.c中有alsa_sound_init()函数,定义如下: [c-sharp] view plain copy
1. static int __init alsa_sound_init(void) 2. {
3. snd_major = major;
4. snd_ecards_limit = cards_limit;
5. if (register_chrdev(major, \, &snd_fops)) { 6. snd_printk(KERN_ERR \jor device number %d/n\, major); 7. return -EIO; 8. }
9. if (snd_info_init() < 0) {
10. unregister_chrdev(major, \); 11. return -ENOMEM; 12. }
13. snd_info_minor_register(); 14. return 0;
28
15. }
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的定义: [c-sharp] view plain copy
1. static const struct file_operations snd_fops = 2. {
3. .owner = THIS_MODULE, 4. .open = snd_open 5. };
跟入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结构中定义的回调。 [c-sharp] view plain copy
1. static int snd_open(struct inode *inode, struct file *fil
e) 2. {
3. unsigned int minor = iminor(inode); 4. struct snd_minor *mptr = NULL;
5. const struct file_operations *old_fops; 6. int err = 0; 7.
8. if (minor >= ARRAY_SIZE(snd_minors)) 9. return -ENODEV;
10. mutex_lock(&sound_mutex); 11. mptr = snd_minors[minor]; 12. if (mptr == NULL) {
29
13. mptr = autoload_device(minor); 14. if (!mptr) {
15. mutex_unlock(&sound_mutex); 16. return -ENODEV; 17. } 18. }
19. old_fops = file->f_op;
20. file->f_op = fops_get(mptr->f_ops); 21. if (file->f_op == NULL) { 22. file->f_op = old_fops; 23. err = -ENODEV; 24. }
25. mutex_unlock(&sound_mutex); 26. if (err < 0) 27. return err; 28.
29. if (file->f_op->open) {
30. err = file->f_op->open(inode, file); 31. if (err) {
32. fops_put(file->f_op);
33. file->f_op = fops_get(old_fops); 34. } 35. }
36. fops_put(old_fops); 37. return err; 38. }
下面的序列图展示了应用程序如何最终调用到snd_pcm_f_ops结构中的回调函数:
30