if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value) return;
if (value == 2) break;
change_bit(code, dev->key);
if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && dev->timer.data && value) {
dev->repeat_key = code;
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY])); }
case EV_ABS: //判断为绝对坐标事件
if (code > ABS_MAX || !test_bit(code, dev->absbit)) return;
if (dev->absfuzz[code]) {
if ((value > dev->abs[code] - (dev->absfuzz[code] >> 1)) &&
(value < dev->abs[code] + (dev->absfuzz[code] >> 1)))
return;
if ((value > dev->abs[code] - dev->absfuzz[code]) && (value < dev->abs[code] + dev->absfuzz[code])) value = (dev->abs[code] * 3 + value) >> 2;
if ((value > dev->abs[code] - (dev->absfuzz[code] << 1)) &&
(value < dev->abs[code] + (dev->absfuzz[code] << 1)))
value = (dev->abs[code] + value) >> 1; }
if (dev->abs[code] == value) return;
dev->abs[code] = value; break;
…
}
if (type != EV_SYN) dev->sync = 0;
if (dev->grab) //判断有没有声明自定义的处理函数(初始化过程中显然没有定义)
dev->grab->handler->event(dev->grab, type, code, value); else
list_for_each_entry(handle, &dev->h_list, d_node) //遍历handle链表
if (handle->open) //如果某节点上的处理程序被打开了
handle->handler->event(handle, type, code, value); //调用处理函数 }
先看该函数中的一个switch结构,这里只列出了EV_KEY和EV_ABS两个事件的处理,经过switch选择处理后看最后代码加粗的部分。
list_for_each_entry(handle, &dev->h_list, d_node) 这句类似的前面已经介绍过,这里不再说明,接下来if (handle->open),判断处理程序是否被打开,还记得是什么时候打开的吗?前面有介绍过在evdev.c中的open函数里面调用了input_open_device(&list->evdev->handle),而在
input_open_device()中有这么一句handle->open++;没有错,只有在经过evdev_open之后才能这里的条件才能为真,接着看下面的handle->handler->event(handle, type, code, value);继续深入跟踪: static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) {
struct evdev *evdev = handle->private; struct evdev_list *list;
if (evdev->grab) { //显然grab并没有被设置,该条件为假 list = evdev->grab;
do_gettimeofday(&list->buffer[list->head].time); list->buffer[list->head].type = type; list->buffer[list->head].code = code; list->buffer[list->head].value = value;
list->head = (list->head + 1) & (EVDEV_BUFFER_SIZE - 1);
kill_fasync(&list->fasync, SIGIO, POLL_IN); } else
list_for_each_entry(list, &evdev->list, node) {
do_gettimeofday(&list->buffer[list->head].time); //给buffer成员赋值
list->buffer[list->head].type = type; list->buffer[list->head].code = code; list->buffer[list->head].value = value;
list->head = (list->head + 1) & (EVDEV_BUFFER_SIZE - 1);
kill_fasync(&list->fasync, SIGIO, POLL_IN); }
wake_up_interruptible(&evdev->wait); //用来唤醒一个等待队列(我也不懂) }
先看前面的struct evdev *evdev = handle->private,还记得
handler->private吗,在input_handle结构体中它是一个空指针,在前面有讲到在执行static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)函数的时候就被赋值了(evdev->handle.private = evdev;)。 再看最后代码加粗的部分,显然是给evdev_list结构体中的buffer成员赋上从设备中传过来的数据,当数据存放好了之后,head加1,当head的值达到EVDEV_BUFFER_SIZE时又回到0;
至此数据的传递就算是结束了,接下来就是等着被读走,只要在用户空间进行read操作即可。
第五章、数据读取过程
读取就变得很简单了,做过linux编程的都能知道,只要在应用中定义了个input_event结构体,通过open打开设备,然后进行read即可,再来看该设备的read函数:
static ssize_t evdev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos) {
struct evdev_list *list = file->private_data; int retval;
if (count < evdev_event_size())//每次读取的字节数至少是input_event的大小
return -EINVAL;
if (list->head == list->tail && list->evdev->exist && (file->f_flags & O_NONBLOCK)) //是否满足读取条件 return -EAGAIN;
retval = wait_event_interruptible(list->evdev->wait,
list->head != list->tail || (!list->evdev->exist)); //等待唤醒,和前面说的等待队列对应(我也不懂) if (retval)
return retval;
if (!list->evdev->exist) return -ENODEV;
while (list->head != list->tail && retval + evdev_event_size() <= count) { struct input_event *event = (struct input_event *) list->buffer + list->tail;
if (evdev_event_to_user(buffer + retval, event)) //复制数据到用户空间
return -EFAULT;
list->tail = (list->tail + 1) & (EVDEV_BUFFER_SIZE - 1); retval += evdev_event_size(); }
return retval; }
函数前面基本上都是在做一系列的条件判断,判断是否符合读取的条件,
evdev_event_to_user(buffer + retval, event)这句是真正的把数据复制到用户空间,进入查看:
static int evdev_event_to_user(char __user *buffer, const struct input_event *event) {
if (copy_to_user(buffer, event, sizeof(struct input_event))) return -EFAULT;
return 0; }
该函里面也只是调用了copy_to_user()函数而已,该函数相比我们都不会陌生了,就是把event所指向地址空间的数据复制到buffer所指向的地址空间中而已。 再回到read函数中来,函数在末尾返回读取到的字节数,至于要把这些数据拿去干什么用,那就是应用层的事情了。
可以看到接口函数中除了read以外还有write、ioctl等函数,因为对于触摸屏来说我们主要就是要获取触笔在触摸屏上点击的坐标位置而已,所以write、ioctl等函数会很少用到,这里就不再做介绍。
注:本文只在一定的层面(本人的理解)上对linux下的input子系统的介绍,里边还有很多要点没有办法去涉及,只是一个入门级的学习过程,紧限于在在完
善的input子系统下进行做驱动修改,而不具备做驱动开发的能力。--以触摸
屏驱动为例
第一章、了解linux input子系统
Linux输入设备总类繁杂,常见的包括有按键、键盘、触摸屏、鼠标、摇杆等等,他们本身就是字符设备,而linux内核将这些设备的共同性抽象出来,简化驱动开发建立了一个input子系统。子系统共分为三层,如图1所示。
图1 input输入子系统
驱动层和硬件相关,直接捕捉和获取硬件设备的数据信息等(包括触摸屏被按下、按下位置、鼠标移动、键盘按下等等),然后将数据信息报告到核心层。核心层负责连接驱动层和事件处理层,设备驱动(device driver)和处理程序(handler)的注册需要通过核心层来完成,核心层接收来自驱动层的数据信息,并将数据信息选择对应的handler去处理,最终handler将数据复制到用户空间。 先了解三个定义在/linux/input.h下重要的结构体input_dev、input_handler、input_handle。 struct input_dev { void *private;