分析 Linux 核心聲音子系統 ALSA SoC (ASoC) 中 Platform 層驅動的註冊流程,以 sound/soc/soc-core.c 為例,說明 platform_driver 結構、probe 回調中 snd_soc_register_card 的角色、三角關係 (socdev, card, platform_device)。
/* soc-core.c -- ALSA SoC Audio Layer */
/* platform_device probe 回調 */
static int soc_probe(struct platform_device *pdev)
{
int ret = 0;
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_card *card = socdev->card;
/* 建立反向關聯 */
card->socdev = socdev;
card->dev = &pdev->dev;
/* 核心註冊:將 card 註冊到 ASoC 核心 */
ret = snd_soc_register_card(card);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to register card\n");
return ret;
}
return 0;
}
/* Platform 驅動結構 */
static struct platform_driver soc_driver = {
.driver = {
.name = "soc-audio",
.owner = THIS_MODULE,
.pm = &soc_pm_ops,
},
.probe = soc_probe,
.remove = soc_remove,
};
/* 模組初始化:註冊 platform_driver */
static int __init snd_soc_init(void)
{
return platform_driver_register(&soc_driver);
}
module_init(snd_soc_init);
platform_device (pdev)
│ platform_get_drvdata()
▼
snd_soc_device (socdev) ← 包含 card 指標
│ socdev->card
▼
snd_soc_card (card) ← 核心聲音卡片抽象
│ card->socdev = socdev (反向)
│ card->dev = &pdev->dev
▼
snd_soc_register_card(card) → 註冊到 ASoC 核心,啟動 DAPM、PCM、控制元件等
module_init(snd_soc_init) → platform_driver_register(&soc_driver),將 soc_driver 註冊到核心 platform 總線。soc-audio 的 platform_device (通常由機器初始化碼 platform_device_register 建立),調用 soc_probe。platform_get_drvdata(pdev) 取得先前綁定的 snd_soc_device 結構 (包含具體機器的 snd_soc_card 定義)。card->socdev 指回 socdev,card->dev 指向 platform_device 的 dev 結構,方便後續 DMA、PM 操作。snd_soc_register_card(card) 將聲音卡片註冊到 ASoC 核心,完成 DAPM 路由建立、PCM 串流註冊、控制元件註冊、Jack 偵測等初始化。platform_device 與 snd_soc_card,儲存機器級私有資料。snd_soc_card 並填入 DAI 連結、控制項等。platform_device 註冊前,用 platform_set_drvdata 綁定 snd_soc_device。soc-audio platform_driver 為通用核心層,**機器驅動無需重寫**,只需提供正確的 platform_device 與私有資料。