zoukankan      html  css  js  c++  java
  • 简单入门linux设备驱动之第三部分:向设备驱动程序传递参数

    声明:内容搬自阿三哥网站,只是翻译了一下。侵删。https://embetronicx.com/tutorials/linux/device-drivers/

    正文如下:

    这是“linux设备驱动系列”的教程。本系列的目的是提供简单实用的示例,使每个人都能以简单的方式理解这些概念。现在让我们即将学习“linux设备驱动第三部分:向设备驱动程序传递参数”。

    内容速览

    ·1 linux设备驱动第三部分:向设备驱动程序传递参数

    ·2 模块参数宏

      ·2.1 module_param()

      ·2.2 module_param_array()

      ·2.3 module_param_cb()

      ·2.3.1 我们什么时候需要通知(notification)?

    ·3 编程
    ·4 编译
    ·5 加载设备驱动程序
    ·6 使用 dmesg 命令验证参数
    ·7 卸载设备驱动程序

     

    ·1 linux设备驱动第三部分:向设备驱动程序传递参数

    在同一个程序中,我们可以向任何函数传递参数。但是有没有可能向任意程序传递参数呢?我觉得可能有。对吗?是的,我们可以。在C语言的编程中,我们可以向程序传递参数。为了实现这个功能,我们需要在main()函数的定义中添加 argc 和 argv ;我想大家应该都知道这个。现在回到我们的话题,我们能够向设备驱动程序传递参数吗?好的,在这一小节的教程中,我们会关注这个话题。

    ·2 模块参数宏

    ·module_param()

    ·module_param_array()

    ·module_param_cb()

    在讨论这些宏之前,我们必须要先了解变量的权限。

    权限有以下几种类型:

    ·S_IWUSR

    ·S_IRUSR

    ·S_IXUSR

    ·S_IRGRP

    ·S_IWGRP

    ·S_IXGRP

    其中的 S_I 是公共的头(header)。

    R = 读(read),W = 写(write),X = 执行(execute)。

    USR = 用户(user),GRP = 组(group)

    使用或运算符“|”我们可以一次设置多个权限。

    ·2.1 module_param()

    这个宏用来初始化变量。module_param()需要三个参数:变量名、变量的类型、变量在sysfs入口的权限。这个宏应该放在函数的外部;通常是放在源文件的头部附近。module_param()宏定义在linux/moduleparam.h中。

            module_param(name, type, perm); 【perm-->permission】

    module_param()宏会在/sys/module目录下创建一个下级目录。举个栗子:

            module_param(valueETX, int, S_IWUSR|S_IRUSR);

    它会创建一个sysfs入口。(/sys/module/hello_world_module/parameters/valueETX)

    模块参数支持很多种参数类型:

    ·bool  布尔变量(true/false)。

    ·invbool  invbool类型取布尔类型的相反值,比如true值会变成false值,false值会变成true值。

    ·charp  字符指针(char pointer)变量。系统会给用户提供的字符串分配内存,指针也被相应的赋值。

    ·int

    ·long

    ·short

    ·uint

    ·ulong

    ·ushort  不同长度的基本整数类型。u开头的变量表示为无符号值。

    ·2.2 module_param_array()

    这个宏可以使用数组来传递参数。模块加载器支持数组参数;参数的值之间由逗号分隔。声明一个数组:

            module_param_array(name, type, num, perm);

    其中:name--数组的名字,

       type--数组元素的类型,

       num--一个整数变量值(可选),不配置时使用NULL,

       perm--权限配置。

    ·2.3 module_param_cb()

    这个宏用来注册参数被改变时的回调函数(callback)。我猜你可能不太能理解。让我来解释一番。

    举个栗子,

    我用module_param()函数创建了一个参数。

    module_param(valueETX, int, S_IWUSR|S_IRUSR);

    你可以在命令行改变valueETX的值,如下:

    >>sudo su
    >>echo 1 > /sys/module/hello_world_module/parameters/valueETX

    它会更新变量valueETX的值。但是没有办法通知你的模块:valueETX的值已经改变了。

    使用module_param_cb(),我们就能收到通知。

    如果你想你的变量发生改变时你能收到通知,我们就需要注册我们的处理函数到文件操作结构体中。

    struct kernel_param_ops {
            int (*set)(const char *val, const struct kernel_param *kp);
            int (*get)(char *buffer, const struct kernel_param *kp);
            void (*free)(void *arg);
    };

     更详细的解释请参考之后的程序。

    ·2.3.1 我们什么时候需要通知(notification)?

    我来告诉你实际应用场景。无论什么时候变量被置为1,你都必须往硬件寄存器写入一些东西。如果改变了变量但是没有人通知你你要怎么办呢?我想你应该明白了。如果你还没理解,请看下面的解释吧。

    ·3 编程

    在这个例子里,我们解释了一切(module_param, module_param_array, module_param_cb)。

    对于module_param(),我创建了两个变量。一个是整数(valueETX),另一个是字符串(namaETX)。

    对于module_param_array(),我创建了一个整数数组变量(arr_valueETX)。

    对于module_param_cb(),我创建了一个整数变量(cb_valueETX)。

    你可以使用sysfs入口改变所有变量的值,入口位于/sys/module/hello_world_module/parameters/目录下。

    但是你改变时不会受到任何通知,除了你使用module_param_cb()宏创建的变量。

    github上获取源代码

    /***************************************************************************//**
    *  file       hello_world.c
    *
    *  details    Simple hello world driver
    *
    *  author     EmbeTronicX
    *
    * *******************************************************************************/
    #include<linux/kernel.h>
    #include<linux/init.h>
    #include<linux/module.h>
    #include<linux/moduleparam.h>
     
    int valueETX, arr_valueETX[4];
    char *nameETX;
    int cb_valueETX = 0;
     
    module_param(valueETX, int, S_IRUSR|S_IWUSR);                      //integer value
    module_param(nameETX, charp, S_IRUSR|S_IWUSR);                     //String
    module_param_array(arr_valueETX, int, NULL, S_IRUSR|S_IWUSR);      //Array of integers
     
    /*----------------------Module_param_cb()--------------------------------*/
    int notify_param(const char *val, const struct kernel_param *kp)
    {
            int res = param_set_int(val, kp); // Use helper for write variable
            if(res==0) {
                    printk(KERN_INFO "Call back function called...
    ");
                    printk(KERN_INFO "New value of cb_valueETX = %d
    ", cb_valueETX);
                    return 0;
            }
            return -1;
    }
     
    const struct kernel_param_ops my_param_ops = 
    {
            .set = &notify_param, // Use our setter ...
            .get = &param_get_int, // .. and standard getter
    };
     
    module_param_cb(cb_valueETX, &my_param_ops, &cb_valueETX, S_IRUGO|S_IWUSR );
    /*-------------------------------------------------------------------------*/
    
    /*
    ** Module init function
    */
    static int __init hello_world_init(void)
    {
            int i;
            printk(KERN_INFO "ValueETX = %d  
    ", valueETX);
            printk(KERN_INFO "cb_valueETX = %d  
    ", cb_valueETX);
            printk(KERN_INFO "NameETX = %s 
    ", nameETX);
            for (i = 0; i < (sizeof arr_valueETX / sizeof (int)); i++) {
                    printk(KERN_INFO "Arr_value[%d] = %d
    ", i, arr_valueETX[i]);
            }
            printk(KERN_INFO "Kernel Module Inserted Successfully...
    ");
        return 0;
    }
    
    /*
    ** Module Exit function
    */
    static void __exit hello_world_exit(void)
    {
        printk(KERN_INFO "Kernel Module Removed Successfully...
    ");
    }
     
    module_init(hello_world_init);
    module_exit(hello_world_exit);
     
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("EmbeTronicX <embetronicx@gmail.com>");
    MODULE_DESCRIPTION("A simple hello world driver");
    MODULE_VERSION("1.0");

    ·4 编译

    Makefile代码如下

    obj-m += hello_world_module.o
    
    KDIR = /lib/modules/$(shell uname -r)/build
    
    all:
        make -C $(KDIR)  M=$(shell pwd) modules
    
    clean:
        make -C $(KDIR)  M=$(shell pwd) clean

    在终端输入 sudo make

    ·5 加载设备驱动程序

     sudo insmod hello_world_module.ko valueETX=14 nameETX="EmbeTronicX" arr_valueETX=100,102,104,106

     

    ·6 使用 dmesg 命令验证参数

     现在我们的模块已经加载好了。使用 dmesg 命令查看。下面的图片中,每个值都被传递到了我们的设备驱动程序中。

     

     现在我们来检验一下module_param_cb()是否被调用。为此,我要在sysfs中改变变量的值。你有两种方法来写这个变量。

    1.  sudo sh -c "echo 13 > /sys/module/driver/parameters/cb_valueETX"

    2. 输入 sudo su 。输入密码如果需要的话。然后输入  echo 13 > /sus/module/hello_world_module/parameters/cb_valueETX

    现在使用 demsg 命令检验。

     结果如上。我们的回调函数已经被调用了。但是你改变其他变量的值,你却不会收到通知。

     ·7 卸载设备驱动程序

     最后使用命令 sudo rmmod hello_world_module 卸载驱动程序。

    ---------------------分割线-------------------

    我希望大家都已经理解了。如果你有任何疑问,请在下面评论。在下一小节中,我们会了解linux设备驱动程序中的major号和minor号。

    -------------BE SEXY AND BRAINY---------
  • 相关阅读:
    捕捉整个桌面的图片
    在Image控件中绘制文字
    绘图
    Image1.Canvas画图笔刷
    将图片序列保存为GIF文件
    拷贝剪贴板图像到窗体
    显示 png 图片
    将图片以字符串方式保存
    复制图片的一部分
    反转字符串
  • 原文地址:https://www.cnblogs.com/yuxiaolan/p/14401061.html
Copyright © 2011-2022 走看看