zynq音楽再生プロセス
8013 ワード
『zynq audio pcm DMA』でsnd_に言及pcm_writei()という関数はalsa libのインタフェースであり、以下のように実現されています.
ioctlシステム呼び出しはlinuxカーネルヘッダファイルsyscallsである.hで宣言した.
snd_pcm_playback_ioctl1-->snd_pcm_common_ioctl1-->snd_pcm_action_lock_irq-->snd_pcm_action-->snd_pcm_action_single-->snd_pcm_do_start-->soc_pcm_trigger
インプリメンテーション
snd_pcm_sframes_t snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
{
assert(pcm);
assert(size == 0 || buffer);
if (CHECK_SANITY(! pcm->setup)) {
SNDMSG("PCM not set up");
return -EIO;
}
if (pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED) {
SNDMSG("invalid access type %s", snd_pcm_access_name(pcm->access));
return -EINVAL;
}
return _snd_pcm_writei(pcm, buffer, size);
}
この関数が呼び出されました_snd_pcm_writeiは具体的な任務を実現する.static inline snd_pcm_sframes_t _snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
{
return pcm->fast_ops->writei(pcm->fast_op_arg, buffer, size);
}
そして_snd_pcm_writeiは実際に関数ポインタ操作セットを呼び出して書き込み操作を実現した.このwritei関数は実際にalsaライブラリのsnd_ですpcm_hw_writei(). static snd_pcm_sframes_t snd_pcm_hw_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
{
int err;
snd_pcm_hw_t *hw = pcm->private_data;
int fd = hw->fd;
struct snd_xferi xferi;
xferi.buf = (char*) buffer;
xferi.frames = size;
xferi.result = 0; /* make valgrind happy */
err = ioctl(fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &xferi);
err = err >= 0 ? sync_ptr(hw, SNDRV_PCM_SYNC_PTR_APPL) : -errno;
#ifdef DEBUG_RW
fprintf(stderr, "hw_writei: frames = %li, xferi.result = %li, err = %i
", size, xferi.result, err);
#endif
if (err < 0)
return snd_pcm_check_error(pcm, err);
return xferi.result;
}
この関数は実際にioctl関数を呼び出して関連する操作を完了し、実際にalsa libはpcm操作のパッケージであり、alsaのユーザ空間プログラミングを簡略化した.ioctlシステム呼び出しはlinuxカーネルヘッダファイルsyscallsである.hで宣言した.
<4>[] (snd_pcm_playback_ioctl1) from [] (do_vfs_ioctl+0x564/0x688)
<4>[] (do_vfs_ioctl) from [] (SyS_ioctl+0x34/0x5c)
<4>[] (SyS_ioctl) from [] (ret_fast_syscall+0x0/0x3c)
なぜlinux kernel対応にsnd_pcm_writei?これは登録時に確定します.snd_soc_register_card-->snd_card_register--->snd_device_register_all-->__snd_device_register.part.0-->
snd_pcm_dev_register--->snd_pcm_dev_register()
{
err = snd_register_device(devtype, pcm->card, pcm->device,
&snd_pcm_f_ops[cidx], pcm,
&pcm->streams[cidx].dev);
}
.unlocked_ioctl = snd_pcm_playback_ioctl,
snd_pcm_playback_ioctl--->snd_pcm_playback_ioctl1
ユーザ空間伝達のioctlコマンドはSNDRV_であるためPCM_IOCTL_WRITEI_FRAMESでは、対応するcase文が実行されます.プレイコールの流れは次のとおりです.snd_pcm_playback_ioctl1-->snd_pcm_common_ioctl1-->snd_pcm_action_lock_irq-->snd_pcm_action-->snd_pcm_action_single-->snd_pcm_do_start-->soc_pcm_trigger
2807 static int snd_pcm_playback_ioctl1(struct file *file,
2808 struct snd_pcm_substream *substream,
2809 unsigned int cmd, void __user *arg)
2810 {
2811 if (snd_BUG_ON(!substream))
2812 return -ENXIO;
2813 if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_PLAYBACK))
2814 return -EINVAL;
2815
2816 switch (cmd) {
2817 case SNDRV_PCM_IOCTL_WRITEI_FRAMES:
2818 {
2819 struct snd_xferi xferi;
2820 struct snd_xferi __user *_xferi = arg;
2821 struct snd_pcm_runtime *runtime = substream->runtime;
2822 snd_pcm_sframes_t result;
2823 if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
2824 return -EBADFD;
2825 if (put_user(0, &_xferi->result))
2826 return -EFAULT;
2827 if (copy_from_user(&xferi, _xferi, sizeof(xferi)))
2828 return -EFAULT;
2829 result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames);
2830 __put_user(result, &_xferi->result);
2831 return result < 0 ? result : 0;
2832 }
この関数呼び出しのwrite、最終呼び出しインプリメンテーション
static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai;
int i, ret;
for (i = 0; i < rtd->num_codecs; i++) {
codec_dai = rtd->codec_dais[i];
if (codec_dai->driver->ops && codec_dai->driver->ops->trigger) {
ret = codec_dai->driver->ops->trigger(substream,
cmd, codec_dai);
if (ret < 0)
return ret;
}
}
if (platform->driver->ops && platform->driver->ops->trigger) {
ret = platform->driver->ops->trigger(substream, cmd);
if (ret < 0)
return ret;
}
if (cpu_dai->driver->ops && cpu_dai->driver->ops->trigger) {
ret = cpu_dai->driver->ops->trigger(substream, cmd, cpu_dai);
if (ret < 0)
return ret;
}
if (rtd->dai_link->ops && rtd->dai_link->ops->trigger) {
ret = rtd->dai_link->ops->trigger(substream, cmd);
if (ret < 0)
return ret;
}
return 0;
}
ここでは4つのtriger関数が呼び出され、各関数は1つのtriggerに対応する.platformに対応するtrigger関数はsnd_dmaengine_pcm_trigger()、cpu側のtrigger関数はaxi_i2s_trigger().codecはtrigger関数を実現していない.axi-i2s_trigger()は、cpu側daiのイネーブルを設定するために使用されます.本当にDMAに関連する操作は184 int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
185 {
186 struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
187 struct snd_pcm_runtime *runtime = substream->runtime;
188 int ret;
189
190 switch (cmd) {
191 case SNDRV_PCM_TRIGGER_START:
192 ret = dmaengine_pcm_prepare_and_submit(substream);
193 if (ret)
194 return ret;
195 dma_async_issue_pending(prtd->dma_chan);
196 break;
197 case SNDRV_PCM_TRIGGER_RESUME:
198 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
199 dmaengine_resume(prtd->dma_chan);
200 break;
201 case SNDRV_PCM_TRIGGER_SUSPEND:
202 if (runtime->info & SNDRV_PCM_INFO_PAUSE)
203 dmaengine_pause(prtd->dma_chan);
204 else
205 dmaengine_terminate_all(prtd->dma_chan);
206 break;
207 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
208 dmaengine_pause(prtd->dma_chan);
209 break;
210 case SNDRV_PCM_TRIGGER_STOP:
211 dmaengine_terminate_all(prtd->dma_chan);
212 break;
213 default:
214 return -EINVAL;
215 }
216
217 return 0;
218 }
219 EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_trigger);
この関数はdmaengineを呼び出します.pcm_prepare_and_submit()は送信操作を行う.145 static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream)
146 {
147 struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
148 struct dma_chan *chan = prtd->dma_chan;
149 struct dma_async_tx_descriptor *desc;
150 enum dma_transfer_direction direction;
151 unsigned long flags = DMA_CTRL_ACK;
152
153 direction = snd_pcm_substream_to_dma_direction(substream);
154
155 if (!substream->runtime->no_period_wakeup)
156 flags |= DMA_PREP_INTERRUPT;
157
158 prtd->pos = 0;
159 desc = dmaengine_prep_dma_cyclic(chan,
160 substream->runtime->dma_addr,
161 snd_pcm_lib_buffer_bytes(substream),
162 snd_pcm_lib_period_bytes(substream), direction, flags);
163
164 if (!desc)
165 return -ENOMEM;
166
167 desc->callback = dmaengine_pcm_dma_complete;
168 desc->callback_param = substream;
169 prtd->cookie = dmaengine_submit(desc);
170
171 return 0;
172 }
上のこの関数159はpl 330を呼び出します.prep_dma_cyclic()関数、dmaengine_submit()呼び出しpl 330_tx_submit()関数は送信記述子を追加します.