(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

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

  1. Linux ALSA驱动框架(六)--ASoC架构中的Platfrom

    (1) Platform驱动在ASoC中的作用 ASoC被分为Machine,Platform和Codec三大部件,Platform驱动的主要作用是完成音频数据的管理,最终通过CPU的数字音频接口(D ...

  2. 转载:Linux音频驱动-OSS和ALSA声音系统简介及其比较

    Linux音频驱动-OSS和ALSA声音系统简介及其比较 概述 昨天想在Ubuntu上用一下HTK工具包来绘制语音信号的频谱图和提取MFCC的结果,但由于前段时间把Ubuntu升级到13.04,系统的 ...

  3. ALSA驱动框架分析

    一.ALSA驱动框架介绍 1.ALSA介绍 ALSA(Advanced Linux Sound Architecture-高级linux声音架构),目前已经成为了linux的主流音频体系结构,ALSA ...

  4. Linux PCI驱动框架分析:(Peripheral Component Interconnect,外部设备互联)

    <DPDK 20.05 | rte_pci_bus思维导图 | 第一版> <linux系统下:IO端口,内存,PCI总线 的 读写(I/O)操作> <Linux指令:ls ...

  5. Linux USB驱动框架分析 【转】

    转自:http://blog.chinaunix.net/uid-11848011-id-96188.html 初次接触与OS相关的设备驱动编写,感觉还挺有意思的,为了不至于忘掉看过的东西,笔记跟总结 ...

  6. Linux uart驱动框架

    Linux uart驱动框架 串口驱动框架包括两部分 struct uart_driver int uart_register_driver(struct uart_driver *uart); vo ...

  7. Linux SPI驱动框架(2)——控制器驱动层

    SPI控制器驱动层   上节中,讲了SPI核心层的东西,这一部分,以全志平台SPI控制器驱动为例,对SPI控制器驱动进行说明. SPI控制器驱动,即SPI硬件控制器对应的驱动,核心部分需要实现硬件SP ...

  8. Linux I2C驱动框架(超详细)

    Linux I2C驱动框架 文章目录 Linux I2C驱动框架 一.几个重要的对象 1.I2C总线 2.I2C驱动 3.I2C设备 4.I2C设配器 小结 二.内核源码分析 1.注册I2C驱动 2. ...

  9. Linux PCIe驱动框架分析(第二章)

    目录 项目背景 1. 概述 2. 数据结构 3. 流程分析 3.1 设备驱动模型 3.2 初始化 3.2.1 pci_bus_match 3.2.2 pci_device_probe 3.3 枚举 项 ...

最新文章

  1. Mybatis常见的面试题总结
  2. 科大星云诗社动态20210830
  3. 四种排序方法用java实现
  4. SQL Server 2008中的代码安全(四):主密钥
  5. Thrift序列化字节数组存取redis VS 对象转Json存取Redis
  6. 图论 —— 网络流 —— 最大流 —— 压入与重标记算法
  7. kali linux下sqlmap使用教程
  8. 2022.5.23-5.29 AI行业周刊(第99期):AI创业道路
  9. matlab平滑处理例题,(完整word版)matlab中smooth函数平滑处理数据实例
  10. EA开发系列---技术指标的使用
  11. 解决unable to find valid certification path to requested target
  12. Qt编写自定义控件:带阴影、圆角、可拉伸的弹窗
  13. 机器学习Machine Learning
  14. JS:利用函数,求任意三个数最大值,任意两个数的任意运算结果,判断任意数值是否为素数。
  15. 地图定位技术揭秘(一)
  16. 软件测试及Java开发前了解的基础
  17. 测试用例----测试大纲法
  18. 超好看的3D烟花代码(html+css+js)带音乐
  19. PySide2 connect 时指定槽函数的参数
  20. NeurlPS2020:Neuron-level Structured Pruning using Polarization Regularizer(polarization正则化技术)

热门文章

  1. 好消息:Dubbo Spring Boot要来了
  2. backbone学习笔记:集合(Collection)
  3. 在Myeclipse中创建自定义用户类库
  4. iscsi网络存储服务
  5. 现任明教教主DM×××的3G链路备份
  6. 介绍几种不同的标志符号 CMC CPA CPE CMA
  7. 防爆技术在工业电子秤中的最新应用(转)
  8. 【LeetCode】开始LeetCode的第一题offer03
  9. python调用dll出现错误总结如下(持续更新)
  10. 有序序列的二分查找、冒泡排序、归并排序算法实战解析