声卡的操作实质剖析1


看看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.csdn.net/l_d_d/article/details/8274641 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值