硬件平台:XCZ7020 CLG484-1 完全适配Zedboard
开发环境:Widows下Vivado 2016.2 、 SDK2016.2 、 Linux机器:debin
目的:操作板载的LED灯LD9,受PS部分的MIO7控制
linux设备驱动大体分三种:字符设备、块设备、网络设备。字符设备指可以以字节为单位访问内存,块设备只能以数据块进行访问,比如NandFlash等,网络设备就指以太网等网卡驱动了。
在原始的设备驱动编写风格来看,主要是搭建框架,然后填充框架,填充的内容就和裸机的驱动文件一样了,所以设备驱动的核心还是设备的裸机程序。
目前我用的设备驱动方案大体框架如下:
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
static int hello_init(void)
{
printk("Module init complete!\nHello, Linux Driver!\n");
return 0;
}
static void hello_exit(void)
{
printk("Module exit!\nBye, Linux Driver!\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_AUTHOR("Cuter@ChinaAET");
MODULE_DESCRIPTION("HelloLinuxDriver");
MODULE_ALIAS("It's only a test");
模块刚开始加载的时候执行module_init,从而执行hello_init;模块退出的时候执行module_exit从而执行hello_exit。Linux一切皆文件,包括对应用程序对驱动的操作也都是读文件,写文件等等,所以除了模块的初始化和模块的退出,设备驱动还需要为应用程序提供读写文件的功能,这些接口的提供是通过file_operations结构体来实现的。
static struct file_operations gpio_drv_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = gpio_open,
.write = gpio_write,
};
Gpio_open和gpio_write就是驱动中具体的实现函数,填充完结构体后,通过对注册字符设备将此结构体传递给内核,从而构建了系统对驱动的读写操作,注册是在模块的初始化中实现的,除了注册设备,为了在目标板中加载模块方便还需自动注册类与设备。
除了注册字符设备,在init函数中最重要的操作就是内存映射。通过MMU,将设备的物理地址映射为虚拟地址,用户可以对系统的操作均为虚拟地址。下面给出全部代码。
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/miscdevice.h>