<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>

新聞中心

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

Linux ALSA聲卡驅動(dòng)之三:PCM設備的創(chuàng )建

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

  4. 設備文件節點(diǎn)的建立(dev/snd/pcmCxxDxxp、pcmCxxDxxc)

本文引用地址:http://dyxdggzs.com/article/201612/341593.htm

  4.1 struct snd_minor

  每個(gè)snd_minor結構體保存了聲卡下某個(gè)邏輯設備的上下文信息,他在邏輯設備建立階段被填充,在邏輯設備被使用時(shí)就可以從該結構體中得到相應的信息。pcm設備也不例外,也需要使用該結構體。該結構體在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中定義了一個(gè)snd_minor指針的全局數組:

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

  前面說(shuō)過(guò),在聲卡的注冊階段(snd_card_register),會(huì )調用pcm的回調函數snd_pcm_dev_register(),這個(gè)函數里會(huì )調用函數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);

  ......

  }

  我們再進(jìn)入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;

  }

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

  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的實(shí)例

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

  把該snd_minor結構的地址放入全局數組snd_minors[minor]中

  最后,調用device_create創(chuàng )建設備節點(diǎn)

  4.2 設備文件的建立

  在4.1節的最后,設備文件已經(jīng)建立,不過(guò)4.1節的重點(diǎn)在于snd_minors數組的賦值過(guò)程,在本節中,我們把重點(diǎn)放在設備文件中。

  回到pcm的回調函數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);

  ......

  }

  ......

  }

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

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

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

  snd_pcm_f_ops

  snd_pcm_f_ops是一個(gè)標準的文件系統file_operations結構數組,它的定義在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的參數被傳入,并被記錄在snd_minors[minor]中的字段f_ops中。最后,在snd_register_device_for_dev中創(chuàng )建設備節點(diǎn):

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

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

  private_data, "%s", name);

  4.3 層層深入,從應用程序到驅動(dòng)層pcm

  4.3.1 字符設備注冊

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

  [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中的參數major與之前創(chuàng )建pcm設備是device_create時(shí)的major是同一個(gè),這樣的結果是,當應用程序open設備文件/dev/snd/pcmCxDxp時(shí),會(huì )進(jìn)入snd_fops的open回調函數,我們將在下一節中講述open的過(guò)程。

  4.3.2 打開(kāi)pcm設備

  從上一節中我們得知,open一個(gè)pcm設備時(shí),將會(huì )調用snd_fops的open回調函數,我們先看看snd_fops的定義:

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

  {

  .owner = THIS_MODULE,

  .open = snd_open

  };

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

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

  }

  下面的序列圖展示了應用程序如何最終調用到snd_pcm_f_ops結構中的回調函數:

  圖4.3.2.1 應用程序操作pcm設備


上一頁(yè) 1 2 下一頁(yè)

關(guān)鍵詞: Linux ALSA

評論


相關(guān)推薦

技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>