linux驱动子系统之输入子系统(1)
1.输入子系统框架
1.1 概述
内核提供的输入子系统是对分散的、多种不同类别的输入设备(键盘、鼠标、触摸屏、加速计、跟踪球、操纵杆等)进行统一处理的驱动程序。
输入子系统带来的好处:
l 抽象底层形态各异的硬件输入设备,为上层提供了统一的操作接口
l 提高了代码重用率和效率,减少了bug
1.2 系统框架
输入子系统的三层结构:
l 事件驱动层
负责和应用程序的接口
l 核心层
提供事件驱动层和设备驱动层所需的函数接口
l 设备驱动层
负责和底层输入设备通信
事件驱动层和核心层都是通用的,我们需要实现的是设备驱动层。输入设备驱动会把硬件产生的事件信息用统一的格式(struct input_event)上报给核心层,然后核心层进行分类后,再上报给相应的事件处理驱动程序,最后通过事件层传递到用户空间,应用程序可以通过设备节点来获取事件信息。比如底层报上来的是一个按键事件,核心层会报给evdev来处理;如果报的是一个鼠标事件,核心层会报给mousedev来处理
1.3 统一的格式
底层驱动可以通过struct input_event上报事件信息,上层应用也可以通过此结构体来获取事件信息
struct input_event {
structtimeval time; /* 事件产生时间 */
__u16type; /* 事件类型 */
__u16code; /* 事件代码 */
__s32value; /* 事件值 */
};
l 事件类型,定义在linux/input.h中
1. #define EV_SYN 0x00 /*表示设备支持所有的事件*/
2. #define EV_KEY 0x01 /*键盘或者按键,表示一个键码*/
3. #define EV_REL 0x02 /*鼠标设备,表示一个相对的光标位置结果*/
4. #define EV_ABS 0x03 /*手写板产生的值,其是一个绝对整数值*/
5. #define EV_MSC 0x04 /*其他类型*/
6. #define EV_LED 0x11 /*LED灯设备*/
7. #define EV_SND 0x12 /*蜂鸣器,输入声音*/
8. #define EV_REP 0x14 /*允许重复按键类型*/
9. #define EV_PWR 0x16 /*电源管理事件*/
l 事件代码,不同事件类型包含了多种事件代码
EV_KEY KEY_1,KEY_2,KEY_3…
EV_REL REL_X,REL_Y, REL_Z…
EV_ABS ABS_X,ABS_Y, ABS_Z…
And so on….
l 事件值,由硬件输入设备产生的,比如按键设备驱动会去获取GPIO引脚的状态(假设低电平有效),按下时是0,弹起时是1
1.4 输入设备驱动的实现
l 子系统中用input_dev结构体来描述一个输入设备
struct input_dev{
/* 导出到用户空间的相关信息,在sys文件可以看到*/
const char *name;
const char *phys;
const char *uniq;
struct input_id id;
unsigned longevbit[BITS_TO_LONGS(EV_CNT)]; /*输入设备的事件支持位图*/
unsigned longkeybit[BITS_TO_LONGS(KEY_CNT)];/*按键设备支持的事件代码位图*/
……
}
Evbit的位图,设置了相应的位表示设备对此事件的支持
l 设置输入设备所支持的事件类型和事件代码,也就是输入设备对哪些事件具有处理能力,通常我们使用set_bit函数来设置,而不采用直接赋值
n 设置支持的事件类型(支持按键事件)
set_bit(EV_KEY, input_dev->evbit);
n 设置支持的事件代码(支持按键1)
set_bit(KEY_1, input_dev->keybit);
n 也可以直接调用input_set_capability来完成
input_set_capability(input_dev,EV_KEY, KEY_1);
l 内核为我们提供了一组函数接口,来实现输入设备驱动
n 分配/释放一个输入设备
struct input_dev*input_allocate_device(void)
voidinput_free_device(struct input_dev *dev)
input_allocate_device分配成功后,会返回一个Input_dev的结构体
n 注册和注销输入设备
int input_register_device(struct input_dev*dev)
void input_unregister_device(struct input_dev*dev)
注册函数用于将Input_dev注册进核心层中,注销则相反。参数dev是input_allocate_device的返回值
n 报告输入事件
报告按键值
voidinput_report_key(struct input_dev *dev, unsigned int code, int value)
报告相对坐标
voidinput_report_rel(struct input_dev *dev, unsigned int code, int value)
报告绝对坐标
voidinput_report_abs(struct input_dev *dev, unsigned int code, int value)
用于事件同步 ,防止数据混乱
voidinput_sync(struct input_dev *dev)
以上都是对Input_event的封装
voidinput_event(struct input_dev *dev,unsigned int type, unsigned int code, intvalue)
and so on…
1.5 实例解析
基于input子系统实现按键驱动代码,硬件平台为s3c2440
#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <mach/gpio.h>
#include <mach/regs-gpio.h>
#include <asm/irq.h>
#include <asm/io.h>
#define DEV_NAME "KEY1"
#define BUTTON_IRQ IRQ_EINT8
static struct input_dev *button_dev;
static irqreturn_t button_interrupt(intirq, void *p)
{
/*get pin value <down 0, up 1> */
intval = s3c2410_gpio_getpin(S3C2410_GPG(0));
input_report_key(button_dev,KEY_1, val);
input_sync(button_dev);
returnIRQ_RETVAL(IRQ_HANDLED);
}
static int __init button_init(void)
{
interr;
/* 申请中断 */
if(request_irq(BUTTON_IRQ, button_interrupt,
IRQ_TYPE_EDGE_BOTH,DEV_NAME, NULL)) {
printk(KERN_ERR"cannotallocate irq");
return- EBUSY;
}
/*分配input_dev */
button_dev= input_allocate_device();
if(button_dev == NULL) {
printk(KERN_ERR"notenough memory\n");
err= - ENOMEM;
gotoerr_free_irq;
}
/*设置输入设备支持的事件类型和事件代码 */
set_bit(EV_KEY,button_dev->evbit);
set_bit(KEY_1,button_dev->keybit);
/*把输入设备注册进核心层 */
err= input_register_device(button_dev);
if(err) {
printk(KERN_ERR"failedto register device\n");
gotoerr_free_dev;
}
printk("initialized\n");
return0;
err_free_dev:
input_free_device(button_dev);
err_free_irq:
free_irq(BUTTON_IRQ,NULL);
returnerr;
}
static void __exit button_exit(void)
{
input_unregister_device(button_dev);
input_free_device(button_dev);
free_irq(BUTTON_IRQ,NULL);
}
module_init(button_init);
module_exit(button_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("CJOK<cjok.liao@gmail.com>");