概述
(1)ALSA简介
(1)
Native ALSA Application:tinyplay/tinycap/tinymix,这些用户程序直接调用 alsa 用户库接口来实现放音、录音、控制
ALSA Library API:alsa 用户库接口,常见有 tinyalsa、alsa-lib
ALSA CORE:alsa 核心层,向上提供逻辑设备(PCM/CTL/MIDI/TIMER/…)系统调用,向下驱动硬件设备(Machine/I2S/DMA/CODEC)
ASoC CORE:asoc 是建立在标准 alsa core 基础上,为了更好支持嵌入式系统和应用于移动设备的音频 codec 的一套软件体系
Hardware Driver:音频硬件设备驱动,由三大部分组成,分别是 Machine、Platform、Codec
(2)
alsa驱动框架核心层给我们干的活:创建声卡设备的控制接口和PCM设备
snd_soc_init()--->
platform_driver_register(&soc_driver)--->
soc_probe()--->
snd_soc_register_card(card)--->注册自己的声卡设备
snd_soc_instantiate_card(card)--->
static int snd_soc_instantiate_card(struct snd_soc_card *card)
{
ret = snd_card_create();创建声卡设备-->snd_ctl_create()-->snd_ctl_dev_register创建声卡设备的控制接口函数-->snd_register_device()-->snd_register_device_for_dev()
ret = soc_probe_link_dais(card, i, order); --->soc_new_pcm()创建一个新的PCM设备-->snd_pcm_new()--->_snd_pcm_new()--->snd_pcm_new_stream()
ret = snd_card_register(card->snd_card);-->snd_pcm_dev_register()创建pcm设备文件-->snd_register_device_for_dev()创建pcm设备节点
}
(3)
在内核设备驱动层,ALSA提供了alsa-driver,同时在应用层,ALSA为我们提供了alsa-lib,应用程序只要调用alsa-lib提供的API,即可以完成对底层音频硬件的控制。
ls sound
core 该目录包含了ALSA驱动的中间层,它是整个ALSA驱动的核心部分
core/oss 包含模拟旧的OSS架构的PCM和Mixer模块
core/seq 有关音序器相关的代码
include ALSA驱动的公共头文件目录,该目录的头文件需要导出给用户空间的应用程序使用,通常,驱动模块私有的头文件不应放置在这里
drivers 放置一些与CPU、BUS架构无关的公用代码
i2c ALSA自己的I2C控制代码
pci pci声卡的顶层目录,子目录包含各种pci声卡的代码
isa isa声卡的顶层目录,子目录包含各种isa声卡的代码
soc 针对system-on-chip体系的中间层代码
soc/codecs 针对soc体系的各种codec的代码,与平台无关
(4)
alsa驱动的设备文件结构: 字符设备
# ls /dev/snd/ -lh
total 0
crw-rw---- 1 root root 116, 0 Aug 21 16:01 controlC0
crw-rw---- 1 root root 116, 24 Aug 21 16:01 pcmC0D0c
crw-rw---- 1 root root 116, 16 Aug 21 16:01 pcmC0D0p
crw-rw---- 1 root root 116, 25 Aug 21 16:01 pcmC0D1c
crw-rw---- 1 root root 116, 17 Aug 21 16:01 pcmC0D1p
crw-rw---- 1 root root 116, 26 Aug 21 16:01 pcmC0D2c
crw-rw---- 1 root root 116, 33 Aug 21 16:01 timer
controlC0 用于声卡的控制,例如通道选择,混音,麦克风的控制等
pcmC0D0c 用于录音的pcm设备
pcmC0D0p 用于播放的pcm设备
timer 定时器
C0D0代表的是声卡0中的设备0,pcmC0D0c最后一个c代表capture,pcmC0D0p最后一个p代表playback,这些都是alsa-driver中的命名规则。
(5)
sound/core/sound.c
static int __init alsa_sound_init(void)
{
if (register_chrdev(major, "alsa", &snd_fops)) //注册alsa字符设备
}
static const struct file_operations snd_fops =
{
.owner = THIS_MODULE,
.open = snd_open,
.llseek = noop_llseek,
};
static struct snd_minor *snd_minors[SNDRV_OS_MINORS];
谁来设置snd_minors[]结构体数组,snd_register_device_for_dev函数里面有数组项snd_minors外,为了mdev或udev能自动创建设备节点,我们有class_create()函数创建类,下面创建设备device_create(),类在Sound_ core.c入口函数里面被创建,sound.c的入口函数里面注册字符设备函数,snd_register_device_for_dev()函数里面创建声卡逻辑设备时有device_create()
(6)
谁调用snd_register_device_for_dev()(两路调用)
一路:声卡设备的控制接口
另一路:声卡设备的数据接口
声卡设备的控制接口
sound/core/control.c
snd_card_create---> 创建一个snd_card结构体
snd_ctl_create--->
static int snd_ctl_dev_register(struct snd_device *device)--> //创建声卡设备的控制接口函数
snd_register_device--->
snd_register_device_for_dev()
int snd_ctl_create(struct snd_card *card)
{
static struct snd_device_ops ops = {
.dev_free = snd_ctl_dev_free,
.dev_register = snd_ctl_dev_register,
.dev_disconnect = snd_ctl_dev_disconnect,
};
if (snd_BUG_ON(!card))
return -ENXIO;
return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
}
创建声卡设备的数据接口(声卡驱动)
snd_pcm_new----> 创建一个新的PCM设备
_snd_pcm_new---> 创建播放流和录音流
static int snd_pcm_dev_register(struct snd_device *device) //创建声卡设备的数据接口
总结:
(1)构造snd_card结构体,snd_card_create()构造snd_card结构体并自动创建控制接口。调用函数snd_ctrl_create
(2)初始化;如snd_pcm_new(),创建逻辑设备(播放设备或录音设备)
(3)注册 snd_card_register
(2)声卡的创建
(1)
struct snd_card是什么
snd_card可以说是整个ALSA音频驱动最顶层的一个结构,整个声卡的软件逻辑结构开始于该结构,几乎所有与声音相关的逻辑设备都是在snd_card的管理之下,声卡驱动的第一个动作通常就是创建一个snd_card结构体。
struct snd_card {
struct list_head devices 记录该声卡下所有逻辑设备的链表
struct list_head controls 记录该声卡下所有的控制单元的链表
void *private_data 声卡的私有数据,可以在创建声卡时通过参数指定数据的大小
}
snd_soc_init
{
return platform_driver_register(&soc_driver);
}
static int soc_probe(struct platform_device *pdev)
{
return snd_soc_register_card(card);
}int snd_soc_register_card(struct snd_soc_card *card)
{
ret = snd_soc_instantiate_card(card);
}
static int snd_soc_instantiate_card(struct snd_soc_card *card)
{
ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,card->owner, 0, &card->snd_card); -->snd_ctl_create()
ret = soc_probe_link_dais(card, i, order); --->soc_new_pcm()-->snd_pcm_new()
ret = snd_card_register(card->snd_card);
}
snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,card->owner, 0, &card->snd_card);创建snd_card的一个实例
index:一个整数值,该声卡的编号 id:字符串,声卡的标识符 第四个参数:该参数决定在创建snd_card实例时,需要同时额外分配的私有数据的大小,该数据的指针最终会赋值给snd_card的private_data数据成员
card:返回所创建的snd_card实例的指针
设置Driver的ID和名字
static int __init alsa_sound_init(void) subsys_initcall(alsa_sound_init);
snd_info_init()-->
snd_card_info_init()-->
snd_card_info_read()-->
{
snd_iprintf(buffer, "%2i [%-15s]: %s - %sn",idx,card->id,card->driver,card->shortname);
snd_iprintf(buffer, "%sn",card->longname);
}
创建声卡的功能部件(逻辑设备),例如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()
注册声卡 static int snd_soc_instantiate_card(struct snd_soc_card *card)
{
ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,card->owner, 0, &card->snd_card);
ret = soc_probe_link_dais(card, i, order); --->soc_new_pcm()-->snd_pcm_new()
ret = snd_card_register(card->snd_card);
}
比如我们的例子:
sound/soc/ingenic/asoc-board/phoenix_icdc.c
static int snd_phoenix_probe(struct platform_device *pdev)
{
ret = snd_soc_register_card(&phoenix);
}
(2)
int snd_card_create(int idx, const char *xid,struct module *module, int extra_size,struct snd_card **card_ret)
{
if (extra_size < 0)
extra_size = 0;
card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);
//根据extra_size参数的大小分配内存,该内存区可以作为芯片的专有数据使用
if (xid)
strlcpy(card->id, xid, sizeof(card->id));
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;
}
}
}
//拷贝声卡的ID字符串
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_PM
mutex_init(&card->power_lock);
init_waitqueue_head(&card->power_sleep);
#endif
//初始化snd_card结构中必要的字段
err = snd_ctl_create(card);
if (err < 0) {
snd_printk(KERN_ERR "unable to register control minorsn");
goto __error;
}
//建立逻辑设备:Control
err = snd_info_card_create(card);
if (err < 0) {
snd_printk(KERN_ERR "unable to create card infon");
goto __error_ctl;
}
//建立proc文件中的info节点:通常就是/proc/asound/card0
if (extra_size > 0)
card->private_data = (char *)card + sizeof(struct snd_card);
*card_ret = card;
return 0;
//分配的内存指针放入private_data字段中
}
int snd_card_register(struct snd_card *card)
{
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;
}
//创建sysfs下的设备
if ((err = snd_device_register_all(card)) < 0)
return err;
//通过snd_device_register_all()注册所有挂在该声卡下的逻辑设备,snd_device_register_all()实际上是通过snd_card的devices链表,遍历所有的snd_device,并且调用snd_device的ops->dev_register()来实现各自设备的注册的。
最后代码就是建立一些相应的proc和sysfs下的文件或属性节点
}
static int __init init_soundcore(void) subsys_initcall(init_soundcore)
{
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;
}
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));
}
//声卡的class将会出现在文件系统的/sys/class/sound/下面,并且,sound_devnode()也决定了相应的设备节点也将会出现在/dev/snd/下面。
# ls /dev/snd/ -lh
total 0
crw-rw---- 1 root root 116, 0 Aug 21 12:00 controlC0
crw-rw---- 1 root root 116, 24 Aug 21 12:00 pcmC0D0c
crw-rw---- 1 root root 116, 16 Aug 21 12:00 pcmC0D0p
crw-rw---- 1 root root 116, 25 Aug 21 12:00 pcmC0D1c
crw-rw---- 1 root root 116, 17 Aug 21 12:00 pcmC0D1p
crw-rw---- 1 root root 116, 26 Aug 21 12:00 pcmC0D2c
crw-rw---- 1 root root 116, 33 Aug 21 12:00 timer
ls /sys/class/sound/ -lh
total 0
lrwxrwxrwx 1 root root 0 Aug 21 12:00 card0 -> ../../devices/platform/ingenic-alsa.0/sound/card0
lrwxrwxrwx 1 root root 0 Aug 21 12:00 controlC0 -> ../../devices/platform/ingenic-alsa.0/sound/card0/controlC0
lrwxrwxrwx 1 root root 0 Aug 21 12:00 pcmC0D0c -> ../../devices/platform/ingenic-alsa.0/sound/card0/pcmC0D0c
lrwxrwxrwx 1 root root 0 Aug 21 12:00 pcmC0D0p -> ../../devices/platform/ingenic-alsa.0/sound/card0/pcmC0D0p
lrwxrwxrwx 1 root root 0 Aug 21 12:00 pcmC0D1c -> ../../devices/platform/ingenic-alsa.0/sound/card0/pcmC0D1c
lrwxrwxrwx 1 root root 0 Aug 21 12:00 pcmC0D1p -> ../../devices/platform/ingenic-alsa.0/sound/card0/pcmC0D1p
lrwxrwxrwx 1 root root 0 Aug 21 12:00 pcmC0D2c -> ../../devices/platform/ingenic-alsa.0/sound/card0/pcmC0D2c
lrwxrwxrwx 1 root root 0 Aug 21 12:00 timer -> ../../devices/virtual/sound/timer
最后
以上就是感性可乐为你收集整理的Linux ALSA驱动框架(一)--ALSA架构简介--声卡的创建的全部内容,希望文章能够帮你解决Linux ALSA驱动框架(一)--ALSA架构简介--声卡的创建所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复