zoukankan      html  css  js  c++  java
  • Linux字符驱动中动态分配设备号与动态生成设备节点

    转自:http://www.cnblogs.com/zhuyp1015/archive/2012/05/22/2514008.html  bird1015的博客

    在编写Linux内核驱动程序的时候,如果不动态生成设备号的话,需要自己手动分配设备号,有可能你分配的设备号会与已有设备号相同而产生冲突。因此推荐自动分配设备号。使用下面的函数:

    int alloc_chrdev_region(dev_t *dev,  unsigned baseminor,  unsigned count,  const char *name)

    该函数需要传递给它指定的第一个次设备号baseminor(一般为0)和要分配的设备数count,以及设备名,调用该函数后自动分配得到的设备号保存在dev中。

    当使用了alloc_chrdev_region()动态分配设备号之后,需要依次使用:

    cdev_init(struct cdev * cdev,const struct file_operations * fops)

    cdev_add(struct cdev * p,dev_t dev,unsigned count)

    将字符设备注册到内核中。通过上面三个函数就可以动态生成设备号了。

    在卸载的时候需要使用:unregister_chrdev_region(dev_t from,unsigned count) 来释放设备编号

    动态创建设备号之后,将驱动加载到内核,通过 : cat /proc/devices   命令可以查看设备号

    如果上层应用程序需要访问驱动程序,则需要为该驱动创建设备节点。

    如果手动创建设备结点需要这样做:(这里假设通过 cat /proc/devices 发现字符设备 CDEV_ZHU的设备号为 254)

    $mknod  /dev/CDEV_ZHU c 254 0

    如果我们在驱动里面动态创建的话需要这样做:

    cdev_class = class_create(owner,name)         // cdev_class 为 struct class 类型

    然后使用:

    device_create(_cls,_parent,_devt,_device,_fmt)

    当动态创建了设备节点之后,在卸载的时候需要使用:

    device_destroy(_cls,_device)  和 class_destroy(struct class * cls)

    来销毁设备和类。

    下面给出一组测试代码:(该组代码实现了应用程序通过打开驱动访问和修改驱动的一个全局变量 “global_var”)

    /*驱动部分:globalvar.c */

    #include <linux/module.h> 
    #include <linux/init.h> 
    #include <linux/fs.h> 
    #include <asm/uaccess.h> 
    #include <asm/device.h>  //下面这三个头文件是由于动态创建需要加的
    #include <linux/device.h>
    #include <linux/cdev.h>

     
    MODULE_LICENSE("GPL"); 
     
    #define DEVICE_NAME  "CDEV_ZHU"
    static struct class *cdev_class; 
     
    static ssize_t globalvar_read(struct file *, char *, size_t, loff_t*); 
    static ssize_t globalvar_write(struct file *, const char *, size_t, loff_t*); 
     
    //初始化字符设备驱动的 file_operations 结构体 
    struct file_operations globalvar_fops =  

        read: globalvar_read, 
        write: globalvar_write, 
    };

    static int global_var = 0;      //CDEV_ZHU设备的全局变量

    dev_t dev = 0;                 //这里是动态分配设备号和动态创建设备结点需要用到的
    struct cdev  dev_c;
     
    static int __init globalvar_init(void) 

        int ret,err; 
     
        //注册设备驱动 
     
        ret = alloc_chrdev_region(&dev, 0, 1,DEVICE_NAME); //动态分配设备号
        if (ret) 
        { 
            printk("globalvar register failure\n"); 
        unregister_chrdev_region(dev,1);
        return ret;
        } 
        else 
        { 
            printk("globalvar register success\n"); 
        }

       cdev_init(&dev_c, &globalvar_fops);
     
       err = cdev_add(&dev_c, dev, 1);

       if(err)
       {
        printk(KERN_NOTICE "error %d adding FC_dev\n",err);
        unregister_chrdev_region(dev, 1);
        return err;
       }
     
     cdev_class = class_create(THIS_MODULE, DEVICE_NAME);//动态创建设备结点
     if(IS_ERR(cdev_class))
     {  
            printk("ERR:cannot create a cdev_class\n");  
        unregister_chrdev_region(dev, 1);
        return -1;
        }
     device_create(cdev_class,NULL, dev, 0, DEVICE_NAME);
     
        return ret; 

     
    static void __exit globalvar_exit(void) 

     
        //注销设备驱动 
        
     device_destroy(cdev_class, dev);
     class_destroy(cdev_class);
     unregister_chrdev_region(dev,1);
     printk("globalvar_exit \n");

     
    static ssize_t globalvar_read(struct file *filp, char *buf, size_t len, loff_t *off) 

        //将 global_var 从内核空间复制到用户空间 
        if(copy_to_user(buf, &global_var, sizeof(int))) 
        { 
            return    - EFAULT;     
        }   
        return sizeof(int); 

     
    static ssize_t globalvar_write(struct file *filp, const char *buf, size_t len, loff_t *off) 

        //将用户空间的数据复制到内核空间的 global_var 
        if(copy_from_user(&global_var, buf, sizeof(int))) 
        { 
            return    - EFAULT; 
        }   
        return sizeof(int); 

     
    module_init(globalvar_init); 
    module_exit(globalvar_exit);

    /*应用程序: globalvartest.c  */

    #include <sys/types.h> 
    #include <sys/stat.h> 
    #include <stdio.h> 
    #include <fcntl.h> 
    int main() 

        int fd, num; 
        //打开"/dev/CDEV_ZHU"
        fd = open("/dev/CDEV_ZHU", O_RDWR, S_IRUSR | S_IWUSR); 
        if (fd != -1 ) 
        { 
          //初次读 global_var 
            read(fd, &num, sizeof(int)); 
            printf("The globalvar is %d\n", num); 
     
          //写 global_var 
            printf("Please input the num written to globalvar\n"); 
            scanf("%d", &num); 
            write(fd, &num, sizeof(int)); 
     
          //再次读 global_var 
            read(fd, &num, sizeof(int)); 
            printf("The globalvar is %d\n", num); 
     
            //关闭“/dev/CDEV_ZHU” 
            close(fd); 
        } 
        else 
        { 
            printf("Device open failure\n"); 
        }

        return 0;
    }

    说明:这个程序是我修改了“深入浅出Linux设备编程”这本书的代码的来的,在项目中使用动态创建设备节点和动态生成设备号比较方便,于是就在这里分享了。

    使用一个简单的makefile将(驱动) globalvar.c  编译过后 使用 insmod globalvar.ko 将驱动加载到内核,然后就将globalvartest.c 生成的可执行文件运行起来就可以操作驱动中的全局变量了。不用像书上一样还要在命令行去创建设备节点。

  • 相关阅读:
    memmove 的实现
    [转]SGI STL 红黑树(Red-Black Tree)源代码分析
    [转]让我看了很有感触
    [转]C++ list 类学习笔记
    [转]码农自白:这样成为谷歌工程师
    [转]Traits 编程技法+模板偏特化+template参数推导+内嵌型别编程技巧
    泛型指针,原生指针和智能指针
    [转]C++基本功和 Design Pattern系列 ctor & dtor
    python+opencv滤波操作
    python+opencv阈值
  • 原文地址:https://www.cnblogs.com/rzq232/p/3064795.html
Copyright © 2011-2022 走看看