也正是因为Linux内核的唯一性,各个不同发布版本拥有相同的框架。Linux内核是在整个Linux系统的最底层,它负责管理硬件,运行用户程序,并保持系统整体的安全性和完整性。可以说是Linux系统的根和灵魂。图3-1中看到Linux内核还有另一层面的含义。这对于Linux内核同样适用。所以对Linux内核的剖析、学习和研究也不能独立于系统进行,而是要综合考虑应用、内核和硬件等各方面的信息和内容。这样才能更全面、深刻地理解Linux内核。......
2023-11-22
目前Linux内核的音频框架采用ALSA(Advance Linux Sound Architecture)架构,该架构的设计充分考虑了以上各种需求。ALSA整体框架如图6-13所示。
从图6-13可见ALSA不仅包括驱动部分,还包括应用层的库。应用层的库主要是对驱动提供的服务进行封装,可以避免应用程序直接进行一些繁杂的ioctl调用,通过库的封装可以使得调用流程更易于理解,从而整体更易于应用程序的开发。
1ᤫ整体设备管理
这里主要对于ALSA架构的驱动部分进行解析。首先从整个框架上看,ALSA可以分为control、pcm、midi、sequencer等几个部分,对这些部分通常是通过多个设备文件来实现应用与内核交互的。框架层是如何对设备文件进行划分的呢?这一点可以通过子设备号的划分来了解。ALSA提供子设备号的静态分配和动态分配两种方式。通过静态分配更容易了解各个设备文件的特点。下面来看看相关代码:
图6-13 ALSA整体框架
从代码中可以了解不同的设备类型以及设备特点。从分类的角度,音频设备中可以包括的类型有SEQUENCER、TIMER、CONTROL、HWDEP、RAWMIDI、PCM_PLAYBACK以及PCM_CAPTURE。从代码可见,除了SEQUENCER和TIMER之外,都是和snd_card相关联的。这是由于SEQUENCER和TIMER要用于系统级的音频设备,SEQUENCER允许使用系统中所有的MIDI设备,而TIMER是允许获得系统中所有音频设备中的timer,自然是系统级的设备,就不需要与card进行关联。而其他类型的设备都是与snd_card有关联的,其中CONTROL每个snd_card只能有一个,其他的设备则是允许有多个。这种数量的关系一定要明确。
对众多的音频设备来说,CONTROL、PCM_PLAYBACK和PCM_CAPTURE是一定要存在的,其他的设备根据具体情况可有可无。所以后续主要以CONTROL、PCM_PLAYBACK和PCM_CAPTURE进行分析。
从以上部分可见,ALSA对设备管理是有层次的,组织上snd_card是设备的容器,同时也是设备的组织者与管理者,逻辑上snd_card是与硬件声卡关联。声卡硬件本身包含不同功能的硬件模块,这些不同的模块逻辑上也是设备,所以软件的这种组织与硬件上的逻辑组织形式是一致的。对驱动来说,需要实现的是整个声卡的驱动,这样在组织上要以card为核心,驱动中功能性的操作则要能够深入到具体对应的子模块。
要了解ALSA如何组织这些不同层次的设备,先来看看snd_card的内容:
从snd_card的整体分析可见,其主要负责整体的功能以及设备的组织。对于整体功能主要是控制功能以及整体的电源管理方面;在设备组织方面,是通过devices链表来对所有的子设备进行管理,子设备的类型就是之前介绍的各种类型。为了方便管理,ALSA将各种类型的子设备统一抽象出snd_device,用于card对子设备进行管理,细节如下:
可见主要进行不同类型子设备实例化的部分就是type、device_data以及ops。其中snd_device_ops的定义如下:
主要是操作接口,这样可以将操作与属性完全隔离,方便后续扩展。
系统还提供统一实例化各种类型设备的接口snd_device_new,内容如下:
每种类型的子设备都是通过snd_device_new接口来创建设备并与card进行关联的。以control子设备为例,control子设备是通过snd_ctl_create创建的。
可见control子设备是和card直接关联的,也说明了具体的控制由card统一来执行。最终card以及子设备的关联关系如图6-14所示。
图6-14 snd_card及子设备关联关系
以上主要是设备组织的部分,与设备模型的注册以及设备文件的产生并没有关联。思考一下,这些工作应该由子设备自己来完成,并进行文件操作的设定,这个操作就是由snd_device_ops中的dev_register操作完成的。相应的ALSA为每种类型的子设备提供了相关的接口snd_register_device_for_dev。内容分析如下:
所有的子类型设备都会在其dev_register中对snd_register_device_for_dev进行调用,从而完成相关的注册。还是以control子类型为例:
可见其主要工作就是根据card号为设备文件指定名字,然后完成相关的注册工作。
接下来看看具体的card及子设备的初始化及注册流程。首先要创建card实体,相应的接口是snd_card_create,详细分析如下:
通过分析可见,这里除了对card的空间及内容进行初始化外,最主要的就是对control子设备初始化并加入到card中进行管理。
分配了card之后,就要通过snd_device_new初始化并加入必要的子设备,由于control已经加入,必需的子设备就是pcm(会根据需要创建playback和capture两种文件),通过snd_pcm_new初始化并加入到card中。最后通过snd_card_register来完成所有设备的注册工作。snd_card_register细节如下:
其中除了注册系统以及创建sysfs文件外,最主要的就是通过snd_device_register_all进行子设备的注册。snd_device_register_all的操作很简单,就是遍历card的devices链表,并调用子设备的dev_register接口(最终调用snd_register_device_for_dev完成注册操作),将子设备的状态标记为SNDRV_DEV_REGISTERED,从而完成整个的注册流程。至此应用层就可以使用相关的设备文件对设备进行操作了。
2ᤫcontrol子设备
control子设备主要负责card中所包含的各种功能的设置,这些设置并不是固定于子设备上的,而是属于功能型的,所以通过card直接进行管理,将一个控制项加入card的接口snd_ctl_add,具体细节如下:
从以上代码可见,音频控制的主要结构就是snd_kcontrol,详细内容如下:
从分析中可知,每一个控制接口可以将多个相同功能统一进行管理,而其中的元素就使用snd_ctl_elem_id表示,count表示整体的管理数目,针对每个控制实体都可能会对应不同用户任务来进行操作以及访问控制,这些都属于针对应用层变化的信息,通过统一的snd_kcontrol_volatile来进行管理。对应控制的内容、需要进行设备特殊的操作,这些都是通过info、get、put以及private_value和private_data来共同实现的。所以这里包括了针对上层应用层的管理部分,以及针对下层驱动的操作属性。
应用层更关注的是控制元素的信息以及相关值的属性,基本都是通过snd_ctl_elem_xxx来获得的,这些结构定义都在asound.h中。
所有的控制都是底层驱动提供的,所以控制信息应该由底层驱动进行定义,而由于snd_kcontrol中包含了针对上层的管理信息,相应的并不适用于底层驱动直接定义控制信息。针对这种情况,ALSA框架提供了snd_kcontrol_new来进行定义,详细内容如下:
从其结构体内容可见,其中主要是控制与设备信息的设置,符合底层的数据特点。这样就需要接口能将底层使用的实体转换为上层使用的实体,这个接口就是snd_ctl_new1,其定义如下:
实际应用中通常将snd_ctl_add与snd_ctl_new1结合,就可以完成每个控制元素的注册。驱动部分的控制元素只要完成注册就可以了,而在上层中针对应用层的处理则由ALSA提供的control子设备的操作接口来完成,主要将每个kcontrol的元素看做element,而通过snd_ctl_elem_xxx的接口进行遍历、读、写等操作(这些操作都是ALSA应用层的ioctl命令)。这样就完成了整个的控制流程,可见在注册之后控制流的调用层次是很少的,主要因为控制操作本来就是很直接的工作,设计上也体现了这种直接性的特点。
利用kcontrol,可以完成对音频系统中的mixer、mux、音量控制、音效控制以及各种开关量的控制,这些操作都是通过对各种不同类型kcontrol(不同类型的控制元素)的操作来完成的,通过这些控制可以使得音频设备按照应用的设计进行工作,完成各种音频需求。
以上是control子设备中包含的各模块功能的kcontrl控制部分,还有一部分就是对card其他类型子设备的查询等控制也是要通过control子设备来统一完成,这样可以有统一的接口来遍历整个音频设备card的情况,相应的可以从snd_ctl_ioctl来了解它是如何工作的。
从代码中可以了解kcontrol的操作方法,另外还提供了获得其他子设备情况的接口,这需要其他类型子设备如pcm向系统注册操作接口,通过snd_ctl_register_ioctl加入链表snd_control_ioctls进行注册。这样的设计可以保证应用通过control子设备来查询音频设备的整体状况并进行相关的操作,这样可以有统一的操作界面,适合应用的需求。
3ᤫpcm子设备
前面介绍了音频设备的控制流程,接下来看看音频设备的pcm数据流程。对pcm数据流,ALSA设计了统一的管理方式,其中为下层驱动提供的添加pcm数据通道的接口函数snd_pcm_new,详细内容以及分析如下:(www.chuimin.cn)
以上是驱动加入pcm流的过程,而具体的注册是在card register中调用子设备的snd_pcm_dev_register来完成的。具体内容如下:
从添加和注册的细节可见,ALSA框架中对pcm进行了统一的管理,由snd_pcm来提供,而其中会根据playback和capture来区分为不同类型的snd_pcm_str,在不同类型snd_pcm_str中则可以有多个substream由snd_pcm_substream进行管理。这些管理实体的关系如图6-15所示。
所有这些管理实体snd_pcm和snd_pcm_str更偏向于组织,而snd_pcm_substream是直接对应到实际的音频流,可以说是音频设备数据流的核心。下面看一下snd_pcm_substream的详细内容。
图6-15 pcm中管理实体关系
重要的管理实体是snd_pcm_runtime,其中包含了运行时的各种状态和属性,与音频流息息相关。具体内容如下:
可见其很多属性是从其他管理实体中复制过来的,比如dma空间的属性,以及其中一部分硬件的属性等,这是为了操作的方便;runtime的状态更新涉及上层应用和底层驱动主要是运行时的状态、音频流状态的更新,系统提供了统一的接口进行相应的更新操作,如snd_pcm_update_hw_ptr和snd_pcm_period_elapsed(驱动进行更新的接口)。下面来看看sn_pcm_period_elapsed的具体内容:
这里有一个重要的概念就是周期(period),这是因为一次DMA操作唤醒一次中断太浪费系统资源,所以通常定义一个数据块大小表示一个周期可以传送数据的大小,这样的设计考虑整体的系统性能。
对驱动来说,重要接口是设置snd_pcm_runtime中的snd_pcm_ops。snd_pcm_ops的具体内容如下:
驱动主要就是实现这些接口,并通过snd_pcm_set_ops进行设置。
而应用层的各种操作会使得PCM流在不同的状态中转换,具体的状态如下:
ALSA的框架会针对不同的操作调用合适的驱动snd_pcm_ops接口函数,从而完成合适的操作。
对整体的pcm子设备的理解,同样要分为控制部分和数据部分,控制部分就是各种命令使得pcm流在不同状态间进行转换。基本的控制流程是用户通过open打开子码流并获得该子码流的能力参数,从而确定出一套可用的参数;调用hw_params设置这些参数;调用prepare进行最后的准备工作,比如清除fifo;调用trigger启动或停止工作;通过pointer了解工作进展;工作停止后,调用hw_free释放资源,使用close关闭子码流。而数据部分是通过runtime进行状态的维护和同步,由应用层的read、write或者mmap方式和驱动(DMA的方式)共同操作ringbuffer的空间,从而完成整个的数据操作。
对数据部分,应用层read和write音频流数据需要考虑数据的组织形式,通过不同的ioctl命令来完成,当多个channel的音频数据放在同一块连续存储空间时是属于interleave方式存放,需要进行readi和writei操作;否则就是不同的channel数据放入分离的存储空间中,需要通过readn和writen进行操作。应用层需要根据数据的具体属性进行相关的操作。这样框架层就完成了数据的操作,其余的数据操作属于驱动的实现部分。
4ᤫASoC(ALSA SoC)子框架
以上的框架主要适合于PC上的音频设备,这些音频设备的特定是数字部分和模拟部分集成在一起,所以通过统一的控制就可以完成。而随着嵌入式的发展,SoC处理器大量地出现,这些SoC的特点是在片上包含音频数据流的传输通道,而具体的音频处理以及模拟部分是在SoC外部的音频codec进行处理的,这就使通道和处理是分离的,这样设计的特点是使得数字部分和模拟部分隔离,降低芯片开发的难度,也可以集成不同的codec来完成不同的功能。在软件上这就带来了问题,之前的架构对SoC来说并不适用。如果使用原有的音频框架,要完成SoC音频数据接口的驱动还需要包括音频codec的操作,这样才是完整的音频设备,这样就不能在不同的SoC之间重用音频codec的驱动,从而提高了系统的复杂程度。为了解决这些问题,ALSA提供了ASoC子架构,如图6-16所示。
从图6-16可见,ALSASoC将不同的设备主要分为machine、codec和platform几个部分。codec主要负责audio codec部分;platform主要负责音频流的传输控制;machine则是描述相关的连接,使得ALSASoC框架可以将这些分离的组件进行正确的连接。另外audio co-dec和SoC都包含数据通道的控制,在ALSASoC框架中称为DAI(digital audio interfaces)这部分的属性和控制单独进行管理,在audio codec和platform双方都有相应的管理实体和操作。这样整体上系统就可以分离成各种不同的管理实体,各种实体可以分别进行驱动的开发,最终通过machine(相当于板级的连接管理)将这些实体关联成系统,从而使得整个系统正常工作。
图6-16 alsa soc框架
ALSA SoC框架针对以上的实体分别提供了不同的管理结构,具体如下:
●snd_soc_dai:用于描述SoC处理器端或codec端接口的硬件能力。在运行过程中,ASoC框架会综合两端的硬件能力,生成两端都可接收硬件能力参数(snd_pcm_hard-ware)。
●snd_soc_platform:用于管理SoC处理器端数据流的操作。
●snd_soc_dai_link:用于描述SoC处理器端和codec端两端的实际硬件连接。即SoC处理器端哪个硬件通道与哪个codec进行连接。
●snd_soc_card:用于描述所有的硬件连接,最终会建立一个card,而每个连接会建立一个pcm通道。
●snd_soc_codec:用于描述audio codec属性,以及相关操作。
而以上所有这些硬件连接的各个模块都通过snd_soc_pcm_runtime进行关联,其内容如下:
从结构的实际属性可见,其中将ALSA SoC下层具体的功能模块与上层ALSA的pcm流进行了关联,这样就建立了上层与下层的连接,保证操作的畅通。
ALSA SoC框架则提供将底层各个模块关联的操作,通过soc_bind_dai_link来进行,详细分析如下:
从代码中可见,只有rtd->codec、rtd->codec_dai、rtd->platform和rtd->cpu_dai都绑定才是真正的绑定成功,从而进行后续操作。对于每种组件ALSASoC框架都提供注册接口,接口如下:
各个组件会在内部通过以上接口向ALSA SoC注册相应的组件。为了适应各种不同的组件注册情况,系统提供了整体的card级别的初始化操作,并会在任何组件注册时被调用,保证系统能正确地发现不同的组件并关联。这个逻辑最终由snd_soc_instantiate_card来完成,详细内容如下:
从代码分析可见,主要是对ALSA层的设备管理部分的封装,通过内部的各个组件的操作来完成整体的注册过程。
对control子设备,主要是注册control元素;而对pcm子设备,则通过snd_pcm_set_ops将pcm管理实体中snd_pcm_ops重新定义为soc_pcm_ops,具体内容如下:
这样上层的操作就可以转入ALSA SoC层执行。以soc_pcm_hw_params为例来看看具体的操作:
可见其主要的功能就是按照一定的顺序对各个组件进行相关的操作,这样整个pcm的操作就从上到下打通了。
最后对SoC处理器来说,大部分的设备都是以platform device出现的,对ALSA SoC的架构也要考虑到这一点,其整体上定义为platform driver,与整个SoC系统关联,细节如下:
这样ALSA SoC中machine层面只要注册对应的platform device就可以通过bus完成整体的操作,其中soc_probe会试图进行soc card的实例化,而每个组件也都可以通过plat-form driver的方式与实际的SoC处理器中设备关联,这样在probe中注册对应的ALSA SoC组件,就可以在底层保证整个物理连接的各个设备最终关联组成上层针对应用的音频设备,这样的设计,既体现了设备层次,也体现了物理连接的实际情况,是一种灵活好用的框架。
这样音频设备框架的整体层次就介绍完了。
有关深入剖析Linux内核与设备驱动的文章
也正是因为Linux内核的唯一性,各个不同发布版本拥有相同的框架。Linux内核是在整个Linux系统的最底层,它负责管理硬件,运行用户程序,并保持系统整体的安全性和完整性。可以说是Linux系统的根和灵魂。图3-1中看到Linux内核还有另一层面的含义。这对于Linux内核同样适用。所以对Linux内核的剖析、学习和研究也不能独立于系统进行,而是要综合考虑应用、内核和硬件等各方面的信息和内容。这样才能更全面、深刻地理解Linux内核。......
2023-11-22
VFS的实现是以“一切皆是文件”为需求出发点的。要理解VFS的框架首先看一下VFS和系统的静态关系框图,如图5-1所示。从图5-1可见,VFS是用户层的直接接口,是面向用户的服务。图5-1VFS和系统静态关系图在图5-2中每个节点被使用时都会在VFS层中创建dentry,这样可以快速通过文件名进行查找和定位,Linux内核中对dentry的管理组织形式如图5-3所示。以上是系统运行时VFS对文件名的组织管理,这是VFS管理的一个方面。......
2023-11-22
SPI总线框架对这两类设备进行管理,具体的框架如图7-6所示。通常的SPI驱动都是在内核实现并在内核态执行的,所以就不对spidev进行详细的分析。SPI总线框架整体的功能就是这样,接下来看看具体各部分的实现。在SPI总线框架中相应的实体是spi_board_info,内容如下:其中的主要信息是与总线信号相关的属性。......
2023-11-22
为了满足以上的需求,视频驱动框架分别在不同的层次进行了设计,以实现不同层面的巨大挑战。从中可以一定程度上了解视频驱动的框架。视频驱动框架也考虑到这两种需求,并提供了解决方法。2设备层次管理前面部分主要涉及视频驱动框架中对应用层接口实体的管理,框架还需要提供实际设备的管理层。......
2023-11-22
考虑到电源管理的需求涉及处理器和各种设备,一方面是处理器尽可能减少功耗,另一方面是设备尽可能减少功耗。图5-30Linux电源管理各个功能从图5-30可见Linux内核的电源管理功能有与处理器相关的CPUIdle和CPUFreq,也有与设备相关的runtime pm,另外还有与整个系统待机时SLM相关的低功耗电源管理功能。下面分别对这些功能框架进行介绍。具体的驱动同样会在SoC电源管理部分进行讲解。对具体设备的电源管理实现,将在设备驱动中进行详细分析。......
2023-11-22
对于音频驱动的电源管理部分,ALSASoC框架层提供了基本的电源管理框架,之前在其与底层platform关联的platform driver中可见有电源管理的操作接口soc_pm_ops。DAPM是dynamic audio power management的缩写。DAPM的目的是降低音频设备的功耗。在实现过程中通过将与音频路径相关的kcontrol控制元素包含进DAPM管理实体,就可以监控系统设置以及应用设置导致的状态变化,从而进行正确的操作。这样就完整实现了整个音频的电源管理功能。......
2023-11-22
整体的frame buffer框架如图6-5所示。了解frame buffer的框架还要先从为用户提供的接口开始。图6-7可变参数中硬件信息的含义固定信息是在frame buffer的操作过程中并不发生变化的。从整体分析,frame buffer的框架很直接,主要就是直接管理实际的设备,而相关的操作与应用层直接相关。......
2023-11-22
相关推荐