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的简单介绍:

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

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

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

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

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

完整代码Code:

/* 参考drivers\input\keyboard\gpio_keys.c */

#include <linux/module.h>
#include <linux/version.h> #include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/irq.h> #include <asm/gpio.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h> struct pin_desc{
int irq;
char *name;
unsigned int pin;
unsigned int key_val;
}; struct pin_desc pins_desc[] = {
{IRQ_EINT0, "S2", S3C2410_GPF0, KEY_L},
{IRQ_EINT2, "S3", S3C2410_GPF2, KEY_S},
{IRQ_EINT11, "S4", S3C2410_GPG3, KEY_ENTER},
{IRQ_EINT19, "S5", S3C2410_GPG11, KEY_LEFTSHIFT},
}; static struct input_dev *buttons_dev;
static struct pin_desc *irq_pd;
static struct timer_list buttons_timer; static irqreturn_t buttons_irq(int irq, void *dev_id)
{
/* 10ms后启动定时器 */
irq_pd = (struct pin_desc *)dev_id;
mod_timer(&buttons_timer, jiffies+HZ/);
return IRQ_RETVAL(IRQ_HANDLED);
} static void buttons_timer_function(unsigned long data)
{
struct pin_desc * pindesc = irq_pd;
unsigned int pinval; if (!pindesc)
return; pinval = s3c2410_gpio_getpin(pindesc->pin); if (pinval)
{
/* 松开 : 最后一个参数: 0-松开, 1-按下 */
input_event(buttons_dev, EV_KEY, pindesc->key_val, );//报告一个新的输入事件
input_sync(buttons_dev);//同步事件
}
else
{
/* 按下 */
input_event(buttons_dev, EV_KEY, pindesc->key_val, );
input_sync(buttons_dev);
}
} static int buttons_init(void)
{
int i; /* 1. 分配一个input_dev结构体 */
buttons_dev = input_allocate_device();; /* 2. 设置 */
/* 2.1 能产生哪类事件 */
set_bit(EV_KEY, buttons_dev->evbit);//按键类
set_bit(EV_REP, buttons_dev->evbit);//按键可重复,即按下不松开的时候,一直保持这个值 /* 2.2 能产生这类操作里的哪些事件: L,S,ENTER,LEFTSHIT */
set_bit(KEY_L, buttons_dev->keybit);
set_bit(KEY_S, buttons_dev->keybit);
set_bit(KEY_ENTER, buttons_dev->keybit);
set_bit(KEY_LEFTSHIFT, buttons_dev->keybit); /* 3. 注册 */
input_register_device(buttons_dev); /* 4. 硬件相关的操作 */
init_timer(&buttons_timer);
buttons_timer.function = buttons_timer_function;
add_timer(&buttons_timer); for (i = ; i < ; i++)
{
request_irq(pins_desc[i].irq, buttons_irq, IRQT_BOTHEDGE, pins_desc[i].name, &pins_desc[i]);
} return ;
} static void buttons_exit(void)
{
int i;
for (i = ; i < ; i++)
{
free_irq(pins_desc[i].irq, &pins_desc[i]);
} del_timer(&buttons_timer);
input_unregister_device(buttons_dev);
input_free_device(buttons_dev);
} module_init(buttons_init); module_exit(buttons_exit); 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. 什么是分表和分区 MySql数据库分区和分表方法

    1.为什么要分表和分区 日常开发中我们经常会遇到大表的情况,所谓的大表是指存储了百万级乃至千万级条记录的表.这样的表过于庞大,导致数据库在查询和插入的时候耗时太长,性能低下,如果涉及联合查询的情况,性 ...

  2. mysql/mariadb基于ssl的主从复制

    当mysql/mariadb跨越互联网进行复制时别人可以窃取到mysql/mariadb的复制信息, 这些信息是明文的, 因此存在不安全性, 这里通过ssl对复制的信息进行加密 1. 创建证书中心 在 ...

  3. Java 时间相比较

    private String getRunTime() { String start = new Date.getTime(); String end= new Date.getTime(); lon ...

  4. C# Httpclient客户端操作

    原文地址:https://www.cnblogs.com/Xujg/p/4113387.html HttpClient 当前主流用法,异步请求,自.NET4.5开始可从Nuget包管理中获取. usi ...

  5. 【struts2】名为dispatcher的ResultType

    1)基本使用 名称为“dispatcher”的ResultType,在struts-default.xml里的配置如下: <result-type name="dispatcher&q ...

  6. Fix Backup Database is terminating abnormally When performing a Farm Backup

    Problem I am trying to backup SharePoint 2013 Farm Automatically with PowerShell and Windows Task Sc ...

  7. 微信小程序 confirm(删除提示)提示框,询问框,小程序操作成功提示后跳转

    微信小程序删除处理 没有 confrim 那怎么实现这个效果呢 可以使用小程序里的模态框 代码: wx.showModal({ title: '提示', content: '确定要删除吗?', suc ...

  8. [转载]Cortana 设计指导方针

    来源:@微软中国MSDN,源地址:http://weibo.com/p/1001603898586285925224 使用语音命令,延伸 Cortana 与您的应用程序所提供的功能.启动应用程序,启动 ...

  9. eclipse 创建Maven 架构的dynamic web project 问题解决汇总

    Eclipse创建Maven结构的web项目的时候选择Artifact Id为maven-artchetype-webapp,点击finish之后,一般会遇到如下问题 1. The superclas ...

  10. js递归函数使用介绍

    所谓的递归函数就是在函数体内调用本函数.使用递归函数一定要注意,处理不当就会进入死循环.递归函数只有在特定的情况下使用 ,比如阶乘问题 一个10以内的阶乘,js递归函数实例代码: <!DOCTY ...