新聞中心

EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計應(yīng)用 > Linux ALSA聲卡驅(qū)動之三:PCM設(shè)備的創(chuàng)建

Linux ALSA聲卡驅(qū)動之三:PCM設(shè)備的創(chuàng)建

作者: 時間:2016-12-14 來源:網(wǎng)絡(luò) 收藏

  4. 設(shè)備文件節(jié)點的建立(dev/snd/pcmCxxDxxp、pcmCxxDxxc)

本文引用地址:http://www.butianyuan.cn/article/201612/341593.htm

  4.1 struct snd_minor

  每個snd_minor結(jié)構(gòu)體保存了聲卡下某個邏輯設(shè)備的上下文信息,他在邏輯設(shè)備建立階段被填充,在邏輯設(shè)備被使用時就可以從該結(jié)構(gòu)體中得到相應(yīng)的信息。pcm設(shè)備也不例外,也需要使用該結(jié)構(gòu)體。該結(jié)構(gòu)體在include/sound/core.h中定義。

  [c-sharp] view plain copystruct 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 */

  };

  在sound/sound.c中定義了一個snd_minor指針的全局?jǐn)?shù)組:

  [c-sharp] view plain copystatic struct snd_minor *snd_minors[256];

  前面說過,在聲卡的注冊階段(snd_card_register),會調(diào)用pcm的回調(diào)函數(shù)snd_pcm_dev_register(),這個函數(shù)里會調(diào)用函數(shù)snd_register_device_for_dev():

  [c-sharp] view plain copystatic int snd_pcm_dev_register(struct snd_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():

  [c-sharp] view plain copyint 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;

  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 < 0) {

  mutex_unlock(&sound_mutex);

  kfree(preg);

  return minor;

  }

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

  return minor;

  }

  mutex_unlock(&sound_mutex);

  return 0;

  }

  首先,分配并初始化一個snd_minor結(jié)構(gòu)中的各字段

  type:SNDRV_DEVICE_TYPE_PCM_PLAYBACK/SNDRV_DEVICE_TYPE_PCM_CAPTURE

  card: card的編號

  device:pcm實例的編號,大多數(shù)情況為0

  f_ops:snd_pcm_f_ops

  private_data:指向該pcm的實例

  根據(jù)type,card和pcm的編號,確定數(shù)組的索引值minor,minor也作為pcm設(shè)備的此設(shè)備號

  把該snd_minor結(jié)構(gòu)的地址放入全局?jǐn)?shù)組snd_minors[minor]中

  最后,調(diào)用device_create創(chuàng)建設(shè)備節(jié)點

  4.2 設(shè)備文件的建立

  在4.1節(jié)的最后,設(shè)備文件已經(jīng)建立,不過4.1節(jié)的重點在于snd_minors數(shù)組的賦值過程,在本節(jié)中,我們把重點放在設(shè)備文件中。

  回到pcm的回調(diào)函數(shù)snd_pcm_dev_register()中:

  [c-sharp] view plain copystatic int snd_pcm_dev_register(struct snd_device *device)

  {

  int cidx, err;

  char str[16];

  struct snd_pcm *pcm;

  struct device *dev;

  pcm = device->device_data;

  ......

  for (cidx = 0; cidx < 2; cidx++) {

  ......

  switch (cidx) {

  case SNDRV_PCM_STREAM_PLAYBACK:

  sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device);

  devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;

  break;

  case SNDRV_PCM_STREAM_CAPTURE:

  sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device);

  devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;

  break;

  }

  /* device pointer to use, pcm->dev takes precedence if

  * it is assigned, otherwise fall back to card's device

  * if possible */

  dev = pcm->dev;

  if (!dev)

  dev = snd_card_get_device_link(pcm->card);

  /* register pcm */

  err = snd_register_device_for_dev(devtype, pcm->card,

  pcm->device,

  &snd_pcm_f_ops[cidx],

  pcm, str, dev);

  ......

  }

  ......

  }

  以上代碼我們可以看出,對于一個pcm設(shè)備,可以生成兩個設(shè)備文件,一個用于playback,一個用于capture,代碼中也確定了他們的命名規(guī)則:

  playback -- pcmCxDxp,通常系統(tǒng)中只有一各聲卡和一個pcm,它就是pcmC0D0p

  capture -- pcmCxDxc,通常系統(tǒng)中只有一各聲卡和一個pcm,它就是pcmC0D0c

  snd_pcm_f_ops

  snd_pcm_f_ops是一個標(biāo)準(zhǔn)的文件系統(tǒng)file_operations結(jié)構(gòu)數(shù)組,它的定義在sound/core/pcm_native.c中:

  [c-sharp] view plain copyconst struct file_operations snd_pcm_f_ops[2] = {

  {

  .owner = THIS_MODULE,

  .write = snd_pcm_write,

  .aio_write = snd_pcm_aio_write,

  .open = snd_pcm_playback_open,

  .release = snd_pcm_release,

  .llseek = no_llseek,

  .poll = snd_pcm_playback_poll,

  .unlocked_ioctl = snd_pcm_playback_ioctl,

  .compat_ioctl = snd_pcm_ioctl_compat,

  .mmap = snd_pcm_mmap,

  .fasync = snd_pcm_fasync,

  .get_unmapped_area = snd_pcm_get_unmapped_area,

  },

  {

  .owner = THIS_MODULE,

  .read = snd_pcm_read,

  .aio_read = snd_pcm_aio_read,

  .open = snd_pcm_capture_open,

  .release = snd_pcm_release,

  .llseek = no_llseek,

  .poll = snd_pcm_capture_poll,

  .unlocked_ioctl = snd_pcm_capture_ioctl,

  .compat_ioctl = snd_pcm_ioctl_compat,

  .mmap = snd_pcm_mmap,

  .fasync = snd_pcm_fasync,

  .get_unmapped_area = snd_pcm_get_unmapped_area,

  }

  };

  snd_pcm_f_ops作為snd_register_device_for_dev的參數(shù)被傳入,并被記錄在snd_minors[minor]中的字段f_ops中。最后,在snd_register_device_for_dev中創(chuàng)建設(shè)備節(jié)點:

  [c-sharp] view plain copysnd_minors[minor] = preg;

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

  private_data, "%s", name);

  4.3 層層深入,從應(yīng)用程序到驅(qū)動層pcm

  4.3.1 字符設(shè)備注冊

  在sound/core/sound.c中有alsa_sound_init()函數(shù),定義如下:

  [c-sharp] view plain copystatic 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中的參數(shù)major與之前創(chuàng)建pcm設(shè)備是device_create時的major是同一個,這樣的結(jié)果是,當(dāng)應(yīng)用程序open設(shè)備文件/dev/snd/pcmCxDxp時,會進入snd_fops的open回調(diào)函數(shù),我們將在下一節(jié)中講述open的過程。

  4.3.2 打開pcm設(shè)備

  從上一節(jié)中我們得知,open一個pcm設(shè)備時,將會調(diào)用snd_fops的open回調(diào)函數(shù),我們先看看snd_fops的定義:

  [c-sharp] view plain copystatic const struct file_operations snd_fops =

  {

  .owner = THIS_MODULE,

  .open = snd_open

  };

  跟入snd_open函數(shù),它首先從inode中取出此設(shè)備號,然后以次設(shè)備號為索引,從snd_minors全局?jǐn)?shù)組中取出當(dāng)初注冊pcm設(shè)備時填充的snd_minor結(jié)構(gòu)(參看4.1節(jié)的內(nèi)容),然后從snd_minor結(jié)構(gòu)中取出pcm設(shè)備的f_ops,并且把file->f_op替換為pcm設(shè)備的f_ops,緊接著直接調(diào)用pcm設(shè)備的f_ops->open(),然后返回。因為file->f_op已經(jīng)被替換,以后,應(yīng)用程序的所有read/write/ioctl調(diào)用都會進入pcm設(shè)備自己的回調(diào)函數(shù)中,也就是4.2節(jié)中提到的snd_pcm_f_ops結(jié)構(gòu)中定義的回調(diào)。

  [c-sharp] view plain copystatic 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;

  }

  下面的序列圖展示了應(yīng)用程序如何最終調(diào)用到snd_pcm_f_ops結(jié)構(gòu)中的回調(diào)函數(shù):

  圖4.3.2.1 應(yīng)用程序操作pcm設(shè)備


上一頁 1 2 下一頁

關(guān)鍵詞: Linux ALSA

評論


相關(guān)推薦

技術(shù)專區(qū)

關(guān)閉