Linux驱动开发 实践1
将hello.c驱动移植到micro2440开发板运行
//Hello.c
#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> //模块申明 MODULE_LICENSE("Dual BSD/GPL"); static int hello_init(void) { printk("<0> Hello, world\n"); //类似于printf函数 return 0; } static void hello_exit(void) { printk("<0> Goodbye, cruel world\n"); } // module_init module_exit 都是宏定义 module_init(hello_init); //模块装载 相当于 main函数的作用 module_exit(hello_exit); //模块卸载
printk是在内核中运行的向控制台输出显示的函数,printk函数中可以使用附加不同的日志级别或消息优先级。
用printk,内核会根据日志级别,可能把消息打印到当前控制台上,这个控制台通常是一个字符模式的终端、一个串口打印机或是一个并口打印机。这些消息正常输出的前提是──日志输出级别小于console_loglevel(在内核中数字越小优先级越高)。
没有指定日志级别的printk语句默认采用的级别是 DEFAULT_ MESSAGE_LOGLEVEL(这个默认级别一般为<4>,即与KERN_WARNING在一个级别上),其定义在linux26/kernel/printk.c中可以找到。
日志级别一共有8个级别,printk的日志级别定义如下(在include/linux/kernel.h中):
#define KERN_EMERG 0
#define KERN_ALERT 1
#define KERN_CRIT 2
#define KERN_ERR 3
#define KERN_WARNING 4
#define KERN_NOTICE 5
#define KERN_INFO 6
#define KERN_DEBUG 7
本例子使用的最高级别0,如果用其他数值,可能在控制台显示不出打印信息。
//Makefile
//red hat
obj-m := hello.o //与c文件对应 #这个路径根据你主机上的linux内核版本及路径而定 #build是个符号链接,其实际指向内核源文件的路径 KERNELDIR := /lib/modules/2.6.18-194.el5/build PWD := $(shell pwd) modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules modules_install: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install clean: rm -f *.o *.ko *.mod.c *.markers *.symvers
//arm
obj-m := hello.o //与c文件对应 CC = arm-linux-gcc //指定交叉编译工具 默认 gcc #这个路径根据你主机上的linux内核版本及路径而定 #build是个符号链接,其实际指向内核源文件的路径 #KERNELDIR := /lib/modules/2.6.18-194.el5/build KERNELDIR := /root/Micro2440/linux-2.6.29 //需要跟开发板内核版本一致 PWD := $(shell pwd) modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules modules_install: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install clean: rm -f *.o *.ko *.mod.c *.markers *.symvers
测试:
Red hat 5.5
Insmod hello.ko //加载驱动
lsmod //查看当前加载的驱动
rmmod hello //卸载驱动
Micro2440 使用的是linux-2.6.29的内核版本,所以编译时需要与开发板内核版本一致
NFS挂载到开发板上运行时,能正常挂载,但是发现卸载不了。
[root@FriendlyARM module]# rmmod hello
rmmod: chdir(2.6.29.4-FriendlyARM): No suchfile or directory
解决方法是:创建 /lib/modlues/2.6.29.4-FriendlyARM 目录
能成功卸载了,出现rmmod: module 'hello' not found 不影响,应该是busybox的问题。
模块参数
在用户态下编程可以通过main()来传递命令行参数,而编写一个内核模块则可通过module_param()来传递命令行参数。module_param宏是Linux 2.6内核中新增的。相关的宏主要有:
module_param(name,type, perm);
module_param_named(name,value, type, perm)
模块参数的类型(即type)有以下几种:
bool
invbool :一个布尔型( true 或者 false)值(相关的变量应当是 int 类型). invbool 类型颠倒了值, 所以真值变成 false,反之亦然.
charp :一个字符指针值. 内存为用户提供的字串分配,指针因此设置.
int
long
short
uint
ulong
ushort
module_param使用了3个参数:变量名,它的类型,以及一个权限掩码用来做一个辅助的sysfs入口。
通过宏module_param指定保存模块参数的变量。模块参数
用于在加载模块时传递参数给模块。
module_param(name, type, perm)
name:变量的名称
type:变量类型,bool:布尔型 int:整型 charp:字符串型
perm是访问权限。 S_IRUGO:读权限 S_IWUSR:写权限
例:
int a = 3;
char *st;
module_param(a,int, S_IRUGO);
module_param(st,charp, S_IRUGO);
说白了就是传递参数而已,直接上代码
//hello.c
#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> MODULE_LICENSE("Dual BSD/GPL"); static char *whom = "world"; static int howmany = 1; static int hello_init(void) { int i=0; for(i = 0;i < howmany;i++) { printk("<0>%d:Hello, %s!\n",i+1,whom); } return 0; } static void hello_exit(void) { printk("<0>Goodbye, cruel world!\n"); } module_init(hello_init); module_exit(hello_exit); module_param(howmany, int, S_IRUGO); module_param(whom, charp, S_IRUGO); insmod hello_param.ko howmany=3 whom=”test” //特别注意 =号两边不要留空格