Linux ALSA架构:驱动综述(七)
一、驱动综述
在声卡及设备、Platform & Codec驱动和Machine驱动中已经简单介绍了各个部分的实现。本节综合以上内容进行简单总结。
二、驱动实现过程
Machine驱动的probe会调用到snd_soc_bind_card。
snd_soc_bind_card(card);
struct snd_soc_pcm_runtime *rtd;
struct snd_soc_component *component;
struct snd_soc_dai_link *dai_link;
//为每个dai_link初始化一个rtd
for_each_card_prelinks(card, i, dai_link)
ret = snd_soc_add_pcm_runtime(card, dai_link);
//初始化snd_card,并创建Control设备
ret = snd_card_new(card->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
card->owner, 0, &card->snd_card);
//为每个rtd创建对应的pcm设备
for_each_card_rtds(card, rtd)
ret = soc_init_pcm_runtime(card, rtd);
//注册设备
ret = snd_card_register(card->snd_card);
1. snd_soc_add_pcm_runtime对rtd的初始化
snd_soc_add_pcm_runtime
rtd = soc_new_pcm_runtime(card, dai_link);
struct snd_soc_pcm_runtime *rtd;
struct snd_soc_component *component;
struct device *dev;
dev = kzalloc(sizeof(struct device), GFP_KERNEL);
ret = device_register(dev);
rtd = devm_kzalloc(dev, sizeof(*rtd) + sizeof(*component) * (dai_link->num_cpus +
dai_link->num_codecs + dai_link->num_platforms), GFP_KERNEL);
rtd->dais = devm_kcalloc(dev, dai_link->num_cpus + dai_link->num_codecs,
sizeof(struct snd_soc_dai *), GFP_KERNEL);
rtd->dai_link = dai_link;
ret = device_add_groups(dev, soc_dev_attr_groups);
//把对应component加到rtd中
//i = 0;(i < link->num_cpus) && (cpu = &link->cpus[i]); i++
for_each_link_cpus(dai_link, i, cpu)
// asoc_rtd_to_cpu(rtd, i) = rtd->dais[i]
asoc_rtd_to_cpu(rtd, i) = snd_soc_find_dai(cpu);
snd_soc_rtd_add_component(rtd, asoc_rtd_to_cpu(rtd, i)->component);
../codec & platform
2. snd_card_new 声卡初始化
snd_card_new
struct snd_card *card;
card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);
err = kobject_set_name(&card->card_dev.kobj, "card%d", idx);
//初始化card参数
...
// dev/snd里的controlC0设备
err = snd_ctl_create(card);
static const struct snd_device_ops ops = {
.dev_free = snd_ctl_dev_free,
.dev_register = snd_ctl_dev_register,
.dev_disconnect = snd_ctl_dev_disconnect,
};
dev_set_name(&card->ctl_dev, "controlC%d", card->number);
err = snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
struct snd_device *dev;
struct list_head *p;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
... //初始化dev
dev->ops = ops;
list_for_each_prev(p, &card->devices) {
struct snd_device *pdev = list_entry(p, struct snd_device, list);
list_add(&dev->list, p);
err = snd_info_card_create(card);
entry = create_subdir(card->module, str);
3. soc_init_pcm_runtime pcm初始化
soc_init_pcm_runtime
struct snd_soc_dai_link *dai_link = rtd->dai_link;
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
struct snd_soc_component *component;
ret = soc_new_pcm(rtd, num);
snd_pcm_new(rtd->card->snd_card, new_name, num, playback, capture, &pcm);
struct snd_pcm *pcm;
staic const struct snd_device_ops ops = {
.dev_free = snd_pcm_dev_free,
.dev_register = snd_pcm_dev_register,
.dev_disconnect = snd_pcm_dev_disconnect,
};
pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count);
struct snd_pcm_str *pstr = &pcm->streams[stream];
struct snd_pcm_substream *substream, *prev;
pstr->stream = stream;
pstr->pcm = pcm;
pstr->substream_count = substream_count;
err = snd_device_new(card, SNDRV_DEV_PCM, pcm,
internal ? &internal_ops : &ops);
rtd->pcm = pcm;
pcm->private_data = rtd;
//no dynamic
rtd->ops.open = soc_pcm_open;
rtd->ops.hw_params = soc_pcm_hw_params;
rtd->ops.prepare = soc_pcm_prepare;
rtd->ops.trigger = soc_pcm_trigger;
rtd->ops.hw_free = soc_pcm_hw_free;
rtd->ops.close = soc_pcm_close;
rtd->ops.pointer = soc_pcm_pointer;
if (playback)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops);
substream->ops = ops;
ret = snd_soc_pcm_component_new(rtd);
//调用platform的pcm_construct函数
ret = component->driver->pcm_construct(component, rtd);
4. snd_card_register 声卡注册
snd_card_register
err = device_add(&card->card_dev);
err = snd_device_register_all(card)
list_for_each_entry(dev, &card->devices, list)
err = __snd_device_register(dev);
err = dev->ops->dev_register(dev);
snd_pcm_dev_register(struct snd_device *device)
struct snd_pcm_substream *substream;
struct snd_pcm *pcm;
err = snd_pcm_add(pcm);
err = snd_register_device(devtype, pcm->card, pcm->device,
&snd_pcm_f_ops[cidx], pcm,
&pcm->streams[cidx].dev);
struct snd_minor *preg;
preg = kmalloc(sizeof *preg, GFP_KERNEL);
preg->f_ops = f_ops;
device->devt = MKDEV(major, minor);
err = device_add(device);
snd_minors[minor] = preg;
snd_pcm_timer_init(substream);
*snd_pcm_f_ops的借口如下:
const struct file_operations snd_pcm_f_ops[2] = {
{
.owner = THIS_MODULE,
.write = snd_pcm_write,
.write_iter = snd_pcm_writev,
.open = snd_pcm_playback_open,
.release = snd_pcm_release,
.llseek = no_llseek,
.poll = snd_pcm_poll,
.unlocked_ioctl = snd_pcm_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,
.read_iter = snd_pcm_readv,
.open = snd_pcm_capture_open,
.release = snd_pcm_release,
.llseek = no_llseek,
.poll = snd_pcm_poll,
.unlocked_ioctl = snd_pcm_ioctl,
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
.get_unmapped_area = snd_pcm_get_unmapped_area,
}
};
三、总结
本章简介总结了machine驱动与其他部分的联系。声卡注册之后,用户层可以调用对应的借口对设备进行操作。