新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > Linux ALSA聲卡驅動之二:聲卡的創建

        Linux ALSA聲卡驅動之二:聲卡的創建

        作者: 時間:2016-12-07 來源:網絡 收藏

          2.1.4. 第四步,創建聲卡的功能部件(邏輯設備),例如PCM,Mixer,MIDI等

        本文引用地址:http://www.104case.com/article/201612/341246.htm

          這時候可以創建聲卡的各種功能部件了,還記得開頭的snd_card結構體的devices字段嗎?每一種部件的創建最終會調用snd_device_new()來生成一個snd_device實例,并把該實例鏈接到snd_card的devices鏈表中。

          通常,alsa-driver的已經提供了一些常用的部件的創建函數,而不必直接調用snd_device_new(),比如:

          PCM ---- snd_pcm_new()

          RAWMIDI -- snd_rawmidi_new()

          CONTROL -- snd_ctl_create()

          TIMER -- snd_timer_new()

          INFO -- snd_card_proc_new()

          JACK -- snd_jack_new()

          2.1.5. 第五步,注冊聲卡

          err = snd_card_register(card);

          if (err < 0) {

          snd_card_free(card);

          return err;

          }

          2.2. 一個實際的例子

          我把/sound/arm/pxa2xx-ac97.c的部分代碼貼上來:

          static int __devinit pxa2xx_ac97_probe(struct platform_device *dev)

          {

          struct snd_card *card;

          struct snd_ac97_bus *ac97_bus;

          struct snd_ac97_template ac97_template;

          int ret;

          pxa2xx_audio_ops_t *pdata = dev->dev.platform_data;

          if (dev->id >= 0) {

          dev_err(&dev->dev, "PXA2xx has only one AC97 port./n");

          ret = -ENXIO;

          goto err_dev;

          }

          ////(1)////

          ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,

          THIS_MODULE, 0, &card);

          if (ret < 0)

          goto err;

          card->dev = &dev->dev;

          ////(3)////

          strncpy(card->driver, dev->dev.driver->name, sizeof(card->driver));

          ////(4)////

          ret = pxa2xx_pcm_new(card, &pxa2xx_ac97_pcm_client, &pxa2xx_ac97_pcm);

          if (ret)

          goto err;

          ////(2)////

          ret = pxa2xx_ac97_hw_probe(dev);

          if (ret)

          goto err;

          ////(4)////

          ret = snd_ac97_bus(card, 0, &pxa2xx_ac97_ops, NULL, &ac97_bus);

          if (ret)

          goto err_remove;

          memset(&ac97_template, 0, sizeof(ac97_template));

          ret = snd_ac97_mixer(ac97_bus, &ac97_template, &pxa2xx_ac97_ac97);

          if (ret)

          goto err_remove;

          ////(3)////

          snprintf(card->shortname, sizeof(card->shortname),

          "%s", snd_ac97_get_short_name(pxa2xx_ac97_ac97));

          snprintf(card->longname, sizeof(card->longname),

          "%s (%s)", dev->dev.driver->name, card->mixername);

          if (pdata && pdata->codec_pdata[0])

          snd_ac97_dev_add_pdata(ac97_bus->codec[0], pdata->codec_pdata[0]);

          snd_card_set_dev(card, &dev->dev);

          ////(5)////

          ret = snd_card_register(card);

          if (ret == 0) {

          platform_set_drvdata(dev, card);

          return 0;

          }

          err_remove:

          pxa2xx_ac97_hw_remove(dev);

          err:

          if (card)

          snd_card_free(card);

          err_dev:

          return ret;

          }

          static int __devexit pxa2xx_ac97_remove(struct platform_device *dev)

          {

          struct snd_card *card = platform_get_drvdata(dev);

          if (card) {

          snd_card_free(card);

          platform_set_drvdata(dev, NULL);

          pxa2xx_ac97_hw_remove(dev);

          }

          return 0;

          }

          static struct platform_driver pxa2xx_ac97_driver = {

          .probe = pxa2xx_ac97_probe,

          .remove = __devexit_p(pxa2xx_ac97_remove),

          .driver = {

          .name = "pxa2xx-ac97",

          .owner = THIS_MODULE,

          #ifdef CONFIG_PM

          .pm = &pxa2xx_ac97_pm_ops,

          #endif

          },

          };

          static int __init pxa2xx_ac97_init(void)

          {

          return platform_driver_register(&pxa2xx_ac97_driver);

          }

          static void __exit pxa2xx_ac97_exit(void)

          {

          platform_driver_unregister(&pxa2xx_ac97_driver);

          }

          module_init(pxa2xx_ac97_init);

          module_exit(pxa2xx_ac97_exit);

          MODULE_AUTHOR("Nicolas Pitre");

          MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip");

          驅動程序通常由probe回調函數開始,對一下2.1中的步驟,是否有相似之處?

          經過以上的創建步驟之后,聲卡的邏輯結構如下圖所示:

            

         

          圖 2.2.1 聲卡的軟件邏輯結構

          下面的章節里我們分別討論一下snd_card_create()和snd_card_register()這兩個函數。

          3. snd_card_create()

          snd_card_create()在/sound/core/init.c中定義。

          /**

          * snd_card_create - create and initialize a soundcard structure

          * @idx: card index (address) [0 ... (SNDRV_CARDS-1)]

          * @xid: card identification (ASCII string)

          * @module: top level module for locking

          * @extra_size: allocate this extra size after the main soundcard structure

          * @card_ret: the pointer to store the created card instance

          *

          * Creates and initializes a soundcard structure.

          *

          * The function allocates snd_card instance via kzalloc with the given

          * space for the driver to use freely. The allocated struct is stored

          * in the given card_ret pointer.

          *

          * Returns zero if successful or a negative error code.

          */

          int snd_card_create(int idx, const char *xid,

          struct module *module, int extra_size,

          struct snd_card **card_ret)

          首先,根據extra_size參數的大小分配內存,該內存區可以作為芯片的專有數據使用(見前面的介紹):

          card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);

          if (!card)

          return -ENOMEM;

          拷貝聲卡的ID字符串:

          if (xid)

          strlcpy(card->id, xid, sizeof(card->id));

          如果傳入的聲卡編號為-1,自動分配一個索引編號:

          if (idx < 0) {

          for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++)

          /* idx == -1 == 0xffff means: take any free slot */

          if (~snd_cards_lock & idx & 1<

          if (module_slot_match(module, idx2)) {

          idx = idx2;

          break;

          }

          }

          }

          if (idx < 0) {

          for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++)

          /* idx == -1 == 0xffff means: take any free slot */

          if (~snd_cards_lock & idx & 1<

          if (!slots[idx2] || !*slots[idx2]) {

          idx = idx2;

          break;

          }

          }

          }

          初始化snd_card結構中必要的字段:

          card->number = idx;

          card->module = module;

          INIT_LIST_HEAD(&card->devices);

          init_rwsem(&card->controls_rwsem);

          rwlock_init(&card->ctl_files_rwlock);

          INIT_LIST_HEAD(&card->controls);

          INIT_LIST_HEAD(&card->ctl_files);

          spin_lock_init(&card->files_lock);

          INIT_LIST_HEAD(&card->files_list);

          init_waitqueue_head(&card->shutdown_sleep);

          #ifdef CONFIG_PM

          mutex_init(&card->power_lock);

          init_waitqueue_head(&card->power_sleep);

          #endif

          建立邏輯設備:Control

          /* the control interface cannot be accessed from the user space until */

          /* snd_cards_bitmask and snd_cards are set with snd_card_register */

          err = snd_ctl_create(card);

          建立proc文件中的info節點:通常就是/proc/asound/card0

          err = snd_info_card_create(card);

          把第一步分配的內存指針放入private_data字段中:

          if (extra_size > 0)

          card->private_data = (char *)card + sizeof(struct snd_card);

          4. snd_card_register()

          snd_card_create()在/sound/core/init.c中定義。

          /**

          * snd_card_register - register the soundcard

          * @card: soundcard structure

          *

          * This function registers all the devices assigned to the soundcard.

          * Until calling this, the  control interface is blocked from the

          * external accesses. Thus, you should call this function at the end

          * of the initialization of the card.

          *

          * Returns zero otherwise a negative error code if the registrain failed.

          */

          int snd_card_register(struct snd_card *card)

          首先,創建sysfs下的設備:

          if (!card->card_dev) {

          card->card_dev = device_create(sound_class, card->dev,

          MKDEV(0, 0), card,

          "card%i", card->number);

          if (IS_ERR(card->card_dev))

          card->card_dev = NULL;

          }

          其中,sound_class是在/sound/sound_core.c中創建的:

          static char *sound_devnode(struct device *dev, mode_t *mode)

          {

          if (MAJOR(dev->devt) == SOUND_MAJOR)

          return NULL;

          return kasprintf(GFP_KERNEL, "snd/%s", dev_name(dev));

          }

          static int __init init_soundcore(void)

          {

          int rc;

          rc = init_oss_soundcore();

          if (rc)

          return rc;

          sound_class = class_create(THIS_MODULE, "sound");

          if (IS_ERR(sound_class)) {

          cleanup_oss_soundcore();

          return PTR_ERR(sound_class);

          }

          sound_class->devnode = sound_devnode;

          return 0;

          }

          由此可見,聲卡的class將會出現在文件系統的/sys/class/sound/下面,并且,sound_devnode()也決定了相應的設備節點也將會出現在/dev/snd/下面。

          接下來的步驟,通過snd_device_register_all()注冊所有掛在該聲卡下的邏輯設備,snd_device_register_all()實際上是通過snd_card的devices鏈表,遍歷所有的snd_device,并且調用snd_device的ops->dev_register()來實現各自設備的注冊的。

          if ((err = snd_device_register_all(card)) < 0)

          return err;

          最后就是建立一些相應的proc和sysfs下的文件或屬性節點,代碼就不貼了。

          至此,整個聲卡完成了建立過程。


        上一頁 1 2 下一頁

        關鍵詞: Linux ALSA

        評論


        相關推薦

        技術專區

        關閉
        主站蜘蛛池模板: 盐山县| 平南县| 淮安市| 黄陵县| 镇赉县| 德令哈市| 来凤县| 马公市| 敦化市| 湟源县| 松江区| 万载县| 新丰县| 井研县| 江安县| 石泉县| 敦煌市| 家居| 古浪县| 武威市| 蕉岭县| 南汇区| 赤城县| 格尔木市| 白银市| 简阳市| 登封市| 买车| 陆河县| 萝北县| 普定县| 泰安市| 桃园市| 奉贤区| 德阳市| 咸丰县| 丘北县| 石林| 浦东新区| 大悟县| 连州市|