按键或者触摸屏输入设备是最常用不过的设备,那么如果一个按键信息是如何从内核传递到android的呢,首先我们得先清楚Linux的input子系统框架,下面是我在公司自己写的一篇文档,现在先粘帖过来 Linux之Input子系统分析 目前Android、QT等众多应用对于linux系统中键盘、鼠标、触摸屏等输入设备的支持都通过标准的input输入子系统。因为input子系统已经完成了字符驱动的文件操作接口,所以编写驱动的核心工作是完成input系统留出的接口,工作量相对小很多,下面我们将从input输入子系统框架以及基于3202中的触摸屏代码为实例进行分析。 一、input输入子系统框架 下图是input输入子系统框架,输入子系统由输入子系统核心层(input core),驱动层和事件处理层(Event Handler)三部分组成。一个输入事件,比如滑动触摸屏都是通过input driver -> input core -> event handler -> user space 到达用户空间传给应用程序。
Input输入子系统框架
二、Input driver编写要点 1. 分配、注册、注销input设备 参见触摸屏驱动ctp_it7250.c struct input_dev *input_allocate_device(void) int input_register_device(struct input_dev *dev) void input_unregister_device(struct input_dev *dev)
2. 设置input设备支持的事件类型、事件码、事件值的范围等信息 参见触摸屏驱动ctp_it7250.c __set_bit(EV_ABS, Ctp_it7250->input_dev->evbit); __set_bit(ABS_X, Ctp_it7250->input_dev->absbit); __set_bit(ABS_Y, Ctp_it7250->input_dev->absbit); __set_bit(EV_SYN, Ctp_it7250->input_dev->evbit); __set_bit(EV_KEY, Ctp_it7250->input_dev->evbit); __set_bit(BTN_TOUCH, Ctp_it7250->input_dev->keybit); Include/linux/input.h中定义了支持的类型 #define EV_SYN 0x00 #define EV_KEY 0x01 #define EV_REL 0x02 #define EV_ABS 0x03 #define EV_MSC 0x04 #define EV_SW 0x05 #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 #define EV_CNT (EV_MAX+1) 一个设备可以支持一个或多个事件类型。每个事件类型下面还需要设置具体的触发事件码。比如:EV_KEY事件,需要定义其支持哪些按键事件码。
3. 如果需要,设置input设备的打开、关闭的处理方法 参见触摸屏驱动ctp_it7250.c Ctp_it7250->input_dev->open = Ctp_it7250_touch_open; Ctp_it7250->input_dev->close = Ctp_it7250_touch_close;
4. 在发生输入事件时,向子系统报告事件 参见触摸屏驱动ctp_it7250.c input_report_key(Ctp_it7250->input_dev,ABS_MT_TRACKING_ID,0); input_report_abs(Ctp_it7250->input_dev, ABS_MT_POSITION_X, gpdwSampleX[0]); input_report_abs(Ctp_it7250->input_dev, ABS_MT_POSITION_Y, gpdwSampleY[0]); input_report_abs(Ctp_it7250->input_dev, ABS_MT_TOUCH_MAJOR, 1); input_mt_sync(Ctp_it7250->input_dev); input_sync(Ctp_it7250->input_dev); 而上述函数其实都是通过下面这个函数实现的 void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) 而此函数最终会调用handle->handler->event,也就是evdev_event函数 static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
三、Event Handler层解析 1. Input输入子系统数据结构关系图
2. Input_handler结构体 以evdev.c中的evdev_handler为例: static struct input_handler evdev_handler = { .event = evdev_event, //向系统报告input事件,系统通过read方法读取 .connect = evdev_connect, //和input_dev匹配后调用connect构建 .disconnect = evdev_disconnect, .fops = &evdev_fops, //event设备文件的操作方法 .minor = EVDEV_MINOR_BASE, //次设备号基准值 .name = "evdev", .id_table = evdev_ids, //匹配规则 };
3. Input字符设备注册过程 drivers/input/input.c中: static int __init input_init(void) { int err; …… err = class_register(&input_class); …… err = register_chrdev(INPUT_MAJOR, "input", &input_fops); …… } Input_fops定义: static const struct file_operations input_fops = { .owner = THIS_MODULE, .open = input_open_file, }; input_dev和input_handler匹配后调用input_handler的connect。以evdev_handler为例: static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id) { struct evdev *evdev; int minor; int error;
for (minor = 0; minor < EVDEV_MINORS; minor++) //找到字符设备event对应的次设备号 if (!evdev_table[minor]) break;
if (minor == EVDEV_MINORS) { printk(KERN_ERR "evdev: no more free evdev devices\n"); return -ENFILE; }
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); //为每个匹配evdev_handler的设备创建一个evdev if (!evdev) return -ENOMEM;
INIT_LIST_HEAD(&evdev->client_list); spin_lock_init(&evdev->client_lock); mutex_init(&evdev->mutex); init_waitqueue_head(&evdev->wait);
dev_set_name(&evdev->dev, "event%d", minor); evdev->exist = 1; evdev->minor = minor;
evdev->handle.dev = input_get_device(dev); evdev->handle.name = dev_name(&evdev->dev); evdev->handle.handler = handler; evdev->handle.private = evdev;
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor); //创建event字符设备节点 ……
4. Input字符设备的打开过程 static int input_open_file(struct inode *inode, struct file *file) { struct input_handler *handler; const struct file_operations *old_fops, *new_fops = NULL; int err;
lock_kernel(); /* No load-on-demand here? */ handler = input_table[iminor(inode) >> 5]; //得到对应的input_handler if (!handler || !(new_fops = fops_get(handler->fops))) { //取出对应input_handler的file_operations err = -ENODEV; goto out; }
/* * That's _really_ odd. Usually NULL ->open means "nothing special", * not "no device". Oh, well... */ if (!new_fops->open) { fops_put(new_fops); err = -ENODEV; goto out; } old_fops = file->f_op; file->f_op = new_fops; //重定位打开的设备文件的操作方法
err = new_fops->open(inode, file);
if (err) { fops_put(file->f_op); file->f_op = fops_get(old_fops); } fops_put(old_fops); out: unlock_kernel(); return err; }
5. Input字符设备的事件处理 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; //构造一个input_event结构体 struct timespec ts;
ktime_get_ts(&ts); event.time.tv_sec = ts.tv_sec; //设置事件的相关属性 event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC; event.type = type; event.code = code; event.value = value;
rcu_read_lock();
client = rcu_dereference(evdev->grab); if (client) evdev_pass_event(client, &event); //把事件加入到client链表中 else list_for_each_entry_rcu(client, &evdev->client_list, node) evdev_pass_event(client, &event);
rcu_read_unlock();
wake_up_interruptible(&evdev->wait); //解锁中断唤醒 }
6. Input字符设备的读操作 static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { struct evdev_client *client = file->private_data; struct evdev *evdev = client->evdev; struct input_event event; int retval;
if (count < input_event_size()) return -EINVAL;
if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK)) return -EAGAIN;
retval = wait_event_interruptible(evdev->wait, client->head != client->tail || !evdev->exist); //等待中断唤醒,并判断是否有数据,如果没有进入休眠 if (retval) return retval;
if (!evdev->exist) return -ENODEV;
while (retval + input_event_size() <= count && evdev_fetch_next_event(client, &event)) { //从buffer中取出数据传给user space,内部是通过copy_to_user函数实现的 if (input_event_to_user(buffer + retval, &event)) return -EFAULT;
retval += input_event_size(); }
return retval; }
如果上面的您看明白了,那么好我们继续往上走,以Android2.1为例,input上报的流程如下,蓝色为文件以及路径,红色为用到的函数 frameworks/base/services/java/com/android/server/KeyInputQueue.java->:Thread mThread = new Thread("InputDeviceReader") 其中EventHub.cpp为HAL层代码、com_android_server_KeyInputQueue.cpp为jni本地调用,KeyInputQueue.java为framework的server代码,具体细节可以自己去研究其代码。在Android2.3里多了几层的封装 frameworks/base/services/java/com/android/server/InputManager.java->:public void start() |
|