linux驱动开发之module导出符号

前言

驱动开发中,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中进行了使用。利用导出符号,实现了一种分层的效果。

总结

  • 导出模块先编译,使用者后编译
  • 先加载导出者,才能加载使用者
  • 先卸载使用者,才能卸载导出者

  • 合理的利用导出模块,可以实现公用的代码和平台相关的代码的一种分离,代码层次更加清晰。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值