結合以前對Linux內核的platform總線與input子系統的分析 ,本文將編寫基於platform總線和input子系統的Button設備的實例代碼並對其進行分析。html
platform總線的分析,詳見Linux platform驅動模型。linux
input子系統的分析,詳見Linux字符設備驅動框架(四):Linux內核的input子系統。數據結構
硬件接口:框架
CPU:s5pv210;測試
Button的GPIO:GPIO_H0_2,EINT2;spa
LED的工做方式:按鍵彈起,低電平;按鍵按下,高電平。code
在/kernel/arch/arm/mach-s5pv210/include/mach目錄下,創建一個buttons_gpio.h文件,並填充以下內容。orm
#ifndef __ASM_ARCH_BUTTONSGPIO_H #define __ASM_ARCH_BUTTONSGPIO_H "buttons-gpio.h" //定義一個Button設備的數據結構 struct s5pv210_button_platdata { char *name; unsigned int gpio; unsigned int irqnum; unsigned int flags; }; #endif
在/kernel/arch/arm/mach-s5pv210/mach-x210.c下,添加以下內容,並添加對buttons_gpio.h的包含。htm
/*Buttons*/ static struct s5pv210_button_platdata s5pv210_button_pdata =
{ .name = "button1", .gpio = S5PV210_GPH0(2), .irqnum = IRQ_EINT2,//中斷號 .flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,//上升沿觸發+降低沿觸發 }; static struct platform_device s5pv210_button = { .name = "s5pv210_button", .id = 1, .dev = { .platform_data = &s5pv210_button_pdata, }, };
將LED設備信息集成至smdkc110_devices,內核初始化時smdkc110_devices中的設備將被註冊進內核。blog
static struct platform_device *smdkc110_devices[] __initdata = { ...... &s5pv210_button,
};
#include <linux/kernel.h> #include <linux/init.h> #include <linux/platform_device.h> #include <linux/gpio.h> #include <linux/slab.h> #include <linux/input.h> #include <mach/buttons_gpio.h> #include <mach/hardware.h> #include <mach/regs-gpio.h> #include <mach/irqs.h> #include <linux/interrupt.h> static struct s5pv210_button_platdata *pdata; static struct input_dev *button_dev = NULL; static irqreturn_t button_interrupt(int irq, void *dummy) { int flag; s3c_gpio_cfgpin(pdata->gpio, S3C_GPIO_SFN(0x0)); //設置GPIO爲input模式 flag = gpio_get_value(pdata->gpio); //讀取GPIO的值 s3c_gpio_cfgpin(pdata->gpio, S3C_GPIO_SFN(0x0f));//設置GPIO爲eint2模式 input_report_key(button_dev, KEY_LEFT, !flag); //上報事件 input_sync(button_dev); //同步事件 return IRQ_HANDLED; } static int s5pv210_button_remove(struct platform_device *dev) { input_free_device(button_dev); //釋放button_dev內存 free_irq(pdata->irqnum, button_interrupt);//釋放中斷資源 gpio_free(pdata->gpio); //釋放GPIO return 0; } static int s5pv210_button_probe(struct platform_device *dev) { int ret; pdata = dev->dev.platform_data; /*****************************申請資源******************************/ //申請GPIO ret = gpio_request(pdata->gpio, pdata->name); if (ret) { printk(KERN_ERR "gpio_request failed, ret = %d.\n", ret); return -EBUSY; } //申請IRQ if (request_irq(pdata->irqnum, button_interrupt, pdata->flags, pdata->name, NULL)) { printk(KERN_ERR "key-s5pv210.c: Can't allocate irq %d\n", pdata->irqnum); ret = -EBUSY; goto ERR_STER0; } /************************初始化GPIO資源*************************/ s3c_gpio_setpull(pdata->gpio, S3C_GPIO_PULL_UP); //設置GPIO爲上拉模式 s3c_gpio_cfgpin(pdata->gpio, S3C_GPIO_SFN(0x0f));//設置GPIO爲eint模式 /*******************************建立接口*******************************/ //申請button_dev內存空間 button_dev = input_allocate_device(); if(!button_dev) { ret = -ENOMEM; goto ERR_STER1; } //初始化button_dev set_bit(EV_KEY, button_dev->evbit); //支持EV_KEY事件 set_bit(KEY_LEFT, button_dev->keybit); //支持KEY_LEFT子事件 //註冊button_dev if(input_register_device(button_dev) != 0) { printk("s5pv210-button input register device fail!!\n"); ret = -ENODEV; goto ERR_STER2; } return 0; /****************************倒映式錯誤處理****************************/ ERR_STER2: input_free_device(button_dev); ERR_STER1: free_irq(pdata->irqnum, button_interrupt); ERR_STER0: gpio_free(pdata->gpio); return ret; } //定義並初始化驅動信息 static struct platform_driver s5pv210_button_driver = { .probe = s5pv210_button_probe, .remove = s5pv210_button_remove, .driver = { .name = "s5pv210_button", .owner = THIS_MODULE, }, }; //註冊驅動 static int __init s5pv210_button_init(void) { return platform_driver_register(&s5pv210_button_driver); } //註銷驅動 static void __exit s5pv210_button_exit(void) { platform_driver_unregister(&s5pv210_button_driver); } module_init(s5pv210_button_init); module_exit(s5pv210_button_exit); MODULE_AUTHOR("Lin"); MODULE_DESCRIPTION("S5PV210 BUTTON driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:s5pv210_button");
編寫一個簡易的應用程序,來測試上述驅動程序。
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <linux/input.h> #include <string.h> #define X210_KEY "/dev/input/event1" int main(void) { int fd = -1, ret = -1; struct input_event ev; //打開設備文件 fd = open(X210_KEY, O_RDONLY); if (fd < 0) { perror("open"); return -1; } while (1) { //讀取一個event事件包 memset(&ev, 0, sizeof(struct input_event)); ret = read(fd, &ev, sizeof(struct input_event)); if (ret != sizeof(struct input_event)) { perror("read"); close(fd); return -1; } //解析event包 printf("-------------------------\n"); printf("type: %hd\n", ev.type); printf("code: %hd\n", ev.code); printf("value: %d\n", ev.value); printf("\n"); } //關閉設備 close(fd); return 0; }
裝載驅動模塊後,在Linux終端運行該應用程序。在一次按鍵按下並彈起的過程當中,終端中將打印出以下的信息,代表驅動程序工做正常。
-------------------------//按鍵按下 type: 1 code: 105 value: 1 -------------------------//同步事件 type: 0 code: 0 value: 0 -------------------------//按鍵彈起 type: 1 code: 105 value: 0 -------------------------//同步事件 type: 0 code: 0 value: 0