asoc 設計思路
參數
1 表明支援哪些參數 2 根據指定的參數 進行硬體設定
使用strace 指令查詢執行流程
ANDROID TINYPLAY與TINYCAP流程
https://www.freesion.com/article/7678406915/
Android下的音頻通道配置檔案mixer_paths.xml
【Audio driver】mixer_paths.xml檔案分析
xml 裡面的資料,當其中一個裝置節點被改變,那會追蹤相關的裝置節點一起改變。
audiohardware class作為向下通路的接口 (由廠家提供)
派生自 audiohardwarebase audiohard interface
audiohardware 裡面有有 audio_stream_out 還有 audio_stream_in
我感覺 audio_stream_out 這個得到是 pcm_open 得到的呢
Android Audio代碼分析8 - AudioHardwareALSA::openOutputStream函數
tinyalsa 裡面有 pcm_open pcm_write pcm_read 還有設定 kconfig 的函數
1 驅動程式配置設定的buffer
2 app 不斷寫入 一個 period 的資料到buffer
period 裡面有多個 frame ,一個 frame 就是一個采樣率
3 驅動不斷從 buffer 裡面 取出 一個 period 的資料送到 聲霸卡
判斷
首先 period 是一個dma 的大小
4 pcm_new 是建立聲霸卡時候被調用,很适合申請 dma buff
5 dma 需要虛拟位址 實體位址 size大小
6 申請的buffer,是一個 環形buffer,是以有需要有一個描述的讀寫的狀态
7 prepare 每次調用 snd_pcm_prepare 都會使用這個函數段 ,在這個函數裡面會設定硬體參數。
8 trigger 在 pcm 開始 停止 暫停 都會調用它
9 會在 dma 中斷回調函數中執行
/* 更新hw_ptr等資訊,
* 并且判斷:如果buffer裡沒有資料了,則調用trigger來停止DMA
*/
snd_pcm_period_elapsed(substream);
10 snd_pcm_period_elapsed 會調用 pointer 函數字段來更新位址
11 pointer 函數字段會調用 bytes_to_frames 把 更新的 标号
12 bytes_to_frames 第二個參數的 标号 要手動實時更新
13 傳輸資料 用的是 ioctl
調用 snd_pcm_ioctl_compat
然後 會調用
snd_pcm_playback_ioctl1
snd_pcm_capture_ioctl1
然後調用
copy_from_user
14 資料是放到 dma buff 裡面的
15 當 使用 arecord,控制節點會 使用 ioctl , pcm 也會 ioctl
但是控制節點,不涉及到 硬體操作
16 sndrv_pcm_ioctl_sync_ptr (應該大寫,當寫入資料以後,就應該更新位址) 指針同步
17 SNDRV_PCM_IOCTL_WRITEL_FRAMES (當調用這個 cmd 就會實作資料的傳輸)會使用 copy_from_user 把資料與 dma buff 互動
18
case SNDRV_PCM_IOCTL_READI_FRAMES:
{
struct snd_xferi xferi;
struct snd_xferi __user *_xferi = arg;
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_sframes_t result;
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
if (put_user(0, &_xferi->result))
return -EFAULT;
if (copy_from_user(&xferi, _xferi, sizeof(xferi)))
return -EFAULT;
result = snd_pcm_lib_read(substream, xferi.buf, xferi.frames);
__put_user(result, &_xferi->result);
return result < 0 ? result : 0;
}
上面會調用 snd_pcm_lib_read
snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __user *buf, snd_pcm_uframes_t size)
{
struct snd_pcm_runtime *runtime;
int nonblock;
int err;
err = pcm_sanity_check(substream);
if (err < 0)
return err;
runtime = substream->runtime;
nonblock = !!(substream->f_flags & O_NONBLOCK);
if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED)
return -EINVAL;
return snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer);
}
上面調用 snd_pcm_lib_read_transfer
static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream,
unsigned int hwoff,
unsigned long data, unsigned int off,
snd_pcm_uframes_t frames)
{
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
char __user *buf = (char __user *) data + frames_to_bytes(runtime, off);
if (substream->ops->copy) {
if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0)
return err;
} else {
char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames)))
return -EFAULT;
}
return 0;
}
會調用 copy_to_user 讀到 使用者層
19 類比上面的
static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream,
unsigned int hwoff,
unsigned long data, unsigned int off,
snd_pcm_uframes_t frames)
{
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
char __user *buf = (char __user *) data + frames_to_bytes(runtime, off);
if (substream->ops->copy) {
if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0)
return err;
} else {
char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames)))
return -EFAULT;
}
return 0;
}
copy_from_user 把使用者層的資料 搞到 dma buff 裡面
20 snd_soc_pcm_runtime 什麼時間建立,同時代表的什麼意思
1 當底層調用了snd_soc_register_card 時, ASoC核心層會從全局連結清單中找到dai_link指定的platform、
cpu_dai、codec_dai、codec,
snd_soc_register_card
snd_soc_instantiate_cards