将Linux代码从用户态挪到内核的一种简便方法

背景

最近开发一款仪器,ARM和FPGA之间的通信接口是共享内存,于是之前的同事直接在用户态用mmap映射来读写FPGA,但是最近发现存在cache没法invalidate的缺陷,需要将代码挪到内核态,因为ioremap出来的内存默认是关闭cache的,省去了invalidate操作。

思路

没有必要开发专门的驱动,只需要注册一个自定义的misc设备即可

代码

#define pr_fmt(fmt) KBUILD_MODNAME ":%s:%d: " fmt, __func__, __LINE__
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/ioctl.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>

struct dab_ctx {
    void __iomem *cfg_base_addr;
};

static int dab_drv_open(struct inode *inode , struct file *pfile)
{
    int ret = 0;
    struct dab_ctx *ctx = NULL;

    ctx = (struct dab_ctx *) kzalloc(sizeof(struct dab_ctx), GFP_KERNEL);
    if (!ctx) {
        pr_err("Cound not allocate dab device\n");
        return -ENOMEM;
    }
    pfile->private_data = ctx; // store ctx in struct file's private_data

    ctx->cfg_base_addr = ioremap(FPGA_CFG_ADDR, FPGA_CFG_SIZE);
    if (!ctx->cfg_base_addr) {
        pr_err("dab: Could not allocate iomem0\n");
        return -EIO;
    }

    return ret;
}

static ssize_t dab_drv_close(struct inode *inode , struct file *pfile)
{
    int ret = 0;
    struct dab_ctx *p;

    p = pfile->private_data;
    if (!p) {
        pr_warning("invalid ctx\n");
        goto exit;
    }

    if(p->cfg_base_addr)
    {
        iounmap(p->cfg_base_addr);
    }

    kfree(p);
exit:
    return ret;
}

static long dab_drv_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg)
{
    int ret = 0;
    struct dab_ctx *p;

    pr_info("enter\n");
    p = pfile->private_data;
    switch(cmd)
    {
    case IOCTL_DAB_WRITE_REG:
        ret = dab_fpga_write_reg(p, arg);
        break;
    case IOCTL_DAB_READ_REG:
        ret = dab_fpga_read_reg(p, arg);
        break;
    default:
        break;
    }

    pr_info("exit\n");
    return ret;
}

static const struct file_operations dab_fops =
{
    .owner   = THIS_MODULE,
    .open    = dab_drv_open,
    .release = dab_drv_close,
    .unlocked_ioctl   = dab_drv_ioctl,
};

static struct miscdevice g_dab_dev =
{
    .minor = 23,
    .name  = "dab",
    .fops  = &dab_fops,
};

static int __init dab_init(void)
{
    int ret = 0;

    ret = misc_register(&g_dab_dev);
    if (ret < 0) {
        printk("failed to reg dev, err %d\n", ret);
    }
    return ret;
}

static void __exit dab_exit(void)
{
    misc_deregister(&g_dab_dev);
}

module_init(dab_init);
module_exit(dab_exit);

一些说明

  1. 模块和驱动是两个概念,模块里可以包含多个驱动,也可以一个驱动都没有,模块只是内核提供的一个代码注入机制
  2. 如果你的代码要驱动参数高度可定制的设备,比如PCIE、USB等,那必须写驱动,然后通过各种总线驱动自动绑定到设备
  3. 如果是FPGA这种跟业务紧密绑定的设备,则一个定制misc设备足矣,然后在模块入口函数里手动注册设备。
  4. ioremap前一般要先request_mem_region,但如果那片内存已经被系统自带驱动申请了,那我们也可以忽略,强行ioremap,只要我们清楚那块内存不会被自带驱动实际用到就好
  5. 切记open函数要把ctx保存到struct fileprivate_data,这样后面的ioctlclose等成员函数才不会触发空指针异常。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值