linux input系统简析
来源:互联网 发布:js中的eval优缺点 编辑:程序博客网 时间:2024/06/16 09:08
输入子系统由驱动层(Drivers),输入子系统核心层(Input Core)和事件处理层(EventHandler)三部份组成;
(1)从设备驱动开始分析,以kernel/driver/touchscreen/ft5x06_ts.c的tp驱动为例,在tp驱动probe的过程当中,创建input_dev;
struct input_dev { ......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)];//绝对设备,tp的坐标x,y就用这表示unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];//杂项设备unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];//led灯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 hint_events_per_packet; ......struct device dev;struct list_headh_list;struct list_headnode;};
然后分配大小input = input_allocate_device();原型在/kernel/driver/input/input.c里面,很简单通过kmalloc分配大小,并初始话后面会用到的两个链表;
下面为tp驱动当中设置input事件的类型和初始值和范围,
__set_bit(EV_KEY, input->evbit);__set_bit(EV_ABS, input->evbit);__set_bit(EV_SYN, input->evbit);__set_bit(BTN_TOUCH, input->keybit);__set_bit(ABS_MT_TOUCH_MAJOR, input->absbit);//tp按下去压力值__set_bit(ABS_MT_POSITION_X, input->absbit);//x坐标值__set_bit(ABS_MT_POSITION_Y, input->absbit);//y坐标值__set_bit(ABS_MT_WIDTH_MAJOR, input->absbit);//按键的范围值__set_bit(KEY_MENU, input->keybit);__set_bit(KEY_BACK, input->keybit);__set_bit(KEY_HOME, input->keybit);__set_bit(KEY_SEARCH, input->keybit);input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);input_set_abs_params(input, ABS_MT_POSITION_X, 0, X_MAX, 0, 0);input_set_abs_params(input, ABS_MT_POSITION_Y, 0, Y_MAX, 0, 0);input_set_abs_params(input, ABS_MT_WIDTH_MAJOR, 0, 200, 0, 0);
然后注册中断 和初始化工作队列,中端函数里面定时去读取取tp所报的值,然后调度队列当中的工作,通过input_report_key和input_report_abs上报键值core层处理;
input_report_abs(data->input, ABS_MT_TOUCH_MAJOR, event->pressure); input_report_abs(data->input, ABS_MT_POSITION_X, event->x); input_report_abs(data->input, ABS_MT_POSITION_Y, event->y); input_report_abs(data->input, ABS_MT_WIDTH_MAJOR, 1); input_mt_sync(data->input);//report结束
input_report_abs在input.h实现最后调用input_event,这里就到了input的重点;
probe后面就进行input_register_device;int input_register_device(struct input_dev *dev){ ..... init_timer(&dev->timer); if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) { dev->timer.data = (long) dev; dev->timer.function = input_repeat_key; dev->rep[REP_DELAY] = 250; dev->rep[REP_PERIOD] = 33; } ..... list_add_tail(&dev->node, &input_dev_list); list_for_each_entry(handler, &input_handler_list, node) input_attach_handler(dev, handler); ....}EXPORT_SYMBOL(input_register_device);最后去遍历hanlderlist的链表根据设备去配对相应的hanlder就此驱动这边已经走完;
(2)先分析先看看/kernel/driver/input/evdev,evdev输入事件驱动,为输入子系统提供了一个默认的事件处理方法。其接收来自底层驱动的大多数事件,并使用相应的逻辑对其进行处理。
先看evdev_init函数,简单的调用input_register_handler函数并传递一个input_handler的一个结构体;
int input_register_handler(struct input_handler *handler){......list_add_tail(&handler->node, &input_handler_list);list_for_each_entry(dev, &input_dev_list, node)input_attach_handler(dev, handler); ......}通过list_add_tail把传递进来的这个handler加入到input_handler_list;然后通过遍历整个input_dev_list通过input_attach_handler函数去一个一个的比较
input_attach_handler --> input_match_device ;注册handler的结构体里有.id_table = evdev_ids
static const struct input_device_id evdev_ids[] = {{ .driver_info = 1 },/* Matches all devices */{ },/* Terminating zero entry */};
inputdevice都挂在input_dev_list链上,所有的handler都挂在input_handler_list上,通过input_attach_handler来进行匹配,并将其挂以input_dev_list.与挂载在input_handler_list中的handler相匹配。如果匹配成功,就会调用handler的connect函数.
static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id){ struct evdev *evdev; ...... //创建并初始化一个evdev设备; error = input_register_handle(&evdev->handle); ......}
创建并初始化一个evdev设备,最终去调用input_register_handle函数
int input_register_handle(struct input_handle *handle){struct input_handler *handler = handle->handler;struct input_dev *dev = handle->dev;int error; ......if (handler->filter)list_add_rcu(&handle->d_node, &dev->h_list);elselist_add_tail_rcu(&handle->d_node, &dev->h_list);mutex_unlock(&dev->mutex); ........list_add_tail_rcu(&handle->h_node, &handler->h_list);if (handler->start)handler->start(handle);//start 为NULLreturn 0;}EXPORT_SYMBOL(input_register_handle);
handle就注册成功,这里注册成功的handle最后添加到handler和input dev这两个list上面,handle就代表一个成功配对;
struct input_handle { void *private; //每个配对的事件处理器都会分配一个对应的设备结构,如evdev事件处理器的evdev结构,注意这个结构与设备驱动层的input_dev不同,初始化handle时,保存到这里。 int open; //打开标志,每个input_handle 打开后才能操作,这个一般通过事件处理器的open方法间接设置 const char *name; struct input_dev *dev; //关联的input_dev结构 struct input_handler *handler; //关联的input_handler结构 struct list_head d_node; //input_handle通过d_node连接到了input_dev上的h_list链表上 struct list_head h_node; //input_handle通过h_node连接到了input_handler的h_list链表上 };
(3)进入重要的环节input_event
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { unsigned long flags; if (is_event_supported(type, dev->evbit, EV_MAX)) { //判断是否支持此种事件类型和事件类型中的编码类型 spin_lock_irqsave(&dev->event_lock, flags); add_input_randomness(type, code, value); //对系统随机熵池有贡献,因为这个也是一个随机过程 input_handle_event(dev, type, code, value); //这个函数是事件处理的关键函数,下面详细分析 spin_unlock_irqrestore(&dev->event_lock, flags); } }
input_handle_event:
这个函数主要是根据事件类型的不同,做相应的处理。这里之关心EV_KEY类型,其他函数和事件传递关系不大,只要关心,disposition这个是事件处理的方式,默认的是INPUT_IGNORE_EVENT,忽略这个事件,如果是INPUT_PASS_TO_HANDLERS则是传递给事件处理器,如果是INPUT_PASS_TO_DEVICE,则是传递给设备处理,触摸屏驱动没有定义这个。input_handle_abs_event -> input_pass_event:
static void input_pass_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { struct input_handle *handle; rcu_read_lock(); handle = rcu_dereference(dev->grab); //如果是绑定的handle,则调用绑定的handler->event函数 if (handle) handle->handler->event(handle, type, code, value); else //如果没有绑定,则遍历dev的h_list链表,寻找handle,如果handle已经打开,说明有进程读取设备关联的evdev。 list_for_each_entry_rcu(handle, &dev->h_list, d_node) if (handle->open) handle->handler->event(handle, type, code, value); // 调用相关的事件处理器的event函数,进行事件的处理 rcu_read_unlock(); }最后调用evdev的evnet函数
static void evdev_event(struct input_handle *handle,unsigned int type, unsigned int code, int value){.....list_for_each_entry_rcu(client, &evdev->client_list, node)evdev_pass_event(client, &event);.....}最后调用evdev_pass_event函数:static void evdev_pass_event(struct evdev_client *client, struct input_event *event){ /* Interrupts are disabled, just acquire the lock. */ spin_lock(&client->buffer_lock); wake_lock_timeout(&client->wake_lock, 5 * HZ); client->buffer[client->head++] = *event;//将事件复制给input_event数组; client->head &= client->bufsize - 1; if (unlikely(client->head == client->tail)) { /* * This effectively "drops" all unconsumed events, leaving * EV_SYN/SYN_DROPPED plus the newest event in the queue. */ client->tail = (client->head - 2) & (client->bufsize - 1); client->buffer[client->tail].time = event->time; client->buffer[client->tail].type = EV_SYN; client->buffer[client->tail].code = SYN_DROPPED; client->buffer[client->tail].value = 0; client->packet_head = client->tail; } if (event->type == EV_SYN && event->code == SYN_REPORT) { client->packet_head = client->head; kill_fasync(&client->fasync, SIGIO, POLL_IN); } spin_unlock(&client->buffer_lock);}
evdev_pass_event函数最终将事件传递给了用户端的client结构中的input_event数组中,只需将这个input_event数组复制给用户空间,进程就能收到触摸屏按下的信息了。具体处理由具体的应用程序来完成。
- linux input系统简析
- linux input系统
- 我画的Linux Input系统框图
- input系统
- 【Linux系统编程应用】 Linux Input子系统(一)
- 和菜鸟一起学linux之input系统简单实例
- Linux/Android——Input系统之InputReader (七)
- Linux/Android——Input系统之InputMapper 处理 (八)
- linux input系统的分析笔记(一)
- Linux/Android——Input系统之InputReader (七)
- 【doc/input】input系统基础
- Nebula3的Input系统
- Unity3D Input按键系统
- andriod input 系统架构
- android input 系统----1
- [Unity3D] Input按键系统
- Unity3D Input按键系统
- android input系统
- c# 常用固定常量
- ios中常用的裁剪图片方法小结
- POJ 2418--Hardwood Species【map】
- 终极解决:iOS 与 Java 服务器之间 SSL 握手失败的解决:Cipher Suites
- 自动人脸识别基本原理
- linux input系统简析
- C++编程技巧总结
- Oracle 11g表空间和数据文件管理(《Oracle从入门到精通》读书笔记3)
- MySQL升级后1728错误解决方案
- 使用Windows CryptoAPI计算MD5
- 打开远程桌面命令
- 012_ViewPager 翻译学习
- 这篇dijkstra算法写得比书上简单
- POJ 1182--食物链【并查集,向量偏移】