之前的四篇博文记录的都是linux中的input体系相关的东西,最底层以我调试的usb触摸屏的设备驱动为例,贴出链接:
Linux/Android——usb触摸屏驱动 - usbtouchscreen (一)
Linux/Android——输入子系统input_event传递 (二)
Linux/Android——input子系统核心 (三)
Linux/Android——input_handler之evdev (四)
在第二篇有记录input体系整体脉络,博文顺序也差不多是从下往上,这些都没有涉及到android这边的内容,这篇记录一下kernel与android的framework层的关联.
撰写不易,转载需注明出处:http://blog.csdn.net/jscese/article/details/42291149#t6
在kernel启动完全之后,input以及evdev都已初始化完,先看在kernel中打开input核心设备的接口input_open_file,
也是android这边frameworks首先会调用到的地方,至于怎么调用到的,后面分析
在第三篇input核心中,有介绍到注册input这个设备的时候,fops中就有这个input_open_file,现在来看看:
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; err = mutex_lock_interruptible(&input_mutex); if (err) return err; /* No load-on-demand here? */ handler = input_table[iminor(inode) >> 5]; //根据索引节点求次设备号除以32,找对应的绑定的事件处理器handler,这里如果得到的次设备号是64~96之间 即为evdev的handler,这个input_table数组的维护在上篇有介绍 if (handler) new_fops = fops_get(handler->fops); //获取handler的fops mutex_unlock(&input_mutex); /* * That's _really_ odd. Usually NULL ->open means "nothing special", * not "no device". Oh, well... */ if (!new_fops || !new_fops->open) { fops_put(new_fops); err = -ENODEV; goto out; } old_fops = file->f_op; file->f_op = new_fops; //如果事件处理器有 file_operarions 就赋值给现在的file->f_op ,替换了原来的 err = new_fops->open(inode, file); //这里调用的是 事件处理器file方法中的open方法, if (err) { fops_put(file->f_op); file->f_op = fops_get(old_fops); } fops_put(old_fops); out: return err; }
前文有介绍evdev_handler的功能与注册,这里看下这个handler注册的方法:
static const struct file_operations evdev_fops = { .owner = THIS_MODULE, .read = evdev_read, .write = evdev_write, .poll = evdev_poll, .open = evdev_open, .release = evdev_release, .unlocked_ioctl = evdev_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = evdev_ioctl_compat, #endif .fasync = evdev_fasync, .flush = evdev_flush, .llseek = no_llseek, };
这个字符设备文件就是连接kernel与framework的桥梁了!
可以看到这里有个evdev_open方法,这个方法就是打开设备文件的
static int evdev_open(struct inode *inode, struct file *file) { struct evdev *evdev; struct evdev_client *client; int i = iminor(inode) - EVDEV_MINOR_BASE; //通过节点算出 minor序号,这个在connect 生成event%d 时有+操作,所以减去BASE unsigned int bufsize; int error; if (i >= EVDEV_MINORS) return -ENODEV; error = mutex_lock_interruptible(&evdev_table_mutex); if (error) return error; evdev = evdev_table[i]; // 这个数组就是以minor为索引,存每次匹配上的evdev ... bufsize = evdev_compute_buffer_size(evdev->handle.dev); //往下都是 分配初始化一个evdev_client 变量 client = kzalloc(sizeof(struct evdev_client) + bufsize * sizeof(struct input_event), GFP_KERNEL); if (!client) { error = -ENOMEM; goto err_put_evdev; } client->bufsize = bufsize; spin_lock_init(&client->buffer_lock); snprintf(client->name, sizeof(client->name), "%s-%d", dev_name(&evdev->dev), task_tgid_vnr(current)); client->evdev = evdev; evdev_attach_client(evdev, client); //将这个client加入到evdev的client_list链表中 error = evdev_open_device(evdev); // 这里深入打开,传入的是设备匹配成功时在evdev_handler中创建的evdev ... }
static int evdev_open_device(struct evdev *evdev) { int retval; retval = mutex_lock_interruptible(&evdev->mutex); if (retval) return retval; if (!evdev->exist) retval = -ENODEV; else if (!evdev->open++) { //判断是否打开了,初始分配kzalloc,所以open为0,没有打开的话,这里继续调用打开,open计数为1 retval = input_open_device(&evdev->handle); if (retval) evdev->open--; } mutex_unlock(&evdev->mutex); return retval; }
可以看到这里绕了一圈,由最开始的input_open_file,最后又回到input核心中去了。调用到input.c中的接口,传入的是设备当初匹配成功的组合handle
int input_open_device(struct input_handle *handle) { struct input_dev *dev = handle->dev; //取对应的input_dev int retval; retval = mutex_lock_interruptible(&dev->mutex); if (retval) return retval; if (dev->going_away) { retval = -ENODEV; goto out; } handle->open++; // handle数++ ,上面是evdev的open++ 不一样 if (!dev->users++ && dev->open) // 这个dev没有被其它进程占用,并且设备有open方法 retval = dev->open(dev); //调用input_dev设备的open方法 ,这个是在设备驱动注册这个input_dev时初始化的 if (retval) { //失败处理情况 dev->users--; if (!--handle->open) { /* * Make sure we are not delivering any more events * through this handle */ synchronize_rcu(); } } out: mutex_unlock(&dev->mutex); return retval; }
input_dev->open = usbtouch_open;
到这里打开设备这一步就完成了!
这个是evdev设备的读取函数,注册在fops里:
static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { struct evdev_client *client = file->private_data; //这个客户端结构在打开的时候分配并保存在file->private_data中 struct evdev *evdev = client->evdev; struct input_event event; int retval; if (count < input_event_size()) return -EINVAL; //这条语句提示,用户进程每次读取设备的字节数,不要少于input_event结构的大小 if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK)) return -EAGAIN; //head等于tail说明目前还没有事件传回来,如果设置了非阻塞操作,则会立刻返回 retval = wait_event_interruptible(evdev->wait, client->head != client->tail || !evdev->exist); //没有事件就会睡在evdev的等待队列上了,等待条件是有事件到来或者设备不存在了(设备关闭的时候,清这个标志) if (retval) return retval; //如果能执行上面这条语句说明有事件传来或者,设备被关闭了,或者内核发过来终止信号 if (!evdev->exist) return -ENODEV; while (retval + input_event_size() <= count && evdev_fetch_next_event(client, &event)) { // evdev_fetch_next_event这个函数遍历client里面的input_event buffer数组 if (input_event_to_user(buffer + retval, &event)) //将事件复制到用户空间 return -EFAULT; retval += input_event_size(); } return retval; //返回复制的数据字节数 }
接下来看android的frameworks层 怎么去打开这个input 设备文件的.
framework层相关的处理机制,后续的博文会详细分析,这里只是简单的记录一下与kernel中这些接口的交互,好对android input的运作体系有个整体的概念 !
这个的源码在/frameworks/base/services/input/InputReader.cpp 这个在第二篇,总的脉络图上有,属于input service中一部分,看名字就知道这是一个读取input事件的.
等待输入事件到来的自然会是个loop结构设计.
bool InputReaderThread::threadLoop() { mReader->loopOnce(); return true; }
void InputReader::loopOnce() { int32_t oldGeneration; int32_t timeoutMillis; ... size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); //这里就是关键了,通过另外一个中间者EventHub 获取的input事件 ... }
源码位于/frameworks/base/services/input/EventHub.cpp
这个里面其它的先不管,这里先介绍下跟本篇有关系的
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) { ... for (;;) { ... scanDevicesLocked(); //这个往里走就是通过EventHub::openDeviceLocked 打开*DEVICE_PATH = "/dev/input" 这个设备 ,最终用的open,实际到kernel层就是input设备注册的open ... int32_t readSize = read(device->fd, readBuffer, //这里的device->fd就是/dev/input/event%d这个设备文件,就是从这里读取出event的buffer sizeof(struct input_event) * capacity); ... } .. }
这里的read实际上的操作就是上面介绍的 evdev_read 函数!
至此,kernel层的设备以及事件与android这边的frameworks的input服务处理之间就联系起来了,这里frameworks这边稍微提一下,后续分析细节!