1、字符设备注册的两种方法
(1)早期经典
int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);
void unregister_chrdev(unsigned int major, const char *name); ( 注:书上写的是int型,我在3.2内核上编译并查看原型为void)
声明fops
struct file_operations fops {
.owner = THIS_MODULE,
};
register_chrdev的调用:为给定的主设备号注册0~255作为次设备号,为每个设备建立一个对应的默认cdev结构。
unregister_chrdev:其中major、name 必须与传递给register_chrdev函数的值保持一致,否则会调用失败。
一些驱动程序通过此类方法注册字符设备,也省去了register_chrdev_region的调用。
8 int major = 250;
9 int minor = 25;
10 unsigned int dev_num = 1;
11 char *dev_name = "Scull";
12
13 static struct file_operations fops = {
14 .owner = THIS_MODULE,
15 };
16
17 static int __init scull_init(void)
18 {
19 int ret;
20
21 ret = register_chrdev(major, dev_name, &fops);
22 if(ret < 0) {
23 printk(KERN_ALERT "register_chrdev fail\n");
24 goto reg_fail;
25 }
26 printk(KERN_INFO "register_chrdev ok!\n");
27
28 return 0;
29
30 reg_fail:
31 return ret;
32
33 }
34 static void __exit scull_exit(void)
35 {
36 unregister_chrdev(major, dev_name);
37 printk(KERN_INFO "unregister_chrdev ok!\n");
38 }
39
40 module_init(scull_init);
41 module_exit(scull_exit);
(2)cdev结构
#include <linux/cdev.h>
运行时动态分配cdev结构
struct cdev cdev = cdev_alloc();
cdev->owner = THIS_MODULE;
cdev->ops = &cdev_fops;
静态分配cdev
struct cdev cdev;
void cdev_init(struct cdev *cdev, struct file_operations *fops);
cdev.owner = THIS_MODULE;
cdev设置好后,通过cdev_add告诉内核,设备可以操作了
int cdev_add(struct cdev *cdev, dev_t num, unsigned int count);
void cdev_del(struct cdev *cdev);
调用cdev_del后,就不能再访问cdev结构了。所以该操作常在exit时调用。
13 struct cdev my_cdev;
14 struct file_operations scull_fops = {
15 .owner = THIS_MODULE,
16 };
17
18 static void init_cdev(void)
19 {
20 int ret;
21 dev_t devno = MKDEV(major, minor);
22
23 cdev_init(&my_cdev, &scull_fops);
24 my_cdev.owner = THIS_MODULE;
25 ret = cdev_add(&my_cdev, devno, dev_num);
26 if (ret) {
27 printk(KERN_ALERT "cdev_add error\n");
28 goto err1;
29 }
30 printk(KERN_INFO "cdev_add ok\n");
31 err1:
32 unregister_chrdev_region(devno, dev_num);
33 }
34 static int __init scull_init(void)
35 {
36 dev_t devno = MKDEV(major, minor);
37 int ret;
38
39 ret = register_chrdev_region(devno, dev_num, dev_name);
40 if(ret < 0) {
41 printk(KERN_ALERT "register chrdev_region fail\n");
42 goto err1;
43 }
44 init_cdev();
45
46 printk(KERN_INFO "scull init ok\n");
47
48 return 0;
49 err1:
50 return ret;
51 }
52 static void __exit scull_exit(void)
53 {
54 dev_t devno = MKDEV(major, minor);
55
56 cdev_del(&my_cdev);
57 unregister_chrdev_region(devno, dev_num);
58
59 printk(KERN_INFO "scull exit ok! Googbye!");
60 }
61 module_init(scull_init);
62 module_exit(scull_exit);
注:驱动程序中习惯将设备封装为一个结构体:(这是今天纠结最久的一件事,甚至引爆了oops,当然最主要的原因在于C语言指针基础不牢,一定要好好记录出的几种错)
首先,封装一个设备结构体:(这个应该没有什么问题,很好地体现了驱动中面向对象的思想)
struct scull_dev {
int quantum;
int qset;
unsigned long size;
unsigned long access_key;
struct cdev my_cdev;
};
2、定义变量:(开始出错)
struct scull_dev *dev;
我最开始定义的不是指针,而是struct scull_dev dev。所以调用时&dev->my_cdev,编译出错:
invalid type argument of '->'(不是指针,怎么能用 ->呢)
后来我用了指针,直接调用;(很开心,编译通过,但是大灰狼来了。。。)
指针不给他分配空间怎么能直接用呢???当发生了这种错误,你修改后想卸载重新insmod恐怕也不行了,估计只能重启咯
3、后来我意识到应该kmalloc(可是我在函数外面定义的时候同时调用malloc,编译报错,我很纠结,查malloc的用法明明就是这么写的啊。)
struct scull_dev *dev = kmalloc(sizeof(struct scull_dev), GFP_KERNEL);
error: initializer element is not constant
4、我接着查看Linux源码中的一些kmalloc例子用法,发现malloc之后,都会进行检查如下:
30 if (!dev) {
31 printk(KERN_ALERT "kmalloc fail\n");
32 return -ENOMEM;
33 }
突然意识到我不在函数中写的话,return哪里啊,return毛线啊,于是仔细参考源码中使用kmalloc的用法,终于明白了应该怎么办。我被自己蠢哭了。
5、为了养成好习惯,在exit时,添加了kfree释放:kfree(&dev);结果大灰狼又来了,(基础不牢,真的地动山摇啊)明明是
kfree(dev);就可以了,非要画蛇添足。完全被自己蠢哭。
最后附上正确用法:
#include <linux/slab.h> //kmalloc需要的头文件
27 struct scull_dev *dev;
37 static void init_cdev(void) {
......
42 dev = kmalloc(sizeof(struct scull_dev), GFP_KERNEL);
43 if (!dev) {
44 printk(KERN_ALERT "kmalloc fail\n");
45 // return -ENOMEM;
46 }
......
}
77 static void __exit scull_exit(void)
{
........
81 cdev_del(&dev->my_cdev);
82 kfree(dev);
.......
}
(2)cdev结构
#include <linux/cdev.h>
运行时动态分配cdev结构
struct cdev cdev = cdev_alloc();
cdev->owner = THIS_MODULE;
cdev->ops = &cdev_fops;
静态分配cdev
struct cdev cdev;
void cdev_init(struct cdev *cdev, struct file_operations *fops);
cdev.owner = THIS_MODULE;
cdev设置好后,通过cdev_add告诉内核,设备可以操作了
int cdev_add(struct cdev *cdev, dev_t num, unsigned int count);
void cdev_del(struct cdev *cdev);
调用cdev_del后,就不能再访问cdev结构了。所以该操作常在exit时调用。