您的位置:首页 > 其它

alsa声卡分析alsa-utils调用过程(一)-tinyplay

2018-03-07 15:04 302 查看

如何分析tinyplay 播放音频和tinymix的过程?需要相应的工具来支持追查;

 

一、分析tinyplay和tinymix:

1.1 利用strace工具:

strace -o tinyplay.log tinyplay 1.wav

strace -o tinymixer.log tinymixer "SEC_MI2S_RX Audio Mixer MultiMedia1" 1

 利用strace工具获取APP的log,从应用层往下看;

 

1.2 分析alsa-utils源码:

tiny工具源码在android/external/tinyalsa目录下;

 

二、tinyplay调用分析(tinyplay.log搜索设备节点“/dev/snd/pcmC0D0p”)

2.1 tinyplay的open过程:

snd_pcm_f_ops[0]是播放音频的file_operations,snd_pcm_f_ops[1]则是录音的file_operations:

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,
.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,
}
};

我们从snd_pcm_playback_open函数开始向下分析:

static int snd_pcm_playback_open(struct inode *inode, struct file *file)
{
struct snd_pcm *pcm;
int err = nonseekable_open(inode, file);
if (err < 0)
return err;
pcm = snd_lookup_minor_data(iminor(inode),
SNDRV_DEVICE_TYPE_PCM_PLAYBACK);  //取得其私有数据并返回的
err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK);
if (pcm)
snd_card_unref(pcm->card);      //减少设备对象的引用计数 snd_card_unref(card);
return err;
}

在下面调用了snd_pcm_open函数:

static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream)
{
int err;
wait_queue_t wait;

if (pcm == NULL) {
err = -ENODEV;
goto __error1;
}
err = snd_card_file_add(pcm->card, file);
if (err < 0)
goto __error1;
if (!try_module_get(pcm->card->module)) {
err = -EFAULT;
goto __error2;
}
init_waitqueue_entry(&wait, current);
add_wait_queue(&pcm->open_wait, &wait);
mutex_lock(&pcm->open_mutex);
while (1) {
err = snd_pcm_open_file(file, pcm, stream);  // 将操作该声卡card的应用程序添加到card->files_list
if (err >= 0)
break;
if (err == -EAGAIN) {
if (file->f_flags & O_NONBLOCK) {
err = -EBUSY;
break;
}
} else
break;
set_current_state(TASK_INTERRUPTIBLE);
mutex_unlock(&pcm->open_mutex);
schedule();
mutex_lock(&pcm->open_mutex);
if (pcm->card->shutdown) {
err = -ENODEV;
break;
}
if (signal_pending(current)) {
err = -ERESTARTSYS;
break;
}
}
remove_wait_queue(&pcm->open_wait, &wait);
mutex_unlock(&pcm->open_mutex);
if (err < 0)
goto __error;
return err;

__error:
module_put(pcm->card->module);
__error2:
snd_card_file_remove(pcm->card, file);
__error1:
return err;
}

 再从snd_pcm_open_file继续向下看:

static int snd_pcm_open_file(struct file *file,
struct snd_pcm *pcm,
int stream)
{
struct snd_pcm_file *pcm_file;
struct snd_pcm_substream *substream;
int err;

err = snd_pcm_open_substream(pcm, stream, file, &substream);    //打开substream结构体
if (err < 0)
return err;

pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL);
if (pcm_file == NULL) {
snd_pcm_release_substream(substream);
return -ENOMEM;
}
pcm_file->substream = substream;
if (substream->ref_count == 1) {
substream->file = pcm_file;
substream->pcm_release = pcm_release_private;
}
file->private_data = pcm_file;

return 0;
}
int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
struct file *file,
struct snd_pcm_substream **rsubstream)
{
struct snd_pcm_substream *substream;
int err;

err = snd_pcm_attach_substream(pcm, stream, file, &substream);
if (err < 0)
return err;
if (substream->ref_count > 1) {
*rsubstream = substream;
return 0;
}

err = snd_pcm_hw_constraints_init(substream);    //初始化substream结构体
if (err < 0) {
snd_printd("snd_pcm_hw_constraints_init failed\n");
goto error;
}

if ((err = substream->ops->open(substream)) < 0)
goto error;

substream->hw_opened = 1;

err = snd_pcm_hw_constraints_complete(substream);
if (err < 0) {
snd_printd("snd_pcm_hw_constraints_complete failed\n");
goto error;
}

*rsubstream = substream;
return 0;

error:
snd_pcm_release_substream(substream);
return err;
}
snd_pcm_open_substream

 

 在snd_pcm_open_substream函数中:

if ((err = substream->ops->open(substream)) < 0)    // substream->ops : snd_pcm_ops结构体
goto error;

 依次调用cpu_dai, dma, codec_dai, machine(三大模块)的open或startup函数;

 

msm_mi2s_snd_startup函数:

struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_card *card = rtd->card;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_codec *codec = rtd->codec;

 

设置snd_soc_pcm_runtime的cpu、codec等模块;然后在snd_soc_pcm_runtime函数中对codec函数进行相应的设置,之后通过音频数据流通道播放出声音;

调用过程如下图:

 

 

2.2 tinyplay的ioctl过程:

 同样也是snd_pcm_f_ops[0]结构体的file_operations:

{
.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,
.compat_ioctl =     snd_pcm_ioctl_compat,
.mmap =            snd_pcm_mmap,
.fasync =        snd_pcm_fasync,
.get_unmapped_area =    snd_pcm_get_unmapped_area,
},

 

从snd_pcm_playback_ioctl函数向下看:

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') && (((cmd >> 8) & 0xff) != 'C'))
return -ENOTTY;

return snd_pcm_playback_ioctl1(file, pcm_file->substream, cmd,
(void __user *)arg);
}

 

snd_pcm_playback_ioctl1:

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;
if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_PLAYBACK))
return -EINVAL;
//根据case不同,对播放进行相应的不同操作
switch (cmd) {
case SNDRV_PCM_IOCTL_WRITEI_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_write(substream, xferi.buf, xferi.frames);
__put_user(result, &_xferi->result);
return result < 0 ? result : 0;
}
case SNDRV_PCM_IOCTL_WRITEN_FRAMES:
{
struct snd_xfern xfern;
struct snd_xfern __user *_xfern = arg;
struct snd_pcm_runtime *runtime = substream->runtime;
void __user **bufs;
snd_pcm_sframes_t result;
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
if (runtime->channels > 128)
return -EINVAL;
if (put_user(0, &_xfern->result))
return -EFAULT;
if (copy_from_user(&xfern, _xfern, sizeof(xfern)))
return -EFAULT;

bufs = memdup_user(xfern.bufs,
sizeof(void *) * runtime->channels);
if (IS_ERR(bufs))
return PTR_ERR(bufs);
result = snd_pcm_lib_writev(substream, bufs, xfern.frames);
kfree(bufs);
__put_user(result, &_xfern->result);
return result < 0 ? result : 0;
}
case SNDRV_PCM_IOCTL_REWIND:
{
snd_pcm_uframes_t frames;
snd_pcm_uframes_t __user *_frames = arg;
snd_pcm_sframes_t result;
if (get_user(frames, _frames))
return -EFAULT;
if (put_user(0, _frames))
return -EFAULT;
result = snd_pcm_playback_rewind(substream, frames);
__put_user(result, _frames);
return result < 0 ? result : 0;
}
case SNDRV_PCM_IOCTL_FORWARD:
{
snd_pcm_uframes_t frames;
snd_pcm_uframes_t __user *_frames = arg;
snd_pcm_sframes_t result;
if (get_user(frames, _frames))
return -EFAULT;
if (put_user(0, _frames))
return -EFAULT;
result = snd_pcm_playback_forward(substream, frames);
__put_user(result, _frames);
return result < 0 ? result : 0;
}
}
return snd_pcm_common_ioctl1(file, substream, cmd, arg);
}

 

从snd_pcm_common_ioctl1继续分析,进入函数的prepare中:

当函数prepare完毕后,就一切准备就绪了,只等一个trigger;而trigger的执行会在上层的alsalib调用write的函数触发;prepare过程可以看下图,具体就不继续分析了:

 

下一节我们将来分析tinymixer的调用过程;

alsa声卡分析alsa-utils调用过程(二)-tinymixer

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: