当前位置:首页 > > 充电吧
[导读]Alsa驱动分析GuideRevisionHistoryDateIssueDescriptionAuthorFirst draftWylhistory    目录1.    Abstract.. 32

Alsa驱动分析

Guide


RevisionHistory

Date

Issue

Description

Author

First draft

Wylhistory

 

 

 

 

目录

1.    Abstract.. 3

2.    Introduction.. 3

3.    音频驱动框架介绍.... 3

3.1      音频设备的注册... 3

3.2     音频驱动的注册... 4

3.2.1       Probe函数的调用... 4

3.2.2       Soc_probe函数... 4

4.    通常的使用流程的分析.... 6

4.1.1       open过程介绍... 6

4.1.2       snd_pcm_hw_params流程分析... 8

4.1.3       prepare流程分析... 9

4.1.4       write的流程... 15

4.1.5       使用流程的总结t18

5.    Amixer调用的相关逻辑.... 18

5.1.1       Amixer调用的上层逻辑... 19

5.1.2       Amixer的内核流程... 20

6.    总结.... 21

7.    未讨论.... 21

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1.              Abstract

主要是讲2.6.21内核里面的alsa驱动的架构,以及在我们的平台上需要注意的东西。.

2.              Introduction

分成几个部分:

驱动整体框架,一个简单的播放流程介绍,以及我们的平台需要注意的地方;

3.              音频驱动框架介绍 3.1              音频设备的注册

这就是设备的注册了,设备本身非常简单,复杂的是这个设备的drvdata,drvdata里面包含了三部分,关于machine的,关于platform的,关于codec的,从大体上说machine主要是关于cpu这边的也可以说是关于ssp本身设置的,而platform是关于平台级别的,就是说这个平台本身实现相关的,而codec就是与我们所用的音频codec相关的;基本上这里就可以看出整个音频驱动的架构特点,就是从alsa层进入——>内核alsa层接口->core层,这里再调用上面说的三个方面的函数来处理,先是cpu级别的,再是platform的,再是codec级别的,这几层做完了,工作也就做得差不多了,后面会详细讲讲,当然这个执行顺序不是固定的(不知道是不是marvel写代码不专业导致的),但多半都包括了这三部分的工作;

3.2           音频驱动的注册 3.2.1         Probe函数的调用      

                 

                   前面讲了设备的注册,里面的设备的名字就是”soc-audio”,而这里的driver的注册时名字也是” soc-audio”,对于platform的设备匹配的原则是根据名字的,所以将会匹配成功,成功后就会执行audio驱动提供的probe函数soc_probe;

3.2.2         Soc_probe函数

这个函数本身架构很简单,和前面说的逻辑一样,先调用了cpu级别的probe,再是codec级别的,最后是platform的(这里三个的顺序就不一样),但是因为cpu级别的和platform级别的都为空,最后都调用了codec级别的probe函数,也就是micco_soc_probe,这个函数基本上就完成了所有应该完成的音频驱动的初始化了;简单的划分,分成两部分,对上和对下:对上主要是注册设备节点,以及这些设备节点对应的流的创建;对下主要是读写函数的设置,codec本身的dai设置,初始化寄存器的设置,最重要的就是后面的control的创建和门的创建了,如下图所示:

这里面的第一部分就是负责初始化的;

 

第二部分就是创建卡和流,对于alsa驱动来说,是先分成卡0,卡1…,然后对于每一个卡的每一个cpu支持的dai(digit audio interface)也就是pcm接口或者i2S接口等都要建立对应的流,一个dai有可能包含两个流,一个是录的一个是play的,但在我们的平台上对于i2S的dai是没有录音功能的,所以我们的平台只有一个卡,三个流,pcm的录和play,i2S的play;流的创建还是更多的考虑为上层服务的,它所提供的接口都是soc层的,这里非常重要的地方在于驱动的一个典型做法那就是如何把关键的内核数据结构和export到外部的/dev下的设备节点实现关联,比如:

 

关键数据结构structsnd_pcm,是根据cpu所固有的dai创建的,而对于每一个struct  snd_pcm又可能用到两个substream(它们实现具体的流的播放等),它们之间的链接是通过它的内部数据成员struct snd_pcm_str streams[2];来连接的,而这个snd_pcm类型的指针是在函数snd_device_new里面通过device_data放到设备里面的,这个设备会在snd_device_register_all

的时候注册到/dev下面,并且调用dev_set_drvdata(preg->dev, private_data);来把这个指针放到设备的私有数据里面;而在需要使用的时候通过snd_pcm_playback_open里面的snd_lookup_minor_data函数取得其私有数据并返回的,这样就实现了设备节点和对应的驱动的数据结构的关联,这是一种非常普遍的做法;有了这个数据结构它就可以根据一定的原则取得对应于这个需求的substream,于是一切的操作都可以交给这个substream了;

 

第三部分就是control的创建,这个函数比较简单,就是把表micco_snd_controls里面已经定义好的controls模板创建controls,然后加入到card的controls列表中去;本身功能很清晰,但是对于我们平台来说,需要非常小心,因为这里决定了各个controls的序号,而这个序号是audio_controller访问硬件的索引,所以千万要小心尽量要维持目前的controls的序号,如果要额外添加新的controls一定要记得要放在micco_add_widgets后面来做,这样可以做到兼容,否则audio_controller的工作量就大了!

 

第四部分就是门的创建了,这个函数也是很清楚,就是把codec对应的门都加入到codec->dapm_widgets列表中去(这里的门的概念可以简单的理解为水管与水管之间的连接的地方,声音数据像水一样从水管里面流出来,源头可以是CPU了,也可以是modem,然后通过不同的门,流向不同的地方,比如speaker,比如蓝牙耳机等等),然后根据micco_audio_map把所有可能连在一起的门连接起来,这个表micco_audio_map的意思是{目的名字,控制点名字,源头名字},然后函数snd_soc_dapm_connect_input会根据这些名字去查表codec->dapm_widgets(先前已经把所有的门都加入了)找到它们再根据不同的类型做不同的连接,比如是mux之间的连接,mux和pga之间的连接等等,注意这里的连接其实只不过是说找到连接的可能性,它对于不同的门,找到其可能的source和sink,加入到对应的列表中去,具体细节如下:

首先,扫描整个codec所拥有的所有的门,如果它的名字和传入的sink的名字相同,则认为它就是这个路径的sink,如果它的名字和传入的source名字相同,则认为它是这个路径的source,如果源头或者sink没有找到都返回错误;然后分配一个struct snd_soc_dapm_path,这个数据结构的主要成分包括名字,source门,sink门,这条路径的control,这个源头和sink是否已经连接,是否已经走过(用在后面),这个数据结构会被挂在三个链表里面,一个是source的就是这个门会在很多的路径中,把它在这个路径中做source的path都连在一起,一个是sink的就是把这个门在所有这些由它做sink的path都连接在一起,一个是把所有的路径都需要连接在一起的这个是通过codec的dapm_paths来访问的;

list_add(&path->list,&codec->dapm_paths);

list_add(&path->list_sink,&wsink->sources);

list_add(&path->list_source,&wsource->sinks);

需要注意的时候,这里把路径的list_sink加入到了wsink门的sources列表里面,而把路径的list_source加入到wsource的sinks列表里面,所以当访问的时候从wsink门的sources出发就可以找到连接这个门作为sink的所有的路径,而从wsource的sinks列表出发就可以找到所有以这个门作为source的路径;

第三步就是为这个数据结构赋值:source,sink,初始化三个链表;第四步:如果control为空则把这个路径加入到相应的三个链表中去,并且路径设为已经连接,并返回;第五步:否则,根据sink的类型,如果是adc,dac,input,output,micbias,vmid,pre,post,则把路径加入到三个链表,设置已经连接的标志;如果是snd_soc_dapm_mux则调用dapm_connect_mux来处理;如果是mixer和switch则调用dapm_connect_mixer来处理,如果是hp,mic,line,spk,则把path加入到三个链表中去,但是设置成为连接的状态。

大约就是这样的了。

也许您要问,为什么要这么做呢?

这个,我也有想过,甚至我认为在门比较少的时候,确实没什么必要,但是这么做的好处在于当要播放音乐的时候,它可以实现自动的寻找路径并且自动poweron那些门,不需要上层做任何的控制,因为它真的到达目的地的所有的路径,这样它可以自己选择走哪条路;如果不这么连接起来的话,就需要提供给上层连接的接口,完全由上层来决定该连接哪些门,也必须由上层来负责poweron和off这些门;

第五部分就是注册了,这里就是向/dev注册设备节点,因为这些设备节点会由alsa层来访问的,这些设备节点和驱动的连接我前面已经说了,主要是提供了对上的alsa接口,给alsa层调用。  

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

在本节中,我们将探究集成模式的数组,每个模式都是为了提供无缝集成解决方案而定制的。这些模式作为结构化的框架,促进了不同系统之间的联系和数据交换。它们大致分为三类:

关键字: 数据整合 数据结构

学习C语言是程序员的入门教育,但是在学习过程中,常常会遇到一些常见的误区。这些误区可能会让学习者的学习经验不佳,影响到他们掌握该语言的能力。

关键字: C语言 数据结构

常用的数据结构可根据数据访问的特点分为线性结构和非线性结构。线性结构包括常见的链表、栈、队列等,非线性结构包括树、图等。

关键字: 数据结构 非线性结构

堆(heap)和栈(stack)是在计算机中常用的两种数据结构。它们具有不同的特点和用途,对于程序员来说,了解堆和栈的区别是非常重要的。

关键字: 内存 数据结构

南京2022年10月27日 /美通社/ -- 10月18日,由南瑞集团主导编制的IEC国际标准《电动汽车充电漫游服务信息交互 第2部分:用例》(IEC 63119-2:2022)正式发布。该标准的发布是南瑞集团在国际电动...

关键字: 电动汽车充电 充电站 数据结构 电动汽车电池

大家都听说过红黑树,也都知道红黑树很厉害,是计算机里面评价非常高的数据结构。但是每当想学习红黑树的时候,却总是找不到通俗易懂很好理解的学习资料。很多书上上来就是红黑树的定义,然后就是红黑树的实现,直接就把人给整晕了。光看...

关键字: 计算机 数据结构 红黑树

(全球TMT2022年7月11日讯)7月7日,由径硕科技(JINGdigital)主办、MEC睿达会承办的“万数有灵·2022中国数字营销创新增长峰会”在深圳举行。作为一家营销科技公司,径硕科技提供的是一流的营销软件产...

关键字: CE DIGITAL 数字化 数据结构

Redis为什么那么快?除了它是内存数据库,使得所有的操作都在内存上进行之外,还有一个重要因素,它实现的数据结构,使得我们对数据进行增删查改操作时,Redis能高效的处理。因此,这次我们就来好好聊一下Redis数据结构,...

关键字: 数据结构 REDIS 字符串 节点

哈喽,大家好,我是瓜哥,致力于分享互联网各领域干货。前几天,有人问瓜哥,学习编程语言有什么好的建议没?今天简单和大家分享几点学习编程的建议,希望可以帮助到大家。1.只要开始,就不要怕晚瓜哥经常看到这些问题,大四学编程还来...

关键字: 编程 代码 基础知识 数据结构

模块化是指解决一个复杂问题时自顶向下逐层把系统划分成若干模块的过程,有多种属性,分别反映其内部特性。

关键字: 模块化 软件模块 数据结构
关闭