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

/* main structure for soundcard */struct snd_card {int number;           /* number of soundcard (index tosnd_cards) */char id[16];           /* id string of this card */char driver[16];        /* driver name */char shortname[32];        /* short name of this soundcard */char longname[80];        /* name of this soundcard */char mixername[80];     /* mixer name */char components[128];       /* card components delimited withspace */struct module *module;     /* top-level module */void *private_data;       /* private data for soundcard */void (*private_free) (struct snd_card *card); /* callback for freeing ofprivate data */struct list_head devices;    /* devices */unsigned int last_numid;   /* last used numeric ID */struct rw_semaphore controls_rwsem;   /* controls list lock */rwlock_t ctl_files_rwlock;  /* ctl_files list lock */int controls_count;        /* count of all controls */int user_ctl_count;      /* count of all user controls */struct list_head controls;  /* all controls for this card */struct list_head ctl_files; /* active control files */struct snd_info_entry *proc_root; /* root for soundcard specific files */struct snd_info_entry *proc_id;  /* the card id */struct proc_dir_entry *proc_root_link; /* number link to real id */struct list_head files_list;    /* all files associated to this card */struct snd_shutdown_f_ops *s_f_ops; /* file operations in the shutdownstate */spinlock_t files_lock;     /* lock the files for this card */int shutdown;         /* this card is going down */int free_on_last_close;        /* free in context of file_release */wait_queue_head_t shutdown_sleep;atomic_t refcount;        /* refcount for disconnection */struct device *dev;     /* device assigned to this card */struct device *card_dev;  /* cardX object for sysfs */#ifdef CONFIG_PMunsigned int power_state;   /* power state */struct mutex power_lock;   /* power lock */wait_queue_head_t power_sleep;
#endif#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)struct snd_mixer_oss *mixer_oss;int mixer_oss_change_count;
#endif
};
  • struct list_head devices 记录该声卡下所有逻辑设备的链表
  • struct list_head controls 记录该声卡下所有的控制单元的链表
  • void *private_data 声卡的私有数据,可以在创建声卡时通过参数指定数据的大小

2. 声卡建立

2.1. 声卡的建立流程

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

struct snd_card *card;
int err;
....
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自己创建
// struct mychip 用于保存专用数据
err = snd_card_create(index, id, THIS_MODULE,sizeof(struct mychip), &card);
// 从private_data中取出
struct mychip *chip = card->private_data;
  • 自己创建:
struct mychip {struct snd_card *card;....
};
struct snd_card *card;
struct mychip *chip;chip = kzalloc(sizeof(*chip), GFP_KERNEL);
......
err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
// 专用数据记录snd_card实例
chip->card = card;
.....

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

static int snd_mychip_dev_free(struct snd_device *device)
{return snd_mychip_free(device->device_data);
}static struct snd_device_ops ops = {.dev_free = snd_mychip_dev_free,
};
....
snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);

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

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

strcpy(card->driver, "My Chip");
strcpy(card->shortname, "My Own Chip 123");
sprintf(card->longname, "%s at 0x%lx irq %i",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. 第五步,注册声卡

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;}ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,THIS_MODULE, 0, &card);if (ret < 0)goto err;card->dev = &dev->dev;strncpy(card->driver, dev->dev.driver->name, sizeof(card->driver));ret = pxa2xx_pcm_new(card, &pxa2xx_ac97_pcm_client, &pxa2xx_ac97_pcm);if (ret)goto err;ret = pxa2xx_ac97_hw_probe(dev);if (ret)goto err;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;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);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},
};module_platform_driver(pxa2xx_ac97_driver);MODULE_AUTHOR("Nicolas Pitre");
MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:pxa2xx-ac97");

驱动程序通常由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<<idx2) {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<<idx2) {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);atomic_set(&card->refcount, 0);
#ifdef CONFIG_PMmutex_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);if (err < 0) {snd_printk(KERN_ERR "unable to register control minors\n");goto __error;}

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

 err = snd_info_card_create(card);if (err < 0) {snd_printk(KERN_ERR "unable to create card info\n");goto __error_ctl;}

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

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

4. snd_card_register()

snd_card_register()/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 ALSA 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 registration 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 中创建的:

struct class *sound_class;
EXPORT_SYMBOL(sound_class);MODULE_DESCRIPTION("Core sound module");
MODULE_AUTHOR("Alan Cox");
MODULE_LICENSE("GPL");static char *sound_devnode(struct device *dev, umode_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下的文件或属性节点,代码就不贴了。

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

Linux ALSA声卡驱动之二:声卡的创建相关推荐

  1. Linux ALSA声卡驱动之二:Platform

    ALSA声卡驱动: 1.Linux ALSA声卡驱动之一:ALSA架构简介和ASOC架构简介 2.Linux ALSA声卡驱动之二:Platform 3. Linux ALSA声卡驱动之三:Platf ...

  2. USB声卡驱动(二):USB音频设备描述符

    USB声卡驱动(二)USB音频设备描述符 本篇笔记,分两部分,第一部分,是基本知识的记录.第二部分是一个实际的例子. 一.基本知识 一个音频设备(Audio Device)含有多个音频功能(Audio ...

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

    linux 块设备驱动(二)--块设备数据结构 本文来源于: 1. http://www.cnblogs.com/dyllove98/archive/2013/07/01/3165567.html 块 ...

  4. 浅谈Linux PCI设备驱动(二)

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

  5. linux usb声卡驱动安装失败,声卡驱动安装出现错误?

    声卡驱动安装出现错误? 发布时间:2007-12-03 08:49:07来源:红联作者:jerrya 之前一打开什么音频视频,Amarok啊,Xine啊,Kaffeine啊就跟我玩崩溃 昨晚上按照一个 ...

  6. Linux ALSA音频驱动之一:框架概述

    1.ALSA概述 ALSA表示高级Linux声音体系结构(Advanced Linux Sound Architecture).它由一系列内核驱动,应用程序编译接口(API)以及支持Linux下声音的 ...

  7. linux 驱动学习笔记-ALSA声卡驱动(二)

    前言 ASoC是建立在标准ALSA驱动层上 ,对底层的alsa框架封装了一层,为了更好的支持嵌入式cpu和音频解码器设备的一套软件体系 在ASOC出现之前 解码器驱动和平台CPU驱动联系过于紧密,导致 ...

  8. Linux SD卡驱动开发(二) —— SD 卡驱动分析HOST篇

    回顾一下前面的知识,MMC 子系统范围三个部分: HOST 部分是针对不同主机的驱动程序,这一部是驱动程序工程师需要根据自己的特点平台来完成的. CORE 部分: 这是整个MMC 的核心存,这部分完成 ...

  9. Linux块设备驱动(二)————块设备的体系架构

    块设备的体系架构从上到下依次为VFS虚拟文件系统.磁盘缓冲.各种类型的磁盘系统.通用块设备层.I/O调度层(优化访问上层的请求(读写请求)).块设备驱动层.块设备硬件层. 1.虚拟文件系统(VFS) ...

  10. Linux ALSA音频驱动一:音频系统概述

    音频系统概述 音频系统通过数据+控制总线与CODEC连接,控制通路用I2C,数据通常为I2S,框图如图1所示. I2C:寄存器读写,用于配置CODEC控制通路. I2S:音频数据传输,通常与platf ...

最新文章

  1. systemd进程管理工具实战教程
  2. python操作excel表格-Python自动化办公之操作Excel文件
  3. F - Tmutarakan Exams URAL - 1091 -莫比乌斯函数-容斥 or DP计数
  4. 阿里短信服务的使用流程
  5. C#中new和override区别
  6. SAP应用搜索分页的实现原理
  7. python数据结构编程_写给Python编程高手之 数据结构
  8. linux浮动ip添加 手动,在Linux 双机下自己手动实现浮动ip技术
  9. spring-boot配置文件中server.context-path不起作用
  10. .Net MVC中设置默认启动为某区域的视图
  11. python session过期_设置session过期时间
  12. 高等数学(第七版)同济大学 习题7-3 个人解答
  13. poedit 图文教程 转载
  14. 计算机英语面试翻译,英语面试问题及回答带翻译
  15. DBF文件的初步了解(一)
  16. 黑鲨怎么修改服务器,黑鲨自定义安装系统教程图解
  17. ch340串口驱动_如何使用串口来给STM32下载程序
  18. 计算机硬盘是什么材质的好,电脑硬盘长什么样子_电脑硬盘什么样的好
  19. 如何在贵金属白银现货走势分析中积累经验?
  20. N以内双素数 马蹄集

热门文章

  1. InVEST模型(工具)下载及安装(更新中)
  2. 时空行为检测数据集 JHMDB UCF101_24 详解
  3. div之间横竖方向的5px间距
  4. 【Pix4d精品教程】垂直摄影空三加密生成DOM和DSM,并按10m间距提取高程点,生成等高线
  5. opencv——Mat 矩阵数据类型转换convertTo
  6. windows 下载 gcc
  7. 服务器安装虚拟声卡,虚拟声卡安装使用 虚拟声卡注意事项
  8. 《麻省理工公开课:线性代数》中文笔记来了!
  9. ZZULIOJ-1047,对数表(Python)
  10. 高级语言程序设计(c语言版)课后答案,高级语言程序设计习题与解答(C语言版)/高等院校教材...