首页文章android aosp 音频驱动分析 安卓手机音频驱动android手机驱动「android aosp 音频驱动分析 安卓手机音频驱动」

android aosp 音频驱动分析 安卓手机音频驱动android手机驱动「android aosp 音频驱动分析 安卓手机音频驱动」

时间2025-01-17 02:27:31发布yu分类文章浏览127
导读:1 Linux ALSA声卡驱动 众所周知,android是基于linux的。讲android的audio的系统,就不得不从linux的声卡驱动说起。为了更好的支持嵌入式CPU,linux在标准的ALSA驱动上建立了ASoC(ALSA System on Chip)。下面我们...


1  Linux ALSA声卡驱动


   众所周知,android是基于linux的。讲android的audio的系统,就不得不从linux的声卡驱动说起。为了更好的支持嵌入式CPU,linux在标准的ALSA驱动上建立了ASoC(ALSA System on Chip)。下面我们就从ASoC说起。
    ASoC的驱动代码位于soundsoc目录下。ASoC音频系统可以被划分为Machine、Platform、Codec三大部分。Codec驱动通常都在soundsoccodecs目录下。Machine、Platform的驱动通常在soundsoc于CPU相关的目录下,例如,飞思卡尔的Machine、Platform的驱动就在soundsocimx目录下,全志的就在soundsocsunxi目录下。
    .Codec驱动主要是针对音频CODEC的驱动,主要是进行AD、DA转换,对音频通路的控制,音量控制、EQ控制等等。
    .Platform驱动主要是针对CPU端的驱动,主要包括DMA的设置,数据音频接口的配置,时钟频率、数据格式等等。
    .Machine驱动主要是针对设备的,实现Codec和Platform耦合。


    1.1 Machine驱动
    不同的设备硬件形态是各式各样的,不同的CPU、不同的CODEC芯片。Machine驱动就是负责将不同的Platform驱动和Codec驱动关联起来,形成一个完整的音频驱动。没有Machine驱动,Platform驱动和Codec驱动是无法独立工作的。下面就来看看Machine驱动是如何实现的。
    首先来看看两个重要的结构:
 


   在这个结构里面我们重点需要关注下面几个成员
   codec_name :codec的名称,系统将根据这个名字匹配相应的CODEC驱动
   codec_dai_name :codec的数字音频接口(DAI)的名称,系统根据这个匹配codec_dai驱动。
   platform_name : Platform的名称,用来匹配Platform驱动的。
   cpu_dai_name :Platform的数字音频接口(DAI)的名称,系统根据这个匹配Platform_dai驱动。(codec_dai和Platform_dai将在codec和Platform驱动里面说明)。
   ops 回调函数指针的结构,这这里可以定义对设备的硬件设置的一些代码,比较常用的是ops.hw_params实现设备级硬件的一些配置。


    这个结构看起来非常复杂,实际上我们只要关注下面几个成员就好了
    name :为我们的声卡定义一个名字
    dai_link :前面介绍的snd_soc_dai_link结构的实例的地址。可以是一个snd_soc_dai_link的变量的地址,也可以是snd_soc_dai_link数组的首地址的指针。
    num_links : dai_link 的数量,例如我们定义了一个结构变量
    struct snd_soc_dai_link a;
    .dai_link = &a ;
    那么num_links就是1。
    如果定义了一个数值
    struct snd_soc_dai_link a[5]={......};
    .dai_link = a ;
    那么num_links就是5。


    下面看看怎么注册一个Machine驱动
    首先定义前面介绍的结构


    
 


如果需要进行设备的一些硬件设置,可以定义.ops = &sndpcm_ops,如下:


sndpcm_hw_params进行一些硬件的设置。
 


  
  Machine驱动是一个Platform Device,所以我们要先定义一个Platform Device,这里就是实现一个标准的Platform Device,就不详细说明了。
  在Platform Device的probe函数中注册 Machine驱动。有两种写法


  
   
    1.2 Platform驱动
    Platform驱动分为两个部分:snd_soc_platform_driver和snd_soc_dai_driver。其中,platform_driver负责管理音频数据,简单的说就是对音频DMA的设置。dai_driver则主要完成cpu一侧的dai的参数配置,也就是对cpu端音频控制器的寄存器的设置,例如时钟频率、采样率、数据格式等等的设置。
    1.2.1 snd_soc_platform_driver的注册
    先介绍两个数据结构
 


    这个结构通常我们需要关注下面的几个成员
    .pcm_new :函数指针,在驱动创建的时候由系统回调。
    .pcm_free:函数指针,在驱动销毁的时候由系统回调。
    .ops     :snd_pcm_ops结构的指针,定义了一系列回调函数。


   


    .open :打开设备,准备开始播放的时候调用,这个函数主要是调用snd_soc_set_runtime_hwparams设置支持的音频参数。snd_dmaengine_pcm_open打开DMA引擎。
    .close:关闭播放设备的时候回调。该函数负责关闭DMA引擎。释放相关的资源。
    .ioctl:应用层调用的ioctl会调用这个回调。
    .hw_params:在open后,应用设置播放参数的时候调用,根据设置的参数,设置DMA,例如数据宽度,传输块大小,DMA地址等。
    .hw_free :  关闭设备前被调用,释放缓冲。
    .trigger:  DAM开始时传输,结束传输,暂停传世,恢复传输的时候被回调。
    .pointer: 返回DMA缓冲的当前指针。
    .mmap :   建立内存映射。


    介绍完数据结构,下面介绍如何注册snd_soc_platform_driver。首先定义一个snd_soc_platform_driver 
 


    snd_soc_platform_driver也是一个platform driver ,所以首先要定义一个platform driver ,这里要注意的是我们定义的这个platform driver的name一定要和前面snd_soc_dai_link 结构中定义的platform_name相同,这样我们定义的snd_soc_platform_driver才会被关联。
    然后再这个驱动的probe函数中,调用snd_soc_register_platform(&pdev->dev, &soc_platform );就完成了snd_soc_platform_driver的注册。


    1.2.1 snd_soc_dai_driver驱动的注册
    
   


    主要的成员如下:
    .probe   :回调函数,分别在声卡加载时被调用; 
    .remove  :回调函数,分别在声卡卸载时被调用;
    .suspend .resume:  分别在休眠唤醒的时候被调用
    .ops     :指向snd_soc_dai_ops结构,用于配置和控制该dai;
    .playback:  snd_soc_pcm_stream结构,用于说明播放时支持的声道数,码率,数据格式等能力;
    .capture : snd_soc_pcm_stream结构,用于说明录音时支持的声道数,码率,数据格式等能力;


 


    同样的snd_soc_dai_driver也是一个platform driver ,所以首先要定义一个platform driver ,这里要注意的是我们定义的这个platform driver的name一定要和前面snd_soc_dai_link 结构中定义的cpu_dai_name相同,这样我们定义的snd_soc_dai_driver才会被关联。
    然后再这个驱动的probe函数中,调用snd_soc_register_dai(&pdev->dev, &pcm_dai );就完成了snd_soc_dai_driver的注册。


    1.3 Codec驱动
    还是先介绍下相关的数据结构。
 




     另外一个重要的结构
   


    这个结构是codec驱动工作的核心,通过这个结构控制许多开关(switch)和调节器(slider)等等,从而读写Codec相关寄存器,实现几乎codec支持的所有功能。下面介绍下这个结构的成员。
    .iface : 定义了control的类型,形式为SNDRV_CTL_ELEM_IFACE_XXX,对于mixer是SNDRV_CTL_ELEM_IFACE_MIXER,对于不属于mixer的全局控制,使用CARD;如果关联到某类设备,则是PCM、RAWMIDI、TIMER或SEQUENCER。
    .name :名称标识,这个字段非常重要,因为control的作用由名称来区分(如果名称相同需要通过index来区分,且后加的index的值要大于之前的index)。上层应用就是根据name名称标识来找到底层相应的control(上层应用也可以通过id来匹配,id对应的就是每一个control的下标)。name定义的标准是“SOURCE DIRECTION FUNCTION”即“源 方向 功能”,SOURCE定义了control的源,如“Master”、“PCM”等;DIRECTION 则为“Playback”、“Capture”等,如果DIRECTION忽略,意味着Playback和capture双向;FUNCTION则可以是“Switch”、“Volume”和“Route”等。
    .access :访问控制权限。SNDRV_CTL_ELEM_ACCESS_READ意味着只读,这时put()函数不必实 现;SNDRV_CTL_ELEM_ACCESS_WRITE意味着只写,这时get()函数不必实现。若control值频繁变化,则需定义 VOLATILE标志。当control处于非激活状态时,应设置INACTIVE标志。
    .private_value:包含1个长整型值,可以通过它给info()、get()和put()函数传递参数。在通常的使用中是一个指针。
    .info : 函数指针,获取相应的控制项的参数,例如取值范围
    .get :函数指正,获取相关控制项的值
    .put :函数指正,设置相关的寄存器。


    在include/sound/soc.h文件中定义了一些宏,来实现snd_kcontrol_new 的定义,有兴趣的话可以自己看看。


    下面来介绍如何注册一个codec驱动
    首先需要实现相关的数据结构
 




    同样的codec驱动也是一个platform driver ,所以首先要定义一个platform driver ,这里要注意的是我们定义的这个platform driver的name一定要和前面snd_soc_dai_link 结构中定义的codec_name相同,这样我们定义的codec驱动才会被关联。
    然后再这个驱动的probe函数中,调用snd_soc_register_codec(&pdev->dev, &soc_codec_dev_sndpcm , &sndcodec_dai , 1);就完成了snd_soc_dai_driver的注册。
    snd_soc_register_codec的最后一个参数是snd_soc_dai_driver 的个数,我们只定义了一个所以就是1,如果是一个snd_soc_dai_driver 的数组,那么这个参数就是数组元素的个数。
    讲到这里,我们的驱动都已经注册好了,ALSA已经可以正常工作了,但是如果这个时候播放一段音乐,我们是听不到声音的。为什么呢?因为我们还没有添加control呢,所以实际上codec还没有工作呢!下面介绍下添加control的方法。
    第一个办法是,直接在snd_soc_codec_driver 的结构中添加两个字段
   


    这样调用snd_soc_register_codec 的时候control就添加了。
    第二个方法是在snd_soc_codec_driver 结构定义的probe函数中添加。
    probe函数会在调用snd_soc_register_codec后被系统回调,我们实现下面的代码就好了。
   

诸暨版权声明:本网信息来自于互联网,目的在于传递更多信息,并不代表本网赞同其观点。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,并请自行核实相关内容。本站不承担此类作品侵权行为的直接责任及连带责任。如若本网有任何内容侵犯您的权益,请及时联系我们,本站将会在24小时内处理完毕,E-mail:xinmeigg88@163.com

展开全文READ MORE
驱动音频
急用钱手机快速借款「急用钱」 银行卡转账怎么转?手机银行怎么转账「银行卡转账怎么转?」