为linux内核添加FPGA字符设备驱动程序

Linux中驱动程序位于操作系统和硬件之间,是连接操作系统与硬件之间的纽带,今天我们就来向Linux中添加一个简单的FPGA字符驱动程序。

I. Linux字符驱动程序简介

在Linux中驱动程序分为字符驱动,块设备驱动,网络设备驱动三种,字符设备驱动是其中比较简单的一种。字符设备是指只能一个字节一个字节进行读写操作的设备,不能随机读取设备中的某一数据、读取数据要按照先后数据。

II.添加字符驱动程序的步骤

在这里插入图片描述
在这里插入图片描述
更多C/C++Linux视频资料请加qun:832218493或vx关注零声学院免费领取!

1. 获取字符设备号
在linux中我们通过cat /proc/devices 命令即可查看到各种设备以及其对应的设备编号。在内核中,我们通常用如下两个函数来获取设备编号

int register_chrdev_region(dev_t first, unsigned int count, char *name);
/* 参数:
    dev_t first - 要申请的设备号(起始)
    unsigned int count - 要申请的设备号数量
    const char *name - 设备名
   返回值:
    成功:0
    失败:负数
   需要事先知道哪个设备号没有被占用!!!
*/
int alloc_chrdev_region(dev_t *dev,unsigned int firstminor,unsigned int count,char *name);
/* 参数:
    dev_t *dev - 用于保存分配到的第一个设备号(起始)
    unsigned int firstminor - 起始次设备号
    unsigned int count - 要分配设备号的数量
    char *name - 设备名
   返回值:
    成功:0
    失败:负数(绝对值是错误码)
   不需要事先知道哪个设备号被占用,系统会根据实际情况自行分配
*/

2. 向cdev结构体注册文件操作
在Linux内核代码中,字符设备的描述通常是由struct cdev来完成的,其中有一个成员是一个指向file_operations的指针,我们将来想要像操作普通文件一样操作硬件设备,就需要完成file_operations中的.open, .read, .write, .release, .llseek函数,将它们映射成具体的硬件设备操作,如下代码所示

static const struct file_operations fpga_fops = {
    
    
    .read        = fpga_read,
    .write        = fpga_write,
    .llseek = fpga_llseek,
    .open        = fpga_open,
    .release    = fpga_release,
};

定义完文件操作之后,就需要将文件操作注册到cdev结构体之后就需要向内核注册设备驱动,代码如下:

III. FPGA地址映射以及读写通信

在我写的这个字符驱动程序中,我是用的ioremap()函数来实现地址映射,这里需要注意的是:要根据自己核心板的FPGA使能引脚和地址引脚来确定实际地址切不可盲目拷贝;copy_to_user()和copy_from_user()函数来实现FPGA的读写操作,具体见代码:

#include <linux/init.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/errno.h>
#include <linux/device.h>

#include <asm/io.h>
#include <asm/uaccess.h>


#define FPGA_BASE_ADDR 0xF0000000
#define FPGA_SIZE 0x4000000
#define DEV_NAME "fpga"

struct cdev fpga_dev;
dev_t fpga_no;
struct class *fpga_class;


static char *fpga_addr = NULL;

static int fpga_open(struct inode *ino, struct file *filp);
static int fpga_release(struct inode *ino, struct file *filp);
static loff_t fpga_llseek(struct file *, loff_t, int);
static ssize_t fpga_read(struct file *, char __user *, size_t, loff_t *);
static ssize_t fpga_write(struct file *, const char __user *, size_t, loff_t *);

static const struct file_operations fpga_fops = {
    
    
    .read        = fpga_read,
    .write        = fpga_write,
    .llseek = fpga_llseek,
    .open        = fpga_open,
    .release    = fpga_release,
};


static int fpga_open(struct inode *ino, struct file *filp)
{
    
    
    return 0;
}
static int fpga_release(struct inode *ino, struct file *filp)
{
    
    
    return 0;
}
static loff_t fpga_llseek(struct file *file, loff_t off, int whence)
{
    
    
    if(fpga_addr)
        iounmap(fpga_addr);
    if(off <= FPGA_SIZE)
        fpga_addr=ioremap(FPGA_BASE_ADDR+off,FPGA_SIZE-off);
    return off;
}
static ssize_t fpga_read(struct file *file, char __user *buf, size_t count,
            loff_t *ppos)
{
    
    
    printk("user data: %x", buf[0]);
    printk("kern data: %x", fpga_addr[0]);
    if(copy_to_user(buf, fpga_addr, count))
        return -EFAULT;
    
    return count;
}

static ssize_t fpga_write(struct file *file, const char __user *buf,
                        size_t count, loff_t *ppos)
{
    
    
    printk("user data: %x", buf[0]);
    printk("kern data: %x", fpga_addr[0]);
    if(copy_from_user(fpga_addr, buf, count))
        return -EFAULT;
    printk("kern data: %x", fpga_addr[0]);
    return count;
}
static int __init fpga_init(void)
{
    
    
    int ret;
    ret = alloc_chrdev_region(&fpga_no, 0, 1, "fpga"); /* device num you get, from where to allocate, num of this kind of device, device name */
    if(ret)
    {
    
    
        printk("alloc_chrdev_region failed!\n");
        unregister_chrdev_region(fpga_no, 1); /* first device number, num of this kind of device */
        return ret;
    }
    else
    {
    
    
        printk("alloc_chrdev_region success!\n");
    }

    cdev_init(&fpga_dev, &fpga_fops); /* initialize your device settings */

    ret = cdev_add(&fpga_dev, fpga_no, 1); /* register your device to the kernel */
    if(ret)
    {
    
    
        printk("fpga dev add fail.\n");
        unregister_chrdev_region(fpga_no, 1);
        return ret;
    }
    else
    {
    
    
        printk("fpga dev add success!\n");
    }
    /* ==================add module to /dev/fpga=================== */
    fpga_class = class_create(THIS_MODULE, "fpga");
    device_create(fpga_class, NULL, fpga_no, NULL, "fpga");
    /* ==================address mapping ===================== */
    fpga_addr = ioremap(FPGA_BASE_ADDR, FPGA_SIZE);
    if(fpga_addr)
    {
    
    
        printk("ioremap success~\n");
    }
    else
    {
    
    
        unregister_chrdev_region(fpga_no, 1);
        return -ENODEV;
    }
    return 0;
}
static void __exit fpga_exit(void)
{
    
    
    if(fpga_addr)
        iounmap(fpga_addr);
    cdev_del(&fpga_dev);
    unregister_chrdev_region(fpga_no, 1);
    printk("fpga dev exit success!\n");
}
module_init(fpga_init);

将上述代码编入内核,便可使用cat /proc/devices 来看到fpga设备以及其设备号,并且可以在/dev/目录下查看到fpga啦~~~

猜你喜欢

转载自blog.csdn.net/lingshengxueyuan/article/details/108552547