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啦~~~