一、 驱动程序相关shell命令
1、insmod [选项] [驱动模块]
功能:将驱动程序加载到Linux内核中添加新用户
2、rmmod [选项] [驱动模块]
功能:将驱动程序从Linux内核删除
3、lsmod [驱动模块]
功能:查看内核存在哪些模块。本质是读取/proc/moduls文件
4、modinfo [选项] [驱动模块]
功能:用来查看模块信息、包含功能、作者信息等
5、dmesg [选项]
选项中 [-c] 清空打印日志信息
功能:查看printk函数打印信息
二、驱动程序框架
1、驱动程序(不管字符设备、块设备、网络设备)的框架必须包含如下三部分:
(1)、MODULE_LICENSE(“Dual BSD/GPL”);
表示源码开发,不用于商业用途。如不包含会打印如下信息:
module license 'unspecified' taints kernel
(2)、module_init(parame);
parame:实现什么驱动功能的函数名
驱动加载函数(宏),ismod命令会调用驱动程序module_init()。一般为驱动入口
(3)、module_exit(parame);
parame:退出什么驱动功能的函数名
驱动卸载函数(宏),rmmod命令会调用驱动程序module_exit()。一般为驱动出口
非必须组成部分:
MODULE_AUTHOR (author); —声明模块的作者
MODULE_DESCRIPTION (description); —声明模块的描述
MODULE_VERSION (version_string); —声明模块的版本
MODULE_DEVICE_TABLE (table_info); —声明模块的设备表
MODULE_ALIAS (alternate_name); —声明模块的别名
hello.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
MODULE_LICENSE("Dual BSD/GPL");
static int __init hello_init(void) //必须为 int __init xxxx(void)
{
int temp = 666;
printk(KERN_WARNING "hello_init %d \n",temp);
return temp;
}
static void __exit hello_exit(void) //必须为 void __exit xxxx(void)
{
printk(KERN_ALERT "hello_exit\n");
}
module_init(hello_init);
module_exit(hello_exit);
非必须
MODULE_AUTHOR("Y__Can");
MODULE_VERSION("1.0");
MODULE_DESCRIPTION("hello_world");
Makefile
ifneq ($(KERNELRELEASE),)
obj-m := hello.o #把对应 .c 文件改为 .o
else
KERNELDIR ?= /imx/linux-2.6.35.3#用于开发板
#KERNELDIR ?= /lib/modules/$(shell uname -r)/build # 用于虚拟机编译 路径为已编译过内核下的路径
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.symvers *.order
如果我们想由两个源文件(比如file1.c和file2.c )构造出一个名称为module.ko的模块, 则正确的makefile可如下编写:
obj-m := module.o
module-objs := file1.o file2.o
要点说明:
1、文件名字必须为Makefile
2、KERNELRELEASE 是在内核源码的顶层Makefile中定义的一个变量,内核发行的版本号
3、$(obj-m)表示对象文件(object files)编译成可加载的内核模块。
流程说明:
1、第一次进入Makefile,KERNELRELEASE没有被定义
2、执行else后面的语句,给KERNELDIR, PWD赋值
3、执行default,编译
3.1、Make -C选项进入内核源代码目录,找到顶层的Makefile
3.2、M=PWD,返回当前目录执行Makefile文件
3.3、这就是第二次进入这个Makefile,在这次,由于KERNELRELEASE变量已经定义,因此不需要进入else语言,在这里,obj-m:=hello.o,在这里内核会帮你处理一切,这句话是告诉内核,需要从hello.o创建一个驱动模型(module)模块参数
2、驱动模块参数
module_param (参数名,参数类型,参数读/写权限)
static char *whom = "world";
static int howmany = 1;
module_param(howmany, int, S_IRUGO);
module_param(whom, charp, S_IWUSR );
内核支持的模块参数类型包括:
byte、short、ushort、int、uint、long、ulong、charp(字符指针)、bool以‘u’开头的为无符号值。
参数3:
#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 其他用户对该文件具有执行权限
#define S_IRUGO (S_IRUSR|S_IRGRP|S_IROTH) 表示只可以读,不可以写
#define S_IWUGO (S_IWUSR|S_IWGRP|S_IWOTH) 表示可以写
$ insmod mydriver.ko whom="Student" howmany = 5//可使对应变量值发生改变
$echo x > howmany //修改运行后的值
3、驱动模块数组
module_param_array(数组名,数组类型,数组长,参数读/写权限)
static int int_array[6];
int narr;
module_param_array(int_array, int, &narr, 0644);
$insmod hello.ko int_array=128,256,512
4、驱动模块函数
EXPORT_SYMBOL(name);
EXPORT_SYMBOL_GPL(name); //_GPL 版本的宏定义的导出符号只能对 GPL 许可的模块可用
int add_integar(int a, int b)
{
return a+b;
}
int sub_integar(int a, int b)
{
return a-b;
}
EXPORT_SYMBOL(add_integar); //EXPORT 才可以在别的模块中调用
EXPORT_SYMBOL(sub_integar);
Makefile加上
KBUILD_EXTRA_SYMBOLS+=/home/lyc/imx/work/1/Module.symvers //路径为驱动.c下生成的Module.symvers
export KBUILD_EXTRA_SYMBOLS
调用者需要extern int add_integar(int a,int b);
# cat /proc/kallsyms | grep integar //查看可以调用的函数
源文件:
hello.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/moduleparam.h>
MODULE_LICENSE("Dual BSD/GPL");
//模块参数
static char *whom = "world";
static int howmany = 1;
module_param(howmany,int,S_IRUGO);
module_param(whom,charp,S_IWUSR);
//模块数组
static int int_array[6] = {0};
int narr;
module_param_array(int_array, int, &narr, 0644);
//模块函数
int add_integar(int a, int b)
{
return a+b;
}
int sub_integar(int a, int b)
{
return a-b;
}
EXPORT_SYMBOL(add_integar);
EXPORT_SYMBOL(sub_integar);
static int __init hello_init(void) //必须为 int __init xxxx(void)
{
int temp = 666;
printk(KERN_WARNING "hello_init %d %d %s \n",temp,howmany,whom);
printk("int_arr[0]=%d,int_arr[1]=%d\n",int_array[0],int_array[1]);
printk("narr=%d\n",narr);
return temp;
}
static void __exit hello_exit(void) //必须为 void __exit xxxx(void)
{
printk(KERN_ALERT "hello_exit\n");
}
module_init(hello_init);
module_exit(hello_exit);
非必须
MODULE_AUTHOR("Y__Can");
MODULE_VERSION("1.0");
MODULE_DESCRIPTION("hello_world");
hello1.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/moduleparam.h>
MODULE_LICENSE("Dual BSD/GPL");
extern int add_integar(int a, int b);
extern int sub_integar(int a, int b);
static int __init hello_init(void) //必须为 int __init xxxx(void)
{
printk("add_int=%d\n",add_integar(1,2));
printk("sub_int=%d\n",sub_integar(1,2));
return 0;
}
static void __exit hello_exit(void) //必须为 void __exit xxxx(void)
{
printk(KERN_ALERT "hello_exit\n");
}
module_init(hello_init);
module_exit(hello_exit);