本文转载自:http://blog.csdn.net/jscese/article/details/42291149

之前的四篇博文记录的都是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_open_file:

在第三篇input核心中,有介绍到注册input这个设备的时候,fops中就有这个input_open_file,现在来看看:

  1. static int input_open_file(struct inode *inode, struct file *file)
  2. {
  3. struct input_handler *handler;
  4. const struct file_operations *old_fops, *new_fops = NULL;
  5. int err;
  6. err = mutex_lock_interruptible(&input_mutex);
  7. if (err)
  8. return err;
  9. /* No load-on-demand here? */
  10. handler = input_table[iminor(inode) >> 5];   //根据索引节点求次设备号除以32,找对应的绑定的事件处理器handler,这里如果得到的次设备号是64~96之间 即为evdev的handler,这个input_table数组的维护在上篇有介绍
  11. if (handler)
  12. new_fops = fops_get(handler->fops); //获取handler的fops
  13. mutex_unlock(&input_mutex);
  14. /*
  15. * That's _really_ odd. Usually NULL ->open means "nothing special",
  16. * not "no device". Oh, well...
  17. */
  18. if (!new_fops || !new_fops->open) {
  19. fops_put(new_fops);
  20. err = -ENODEV;
  21. goto out;
  22. }
  23. old_fops = file->f_op;
  24. file->f_op = new_fops;  //如果事件处理器有 file_operarions 就赋值给现在的file->f_op ,替换了原来的
  25. err = new_fops->open(inode, file);  //这里调用的是 事件处理器file方法中的open方法,
  26. if (err) {
  27. fops_put(file->f_op);
  28. file->f_op = fops_get(old_fops);
  29. }
  30. fops_put(old_fops);
  31. out:
  32. return err;
  33. }

这里如果是打开evdev的,调用到的是evdev_handler中的evdev_fops的open方法!

evdev_fops:

前文有介绍evdev_handler的功能与注册,这里看下这个handler注册的方法:

  1. static const struct file_operations evdev_fops = {
  2. .owner      = THIS_MODULE,
  3. .read       = evdev_read,
  4. .write      = evdev_write,
  5. .poll       = evdev_poll,
  6. .open       = evdev_open,
  7. .release    = evdev_release,
  8. .unlocked_ioctl = evdev_ioctl,
  9. #ifdef CONFIG_COMPAT
  10. .compat_ioctl   = evdev_ioctl_compat,
  11. #endif
  12. .fasync     = evdev_fasync,
  13. .flush      = evdev_flush,
  14. .llseek     = no_llseek,
  15. };

都是字面意思,前文也提到匹配connect的时候,在evdev_connect中注册生成了 /sys/class/input/event%d ,

这个字符设备文件就是连接kernel与framework的桥梁了!

可以看到这里有个evdev_open方法,这个方法就是打开设备文件的

evdev_open:

  1. static int evdev_open(struct inode *inode, struct file *file)
  2. {
  3. struct evdev *evdev;
  4. struct evdev_client *client;
  5. int i = iminor(inode) - EVDEV_MINOR_BASE;  //通过节点算出 minor序号,这个在connect 生成event%d 时有+操作,所以减去BASE
  6. unsigned int bufsize;
  7. int error;
  8. if (i >= EVDEV_MINORS)
  9. return -ENODEV;
  10. error = mutex_lock_interruptible(&evdev_table_mutex);
  11. if (error)
  12. return error;
  13. evdev = evdev_table[i]; // 这个数组就是以minor为索引,存每次匹配上的evdev
  14. ...
  15. bufsize = evdev_compute_buffer_size(evdev->handle.dev);  //往下都是 分配初始化一个evdev_client 变量
  16. client = kzalloc(sizeof(struct evdev_client) +
  17. bufsize * sizeof(struct input_event),
  18. GFP_KERNEL);
  19. if (!client) {
  20. error = -ENOMEM;
  21. goto err_put_evdev;
  22. }
  23. client->bufsize = bufsize;
  24. spin_lock_init(&client->buffer_lock);
  25. snprintf(client->name, sizeof(client->name), "%s-%d",
  26. dev_name(&evdev->dev), task_tgid_vnr(current));
  27. client->evdev = evdev;
  28. evdev_attach_client(evdev, client);  //将这个client加入到evdev的client_list链表中
  29. error = evdev_open_device(evdev); // 这里深入打开,传入的是设备匹配成功时在evdev_handler中创建的evdev
  30. ...
  31. }

继续看

  1. static int evdev_open_device(struct evdev *evdev)
  2. {
  3. int retval;
  4. retval = mutex_lock_interruptible(&evdev->mutex);
  5. if (retval)
  6. return retval;
  7. if (!evdev->exist)
  8. retval = -ENODEV;
  9. else if (!evdev->open++) {  //判断是否打开了,初始分配kzalloc,所以open为0,没有打开的话,这里继续调用打开,open计数为1
  10. retval = input_open_device(&evdev->handle);
  11. if (retval)
  12. evdev->open--;
  13. }
  14. mutex_unlock(&evdev->mutex);
  15. return retval;
  16. }


可以看到这里绕了一圈,由最开始的input_open_file,最后又回到input核心中去了。调用到input.c中的接口,传入的是设备当初匹配成功的组合handle

input_open_device:

  1. int input_open_device(struct input_handle *handle)
  2. {
  3. struct input_dev *dev = handle->dev;  //取对应的input_dev
  4. int retval;
  5. retval = mutex_lock_interruptible(&dev->mutex);
  6. if (retval)
  7. return retval;
  8. if (dev->going_away) {
  9. retval = -ENODEV;
  10. goto out;
  11. }
  12. handle->open++;  // handle数++ ,上面是evdev的open++ 不一样
  13. if (!dev->users++ && dev->open)   // 这个dev没有被其它进程占用,并且设备有open方法
  14. retval = dev->open(dev); //调用input_dev设备的open方法 ,这个是在设备驱动注册这个input_dev时初始化的
  15. if (retval) {  //失败处理情况
  16. dev->users--;
  17. if (!--handle->open) {
  18. /*
  19. * Make sure we are not delivering any more events
  20. * through this handle
  21. */
  22. synchronize_rcu();
  23. }
  24. }
  25. out:
  26. mutex_unlock(&dev->mutex);
  27. return retval;
  28. }

这里最终是调用设备驱动注册input_dev时的open方法,现在返回看看我这边注册usbtouchscreen时的input_dev 的open方法:

  1. input_dev->open = usbtouch_open;

这个再往下就是设备驱动干的事了。。这里就不分析usbtouch_open 做了什么了,无非是一些初始化操作之类的

到这里打开设备这一步就完成了!

evdev_read:

这个是evdev设备的读取函数,注册在fops里:

  1. static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
  2. {
  3. struct evdev_client *client = file->private_data; //这个客户端结构在打开的时候分配并保存在file->private_data中
  4. struct evdev *evdev = client->evdev;
  5. struct input_event event;
  6. int retval;
  7. if (count < input_event_size())
  8. return -EINVAL;
  9. //这条语句提示,用户进程每次读取设备的字节数,不要少于input_event结构的大小
  10. if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))
  11. return -EAGAIN;
  12. //head等于tail说明目前还没有事件传回来,如果设置了非阻塞操作,则会立刻返回
  13. retval = wait_event_interruptible(evdev->wait, client->head != client->tail || !evdev->exist);
  14. //没有事件就会睡在evdev的等待队列上了,等待条件是有事件到来或者设备不存在了(设备关闭的时候,清这个标志)
  15. if (retval)
  16. return retval;
  17. //如果能执行上面这条语句说明有事件传来或者,设备被关闭了,或者内核发过来终止信号
  18. if (!evdev->exist)
  19. return -ENODEV;
  20. while (retval + input_event_size() <= count && evdev_fetch_next_event(client, &event))
  21. {
  22. // evdev_fetch_next_event这个函数遍历client里面的input_event buffer数组
  23. if (input_event_to_user(buffer + retval, &event))
  24. //将事件复制到用户空间
  25. return -EFAULT;
  26. retval += input_event_size();
  27. }
  28. return retval; //返回复制的数据字节数
  29. }

接下来看android的frameworks层 怎么去打开这个input 设备文件的.

framework层相关的处理机制,后续的博文会详细分析,这里只是简单的记录一下与kernel中这些接口的交互,好对android input的运作体系有个整体的概念 !

InputReader:

这个的源码在/frameworks/base/services/input/InputReader.cpp 这个在第二篇,总的脉络图上有,属于input service中一部分,看名字就知道这是一个读取input事件的.

等待输入事件到来的自然会是个loop结构设计.

  1. bool InputReaderThread::threadLoop() {
  2. mReader->loopOnce();
  3. return true;
  4. }

然后看一下这个loopOnce:

  1. void InputReader::loopOnce() {
  2. int32_t oldGeneration;
  3. int32_t timeoutMillis;
  4. ...
  5. size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); //这里就是关键了,通过另外一个中间者EventHub 获取的input事件
  6. ...
  7. }

EventHub:

源码位于/frameworks/base/services/input/EventHub.cpp

这个里面其它的先不管,这里先介绍下跟本篇有关系的

  1. size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
  2. ...
  3. for (;;) {
  4. ...
  5. scanDevicesLocked(); //这个往里走就是通过EventHub::openDeviceLocked  打开*DEVICE_PATH = "/dev/input" 这个设备 ,最终用的open,实际到kernel层就是input设备注册的open
  6. ...
  7. int32_t readSize = read(device->fd, readBuffer, //这里的device->fd就是/dev/input/event%d这个设备文件,就是从这里读取出event的buffer
  8. sizeof(struct input_event) * capacity);
  9. ...
  10. }
  11. ..
  12. }

这里的read实际上的操作就是上面介绍的 evdev_read 函数!

至此,kernel层的设备以及事件与android这边的frameworks的input服务处理之间就联系起来了,这里frameworks这边稍微提一下,后续分析细节!

Linux/Android——input系统之 kernel层 与 frameworks层交互 (五)【转】的更多相关文章

  1. Linux/Android——Input系统之frameworks层InputManagerService (六)【转】

    本文转载自:http://blog.csdn.net/u013491946/article/details/72638954 版权声明:免责声明: 本人在此发文(包括但不限于汉字.拼音.拉丁字母)均为 ...

  2. Linux/Android——Input系统之InputReader (七)【转】

    本文转载自:http://blog.csdn.net/jscese/article/details/42739197 在前文Linux/Android——Input系统之frameworks层Inpu ...

  3. Linux/Android——Input系统之InputMapper 处理 (八)【转】

    本文转载自:http://blog.csdn.net/jscese/article/details/43561773 前文Linux/Android——Input系统之InputReader (七)介 ...

  4. input系统——android input系统

    AndroidInput系统--JNI NativeInputManager InputManger InputReader AndroidInput系统--InputReader AndroidIn ...

  5. Linux/Android——input子系统核心 (三)【转】

    本文转载自:http://blog.csdn.net/jscese/article/details/42123673 之前的博客有涉及到linux的input子系统,这里学习记录一下input模块. ...

  6. Linux 下Input系统应用编程实战

    作者:杨源鑫(也是我们的校园代理) 经授权转载于公众号嵌入式开发圈,有些许修改. 什么是input子系统?不管是什么操作系统,都有一个程序用于管理各种输入设备,哪些是输入设备?比如,电脑键盘.鼠标,智 ...

  7. Linux/Android——input_handler之evdev (四) 【转】

    转自:http://blog.csdn.net/u013491946/article/details/72638919 版权声明:免责声明: 本人在此发文(包括但不限于汉字.拼音.拉丁字母)均为随意敲 ...

  8. Linux & Android 多点触摸协议

    Linux & Android 多点触摸协议 Android4.0多点触摸入门 1 KERNEL 对于触摸屏的驱动我们简单的划分为两个主要的部分,一个是注册,另一个是上报. 1.1 注册 单点 ...

  9. Linux/Android——输入子系统input_event传递 (二)【转】

    本文转载自:http://blog.csdn.net/jscese/article/details/42099381 在前文Linux/Android——usb触摸屏驱动 - usbtouchscre ...

随机推荐

  1. python爬取网页图片

    # html:网页地址 def getImg2(html): soup = BeautifulSoup(html, 'html.parser') href_regex = re.compile(r'^ ...

  2. <Linux> 下安装和卸载JDK

    安装 下载jdk https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 在local ...

  3. Linux修改启动界面、分辨率

    初识Linux 初识Linux(Centos 7.x),积累一些小技巧. 修改命令行界面的分辨率 # 备份配置文件 # 有些系统路径是/boot/grub...或者/boot/grub/menu.ls ...

  4. 合并多个MP4文件

    把多个MP4文件连接起来的方法与音频文件不太一样,比较有效的方法是: $ cat mylist.txt file '/path/to/file1' file '/path/to/file2' file ...

  5. GPIO——端口位设置/清除寄存器BSRR,端口位清除寄存器BRR

    端口位设置/复位寄存器BSRR: 注:如果同时设置了BSy和BRy的对应位,BSy位起作用. 位31:16  BRy: 清除端口x的位y (y = 0…15)      这些位只能写入并只能以字(16 ...

  6. 洛谷 3953 NOIP2017提高组Day1 T3 逛公园

    [题解] 先建反向图,用dijkstra跑出每个点到n的最短距离dis[i] 设f[u][k]表示dis(u,n)<=mindis(u,n)+k的方案数.对于边e(u,v,w),走了这条边的话需 ...

  7. javascript倒计时代码及倒计时弹窗

    在前端开发中,难免会用到倒计时.如做的双十一活动,在距活动开始的半个月前需要做些宣传工作,需要告知用户优惠活动什么时候开始.这个时候就要用到倒计时,如在整站的某个页面提醒用户活动什么时候开始等.而在活 ...

  8. TCP/IP学习笔记(5)------IP选路

    静态IP选路 一个简单的路由表 选路是IP层最重要的一个功能之一.前面的部分已经简单的讲过路由器是通过何种规则来根据IP数据包的IP地址来选择路由.这里就不重复了.首先来看看一个简单的系统路由表. D ...

  9. Redundant Paths-POJ3177(强连通缩点)

    http://poj.org/problem?id=3177 题目大意:给你几个点和几条边   求你能加几条边  就可以让每一个点到达任意点都有两种方法. Description In order t ...

  10. codevs——2750 心系南方灾区

    2750 心系南方灾区  时间限制: 1 s  空间限制: 2000 KB  题目等级 : 青铜 Bronze 题解  查看运行结果     题目描述 Description 现在我国南方正在承受百年 ...