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);


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章