1.代码

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

input_subsys.drv.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 *input_subsys_dev;
static struct pin_desc *irq_pd;
static struct timer_list buttons_timer; static irqreturn_t buttons_irq(int irq, void *dev_id)
{
/* [cgw]: 按键IO发生边沿中断时重新设置定时间隔
* 用于按键消抖
*/
irq_pd = (struct pin_desc *)dev_id;
buttons_timer.data = irq_pd->pin;
mod_timer(&buttons_timer, jiffies+USER_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; /* [cgw]: 获取按键IO状态 */
pinval = s3c2410_gpio_getpin((unsigned int)data); /* [cgw]: 根据按键IO状态上报按键事件 */
if (pinval)
{
/* [cgw]: 上报按键弹起 */
input_report_key(input_subsys_dev, pindesc->key_val, );
//input_sync(input_subsys_dev);
}
else
{
/* [cgw]: 上报按键按下 */
input_report_key(input_subsys_dev, pindesc->key_val, );
//input_sync(input_subsys_dev);
} //printk("timer occur!\n");
} static int event_handler(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
#if 0
/* [cgw]: 根据应用程序下发的LED控制事件
* 亮灭LED
*/
//if (code == SND_BELL) {
if (code == LED_MUTE) {
if (value == 0xAA) {
/* [cgw]: 点亮 */
s3c2410_gpio_setpin(S3C2410_GPF4, );
} else if (value == 0xEE) {
/* [cgw]: 熄灭 */
s3c2410_gpio_setpin(S3C2410_GPF4, );
} return ;
}
#endif switch (type) {
case EV_REP:
return ;
//break; case EV_LED:
if (code == LED_MUTE) {
if (value == 0xAA) {
/* [cgw]: 点亮 */
s3c2410_gpio_setpin(S3C2410_GPF4, );
} else if (value == 0xEE) {
/* [cgw]: 熄灭 */
s3c2410_gpio_setpin(S3C2410_GPF4, );
} return ;
}
//break; case EV_SND:
return ;
//break;
} return -;
} int input_subsys_open(struct input_dev *dev)
{
int i, retval; /* [cgw]: 设置按键IO为中断输入 */
s3c2410_gpio_cfgpin(S3C2410_GPF0, S3C2410_GPF0_EINT0);
s3c2410_gpio_cfgpin(S3C2410_GPF2, S3C2410_GPF2_EINT2);
s3c2410_gpio_cfgpin(S3C2410_GPG3, S3C2410_GPG3_EINT11);
s3c2410_gpio_cfgpin(S3C2410_GPG11, S3C2410_GPG11_EINT19); /* [cgw]: 设置LED IO为输出,初始为熄灭LED */
s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP);
s3c2410_gpio_setpin(S3C2410_GPF4, ); s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP);
s3c2410_gpio_setpin(S3C2410_GPF5, ); s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP);
s3c2410_gpio_setpin(S3C2410_GPF5, ); /* [cgw]: 为按键IO分配中断线 */
for (i = ; i < ; i++)
{
retval = request_irq(pins_desc[i].irq, buttons_irq, IRQT_BOTHEDGE, pins_desc[i].name, &pins_desc[i]);
} printk("input subsys open!\n");
//printk("USER_HZ: %d\n", USER_HZ); return ;
} static int input_subsys_init(void)
{
/* [cgw]: 分配一个输入设备 */
input_subsys_dev = input_allocate_device();
input_subsys_dev->name = "input_subsys_dev"; /* [cgw]: 设置支持的事件类型 */
set_bit(EV_KEY, input_subsys_dev->evbit);
set_bit(EV_REP, input_subsys_dev->evbit); set_bit(EV_LED, input_subsys_dev->evbit);
//set_bit(EV_SND, input_subsys_dev->evbit); /* [cgw]: 设置支持的事件码 */
set_bit(KEY_L, input_subsys_dev->keybit);
set_bit(KEY_S, input_subsys_dev->keybit);
set_bit(KEY_ENTER, input_subsys_dev->keybit);
set_bit(KEY_LEFTSHIFT, input_subsys_dev->keybit); set_bit(LED_MUTE, input_subsys_dev->ledbit);
//set_bit(SND_BELL, input_subsys_dev->sndbit); /* [cgw]: 分配输入设备的open方法 */
input_subsys_dev->open = input_subsys_open;
/* [cgw]: 分配输入设备的event方法,用户在应用程序write()时 */
input_subsys_dev->event = event_handler; /* [cgw]: 注册输入设备 */
input_register_device(input_subsys_dev); //input_subsys_dev->rep[REP_DELAY] = 250;
//input_subsys_dev->rep[REP_PERIOD] = 100; /* [cgw]: 初始化定时器,用于按键消抖 */
init_timer(&buttons_timer);
buttons_timer.function = buttons_timer_function;
add_timer(&buttons_timer); printk("input subsys init!\n"); return ;
} static void input_subsys_exit(void)
{
int i; /* [cgw]: 释放按键IO中断 */
for (i = ; i < ; i++)
{
free_irq(pins_desc[i].irq, &pins_desc[i]);
} /* [cgw]: 删除定时器 */
del_timer(&buttons_timer);
/* [cgw]: 注销输入设备 */
input_unregister_device(input_subsys_dev);
/* [cgw]: 释放输入设备内存空间 */
input_free_device(input_subsys_dev);
} module_init(input_subsys_init); module_exit(input_subsys_exit); MODULE_LICENSE("GPL");

2. input_subsys_drv.c, input.c, evdev.c 三者之间的关系:

input_subsys_drv.c: 负责获取底层硬件产生的事件,如:中断,按键输入等,收集到这些事件传递给input.c, 并通过设置evdev.c可以支持的事件类型,和evdev.c建立连接

input.c: 输入子系统内核,收集底层硬件发来的(如:如中断,按键输入)和用户空间发来的(如:write,ioctl)事件,传递给evdev.c

evdev.c: 收集从input.c传递过来的事件,存储到一个环形缓冲队列,并产生一个异步通知,通知用户空间读取事件

3. 按键输入(底层硬件)和LED(用户空间)事件处理过程

3.1 按键输入事件处理过程:

input_subsys_drv.c 同过外部中断获得按键的状态,经过消抖之后,向input.c上报:

 static void buttons_timer_function(unsigned long data)
{
struct pin_desc * pindesc = irq_pd;
unsigned int pinval; if (!pindesc)
return; /* [cgw]: 获取按键IO状态 */
pinval = s3c2410_gpio_getpin((unsigned int)data); /* [cgw]: 根据按键IO状态上报按键事件 */
if (pinval)
{
/* [cgw]: 上报按键弹起 */
input_report_key(input_subsys_dev, pindesc->key_val, );
//input_sync(input_subsys_dev);
}
else
{
/* [cgw]: 上报按键按下 */
input_report_key(input_subsys_dev, pindesc->key_val, );
//input_sync(input_subsys_dev);
} //printk("timer occur!\n");
}

input_report_key():

 static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_KEY, code, !!value);
}

input_event()

 void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
... switch (type) {
... case EV_KEY: if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value)
return; if (value == )
break; change_bit(code, dev->key); if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && dev->timer.data && value) {
dev->repeat_key = code;
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
} break; ... } ....
handle->handler->event(handle, type, code, value);
}

其中:case EV_KEY中,对按键连发做初步检测,即检测是否有按键的按下和弹起这两个状态,缺一个都不行(后面解析)。

接着就调用handle->handler->event(),实际上是调用了evdev_event();

因为

 static struct input_handler evdev_handler = {
.event = evdev_event,
...
};

evdev_event():

 static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
{
...
/* [cgw]: 把接收到的事件加入到一个环形队列 */
do_gettimeofday(&client->buffer[client->head].time);
client->buffer[client->head].type = type;
client->buffer[client->head].code = code;
client->buffer[client->head].value = value;
client->head = (client->head + ) & (EVDEV_BUFFER_SIZE - ); /* [cgw]: 发送一个异步通知 */
kill_fasync(&client->fasync, SIGIO, POLL_IN); /* [cgw]: 唤醒正在等待这个事件的进程 */
wake_up_interruptible(&evdev->wait);
}

在evdev_event中发送了异步通知并唤醒了再睡眠的进程,所以在应用程序调用read时,就会获得这个事件。

 /* [cgw]: 异步通知产生时返回的数据 */
read(fd, &buttons_event, sizeof(struct input_event));

3.1.1 按键连发的处理过程

首先在input_subsys_init() 使能EV_REP按键连发功能

 static int input_subsys_init(void)
{
/* [cgw]: 分配一个输入设备 */
input_subsys_dev = input_allocate_device();
input_subsys_dev->name = "input_subsys_dev"; /* [cgw]: 设置支持的事件类型 */
set_bit(EV_KEY, input_subsys_dev->evbit);
set_bit(EV_REP, input_subsys_dev->evbit); set_bit(EV_LED, input_subsys_dev->evbit);
//set_bit(EV_SND, input_subsys_dev->evbit); /* [cgw]: 设置支持的事件码 */
set_bit(KEY_L, input_subsys_dev->keybit);
set_bit(KEY_S, input_subsys_dev->keybit);
set_bit(KEY_ENTER, input_subsys_dev->keybit);
set_bit(KEY_LEFTSHIFT, input_subsys_dev->keybit); set_bit(LED_MUTE, input_subsys_dev->ledbit);
//set_bit(SND_BELL, input_subsys_dev->sndbit); /* [cgw]: 分配输入设备的open方法 */
input_subsys_dev->open = input_subsys_open;
/* [cgw]: 分配输入设备的event方法,用户在应用程序write()时 */
input_subsys_dev->event = event_handler; /* [cgw]: 注册输入设备 */
input_register_device(input_subsys_dev); ... return ;
}
 void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
... switch (type) {
... case EV_KEY: if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value)
return; /* [cgw]: 收到连发按键的事件,返回 */
if (value == )
break; /* [cgw]: 这个函数的设置,用于上面!!test_bit(code, dev->key) == value判断
* 是否为按下弹起操作
*/
change_bit(code, dev->key); /* [cgw]: 如果当前操作为按下,并且连发功能使能,则设置连发的触发时间 */
if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && dev->timer.data && value) {
dev->repeat_key = code;
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
} break; ... } ....
handle->handler->event(handle, type, code, value);
}

在input_event()中如果检测到按键按下,一直到连发功能触发,则定时器超时调用超时处理函数:

因为在注册输入设备时,就分配了定时器的超时处理函数input_repeat_key()

 int input_register_device(struct input_dev *dev)
{
...
/*
* If delay and period are pre-set by the driver, then autorepeating
* is handled by the driver itself and we don't do it in input.c.
*/ init_timer(&dev->timer);
if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
dev->timer.data = (long) dev;
dev->timer.function = input_repeat_key;
dev->rep[REP_DELAY] = ;
dev->rep[REP_PERIOD] = ;
} ...
}

在input_repeat_key()中

 static void input_repeat_key(unsigned long data)
{
struct input_dev *dev = (void *) data; /* [cgw]: 是否分配了连发的键值 */
if (!test_bit(dev->repeat_key, dev->key))
return; /* [cgw]: 发送连发事件 */
input_event(dev, EV_KEY, dev->repeat_key, );
input_sync(dev); /* [cgw]: 设置连发间隔 */
if (dev->rep[REP_PERIOD])
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_PERIOD]));
}

3.2 led控制事件处理过程

用户在应用程序中操作write时

 /* [cgw]: 发送LED控制事件 */
write(fd, &leds_event, sizeof(struct input_event));

对应的是操作了evdev_write()

 static ssize_t evdev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
{
...
/* [cgw]: 收到来自用户空间的事件 */
if (evdev_event_from_user(buffer + retval, &event))
return -EFAULT;
/* [cgw]: 调用input_event() */
input_inject_event(&evdev->handle, event.type, event.code, event.value); return retval;
}

input_event()

 void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
... switch (type) {
... case EV_LED: if (code > LED_MAX || !test_bit(code, dev->ledbit) || !!test_bit(code, dev->led) == value)
return; /* [cgw]: 这个函数用于上面!!test_bit(code, dev->led) == value是否为不同的LED状态(亮,灭) */
change_bit(code, dev->led); /* [cgw]: 调用事件处理,这个事件处理需要驱动提供,做一些特别的处理
* 本例提供了
* static int event_handler(struct input_dev *dev, unsigned int type, unsigned int code, int value)
* 不影响调用evdev_event()
*/
if (dev->event)
dev->event(dev, type, code, value); break; ... } ....
handle->handler->event(handle, type, code, value);
}

event_handler()

 static int event_handler(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{ switch (type) {
case EV_REP:
return ;
//break; case EV_LED:
if (code == LED_MUTE) {
if (value == 0xAA) {
/* [cgw]: 点亮 */
s3c2410_gpio_setpin(S3C2410_GPF4, );
} else if (value == 0xEE) {
/* [cgw]: 熄灭 */
s3c2410_gpio_setpin(S3C2410_GPF4, );
} return ;
}
//break; case EV_SND:
return ;
//break;
} return -;
}

因此用户空间发下来的事件,分两个路径处理

1.dev->event()  即:event_handler,由驱动程序提供

2.handler-event()  即:evdev_event(), 由evdev.c提供

linux输入子系统(input subsystem)之evdev.c事件处理过程的更多相关文章

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

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

  2. Linux 输入子系统 input

    一.输入子系统 针对输入设备设计:触摸屏.键盘.按键.传感器.鼠标...... 二.每种设备都属于字符设备驱动,程序的写法步骤也相同 1.实现入口函数 xxx_init() 和卸载函数 xxx_exi ...

  3. 10. linux输入子系统/input 设备【转】

    转自:https://www.cnblogs.com/crmn/articles/6696819.html 按键事件信息之上报绝对事件信息之上报相对事件信息之上报功能键驱动编写多点触控事件的上报 只产 ...

  4. Linux输入子系统(转)

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

  5. Linux输入子系统详解

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

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

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

  7. Linux 输入子系统

    Technorati 标签: Kernel 输入子系统 Input      在Linux中,输入设备(如按键.键盘.触摸屏.鼠标等)是典型的字符设备,其一般的工作机理,是底层在按键.触摸时,触发一个 ...

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

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

  9. linux输入子系统

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

随机推荐

  1. css的五种属性值----在路上(21)

    在CSS中,每个属性的属性值都有一定的范围,并且不同类型的属性值有不同的值.对于一个属性,必须取得正确的属性值,才能被浏览器正确地解释,因此一定要弄清每种类型的属性值范围.在CSS中属性一般有以下几种 ...

  2. C#学习总结之集合

    一.集合接口和类型 命名空间:  集合类型  命名空间  一般集合 System.Collections   泛型集合 System.Collections.Generic   特定类型集合 Syst ...

  3. MVC5 网站开发之四 业务逻辑层的架构和基本功能

    业务逻辑层在Ninesky.Core中实现,主要功能封装一些方法通过调用数据存储层,向界面层提供服务.   目录 奔跑吧,代码小哥! MVC5网站开发之一 总体概述 MVC5 网站开发之二 创建项目 ...

  4. 前端学HTTP之数据传输

    × 目录 [1]客户机处理 [2]集线器处理 [3]路由器1处理[4]路由器2处理[5]交换机处理[6]服务器处理[7]反向传输 前面的话 上一篇中,介绍了网络基础.本文将详细介绍客户机在浏览网页ab ...

  5. 设计模式(九): 从醋溜土豆丝和清炒苦瓜中来学习"模板方法模式"(Template Method Pattern)

    今天是五.四青年节,祝大家节日快乐.看着今天这标题就有食欲,夏天到了,醋溜土豆丝和清炒苦瓜适合夏天吃,好吃不上火.这两道菜大部分人都应该吃过,特别是醋溜土豆丝,作为“鲁菜”的代表作之一更是为大众所熟知 ...

  6. 现代3D图形编程学习-基础简介(1) (译)

    本书系列 现代3D图形编程学习 基础简介 并不像本书的其他章节,这章内容没有相关的源代码或是项目.本章,我们将讨论向量,图形渲染理论,以及OpenGL. 向量 在阅读这本书的时候,你需要熟悉代数和几何 ...

  7. 设置eclipse中自动添加get,set的注释为字段属性的注释

    一:说明 首先具体来看一下是什么效果,上图可能会更清楚一点 就是在get/set中自动加上属性的注释,那我们要怎么配置呢? 二:配置 2.1:下载附件 下载附件 2.2:替换class 原生的ecli ...

  8. 【DDD/CQRS/微服务架构案例】在Ubuntu 14.04.4 LTS中运行WeText项目的服务端

    在<WeText项目:一个基于.NET实现的DDD.CQRS与微服务架构的演示案例>文章中,我介绍了自己用Visual Studio 2015(C# 6.0 with .NET Frame ...

  9. HTML5 网络拓扑图整合 OpenLayers 实现 GIS 地图应用

    在前面<百度地图.ECharts整合HT for Web网络拓扑图应用>我们有介绍百度地图和 HT for Web 的整合,我们今天来谈谈 OpenLayers 和 HT for Web  ...

  10. TeamCity : .NET Core 插件

    笔者在<TeamCity : 配置 Build 过程>一文中提到 "TeamCity 内置支持几乎所有的 build 类型".在当今这个软件语言和各种框架飞速发展的时代 ...