linux驱动之input子系统及分层/分离设计实例按键驱动
之前介绍过linux驱动的分层/分离思想,这里写个简单的例子。
按键处理在linux内核中有做好的驱动,可以利用输入子系统模块,所以驱动只需写硬件相关的就行,比如中断向量号、IO口、管脚数量等等即可;
本代码使用input子系统,另外采用platform总线,将硬件相关的驱动分成两部分,一部分是platformdevice相关的,另一部分是platformdriver相关的部分,其实整个驱动也就是写这两部分,至于驱动接收到按键中断怎么处理这个事件,只需往input子系统发送事件即可,事件处理/解析完全有子系统做;
具体的流程是:
1、在platformdevice里面设置好io口中断号等信息,然后注册;
2、在platformdriver里面注册platformdriver结构体
3、初始化probe函数,分配inputdevice结构
4、填充需要实现的事件类型及事件信息
5、注册inputdevice
6、处理按键中断,编制中断服务程序
7、在中断服务里面发送事件信息
platform_device 结构体内包含一个struct device结构体,在这个结构体中,主要是硬件相关的数据,期中有一项比较有意思,它是void类型的指针!说明可以自由转换成任意数据类型,比如自定义的结构体!这个在扩展驱动的时候很重要!
void *platform_data; /* Platform specific data, device
在系统自带的输入子系统里面,按键相关的事件处理,可以参考gpio_keys.h文件,在该文件内定义了一个gpio_keys_platform_data的结构体,这个结构体我们就可以赋值给上面那个万能的platform_data,然后再注册platformdevice的时候把这个数据传递给platformdriver那边进行处理;
简单的来看看gpio_keys_platform_data结构体:
struct gpio_keys_platform_data { struct gpio_keys_button *buttons; int nbuttons; unsigned int poll_interval; /* polling interval in msecs - for polling driver only */ unsigned int rep:1; /* enable input subsystem auto repeat */ int (*enable)(struct device *dev); void (*disable)(struct device *dev); const char *name; /* input device name */ };
可以看到,该结构体中还包含了一个专门为button定义的结构体,看看里面有什么信息
struct gpio_keys_button { /* Configuration parameters */ unsigned int code; /* input event code (KEY_*, SW_*) */ int gpio; /* -1 if this key does not support gpio */ int active_low; const char *desc; unsigned int type; /* input event type (EV_KEY, EV_SW, EV_ABS) */ int wakeup; /* configure the button as a wake-up source */ int debounce_interval; /* debounce ticks interval in msecs */ bool can_disable; int value; /* axis value for EV_ABS */ unsigned int irq; /* Irq number in case of interrupt keys */ };
粗劣看看可以知道包括引脚信息、中断号、名字描述、触发方式等等,我们写自己的按键程序的时候完全可以借用这个结构体,不需要自己另外定义一个结构体。
以下看看具体的代码:
本代码采用内核为3.4.2,编译链为4.3.2,主机为ubuntu12.04,开发板是JZ2440.
platformdevice:
#include <linux/module.h> #include <linux/version.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/sched.h> #include <linux/pm.h> #include <linux/sysctl.h> #include <linux/proc_fs.h> #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/input.h> #include <linux/irq.h> #include <linux/gpio_keys.h> #include <asm/gpio.h> #include <asm/io.h> static buttondev_release ( struct device *dev ); static struct gpio_keys_button buttonkey[] = { { .gpio = S3C2410_GPF ( 0 ), .code = KEY_L, .desc = "s1", .active_low = 1, .irq = IRQ_EINT0, }, { .gpio = S3C2410_GPF ( 2 ), .code = KEY_S, .desc = "s2", .active_low = 1, .irq = IRQ_EINT2, }, { .gpio = S3C2410_GPG ( 3 ), .code = KEY_ENTER, .desc = "s3", .active_low = 1, .irq = IRQ_EINT11, }, { .gpio = S3C2410_GPG ( 11 ), .code = KEY_LEFTSHIFT, .desc = "s4", .active_low = 1, .irq = IRQ_EINT19, }, }; static struct gpio_keys_platform_data buttonkeydata = { .buttons = buttonkey, .nbuttons = ARRAY_SIZE ( buttonkey ), }; struct platform_device buttondev = { .name = "mybuttons", .id = -1, .dev = { .release = buttondev_release, .platform_data = &buttonkeydata, }, }; static buttondev_release ( struct device *dev ) { } static int button_init ( void ) { printk("button dev init function running\n"); platform_device_register ( &buttondev ); return 0; } static void button_exit ( void ) { platform_device_unregister ( &buttondev ); } module_init ( button_init ); module_exit ( button_exit ); MODULE_LICENSE ( "GPL" );
platformdriver:
#include <linux/module.h> #include <linux/version.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/sched.h> #include <linux/pm.h> #include <linux/sysctl.h> #include <linux/proc_fs.h> #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/input.h> #include <linux/irq.h> #include <linux/gpio_keys.h> #include <asm/gpio.h> #include <asm/io.h> static int button_probe ( struct platform_device *dev ); static int button_remove ( struct platform_device *pdev ); static struct input_dev *iputdev; static struct timer_list buttons_timer; struct gpio_keys_button *irq_pd; struct platform_driver buttondrv = { .probe = button_probe, .remove = button_remove, .driver = { .name = "mybuttons", .owner = THIS_MODULE, }, }; static irqreturn_t buttons_irqhandler ( int irq, void *irqdev ) { irq_pd = ( struct gpio_keys_button * ) irqdev; mod_timer ( &buttons_timer, jiffies + HZ / 100 ); return IRQ_RETVAL ( IRQ_HANDLED ); } static void buttons_timer_function ( unsigned long data ) { struct gpio_keys_button *buttonkey = ( struct gpio_keys_button * ) irq_pd; u32 pinval; pinval = s3c2410_gpio_getpin ( buttonkey->gpio ); if ( pinval ) { input_event ( iputdev, EV_KEY, buttonkey->code, 0 ); input_sync ( iputdev ); } else { input_event ( iputdev, EV_KEY, buttonkey->code, 1 ); input_sync ( iputdev ); } } static int __devinit button_probe ( struct platform_device *dev ) { int i; struct gpio_keys_platform_data *platdata = ( struct gpio_keys_platform_data * ) dev->dev.platform_data; iputdev = input_allocate_device(); iputdev->name = "mybutton"; set_bit ( EV_KEY, iputdev->evbit ); set_bit ( EV_REP, iputdev->evbit ); set_bit ( KEY_L, iputdev->keybit ); set_bit ( KEY_S, iputdev->keybit ); set_bit ( KEY_ENTER, iputdev->keybit ); input_register_device ( iputdev ); init_timer ( &buttons_timer ); buttons_timer.function = buttons_timer_function; add_timer ( &buttons_timer ); for ( i = 0; i < platdata->nbuttons; i++ ) { request_irq ( platdata->buttons[i].irq, buttons_irqhandler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, platdata->buttons[i].desc, &platdata->buttons[i] ); } return 0; } static int button_remove ( struct platform_device *pdev ) { int i; struct gpio_keys_platform_data *platdata = pdev->dev.platform_data; for ( i = 0; i < platdata->nbuttons; i++ ) { free_irq ( platdata->buttons[i].irq, &platdata->buttons[i] ); } del_timer(&buttons_timer); input_unregister_device ( iputdev ); input_free_device ( iputdev ); } static int button_init ( void ) { platform_driver_register ( &buttondrv ); return 0; } void button_exit ( void ) { platform_driver_unregister ( &buttondrv ); } module_init ( button_init ); module_exit ( button_exit ); MODULE_LICENSE ( "GPL" );