声卡下面有多种device,最重要的是pcm和control两类。
先看pcm device
创建pcm
创建device最终使用的都是snd_device_new。常使用的是
snd_pcm_new来创建pcm device。(类似的还有snd_ctl_create)
先贴下调用关系
snd_soc_register_card
snd_soc_instantiate_card
soc_probe_link_dais
soc_new_pcm
snd_pcm_new
_snd_pcm_new
snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)
snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)
snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)
rtd->ops.open = soc_pcm_open;
在创建pcm是可以看到如下log
sprd-codec-v3-i2s <-> vbc-r2p0 mapping ok
sprd-codec-v3-vaudio <-> vaudio mapping ok
codec-i2s-ext <-> vbc-r2p0-ad23 mapping ok
codec-vaudio-ext <-> vaudio-ad23 mapping ok
sprd-codec-v3-fm <-> vbc-dfm mapping ok
null-codec-dai <-> i2s.0 mapping ok
分别对应vbc_r2p0_codec_v3_dai和all_i2s_dai两个dai.这里vbc_r2p0_codec_v3_dai中platform_name是 sprd-pcm-audio。
注意snd_device_new最后一个参数
static struct snd_device_ops ops = {
.dev_free = snd_pcm_dev_free,
.dev_register = snd_pcm_dev_register,
.dev_disconnect = snd_pcm_dev_disconnect,
};
static int snd_pcm_dev_register(struct snd_device *device)
{
err = snd_register_device_for_dev(devtype, pcm->card,
pcm->device,
&snd_pcm_f_ops[cidx],
pcm, str, dev);
}
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,
}
};
用户态打开pcm device(pcmC0D0p)就会调用这里的函数
看snd_pcm_write
snd_pcm_write
snd_pcm_lib_write
snd_pcm_lib_write1
snd_pcm_start
snd_pcm_action
snd_pcm_action_start
snd_pcm_do_start
substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START);
这个substream又
来自那?
看snd_pcm_write
snd_pcm_write
{
struct snd_pcm_file *pcm_file;
pcm_file = file->private_data;
substream = pcm_file->substream;
}
关键是这个pcm_file,其实是来自snd_pcm_open_file
snd_pcm_open_substream(pcm, stream, file, &substream);
pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL);
pcm_file->substream = substream;
而snd_pcm_open_file被snd_pcm_open调用,再往上是snd_pcm_playback_open
snd_pcm_playback_open是snd_pcm_f_ops的一个open函数
再回到snd_soc_register_card,这里注册的card的dai中是.platform_name = "sprd-pcm-audio",
这个sprd-pcm-audio对于的pcm操作在那?
static struct snd_pcm_ops sprd_pcm_ops = {
.open = sprd_pcm_open,
.close = sprd_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = sprd_pcm_hw_params,
.hw_free = sprd_pcm_hw_free,
.prepare = sprd_pcm_prepare,
.trigger = sprd_pcm_trigger,
.pointer = sprd_pcm_pointer,
.mmap = sprd_pcm_mmap,
};
static struct platform_driver sprd_pcm_driver = {
.driver = {
.name = "sprd-pcm-audio",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(sprd_pcm_of_match),
},
.probe = sprd_soc_platform_probe,
.remove = sprd_soc_platform_remove,
};
platform怎么和substream联系起来那?
看snd_soc_instantiate_card。这个函数先调用soc_bind_dai_link,找到dai对应的platform,放到rtd中。
而substream来自与soc_new_pcm.soc_new_pcm第一个参数就是这个rtd
snd_soc_instantiate_card
soc_bind_dai_link
<strong>rtd</strong>->platform = platform;
soc_probe_link_dais
soc_new_pcm(<strong>rtd</strong>, num);
DroidPhone的blog中有几个图片非常好,这里转一下
pcm的数据结构
创建pcm device
用户写数据的流程