关于register_chrdev函数
最近在alsa的架构,看的是一头雾水,尤其是关于设备注册的地方。仔细研究register_chrdev函数。
其在内核中的原型为:
int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops);
根据个人理解,该函数根据major(主设备号)申请并注册设备号,次设备号为0~255,因此理论上该驱动程序可以处理256个设备。主要是通过fops中的open函数指针实现。
file_operations的定义如下(省略了部分属性):
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
int (*readdir) (struct file *, void *, filldir_t);
...........................................
int (*open) (struct inode *, struct file *);
....................................
int (*fsetattr)(struct file *, struct iattr *);
};
通常情况下在open例程的实现中,要根据次设备号重新对file结构的fops属性重新复制,以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;
}
声卡的主设备号为116,该函数以116为主设备号,注册256个字符设备,对应的文件操作集为:snd_fops,定义如下:
static const struct file_operations snd_fops =
{
.owner = THIS_MODULE,
.open = snd_open
};
函数snd_open的主要执行代码如下:
if (minor >= ARRAY_SIZE(snd_minors))
return -ENODEV;
mptr = snd_minors[minor];
if (mptr == NULL) {
#ifdef CONFIG_MODULES
int dev = SNDRV_MINOR_DEVICE(minor);
if (dev == SNDRV_MINOR_CONTROL) {
/* /dev/aloadC? */
int card = SNDRV_MINOR_CARD(minor);
if (snd_cards[card] == NULL)
snd_request_card(card);
} else if (dev == SNDRV_MINOR_GLOBAL) {
/* /dev/aloadSEQ */
snd_request_other(minor);
}
#ifndef CONFIG_SND_DYNAMIC_MINORS
/* /dev/snd/{controlC?,seq} */
mptr = snd_minors[minor];
if (mptr == NULL)
#endif
#endif
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;
return -ENODEV;
}
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;
该函数根据此设备号从全局变量snd_minors中选择合适的f_op指针对file_fop重新赋值,选择正确的设备操作。
通过device_create添加新的设备。
阅读关于miscdev的代码可以发现,miscdev的实现也是基于 register_chrdev 和 device_create两个函数
阅读(1863) | 评论(0) | 转发(0) |