嵌入式 pcm音频二

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

4.1 struct snd_minor

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

 

[c-sharp]   view plain copy
  1. struct snd_minor {  
  2.     int type;             
  3.     int card;             
  4.     int device;           
  5.     const struct file_operations *f_ops;      
  6.     void *private_data;       
  7.     struct device *dev;       
  8. };  

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

 

[c-sharp]   view plain copy
  1. static struct snd_minor *snd_minors[256];  

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

 

[c-sharp]   view plain copy
  1. static int snd_pcm_dev_register(struct snd_device *device)  
  2. {  
  3.     ......  
  4.   
  5.       
  6.     err = snd_register_device_for_dev(devtype, pcm->card,  
  7.                          pcm->device,  
  8.                     &snd_pcm_f_ops[cidx],  
  9.                     pcm, str, dev);  
  10.     ......  
  11. }  

我们再进入snd_register_device_for_dev():

 

[c-sharp]   view plain copy
  1. int snd_register_device_for_dev(int type, struct snd_card *card, int dev,  
  2.                 const struct file_operations *f_ops,  
  3.                 void *private_data,  
  4.                 const char *name, struct device *device)  
  5. {  
  6.     int minor;  
  7.     struct snd_minor *preg;  
  8.   
  9.     if (snd_BUG_ON(!name))  
  10.         return -EINVAL;  
  11.     preg = kmalloc(sizeof *preg, GFP_KERNEL);  
  12.     if (preg == NULL)  
  13.         return -ENOMEM;  
  14.     preg->type = type;  
  15.     preg->card = card ? card->number : -1;  
  16.     preg->device = dev;  
  17.     preg->f_ops = f_ops;  
  18.     preg->private_data = private_data;  
  19.     mutex_lock(&sound_mutex);  
  20. #ifdef CONFIG_SND_DYNAMIC_MINORS  
  21.     minor = snd_find_free_minor();  
  22. #else  
  23.     minor = snd_kernel_minor(type, card, dev);  
  24.     if (minor >= 0 && snd_minors[minor])  
  25.         minor = -EBUSY;  
  26. #endif  
  27.     if (minor < 0) {  
  28.         mutex_unlock(&sound_mutex);  
  29.         kfree(preg);  
  30.         return minor;  
  31.     }  
  32.     snd_minors[minor] = preg;  
  33.     preg->dev = device_create(sound_class, device, MKDEV(major, minor),  
  34.                   private_data, "%s", name);  
  35.     if (IS_ERR(preg->dev)) {  
  36.         snd_minors[minor] = NULL;  
  37.         mutex_unlock(&sound_mutex);  
  38.         minor = PTR_ERR(preg->dev);  
  39.         kfree(preg);  
  40.         return minor;  
  41.     }  
  42.   
  43.     mutex_unlock(&sound_mutex);  
  44.     return 0;  
  45. }  
  • 首先,分配并初始化一个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创建设备节点

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, "pcmC%iD%ip", pcm->card->number, pcm->device);  
  15.             devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;  
  16.             break;  
  17.         case SNDRV_PCM_STREAM_CAPTURE:  
  18.             sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device);  
  19.             devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;  
  20.             break;  
  21.         }  
  22.           
  23.         dev = pcm->dev;  
  24.         if (!dev)  
  25.             dev = snd_card_get_device_link(pcm->card);  
  26.           
  27.         err = snd_register_device_for_dev(devtype, pcm->card,  
  28.                           pcm->device,  
  29.                           &snd_pcm_f_ops[cidx],  
  30.                           pcm, str, dev);  
  31.                   ......  
  32.     }  
  33.          ......  
  34. }  

 

以上代码我们可以看出,对于一个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,  
  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, "%s", name);  

 

你可能感兴趣的:(嵌入式 pcm音频二)