前言
驱动开发中,module 是基本的组成,在一个模块中定义的函数,如果想在另一个模块中进行调用,这个时候,就需要进行导出,称为导出符号。
正文
我们所要导出的符号,是在一个模块中,也需要使用 modlue_init 和 module_exit 进行修饰这模块的入口函数。在需要导出符号的地方,使用 EXPORT_SYMBOL_GPL() 或EXPORT_SYMBOL() 将函数导出。在需要调用的地方,使用 extern 进行声明需要的函数。
下边通过一个示例进行分析,这个实例中有三个文件,分别是 export.c export.h used.c ,
在export.c中进行导出符号,在used.c中进行使用导出的符号。
示例
//export.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/moduleparam.h>
#include "export.h"
int demo_printf(void)
{
printk("%s\n",__func__);
printk("this is demo_printf");
}
//此处将demo_printf 使用EXPORT_SYMBOL_GPL进行导出
EXPORT_SYMBOL_GPL(demo_printf);
//入口函数
static int __init export_init(void)
{
printk("%s\n",__func__);
return 0;
}
//出口函数
static void __exit export_exit(void)
{
printk("%s\n",__func__);
}
module_init(export_init);
module_exit(export_exit);
MODULE_LICENSE("GPL");
//export.h
#ifndef _EXPORT_H
#define _EXPORT_H
//进行声明
extern int demo_printf(void);
struct export_content{
int val;
int (*ex_printf)(void);
};
#endif
//used.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/moduleparam.h>
#include "export.h"
static int __init used_init(void)
{
printk("%s\n",__func__);
//此处另外一个模块,在这个中,调用另外一个模块中的导出 demo_printf
demo_printf();
return 0;
}
static void __exit used_exit(void)
{
printk("%s\n",__func__);
}
module_init(used_init);
module_exit(used_exit);
MODULE_LICENSE("GPL");
在编译时,导出模块先编译,使用者后编译;
在加载时,导出模块先加载,使用者后加载;
在卸载时,使用者先卸载,导出模块后卸载;
以上是我们自己实现的一个导出模块的demo示例。那么现在让我们在内核中,找到相关的代码,进一步加深我们的认识,下边我们通过一个内核中的实例,来看看别人是怎么使用的;我们先找仅关注框架,不关注具体的内容。
实例
以下实例是用内核中三星提供的一个adc的实例,来研究
//Adc.h (linux-3.0.86\arch\arm\plat-samsung\include\plat)
//这个文件,是adc通用模块的一个头文件
//使用extern进行声明了导出的s3c_adc_star函数
extern int s3c_adc_start(struct s3c_adc_client *client,
unsigned int channel, unsigned int nr_samples,
wait_queue_head_t *pwake);
//Adc.c (linux-3.0.86\arch\arm\plat-samsung)
//导出模块所在的源文件adc.c
int s3c_adc_start(struct s3c_adc_client *client,
unsigned int channel, unsigned int nr_samples,
wait_queue_head_t *pwake)
{
struct adc_device *adc = adc_dev;
unsigned long flags;
BUG_ON(!adc);
if (client->is_ts && adc->ts_pend)
return -EAGAIN;
if (atomic_xchg(&client->running, 1)) {
WARN(1, "%s: %p is already running\n", __func__, client);
return -EAGAIN;
}
spin_lock_irqsave(&adc->lock, flags);
client->convert_cb = s3c_convert_done;
client->wait = pwake;
client->result = -1;
client->channel = channel;
client->nr_samples = nr_samples;
if (client->is_ts)
adc->ts_pend = client;
else
list_add_tail(&client->pend, &adc_pending);
if (!adc->cur)
s3c_adc_try(adc);
spin_unlock_irqrestore(&adc->lock, flags);
return 0;
}
//使用EXPORT_SYMBOL_GPL将s3c_adc_start进行导出
EXPORT_SYMBOL_GPL(s3c_adc_start);
//使用者 s3c2410_ts.c
S3c2410_ts.c (linux-3.0.86\drivers\input\touchscreen) 14389 2015/10/29
static void touch_timer_fire(unsigned long data)
{
unsigned long data0;
unsigned long data1;
bool down;
data0 = readl(ts.io + S3C2410_ADCDAT0);
data1 = readl(ts.io + S3C2410_ADCDAT1);
down = get_down(data0, data1);
if (down) {
if (ts.count == (1 << ts.shift)) {
ts.xp >>= ts.shift;
ts.yp >>= ts.shift;
if (ts.cal_enable)
ts_calibrate();
dev_dbg(ts.dev, "%s: X=%lu, Y=%lu, count=%d\n",
__func__, ts.xp, ts.yp, ts.count);
input_report_abs(ts.input, ABS_X, ts.xp);
input_report_abs(ts.input, ABS_Y, ts.yp);
input_report_key(ts.input, BTN_TOUCH, 1);
input_sync(ts.input);
ts.xp_pre = ts.xp;
ts.yp_pre = ts.yp;
ts.xp = 0;
ts.yp = 0;
ts.count = 0;
}
//此处使用adc.c中导出的s3c_adc_start函数
s3c_adc_start(ts.client, 0, 1 << ts.shift);
} else {
ts.xp = 0;
ts.yp = 0;
ts.count = 0;
input_report_abs(ts.input, ABS_X, ts.xp_pre);
input_report_abs(ts.input, ABS_Y, ts.yp_pre);
input_report_key(ts.input, BTN_TOUCH, 0);
input_sync(ts.input);
writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);
ts.request_done = true;
}
}
以上的代码中,可以看到三星的平台通用的文件中将s3c_adc_start进行了导出,在一个具体的s3c2410_ts.c中进行了使用。利用导出符号,实现了一种分层的效果。
总结
- 导出模块先编译,使用者后编译
- 先加载导出者,才能加载使用者
先卸载使用者,才能卸载导出者
合理的利用导出模块,可以实现公用的代码和平台相关的代码的一种分离,代码层次更加清晰。
作者:u013377887 发表于2017/3/23 19:39:52 原文链接
阅读:14 评论:0 查看评论