alsa驱动源码分析(一)

ALSA应用程序(播放器)调用ALSA lib库中的函数snd_pcm_writei()向声卡硬件(或虚拟的)写入交错(write后的i代表interleaved)数据。在ALSA lib中最后会调到snd_pcm_hw_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)函数,这个函数调用通用的ioctl接口:
err = ioctl(fd, SNDRV_PCM_IOCTL_WRITEN_FRAMES, &xfern);
在ALSA驱动层中 snd_pcm_playback_ioctl1函数响应ioctl,在这个函数中某个位置会调用
result = snd_pcm_lib_writev(substream, bufs, xfern.frames);它会继续调用:
snd_pcm_lib_write1(substream, (unsigned long)bufs, frames,
nonblock, snd_pcm_lib_writev_transfer)函数,其中snd_pcm_lib_writev_transfer是个函数指针。这个函数很关键,其中包括下面的代码:
static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,
unsigned long data,
snd_pcm_uframes_t size,
int nonblock,
transfer_f transfer)

{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t xfer = 0;
snd_pcm_uframes_t offset = 0;

...
while (size > 0) {
snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
snd_pcm_uframes_t avail;
snd_pcm_uframes_t cont;
if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
snd_pcm_update_hw_ptr(substream);
avail = snd_pcm_playback_avail(runtime);
if (!avail) {
if (nonblock) {
err = -EAGAIN;
goto _end_unlock;
}
err = wait_for_avail_min(substream, &avail);
if (err < 0)
goto _end_unlock;
}
frames = size > avail ? avail : size;
cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;
if (frames > cont)
frames = cont;
snd_assert(frames != 0, snd_pcm_stream_unlock_irq(substream); return -EINVAL);
appl_ptr = runtime->control->appl_ptr;
appl_ofs = appl_ptr % runtime->buffer_size;
snd_pcm_stream_unlock_irq(substream);
if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0)
goto _end;
snd_pcm_stream_lock_irq(substream);
switch (runtime->status->state) {
case SNDRV_PCM_STATE_XRUN:
err = -EPIPE;
goto _end_unlock;
case SNDRV_PCM_STATE_SUSPENDED:
err = -ESTRPIPE;
goto _end_unlock;
default:
break;
}
appl_ptr += frames;
if (appl_ptr >= runtime->boundary)
appl_ptr -= runtime->boundary;
runtime->control->appl_ptr = appl_ptr;
if (substream->ops->ack)
substream->ops->ack(substream);

offset += frames;
size -= frames;
xfer += frames;
if (runtime->status->state == SNDRV_PCM_STATE_PREPARED &&
snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) {
err = snd_pcm_start(substream);
if (err < 0)
goto _end_unlock;
}
}
_end_unlock:
snd_pcm_stream_unlock_irq(substream);
_end:
return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
}

snd_pcm_playback_avail是个内联函数,在include/sound/pcm.h中定义:
/*
* result is: 0 ... (boundary - 1)
*/
static inline snd_pcm_uframes_t snd_pcm_playback_avail(struct snd_pcm_runtime *runtime)
{
snd_pcm_sframes_t avail = runtime->status->hw_ptr + runtime->buffer_size - runtime->control->appl_ptr;
if (avail < 0)
avail += runtime->boundary;
else if ((snd_pcm_uframes_t) avail >= runtime->boundary)
avail -= runtime->boundary;
return avail;
}
上面的函数代码,可以帮助我们理解 alsa 中的数据结构成员所代表的含义。很显然,上面的函数的目的就是返回当前hardware buffer中用户程序可写的帧(frames)数。runtime->status-hw_ptr应该表示硬件指针,指向硬件已经处理(播放/录音)过的数据的位置,runtime->control->buffer_size是整个buffer的大小,而runtime->control->appl_ptr是用户程序已经处理(读/写)过的数据的位置。appl_ptr之后的空间,一直到buffer末尾,这一段是可以写入的(注意这个函数名中有playback,只用于播放),这一段长度为runtime->buffer_size - runtime->control->appl_ptr;另外hw_ptr之前的空间也是可写的(其中的数据已经被播放),所以整个可用的长度为 runtime->status->hw_ptr + runtime->buffer_size - runtime->control->appl_ptr。

 

你可能感兴趣的:(数据结构,Stream,struct,buffer,playback)