Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2763535
  • 博文数量: 505
  • 博客积分: 1552
  • 博客等级: 上尉
  • 技术积分: 2514
  • 用 户 组: 普通用户
  • 注册时间: 2007-09-23 18:24
文章分类

全部博文(505)

文章存档

2019年(12)

2018年(15)

2017年(1)

2016年(17)

2015年(14)

2014年(93)

2013年(233)

2012年(108)

2011年(1)

2009年(11)

分类: LINUX

2014-03-14 19:06:39


本文主要接着讲,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进行数据传输

----àCpu_dai->ops.trigger null

      

       7 close方法

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

        具体的流程图如下,



 

soc_pcm_hw_free中,调用情况如下,

              -----àcodec_dai->dai_ops.digital_mute

                     拉低对应的codec digital mute

-----àmachine->ops->hw_free

       释放所有dai_link相关的参量

-----àplatform->pcm_ops->hw_free

       释放DMA资源

-----àcodec_dai->ops.hw_free

       释放codec_dai相关的参量

-----àcpu_dai->ops.hw_free

       释放cpu_dai相关的参量

 

                     soc_codec_close中, 调用情况如下,

              -----àcpu_dai->ops.shutdown

at91_ssc_shutdown,设置dma_param各参量为空,禁止SSC的控制寄存器,关闭SSC时钟等.

-----àcodec_dai->ops.shutdown

       null

-----àmachine->ops->shutdown

       禁止PCLK1

-----àplatform->pcm_ops->close

at91_pcm_close,释放at91_runtime_data

-----àschedule_delayed_work(&socdev->delayed_work,

                     msecs_to_jiffies(pmdown_time));

       其中参数pmdown_time5000

       即延时5秒后完成工作(保证需要完成的工作都完成,此work为最后一步),在soc_probe中有提到注册的这个工作,为close_delayed_work

       close_delayed_work,将关闭电源的事件发送给dapm,关闭电源,如下       snd_soc_dapm_stream_event(codec, codec_dai->playback.stream_name,

                            SND_SOC_DAPM_STREAM_STOP)

 

 

阅读(1015) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~