1. struct snd_card

1.1. snd_card是什么

snd_card可以说是整个ALSA音频驱动最顶层的一个结构,整个声卡的软件逻辑结构开始于该结构,几乎所有与声音相关的逻辑设备都是在snd_card的管理之下,声卡驱动的第一个动作通常就是创建一个snd_card结构体。正因为如此,本节中,我们也从 struct cnd_card开始吧。

1.2. snd_card的定义

snd_card的定义位于改头文件中:include/sound/core.h

  1. /* main structure for soundcard */
  2. struct snd_card {
  3. int number;         /* number of soundcard (index to
  4. snd_cards) */
  5. char id[16];            /* id string of this card */
  6. char driver[16];        /* driver name */
  7. char shortname[32];     /* short name of this soundcard */
  8. char longname[80];      /* name of this soundcard */
  9. char mixername[80];     /* mixer name */
  10. char components[128];       /* card components delimited with
  11. space */
  12. struct module *module;      /* top-level module */
  13. void *private_data;     /* private data for soundcard */
  14. void (*private_free) (struct snd_card *card); /* callback for freeing of
  15. private data */
  16. struct list_head devices;   /* devices */
  17. unsigned int last_numid;    /* last used numeric ID */
  18. struct rw_semaphore controls_rwsem; /* controls list lock */
  19. rwlock_t ctl_files_rwlock;  /* ctl_files list lock */
  20. int controls_count;     /* count of all controls */
  21. int user_ctl_count;     /* count of all user controls */
  22. struct list_head controls;  /* all controls for this card */
  23. struct list_head ctl_files; /* active control files */
  24. struct snd_info_entry *proc_root;   /* root for soundcard specific files */
  25. struct snd_info_entry *proc_id; /* the card id */
  26. struct proc_dir_entry *proc_root_link;  /* number link to real id */
  27. struct list_head files_list;    /* all files associated to this card */
  28. struct snd_shutdown_f_ops *s_f_ops; /* file operations in the shutdown
  29. state */
  30. spinlock_t files_lock;      /* lock the files for this card */
  31. int shutdown;           /* this card is going down */
  32. int free_on_last_close;     /* free in context of file_release */
  33. wait_queue_head_t shutdown_sleep;
  34. struct device *dev;     /* device assigned to this card */
  35. #ifndef CONFIG_SYSFS_DEPRECATED
  36. struct device *card_dev;    /* cardX object for sysfs */
  37. #endif
  38. #ifdef CONFIG_PM
  39. unsigned int power_state;   /* power state */
  40. struct mutex power_lock;    /* power lock */
  41. wait_queue_head_t power_sleep;
  42. #endif
  43. #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
  44. struct snd_mixer_oss *mixer_oss;
  45. int mixer_oss_change_count;
  46. #endif
  47. };
  • struct list_head devices     记录该声卡下所有逻辑设备的链表
  • struct list_head controls    记录该声卡下所有的控制单元的链表
  • void *private_data            声卡的私有数据,可以在创建声卡时通过参数指定数据的大小

2. 声卡的建立流程

2.1.1. 第一步,创建snd_card的一个实例

  1. struct snd_card *card;
  2. int err;
  3. ....
  4. err = snd_card_create(index, id, THIS_MODULE, 0, &card);
  • index           一个整数值,该声卡的编号
  • id                字符串,声卡的标识符
  • 第四个参数    该参数决定在创建snd_card实例时,需要同时额外分配的私有数据的大小,该数据的指针最终会赋值给snd_card的private_data数据成员
  • card             返回所创建的snd_card实例的指针

2.1.2. 第二步,创建声卡的芯片专用数据

声卡的专用数据主要用于存放该声卡的一些资源信息,例如中断资源、io资源、dma资源等。可以有两种创建方法:

  • 通过上一步中snd_card_create()中的第四个参数,让snd_card_create自己创建
  1. // struct mychip 用于保存专用数据
  2. err = snd_card_create(index, id, THIS_MODULE,
  3. sizeof(struct mychip), &card);
  4. // 从private_data中取出
  5. struct mychip *chip = card->private_data;
  • 自己创建:
  1. struct mychip {
  2. struct snd_card *card;
  3. ....
  4. };
  5. struct snd_card *card;
  6. struct mychip *chip;
  7. chip = kzalloc(sizeof(*chip), GFP_KERNEL);
  8. ......
  9. err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
  10. // 专用数据记录snd_card实例
  11. chip->card = card;
  12. .....

然后,把芯片的专有数据注册为声卡的一个低阶设备:

  1. static int snd_mychip_dev_free(struct snd_device *device)
  2. {
  3. return snd_mychip_free(device->device_data);
  4. }
  5. static struct snd_device_ops ops = {
  6. .dev_free = snd_mychip_dev_free,
  7. };
  8. ....
  9. snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);

注册为低阶设备主要是为了当声卡被注销时,芯片专用数据所占用的内存可以被自动地释放。

2.1.3. 第三步,设置Driver的ID和名字

  1. strcpy(card->driver, "My Chip");
  2. strcpy(card->shortname, "My Own Chip 123");
  3. sprintf(card->longname, "%s at 0x%lx irq %i",
  4. card->shortname, chip->ioport, chip->irq);

snd_card的driver字段保存着芯片的ID字符串,user空间的alsa-lib会使用到该字符串,所以必须要保证该ID的唯一性。shortname字段更多地用于打印信息,longname字段则会出现在/proc/asound/cards中。

2.1.4. 第四步,创建声卡的功能部件(逻辑设备),例如PCM,Mixer,MIDI等

这时候可以创建声卡的各种功能部件了,还记得开头的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. 第五步,注册声卡

  1. err = snd_card_register(card);
  2. if (err < 0) {
  3. snd_card_free(card);
  4. return err;
  5. }

2.2. 一个实际的例子

我把/sound/arm/pxa2xx-ac97.c的部分代码贴上来:

  1. static int __devinit pxa2xx_ac97_probe(struct platform_device *dev)
  2. {
  3. struct snd_card *card;
  4. struct snd_ac97_bus *ac97_bus;
  5. struct snd_ac97_template ac97_template;
  6. int ret;
  7. pxa2xx_audio_ops_t *pdata = dev->dev.platform_data;
  8. if (dev->id >= 0) {
  9. dev_err(&dev->dev, "PXA2xx has only one AC97 port./n");
  10. ret = -ENXIO;
  11. goto err_dev;
  12. }
  13. ////(1)////
  14. ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
  15. THIS_MODULE, 0, &card);
  16. if (ret < 0)
  17. goto err;
  18. card->dev = &dev->dev;
  19. ////(3)////
  20. strncpy(card->driver, dev->dev.driver->name, sizeof(card->driver));
  21. ////(4)////
  22. ret = pxa2xx_pcm_new(card, &pxa2xx_ac97_pcm_client, &pxa2xx_ac97_pcm);
  23. if (ret)
  24. goto err;
  25. ////(2)////
  26. ret = pxa2xx_ac97_hw_probe(dev);
  27. if (ret)
  28. goto err;
  29. ////(4)////
  30. ret = snd_ac97_bus(card, 0, &pxa2xx_ac97_ops, NULL, &ac97_bus);
  31. if (ret)
  32. goto err_remove;
  33. memset(&ac97_template, 0, sizeof(ac97_template));
  34. ret = snd_ac97_mixer(ac97_bus, &ac97_template, &pxa2xx_ac97_ac97);
  35. if (ret)
  36. goto err_remove;
  37. ////(3)////
  38. snprintf(card->shortname, sizeof(card->shortname),
  39. "%s", snd_ac97_get_short_name(pxa2xx_ac97_ac97));
  40. snprintf(card->longname, sizeof(card->longname),
  41. "%s (%s)", dev->dev.driver->name, card->mixername);
  42. if (pdata && pdata->codec_pdata[0])
  43. snd_ac97_dev_add_pdata(ac97_bus->codec[0], pdata->codec_pdata[0]);
  44. snd_card_set_dev(card, &dev->dev);
  45. ////(5)////
  46. ret = snd_card_register(card);
  47. if (ret == 0) {
  48. platform_set_drvdata(dev, card);
  49. return 0;
  50. }
  51. err_remove:
  52. pxa2xx_ac97_hw_remove(dev);
  53. err:
  54. if (card)
  55. snd_card_free(card);
  56. err_dev:
  57. return ret;
  58. }
  59. static int __devexit pxa2xx_ac97_remove(struct platform_device *dev)
  60. {
  61. struct snd_card *card = platform_get_drvdata(dev);
  62. if (card) {
  63. snd_card_free(card);
  64. platform_set_drvdata(dev, NULL);
  65. pxa2xx_ac97_hw_remove(dev);
  66. }
  67. return 0;
  68. }
  69. static struct platform_driver pxa2xx_ac97_driver = {
  70. .probe      = pxa2xx_ac97_probe,
  71. .remove     = __devexit_p(pxa2xx_ac97_remove),
  72. .driver     = {
  73. .name   = "pxa2xx-ac97",
  74. .owner  = THIS_MODULE,
  75. #ifdef CONFIG_PM
  76. .pm = &pxa2xx_ac97_pm_ops,
  77. #endif
  78. },
  79. };
  80. static int __init pxa2xx_ac97_init(void)
  81. {
  82. return platform_driver_register(&pxa2xx_ac97_driver);
  83. }
  84. static void __exit pxa2xx_ac97_exit(void)
  85. {
  86. platform_driver_unregister(&pxa2xx_ac97_driver);
  87. }
  88. module_init(pxa2xx_ac97_init);
  89. module_exit(pxa2xx_ac97_exit);
  90. MODULE_AUTHOR("Nicolas Pitre");
  91. 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中定义。

  1. /**
  2. *  snd_card_create - create and initialize a soundcard structure
  3. *  @idx: card index (address) [0 ... (SNDRV_CARDS-1)]
  4. *  @xid: card identification (ASCII string)
  5. *  @module: top level module for locking
  6. *  @extra_size: allocate this extra size after the main soundcard structure
  7. *  @card_ret: the pointer to store the created card instance
  8. *
  9. *  Creates and initializes a soundcard structure.
  10. *
  11. *  The function allocates snd_card instance via kzalloc with the given
  12. *  space for the driver to use freely.  The allocated struct is stored
  13. *  in the given card_ret pointer.
  14. *
  15. *  Returns zero if successful or a negative error code.
  16. */
  17. int snd_card_create(int idx, const char *xid,
  18. struct module *module, int extra_size,
  19. struct snd_card **card_ret)

首先,根据extra_size参数的大小分配内存,该内存区可以作为芯片的专有数据使用(见前面的介绍):

  1. card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);
  2. if (!card)
  3. return -ENOMEM;

拷贝声卡的ID字符串:

  1. if (xid)
  2. strlcpy(card->id, xid, sizeof(card->id));

如果传入的声卡编号为-1,自动分配一个索引编号:

  1. if (idx < 0) {
  2. for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++)
  3. /* idx == -1 == 0xffff means: take any free slot */
  4. if (~snd_cards_lock & idx & 1<<idx2) {
  5. if (module_slot_match(module, idx2)) {
  6. idx = idx2;
  7. break;
  8. }
  9. }
  10. }
  11. if (idx < 0) {
  12. for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++)
  13. /* idx == -1 == 0xffff means: take any free slot */
  14. if (~snd_cards_lock & idx & 1<<idx2) {
  15. if (!slots[idx2] || !*slots[idx2]) {
  16. idx = idx2;
  17. break;
  18. }
  19. }
  20. }

初始化snd_card结构中必要的字段:

  1. card->number = idx;
  2. card->module = module;
  3. INIT_LIST_HEAD(&card->devices);
  4. init_rwsem(&card->controls_rwsem);
  5. rwlock_init(&card->ctl_files_rwlock);
  6. INIT_LIST_HEAD(&card->controls);
  7. INIT_LIST_HEAD(&card->ctl_files);
  8. spin_lock_init(&card->files_lock);
  9. INIT_LIST_HEAD(&card->files_list);
  10. init_waitqueue_head(&card->shutdown_sleep);
  11. #ifdef CONFIG_PM
  12. mutex_init(&card->power_lock);
  13. init_waitqueue_head(&card->power_sleep);
  14. #endif

建立逻辑设备:Control

  1. /* the control interface cannot be accessed from the user space until */
  2. /* snd_cards_bitmask and snd_cards are set with snd_card_register */
  3. err = snd_ctl_create(card);

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

  1. err = snd_info_card_create(card);

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

  1. if (extra_size > 0)
  2. card->private_data = (char *)card + sizeof(struct snd_card);

4. snd_card_register()

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

  1. /**
  2. *  snd_card_register - register the soundcard
  3. *  @card: soundcard structure
  4. *
  5. *  This function registers all the devices assigned to the soundcard.
  6. *  Until calling this, the ALSA control interface is blocked from the
  7. *  external accesses.  Thus, you should call this function at the end
  8. *  of the initialization of the card.
  9. *
  10. *  Returns zero otherwise a negative error code if the registrain failed.
  11. */
  12. int snd_card_register(struct snd_card *card)

首先,创建sysfs下的设备:

  1. if (!card->card_dev) {
  2. card->card_dev = device_create(sound_class, card->dev,
  3. MKDEV(0, 0), card,
  4. "card%i", card->number);
  5. if (IS_ERR(card->card_dev))
  6. card->card_dev = NULL;
  7. }

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

  1. static char *sound_devnode(struct device *dev, mode_t *mode)
  2. {
  3. if (MAJOR(dev->devt) == SOUND_MAJOR)
  4. return NULL;
  5. return kasprintf(GFP_KERNEL, "snd/%s", dev_name(dev));
  6. }
  7. static int __init init_soundcore(void)
  8. {
  9. int rc;
  10. rc = init_oss_soundcore();
  11. if (rc)
  12. return rc;
  13. sound_class = class_create(THIS_MODULE, "sound");
  14. if (IS_ERR(sound_class)) {
  15. cleanup_oss_soundcore();
  16. return PTR_ERR(sound_class);
  17. }
  18. sound_class->devnode = sound_devnode;
  19. return 0;
  20. }

由此可见,声卡的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()来实现各自设备的注册的。

  1. if ((err = snd_device_register_all(card)) < 0)
  2. return err;

最后就是建立一些相应的proc和sysfs下的文件或属性节点,代码就不贴了。

至此,整个声卡完成了建立过程。

Linux ALSA声卡驱动之二:声卡的创建的更多相关文章

  1. 基于Linux ALSA音频驱动的wav文件解析及播放程序 2012

    本设计思路:先打开一个普通wav音频文件,从定义的文件头前面的44个字节中,取出文件头的定义消息,置于一个文件头的结构体中.然后打开alsa音频驱动,从文件头结构体取出采样精度,声道数,采样频率三个重 ...

  2. 【VS开发】【DSP开发】浅谈Linux PCI设备驱动(二)

    我们在 浅谈Linux PCI设备驱动(一)中(以下简称 浅谈(一) )介绍了PCI的配置寄存器组,而Linux PCI初始化就是使用了这些寄存器来进行的.后面我们会举个例子来说明Linux PCI设 ...

  3. 嵌入式驱动开发之---Linux ALSA音频驱动(一)

    本文的部分内容参考来自DroidPhone的博客(http://blog.csdn.net/droidphone/article/details/6271122),关于ALSA写得很不错的文章,只是少 ...

  4. 调试exynos4412—ARM嵌入式Linux—LEDS/GPIO驱动之二

    /** ****************************************************************************** * @author    暴走的小 ...

  5. Linux 块设备驱动 (二)

    linux下Ramdisk驱动 1 什么是Ramdisk Ramdisk是一种模拟磁盘,其数据实际上是存储在RAM中,它使用一部分内存空间来模拟出一个磁盘设备,并以块设备的方式来组织和访问这片内存.对 ...

  6. linux lcd设备驱动剖析二

    上一节中,分析了s3c2410fb,c的入口出口函数,以及一些重要结构体的分析,初步知道了这是一个平台驱动的架构. 上一节文章链接:http://blog.csdn.net/lwj103862095/ ...

  7. linux 块设备驱动(二)——块设备数据结构

    本文来源于: 1. http://www.cnblogs.com/dyllove98/archive/2013/07/01/3165567.html 块设备相关的数据结构以及接口: 块设备接口则相对复 ...

  8. Linux下GPIO驱动(二) ----s3c_gpio_cfgpin();gpio_set_value();

    首先来看s3c_gpio_cfgpin(); int s3c_gpio_cfgpin(unsigned int pin, unsigned int config) { struct s3c_gpio_ ...

  9. Linux 网络编程详解二(socket创建流程、多进程版)

    netstat -na | grep " --查看TCP/IP协议连接状态 //socket编程提高版--服务器 #include <stdio.h> #include < ...

随机推荐

  1. Android中如何将Bitmap byte裸数据转换成Bitmap图片int数据

    Android中如何将Bitmap byte裸数据转换成Bitmap图片int数据 2014-06-11 10:45:14   阅读375次 我们在JNI中处理得到的BMP图片Raw数据,我们应该如何 ...

  2. 如何参与Hibernate-ORM项目

    1.注册 hibernate jira账户,hibernate中的issue和bug都会在此论坛发布 注册地址:https://hibernate.onjira.com 2.创建Hibernate-O ...

  3. 转:Web安全与Rational AppScan入门

    Web 应用的基础概念 在讨论 Web 应用安全之前,先简单介绍一下 Web 应用基础概念,这样便于理解为什么 Web 应用是脆弱的,容易受到攻击. 1. 什么是 Web 应用 Web 应用是由动态脚 ...

  4. PAT1008

    1008. Elevator (20) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B The highest building in our city has on ...

  5. 将项目同时托管到Github和Git@OSC

    http://my.oschina.net/GIIoOS/blog/404555?fromerr=KHvn8UKH 摘要 Github是最大的git代码托管平台,​GIT@OSC是国内最大的git代码 ...

  6. PHP资源类型

    在PHP中,我们经常使用到资源类型变量.例如:mysql连接.文件句柄等. 这些变量无法使用标量来表示,那么在Zend内核中是如何将PHP中的资源变量与C语言中的资源衔接的呢? 一.资源变量在PHP中 ...

  7. Cisco设备IOS的恢复方法 两种方法

    如果不小心把Router或者Switch的IOS删除了,特别是Flash中的IOS和ROM中的Mini IOS都没有了的话,连启动都不行的话,有什么方法恢复它呢?答案是方法不只一种,而是两种.其实是我 ...

  8. 关于UIView及其子类重绘drawRect

    转载自:https://nezha.gitbooks.io/ios-developmentarticles/content/UIView%E7%9A%84drawRect%E9%87%8D%E7%BB ...

  9. hadoop Yarn运行机制

  10. 使用virsh命令创建KVM虚拟机快照

    查看虚拟机所在主机和虚拟机名称:[root@node-1 ~]# nova show a88dcf5d-c8b2-46a5-af27-a176d8235c9d|grep hyper| OS-EXT-S ...