新聞中心

        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.104case.com/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);

          ......

          }

          我們再進(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;

          }

          首先,分配并初始化一個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時,會進(jìn)入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)用都會進(jìn)入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)閉
        主站蜘蛛池模板: 拜泉县| 阳西县| 青阳县| 崇信县| 揭西县| 乌兰浩特市| 景泰县| 洛南县| 吉首市| 滨海县| 保山市| 巍山| 康平县| 永善县| 略阳县| 五常市| 和平县| 武汉市| 绥宁县| 肥城市| 沈丘县| 潢川县| 广元市| 莱西市| 康平县| 家居| 元朗区| 双柏县| 义马市| 万山特区| 万年县| 车致| 禹州市| 斗六市| 鹿邑县| 靖边县| 鄂伦春自治旗| 六盘水市| 仪征市| 海林市| 白水县|