linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler)、输入子系统核心层(InputCore)和输入子系统设备驱动层。

对于输入子系统设备驱动层而言,主要实现对硬件设备的读写访问,中断设置,并把硬件产生的事件转换为核心层定义的规范提交给事件处理层。

对于核心层而言,为设备驱动层提供了规范和接口。设备驱动层只要关心如何驱动硬件并获得硬件数据(例如按下的按键数据),然后调用核心层提供的接口,核心层会自动把数据提交给事件处理层。

对于事件处理层而言,则是用户编程的接口(设备节点),并处理驱动层提交的数据处理。

对于linux输入子系统的框架结构如下图1所示:

以前我们的驱动程序打开了一个特定的设备文件“/dev/buttons”。而一般写的应用程序不会去打开这个
“/dev/buttons”。一般打开的都是原有的文件,如“ dev/tty* ” ,还有可能是不需要打开什么tty,
而 是直接“scanf()”就去获得了按键的输入。
以前写的那些驱动程序只能自已使用而非通用。要写一个通用的驱动程序,让其他应用
程序“无缝”的使用, 就是说不需要修改人家的应用程序。这需要使用现成的驱动,把自
已的设备相关的驱动放到内核中这种驱动架构 中去。这个现成的驱动就是“输入子系统--
input 子系统”。

输入子系统不仅可以让驱动编写者更加舒服,还可以让应用层编写者不需要特定的更改,就对应新增或者你写的驱动。

以前我们写一些输入设备(键盘、鼠标等)的驱动都是采用字符设备、混杂设备处理的。问题由此而来,Linux开源社区的大牛们看到了这大量输入设备如此分散不堪,有木有可以实现一种机制,可以对分散的、不同类别的输入设备进行统一的驱动,所以才出现了输入子系统

使用内核提供的输入子系统,那么应用层的代码只要是按照输入子系统规范的接口来调用驱动,就不会出现混乱的情况。

驱动层

将底层的硬件输入转化为统一事件形式,向输入核心(Input Core)汇报。

输入子系统核心层

它承上启下为驱动层提供输入设备注册与操作接口,如:input_register_device;通知事件处理层对事件进行处理;在/Proc下产生相应的设备信息。

事件处理层

主要是和用户空间交互(Linux中在用户空间将所有的设备都当作文件来处理,由于在一般的驱动程序中都有提供fops接口,以及在/dev下生成相应的设备文件nod,这些操作在输入子系统中由事件处理层完成)。

1.系统核心层

  主要功能

  1. 注册主设备号
  2. 对于swi进入的open函数进行第一层处理,并通过次设备号选择handler进入第二层open,也就是真正的open所在的file_operation,并返回该file_opration的fd
  3. 提供input_register_device跟input_register_handler函数分别用于注册device跟handler

2.handler层(事件处理层)

  handler层是纯软件层,包含不同的解决方案,如键盘,鼠标,游戏手柄等,但是没有设计到硬件方面的操作

  对于不同的解决方案,都包含一个名为input_handler的结构体,该结构体内含的主要成员如下

    .id_table   一个存放该handler所支持的设备id的表(其实内部存放的是EV_xxx事件,用于判断device是否支持该事件)

    .fops     该handler的file_operation

    .connect   连接该handler跟所支持device的函数

    .disconnect  断开该连接

    .event    事件处理函数,让device调用

    h_list    也是一个链表,该链表保存着该handler到所支持的所有device的中间站:handle结构体的指针

3.device层(驱动层)

  device是纯硬件操作层,包含不同的硬件接口处理,如gpio等

  对于每种不同的具体硬件操作,都对应着不同的input_dev结构体

  该结构体内部也包含着一个h_list

有了输入子系统这样的分层设计之后,我们编写驱动就只用管驱动层了,而应用层也只用管打开对应的输入子系统设备即可。这样就不会必须驱动编写者要告诉应用编写者需要打开哪个设备。

核心层

drivers/input.c 所以输入子系统的代码在这个 c 文件中。
看一个驱动程序是从“入口函数”开始查看。

这里注册了一个主设备号“INPUT_MAJOR”为 13 的字符设备,名字为“input”,它的
file_operations 结构是“input_fops”。这个结构中只有一个“.open”函数。 显然这个open函数里面是做了更多的其他事情的。
源码分析还不是时候,现在也正在进行源码的阅读,还是先把所有的这些函数当做STM32库函数一样来使用,先用起来更符合现实需要。

下面我们只用知道,使用输入子系统这一套方法,我们只需要编写硬件驱动层代码,那么,以什么样的方式编写呢?

入口函数:我相信只要是学过单片机的人都应该能模仿别人的调用方式实现自己的功能

出口函数:

就这样,比之前的操作简洁多了,就实现输入子系统。

实验现象:

在没有加载驱动的时候只要event0,加载驱动之后有event1了

cat tty1之后按键(这里需要注意,按键按下的时候,需要输入enter键才能显示,对应我们的按键S4):

tty的简单介绍:

  1. 控制台终端(/dev/ttyn, /dev/console)
    Linux系统中,电脑显示器通常被称为控制台终端
  2. (Console)。他仿真了类型为Linux的一种终端(TERM=Linux),并且有一些设备特别文档和之相关联:tty0tty1tty2
  3. 等。当您在控制台上登录时,使用的是tty1。使用Alt+[F1F6]组合键时,我们就能够转换到tty2tty3等上面去。tty1tty6
  4. 称为虚拟终端,而tty0则是当前所使用虚拟终端的一个别名,系统所产生的信息会发送到该终端上(这时也叫控制台终端)。因此不管当前正在使用哪个虚拟终
  5. 端,系统信息都会发送到控制台终端上。您能够登录到不同的虚拟终端上去,因而能够让系统同时有几个不同的会话期存在。只有系统或终极用户root能够向
  6. /dev/tty0进行写操作 即下例:
  7. 1、# tty(查看当前TTY)
  8. /dev/tty1
  9. 2、#echo "test tty0" > /dev/tty0
  10. test tty0

这里的标准输入和标准输出以及标准错误都是我们串口控制台/dev/console

现在把tty1重定向成标准输入:

可以看到,第一行的l^H^H是我键盘输入的,此时键盘的l(小写L)是可以被识别,但是我按键盘的退格键,就打印^H。

此时,我们按下2440上面的按键依次为l然后s最后enter键,果然,我们通过按键实现了ls的功能,列举出了现在目录文件信息。我们还有一个S5是Shift键,此时我们按住Shift键的同时,按下l和s,可以看到大写的L和S。证明我们的驱动实验成功。

完整代码Code:

  1. /* 参考drivers\input\keyboard\gpio_keys.c */
  2.  
  3. #include <linux/module.h>
  4. #include <linux/version.h>
  5.  
  6. #include <linux/init.h>
  7. #include <linux/fs.h>
  8. #include <linux/interrupt.h>
  9. #include <linux/irq.h>
  10. #include <linux/sched.h>
  11. #include <linux/pm.h>
  12. #include <linux/sysctl.h>
  13. #include <linux/proc_fs.h>
  14. #include <linux/delay.h>
  15. #include <linux/platform_device.h>
  16. #include <linux/input.h>
  17. #include <linux/irq.h>
  18.  
  19. #include <asm/gpio.h>
  20. #include <asm/io.h>
  21. #include <asm/arch/regs-gpio.h>
  22.  
  23. struct pin_desc{
  24. int irq;
  25. char *name;
  26. unsigned int pin;
  27. unsigned int key_val;
  28. };
  29.  
  30. struct pin_desc pins_desc[] = {
  31. {IRQ_EINT0, "S2", S3C2410_GPF0, KEY_L},
  32. {IRQ_EINT2, "S3", S3C2410_GPF2, KEY_S},
  33. {IRQ_EINT11, "S4", S3C2410_GPG3, KEY_ENTER},
  34. {IRQ_EINT19, "S5", S3C2410_GPG11, KEY_LEFTSHIFT},
  35. };
  36.  
  37. static struct input_dev *buttons_dev;
  38. static struct pin_desc *irq_pd;
  39. static struct timer_list buttons_timer;
  40.  
  41. static irqreturn_t buttons_irq(int irq, void *dev_id)
  42. {
  43. /* 10ms后启动定时器 */
  44. irq_pd = (struct pin_desc *)dev_id;
  45. mod_timer(&buttons_timer, jiffies+HZ/);
  46. return IRQ_RETVAL(IRQ_HANDLED);
  47. }
  48.  
  49. static void buttons_timer_function(unsigned long data)
  50. {
  51. struct pin_desc * pindesc = irq_pd;
  52. unsigned int pinval;
  53.  
  54. if (!pindesc)
  55. return;
  56.  
  57. pinval = s3c2410_gpio_getpin(pindesc->pin);
  58.  
  59. if (pinval)
  60. {
  61. /* 松开 : 最后一个参数: 0-松开, 1-按下 */
  62. input_event(buttons_dev, EV_KEY, pindesc->key_val, );//报告一个新的输入事件
  63. input_sync(buttons_dev);//同步事件
  64. }
  65. else
  66. {
  67. /* 按下 */
  68. input_event(buttons_dev, EV_KEY, pindesc->key_val, );
  69. input_sync(buttons_dev);
  70. }
  71. }
  72.  
  73. static int buttons_init(void)
  74. {
  75. int i;
  76.  
  77. /* 1. 分配一个input_dev结构体 */
  78. buttons_dev = input_allocate_device();;
  79.  
  80. /* 2. 设置 */
  81. /* 2.1 能产生哪类事件 */
  82. set_bit(EV_KEY, buttons_dev->evbit);//按键类
  83. set_bit(EV_REP, buttons_dev->evbit);//按键可重复,即按下不松开的时候,一直保持这个值
  84.  
  85. /* 2.2 能产生这类操作里的哪些事件: L,S,ENTER,LEFTSHIT */
  86. set_bit(KEY_L, buttons_dev->keybit);
  87. set_bit(KEY_S, buttons_dev->keybit);
  88. set_bit(KEY_ENTER, buttons_dev->keybit);
  89. set_bit(KEY_LEFTSHIFT, buttons_dev->keybit);
  90.  
  91. /* 3. 注册 */
  92. input_register_device(buttons_dev);
  93.  
  94. /* 4. 硬件相关的操作 */
  95. init_timer(&buttons_timer);
  96. buttons_timer.function = buttons_timer_function;
  97. add_timer(&buttons_timer);
  98.  
  99. for (i = ; i < ; i++)
  100. {
  101. request_irq(pins_desc[i].irq, buttons_irq, IRQT_BOTHEDGE, pins_desc[i].name, &pins_desc[i]);
  102. }
  103.  
  104. return ;
  105. }
  106.  
  107. static void buttons_exit(void)
  108. {
  109. int i;
  110. for (i = ; i < ; i++)
  111. {
  112. free_irq(pins_desc[i].irq, &pins_desc[i]);
  113. }
  114.  
  115. del_timer(&buttons_timer);
  116. input_unregister_device(buttons_dev);
  117. input_free_device(buttons_dev);
  118. }
  119.  
  120. module_init(buttons_init);
  121.  
  122. module_exit(buttons_exit);
  123.  
  124. MODULE_LICENSE("GPL");

linux输入子系统的更多相关文章

  1. linux输入子系统(input subsystem)之evdev.c事件处理过程

    1.代码 input_subsys.drv.c 在linux输入子系统(input subsystem)之按键输入和LED控制的基础上有小改动,input_subsys_test.c不变. input ...

  2. Linux输入子系统(转)

    Linux输入子系统(Input Subsystem) 1.1.input子系统概述 输入设备(如按键,键盘,触摸屏,鼠标等)是典型的字符设备,其一般的工作机制是低层在按键,触摸等动作发生时产生一个中 ...

  3. Linux输入子系统(Input Subsystem)

    Linux输入子系统(Input Subsystem) http://blog.csdn.net/lbmygf/article/details/7360084 input子系统分析  http://b ...

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

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

  5. Linux输入子系统详解

    input输入子系统框架  linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler).输入子系统核心层(Input ...

  6. linux输入子系统概念介绍

    在此文章之前,我们讲解的都是简单的字符驱动,涉及的内容有字符驱动的框架.自动创建设备节点.linux中断.poll机制.异步通知.同步互斥.非阻塞.定时器去抖动. 上一节文章链接:http://blo ...

  7. linux输入子系统简述【转】

    本文转载自:http://blog.csdn.net/xubin341719/article/details/7678035 1,linux输入子系统简述 其实驱动这部分大多还是转载别人的,linux ...

  8. 7.Linux 输入子系统分析

    为什么要引入输入子系统? 在前面我们写了一些简单的字符设备的驱动程序,我们是怎么样打开一个设备并操作的呢? 一般都是在执行应用程序时,open一个特定的设备文件,如:/dev/buttons .... ...

  9. Android底层开发之Linux输入子系统要不要推断系统休眠状态上报键值

    Android底层开发之Linux输入子系统要不要推断系统休眠状态上报键值 题外话:一个问题研究到最后,那边记录文档的前半部分基本上都是没用的,甚至是错误的. 重点在最后,前边不过一些假想猜測. ht ...

随机推荐

  1. Kafka 如何读取指定topic中的offset -------------用来验证分区是不是均衡!!!(__consumer_offsets)(已验证!)

    我现在使用的是librdkafka 的C/C++ 的客户端来生产消息,用flume来辅助处理异常的数据,,, 但是在前段时间,单独使用flume测试的时候发现,flume不能对分区进行负载均衡!同一个 ...

  2. C语言学习笔记 (008) - C语言字符串操作总结大全(超详细)(转)

    1)字符串操作 strcpy(p, p1) 复制字符串 strncpy(p, p1, n) 复制指定长度字符串 strcat(p, p1) 附加字符串 strncat(p, p1, n) 附加指定长度 ...

  3. Socket模型(二):完成端口(IOCP)

    为什么要采用Socket模型,而不直接使用Socket? 原因源于recv()方法是堵塞式的,当多个客户端连接服务器时,其中一个socket的recv调用时,会产生堵塞,使其他链接不能继续.这样我们又 ...

  4. Vue.js 综合

    <!DOCTYPE HTML> <html> <head> <title>vue.js 处理用户输入</title> <script ...

  5. 彻底解决asp.net mvc5.2.2:vs2013 cshtml视图文件报错(当前上下文中不存在名称“model”,ViewBag,Url)

    最近遇到一个奇葩的问题,在vs2013下cshtml视图文件报错,出现当前上下文中不存在名称“model”,ViewBag,Url等等),在视图中也没有智能提示了,用@model声明视图的model类 ...

  6. hibernate使用注解设置日期默认值

    用注解设置属性的默认值时 使用 @Temporal(TemporalType.TIMESTAMP) @Column(updatable = false,nullable=false,length=20 ...

  7. 禁用gridview,listview回弹或下拉悬停

    不同的安卓厂商对ListView或ScrollView都做了一些动画效果,比如下拉时为了产生弹性美感而有大幅度回弹效果,再比如魅族的下拉悬停,有时做了一个下拉刷新的功能会与之冲突.其实该美化实为多此一 ...

  8. JetBrains PyCharm专业版激活

    PyCharm最新2018激活码 激活时选择License server 填入 http://idea.imsxm.com 然后点击Active即可 PS:在线激活有一个过期时间,这个时间一过就必须再 ...

  9. python pandas replace函数

    在处理数据的时候,很多时候会遇到批量替换的情况,如果一个一个去修改效率过低,也容易出错.replace()是很好的方法. 1.基本结构: df.replace(to_replace, value) 前 ...

  10. Appium 点击屏幕

    由于版本变更,appium 点击屏幕方法已经改变, TouchAction action = new TouchAction(driver); Duration duration = Duration ...