字符设备
2.6版本前使用的结构体和函数
[cpp] typedef __kernel_dev_t dev_t;
-
static inline int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)
-
static inline void unregister_chrdev(unsigned int major, const char *name)
-
MAJOR(dev_t dev)
-
MINOR(dev_t dev)
-
MKDEV(int major,int minor)
-
struct file_operations{
-
.....
-
}
typedef __kernel_dev_t dev_t;//字符设备的设备号
static inline int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)//注册字符设备到内核中,疑问1
static inline void unregister_chrdev(unsigned int major, const char *name)//注销字符设备
MAJOR(dev_t dev)//计算主设备号
MINOR(dev_t dev)//计算次设备号
MKDEV(int major,int minor)//根据major和minor算得dev_t
struct file_operations{
.....
}
在使用以上函数注册字符设备的时候比较简单,拿misc的实现函数举例,已知了dev_t的主设备号,所以可以直接调用register_chrdev将该设备注册到内核中。
-
static int __init misc_init(void)
-
{
-
int err;
-
-
#ifdef CONFIG_PROC_FS
-
proc_create("misc", 0, NULL, &misc_proc_fops);
-
#endif
-
misc_class = class_create(THIS_MODULE, "misc");
-
err = PTR_ERR(misc_class);
-
if (IS_ERR(misc_class))
-
goto fail_remove;
-
err = -EIO;
-
if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))
-
goto fail_printk;
-
misc_class->devnode = misc_devnode;return 0;
-
fail_printk:
-
printk("unable to get major %d for misc devices\n", MISC_MAJOR);
-
class_destroy(misc_class);
-
fail_remove:
-
remove_proc_entry("misc", NULL);
-
-
return err;
-
}
static int __init misc_init(void)
{
int err;
#ifdef CONFIG_PROC_FS
proc_create("misc", 0, NULL, &misc_proc_fops);
#endif
misc_class = class_create(THIS_MODULE, "misc");//dev下创建misc类,后面在具体设备进行misc_register是再执行device_creat注册具体的misc设备
err = PTR_ERR(misc_class);
if (IS_ERR(misc_class))
goto fail_remove;
err = -EIO;
if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))
goto fail_printk;
misc_class->devnode = misc_devnode;return 0;
fail_printk:
printk("unable to get major %d for misc devices\n", MISC_MAJOR);
class_destroy(misc_class);
fail_remove:
remove_proc_entry("misc", NULL);
return err;
}
2.6版本后使用的结构体和函数
-
dev_t;
-
MAJOR(dev_t dev)
-
MINOR(dev_t dev)
-
MKDEV(int major,int minor)
-
struct cdev {
-
struct kobject kobj;
-
struct module *owner;
-
const struct file_operations *ops;
-
struct list_head list;
-
dev_t dev;
-
unsigned int count;
-
};
-
void cdev_init(struct cdev *, const struct file_operations *);
-
struct cdev *cdev_alloc(void);
-
void cdev_put(struct cdev *p);
-
int cdev_add(struct cdev *, dev_t, unsigned);
-
void cdev_del(struct cdev *);
-
extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const charchar *);
-
extern int register_chrdev_region(dev_t, unsigned, const charchar *);
-
struct file_operations{
-
.....
-
}
dev_t;//字符设备的设备号
MAJOR(dev_t dev)//计算主设备号
MINOR(dev_t dev)//计算次设备号
MKDEV(int major,int minor)//根据major和minor算得dev_t
struct cdev { //新增了cdev结构体,可以看到cdev除了包含了dev_t成员,还有kobj相关成员,2.6正是引入了kobj模型
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
void cdev_init(struct cdev *, const struct file_operations *);//初始化cdev,主要是赋值cdev的ops成员
struct cdev *cdev_alloc(void);//动态分配一个cdev结构体,此时是程序中只定义了一个cdev指针的时候使用,如果定义了一个cdev结构体,使用cdev_init
void cdev_put(struct cdev *p);//释放alloc的cdev
int cdev_add(struct cdev *, dev_t, unsigned);//向系统注册字符设备
void cdev_del(struct cdev *);
extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const charchar *);//动态向系统申请设备号dev_t extern int register_chrdev_region(dev_t, unsigned, const charchar *);//已知dev_t时静态申请
struct file_operations{
.....
}
可以看出2.6以后的版本在注册字符设备时稍复杂些,还是举个例子,可以看到在2.6以后的版本中注册字符设备的过程是:申请设备号dev_t->cdev初始化->注册cdev到系统中,对比2.6以前的版本多了针对cdev的操作过程。
-
static int __init pc8736x_gpio_init(void)
-
{
-
int rc;
-
dev_t devid;
-
-
......
-
if (major) {
-
devid = MKDEV(major, 0);
-
rc = register_chrdev_region(devid, PC8736X_GPIO_CT, DEVNAME);
-
} else {
-
rc = alloc_chrdev_region(&devid, 0, PC8736X_GPIO_CT, DEVNAME);
-
major = MAJOR(devid);
-
}
-
-
if (rc < 0) {
-
dev_err(&pdev->dev, "register-chrdev failed: %d\n", rc);
-
goto undo_request_region;
-
}
-
if (!major) {
-
major = rc;
-
dev_dbg(&pdev->dev, "got dynamic major %d\n", major);
-
}
-
-
pc8736x_init_shadow();
-
-
-
cdev_init(&pc8736x_gpio_cdev, &pc8736x_gpio_fileops);
-
cdev_add(&pc8736x_gpio_cdev, devid, PC8736X_GPIO_CT);
-
-
return 0;
-
-
undo_request_region:
-
release_region(pc8736x_gpio_base, PC8736X_GPIO_RANGE);
-
undo_platform_dev_add:
-
platform_device_del(pdev);
-
undo_platform_dev_alloc:
-
platform_device_put(pdev);
-
-
return rc;
-
}
static int __init pc8736x_gpio_init(void)
{
int rc;
dev_t devid;
......
if (major) {
devid = MKDEV(major, 0);
rc = register_chrdev_region(devid, PC8736X_GPIO_CT, DEVNAME);
} else {
rc = alloc_chrdev_region(&devid, 0, PC8736X_GPIO_CT, DEVNAME);
major = MAJOR(devid);
}
if (rc < 0) {
dev_err(&pdev->dev, "register-chrdev failed: %d\n", rc);
goto undo_request_region;
}
if (!major) {
major = rc;
dev_dbg(&pdev->dev, "got dynamic major %d\n", major);
}
pc8736x_init_shadow();
/* ignore minor errs, and succeed */
cdev_init(&pc8736x_gpio_cdev, &pc8736x_gpio_fileops);
cdev_add(&pc8736x_gpio_cdev, devid, PC8736X_GPIO_CT);
return 0;
undo_request_region:
release_region(pc8736x_gpio_base, PC8736X_GPIO_RANGE);
undo_platform_dev_add:
platform_device_del(pdev);
undo_platform_dev_alloc:
platform_device_put(pdev);
return rc;
}
misc设备
misc使用的结构体和函数
misc设备其实也是字符设备,主不过misc设备驱动在字符设备的基础上又进行了一次封装,使用户可以更方便的使用。
-
struct miscdevice {
-
int minor;
-
const charchar *name;
-
const struct file_operations *fops;
-
struct list_head list;
-
struct device *parent;
-
struct device *this_device;
-
const charchar *nodename;
-
mode_t mode;
-
};
-
int misc_register(struct miscdevice * misc)
struct miscdevice {
int minor;
const charchar *name;
const struct file_operations *fops;//还是字符设备中的文件操作结构体,只不过misc结构体对其又封装了一次
struct list_head list;
struct device *parent;
struct device *this_device;
const charchar *nodename;
mode_t mode;
};
int misc_register(struct miscdevice * misc)
还是举个例子来看下,用户在注册misc设备的时候只需要:初始化file_operations结构体->初始化miscdevice结构体->调用misc_register将miscdevice注册到系统中就ok了。
-
static const struct file_operations mmtimer_fops = {
-
.owner = THIS_MODULE,
-
.mmap =mmtimer_mmap,
-
.unlocked_ioctl = mmtimer_ioctl,
-
.llseek = noop_llseek,
-
};
-
static struct miscdevice mmtimer_miscdev = {
-
SGI_MMTIMER,
-
MMTIMER_NAME,
-
&mmtimer_fops
-
};
-
static int __init mmtimer_init(void)
-
{
-
cnodeid_t node, maxn = -1;
-
-
......
-
if (request_irq(SGI_MMTIMER_VECTOR, mmtimer_interrupt, IRQF_PERCPU, MMTIMER_NAME, NULL)) {
-
printk(KERN_WARNING "%s: unable to allocate interrupt.",
-
MMTIMER_NAME);
-
goto out1;
-
}
-
-
if (misc_register(&mmtimer_miscdev)){
-
printk(KERN_ERR "%s: failed to register device\n",
-
MMTIMER_NAME);
-
goto out2;
-
}
-
......
-
-
return 0;
-
-
out3:
-
kfree(timers);
-
misc_deregister(&mmtimer_miscdev);
-
out2:
-
free_irq(SGI_MMTIMER_VECTOR, NULL);
-
out1:
-
return -1;
-
}
static const struct file_operations mmtimer_fops = {
.owner = THIS_MODULE,
.mmap =mmtimer_mmap,
.unlocked_ioctl = mmtimer_ioctl,
.llseek = noop_llseek,
};
static struct miscdevice mmtimer_miscdev = {
SGI_MMTIMER,
MMTIMER_NAME,
&mmtimer_fops
};
static int __init mmtimer_init(void)
{
cnodeid_t node, maxn = -1;
......
if (request_irq(SGI_MMTIMER_VECTOR, mmtimer_interrupt, IRQF_PERCPU, MMTIMER_NAME, NULL)) {
printk(KERN_WARNING "%s: unable to allocate interrupt.",
MMTIMER_NAME);
goto out1;
}
if (misc_register(&mmtimer_miscdev)){
printk(KERN_ERR "%s: failed to register device\n",
MMTIMER_NAME);
goto out2;
}
......
return 0;
out3:
kfree(timers);
misc_deregister(&mmtimer_miscdev);
out2:
free_irq(SGI_MMTIMER_VECTOR, NULL);
out1:
return -1;
}
那么到了这里就会有疑问,为什么linux还费劲的又造了一个misc设备呢?为什么不直接都使用字符设备驱动呢?
为什么要有misc设备
首先的好处就是方便,快捷,用户可以只初始化2个结构体,然后调用一个函数就可以创建一个misc设备了,本质上来说他也是个字符设备。
还有应该就是注册misc设备节约了主设备号的占用,linux中只提供了256个主设备号,本身内核中一些驱动已经占用了部分,试想如果有一大波设备都想以字符设备注册进内核,必然导致主设备号不够用的情况,而misc设备在初始化时占用主设备号10,当设备驱动以misc设备注册进内核的时候,只为其分配次设备号,主设备号不变。
为什么要有misc设备的观点从知乎上看来的,自己思考了下,总结下来,后面如果再想到会继续添加。
疑问1:该函数在fs.h中声明为static inline,静态的内联函数,既然是静态的,不是应该不能被其他文件内的函数调用?
其实该函数声明在.h文件中,那么,只要c文件包含了该头文件就可以调用了,也就是说在编译时,调用该函数的地方会被编译器将实际代码直接展开在该c文件中,那么在该c文件中该函数是static的,其他c文件没包含该头文件的是不可以使用该函数的。
阅读(1115) | 评论(0) | 转发(0) |