天天看點

2021-04-11

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