0. ALSA驅動分析
a) 重要數據結構
i. snd_minors 維護了所有聲音主設備的次設備信息,次設備號是下標
1. 信息包括類型,文件操作,私有數據等
b) 重要概念
i. alsa邏輯設備包括:controlCxx,pcmCxDxp,pcmCxDxc,timer,seq
1. controlCxx用於直接讀寫codec寄存器,打開關閉開關,調節滑塊如音量等
2. pcmCxDxp用於播放,就是playback,關鍵接口是write和ioctl
3. pcmCxDxc 用於錄制,就是capture,關鍵接口是read和ioctl
4. timer和seq作用不明顯
ii. alsa框架基於字符設備,上邊提到的都是alsa邏輯設備,也就是說是同一個主設備下的次設備,共享同一個驅動入口
1. alsa_sound_init(Sound.c)注冊了主設備號為major(CONFIG_SND_MAJOR)的字符設備文件,文件操作是snd_fops,snd_open接口比較重要,snd_open接口通過文件節點inode得到了次設備號,通過snd_minors數組得到對應的聲音邏輯設備的文件操作,調用對應的open接口,並調用fops_put替換成了對應的邏輯設備的文件操作(snd_minors里維護).
iii. Alsa聲音設備驅動probe流程
1. 參考atmel_abdac_probe (abdac.c)接口流程
2. snd_card_create – 創建聲卡
a) 接口里,會自動調用snd_ctl_create創建control邏輯設備
3. snd_ctl_create 創建control邏輯設備
a) 調用snd_device_new,傳遞snd_device_ops ops,這個ops很關鍵
i. snd_device_new接口把control邏輯設備放在了card->devices里
b) ops中的snd_ctl_dev_register接口,實際會被下邊的snd_card_register接口調用到,snd_ctl_dev_register接口調用snd_register_device,傳遞snd_ctl_f_ops,這個ops就是實際使用到的control設備的文件操作.
4. snd_pcm_new創建pcm邏輯設備
a) _snd_pcm_new創建
i. 調用snd_device_new,傳遞snd_device_ops ops,類似control設備,
ii. ops里的snd_pcm_dev_register接口,會被snd_card_register調用,調用snd_register_device_for_dev傳遞snd_pcm_f_ops,這個ops就是實際pcm邏輯設備使用到的文件操作,這個接口里還調用snd_pcm_timer_init創建了timer設備
iii. 調用snd_pcm_new_stream兩次,分別建立了playback和capture連個substream.
iv. 注意,每個pcm邏輯設備包括了兩個substream,PLAYBACK和CAPTURE,snd_pcm_f_ops有對應的兩類ops.
5. snd_pcm_set_ops接口用來設備pcm設備,流的操作,流的操作會被pcm邏輯設備的文件操作所回調.
6. snd_card_register注冊聲卡
a) 調用device_create創建設備
b) 調用snd_device_register_all注冊聲卡所有的邏輯設備,調用了之前注冊的card->devices里的所有邏輯設備的dev->ops->dev_register接口.
c) 至此,邏輯設備節點就建立了.
iv. 邏輯設備打開
1. 上邊分析,所有音頻設備都是聲卡的次設備,主設備相同,共享snd_open接口,snd_open接口里會調用對應邏輯設備的open,並替換文件操作為邏輯設備的文件操作.
2. Control設備
a) snd_ctl_f_ops中的snd_ctl_open在control邏輯設備打開時本snd_open調用.
b) snd_ctl_open
i. snd_minors中存儲的control邏輯設備的私有數據是snd_card *card
ii. 創建一個snd_ctl_file *ctl,同時設備card數據
iii. snd_ctl_file *ctl作為這個file 的private_data,以備后用
3. Pcm設備
a) snd_pcm_f_ops中的snd_pcm_playback_open和snd_pcm_capture_open分別對應pcmXXXp和pcmXXXc邏輯設備的打開.
b) snd_pcm_playback_open
i. 調用snd_lookup_minor_data拿到snd_minors保存的私有數據,是snd_pcm *pcm
ii. 接着調用snd_pcm_open
1. snd_pcm_open調用snd_pcm_open_file,snd_pcm_open_file調用snd_pcm_open_substream打開substream,substream被填充賦值.
2. 之后snd_pcm_open_file用substream填充了snd_pcm_file *pcm_file,作為file的file->private_data.
c) snd_pcm_capture_open
i. 與snd_pcm_playback_open幾乎相同.
v. Pcm邏輯設備文件操作分析
1. snd_pcm_f_ops結構包括了playback和capture兩個流,對應pcmXXXp和pcmXXXc兩個邏輯設備的文件操作.
2. pcm操作過程中,pcm邏輯設備的文件操作不停的被調用,alsa實現了pcm邏輯設備的操作,而alsa允許真正的設備相關的操作是留給上層驅動的接口,也就是substream的ops,所有的substream ops都會在適當的時機被調用.Asoc是alsa驅動的有一層封裝,Asoc正是封裝了substream的接口以此來提供Asoc邏輯層的machine/platform/codec/dai的邏輯操作.
3. Playback
i. .open = snd_pcm_playback_open,
1. snd_lookup_minor_data查找邏輯設備數據
2. snd_pcm_open打開pcm播放流
a) snd_pcm_open_file
i. snd_pcm_open_substream創建substream
a) snd_pcm_attach_substream
b) snd_pcm_hw_constraints_init 初始化常量
c) snd_pcm_hw_constraints_complete初始常量
ii. file->private_data = pcm_file;設置file的私有數據
iii. 設置了substream的runtime數據
3. 打開完成.
ii. .write = snd_pcm_write,
1. 取出file私有數據pcm_file
2. 取出pcm_file的substream
3. 取出substream的runtime
4. 調用snd_pcm_lib_write寫
a) 調用snd_pcm_lib_writ傳遞snd_pcm_lib_write_transfer接口
i. snd_pcm_lib_write_transfer接口被調用傳遞數據.
ii. 寫到了runtime->dma_area區域
iii. 判斷是prepared狀態,並且snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold,調用snd_pcm_start接口
a) 實際會調用到snd_pcm_action_start里的接口substream->ops->trigger最后后被調用到.
b) 這樣就開始了dma傳送數據的過程.
iii. .aio_write = snd_pcm_aio_write,
1. TODO
iv. .release = snd_pcm_release,
1. TODO
v. .llseek = no_llseek,
1. TODO
vi. .poll = snd_pcm_playback_poll,
1. TODO
vii. .unlocked_ioctl = snd_pcm_playback_ioctl,
1. snd_pcm_playback_ioctl1
a) 判斷命令動作類型,包括很多
i. SNDRV_PCM_IOCTL_WRITEI_FRAMES 寫數據
ii. SNDRV_PCM_IOCTL_HW_PARAMS 設置參數
iii. SNDRV_PCM_IOCTL_PREPARE 准備流
iv. SNDRV_PCM_IOCTL_START 開始流
viii. .compat_ioctl = snd_pcm_ioctl_compat,
1. TODO
ix. .mmap = snd_pcm_mmap,
1. TODO
x. .fasync = snd_pcm_fasync,
1. TODO
xi. .get_unmapped_area = snd_pcm_get_unmapped_area,
1. TODO
4. Capture
i. .open = snd_pcm_capture_open,
1. 類似snd_pcm_playback_open
ii. .read = snd_pcm_read,
1. 和snd_pcm_write的流程是相反的
iii. .aio_read = snd_pcm_aio_read,
1. TODO
iv. .release = snd_pcm_release,
1. TODO
v. .llseek = no_llseek,
1. TODO
vi. .poll = snd_pcm_capture_poll,
1. TODO
vii. .unlocked_ioctl = snd_pcm_capture_ioctl,
1. 類似snd_pcm_playback_ioctl
viii. .compat_ioctl = snd_pcm_ioctl_compat,
1. TODO
ix. .mmap = snd_pcm_mmap,
1. TODO
x. .fasync = snd_pcm_fasync,
1. TODO
xi. .get_unmapped_area = snd_pcm_get_unmapped_area,
1. TODO
vi. Control邏輯設備文件操作分析
1. snd_control_f_ops結構包括了control邏輯設備的文件操作
2. 類似pcm設備,alsa實現了control邏輯設備的文件操作,alsa留給上層驅動的接口是kcontrol,kcontrol的info,put,get接口被調用.Asoc對control設備的封裝體現在dapm控件以及提供了一些方便實用的宏.
3. control的控件由驅動實現者類添加,實現.
4. 如下
a) .open = snd_ctl_open,
i. TODO
b) .read = snd_ctl_read,
i. TODO
c) .release = snd_ctl_release,
i. TODO
d) .llseek = no_llseek,
i. TODO
e) .poll = snd_ctl_poll,
i. TODO
f) .unlocked_ioctl = snd_ctl_ioctl,
i. 根據不同的動作,不同的操作,動作由alsa-lib調用
1. SNDRV_CTL_IOCTL_ELEM_LIST獲取所有控件列表
2. SNDRV_CTL_IOCTL_ELEM_INFO獲取指定控件信息
3. SNDRV_CTL_IOCTL_ELEM_READ讀操作
a) snd_ctl_elem_read_user
i. snd_ctl_elem_read
A. snd_ctl_find_id獲取kctl
B. kctl->get接口被調用
4. SNDRV_CTL_IOCTL_ELEM_WRITE
a) 類似SNDRV_CTL_IOCTL_ELEM_READ,最終kctl->put被調用
g) .compat_ioctl = snd_ctl_ioctl_compat,
h) .fasync = snd_ctl_fasync,
