zoukankan      html  css  js  c++  java
  • 驱动基础 —— 驱动编写、参数传递、符号导出

    目录:

    1、驱动模块开发

      编写驱动hello.c

      编写Makefile

      insmod加载KO模块

      lsmod查看系统加载的模块

      rmmod卸载模块

    2、模块ko参数传递

    3、ko模块符号导出

      新建math.c  math.h

      修改Makefile

      hello.c

      调用模块符号

      

    1、驱动模块开发

    驱动代码需要有四个部分
        1.头文件
        2.驱动模块装载和卸载函数入口声明
        3.实现模块装载和卸载函数入口
        4.GPL声明

      1) 编写hello.c

    /*
    驱动代码需要有四个部分
            1.头文件
            2.驱动模块装载和卸载函数入口声明
            3.实现模块装载和卸载函数入口
            4.GPL声明
    */
    //头文件
    #include <linux/init.h>
    #include <linux/module.h>
    
    //实现模块装载和卸载函数入口
    static int __init hello_drv_init(void)   
    {
        //向系统申请资源
        printk("-----------%s-------------
    ",__FUNCTION__);
    
        return 0;
    }
    
    static void __exit hello_drv_exit(void)
    {
        //释放资源
        printk("-----------%s-------------
    ",__FUNCTION__);
    
    }
    //驱动模块装载和卸载函数入口声明
    module_init(hello_drv_init);
    module_exit(hello_drv_exit);
    
    //GPL声明
    MODULE_LICENSE("GPL");

      2)编写Makefile

     1 ROOTFS_DIR = /home/linux/source/rootfs#根文件系统路径
     2 
     3 ifeq ($(KERNELRELEASE),)
     4 
     5 KERNEL_DIR = /home/linux/kernel/linux-3.14-fs4412          #编译过的内核源码的路径
     6 CUR_DIR = $(shell pwd)     #当前路径
     7 
     8 all:
     9     make -C $(KERNEL_DIR) M=$(CUR_DIR) modules  #把当前路径编成modules
    10     @#make -C 进入到内核路径
    11     @#M 指定当前路径(模块位置)
    12 
    13 clean:
    14     make -C $(KERNEL_DIR) M=$(CUR_DIR) clean
    15 
    16 install:
    17     sudo cp -raf *.ko $(ROOTFS_DIR)/drv_module     #把当前的所有.ko文件考到根文件系统的drv_module目录
    18 
    19 else
    20 
    21 obj-m += hello.o    #指定内核要把哪个文件编译成ko
    22 
    23 endif

    看一下Makefile:

      当我们make的时候会进入Makefile。

      第1行,指定ROOTFS_DIR的路径

      第3行,判断语句,判断后面括号里逗号两侧的目标是否相等,$(KERNELRELEASE)是内核目录下Makefile定义的一个宏,无实际意义,由于还没进入内核,故这个宏为空,故条件为真。执行下面的语句,执行到all:规则时,又进行了一次make,这一次进入了内核目录下的Makefile,同时给$(KERNELRELEASE)赋值,还指定了要去那个目录下编译模块,但是万一这个目录下有很多c文件,内核怎么知道把哪个文件编译成内核呢?由此内核会根据路径由回到当下的这个Makefile,读取信息,从第一行开始,此时条件语句不成立,执行else后的语句obj-m += hello.o,得知是把当前路径下的对应hello.o的C文件编译成ko文件

    all:   make -C $(KERNEL_DIR) M=$(CPU_DIR) modules
    -->  make  -C  /home/linux/kernel/linux-3.14-fs4412  M=pwq modules
    //进入到内核目录下的Makefile 将当前目录下的驱动编译为模块
    //但是内核又怎么知道编译哪个c文件呢?

    由此可知Makefile会被读两次

    1. 第一次是make的时候,这时候主要读取的路径
    2. 第二次是内核源码编译的时候,这时候,内核源码要知道要把该路径下哪个文件编译成ko

    以后使用Makefile时,修改相应的内核路径和源码路径即可。

    (注:内核源码要先编译)

      make之后,可以看到生成的相应ko文件,在make install,将ko文件复制到根文件系统下;

    3)insmod 加载ko模块

    加载模块后可以看到模块转载函数打印出相关信息:

    [root@farsight drv_module]# insmod hello.ko
    [   34.280000] -----------hello_drv_init-----------

    4)lsmod 查看当前模块

    [root@farsight drv_module]# lsmod
    hello 809 0 - Live 0xbf000000 (O)

    5)rmmod卸载驱动(不要加ko)

    [root@farsight drv_module]# rmmod hello
    [  228.895000] -----------hello_drv_exit-----------

    2、模块ko参数传递

      1)加载模块时insmod hello.ko myname="guoguo" myvalue=22 ,参数会传递给模块内部

      实例:

    wifi驱动:   wifi硬件中内部也运行内部代码,原厂开发,这些代码叫做固件--firmware.bin
    装载wifi驱动时,必须告诉固件文件在哪里
    insmod  rtxxx.ko path=/lib/modules/firmware/xxx.bin

      2)在代码中如何处理参数

     module_param(name, type, perm)
     //参数1:表示参数到名字,比如myname, myvalue
     //参数2:参数到类型, charp, int
     //参数3: /sys/modules/表示文件到权限: 0666

      3)用法

    module_param(myvalue, int, 0666);
    module_param(myname, charp, S_IRUGO|S_IWUGO|S_IXUGO);

      传参(当驱动内部已经定义了参数后,优先使用外部传入的参数)

      4)测试

    修改代码:

     1 /*
     2 驱动代码需要有四个部分
     3         1.头文件
     4         2.驱动模块装载和卸载函数入口声明
     5         3.实现模块装载和卸载函数入口
     6         4.GPL声明
     7 */
     8 #include <linux/init.h>
     9 #include <linux/module.h>
    10 
    11 static int myvalue = 56;
    12 static char *myname = "jack";
    13 
    14 //实现模块装载和卸载函数入口
    15 static int __init hello_drv_init(void)
    16 {
    17     //一般做系统申请资源
    18     printk("-----------%s-----------
    ", __FUNCTION__);  //打印函数名
    19     printk("name = %s, value = %d
    ", myname, myvalue);
    20     return 0;
    21 }
    22 
    23 static void __exit hello_drv_exit(void)
    24 {
    25     //一般做系统释放资源
    26     printk("-----------%s-----------
    ", __FUNCTION__);
    27 }
    28 
    29 //驱动模块装载和卸载函数入口声明
    30 module_init(hello_drv_init);
    31 module_exit(hello_drv_exit);
    32 
    33 module_param(myvalue, int, 0644);
    34 module_param(myname, charp, S_IRUGO|S_IWUSR);  
    35 
    36 MODULE_LICENSE("GPL");

    板上运行:

    使用默认参数:

    传入参数:

    3、module符号导出

      (部分参考:linux驱动开发之module导出符号

      驱动开发中,module 是基本的组成,在一个模块中定义的函数,如果想在另一个模块中进行调用,这个时候,就需要进行导出,称为导出符号。

      用法:

      所要导出的符号,是在一个模块中,也需要使用 modlue_init 和 module_exit 进行修饰这模块的入口函数。在需要导出符号的地方,使用 EXPORT_SYMBOL_GPL() 或EXPORT_SYMBOL() 将函数导出。在需要调用的地方,使用 extern 进行声明需要的函数。

      下面以实例分析:hello模块调用math模块中的运算函数

     1 /*
     2 驱动代码需要有四个部分
     3         1.头文件
     4         2.驱动模块装载和卸载函数入口声明
     5         3.实现模块装载和卸载函数入口
     6         4.GPL声明
     7 */
     8 
     9 #include <linux/init.h>
    10 #include <linux/module.h>
    11 #include <linux/stat.h>
    12 
    13 #include "math.h"
    14 
    15 static int myvalue = 56;
    16 static char *myname = "jack";
    17 
    18 //实现模块装载和卸载函数入口
    19 static int __init hello_drv_init(void)
    20 {
    21     //一般做系统申请资源
    22     printk("-----------%s-----------
    ", __FUNCTION__);  //打印函数名
    23     printk("name = %s, value = %d
    ", myname, myvalue); 
    24     printk("a+b = %d, a-b = %d
    ", my_add(8,2), my_sub(1,10));    //调用math.c中的函数
    25     
    26     return 0;
    27 }
    28 
    29 static void __exit hello_drv_exit(void)
    30 {
    31     //一般做系统释放资源
    32     printk("-----------%s-----------
    ", __FUNCTION__);
    33 }
    34 
    35 //驱动模块装载和卸载函数入口声明
    36 module_init(hello_drv_init);
    37 module_exit(hello_drv_exit);
    38 
    39 module_param(myvalue, int, 0644);
    40 module_param(myname, charp, S_IRUGO|S_IWUSR);  
    41 
    42 MODULE_LICENSE("GPL");
    hello.c
     1 #include <linux/module.h>
     2 #include <linux/init.h>
     3 
     4 //不需要模块的装载与卸载的入口声明
     5 //直接定义好封装的函数
     6 
     7 int my_add(int a, int b)
     8 {
     9     return a+b;
    10 }
    11 
    12 EXPORT_SYMBOL(my_add);
    13 
    14 int my_sub(int a, int b)
    15 {
    16     return a-b;
    17 }
    18 
    19 EXPORT_SYMBOL(my_sub);
    20 
    21 
    22 MODULE_LICENSE("GPL");
    math.c
    1 #ifndef __MATH_H__
    2 #define __MAHT_H__
    3 
    4 int my_add(int a, int b);
    5 int my_sub(int a, int b);
    6 
    7 #endif
    math.h
     1 ROOTFS_DIR = /home/linux/source/rootfs#根文件系统路径
     2 
     3 ifeq ($(KERNELRELEASE),)
     4 
     5 KERNEL_DIR = /home/linux/kernel/linux-3.14-fs4412          #编译过的内核源码的路径
     6 CUR_DIR = $(shell pwd)     #当前路径
     7 
     8 all:
     9     make -C $(KERNEL_DIR) M=$(CUR_DIR) modules  #把当前路径编成modules
    10     @#make -C 进入到内核路径
    11     @#M 指定当前路径(模块位置)
    12 
    13 clean:
    14     make -C $(KERNEL_DIR) M=$(CUR_DIR) clean
    15 
    16 install:
    17     sudo cp -raf *.ko $(ROOTFS_DIR)/drv_module     #把当前的所有.ko文件考到根文件系统的drv_module目录
    18 
    19 else
    20 
    21 obj-m += hello.o    #指定内核要把哪个文件编译成ko
    22 obj-m += math.o
    23 
    24 endif
    Makefile

    math.c也要作为模块编译,在Makefile也要添加math。

    注意:

      在编译时,导出模块先编译,使用者后编译; 
      在加载时,导出模块先加载,使用者后加载; 
      在卸载时,使用者先卸载,导出模块后卸载;

    若先装载使用模块,则报错:

     应先加载导出模块:

     若先卸载导出模块,报错如下:

  • 相关阅读:
    C#中的扩展方法
    对象的序列化存入数据库,与反序列化
    PowerDesigner15:EAM(Enterprise Architecture Model)企业架构模组
    WPF优化:加速启动时间
    LINQ优化:将GroupBy换做Distinct
    WPF:CheckBox竖向的滑块效果
    微軟提議﹕c#編程四個注意
    Remoting:于.net框架下的序列化機制
    c#編寫聖誕樹算法﹐及相關問題。
    72
  • 原文地址:https://www.cnblogs.com/y4247464/p/12369066.html
Copyright © 2011-2022 走看看