Linux ALSA驱动框架(一)--ALSA架构简介--声卡的创建

(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 - %s\n",idx,card->id,card->driver,card->shortname);
snd_iprintf(buffer, "%s\n",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 minors\n");
        goto __error;
    }
//建立逻辑设备:Control

 err = snd_info_card_create(card);
    if (err < 0) {
        snd_printk(KERN_ERR "unable to create card info\n");
        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








猜你喜欢

转载自blog.csdn.net/sinat_37817094/article/details/80490830