前一章节对整个ALSA的框架进行了分层,本章主要来梳理下ALSA的框架中核心层的声卡注册,对于这部分的代码我们修改的比较少,已理解流程为主,本章只完成对于声卡注册相关流程的梳理。
static int __init alsa_sound_init(void)
{
snd_major = major;
snd_ecards_limit = cards_limit;
if (register_chrdev(major, "alsa", &snd_fops)) {
snd_printk(KERN_ERR "unable to register native major device number %d\n", major);
return -EIO;
}
if (snd_info_init() < 0) {
unregister_chrdev(major, "alsa");
return -ENOMEM;
}
snd_info_minor_register();
#ifndef MODULE
printk(KERN_INFO "Advanced Linux Sound Architecture Driver Version " CONFIG_SND_VERSION CONFIG_SND_DATE ".\n");
#endif
return 0;
}
该函数实现了几个主要的功能
static const struct file_operations snd_fops =
{
.owner = THIS_MODULE,
.open = snd_open,
.llseek = noop_llseek,
}
该snd_fops中只有open和llseek,那么应用怎么能完成对于alsa的操作呢?其实它只是完成一个中转的作用,下面看看open怎么完成这个功能
static int snd_open(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
struct snd_minor *mptr = NULL;
const struct file_operations *old_fops;
int err = 0;
if (minor >= ARRAY_SIZE(snd_minors))
return -ENODEV;
mutex_lock(&sound_mutex);
mptr = snd_minors[minor];
if (mptr == NULL) {
mptr = autoload_device(minor);
if (!mptr) {
mutex_unlock(&sound_mutex);
return -ENODEV;
}
}
old_fops = file->f_op;
file->f_op = fops_get(mptr->f_ops);
if (file->f_op == NULL) {
file->f_op = old_fops;
err = -ENODEV;
}
mutex_unlock(&sound_mutex);
if (err < 0)
return err;
if (file->f_op->open) {
err = file->f_op->open(inode, file);
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
}
fops_put(old_fops);
return err;
}
该函数主要完成
1.【minor = iminor(inode)】以minor变量保存传入节点inode结构体的次设备号
2.【mptr = snd_minors[minor]】以minor为下标在结构体数组中取出一项,让mptr指针指向此项
3.【old_fops = file->f_op;file->f_op = fops_get(mptr->f_ops);】表示取出mptr结构体指针里的file_operations 结构体
4.【err = file->f_op->open(inode, file);】调用file_operations 结构体里面的open函数
那么问题的重点是,谁来配置snd_minors,那么谁就会提供新的fps。
int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
const struct file_operations *f_ops,
void *private_data,
const char *name, struct device *device)
{
int minor;
struct snd_minor *preg;
if (snd_BUG_ON(!name))
return -EINVAL;
preg = kmalloc(sizeof *preg, GFP_KERNEL);
if (preg == NULL)
return -ENOMEM;
preg->type = type;
preg->card = card ? card->number : -1;
preg->device = dev;
preg->f_ops = f_ops;
preg->private_data = private_data;
mutex_lock(&sound_mutex);
#ifdef CONFIG_SND_DYNAMIC_MINORS
minor = snd_find_free_minor(type);
#else
minor = snd_kernel_minor(type, card, dev);
if (minor >= 0 && snd_minors[minor])
minor = -EBUSY;
#endif
if (minor < 0) {
mutex_unlock(&sound_mutex);
kfree(preg);
return minor;
}
snd_minors[minor] = preg;
preg->dev = device_create(sound_class, device, MKDEV(major, minor),
private_data, "%s", name);
if (IS_ERR(preg->dev)) {
snd_minors[minor] = NULL;
mutex_unlock(&sound_mutex);
minor = PTR_ERR(preg->dev);
kfree(preg);
return minor;
}
mutex_unlock(&sound_mutex);
return 0;
}
该函数主要申请了一个struct snd_minor的结构体,将该结构体内type、card、device、f_ops、private_data赋值给snd_minors数组内,并在/sys/class创建设备节点。该函数要完成card、device的初始化。对于这个接口两个功能会使用,一方面在声卡注册会使用,同时对于PCM的注册也会使用。那么对于声卡驱动其流程会是什么样的?
static inline int snd_register_device(int type, struct snd_card *card, int dev,
const struct file_operations *f_ops,
void *private_data,
const char *name)
{
return snd_register_device_for_dev(type, card, dev, f_ops,
private_data, name,
snd_card_get_device_link(card));
}
snd_register_device()被谁调用到control.c中
static int snd_ctl_dev_register(struct snd_device *device)
{
struct snd_card *card = device->device_data;
int err, cardnum;
char name[16];
if (snd_BUG_ON(!card))
return -ENXIO;
cardnum = card->number;
if (snd_BUG_ON(cardnum < 0 || cardnum >= SNDRV_CARDS))
return -ENXIO;
sprintf(name, "controlC%i", cardnum);
if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
&snd_ctl_f_ops, card, name)) < 0)
return err;
return 0;
}
这个函数就是注册了设备的节点,其中涉及到了设备节点的创建和管理,也就是设备的/dev/snd/controlC0的节点,下面我们来认识下这个节点的操作函数snd_ctl_f_ops,也就会替换文章刚开的ops。
int snd_ctl_create(struct snd_card *card)
{
static struct snd_device_ops ops = {
.dev_free = snd_ctl_dev_free,
.dev_register = snd_ctl_dev_register,
.dev_disconnect = snd_ctl_dev_disconnect,
};
if (snd_BUG_ON(!card))
return -ENXIO;
return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
}
snd_ctl_create最终会创建card,并且将这个card添加到dev的list链表中,这个函数会被谁使用呢?就进入声卡创建的核心函数snd_card_new,这个函数只是创建了一个声卡struct snd_card,为后面注册做好准备,暂时不表代码,其主要完成的工作如下图
注册声卡,在这个阶段会遍历声卡下的所有逻辑设备,并且调用各设备的注册回调函数。
int snd_card_register(struct snd_card *card)
{
int err;
if (snd_BUG_ON(!card))
return -EINVAL;
if (!card->registered) {
err = device_add(&card->card_dev);
if (err < 0)
return err;
card->registered = true;
}
if ((err = snd_device_register_all(card)) < 0)
return err;
mutex_lock(&snd_card_mutex);
if (snd_cards[card->number]) {
/* already registered */
mutex_unlock(&snd_card_mutex);
return 0;
}
if (*card->id) {
/* make a unique id name from the given string */
char tmpid[sizeof(card->id)];
memcpy(tmpid, card->id, sizeof(card->id));
snd_card_set_id_no_lock(card, tmpid, tmpid);
} else {
/* create an id from either shortname or longname */
const char *src;
src = *card->shortname ? card->shortname : card->longname;
snd_card_set_id_no_lock(card, src,
retrieve_id_from_card_name(src));
}
snd_cards[card->number] = card;
mutex_unlock(&snd_card_mutex);
init_info_for_card(card);
#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
if (snd_mixer_oss_notify_callback)
snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);
#endif
return 0;
}
其主要完成以下几件事情:
- 如果该card是没有注册的,就调用device_add(&card->card_dev)加入到device链表,并将标志位修改为已注册
- 实际上是通过snd_card的devices链表,遍历所有的snd_device,并且调用snd_device的ops->dev_register()来实现各自设备的注册的。
- init_info_for_card建立一些相应的proc和sysfs下的文件或属性节点
对于如何要完成一个声卡驱动,其应该满足3步曲,申请,初始化,注册
- 创建snd_card的一个实例snd_card_new
- 初始化结构体
- 通过snd_card_register注册到内核中