zoukankan      html  css  js  c++  java
  • 2.制作第一个驱动程序

    先讲解驱动框架,然后写出first_drv驱动程序,来打印一些信息

    写出first_drv驱动程序需要以下几步:

    (1)写出驱动程序first_drv_open first_drv_write

    (2)需要定义file_operations结构体来封装驱动函数first_drv_open first_drv_write

    对于字符设备来说,常用file_operations以下几个成员:

    (3)  模块加载函数,通过函数 register_chrdev(major, “first_drv”, &first_drv_fops) 来

    注册字符设备

    (4)写驱动的first_drv_init 入口函数来调用这个register_chrdev()注册函数,

    (5)通过module_init()来修饰入口函数,使内核知道有这个函数

    (6)写驱动的first_drv_exit出口函数,调用这个unregister_chrdev()函数卸载,

    (7) 通过module_exit()来修饰出口函数

    (8) 模块许可证声明, 最常见的是以MODULE_LICENSE( "GPL v2" )来声明

    1.首先创建first_drv.c文件

    代码如下:

    #include <linux/module.h>
    
    #include <linux/kernel.h>
    
    #include <linux/fs.h>
    
    #include <linux/init.h>
    
    #include <linux/delay.h>
    
    #include <asm/irq.h>
    
    #include <asm/arch/regs-gpio.h>
    
    #include <asm/hardware.h>
    
    #include <asm/uaccess.h>
    
    #include <asm/io.h>
    
     
    /*1写出驱动程序first_drv_open first_drv_write */
    
    /*  inode结构表示具体的文件,file结构体用来追踪文件在运行时的状态信息。*/
    
    static int first_drv_open(struct inode *inode, struct file  *file)
    {
       printk(“first_drv_open
    ”);      //打印,在内核中打印只能用printk()
       return 0;
    }
    
    /*参数filp为目标文件结构体指针,buffer为要写入文件的信息缓冲区,count为要写入信息的长度,ppos为当前的偏移位置,这个值通常是用来判断写文件是否越界*/
    
    static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
    {
      printk(“first_drv_write
    ”);      //打印,在内核中打印只能用printk()
       return 0;
    }
    
     
    /*2定义file_operations结构体来封装驱动函数first_drv_open first_drv_write */
    
     static struct file_operations first_drv_fops = {
        .owner  =   THIS_MODULE,     //被使用时阻止模块被卸载
        .open   =   first_drv_open,      
        .write   =   first_drv_write,   
      };
    
     
    
    /*4写first_drv_init入口函数来调用这个register_chrdev()注册函数*/
    int first_drv_init(void)
    {
       /*3 register_chrdev注册字符设备,并设置major=111*/
      /*如果设置major为0,表示由内核动态分配主设备号,函数的返回值是主设备号*/
    register_chrdev (111, “first_drv”, &first_drv_fops); //111:主设备号,”first_drv”:设备名
    /*
    register_chrdev作用:在VFS虚拟文件系统中找到字符设备,然后通过主设备号找到内核数组里对应的位置,最后将设备名字和fops结构体填进去
    */
       return 0;
    }
    
    /*5 module_init修饰入口函数*/
    module_init(first_drv_init);
    
     
    /*6 写first_drv_exit出口函数*/
    void first_drv_exit(void)
    {
    unregister_chrdev (111, “first_drv”);  //卸载驱动,只需要主设备号和设备名就行 
    }
    
    /*7 module_exit修饰出口函数*/
    module_exit(first_drv_exit);
    
    /*8许可证声明, 描述内核模块的许可权限,如果不声明LICENSE,模块被加载时,将收到内核被污染 (kernel tainted)的警告。*/
    MODULE_LICENSE( "GPL v2" );

    2然后写Makefile编译脚本:

    KERN_DIR = /work/system/linux-2.6.22.6   //依赖的内核目录,前提内核是编译好的
     
    
    all:                                
           make -C $(KERN_DIR) M=`pwd` modules   
    // M=`pwd`:指定当前目录 //make -C $(KERN_DIR) 表示将进入(KERN_DIR)目录,执行该目录下的Makefile
    //等价于在linux-2.6.22.6目录下执行: make M=(当前目录) modules
    // modules:要编译的目标文件    clean: make -C $(KERN_DIR) M=`pwd` modules clean rm -rf modules.order obj-m += frist_drv.o //obj-m:内核模块文件,指将myleds.o编译成myleds.ko

    3. make,编译生成frist_drv.ko文件

     

    4.然后开发板通过nfs网络文件系统来加载frist_drv.ko

    加载之前首先通过 cat /proc/devices来查看字符主设备号111是否被占用

     

    然后通过 insmod first_drv.ko来挂载, 通过 cat /proc/devices就能看到first_drv已挂载好

     

    5.通过测试程序测试frist_drv模块

    测试程序first_driver_text.c代码如下

    #include <sys/types.h>    //调用sys目录下types.h文件
    #include <sys/stat.h>      //stat.h获取文件属性
    #include <fcntl.h>
    #include <stdio.h>
    
    /*输入”./first_driver_text”,     agc就等于1, argv[0]= first_driver_text  */
    /*输入”./first_driver_text on”,   agc就等于2, argv[0]= first_driver_text,argv[1]=on;  */
    
    int main(int argc,char **argv) 
    {
    int fd1, fd2;
    int val=1;
    fd1 = open("/dev/xxx",O_RDWR);  //打开/dev/xxx设备节点
    if(fd1<0)                   //无法打开,返回-1
      printf("can't open%d!
    ", fd1);
    else
       printf("can open%d!
    ", fd1);    //打开,返回文件描述符
    
    write(fd1,
    &val, 4);    //写入数据1 return 0; }

    6.然后arm-linux-gcc -o first_driver_text first_driver_text.c生成执行文件

    回到板子串口上使用./first_driver_text来运行,发现如果open()打不开,会返回-1

     

    是因为我们没有创建dev/xxx这个设备节点,然后我们来创建,使它等于刚刚挂载好的first_drv模块

    mknod -m 660 /dev/xxx c 111 0            // first_drv模块的主设备号=111

    ./first_driver_text

     

    结果如上图,发现测试程序里的open()函数调用了驱动中的first_drv_open()

    write()函数调用了驱动中的first_drv_write(),

    其中open()函数返回值为3,是因为描述符0,1,2都已经被控制台占用了,所以从3开始

    7.改进底层驱动,使用动态装载:

    除了静态装载驱动外,还可以动态装载,让系统自动为我们驱动设备自动分配设备号

    7.1 修改first_drv_init入口函数和first_drv_exit 出口函数:

    代码如下:

    int major;              //定义一个全局变量,用来保存主设备号
    int first_drv_init(void)
    {
     /*设置major为0,由内核动态分配主设备号,函数的返回值是主设备号*/
      major =register_chrdev (0, “first_drv”, &first_drv_fops);  
      return 0;
    
    }
    
    void first_drv_exit(void)
    {
    unregister_chrdev (major, “first_drv”);  //卸载驱动, 将major填入即可
    }

    如下图,通过动态分配得出它的主设备号是252,然后重创252的测试程序

     

    rm dev/xxx

    mknod -m 660 /dev/xxx c 252 0

    ./first_driver_text

     

    7.2 每次都要手工创建设备节点,大家肯定也会觉得这样做太麻烦了。

    可以使用自动创建设备节点,Linux有udev、mdev的机制,而我们的ARM开发板上移植的busybox有mdev机制,然后mdev机制会通过class类来找到相应类的驱动设备来自动创建设备节点 (前提需要有mdev)

    在哪里设置了mdev机制?

    制作根文件系统之使用里有介绍

    7.3 接下来使用insmod自动创建设备节点, rmmod自动注销设备节点

    (1)首先创建一个class设备类,class是一个设备的高级视图,它抽象出低级的实现细节,然后在class类下,创建一个class_device,即类下面创建类的设备:(在C语言中class就是个结构体)

    static struct class *firstdrv_class;               //创建一个class类
    static struct class_device   *firstdrv_class_devs; //创建类的设备

    (2)在first_drv_init入口函数中添加:

    firstdrv_class= class_create(THIS_MODULE,"firstdrv");  
    
    //创建类,它会在sys/class目录下创建firstdrv_class这个类
     
    
    firstdrv_class_devs=class_device_create(firstdrv_class,NULL,MKDEV(major,0),NULL,"xyz");
    
    //创建类设备,会在sys/class/firstdrv_class类下创建xyz设备,然后mdev通过这个自动创建/dev/xyz这个设备节点,            

    (3)在first_drv_exit出口函数中添加:

     class_device_unregister(firstdrv_class_devs);      //注销类设备,与class_device_create对应
      class_destroy(firstdrv_class);                    //注销类,与class_create对应

     重新编译insmod后,会发现在/dev下自动的创建了xyz设备节点

     

    其中在sys/class里有各种类的设备, 比如sys/class/fristdev下就有xyz

     

    然后mdv通过insmod xxx 就去class找到相应类的驱动设备来自动创建设备节点

    为什么内容一更改,mdv就能自动运行创建设备节点呢?

    是因为以前创建根文件系统时候,

    在etc/init.d/rcS里添加了这么一段:

    echo /sbin/mdev > /proc/sys/kernel/hotplug             //支持热拔插

    然后kernel每当设备出现变动时,调用/sbin/mdev来处理对应的信息,使mdev应用程序操作/dev目录下的设备,进行添加或删除

    (4).再修改测试程序里open函数,将/dev/xxx改为/dev/xyz,这样就测试模块,就不需要再mknod了.

     驱动程序first_drv_open first_drv_write中只是打印数据,接下来下一节便开始来点亮LED.

     

     

  • 相关阅读:
    Oracle导出导入表空间创建
    ASP.NET 缓存 SqlCacheDependency 监视数据库表变化 让缓存更新的更及时更提高节能
    Silverlight在添加WCF服务引用时报错
    springboot中如何动态更换 配置文件 spring.profiles.active
    maven之根据profile动态切换resource
    java synchronized 关键字的锁升级过程
    子类中的方法和父类同名,但是参数不同,是重写(overload)不是覆盖(override)
    Java的协变(extends)和逆变(super),说白了都是子类的实例赋值给父类的变量
    Mybatis缓存
    [转]Spring MVC之 @PathVariable @CookieValue@RequestParam @RequestBody @RequestHeader@SessionAttributes, @ModelAttribute
  • 原文地址:https://www.cnblogs.com/lifexy/p/7506277.html
Copyright © 2011-2022 走看看