现在的位置: 首页 > 综合 > 正文

再读声卡驱动(2)

2013年07月22日 ⁄ 综合 ⁄ 共 4385字 ⁄ 字号 评论关闭

数据流:

 声卡设备的主设备号为:

文件core.h中

#define CONFIG_SND_MAJOR 116

文件sound.c中

static int major = CONFIG_SND_MAJOR;

这个主设备号关联一个上层的 file_operations,snd_fops。

在文件sound.c中:

static int __init alsa_sound_init(void)

{

snd_major = major;

snd_ecards_limit = cards_limit;

if (register_chrdev(major, "alsa", &snd_fops)) {  //注册主设备号116并关联文件操作函数集snd_fops。

snd_printk(KERN_ERR "unable to register native major device number %d\n", major);

return -EIO;

}

。。。。。。

return 0;

}

 

声卡设备都是字符设备,都有自己的文件操作函数集,在设备节点创建时这些操作函数集

被包装成结构体snd_minor,根据其此设备号存放在结构体数组

static struct snd_minor *snd_minors[SNDRV_OS_MINORS];中。

结构体snd_minor的原型如下:

struct snd_minor {
 int type;   /* SNDRV_DEVICE_TYPE_XXX */
 int card;   /* card number */
 int device;   /* device number */
 const struct file_operations *f_ops; /* file operations */
 void *private_data;  /* private data for f_ops->open */
 struct device *dev;  /* device for sysfs */
};

 

声卡设备节点的创建和文件操作函数的包装都是调用函数函数snd_register_device()来实现的。

static inline int snd_register_device(int type, struct snd_card *card, int dev,

                                                   const struct file_operations *f_ops,
                                                   void *private_data,
                                                   const char *name )
{
 return snd_register_device_for_dev(type, card, dev, f_ops, private_data, name,snd_card_get_device_link(card));
}

 

int snd_register_device_for_dev( int type, struct snd_card *card, int dev,
                                                     const struct file_operations *f_ops,
                                                     void *private_data,
                                                    const char *name, struct device *device  )
{
 int minor;
 struct snd_minor *preg;


 preg = kmalloc(sizeof *preg, GFP_KERNEL);

 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);

 minor = snd_kernel_minor(type, card, dev);  

 snd_minors[minor] = preg; //将文件操作函数集f_ops包装成结构体preg放到结构体数组snd_minors[]中

//创建名为name的设备节点。
 preg->dev = device_create(sound_class, device, MKDEV(major, minor), private_data, "%s", name);
。。。。。。
}

在声卡uda1341的声卡体系中总共创建了四个设备节点:

"timer","controlC%i", "pcmC%iD%ip","pcmC%iD%ic"

分别代表定时器,控制,播放流,录音流。

定时器:

在文件timer.c中

static int __init alsa_timer_init(void)

{

。。。。。。

//创建名为"timer"的设备节点,其关联的文件操作函数集为snd_timer_f_ops。

if ((err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0,

                                                &snd_timer_f_ops, NULL, "timer")) < 0)

     snd_printk(KERN_ERR "unable to register timer device (%i)\n", err);

。。。。。。

}

 

文件uda134x.c中实现了一个重要函数

uda134x_soc_probe(struct platform_device *pdev)

在该函数中调用函数了snd_soc_init_card(socdev)

这个函数最终会调用如下函数:

int snd_device_register_all(struct snd_card *card)
{
 struct snd_device *dev;

 list_for_each_entry(dev, &card->devices, list) {  //将创建剩余三个设备节点
  if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) {
          if ((err = dev->ops->dev_register(dev)) < 0)  
            return err;
           dev->state = SNDRV_DEV_REGISTERED;
    }
 }
 return 0;
}

这些snd_device 何时被添加到card->devices上的,在上一篇日志中已有论述。

 

控制:

在文件control.c中实现函数

static int snd_ctl_dev_register(struct snd_device *device)
{
 struct snd_card *card = device->device_data;
 int err, cardnum;
 char name[16];

。。。。。。
 cardnum = card->number;
//创建名为"controlC%i"的设备节点,关联文件操作函数集snd_ctl_f_ops
 sprintf(name, "controlC%i", cardnum);
 if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,

                                                     &snd_ctl_f_ops, card, name)) < 0)

              return err;
。。。。。。
}

播放流 和 录音流:

static int snd_pcm_dev_register(struct snd_device *device)
{
 int cidx, err;
 struct snd_pcm_substream *substream;
 struct snd_pcm_notify *notify;
 char str[16];
 struct snd_pcm *pcm = device->device_data;
 struct device *dev;

。。。。。。
 for (cidx = 0; cidx < 2; cidx++) { //有播放流和录音流所以是两次循环
  switch (cidx) {
  case SNDRV_PCM_STREAM_PLAYBACK:
   sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device); //设备节点名为"pcmC%iD%ip"
   devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;
   break;
  case SNDRV_PCM_STREAM_CAPTURE:
   sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device); //设备节点名为"pcmC%iD%ic"
   devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
   break;
  }
。。。。。。

//创建设备节点,关联的文件操作函数集snd_pcm_f_ops[cidx]。
  err = snd_register_device_for_dev(devtype, pcm->card, pcm->device,&snd_pcm_f_ops[cidx],
                                                         pcm, str, dev);
    。。。。。。
     for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
         snd_pcm_timer_init(substream);  //初始化每一个子流与定时器相关的部分。
 }

。。。。。。。
}

转自:http://chxxxyg.blog.163.com/blog/static/15028119320107983813906/

抱歉!评论已关闭.