<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

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

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

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

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

          本文引用地址:http://www.ex-cimer.com/article/201612/341593.htm

            4.1 struct snd_minor

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

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

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

            4.2 設備文件的建立

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

            回到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設備,可以生成兩個設備文件,一個用于playback,一個用于capture,代碼中也確定了他們的命名規(guī)則:

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

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

            snd_pcm_f_ops

            snd_pcm_f_ops是一個標準的文件系統(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)建設備節(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 層層深入,從應用程序到驅(qū)動層pcm

            4.3.1 字符設備注冊

            在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設備是device_create時的major是同一個,這樣的結(jié)果是,當應用程序open設備文件/dev/snd/pcmCxDxp時,會進入snd_fops的open回調(diào)函數(shù),我們將在下一節(jié)中講述open的過程。

            4.3.2 打開pcm設備

            從上一節(jié)中我們得知,open一個pcm設備時,將會調(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中取出此設備號,然后以次設備號為索引,從snd_minors全局數(shù)組中取出當初注冊pcm設備時填充的snd_minor結(jié)構(gòu)(參看4.1節(jié)的內(nèi)容),然后從snd_minor結(jié)構(gòu)中取出pcm設備的f_ops,并且把file->f_op替換為pcm設備的f_ops,緊接著直接調(diào)用pcm設備的f_ops->open(),然后返回。因為file->f_op已經(jīng)被替換,以后,應用程序的所有read/write/ioctl調(diào)用都會進入pcm設備自己的回調(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;

            }

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

            圖4.3.2.1 應用程序操作pcm設備


          上一頁 1 2 下一頁

          關(guān)鍵詞: Linux ALSA

          評論


          相關(guān)推薦

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

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();