内容简介: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——设备驱动初探》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Kafka权威指南
Neha Narkhede、Gwen Shapira、Todd Palino / 薛命灯 / 人民邮电出版社 / 2017-12-26 / 69.00元
每个应用程序都会产生数据,包括日志消息、度量指标、用户活动记录、响应消息等。如何移动数据,几乎变得与数据本身一样重要。如果你是架构师、开发者或者产品工程师,同时也是Apache Kafka新手,那么这本实践指南将会帮助你成为流式平台上处理实时数据的专家。 本书由出身于LinkedIn的Kafka核心作者和一线技术人员共同执笔,详细介绍了如何部署Kafka集群、开发可靠的基于事件驱动的微服务,......一起来看看 《Kafka权威指南》 这本书的介绍吧!