linux ALSA音頻驅動框架

ALSA中的鏈表結構

       在ALSA中設計到很多的鏈表結構,理解這些鏈表能更好的理解ALSA

a)       card->devices

card->devices鏈表的建立方便了card相關設備的註冊過程和設備的管理。通過這個鏈表,在註冊設備的過程中,可以先將設備(包括設備編號,設備相應的操作指針等)添加進鏈表中,然後再遍歷鏈表,各自的設備調用本身的註冊函數將自身註冊,完成card相關所有設備的註冊過程。

b)      snd_pcm_devices

該鏈表結構則是爲了將已經存在了的PCM接口鏈接到該鏈表上,方便pcm的管理

c)       snd_pcm_notify_list

此鏈表是爲pcm註冊的通用方法,如果只註冊了一個snd_pcm_oss_notify

,則在遍歷snd_pcm_devices時,查找到的pcm device均使用該notify的.n_register 進行PCM的註冊。

d)      card->controls

cadr->controls鏈表和card->devices鏈表類似,只不顧一個負責管設備,一個負責管控制接口,基本操作類似,不過PCM中沒有相關寄存器,所以未應用.

e)       snd_control_ioctls

與control有關的鏈表,如果在control中snd_ctl_add則是將control添加到此鏈表中。

f)       timer->active_list_head

與timer有關的鏈表

      

九.驅動中各個結構體和各個模塊的關係

       a) soc_core所用到的各個結構體之間的關聯圖,可以說是體系中的CORE層。如下,

 

 



 

從上圖中看,soc_core中多數函數以soc_device指針爲函數參量的原因也很顯然。

而遵循soc_core的調用關係,即cpu----àplatform----àcodec,在上述結構總很好的體現。

還有一個重要的結構就是runtime,該結構代表的是DAI runtime的信息。

 

b)上面的結構圖是以snd_soc_device爲主線的,即以設備驅動的創建過程分析所得。而下面的這個結構圖,是以snd_pcm_substream爲主線,主要是分析在open,hw_params,Write,read等操作中由substream得到所需結構的過程。

 

十.從內核調用到驅動的全過程

       1.Open /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_info,period_bytes_min,period_bytes_max,

periods_min,periods_max,buffer_bytes_max等,

Codec_dai->ops.startup 爲NULL

Machine->ops->startup 爲epayment_snd_startup,該函數數主要設置cpu_dai的clk爲系統時鐘AT91_SYSCLK_MCK,設置codec_dai的clk爲PCMXXX_SYSCLK,,然後使能codec_dai的clk


2.Open /dev/mixer

Open /dev/mixer的大致過程與上述open /dev/dsp類似,只是後面的操作指針爲mixer ops

具體的就是調用mixer_oss.c中的snd_mixer_oss_open,該函數大致就是完成了struct snd_mixer_oss_file *fmixer,fmixer的填充,然後將其賦值爲文件的私有數據。

 

3.配置參數的過程(dsp ioctl)

配置參數的過程具體是通過ioctl來實現的

例如在庫文件中,對參數的設置都是通過ioctl來實現的,一般包括,sync,channel,fmt,rate等參數的設置。

舉設置採樣速率爲例,其流程圖如下,

 

 

 

 

 


同樣的,在函數soc_pcm_hw_params中,仍然遵循CPUàplatformàcodec

的大致順序。

       具體的,

a)                 machine->ops->hw_params,(該machine爲dai_link結構指針),爲函數epayment_snd_hw_params,主要完成 設置cpu_dai,codec_dai的fmt,設置cpu_dai的cmr_div和period(通過採樣計算所得)

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 register,ssc_base,pdc_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_handler爲at91_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->xnpr和pdc->xncr。

若中斷類型爲dma_buffer已滿,則先關閉PDC,然後重新定位prtd->period_ptr指針,(設置爲dma_buffer的頭指針),設置pdc->xpr,pdc->xcr,然後重新開啓PDC。

 

       4.Mixer_ioctl

同上,mixer的相關操作是通過ioctl來實現的,它本身甚至不需要read和Write。

由於PCMXXX不支持音量調節,略。

 

5.Pcm 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,

                            具體電源管理的見電源部分

 

6.Pcm 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->state和check是否還有數據保留在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_time爲5000

       即延時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)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章