04 字符设备的注册和kmalloc的故事

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时调用。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值