看看snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
对于每个声卡都得调用这个函数,并且参数都是如此,所以这个函数式共性的。
这个函数第一个函数ret = snd_card_create(idx, xid, codec->owner, 0, &codec->card);
1、分配:struct snd_card *card; card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);
2、申请该卡snd_card的id号:if (!slots[idx2] || !*slots[idx2]) idx = idx2;
3、锁住申请的id号:snd_cards_lock |= 1 << idx;
4、设置snd_card结构体:card->number = idx;card->module = module;
INIT_LIST_HEAD(&card->devices);
INIT_LIST_HEAD(&card->controls);
INIT_LIST_HEAD(&card->ctl_files);
5、把该snd_card当成设备放入链表:
err = snd_ctl_create(card);
static struct snd_device_ops ops = {....}
snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);//类型SNDRV_DEV_CONTROL
struct snd_device *dev;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
dev->card = card;
dev->type = type; //SNDRV_DEV_CONTROL
dev->device_data = device_data;//snd_card
dev->ops = ops;//该设备的操作函数,也就是snd_card
list_add(&dev->list, &card->devices);//放入链表
6、创建proc文件:err = snd_info_card_create(card);
7、使得codec->card指向我们设置好的snd_card:*card_ret = card;
8、最终将通过某个函数吧链表的设备都会注册到内核: snd_ctl_dev_register(struct snd_device *device)
在/dev将生成controlC0设备节点:
snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,&snd_ctl_f_ops, card, name);
第二个函数 soc_new_pcm(socdev, &card->dai_link[i], i);
这个要用到定义好的snd_soc_card:
static struct snd_soc_card smdk = {
.name = "SMDK",
.platform = &s3c24xx_soc_platform,
.dai_link = &smdk_dai,
.num_links = 1,
};
1、每个dai_link 就有一个snd_soc_pcm_runtime结构体:
rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime), GFP_KERNEL);
rtd->dai = dai_link;
snprintf(new_name, sizeof(new_name), "%s %s-%d",dai_link->stream_name, codec_dai->name, num);//"AC97 PCM"+ "AC97 HiFi",
playback = 1;
capture = 1;
2、每个dai_link :设置dai_link->pcm
snd_pcm_new(codec->card, new_name, codec->pcm_devs++, playback,capture, &pcm);
根据playback 个数和capture 个数,调用snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)函数实现pcm_streams流:
snd_pcm_substream里面包含了pre和next指针成员,构成了一条闭合链;而每个pcm就有一条链;
substream->pcm = pcm;//链里的每个元素都对应于同一个snd_pcm结构体(也就是pcm)
substream->stream = stream;//链里的每个元素都对应于同一个pcm的类型,是播放还是录音:SNDRV_PCM_STREAM_PLAYBACK、SNDRV_PCM_STREAM_CAPTURE
sprintf(substream->name, "subdevice #%i", idx); //以序号区分各个元素
把这些元素组成闭合的链表:(substream->pstr = pstr;prev->next = substream;prev = substream;......)
list_add_tail(&substream->link_list, &substream->self_group.substreams);//把闭合链表的每个元素都放到这个链表对应的self_group.substreams链表;
3、把设置好的pcm作为设备放到链表:
snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)
dev->card = card;
dev->device_data = device_data; //pcm
dev->ops = ops; //对应下面的操作函数
list_add(&dev->list, &card->devices); //放到链表
static struct snd_device_ops ops = {
.dev_free = snd_pcm_dev_free,
.dev_register =snd_pcm_dev_register,
.dev_disconnect = snd_pcm_dev_disconnect,
};
后面将会通过链表调用snd_pcm_dev_register函数来注册pcm设备:
snd_register_device_for_dev(devtype, pcm->card, pcm->device, &snd_pcm_f_ops[cidx],pcm, str, dev);
对应于/dev/下的:
/dev/pcmC0D0c
/dev/pcmC0D0p
操作函数:
const struct file_operations snd_pcm_f_ops[2] = {
{
.owner = THIS_MODULE,
.write = snd_pcm_write,
.open = snd_pcm_playback_open,
.release = snd_pcm_release,
......................
},
{
.owner = THIS_MODULE,
.read = snd_pcm_read,
.aio_read = snd_pcm_aio_read,
.open = snd_pcm_capture_open,
.release = snd_pcm_release,
..........................
}
};
这几个函数就是声卡的播放和录音实现函数:
第一个对应录音,第二个对应播放。
而对于这两个操作无非最终会调用到硬件操作,对于播放操作,最终会调用到substream->ops函数来实现,而对于这个结构体的实现就是下一个函数;
第三个函数:
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops);
这个函数就是实现substream->ops;
static struct snd_pcm_ops soc_pcm_ops = {
.open = soc_pcm_open,
.close = soc_codec_close,
.hw_params = soc_pcm_hw_params,
.hw_free = soc_pcm_hw_free,
.prepare = soc_pcm_prepare,
.trigger = soc_pcm_trigger,
.pointer = soc_pcm_pointer,
};
soc_pcm_ops.mmap = platform->pcm_ops->mmap;
soc_pcm_ops.ioctl = platform->pcm_ops->ioctl;
soc_pcm_ops.copy = platform->pcm_ops->copy;
soc_pcm_ops.silence = platform->pcm_ops->silence;
soc_pcm_ops.ack = platform->pcm_ops->ack;
soc_pcm_ops.page = platform->pcm_ops->page;
这些都是共性的东西,就是每个声卡都一样,但是对于硬件操作,最终一定得调用到硬件层:
读函数:
snd_pcm_read----------->substream->ops---->platform->pcm_ops\cpu_dai->ops\codec_dai->ops等操作函数,也就是snd_soc_card结构体的成员的操作函数。
其中platform和dma挂钩,而cpu_dai和codec_daii就是用接口操作声卡,不同的是cpu_dai的操作是适合任何声卡,这应该也等同我们所说的协议,而codec_dai就是针对这块声卡的一些操作。
我声卡的信息:
static struct snd_soc_dai_link smdk_dai = {
.name = "AC97",
.stream_name = "AC97 PCM",
.cpu_dai = &s3c_ac97_dai[S3C_AC97_DAI_PCM],
.codec_dai = &wm9713_dai[WM9713_DAI_AC97_HIFI],
.init = smdk_ac97_init,
};
static struct snd_soc_card smdk = {
.name = "SMDK",
.platform = &s3c24xx_soc_platform,
.dai_link = &smdk_dai,
.num_links = 1,
};
static struct snd_soc_device smdk_snd_ac97_devdata = {
.card = &smdk,
.codec_dev = &soc_codec_dev_wm9713,
};
http://blog.youkuaiyun.com/l_d_d/article/details/8274641