Control介面主要讓使用者空間的應用程式(alsa-lib)可以訪問和控制音頻codec晶片中的多路開關,滑動控制項等。
我們需要在我們的驅動程式初始化時主動調用snd_pcm_new()函數建立pcm裝置,而control裝置則在snd_card_create()內被建立,
snd_card_create()通過調用snd_ctl_create()函數建立control裝置節點。所以我們無需顯式地建立control裝置,只要建立音效卡,control裝置被自動地建立。
同樣,它有一個snd_ctl_dev_register,它調用:
- snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1, &snd_ctl_f_ops, card, name)
/*
* INIT PART
*/
static const struct file_operations snd_ctl_f_ops =
{
.owner = THIS_MODULE,
.read = snd_ctl_read,
.open = snd_ctl_open,
.release = snd_ctl_release,
.llseek = no_llseek,
.poll = snd_ctl_poll,
.unlocked_ioctl = snd_ctl_ioctl,
.compat_ioctl = snd_ctl_ioctl_compat,
.fasync = snd_ctl_fasync,
};
使用者程式需要開啟control裝置時,驅動程式通過snd_minors[]全域數組和此裝置號,可以獲得snd_ctl_f_ops結構中的各個回呼函數,
然後通過這些回呼函數訪問control中的資訊和資料(最終會調用control的幾個回呼函數get,put,info)。可以讀一下代碼:/sound/core/control.c。
ASoC--ALSA System on Chip ,是建立在標準ALSA驅動層上,為了更好地支援嵌入式處理器和行動裝置中的音頻Codec的一套軟體體系。
目前已經被整合至核心的代碼樹中:sound/soc。ASoC不能單獨存在,他只是建立在標準ALSA驅動上的一個它必須和標準的ALSA驅動架構相結合才能工作。
通常,就像軟體領域裡的抽象和重用一樣,嵌入式裝置的音頻系統可以被劃分為板載硬體(Machine)、Soc(Platform)、Codec三大部分,如所示:
音頻系統結構
ASoC中重要的資料結構之間的關聯方式:
Kernel-2.6.35-ASoC中各個結構的靜態關係
Kernel 3.0中的ASoC資料結構
3.0中的資料結構更為合理和清晰,取消了snd_soc_device結構,直接用snd_soc_card取代了它,並且強化了snd_soc_pcm_runtime的作用,同時還增加了另外兩個資料結構snd_soc_codec_driver和snd_soc_platform_driver,用於明確代表Codec驅動和Platform驅動。
/* SoC audio ops */
struct snd_soc_ops {
int (*startup)(struct snd_pcm_substream *);
void (*shutdown)(struct snd_pcm_substream *);
int (*hw_params)(struct snd_pcm_substream *, struct snd_pcm_hw_params *);
int (*hw_free)(struct snd_pcm_substream *);
int (*prepare)(struct snd_pcm_substream *);
int (*trigger)(struct snd_pcm_substream *, int);
};
它的作用是什麼呢???
通過snd_soc_card結構,又引出了Machine驅動的另外兩個個資料結構:
- snd_soc_dai_link(執行個體:smdk_dai[] )
- snd_soc_ops(執行個體:smdk_ops )
其中,snd_soc_dai_link中,指定了Platform、Codec、codec_dai、cpu_dai的名字,稍後Machine驅動將會利用這些名字去匹配已經在系統中註冊的platform,codec,dai,這些註冊的組件都是在另外相應的Platform驅動和Codec驅動的代碼檔案中定義的,這樣看來,Machine驅動的裝置初始化代碼無非就是選擇合適Platform和Codec以及dai,用他們填充以上幾個資料結構,然後註冊Platform裝置即可。當然還要實現串連Platform和Codec的dai_link對應的ops實現,本例就是smdk_ops,它只實現了hw_params函數:smdk_hw_params。
platform_driver通過其probe串連了snd_soc_card,其中的dai_link(snd_soc_dai_link類型)串連了snd_soc_ops結構體。
snd_soc_card的prope函數在platform匯流排匹配platform_device和platform_driver時會被調用,它調用snd_soc_register_card,通過snd_soc_register_card,。。。,大部分的工作都在snd_soc_instantiate_card中實現。
該函數首先利用card->instantiated來判斷該卡是否已經執行個體化,如果已經執行個體化則直接返回,否則遍曆每一對dai_link,進行codec、platform、dai的綁定工作:
/* bind DAIs */
for (i = 0; i < card->num_links; i++)
soc_bind_dai_link(card, i);
ASoC定義了三個全域的鏈表頭變數:codec_list、dai_list、platform_list,系統中所有的Codec、DAI、Platform都在註冊時串連到這三個全域鏈表上。soc_bind_dai_link函數逐個掃描這三個鏈表,根據card->dai_link[]中的名稱進行匹配,匹配後把相應的codec,dai和platform執行個體賦值到card->rtd[]中(snd_soc_pcm_runtime)。經過這個過程後,snd_soc_pcm_runtime:(card->rtd)中儲存了本Machine中使用的Codec,DAI和Platform驅動的資訊。
/* SoC machine DAI configuration, glues a codec and cpu DAI together */
struct snd_soc_pcm_runtime {
struct device dev;
struct snd_soc_card *card;
struct snd_soc_dai_link *dai_link;
unsigned int complete:1;
unsigned int dev_registered:1;
/* Symmetry data - only valid if symmetry is being enforced */
unsigned int rate;
long pmdown_time;
/* runtime devices */
struct snd_pcm *pcm;
struct snd_soc_codec *codec;
struct snd_soc_platform *platform;
struct snd_soc_dai *codec_dai;
struct snd_soc_dai *cpu_dai;
struct delayed_work delayed_work;
};
snd_soc_instantiate_card接著初始化Codec的寄存器緩衝,然後調用標準的alsa函數建立音效卡執行個體:
- /* card bind complete so register a sound card */
- ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, card->owner, 0, &card->snd_card);
- card->snd_card->dev = card->dev;
- card->dapm.bias_level = SND_SOC_BIAS_OFF;
- card->dapm.dev = card->dev;
- card->dapm.card = card;
- list_add(&card->dapm.list, &card->dapm_list);
然後,依次調用各個子結構的probe函數:
/* initialise the sound card only once */
if (card->probe) {
ret = card->probe(card); //可能是空的
if (ret < 0)
goto card_probe_error;
}
for (i = 0; i < card->num_links; i++) {
ret = soc_probe_dai_link(card, i);
if (ret < 0) {
pr_err("asoc: failed to instantiate card %s: %d\n",
card->name, ret);
goto probe_dai_err;
}
}
在上面的soc_probe_dai_link()函數中做了比較多的事情,該函數除了挨個調用了codec,dai和platform驅動的probe函數外,
在最後還調用了soc_new_pcm()函數用於建立標準alsa驅動的pcm邏輯裝置。(它會調用:
ret = snd_pcm_new(rtd->card->snd_card, new_name,
num, playback, capture, &pcm);
)
調用標準alsa驅動中的建立pcm的函數snd_pcm_new()建立音效卡的pcm執行個體,pcm的private_data欄位設定為該runtime變數rtd,然後用platform驅動中的snd_pcm_ops替換部分pcm中的snd_pcm_ops欄位,最後,調用platform驅動的pcm_new回調,該回調實現該platform下的dma記憶體申請和dma初始化等相關工作。到這裡,音效卡和他的pcm執行個體建立完成。
回到snd_soc_instantiate_card函數,完成snd_card和snd_pcm的建立後,接著對dapm和dai支援的格式做出一些初始化合設定工作後,調用了 card->late_probe(card)進行一些最後的初始化合設定工作,最後則是調用標準alsa驅動的音效卡註冊函數對音效卡進行註冊:
//damp controls
if (card->dapm_widgets)
snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,
card->num_dapm_widgets);
if (card->dapm_routes)
snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,
card->num_dapm_routes);
。。。
//in init.c
//create card device, register all the devices on the card.
ret = snd_card_register(card->snd_card);
if (ret < 0) {
printk(KERN_ERR "asoc: failed to register soundcard for %s\n", card->name);
goto probe_aux_dev_err;
}
至此,整個Machine驅動的初始化已經完成,通過各個子結構的probe調用,實際上,也完成了部分Platfrom驅動和Codec驅動的初始化工作,整個過程可以用一下的順序圖表表示:
基於3.0核心 soc_probe順序圖表
基於2.6.35 soc_probe順序圖表