if (handler->fops != NULL)
input_table[handler->minor >> 5] = handler;
list_add_tail(&handler->node, &input_handler_list);
list_for_each_entry(dev, &input_dev_list, node) if (!handler->blacklist
|| !input_match_device(handler->blacklist, dev))
if ((id = input_match_device(handler->id_table, dev))) if ((handle = handler->connect(handler, dev, id))) input_link_handle(handle);
input_wakeup_procfs_readers(); }
该函数中代码加粗的部分,与input_register_device()函数里的如出一辙,这里不再做说明。
至此input_handler的注册已经结束。
第四章、input子系统数据结构
下图3是以触摸屏设备为例子的input子系统数据结构图。
图3 input子系统数据结构图
进行到这里,又多冒出来了一个struct evdev_list的结构体,这在之前并没有提到过,因为在注册input_dev和input_handler过程中并没有用到过,查看该结构体:
struct evdev_list {
struct input_event buffer[EVDEV_BUFFER_SIZE]; //存放设备数据信息
int head; //buffer的下标,标识从设备中过来要存放到buffer的数据的位置
int tail; //buffer的下标,标识用户读取buffer中数据的下标位置 struct fasync_struct *fasync; struct evdev *evdev; struct list_head node; };
注意里面的input_event结构体,在/linux/input.h中有定义: struct input_event { struct timeval time; __u16 type; __u16 code; __s32 value; };
该input_event结构体就是存放着用户层所需的数据信息,这里想必我们都明白,则在struct evdev_list结构体中的struct input_event buffer[EVDEV_BUFFER_SIZE];就是存放着用户所需的数据信息,从硬件设备(触摸屏)获取得到的数据信息最终会存放到该数组中,而struct evdev_list结构体是在什么时候定义的呢?
当然是在被open的时候,查看open函数:
static int evdev_open(struct inode * inode, struct file * file) {
struct evdev_list *list; //定义一个evdev_list结构体 int i = iminor(inode) - EVDEV_MINOR_BASE; int accept_err;
if (i >= EVDEV_MINORS || !evdev_table[ i] || !evdev_table[ i]->exist) return -ENODEV;
if ((accept_err = input_accept_process(&(evdev_table[ i]->handle), file))) return accept_err;
if (!(list = kzalloc(sizeof(struct evdev_list), GFP_KERNEL))) //开辟evdev_list结构体内存空间 return -ENOMEM;
list->evdev = evdev_table[ i]; //令结构体中的evdev指针指向evdec_table[ i]
list_add_tail(&list->node, &evdev_table[ i]->list); //加入链表 file->private_data = list;
if (!list->evdev->open++) //如果设备没有被open,则继续if操作
if (list->evdev->exist)
input_open_device(&list->evdev->handle); //打开设备
return 0; }
注意函数中代码加粗的部分,都有注释说明,在最后以一个操作
input_open_device(&list->evdev->handle);继续跟踪进入该函数 int input_open_device(struct input_handle *handle) {
struct input_dev *dev = handle->dev; int err;
err = mutex_lock_interruptible(&dev->mutex); if (err)
return err;
handle->open++; //handle的内部成员open++ if (!dev->users++ && dev->open) err = dev->open(dev); if (err)
handle->open--;
mutex_unlock(&dev->mutex);
return err; }
函数中加粗的部分,为什么要让open++呢?因为数据要从device传到handler过程中有一个环节需要判断handle->open是否为真,为真表示设备已经打开数据可以传递,这点在下一章将会介绍到。
第四章、数据传递过程
正如图1所示,从硬件设备(触摸屏)中获得的数据需要经过input.c选择相应的handler进行处理,最后上报到用户空间。如何从硬件设备(触摸屏)中获得数据,那就得看xxx_ts.c中的代码了,在xxx_ts.c中的源代码是直接和硬件设备相关的。在xxx_ts.c中我们一方面要完成触摸屏设备相关的寄存器配置,另一方面要完成将获得的数据上报。
至于设备初始化配置方面,根据每个人使用的ARM芯片的Datasheet去初始化配置寄存器,这里也不需要多说了。不管是通过查询法还是中断法(我没见过用查询的),当触摸屏按下时候我们会得到触摸屏被按下的相关数据(主要是被
按下的X和Y坐标值),然后需要将数据信息上报,在触摸屏被按下的时候需要上报:
input_report_key(tsdev, BTN_TOUCH, 1); //报告按键被按下事件 input_report_abs(tsdev, ABS_X, x); //报告触摸屏被按下的x坐标值
input_report_abs(tsdev, ABS_Y, y); //报告触摸屏被按下的y坐标值
input_report_abs(tsdev, ABS_PRESSURE, 1); //报告触摸屏被按下的压力值(0或者1)
input_sync(tsdev); //报告同步事件,表示一次事件结束
当触笔从触摸屏上抬起时需要上报:
input_report_key(tsdev, BTN_TOUCH, 0); //报告按键被松开事件 input_report_abs(tsdev, ABS_PRESSURE, 0); //报告触摸屏被按下的压力值(0或者1)
input_sync(tsdev); //报告同步事件,表示一次事件结束
先不管input_sync()函数,先来看input_report_key()和input_report_abs(),这两个函数在/linux/input.h中有定义:
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value) {
input_event(dev, EV_KEY, code, !!value); }
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value) {
input_event(dev, EV_ABS, code, value); }
太坑爹了,这两个函数里面也就值调用了一个函数(我也不明白linux内核开发者为什么要这么做),继续跟踪查看input_event()函数,在input.c中有定义: void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) {
struct input_handle *handle;
if (type > EV_MAX || !test_bit(type, dev->evbit)) return;
add_input_randomness(type, code, value);
switch (type) {
…
case EV_KEY: //判断为按键事件