zoukankan      html  css  js  c++  java
  • 《驱动学习

    1.1字符设备驱动基础

    字符设备驱动:设备对数据的处理是按照字节流的形式进行的。

    在linux中,“一切皆文件”(除了网络设备),这表示设备最终都会体现为一个文件。设备文件通常位于/dev目录下、

    内核通常用主设备号区别一类设备,次设备号用于区分同一类设备的不同个人或不同分区。

    手动创建设备文件

    mknod   /dev/vser0 c 256 0

    mknod是make node的缩写。用于创建一个节点(设备文件也叫设备节点)、在linux系统中,一个节点代表一个文件。

    1.2 字符设备驱动框架

     example 1.1

     1 #include <linux/init.h>
     2 #include <linux/kernel.h>
     3 #include <linux/module.h>
     4 
     5 #include <linux/fs.h>
     6 
     7 
     8 #define VSER_MAJOR     256
     9 #define VSER_MINOR     0
    10 #define VSER_DEV_CNT   1
    11 #define VSER_DEV_NAME  "vser"
    12 
    13 static int __init vser_init(void)
    14 {
    15     int ret;
    16     dev_t dev;
    17 
    18     /* MKDEV宏将主设备号和次设备号合并成一个设备号 */
    19     dev = MKDEV(VSER_MAJOR, VSER_MINOR);
    20 
    21     /* 将构造的设备号注册到内核中(静态) */
    22     ret = register_chrdev_region(dev, VSER_DEV_CNT, VSER_DEV_NAME);
    23     if(ret)
    24         goto reg_err;
    25     return 0;
    26 
    27 reg_err:
    28     return ret;
    29 }
    30 
    31 static void __exit vser_exit(void)
    32 {
    33     dev_t dev;
    34 
    35     dev = MKDEV(VSER_MAJOR, VSER_MINOR);
    36 
    37     /* 从内核中注销设备号 */
    38     unregister_chrdev_region(dev, VSER_DEV_CNT);
    39 }
    40 
    41 module_init(vser_init);
    42 module_exit(vser_exit);
    43 
    44 MODULE_LICENSE("GPL");

      注意:使用goto函数进行集中错误处理在驱动中非常常见,虽然这和一般的C语言编程规则相悖(因为C语言面向程序的语言,要求模块单入口,单出口,但是goto破坏了这种结构)。 

      register_chrdev_region注册设备号的方式称为静态注册设备号,这样如果两个驱动都是用同样的设备号,那么后加载的驱动将会失败,因为设备号冲突。可以使用动态分配设备号的方式避免这个问题。alloc_chrdev_region函数。

      MKDEV该宏的作用是将主设备号左移20位和次设备号相或。

      MAJOR和MINOR分别是从设备号中取出主设备号和次设备号。

    #define MINORBITS            20
    #define MINORMASK           ((1U << MINORBITS) - 1)
    
    
    #define MAJOR(dev)         ((unsigned int) ((dev) >> MINORBITS))       
    #define MINOR(dev)         ((unsigned int) ((dev) & MINORMASK)) 
    #define MKDEV(ma, mi)      ((ma) << MINORBITS | (mi))
    

    example 1.2

    #include <linux/init.h>
    #include <linux/kernel.h>
    #include <linux/module.h>
    
    #include <linux/fs.h>
    #include <linux/cdev.h>
    
    
    #define VSER_MAJOR     256
    #define VSER_MINOR     0
    #define VSER_DEV_CNT   1
    #define VSER_DEV_NAME  "vser"
    
    /* 代表一个具体得字符设备 */ static struct cdev vsdev;
    /* 操作该设备得一些方法 */
    static struct file_operations vser_ops = { .owner = THIS_MODULE, }; static int __init vser_init(void) { int ret; dev_t dev; /* MKDEV宏将主设备号和次设备号合并成一个设备号 */ dev = MKDEV(VSER_MAJOR, VSER_MINOR); /* 将构造的设备号注册到内核中(静态) */ ret = register_chrdev_region(dev, VSER_DEV_CNT, VSER_DEV_NAME); if(ret) goto reg_err; cdev_init(&vsdev, &vser_ops); vsdev.owner = THIS_MODULE;
       /* 初始化vsdev中得部分成员,将vsdev中得ops指针指向vser_ops */ ret
    = cdev_add(&vsdev, dev, VSER_DEV_CNT); if(ret) goto add_err; return 0; add_err: unregister_chrdev_region(dev, VSER_DEV_CNT); reg_err: return ret; } static void __exit vser_exit(void) { dev_t dev; dev = MKDEV(VSER_MAJOR, VSER_MINOR); /* 删除cdev对象 */ cdev_del(&vsdev); /* 从内核中注销设备号 */ unregister_chrdev_region(dev, VSER_DEV_CNT); } module_init(vser_init); module_exit(vser_exit); MODULE_LICENSE("GPL");

      cdev_init函数初始化了vsdev中得部分成员。另外一个最重要得操作就是将vsdev中得ops指针指向了vser_ops,这样通过设备号找到vsdev对象后,就能找到相关得操作方法集合,并调用其中的方法。

      owner是一个指向struct module类型变量的指针,THIS_MODULE是包含驱动的模块中的struct module类型对象的地址,类似于C++中的this指针。这样就能够通过vsdev或vser_fops找到对应的模块。

      cdev_add函数将cdev对象初始化后添加到内核中的cdev_map散列表中。

      在例1.2中的cdev对象是静态定义的,我们也可以进行动态分配,对应的函数是cdev_alloc。

  • 相关阅读:
    装箱、拆箱操作发生在
    @Data的注解使用以及在IDEA上安装
    Mysql中 BLOB字段转String的方法
    不属于java语言鲁棒性特点的是
    java object默认的基本方法
    哪个类可用于处理 Unicode?
    类和接口的继承
    抽象类的叙述:
    Hashtable 和 HashMap 的区别是:
    编程之美初赛第一场--焦距
  • 原文地址:https://www.cnblogs.com/zhuangquan/p/11223815.html
Copyright © 2011-2022 走看看