输入子系统
linux系统提供了input子系统,按键,触摸屏,鼠标等输入型设备都可以利用input接口函数来实现设备驱动。
体系结构--
输入子系统由驱动层,输入子系统核心层(input core)和事件处理层(event handler)三部分组成。一个输入事件,如鼠标移动,键盘按键按下,通过driver->inputcore->event handler->userspace的顺序到达用户空间的应用程序。
驱动层---
将底层的硬件输入转化为统一事件形式,向输入核心回报。
输入核心层---
为驱动层提供输入设备注册与操作接口,如:input_register_device;通知事件处理层对事件进行处理;在/proc下产生相应的设备信息。
事件处理层---
和用户空间交互,linux在用户空间将所有设备当成文件来处理,在一般的驱动程序中都有提供fops接口,以及在/dev下生成相应的设备文件nod,而在输入子系统中,这些工作都是由事件处理层完成的。
在linux内核中,input设备用input_dev结构体描述,使用input子系统实现输入设备驱动的时候,驱动的核心工作是向系统报告按键,触摸屏,键盘,鼠标,等输入事件(event,通过input_event结构体描述),不再需要关心文件操作接口,因为input子系统已经完成了文件操作接口。驱动报告的事件经过inpoutcore和eventhandler最终到达用户空间。
struct input_dev {
const char *name;
const char *phys;
const char *uniq;
struct input_id id;
unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
unsigned int keycodemax;
unsigned int keycodesize;
void *keycode;
int (*setkeycode)(struct input_dev *dev, int scancode, int keycode);
int (*getkeycode)(struct input_dev *dev, int scancode, int *keycode);
struct ff_device *ff;
unsigned int repeat_key;
struct timer_list timer;
int sync;
int abs[ABS_MAX + 1];
int rep[REP_MAX + 1];
unsigned long key[BITS_TO_LONGS(KEY_CNT)];
unsigned long led[BITS_TO_LONGS(LED_CNT)];
unsigned long snd[BITS_TO_LONGS(SND_CNT)];
unsigned long sw[BITS_TO_LONGS(SW_CNT)];
int absmax[ABS_MAX + 1];
int absmin[ABS_MAX + 1];
int absfuzz[ABS_MAX + 1];
int absflat[ABS_MAX + 1];
int (*open)(struct input_dev *dev);
void (*close)(struct input_dev *dev);
int (*flush)(struct input_dev *dev, struct file *file);
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
struct input_handle *grab;
spinlock_t event_lock;
struct mutex mutex;
unsigned int users;
int going_away;
struct device dev;
struct list_head h_list;
struct list_head node;
};
注册,注销输入设备的函数---
int input_register_device(struct input_dev *dev)
int input_unregister_device(struct input_dev *dev)
驱动实现-----事件支持
设备驱动通过set_bit()告诉input子系统它支持哪些事件,哪些按键。例如:
set_bit(EV_KEY,button_dev.evbit)
struct input_dev中的两个成员:
evbit--事件类型
keybit--按键类型
事件类型:
EV_RST--reset
EV_REL--相对坐标
EV_MSC--其它
EV_SND--声音
EV_FF--力反馈
EV_KEY--按键
EV_ABS--绝对坐标
EV_LED--led
EV_REP--repeat
当事件类型为EV_KEY时,还需指明按键类型:
BTN_LEFT--鼠标左键
BTN_RIGHT--鼠标右键
BTN_MIDDLE--鼠标中键
BTN_0--数字0键
BTN_1--数字1键
用于报告EV_KEYEV_RELEV_ABS事件的函数分别为--
void input_report_key(struct input_dev *dev,unsigned int code,int value)
void input_report_rel(struct input_dev *dev,unsigned int code,int value)
void input_report_abs(struct input_dev *dev,unsigned int code,int value)
structinput_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
code--
事件的代码。如果事件类型是EV_KEY,该代码则为设备的键盘代码。例如:键盘上的按键代码值为0--127,鼠标按键代码为0x110---0x116,其中0x110(BTN_LEFT)为鼠标左键,0x111(BTN_RIGHT)为鼠标左键,其它代码含义参考include/linux/input.h文件。
value--
事件的值。若事件的类型是EV_KEY,当按键按下时值为1,松开值为0
input_sync()用于告诉input core:此次报告已结束。
实例:
在触摸屏设备驱动中,一次点击的整个报告过程如下:
input_report_abs(input_dev,ABS_X,x)
input_report_abs(input_dev,ABS_Y,x)
input_report_abs(input_dev,ABS_PRESSURE,1)
input_sync(input_dev)
实例分析==
在按键中断中报告事件---
static void button_interrupt(int irq,void *dummy,struct pt_regs *fp)
{
input_report_key(&button_dev,BTN_0,inb(BUTTON_PORT0));
input_report_key(&button_dev,BTN_1,inb(BUTTON_PORT1));
input_sync(&button_dev);
}
static int__init button_init(void)
{
申请中断
if(request_irq(BUTTON_IRQ,button_interrupt,0"button",NULL))
return -EBUSY;
set_bit(EV_KEY,button_dev.evbit);支持EV_KEY事件
set_bit(BTN_0,button_dev.keybit);设备支持两个键
set_bit(BTN_1,button_dev.keybit);
input_register_device(&button_dev);注册input设备
}
应用程序--
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>
#include <linux/input.h>
int main(void)
{
int buttons_fd;
int key_value,i=0,count;
struct input_event ev_key;
buttons_fd = open("/dev/event0", O_RDWR);
if (buttons_fd < 0) {
perror("open device buttons");
exit(1);
}
for (;;) {
count = read(buttons_fd,&ev_key,sizeof(struct input_event));读取事件的个数
for(i=0; i<(int)count/sizeof(struct input_event); i++)
if(EV_KEY==ev_key.type)
printf("type:%d,code:%d,value:%d\n", ev_key.type,ev_key.code,ev_key.value);
if(EV_SYN==ev_key.type)
printf("syn event\n\n");
}
close(buttons_fd);
return 0;
}
总结---------两个重要的数据结构
input_dev
input_event