输入设备(如按键、键盘、触摸屏、鼠标等)是典型的字符设备,其一般的工作机理是底层在按键、触摸等动作发送时产生一个中断(或驱动通过timer定时查询),然后CPU通过SPI、I2 C或外部存储器总线读取键值、坐标等数据,放入1个缓冲区,字符设备驱动管理该缓冲区,而驱动的read()接口让用户可以读取键值、坐标等数据。

显然,在这些工作中,只是中断、读值是设备相关的,而输入事件的缓冲区管理以及字符设备驱动的file_operations接口则对输入设备是通用的。基于此,内核设计了输入子系统,由核心层处理公共的工作。硬件驱动层处理中断、读值相关(中断中上报读值)。

input子系统分层设计,共三层:硬件驱动层、子系统核心层、事件处理层。

驱动层由驱动程序员完成,主要处理中断(上报事件-读值)。

子系统核心层是链接其他两个层之间的纽带和桥梁,向下提供驱动层的接口,向上提供事件处理层的接口。

事件处理层负责与用户程序打交道,将硬件驱动层传来的事件报告给用户程序。

设备驱动并不创建文件节点,它只负责将采集到数据通过input.c中的函数input_event() 向上一层汇报而各个事件驱动则分别将他们各自感兴趣的事件信息提取出来,通过文件节点,提供给用户。在这个过程中,input 子系统的核心负责这两层的交互工作,并管理和维护着记录了他们各自信息的链表。

驱动程序中上报的事件,input子系统已规定好,在linux/input.h中。支持的事件类型(Event types)有EV_CNT个(EV_SYN同步事件,EV_KEY按键事件,EV_REL相对坐标事件,EV_ABS绝对坐标事件,EV_MSC零散事件,EV_SW开关事件,EV_LED LED事件,EV_SND,EV_REP重复事件,EV_FF,EV_PWR,EV_FF_STATUS,EV_MAX,EV_CNE)(后几项用于服务事件类型,不代表实际事件),在代码中用“type”表示。每种事件类型都有多种属性(或选项,或编码),代码中用“code”表示,每种属性所对应的值就是要上报的数据(上面所说的“读值”)。“事件-属性-属性值”(type-code-value)唯一表征一个上报事件,“type-code”输入子系统有完整的规定,代码中通过“位掩码”表示支持该事件,该属性。举个简单例子,触摸设备支持绝对坐标事件EV_ABS,EV_ABS有ABS_CNT个code,其中ABS_X代表X坐标,ABS_Y代表Y坐标。
input_dev结构体自带事件相关数组,如下:
unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
所有输入事件,内核都用统一的数据结构来描述,这个数据结构是input_event。
struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
----------------------------------------------------------------------------------------------------
设备支持的事件在驱动模块加载函数中设置,一般设置方法为:
button_dev->evbit[0] = BIT_MASK(EV_KEY); //直接位赋值(兼容性差)
static inline void __set_bit(int nr, volatile unsigned long *addr); // 函数设置,非原子
static inline void set_bit(int nr, volatile unsigned long *addr); //函数设置,原子,asm/bitops/atomic.h
中断上报事件用input_event(),此外还有3个变体函数分别用于报告EV_KEY、EV_REL、EV_ABS事件。
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value);
static inline void input_report_rel(struct input_dev *dev, unsigned int code, int value);
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value);
input_sync()用于事件同步,告知事件接收者驱动已发出一个完整的报告。
static inline void input_sync(struct input_dev *dev)
{
input_event(dev, EV_SYN, SYN_REPORT, 0);
}
在宋宝华的《linux设备驱动开发详解》中,介绍了通过input子系统增加输入设备的驱动工作。
>>在模块加载函数中告知input子系统设备可以报告的事情。(set_bit())
>>在模块加载函数中注册输入设备。(int input_register_device(struct input_dev *dev);)
>>在键被按下/抬起、触摸屏被触摸/抬起/移动、鼠标被移动/单击/抬起时通过input_report_xxx()报告发生的事情及对应的键值/坐标等状态。
>>在模块卸载函数中注销输入设备。(void input_unregister_device(struct input_dev *dev);)
---------------------------------------------------------------------------------------------------
事件处理层与用户程序和输入子系统核心打交道,是两层的桥梁。一般内核有好几个事件处理器,像evdev,mousedev,joydev。evdev事件处理器可以处理所有的事情。
evdev用户端结构:

.    struct evdev_client {
. struct input_event buffer[EVDEV_BUFFER_SIZE];
. //这个是一个input_event数据结构的数组,input_event代表一个事件,基本成员:类型(type),编码(code),值(value)
. int head; //针对buffer数组的索引
. int tail; //针对buffer数组的索引,当head与tail相等的时候,说明没有事件
. spinlock_t buffer_lock; /* protects access to buffer, head and tail */
. struct fasync_struct *fasync; //异步通知函数
. struct evdev *evdev; //evdev设备
. struct list_head node; // evdev_client 链表项
. };

这个结构在进程打开event0设备的时候调用evdev的open方法,在open中创建这个结构,并初始化。在关闭设备文件的时候释放这个结构。在read时复制到用户空间。
用户空间的应用程序如何访问设备呢,设备文件是?

.    struct evdev {
. int exist;
. int open; //打开标志
. int minor; //次设备号
. struct input_handle handle; //关联的input_handle
. wait_queue_head_t wait; //等待队列,当进程读取设备,而没有事件产生的时候,进程就会睡在其上面
. struct evdev_client *grab; //强制绑定的evdev_client结构,这个结构后面再分析
. struct list_head client_list; //evdev_client 链表,这说明一个evdev设备可以处理多个evdev_client,可以有多个进程访问evdev设备
. spinlock_t client_lock; /* protects client_list */
. struct mutex mutex;
. struct device dev; //device结构,说明这是一个设备结构
. };

evdev结构体在配对成功的时候生成,由handler->connect生成,对应设备文件为/dev/input/event(n),如触摸屏驱动的event0,这个设备是用户空间要访问的设备,可以理解它是一个虚拟设备,因为没有对应的硬件,但是通过handle->dev 就可以找到input_dev结构,而它对应着触摸屏,设备文件为/dev/input/input0。这个设备结构生成之后保存在 evdev_table中,索引值是minor。设备文件通过设备号关联。
在evdev_connect()中,有设备文件初始化过程:

dev_set_name(&evdev->dev, "event%d", minor);

evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
evdev->dev.class = &input_class;
evdev->dev.parent = &dev->dev;
evdev->dev.release = evdev_free;
device_initialize(&evdev->dev);

也可通过/proc/bus/input/devices查看设备文件,PC虚拟机下测试如下:
cat /proc/bus/input/devices
I: Bus=0019 Vendor=0000 Product=0001 Version=0000
N: Name="Power Button"
P: Phys=LNXPWRBN/button/input0
S: Sysfs=/devices/LNXSYSTM:00/LNXPWRBN:00/input/input0
U: Uniq=
H: Handlers=kbd event0
B: PROP=0
B: EV=3
B: KEY=100000 0 0 0

I: Bus=0011 Vendor=0002 Product=0005 Version=0000
N: Name="ImPS/2 Generic Wheel Mouse"
P: Phys=isa0060/serio1/input0
S: Sysfs=/devices/platform/i8042/serio1/input/input3
U: Uniq=
H: Handlers=mouse1 event3
B: PROP=0
B: EV=7
B: KEY=70000 0 0 0 0 0 0 0 0
B: REL=103
通过比较VID、PID来找到对应的usb mouse设备,然后找到对应的mouse1、event3
其实也可以不写应用程序,直接通过cat /dev/input/mouse1 | hexdump来获取鼠标数据。
一个网上示例如下:

.    /*
2. 20150101
3. just a simple input test code
4. lei_wang
5. */
.
. #include <stdio.h>
. #include <stdlib.h>
. #include <unistd.h>
. #include <fcntl.h>
. #include <string.h>
. #include <linux/input.h>
.
. int main()
. {
. int fd;
. int version;
. int ret;
. struct input_event ev;
.
. fd = open("/dev/input/event1", O_RDONLY);
. if (fd < ) {
. printf("open file failed\n");
. exit();
. }
.
. ioctl(fd, EVIOCGVERSION, &version);
. printf("evdev driver version is 0x%x: %d.%d.%d\n",
. version, version>>, (version>>) & 0xff, version & 0xff);
.
. while () {
. ret = read(fd, &ev, sizeof(struct input_event));
. if (ret < ) {
. printf("read event error!\n");
. exit();
. }
.
. if (ev.type == EV_KEY)
. printf("type %d,code %d, value %d\n", ev.type, ev.code, ev.value);
. }
.
. return ;
. }

参考:

  1. http://www.linuxidc.com/Linux/2011-09/43187.htm   input子系统分析
  2. http://blog.csdn.net/zhangxizhicn/article/details/6642062  linux input子系统分析
  3. http://blog.csdn.net/21cnbao/article/details/5615493  linux设备驱动的分层设计思想
  4. linux设备驱动详解    宋宝华
  5. http://blog.csdn.net/luckywang1103/article/details/42324229   input子系统—架构、驱动、应用程序
  6. http://www.cnblogs.com/leaven/archive/2011/02/12/1952793.html   linux input子系统io控制字段
  7. https://blog.csdn.net/jxgz_leo/article/details/51149561  driver: Linux设备模型之input子系统详解

linux input输入子系统应用分析的更多相关文章

  1. linux input输入子系统分析《四》:input子系统整体流程全面分析

    1      input输入子系统整体流程 本节分析input子系统在内核中的实现,包括输入子系统(Input Core),事件处理层(Event Handler)和设备驱动层.由于上节代码讲解了设备 ...

  2. linux内核输入子系统分析

    1.为何引入input system? 以前我们写一些输入设备(键盘.鼠标等)的驱动都是采用字符设备.混杂设备处理的.问题由此而来,Linux开源社区的大神们看到了这大量输入设备如此分散不堪,有木有可 ...

  3. ARM Linux内核Input输入子系统浅解

    --以触摸屏驱动为例 第一章.了解linux input子系统         Linux输入设备总类繁杂,常见的包括有按键.键盘.触摸屏.鼠标.摇杆等等,他们本身就是字符设备,而linux内核将这些 ...

  4. input输入子系统

    一.什么是input输入子系统? 1.Linux系统支持的输入设备繁多,例如键盘.鼠标.触摸屏.手柄或者是一些输入设备像体感输入等等,Linux系统是如何管理如此之多的不同类型.不同原理.不同的输入信 ...

  5. INPUT输入子系统【转】

    转自:https://www.cnblogs.com/deng-tao/p/6094049.html 1.Linux系统支持的输入设备繁多,例如键盘.鼠标.触摸屏.手柄或者是一些输入设备像体感输入等等 ...

  6. INPUT输入子系统——按键

    一.什么是input输入子系统? 1.1. Linux系统支持的输入设备繁多,例如键盘.鼠标.触摸屏.手柄或者是一些输入设备像体感输入等等,Linux系统是如何管理如此之多的不同类型.不同原理.不同的 ...

  7. 12.Linux之输入子系统分析(详解)

    版权声明:本文为博主原创文章,转载请标注出处:   在此节之前,我们学的都是简单的字符驱动,涉及的内容有字符驱动的框架.自动创建设备节点.linux中断.poll机制.异步通知.同步互斥/非阻塞.定时 ...

  8. Linux输入子系统框架分析(1)

    在Linux下的输入设备键盘.触摸屏.鼠标等都能够用输入子系统来实现驱动.输入子系统分为三层,核心层和设备驱动层.事件层.核心层和事件层由Linux输入子系统本身实现,设备驱动层由我们实现.我们在设备 ...

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

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

随机推荐

  1. 红米note3的wifi断流或假死

    红米note3的wifi断流/假死 日常使用note3的时,比如长时间浏览网页,点击一个链接会卡住不动,在等待十几秒之后才恢复.第一反应是不是网络不好?但是这种情况常常出现之后,对比其他的手机,比如价 ...

  2. Android通过网页打开App到指定页面并传递数据

    首先在 Android Manifest 文件中注册 intent-filter <activity android:name=".MainActivity" android ...

  3. A VNC server is already running as :1

    root@host:~# rm -f /tmp/.X1-lock root@host:~# rm -f /tmp/.X11-unix/X1 root@host:~# vncserver -geomet ...

  4. JS Questions:Front-end Developer Interview Questions

    Explain event delegation Event delegation allows us to attach a single event listener, to a parent e ...

  5. 【转载】在LoadRunner中执行命令行程序之:popen()取代system()

    我想大家应该都知道在LoadRunner可以使用函数system()来调用系统指令,结果同在批处理里执行一样. 但是system()有个缺陷:无法获取命令的返回结果. 也许你可以用`echo comm ...

  6. Types of compression algorithms

    http://www.html5rocks.com/en/tutorials/speed/img-compression/ Types of compression algorithms There ...

  7. Delphi 中的哈希表(二)—— TStringHash

    unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms ...

  8. Eclipse 安装插件【转】

    本文介绍Eclipse插件的安装方法.Eclipse插件的安装方法大体有三种:直接复制.使用link文件,以及使用eclipse自带的图形界面的插件安装方法. AD: 做为当下最流行的开源IDE之一, ...

  9. Difference Search Path

    1.Framework Search Path           where to search frameworks (.framework bundles) in addition to sys ...

  10. 使用NSTimer过程中最大的两个坑

    坑1. retain cycle问题. 在一个对象中使用循环执行的nstimer时,若希望在对象的dealloc方法中释放这个nstimer,结局会让你很失望. 这个timer会导致你的对象根本不会被 ...