Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1255439
  • 博文数量: 261
  • 博客积分: 4196
  • 博客等级: 上校
  • 技术积分: 3410
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-17 17:05
文章分类

全部博文(261)

文章存档

2018年(1)

2017年(22)

2016年(2)

2015年(8)

2014年(27)

2013年(40)

2012年(161)

分类: LINUX

2013-01-24 14:37:28

led.h

 #define LED_ON(a)   ((a) > 0?1:0)
 #define LED_OFF(a)  ((a) > 0?0:1)


led.c

static int hw_vsion = 0;

……
gpio_direction_output(GpioId,LED_ON(hw_vsion));

……

module_param(hw_vsion, int, S_IRUGO);


//EXPORT_SYMBOL(hw_vsion);     //如果不需要将此变量导出,让其它驱动也可以用,则可以将其屏蔽


加载时:insmod led.ko hw_vsion=0(or 1)

此程序作用为根据传入参数不同,对宏定义LED_ON LED_OFF 分别设置为不同的值。


相关知识:

module_param():

一、在用户态下编程可以通过main()来传递命令行参数,而编写一个内核模块则可通过module_param()来传递命令行参数.


二、 module_param使用了3个参数:变量名,它的类型,以及一个权限掩码用来做一个辅助的sysfs入口。
这个宏定义应当放在任何函数之外,典型地是出现在源文件的前面。
eg: static char *whom="world"
    static int    tige=1;
    module_param(tiger,int,S_IRUGO);
    module_param(whom,charp,S_IRUGO);


三、模块参数支持许多类型:
byte
bool
invbool
一个布尔型( true 或者 false)值(相关的变量应当是 int 类型). invbool 类型颠倒了值, 所以真值变成 false, 反之亦然.
charp :一个字符指针值. 内存为用户提供的字串分配, 指针因此设置.
int
long
short
uint
ulong
ushort
基本的变长整型值. 以 u 开头的是无符号值.


四、数组参数, 用逗号间隔的列表提供的值, 模块加载者也支持。

声明一个数组参数, 使用:
module_param_array(name,type,num,perm);
这里 name 是你的数组的名子(也是参数名),
type 是数组元素的类型,
num 是一个整型变量,
perm 是通常的权限值.
如果数组参数在加载时设置, num 被设置成提供的数的个数. 模块加载者拒绝比数组能放下的多的值.


五、perm参数的作用是什么?
最后的 module_param 字段是一个权限值,表示此参数在sysfs文件系统中所对应的文件节点的属性。你应当使用 中定义的值. 这个值控制谁可以存取这些模块参数在 sysfs 中的表示.当perm为0时,表示此参数不存在 sysfs文件系统下对应的文件节点。 否则, 模块被加载后,在/sys/module/ 目录下将出现以此模块名命名的目录, 带有给定的权限.。
权限在include/linux/stat.h中有定义
比如:
#define S_IRWXU 00700
#define S_IRUSR 00400
#define S_IWUSR 00200
#define S_IXUSR 00100
#define S_IRWXG 00070
#define S_IRGRP 00040
#define S_IWGRP 00020
#define S_IXGRP 00010
#define S_IRWXO 00007
#define S_IROTH 00004
#define S_IWOTH 00002
#define S_IXOTH 00001
使 用 S_IRUGO 参数可以被所有人读取, 但是不能改变; S_IRUGO|S_IWUSR 允许 root 来改变参数. 注意, 如果一个参数被 sysfs 修改, 你的模块看到的参数值也改变了, 但是你的模块没有任何其他的通知. 你应当不要使模块参数可写, 除非你准备好检测这个改变并且因而作出反应.


EXPORT_SYMBOL();

EXPORT_SYMBOL只出现在2.6内核中,在2.4内核默认的非static 函数和变量都会自动导入到kernel 空间的, 都不用EXPORT_SYMBOL() 做标记的。
2.6就必须用EXPORT_SYMBOL() 来导出来(因为2.6默认不到处所有的符号)。


一、EXPORT_SYMBOL的作用是什么?
EXPORT_SYMBOL标签内定义的函数或者符号对全部内核代码公开,不用修改内核代码就可以在您的内核模块中直接调用,即使用EXPORT_SYMBOL可以将一个函数以符号的方式导出给其他模块使用。
这里要和System.map做一下对比:
System.map 中的是连接时的函数地址。连接完成以后,在2.6内核运行过程中,是不知道哪个符号在哪个地址的。
EXPORT_SYMBOL 的符号, 是把这些符号和对应的地址保存起来,在内核运行的过程中,可以找到这些符号对应的地址。而模块在加载过程中,其本质就是能动态连接到内核,如果在模块中引 用了内核或其它模块的符号,就要EXPORT_SYMBOL这些符号,这样才能找到对应的地址连接。

二、使用方法
   第一、在模块函数定义之后使用EXPORT_SYMBOL(函数名)
   第二、在掉用该函数的模块中使用extern对之声明
   第三、首先加载定义该函数的模块,再加载调用该函数的模块

另外,在编译调用某导出函数的模块时,往往会有WARNING: "****" [**********] undefined!
使用dmesg命令后会看到相同的信息。开始我以为只要有这个错误就不能加载模块,后来上网查了一下,发现这主要是因为在编译连接的时候还没有和内核打交 道,当然找不到symbol了,但是由于你生成的是一个内核模块,所以LD不提示error,而是给出一个warning,寄希望于在insmod的时 候,内核能够把这个symbol连接上。



三、使用范例

一个模块mod1中定义一个函数func1;在另外一个模块mod2中定义一个函数func2,func2调用func1。
在模块mod1中,EXPORT_SYMBOL(func1);
在模块mod2中,extern int func1();
就可以在mod2中调用func1了。


在编译模块时如果#include “xxx.h” 使用到了本地的头文件,在makefile中
PRINT_INC = 头文件所在目录                //标示头文件所在目录
EXTRA_CFLAGS += -I $(PRINT_INC)      // 表示在编译模块时需要添加目录

在编译mod2时需要用到mod1中产生的Module.symvers文件
使用:
KBUILD_EXTRA_SYMBOLS=/..../Module.symvers

KBUILD_EXTRA_SYMBOLS 包涵在编译mod1时,产生的符号表文件Module.symvers,这个文件列出mod1中函数的地址没在编译mod2时需要这个列表

阅读(1805) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~