现在的位置: 首页 > 综合 > 正文

ALSA 驱动框架和驱动开发 (二)

2013年01月01日 ⁄ 综合 ⁄ 共 3959字 ⁄ 字号 评论关闭

本文主要接着讲,ALSA驱动框架中,内核调用到驱动的全过程

十.从内核调用到驱动的全过程

       1Open /dev/dsp

              Open操作,通过前面所说的结构图,我们知道,当内核调用open函数时,  首先调用的是soundcore_open,通过__look_for_unit找到chain[3],dsp这一sound_unit指针,然后重新赋值文件的操作指针为dsp设备文件的操作指针。

              这一方法也对其他的文件适用,例如,对于/dev/mixer,调用open操作时,一样调用soundcore_open,然后接下来的操作就是重新定义文件的操作指针为mixer文件的操作指针,并且调用mixer_ops中的open 函数,

              以后对文件的操作就是调用更新后的ops了。

这里调用的是pcm_oss.c中的open,具体流程图如下,

 

具体的,

Cpu_dai.ops->startup at91_ssc_startup 该函数主要是设置传输方向的mask(串口只允许单向传输)

Platform->pcm_ops->open at91_pcm_open 该函数主要设置at91_pcm_hardware,包括pcm_infoperiod_bytes_min,period_bytes_max,

periods_min,periods_max,buffer_bytes_max等,

Codec_dai->ops.startup NULL

Machine->ops->startup epayment_snd_startup,该函数数主要设置cpu_daiclk为系统时钟AT91_SYSCLK_MCK,设置codec_daiclkPCMXXX_SYSCLK,,然后使能codec_daiclk

2Open /dev/mixer

Open /dev/mixer的大致过程与上述open /dev/dsp类似,只是后面的操作指针为mixer ops

具体的就是调用mixer_oss.c中的snd_mixer_oss_open,该函数大致就是完成了struct snd_mixer_oss_file *fmixerfmixer的填充,然后将其赋值为文件的私有数据。

 

3.配置参数的过程(dsp ioctl)

配置参数的过程具体是通过ioctl来实现的

例如在库文件中,对参数的设置都是通过ioctl来实现的,一般包括,syncchannelfmt,rate等参数的设置。

举设置采样速率为例,其流程图如下,

 

 

 

同样的,在函数soc_pcm_hw_params中,仍然遵循CPUàplatformàcodec

的大致顺序。

       具体的,

a)                 machine->ops->hw_params(machinedai_link结构指针),为函数epayment_snd_hw_params,主要完成 设置cpu_daicodec_daifmt,设置cpu_daicmr_divperiod(通过采样计算所得)

b)                codec_dai->ops.hw_params pcmXXX_hw_params null

c)                 cpu_dai->ops.hw_params at91_ssc_hw_params,主要完成初始化一个struct at91_pcm_dma_params *dma_params,并对dma_params进行填充,包括pdc registerssc_basepdc_xfer_size(以字节计)和一些SSC 寄存器的设置,如rcmr, rfmr, tcmr, tfmr.

最后,reset ssc 它的PDC寄存器,请求中断,中断服务例程为at91_ssc_interrupt,在传输过程中,当接收到数据和发送完所有数据时均为产生中断,从而调用中断服务例程。

d)                platform->pcm_ops->hw_params at91_pcm_hw_params,主要完成at91_runtime_data的数据填充,这里还填充了一个dma_intr_handlerat91_pcm_dma_irq(实际上非硬件产生的中断)

此中断服务例程与上面提到的at91_ssc_interrupt中断的关系是,

at91_ssc_interrupt中断服务例程中先判断中断类型,然后再调用at91_pcm_dma_irq这一中断服务例程完成中断服务。

at91_pcm_dma_irq中断服务程序主要完成的工作是,

若中断类型为传输数据结束后的中断,则先重新设置prtd->period_ptr指针(如果刚好是dma_buffer_end,则设置为dma_buffer的头指针,否则指针下移period_size个字节),设置pdc->xnprpdc->xncr

若中断类型为dma_buffer已满,则先关闭PDC,然后重新定位prtd->period_ptr指针,(设置为dma_buffer的头指针),设置pdc->xpr,pdc->xcr,然后重新开启PDC

 

       4Mixer_ioctl

同上,mixer的相关操作是通过ioctl来实现的,它本身甚至不需要readWrite

由于PCMXXX不支持音量调节,略。

 

5Pcm prepare

在设置同步时钟和Write/read之前都会调用snd_pcm_oss_make_ready进行prepare,

同样,是通过ioctl进行实现的。

流程图类似,

-----àsnd_pcm_prepare 先等待上电,后

-----à snd_pcm_action_nonatomic(&snd_pcm_action_prepare,

                                          substream, f_flags);

                static struct action_ops snd_pcm_action_prepare = {

                .pre_action = snd_pcm_pre_prepare,

                .do_action = snd_pcm_do_prepare,

                .post_action = snd_pcm_post_prepare

};      

-----àpre_action 检查runtime->status->state

-----àdo_action     调用substream->ops->prepare(substream)

          即为soc_pcm_prepare

-----àpost_action 设置runtime->status->state

 

              同样soc_pcm_prepare也遵循soc_core调用的基本流程

              ------àmachine->ops->prepare

null

              ------àplatform->pcm_ops->prepare

ssc prepare,ssc interrrupt disable,pdc disable

              ------àcodec_dai->ops.prepare

null

              ------àcpu_dai->ops.prepare

ssc enable

              ------àsnd_soc_dapm_stream_event

                            给电源管理发送事件SND_SOC_DAPM_STREAM_START

                            具体电源管理的见电源部分

 

6Pcm Write 方法(read略)

        Write方法的调用过程也遵循ALSA的体系结构,

        具体的流程图如下,

 

 

snd_pcm_lib_write1中,先调用snd_pcm_lib_write_transfer函数,完成数据从用户空间到内核空间的拷贝过程,这个拷贝过程是以帧的方式进行数据复制的。

static struct action_ops snd_pcm_action_start = {

       .pre_action = snd_pcm_pre_start,

       .do_action = snd_pcm_do_start,

       .undo_action = snd_pcm_undo_start,

       .post_action = snd_pcm_post_start

};

-----àpre_action 先检测runtime->status->statecheck是否还有数据保留在playback buffer

              -----àdo_action  调用substream->ops->trigger soc_pcm_trigger

-----àpost_action 检测是否设置休眠(且最小休眠大于0),如果设置了则进入休眠事件,并且通知timer.

      

soc_pcm_trigger调用如下,

----àCodec_dai->ops.trigger null

----àPlatform->pcm_ops->trigger at91_pcm_trigger

       主要就是设置PDC参数,最后使能PDC进行数据传输

----

抱歉!评论已关闭.