内容简介:Input之我见01——设备驱动初探
在我们日常接触的计算机模型中,可以抽象成输入模块、输出模块和运算单元。随着技术不断发展革新,输入、输出设备都有着不同程度的发展。尤其是输入设备,让人机交互有了很大变化。在Android系统中,对于输出设备事件是处理是基本 linux 的输入子系统,同时也有了自己的特征。我试图通过自己整理资料、研究代码、调试跟踪来理清之间的脉络,同时也是Android系统一个从底层到上层的脉络。由于某些方面的局限,也许无法能解决你心中所有疑问,希望对你有所启发。如果文中有不错误疏漏的,也欢迎你提意见。
该小节先讲Linux Input子系统的内容
在Kernel里面主要分三层,设备驱动层、Input Core、事件驱动层。
相关数据结构
- 静态数据结构
存储Input设备的信息,主要位于input.h
struct input_id { __u16 bustype; __u16 vendor; __u16 product; __u16 version; };
input_event用于存储每一次传输的数据,这是个固定大小的结构体。
struct input_event { struct timeval time; __u16 type; __u16 code; __s32 value; };
其中type定义 在Kernel(3.14)的Input子系统中,定义如下18种Input事件,常用的也就是前面5,6种
/* Events */ #define EV_SYN 0x00 //用于同步 #define EV_KEY 0x01 //用于按键,遥控器等,键值的输入设备 #define EV_REL 0x02 //相对事件,用于鼠标的输入 #define EV_ABS 0x03 //绝对事件,用于触摸屏 #define EV_MSC 0x04 #define EV_LED 0x11 #define EV_SND 0x12 #define EV_REP 0x14 #define EV_FF 0x15 #define EV_PWR 0x16 #define EV_FF_STATUS 0x17 #define EV_MAX 0x1f
在Android7.0的EventHub中,对底层的事件类型,作了自己的分类,但有相应的对应关系
EventHub.h /* * Input device classes. */ enum { /* The input device is a keyboard or has buttons. */ INPUT_DEVICE_CLASS_KEYBOARD= 0x00000001, /* The input device is an alpha-numeric keyboard (not just a dial pad). */ INPUT_DEVICE_CLASS_ALPHAKEY= 0x00000002, /* The input device is a touchscreen or a touchpad (either single-touch or multi-touch). */ INPUT_DEVICE_CLASS_TOUCH= 0x00000004, ... };
- 动态数据结构
在Input事件驱动层, 以dvdev为例
input_handler:input管理者
struct input_handler{ void *private; void (*event)(struct input_handle*handle, unsigned int type, unsigned int code, int value); bool (*filter)(struct input_handle*handle, unsigned int type, unsigned int code, int value); bool (*match)(struct input_handler*handler, struct input_dev*dev); int (*connect)(struct input_handler*handler, struct input_dev*dev, const struct input_device_id*id); void (*disconnect)(struct input_handle*handle); void (*start)(struct input_handle*handle); ...... struct list_head h_list; struct list_head node; };
input_handle:input设备的句柄
struct input_handle { void *private; int open; const char *name; struct input_dev *dev; struct input_handler *handler; struct list_head d_node; struct list_head h_node; };
input_dev:input设备
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)]; .... int (*setkeycode)(struct input_dev *dev, const struct input_keymap_entry *ke, unsigned int *old_keycode); int (*getkeycode)(struct input_dev *dev, struct input_keymap_entry *ke); ... 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 (*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 __rcu *grab; ... struct device dev; struct list_head h_list; struct list_head node; };
这三者 Input_dev,hander,与Hande的关系
应该很清楚了吧,这里只以其中的一个input_dev和handler为例画出来的图,其他的以此类推。其中input_dev_list和input_handler_list是两条全局的链表,每当调用 input_register_device 函数时会将Input设备加入到input_dev_list链表的尾部;每当调用 input_register_handler 函数时会将handler加入到input_handler_list链表的尾部;每当调用 input_register_handle 函数时会将handle加入到其对应的Input设备的h_list链表的尾部,并且还会该handle加入到其对应的handler的h_list链表的尾部。
Input设备驱动
总共有三个核心的函数
input_register_handler(&evdev_handler);
input_register_handle(&evdev->handle);
input_register_device(remote->input);
前面两个是在事件驱动层的evdev.c的init函数和connect函数中调用;最后一个是在设备驱动中注册的。
我们从最底层的input_register_device分析起,以IR遥控器驱动为例
在probe函数中,进行input_register_device把该input设备注册进全局的input_dev_list链表
input_register_device(remote->input);
=》
int input_register_device(struct input_dev *dev) { ... //添加到全局设备 error = device_add(&dev->dev); ... //添加到设备链表 list_add_tail(&dev->node, &input_dev_list); //遍历input_handler_list链表,以node为关键字,把dev加入到handler_list中 list_for_each_entry(handler, &input_handler_list, node) input_attach_handler(dev, handler);
在input_attach_handler中,
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler) { id = input_match_device(handler, dev); if (!id) return -ENODEV; ... error = handler->connect(handler, dev, id); ... return error; }
通过id进行匹配,第一次的话hander链表中没有dev对应的handler,就进行connect,其实是调用input_register_handle()。把input_dev,input_handler,input_handle关联起来。
注册完成之后,一般就调用这两个函数发送数据到input子系统中
input_event(dev,EV_KEY,scancode,type); input_sync(dev);
主要看input_event
=》
input_handle_event(dev, type, code, value);
=》
input_pass_event(dev, type, code, value);
=》
handle->handler->event(handle, type, code, value);
在input_register_handler的时候,为dev分配了一个handler,并与handle进行关联了,此时就直接进入event函数了。
static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) { struct evdev *evdev = handle->private; struct evdev_client *client; struct input_event event; event.type = type; event.code = code; event.value = value; ... client = rcu_dereference(evdev->grab); if (client) evdev_pass_event(client, &event); else list_for_each_entry_rcu(client, &evdev->client_list, node) evdev_pass_event(client, &event); ... if (type == EV_SYN && code == SYN_REPORT) wake_up_interruptible(&evdev->wait); }
把事件通过evdev_pass_event传递给client,如果是同步事件的话,就调用wait,唤醒相应的监听进行。
以上所述就是小编给大家介绍的《Input之我见01——设备驱动初探》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
七周七语言(卷2)
【美】Bruce A. Tate(泰特)、Fred Daoud(达乌德)、Ian Dees(迪斯) / 7ML翻译组 / 人民邮电出版社 / 2016-12 / 59
深入研习对未来编程具有重要意义的7种语言 Lua、Factor、Elixir、Elm、Julia、Idris和MiniKanren 本书带领读者认识和学习7种编程语言,旨在帮助读者探索更为强大的编程工具。 本书延续了同系列的畅销书《七周七语言》《七周七数据库》和《七周七Web开发框架》的体例和风格。 全书共8章,前7章介绍了Lua、Factor、Elm、Elixir、Jul......一起来看看 《七周七语言(卷2)》 这本书的介绍吧!