4.6 构建pcm
接着上节的内容,现在来看看构建pcm的过程。
4.6.1 snd_hda_codec_build_pcms
int snd_hda_codec_build_pcms(struct hda_codec *codec)
{
struct hda_bus *bus = codec->bus;
struct hda_pcm *cpcm;
int dev, err;
err = snd_hda_codec_parse_pcms(codec);
if (err < 0)
return err;
/* attach a new PCM streams */
list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
if (cpcm->pcm)
continue; /* already attached */
if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
continue; /* no substreams assigned */
dev = get_empty_pcm_device(bus, cpcm->pcm_type);
if (dev < 0) {
cpcm->device = SNDRV_PCM_INVALID_DEVICE;
continue; /* no fatal error */
}
cpcm->device = dev;
err = snd_hda_attach_pcm_stream(bus, codec, cpcm);
if (err < 0) {
codec_err(codec,
"cannot attach PCM stream %d for codec #%d\n",
dev, codec->core.addr);
continue; /* no fatal error */
}
}
return 0;
}
snd_hda_codec_parse_pcms的作用是根据codec的信息生成对应的hda_pcm对象。
snd_hda_attach_pcm_stream将调用snd_pcm_new 生成snd_pcm文件,也是我们这一章探寻的一个终点了。
下面将分别介绍这两个函数。
get_empty_pcm_device,这个函数我不太理解。给hda_bus中的pcm_dev_bits赋了值,但遍寻代码,也没发现pcm_dev_bits有什么用处。
4.6.2 snd_hda_codec_parse_pcms
int snd_hda_codec_parse_pcms(struct hda_codec *codec)
{
struct hda_pcm *cpcm;
int err;
// 如果codec->pcm_list_head为空,说明已经解析过了,返回
if (!list_empty(&codec->pcm_list_head))
return 0; /* already parsed */
// 要通过patch_ops.build_pcms来完成,之前看过具体的设置了
if (!codec->patch_ops.build_pcms)
return 0;
err = codec->patch_ops.build_pcms(codec);
if (err < 0) {
codec_err(codec, "cannot build PCMs for #%d (error %d)\n",
codec->core.addr, err);
return err;
}
// 遍历设置所有的stream信息
list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
int stream;
for (stream = 0; stream < 2; stream++) {
struct hda_pcm_stream *info = &cpcm->stream[stream];
if (!info->substreams)
continue;
err = set_pcm_default_values(codec, info);
if (err < 0) {
codec_warn(codec,
"fail to setup d