alsa驅(qū)動(dòng)分析(2.6.21內(nèi)核)之一
掃描二維碼
隨時(shí)隨地手機(jī)看文章
Alsa驅(qū)動(dòng)分析
Guide
RevisionHistory
Date
Issue
Description
Author
First draft
Wylhistory
?
?
?
?
目錄
1.??? Abstract.. 3
2.??? Introduction.. 3
3.??? 音頻驅(qū)動(dòng)框架介紹.... 3
3.1????? 音頻設(shè)備的注冊(cè)... 3
3.2???? 音頻驅(qū)動(dòng)的注冊(cè)... 4
3.2.1?????? Probe函數(shù)的調(diào)用... 4
3.2.2?????? Soc_probe函數(shù)... 4
4.??? 通常的使用流程的分析.... 6
4.1.1?????? open過(guò)程介紹... 6
4.1.2?????? snd_pcm_hw_params流程分析... 8
4.1.3?????? prepare流程分析... 9
4.1.4?????? write的流程... 15
4.1.5?????? 使用流程的總結(jié)t18
5.??? Amixer調(diào)用的相關(guān)邏輯.... 18
5.1.1?????? Amixer調(diào)用的上層邏輯... 19
5.1.2?????? Amixer的內(nèi)核流程... 20
6.??? 總結(jié).... 21
7.??? 未討論.... 21
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
1.??????????????Abstract
主要是講2.6.21內(nèi)核里面的alsa驅(qū)動(dòng)的架構(gòu),以及在我們的平臺(tái)上需要注意的東西。.
2.??????????????Introduction
分成幾個(gè)部分:
驅(qū)動(dòng)整體框架,一個(gè)簡(jiǎn)單的播放流程介紹,以及我們的平臺(tái)需要注意的地方;
3.??????????????音頻驅(qū)動(dòng)框架介紹 3.1??????????????音頻設(shè)備的注冊(cè)
這就是設(shè)備的注冊(cè)了,設(shè)備本身非常簡(jiǎn)單,復(fù)雜的是這個(gè)設(shè)備的drvdata,drvdata里面包含了三部分,關(guān)于machine的,關(guān)于platform的,關(guān)于codec的,從大體上說(shuō)machine主要是關(guān)于cpu這邊的也可以說(shuō)是關(guān)于ssp本身設(shè)置的,而platform是關(guān)于平臺(tái)級(jí)別的,就是說(shuō)這個(gè)平臺(tái)本身實(shí)現(xiàn)相關(guān)的,而codec就是與我們所用的音頻codec相關(guān)的;基本上這里就可以看出整個(gè)音頻驅(qū)動(dòng)的架構(gòu)特點(diǎn),就是從alsa層進(jìn)入——>內(nèi)核alsa層接口->core層,這里再調(diào)用上面說(shuō)的三個(gè)方面的函數(shù)來(lái)處理,先是cpu級(jí)別的,再是platform的,再是codec級(jí)別的,這幾層做完了,工作也就做得差不多了,后面會(huì)詳細(xì)講講,當(dāng)然這個(gè)執(zhí)行順序不是固定的(不知道是不是marvel寫(xiě)代碼不專業(yè)導(dǎo)致的),但多半都包括了這三部分的工作;
3.2???????????音頻驅(qū)動(dòng)的注冊(cè) 3.2.1?????????Probe函數(shù)的調(diào)用??????
???????? ????????
?????????????????? 前面講了設(shè)備的注冊(cè),里面的設(shè)備的名字就是”soc-audio”,而這里的driver的注冊(cè)時(shí)名字也是” soc-audio”,對(duì)于platform的設(shè)備匹配的原則是根據(jù)名字的,所以將會(huì)匹配成功,成功后就會(huì)執(zhí)行audio驅(qū)動(dòng)提供的probe函數(shù)soc_probe;
3.2.2?????????Soc_probe函數(shù)
這個(gè)函數(shù)本身架構(gòu)很簡(jiǎn)單,和前面說(shuō)的邏輯一樣,先調(diào)用了cpu級(jí)別的probe,再是codec級(jí)別的,最后是platform的(這里三個(gè)的順序就不一樣),但是因?yàn)閏pu級(jí)別的和platform級(jí)別的都為空,最后都調(diào)用了codec級(jí)別的probe函數(shù),也就是micco_soc_probe,這個(gè)函數(shù)基本上就完成了所有應(yīng)該完成的音頻驅(qū)動(dòng)的初始化了;簡(jiǎn)單的劃分,分成兩部分,對(duì)上和對(duì)下:對(duì)上主要是注冊(cè)設(shè)備節(jié)點(diǎn),以及這些設(shè)備節(jié)點(diǎn)對(duì)應(yīng)的流的創(chuàng)建;對(duì)下主要是讀寫(xiě)函數(shù)的設(shè)置,codec本身的dai設(shè)置,初始化寄存器的設(shè)置,最重要的就是后面的control的創(chuàng)建和門(mén)的創(chuàng)建了,如下圖所示:
這里面的第一部分就是負(fù)責(zé)初始化的;
?
第二部分就是創(chuàng)建卡和流,對(duì)于alsa驅(qū)動(dòng)來(lái)說(shuō),是先分成卡0,卡1…,然后對(duì)于每一個(gè)卡的每一個(gè)cpu支持的dai(digit audio interface)也就是pcm接口或者i2S接口等都要建立對(duì)應(yīng)的流,一個(gè)dai有可能包含兩個(gè)流,一個(gè)是錄的一個(gè)是play的,但在我們的平臺(tái)上對(duì)于i2S的dai是沒(méi)有錄音功能的,所以我們的平臺(tái)只有一個(gè)卡,三個(gè)流,pcm的錄和play,i2S的play;流的創(chuàng)建還是更多的考慮為上層服務(wù)的,它所提供的接口都是soc層的,這里非常重要的地方在于驅(qū)動(dòng)的一個(gè)典型做法那就是如何把關(guān)鍵的內(nèi)核數(shù)據(jù)結(jié)構(gòu)和export到外部的/dev下的設(shè)備節(jié)點(diǎn)實(shí)現(xiàn)關(guān)聯(lián),比如:
?
關(guān)鍵數(shù)據(jù)結(jié)構(gòu)structsnd_pcm,是根據(jù)cpu所固有的dai創(chuàng)建的,而對(duì)于每一個(gè)struct ?snd_pcm又可能用到兩個(gè)substream(它們實(shí)現(xiàn)具體的流的播放等),它們之間的鏈接是通過(guò)它的內(nèi)部數(shù)據(jù)成員struct snd_pcm_str streams[2];來(lái)連接的,而這個(gè)snd_pcm類型的指針是在函數(shù)snd_device_new里面通過(guò)device_data放到設(shè)備里面的,這個(gè)設(shè)備會(huì)在snd_device_register_all
的時(shí)候注冊(cè)到/dev下面,并且調(diào)用dev_set_drvdata(preg->dev, private_data);來(lái)把這個(gè)指針?lè)诺皆O(shè)備的私有數(shù)據(jù)里面;而在需要使用的時(shí)候通過(guò)snd_pcm_playback_open里面的snd_lookup_minor_data函數(shù)取得其私有數(shù)據(jù)并返回的,這樣就實(shí)現(xiàn)了設(shè)備節(jié)點(diǎn)和對(duì)應(yīng)的驅(qū)動(dòng)的數(shù)據(jù)結(jié)構(gòu)的關(guān)聯(lián),這是一種非常普遍的做法;有了這個(gè)數(shù)據(jù)結(jié)構(gòu)它就可以根據(jù)一定的原則取得對(duì)應(yīng)于這個(gè)需求的substream,于是一切的操作都可以交給這個(gè)substream了;
?
第三部分就是control的創(chuàng)建,這個(gè)函數(shù)比較簡(jiǎn)單,就是把表micco_snd_controls里面已經(jīng)定義好的controls模板創(chuàng)建controls,然后加入到card的controls列表中去;本身功能很清晰,但是對(duì)于我們平臺(tái)來(lái)說(shuō),需要非常小心,因?yàn)檫@里決定了各個(gè)controls的序號(hào),而這個(gè)序號(hào)是audio_controller訪問(wèn)硬件的索引,所以千萬(wàn)要小心盡量要維持目前的controls的序號(hào),如果要額外添加新的controls一定要記得要放在micco_add_widgets后面來(lái)做,這樣可以做到兼容,否則audio_controller的工作量就大了!
?
第四部分就是門(mén)的創(chuàng)建了,這個(gè)函數(shù)也是很清楚,就是把codec對(duì)應(yīng)的門(mén)都加入到codec->dapm_widgets列表中去(這里的門(mén)的概念可以簡(jiǎn)單的理解為水管與水管之間的連接的地方,聲音數(shù)據(jù)像水一樣從水管里面流出來(lái),源頭可以是CPU了,也可以是modem,然后通過(guò)不同的門(mén),流向不同的地方,比如speaker,比如藍(lán)牙耳機(jī)等等),然后根據(jù)micco_audio_map把所有可能連在一起的門(mén)連接起來(lái),這個(gè)表micco_audio_map的意思是{目的名字,控制點(diǎn)名字,源頭名字},然后函數(shù)snd_soc_dapm_connect_input會(huì)根據(jù)這些名字去查表codec->dapm_widgets(先前已經(jīng)把所有的門(mén)都加入了)找到它們?cè)俑鶕?jù)不同的類型做不同的連接,比如是mux之間的連接,mux和pga之間的連接等等,注意這里的連接其實(shí)只不過(guò)是說(shuō)找到連接的可能性,它對(duì)于不同的門(mén),找到其可能的source和sink,加入到對(duì)應(yīng)的列表中去,具體細(xì)節(jié)如下:
首先,掃描整個(gè)codec所擁有的所有的門(mén),如果它的名字和傳入的sink的名字相同,則認(rèn)為它就是這個(gè)路徑的sink,如果它的名字和傳入的source名字相同,則認(rèn)為它是這個(gè)路徑的source,如果源頭或者sink沒(méi)有找到都返回錯(cuò)誤;然后分配一個(gè)struct snd_soc_dapm_path,這個(gè)數(shù)據(jù)結(jié)構(gòu)的主要成分包括名字,source門(mén),sink門(mén),這條路徑的control,這個(gè)源頭和sink是否已經(jīng)連接,是否已經(jīng)走過(guò)(用在后面),這個(gè)數(shù)據(jù)結(jié)構(gòu)會(huì)被掛在三個(gè)鏈表里面,一個(gè)是source的就是這個(gè)門(mén)會(huì)在很多的路徑中,把它在這個(gè)路徑中做source的path都連在一起,一個(gè)是sink的就是把這個(gè)門(mén)在所有這些由它做sink的path都連接在一起,一個(gè)是把所有的路徑都需要連接在一起的這個(gè)是通過(guò)codec的dapm_paths來(lái)訪問(wèn)的;
list_add(&path->list,&codec->dapm_paths);
list_add(&path->list_sink,&wsink->sources);
list_add(&path->list_source,&wsource->sinks);
需要注意的時(shí)候,這里把路徑的list_sink加入到了wsink門(mén)的sources列表里面,而把路徑的list_source加入到wsource的sinks列表里面,所以當(dāng)訪問(wèn)的時(shí)候從wsink門(mén)的sources出發(fā)就可以找到連接這個(gè)門(mén)作為sink的所有的路徑,而從wsource的sinks列表出發(fā)就可以找到所有以這個(gè)門(mén)作為source的路徑;
第三步就是為這個(gè)數(shù)據(jù)結(jié)構(gòu)賦值:source,sink,初始化三個(gè)鏈表;第四步:如果control為空則把這個(gè)路徑加入到相應(yīng)的三個(gè)鏈表中去,并且路徑設(shè)為已經(jīng)連接,并返回;第五步:否則,根據(jù)sink的類型,如果是adc,dac,input,output,micbias,vmid,pre,post,則把路徑加入到三個(gè)鏈表,設(shè)置已經(jīng)連接的標(biāo)志;如果是snd_soc_dapm_mux則調(diào)用dapm_connect_mux來(lái)處理;如果是mixer和switch則調(diào)用dapm_connect_mixer來(lái)處理,如果是hp,mic,line,spk,則把path加入到三個(gè)鏈表中去,但是設(shè)置成為連接的狀態(tài)。
大約就是這樣的了。
也許您要問(wèn),為什么要這么做呢?
這個(gè),我也有想過(guò),甚至我認(rèn)為在門(mén)比較少的時(shí)候,確實(shí)沒(méi)什么必要,但是這么做的好處在于當(dāng)要播放音樂(lè)的時(shí)候,它可以實(shí)現(xiàn)自動(dòng)的尋找路徑并且自動(dòng)poweron那些門(mén),不需要上層做任何的控制,因?yàn)樗娴牡竭_(dá)目的地的所有的路徑,這樣它可以自己選擇走哪條路;如果不這么連接起來(lái)的話,就需要提供給上層連接的接口,完全由上層來(lái)決定該連接哪些門(mén),也必須由上層來(lái)負(fù)責(zé)poweron和off這些門(mén);
第五部分就是注冊(cè)了,這里就是向/dev注冊(cè)設(shè)備節(jié)點(diǎn),因?yàn)檫@些設(shè)備節(jié)點(diǎn)會(huì)由alsa層來(lái)訪問(wèn)的,這些設(shè)備節(jié)點(diǎn)和驅(qū)動(dòng)的連接我前面已經(jīng)說(shuō)了,主要是提供了對(duì)上的alsa接口,給alsa層調(diào)用。??