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. iOS 之项目中遇到的问题总结

    昨天去一家公司面试,面试官问了我在项目开发中遇到过哪些问题,是什么引起的,怎样解决的? 当时由于有点小紧张只说出了一两点,现在就来好好总结一下. 问题: 1.两表联动 所谓的两表联动就是有左右两个表格 ...

  2. Nginx 服务器 之Nginx与tomcat实现负载均衡

      本文讲解我们如何使用Nginx做反向带服务器,实现nginx与tomcat服务器集群做负载均衡. 一.nginx与tomcat实现负载均衡 1.在/usr/local/ngnix/conf  创建 ...

  3. Android Weekly Notes Issue #231

    Android Weekly Issue #231 November 13th, 2016 Android Weekly Issue #231 Android Weekly阅读笔记, Issue #2 ...

  4. GitHub实战系列~2.把本地项目提交到github中 2015-12-10

    GitHub实战系列汇总:http://www.cnblogs.com/dunitian/p/5038719.html ———————————————————————————————————————— ...

  5. 设计模式(一):“穿越火线”中的“策略模式”(Strategy Pattern)

    在前段时间呢陆陆续续的更新了一系列关于重构的文章.在重构我们既有的代码时,往往会用到设计模式.在之前重构系列的博客中,我们在重构时用到了“工厂模式”.“策略模式”.“状态模式”等.当然在重构时,有的地 ...

  6. SQL Server中提前找到隐式转换提升性能的办法

        http://www.cnblogs.com/shanksgao/p/4254942.html 高兄这篇文章很好的谈论了由于数据隐式转换造成执行计划不准确,从而造成了死锁.那如果在事情出现之前 ...

  7. Sublime Text 3 全程详细图文原创教程(持续更新中。。。)

    一. 前言 使用Sublime Text 也有几个年头了,版本也从2升级到3了,但犹如寒天饮冰水,冷暖尽自知.最初也是不知道从何下手,满世界地查找资料,但能查阅到的资料,苦于它们的零碎.片面,不够系统 ...

  8. 简单动态规划-LeetCode198

    题目:House Robber You are a professional robber planning to rob houses along a street. Each house has ...

  9. Hadoop入门学习笔记---part3

    2015年元旦,好好学习,天天向上.良好的开端是成功的一半,任何学习都不能中断,只有坚持才会出结果.继续学习Hadoop.冰冻三尺,非一日之寒! 经过Hadoop的伪分布集群环境的搭建,基本对Hado ...

  10. react-native ListView使用详解

    刚好今天七夕,呆萌的程序猿没有妹纸,刚好发小明天结婚,我还在异地,晚上还要苦逼的赶火车.趁着下午比较闲,更新一下Blog,也算是在百无聊赖之时给众多单身程序猿们的小福利吧,虽然已经好久没更了...囧 ...