zynq 音乐播放流程
来源:互联网 发布:mysql还原数据库 编辑:程序博客网 时间:2024/05/02 04:35
在《zynq audio pcm DMA》里提到了snd_pcm_writei()这个函数,这个函数是alsa lib里的接口,其实现如下:
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_RWfprintf(stderr, "hw_writei: frames = %li, xferi.result = %li, err = %i\n", size, xferi.result, err);#endifif (err < 0)return snd_pcm_check_error(pcm, err);return xferi.result;}这个函数实际上是调用了ioctl函数完成了相关的操作,实际上alsa lib是对pcm操作的封装,简化了alsa的用户空间编程。
ioctl系统调用是在linux 内核头文件syscalls.h里声明的。
<4>[<c03a9a2c>] (snd_pcm_playback_ioctl1) from [<c00d7af0>] (do_vfs_ioctl+0x564/0x688)<4>[<c00d7af0>] (do_vfs_ioctl) from [<c00d7c48>] (SyS_ioctl+0x34/0x5c)<4>[<c00d7c48>] (SyS_ioctl) from [<c000edc0>] (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,最终调用
<sound/core/pcm_lib.c>实现
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;}这里调用了四个triger函数,每一个函数对应一个trigger。platform对应的trigger函数是snd_dmaengine_pcm_trigger(), cpu侧的trigger函数是axi_i2s_trigger()。而codec则没有实现trigger函数。axi-i2s_trigger()用于设置cpu侧dai的使能。真正和DMA相关的操作在
<sound/core/pcm_dmaengine.c>
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 else205 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调用pl330_prep_dma_cyclic()函数,dmaengine_submit()调用pl330_tx_submit()函数添加发送描述符。
0 0
- zynq 音乐播放流程
- ffplay简化版,用于说明ffplay播放音乐主要流程
- [RK3288][Android6.0] 开机播放音乐流程小结
- 播放音乐
- 播放音乐
- 音乐播放
- 播放音乐
- 音乐播放
- 播放音乐
- 播放音乐
- 音乐播放
- 播放音乐
- 播放音乐
- 音乐播放
- 音乐播放
- 音乐播放
- 音乐播放
- 音乐播放
- xcode打包的几种方式
- 基于PHP SLIM 框架搭建 RESTful 风格API 示例
- Xcode 依赖管理带来的静态库动态库分析
- 第十一周上机实践——警察和厨师(2)(补)
- 实现SwipeMenuListView特定item的右滑menu无效
- zynq 音乐播放流程
- mac 命令
- android.support.v4.app.Fragment$InstantiationException问题解决
- 广东海洋大学 电子1151 孔yanfei python语言程序设计 第九周
- This application's application-identifier entitlement does not match that of the installed applicati
- Ubuntu下pdf阅读器
- kafka
- 【网络编程4】Java多线程
- Log4j日志级别