调用write函数实现把数据写到设备里面去,这里会触发trigger函数也就是DMA的启动。
用户层的write到内核里面都是通过ioctl来做的,这里面会触发trigger函数的执行,等trigger执行完以后,
才会真正调用函数把用户层的东西copy到dma分配的空间,其中函数snd_pcm_lib_write1非常复杂,这里面有同步的操作,
也就是要等到有空余的空间的时候才允许写,否则就要等待,唤醒是通过函数snd_pcm_update_hw_ptr_post来做的,
这个函数会在DMA传输完一帧的中断到来的时候被调用,用来更新缓冲区指针。
对于回放的情形,PCM 数据流向大致是:
copy_from_user DMA I2S DAC
^ ^ ^ ^
+---------+ | +----------+ | +-----------+ | +-----+ | +------+
|userspace+-------->DMA Buffer+------->I2S TX FIFO+------->CODEC+------->SPK/HP|
+---------+ +----------+ +-----------+ +-----+ +------+
ALSA的Write流程
snd_pcm_playback_ioctl => snd_pcm_playback_ioctl1 => SNDRV_PCM_IOCTL_WRITEN_FRAMES =>
snd_pcm_lib_writev => snd_pcm_lib_write1 => |||| => snd_pcm_lib_write_transfer => copy_from_user [copy user speace data to dma]
snd_pcm_start => snd_pcm_action => snd_pcm_action_group => snd_pcm_do_start => substream->ops->trigger
// external/tinyalsa/pcm.c,用户层通过ioctl的方式来调用kernel
int pcm_write(struct pcm *pcm, const void *data, unsigned int count)
{
struct snd_xferi x;
if (pcm->flags & PCM_IN)
return -EINVAL;
x.buf = (void*)data;
x.frames = count / (pcm->config.channels *
pcm_format_to_bits(pcm->config.format) / 8);
for (;;) {
if (!pcm->running) {
int prepare_error = pcm_prepare(pcm);
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x))//写入初始化数据
return oops(pcm, errno, "cannot write initial data");
pcm->running = 1;//写入初始化之后设置标致位
return 0;
}
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) {
//写入普通pcm数据
pcm->prepared = 0;//如果写入失败,则重置
pcm->running = 0;//如果写入失败,则重置
if (errno == EPIPE) {
pcm->underruns++;
if (pcm->flags & PCM_NORESTART)
return -EPIPE;
continue;
}
return oops(pcm, errno, "cannot write stream data");
}
return 0;
}
}
// kernel-3.18/sound/core/pcm_native.c,kernel层的实现
// 在内核中发起系统调用,执行本应用户空间发起调用的fops函数集,完成参数设置任务
/*const struct file_operations snd_pcm_f_ops[2] = {
{
.owner = THIS_MODULE,
.write = snd_pcm_write,
.aio_write = snd_pcm_aio_write,
.open = snd_pcm_playback_open,
.release = snd_pcm_release,
.llseek = no_llseek,
.poll = snd_pcm_playback_poll,
.unlocked_ioctl = snd_pcm_playback_ioctl,//pcm文件的ioctl函数
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
.get_unmapped_area = snd_pcm_get_unmapped_area,
},
{
.owner = THIS_MODULE,
.read = snd_pcm_read,
.aio_read = snd_pcm_aio_read,
.open = snd_pcm_capture_open,
.release = snd_pcm_release,
.llseek = no_llseek,
.poll = snd_pcm_capture_poll,
.unlocked_ioctl = snd_pcm_capture_ioctl,
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
.get_unmapped_area = snd_pcm_get_unmapped_area,
}
};*/
static long snd_pcm_playback_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct snd_pcm_file *pcm_file;
pcm_file = file->private_data;
if (((cmd >> 8) & 0xff) != 'A')
return -ENOTTY;
return snd_pcm_playback_ioctl1(file, pcm_file->substream, cmd,
(void __user *)arg);
}
static int snd_pcm_playback_ioctl1(struct file *file,
struct snd_pcm_substream *substream,
unsigned int cmd, void __user *arg)
{
if (snd_BUG_ON(!substream))
return -ENXIO;