入口函数
static int __init alsa_sound_init(void)
{
//116
snd_major = major;
snd_ecards_limit = cards_limit;
/**
注册一个字符设备,主设备号为116.
这里设计的和input.c(input子系统的核心层)类似。
snd_fops中只提供了一个open函数,
在input子系统中 也是只提供了一个open函数。(input_open()函数的具体实现请见我以前的博文分析)
在分析完成snd_open()之后,会比较他们之间的一些差异。
*/
if (register_chrdev(major,"alsa",&snd_fops))
{
snd_printk(KERN_ERR "unable to register native major device number %d\n", major);
return -EIO;
}
...
return 0;
}
static const struct file_operations snd_fops =
{
.owner = THIS_MODULE,
.open = snd_open,
.llseek = noop_llseek,
};
/**
这个函数的实现和input_open()的实现在逻辑上及其类似。
这种在注册的时候,根据次设备号填充到数组中,在open的时候根据次设备号从数组中取出来
在input子系统、LCD子系统、v4l2子系统中都大量的使用。
*/
static int snd_open(struct inode *inode, struct file *file)
{
/**
获取次设备号,是要根据这里获取到的次设备号从数组中取出对应的snd_minor
那么,这里的次设备号就是snd_minor[]被填充的索引值,
这一操作是再snd_register_device_for_dev()函数中 实现的,下面会有分析。
*/
unsigned int minor = iminor(inode);
struct snd_minor *mptr = NULL;
const struct file_operations *old_fops;
int err = 0;
/**
static struct snd_minor *snd_minors[256];
看获取到的次设备号是否大于256。
*/
if (minor >= ARRAY_SIZE(snd_minors))
{
return -ENODEV;
}
mutex_lock(&sound_mutex);
/**
根据上面获取到的次设备号,从snd_minors[]中取出对应的snd_minor
那么,要问了,snd_minors[]是在什么时候被填充的?接着往下看
*/
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_operation被赋予从数组中获取的那个snd_minor的file_operations
问题 :
那么,这里替换打开文件的fops,这个fops是什么。
这个问题也是在下面的一个函数snd_register_device_for_dev()中来实现的。
这里替换了打开文件的fops,以后的函数 使用file->f_op中定义的函数,就是snd_pcm_f_ops中提供的函数了
这一技巧,在内核大多数open函数中都有使用
*/
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;
/**
如果从数组中取出的这个snd_minor 提供了file_operations,并且在file_operations中
提供了open函数,那么就调用它的open函数
*/
if (file->f_op->open)
{
/**
调用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;
}
现在的问题是: snd_minors[]数组在什么时候被填充
/**
看第三个参数 f_ops
[0] :播放
[1] :录音
const struct file_operations snd_pcm_f_ops[2] = {
{
.owner = THIS_MODULE,
.write = snd_pcm_write,
.aio_write = snd_pcm_aio_write,
.open = snd_pcm_playback_open,
.release = snd_pcm_release,
.llseek = no_llseek,
.poll = snd_pcm_playback_poll,
.unlocked_ioctl = snd_pcm_playback_ioctl,
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
...
},
{
.owner = THIS_MODULE,
.read = snd_pcm_read,
.aio_read = snd_pcm_aio_read,
.open = snd_pcm_capture_open,
.release = snd_pcm_release,
.llseek = no_llseek,
.poll = snd_pcm_capture_poll,
.unlocked_ioctl = snd_pcm_capture_ioctl,
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
.get_unmapped_area = snd_pcm_get_unmapped_area,
}
};
在看一下它的使用
err = snd_register_device_for_dev(devtype, pcm->card,
pcm->device,
&snd_pcm_f_ops[cidx],
pcm, str, dev);
在这个函数中有下面一句代码:
preg->f_ops = f_ops;
所以,这个过程 : snd_pcm_f_ops 作为snd_register_device_for_dev的一个参数被传递
进来,最终被赋值给了 snd_minors[minor]->f_ops中,
那么,snd_pcm_f_ops中的这些指针函数什么时候被使用呢? 下面有分析
*/
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;
//必需提供name参数
if (snd_BUG_ON(!name))
{
return -EINVAL;
}
/**
为snd_minor分配内存空间
*/
preg = kmalloc(sizeof(*preg),GFP_KERNEL);
if (preg == NULL)
{
return -ENOMEM;
}
/**
赋值初始化 snd_minor
*/
preg->type = type;
preg->card = card ? card->number : -1;
preg->device = dev;
preg->f_ops = f_ops; //看这个传入的参数已经赋值给了snd_minors->f_ops了
preg->private_data = private_data;
mutex_lock(&sound_mutex);
#indef CONFIG_SND_DYNAMIC_MINORS;
minor = snd_find_free_minor();
#else
/**
静态 分配次设备号,这个函数在下面进行分析
*/
minor = snd_kernel_minor(type,card,dev);
/**
检查分配的minor,snd_minors[minor]是否为空缺
为下面将snd_minors放入奥snd_minors[]中做准备
*/
if (minor >= 0 && snd_minors[minor])
{
minor = -EBUSY;
}
#endif
if (minor < 0)
{
mutex_unlock(&sound_mutex);
kfree(preg);
return minor;
}
/**
#define SNDRV_OS_MINORS 256
static struct snd_minor *snd_minors[SNDRV_OS_MINORS];
将该snd_minor放到全局数组snd_minors[]中,位置是minor为索引值
问题:
snd_minors[]一定会在以后的其他函数中根据minor为索引值取出来,(如:上面的额open函数)
这个指针数组的设计在内核的其他子系统中都有,注册的时候往里放,open或者其他函数从里面依据minor取出来
lcd子系统中的fb_info、input子系统中的input_table、v4l2的video_device 都是这样的设计
既然是共享的资源,那么一定要使用互斥机制来保护这些临界区,发现他们都是使用的互斥锁来保护的。
*/
snd_minors[minor] = preg;
/**
创建设备文件节点
那么,对应的class_create()创建类是再什么时候被调用的?
*/
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(&soun_mutex);
return 0;
}
那么是谁调用了snd_register_device_for_dev()这个函数呢,源头在哪里?
调用流程图