linux驱动基础知识-白阳(二) 内核符号导出及模块参数

一、驱动头文件和额外信息

#include <linux/xxx> 平台无关
#include <asm/xxx> 平台相关
注:内核驱动需要指定一些模块信息
MODULE_LICENSE("GPL"); //指定模块遵循GPL协议,否则加载时出现告警,必加
否则内核一些遵循GPL的API无法调用。告警:module license "unspecified" taints kernel
MODULE_AUTHOR("baiy <[email protected]>"); //作者信息,选填
MODULE_DESCRIPTION("This is a test driver"); //描述信息,选填
MODULE_VERSION("1.0.0.0"); //版本描述,选填
可通过modinfo查看。 modinfo xxx.ko
 

二、内核符号的导出

允许内核模块导出模块中的函数或者变量,供其他内核模块引用:
也就是内核中某个驱动的变量或者函数,给另一个驱动使用
EXPORT_SYMBOL("符号名称");
EXPORT_SYMBOL_GPL("符号名称"); //这里的符号名称可以是变量名也可以是函数名
EXPORT_SYMBOL_GPL:导出的符号只能被拥有GPL许可支持的内核模块导出。
其次,头文件需要声明对应的符号, extern 函数原型/变量
说白了,在标C的基础上多了个EXPORT_SYMBOL_GPL("符号名称");
注:
把mod_a的Module.symvers放到mod_b的当前路径,从而编译mod_b,符号信息会自动连接进去.
或者在mod_b的makefile中使用KBUILD_EXTRA_SYMBOLS指定mod_a的Module.symvers, 如:
KBUILD_EXTRA_SYMBOLS=/mod_a/Module.symvers
编译mod_b时,搜索Module.symvers的路径是:
1, kernel source path, e.g. /usr/src/kernels/linux-2.6.28.10
2, makefile中M=所指定的路径, 它等效于变量KBUILD_EXTMOD的值
3, 变量KBUILD_EXTRA_SYMBOLS的值
注:
也就是几种方法:
1.编写的多个驱动在同一个目录。
2.当前驱动中加入KBUILD_EXTRA_SYMBOLS=/mod_a/Module.symvers 指定依赖驱动的符号库
尝试用modprobe加载模块
问题:如何想使用modprobe,必须先去安装,但这时候已经驱动内核分离,如何install?
Makefile中
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install INSTALL_MOD_PATH=/opt/
先删除以前驱动的依赖,rm -rf /opt/lib/*,然后将安装的mod拷贝进去。
强调:调用GPL的文件必须要支持GPL协议


三、如何将多个.c生成一个.ko?

a.c和b.c生成d.ko
Makefile中
obj-m := d.o
d-objs = a.o b.o
或者
obj-m := ${MODNAME}.ko
${MODNAME}-objs += helloworld1.o
${MODNAME}-objs += helloworld2.o
因为同一个驱动,所以无依赖关系了

 

四、内核的模块参数

内核模块参数就像C语言的argc和argv,作用是调用程序时传入

int main(int argc,char * argv[])
{
printf("argc = %d,argv[0] = %s\n",argc, argv[0]);
int data = stroul(argv[1],NULL,10);
return 0;
}
./a.out 1  //作用:可在执行时动态修改程序某个

模块参数声明:通过module_param修饰后,可在加载驱动时或以后,可动态修改变量值。
static int data = 2; //然后驱动加载时如何设置别的值?或修改值。
作用:可动态修改驱动中某个值
语法:
module_param(name,type,perm);
name:参数名称
type:参数数据类型
perm:用户对模块的操作权限

module_param_array(name,type,nump,type)
nump:数组元素个数的指针,如果数组参数在加载时设置,该值为加载时设置的数据个数,
不允许传递比数据允许个数更多的值。
如:数组元素个数为10,但如果只想修改前5(必须<=10)个,参数可以传递5所对应变量的地址
如果nump为NULL,则默认为为数组元素个数。一般都是NULL。
模块参数描述声明:
MODULE_PARM_DESC(_parm,desc);
parm:待增加描述内容的模块参数,也就是变量名
desc:对模块参数的描述声明
可通过modinfo查看。

支持数据的类型
bool
invbool
charp chat指针,
short
ushort
int
uint
long 长整型
ulong 无符号长整型

模块参数用户操作权限
S_I[R/W/X]USR
S_I[R/W/X]GRP
S_I[R/W/X]OTH
实际使用基本都是8进制数,如0666。如果权限是0,则就是读写执行都可。

模块参数对应的文件系统位置:
/sys/module/modulename/parameters目录
模块参数赋值:
定义变量时初始化
模块加载时初始化
直接修改模块参数文件内容


用法:
如何去改变这两个值?如何使用?

static int io = 0x300;
static int irq;	
module_param(io, int, 0);
module_param(irq, int, 0);

static int io[2];
static int irq[MAX_EL2_CARDS];
static int xcvr[MAX_EL2_CARDS];	/* choose int. or ext. xcvr */
static int cnt = 2
module_param_array(io, int, &cnt, 0);
module_param_array(irq, int, NULL, 0);
module_param_array(xcvr, int, NULL, 0);

static int myint = 100;
static unsigned short myshort = 1;
static char *mystring = "test";
static int myarray[2] = {100,200};
module_param(myint, int , 0);
module_param(myshort,ushort, 0644); //
module_param(mystring,charp,0644);
module_param_array(myarray,int , NULL,0);
//然后在入口函数打印,出口函数也打印下。

使用:
方法1:模块加载时初始化
insmod xxx.ko myint=1000 myshort=2 mystring="china" myarray=2000,3000
方法2:直接修改模块参数文件内容
加载完后,查看/sys/module/xxx/parameters/
会发现如果权限非0的情况,会生成对应变量的文件。文件存放变量的值
如果权限为0,则不生成文件。


经过测试,权限只能为0或者O644,不能写0666
baiy@baiy-ThinkPad-X230:parameters$ cat carrier_timeout
4
权限0则不会产生文件,减少内存核硬盘开销。只能在加载时修改。
如果非0,则可以动态修改 echo 10 > carrier_timeout ,可通过文件修改值。

示例代码:

Makefile:
# If KERNELRELEASE is defined, we've been invoked from the
# kernel build system and can use its language.
ifneq ($(KERNELRELEASE),)
	#这部分第二次执行
	obj-m += first.o #如果同一目录下希望生成多个.ko文件,则用+=
	# Otherwise we were called directly from the command
	# line; invoke the kernel build system.
else
	#这部分第一次执行
	#KERNELDIR ?= /lib/modules/$(shell uname -r)/build
	KERNELDIR ?=/home/baiy/workspace/x2-project/xilinx-kernel/arm64_out
	PWD := $(shell pwd)
default:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install INSTALL_MOD_PATH=${PWD}
endif

clean:
	rm -rf *.ko .*.ko.cmd  *.mod.c  *.mod.o .*.mod.o.cmd *.o .*.o.cmd modules.order  Module.symvers .tmp_versions lib
/*************************************************************************
    > File Name: first.c
    > Author: baiy
    > Mail: [email protected] 
    > Created Time: 2018年07月11日 星期三 09时54分24秒
	> Func: 
 ************************************************************************/

#include <linux/init.h>
#include <linux/module.h>

static int myint = 1;
static short myshort = 2;
static char * mystr = "hello";
static int myarr[3] = {4,5,6};

module_param(myint,int, 0644);

MODULE_PARM_DESC(myint,"test param int");

module_param(myshort,short,0);
MODULE_PARM_DESC(myshort,"test param short");

module_param(mystr,charp,0644);
MODULE_PARM_DESC(mystr,"test param str");

module_param_array(myarr,int, NULL, 0644);
MODULE_PARM_DESC(myarr,"test param arr");

static int __init first_init(void)
{
    printk("%s-%d,test driver init\n",__func__,__LINE__);
    printk("myint = %d, myshort =%d, mystr = %s, myarr[0]=%d\n",
            myint,myshort,mystr,myarr[0]);
    return 0;
}
static void __exit first_exit(void)
{
    printk("myint = %d, myshort =%d, mystr = %s, myarr[0]=%d\n",
            myint,myshort,mystr,myarr[0]);
    printk("%s-%d,test driver exit\n",__func__,__LINE__);
}

		
module_init(first_init);
module_exit(first_exit); //修饰入口和出口函数
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("test driver first");
MODULE_AUTHOR("baiyang <[email protected]>");
MODULE_VERSION("1.0.0.0");

注:makefile编译时最好写 make ARCH=xxx CROSS_COMPILE=xxxx,否则可能会出现各种问题

例如:make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi-

baiy@baiy-ThinkPad-T480:~/workspace/testcode/driver-test/test01$ modinfo first.ko
filename:       /home/baiy/workspace/testcode/driver-test/test01/first.ko
version:        1.0.0.0
author:         baiyang <[email protected]>
description:    test driver first
license:        GPL v2
srcversion:     631A246DF757E1CD0C42042
depends:        
vermagic:       4.9.0-g35cc3bb7-dirty SMP mod_unload aarch64
parm:           myint:test param int (int)
parm:           myshort:test param short (short)
parm:           mystr:test param str (charp)
parm:           myarr:test param arr (array of int)

模块参数测试用例如下:

/*************************************************************************
    > File Name: first.c
    > Author: baiy
    > Mail: [email protected] 
    > Created Time: 2018年07月11日 星期三 09时54分24秒
	> Func: 测试驱动模块的符号导出和模块参数
 ************************************************************************/
 
#include <linux/init.h>
#include <linux/module.h>
 
static int myint = 1;
static short myshort = 2;
static char * mystr = "hello";
static int myarr[3] = {4,5,6};
 
module_param(myint,int, 0644);
 
MODULE_PARM_DESC(myint,"test param int");
 
module_param(myshort,short,0);
MODULE_PARM_DESC(myshort,"test param short");
 
module_param(mystr,charp,0644);
MODULE_PARM_DESC(mystr,"test param str");
 
module_param_array(myarr,int, NULL, 0644);
MODULE_PARM_DESC(myarr,"test param arr");

int extern_num = 0x5A5A5A5A;
EXPORT_SYMBOL_GPL(extern_num);

void print_hello(void)
{
	printk("hello this is a module_extern func\n");
}
EXPORT_SYMBOL_GPL(print_hello);
void print_bye(void)
{
	printk("bye this is a module_extern func\n");
}
EXPORT_SYMBOL_GPL(print_bye);


static int __init first_init(void)
{
    printk("%s-%d,test driver init\n",__func__,__LINE__);
    printk("myint = %d, myshort =%d, mystr = %s, myarr[0]=%d\n",
            myint,myshort,mystr,myarr[0]);
    return 0;
}
static void __exit first_exit(void)
{
    printk("myint = %d, myshort =%d, mystr = %s, myarr[0]=%d\n",
            myint,myshort,mystr,myarr[0]);
    printk("%s-%d,test driver exit\n",__func__,__LINE__);
}


		
module_init(first_init);
module_exit(first_exit); //修饰入口和出口函数
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("test driver first");
MODULE_AUTHOR("baiyang <[email protected]>");
MODULE_VERSION("1.0.0.0");
#ifndef BAIYANG_HELLO_H
#define BAIYANG_HELLO_H

extern int extern_num ;
extern void print_hello(void);
extern void print_bye(void);


#endif
/*************************************************************************
    > File Name: baiyang_second.c
    > Author: baiy
    > Mail: [email protected] 
    > Created Time: 2018年07月11日 星期三 09时54分24秒
	> Func: 内核符号导出使用
 ************************************************************************/
 
#include <linux/init.h>
#include <linux/module.h>
#include "baiyang_hello.h"  //depend on baiyang_hello1.ko




static int __init second_init(void)
{
    printk("%s-%d,test driver init,extern_num = %#x\n",__func__,__LINE__,extern_num);
	print_hello();

    return 0;
}
static void __exit second_exit(void)
{
    printk("%s-%d,test driver exit\n",__func__,__LINE__);
	print_bye();
}


		
module_init(second_init);
module_exit(second_exit); //修饰入口和出口函数
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("test driver symbol export");
MODULE_AUTHOR("baiyang <[email protected]>");
MODULE_VERSION("1.0.0.0");
 
 
#Makefile:
#make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi-
# If KERNELRELEASE is defined, we've been invoked from the
# kernel build system and can use its language.
ifneq ($(KERNELRELEASE),)
	#这部分第二次执行
	obj-m += baiyang_hello1.o
	obj-m += baiyang_second.o
	# Otherwise we were called directly from the command
	# line; invoke the kernel build system.
else
	#这部分第一次执行
	#KERNELDIR ?= /lib/modules/$(shell uname -r)/build
	KERNELDIR ?=/home/baiy/code/iTop4412_Kernel_3.0
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install INSTALL_MOD_PATH=/home/baiy/code/driver-test/test01

	PWD := $(shell pwd)
default:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install INSTALL_MOD_PATH=${PWD}
endif
 
clean:
	rm -rf *.ko .*.ko.cmd  *.mod.c  *.mod.o .*.mod.o.cmd *.o .*.o.cmd modules.order  Module.symvers .tmp_versions lib

编译完成后,将对应的lib/modules和.ko拷贝到开发板的/lib/目录下,然后测试

[root@iTOP-4412]# modprobe  baiyang_hello1
[ 3389.442817] first_init-47,test driver init
[root@iTOP-4412]# modprobe -r baiyang_hello1
[ 3392.986735] first_exit-56,test driver exit
[root@iTOP-4412]# modprobe  baiyang-second
[ 3416.478349] baiyang_second: Unknown symbol print_bye (err 0)
[ 3416.482598] baiyang_second: Unknown symbol print_hello (err 0)
[ 3416.488428] baiyang_second: Unknown symbol extern_num (err 0)   //会尝试加载,
[ 3416.500307] first_init-47,test driver init                      //加载依赖驱动   
[ 3416.516069] second_init-18,test driver init,extern_num = 0x5a5a5a5a  //加载当前驱动
[ 3416.521490] hello this is a module_extern func
[root@iTOP-4412]# 
[root@iTOP-4412]# 
[root@iTOP-4412]# modprobe  baiyang-
[root@iTOP-4412]# lsmod
baiyang_second 778 0 - Live 0xbf030000
baiyang_hello1 2065 1 baiyang_second, Live 0xbf02c000
[root@iTOP-4412]# modprobe  -r baiyang-second
[ 3434.660606] second_exit-25,test driver exit                      //反向卸载,先卸载当前驱动
[ 3434.663312] bye this is a module_extern func
[ 3434.675901] first_exit-56,test driver exit                       //在卸载依赖驱动
[root@iTOP-4412]# lsmod
[root@iTOP-4412]# modprobe  baiyang_hello1
[ 3504.679562] first_init-47,test driver init
[root@iTOP-4412]# modprobe  baiyan-second
modprobe: module 'baiyan_second' not found
[root@iTOP-4412]# modprobe  baiyang-second
[ 3514.229651] hello this is a module_extern func
生成的依赖文件如下:
baiy@baiy-ThinkPad-X230:test01$ cat lib/modules/3.0.15/modules.dep
extra/baiyang_hello1.ko:
extra/baiyang-second.ko: extra/baiyang_hello1.ko

猜你喜欢

转载自blog.csdn.net/sven0223/article/details/81013731