xillinux上的驱动编程

1、获取内核

要编译驱动文件,我们必需要有对应内核的原码,因此,我们需要在ubuntu上获取内核。在ubuntu16.04中输入下面的指令:

git clone https://github.com/xillybus/xillinux-kernel.git

等大约两个小时,就可以下载解压完成了。

给一下编译内核的代码,虽然这里用不到:

export CROSS_COMPILE=/path/to/crosscompiler/arm-xilinx-linux-gnueabi-
make ARCH=armxillinux_defconfig
make ARCH=arm -j 8uImage modules LOADADDR=0x8000

内核的编译用到的是arm-xilinx-linux-gnueabi-这一个编译器。


2、编写驱动程序

xillinux上的驱动开发与一般的arm差别不大,理论这里不讲,直接给出板上的BTN8BTN9两个按键的读取驱动。

首先要看原理图,我们找到BTN8BTN9两个按键对应于MIO50MIO51

下面就来说说写驱动的过程。

加载驱动的时候,首先要进入驱动的入口函数:

module_platform_driver(gpio_miobtn_driver);

通过这个函数,会读取gpio_miobtn_driver这个结构体。

其中,name要与设备树中写的一致,owner就是THIS_MODULEof_match_table会匹配一个结构体gpio_btn_of_match

这个结构体也是要与设备树匹配的。

当所有的参数都匹配后,就会进入probe函数

这个函数就相当于一个初始化函数了,用来初始化一些参数的,这里我用来初始化GPIO

那么,下在就是GPIO的初始化了。

一些初始化定义的参数:

首先要定义一下device_node*np这个结构体,这是用来获取GPIO用的。

我把它定义成了全局变量,方便调用。

初始化时,就先初始化一下这个结构体:

初始化完后就获取一下GPIObtn8btn9与设备树中写的一致。

因为这里要调试,所以打印一下信息:

装载驱动的时候,会发现这两个引脚号分别是3233

获取到GPIO号后,就要进行GPIO初始化了。

来看下GPIO初始化函数。

首先定义一下GPIO的参数,用到gpio这个结构体:

依照地定义如下:

这里只有两个按键。

先检测一下GPIO是否可用:

不可用返回0,可用返回非0数吧。

检测到可用后,就申请一下GPIO

好了,申请成功会返回0,否则返回非0

GPIO的申请可以一组申请或者一个一个地申请。

可写一个申请出错的返回:

至此,GPIO的申请函数就写完了,回到probe函数。

申请到GPIO后,先读取一下它的值看看,这样就可以在装载驱动的时候读取一下它的值,看看效果。

初始化到这时就基本完成了,下面就注册一下杂项设备。

注册杂项设备:

注册杂项设备也是需要有一个结构体的,杂项设备可以在/dev/目录下查找到设备节点。

name不需要与设备树对应,可以自己定义,最后会显示在/dev/目录下的。

同样,在杂项设备下要有一个file_operations的结构体,这是在打开设备的操作设备时用到一个结构体,很重要。

这里,我只定义了它的打开,控制和关闭的三个函数,更多功能不在这里列出来了。

open函数没有什么可以说的。

关闭的函数也是。

这里关键是ioctl函数。

主要是通过CMD来控制一下读取哪个键值而已,其实也没有什么特别。

好了,初始化和操作部分就基本写完了。

卸载驱动的时候也是要做一些动做的,比如释放GPIO和注销杂项设备,这个就在platform_driver结构体的remove函数中。

注释写得很清楚了,这时不多说。


3、编写Makefile文件

新建一个名为Makefile的文件,在里面加入下图中的代码。

其中KDIR需要对应到你的内核代码目录。



4、编译驱动

把上面写好的驱动程序和Makefile文件得制到ubuntu中。

指定编译平台:exportARCH=arm

指定交叉编译器:exportCROSS_COMPILE= arm-xilinx-linux-gnueabi-

输入“make”进行编译。

最后把“.ko”文件复制到开发板上。

装载驱动:insmodfilename.ko

卸载驱动:rmmodfilename


5、编译设备树

为了让上面编译写的驱动程序能够顺利地注册,我们需要修改设备树文件。

在内核根目录下打开设备树文件:vimarch/arm/boot/dts/xillinux-zedboard.dts

按照下面的说明添加代码。

首先要指定匹配名称:compatible= "gpio-btns"。这个与gpio_miobtn_driver这个结构体中的DRIVER_NAME要致。

指定两个GPIO

5051分别对应到原理图上的MIO50MIO51

完整的代码如下:

在内核的根目录下输入指令:makeARCH=arm dtbs编译设备树。

最后在设备树文件夹中找到xillinux-zedboard.dtb文件,改名为devicetree.dtb,复制到SD卡上覆盖原来的文件。

重新启动开发板就可以顺利装载驱动了。


当初,为了写这个驱动,我也搞了很久,书上、网上都没找到参考的。所以,为了方便大家,我还是把完整的驱动代码贴上吧,如果要写其它驱动,也可以以这个为模板来写。

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <asm/uaccess.h>
#include <linux/init.h>
#include <linux/miscdevice.h>

//驱动和设备的名称
#define DRIVER_NAME "gpio-btns"
#define DEVICE_NAME "gpio-btns-ctl"

MODULE_LICENSE("GPL");

//PS按键设备结构体
struct gpio_btn_device {
	const char *name;
	//Pin Assignment 
	unsigned long BTN8;
	unsigned long BTN9;
	//platform device structures
	struct platform_device *pdev;
};
//申请静态变量,方便调用
static struct gpio_btn_device *gpio_btn_dev;

//打开驱动的调用的函数
static int miobtn_open(struct inode *inode, struct file *file)
{
	printk(KERN_EMERG "\tmiobtn open\n");
	return 0;
}

//ioctl函数,主要用来返回读取到的按键值
static long miobtn_ioctl(struct file *filp, unsigned int CMD, unsigned long arg)
{
	int btn;
	printk(KERN_EMERG "\tmiobtn ioctl, CMD = %d, and arg is %ld but unuse!\n", CMD, arg);
	
	switch(CMD)
	{
		case 8:
			btn = gpio_get_value(gpio_btn_dev->BTN8);
			break;
		case 9:
			btn = gpio_get_value(gpio_btn_dev->BTN9);
			break;
		default:
			btn = -1;
			break;
	}
	
	return btn;
}

static int miobtn_release(struct inode *inode, struct file *file)
{
	printk(KERN_EMERG "\tmiobtn release\n");
	return 0;
}

//file_operations结构体
static struct file_operations miobtn_ops = 
{
	.owner = THIS_MODULE,
	.open = miobtn_open,
	.unlocked_ioctl = miobtn_ioctl,
	.release = miobtn_release,
};

//杂项设备的结构体
static struct miscdevice miobtn_dev = 
{
	.minor = MISC_DYNAMIC_MINOR,
	.name = DEVICE_NAME,
	.fops = &miobtn_ops,
};

static int gpio_btn_init_gpio(void)
{
	struct gpio gpio_btn_ctrl[] = {
		{ gpio_btn_dev->BTN8, GPIOF_IN, "BTN8"	},
		{ gpio_btn_dev->BTN9, GPIOF_IN, "BTN9"	},
	};
	int status;
	int i;
	printk(KERN_EMERG "\tgpio init!\n");

	//测试GPIO是否可用
	for (i = 0; i < ARRAY_SIZE(gpio_btn_ctrl); i++) {
		status = gpio_is_valid(gpio_btn_ctrl[i].gpio);
		if (!status) {
			printk(KERN_EMERG "\tBTN%d is invalid\n", i+8);
			goto gpio_invalid;
		}
	}
	printk(KERN_EMERG "\tBTN is valid\n");

	//申请一组GPIO,当然也可以用gpio_request_one对一个GPIO单独申请
	status = gpio_request_array(gpio_btn_ctrl, ARRAY_SIZE(gpio_btn_ctrl));
	if (status) {
		printk(KERN_EMERG "\tBTN request failed, status is %d\n", status);
		//若申请不成功,则释放GPIO
		gpio_free_array(gpio_btn_ctrl, 2);
		goto gpio_invalid;
	}
	printk(KERN_EMERG "\tgpio request successfully!\n");

	return 0;
gpio_invalid:
	return status;
}
static const struct of_device_id gpio_btn_of_match[] = {
	{ .compatible = "gpio-btns", },
	{},
};
MODULE_DEVICE_TABLE(of, gpio_btn_of_match);

static int gpio_btn_of_probe(struct platform_device *pdev)
{
	struct device_node *np = pdev->dev.of_node;
	int status = 0;
	int ret;	//gpio request return value
	int v_btn8, v_btn9;		//初始化GPIO是获取一下键值的返回值变量
	printk(KERN_EMERG "\tenter probe\n");
	
	//Alloc Space for platform device structure 
	gpio_btn_dev = kzalloc(sizeof(*gpio_btn_dev), GFP_KERNEL);
	if (!gpio_btn_dev) {
		status = -ENOMEM;
		goto dev_alloc_err;
	}
	
	//Get the GPIO Pins
	gpio_btn_dev->BTN8 = of_get_named_gpio(np, "btn8", 0);
	gpio_btn_dev->BTN9 = of_get_named_gpio(np, "btn9", 0);
	//打印相关信息
	pr_info(DRIVER_NAME " %s: BTN8: 0x%lx\n", np->name, gpio_btn_dev->BTN8);
	pr_info(DRIVER_NAME " %s: BTN9 : 0x%lx\n", np->name, gpio_btn_dev->BTN9);
	
	printk(KERN_EMERG "BTN8 = %lx!\n",gpio_btn_dev->BTN8);
	printk(KERN_EMERG "BTN9 = %lx!\n",gpio_btn_dev->BTN9);
	
	printk(KERN_EMERG "get the gpio!\n");
	
	//申请GPIO
	ret = gpio_btn_init_gpio();
	if (ret) {
		printk(KERN_EMERG "\tgpio request failed!\n");
		goto gpio_init_error;
	}
	printk(KERN_EMERG "\tgpio request successed!\n");
	
	//获取GPIO的初始值
	v_btn8 = gpio_get_value(gpio_btn_dev->BTN8);
	v_btn9 = gpio_get_value(gpio_btn_dev->BTN9);
	
	printk(KERN_EMERG "\tget BTN value successed!\nBTN8 = %d, BTN9 = %d\n", v_btn8, v_btn9);
	//杂项设备注册
	misc_register(&miobtn_dev);
	
	return 0;
dev_alloc_err:
	return status;
gpio_init_error:
	return ret;
}

static int gpio_btn_of_remove(struct platform_device *pdev)
{
	//button GPIO
	struct gpio gpio_btn_ctrl[] = {
		{ gpio_btn_dev->BTN8, GPIOF_IN, "BTN8"	},
		{ gpio_btn_dev->BTN9, GPIOF_IN, "BTN9"	},
	};
	printk(KERN_EMERG "\tenter remove\n");
	//释放GPIO
	//写一个释放GPIO的函数,因为如果卸载了驱动,没有释放GPIO
	//那么第二次安装驱动的时候就会申请失败,当然你也可以重启一下系统,这太麻烦了。
	gpio_free_array(gpio_btn_ctrl, 2);
	//注销杂项设备
	misc_deregister(&miobtn_dev);
	return 0;
}

//平台文件结构体
static struct platform_driver gpio_miobtn_driver = {
	.driver			= {
		.name		= DRIVER_NAME,
		.owner		= THIS_MODULE,
		.of_match_table = gpio_btn_of_match,
	},
	.probe			= gpio_btn_of_probe,
	.remove			= gpio_btn_of_remove,
};

module_platform_driver(gpio_miobtn_driver);


猜你喜欢

转载自blog.csdn.net/f840764473/article/details/80896782