您的位置:首页 > 运维架构 > Linux

Linux驱动开发十:按键中断之输入子系统

2014-11-05 21:38 639 查看

 一、Input子系统概述

Input子系统是对不同类型的输入设备进行统一处理的驱动程序。一个输入事件,如按键,是通过驱动层到系统核心层到事件处理层到用户空间的顺序到达用户空间并传给应用程序使用。

Input子系统由驱动层、输入子系统核心层和事件处理层三部分组成。此子系统主要包括两类驱动程序:事件驱动程序和设备驱动程序。事件驱动程序负责和应用程序的接口,而设备驱动程序负责和底层输入设备的通信。输入事件驱动程序包括mousedev,evdev,joydev,键盘等。事件驱动程序是标准的,对所有的输入设备都是可用的,所以要实现的是设备驱动程序而不是事件驱动程序,设备驱动程序可用利用一个已经存在的、合适的事件驱动程序通过输入核心和用户应用程序接口。事件驱动程序和设备驱动程序都可以利用输入核心层为之服务。

二、下面是对具体代码的分析过程:

drivers/input/input.c:

 input_init > err = register_chrdev(INPUT_MAJOR, "input", &input_fops);

 

static const struct file_operations input_fops = {

 .owner = THIS_MODULE,

 .open = input_open_file,

};

问:怎么读按键?

input_open_file

 struct input_handler *handler = input_table[iminor(inode) >> 5];

 new_fops = fops_get(handler->fops)  //  =>&evdev_fops

 file->f_op = new_fops;

 err = new_fops->open(inode, file);

app: read > ... > file->f_op->read 

input_table数组由谁构造?

input_register_handler

注册input_handler:

input_register_handler

 // 放入数组

 input_table[handler->minor >> 5] = handler;

 

 // 放入链表

 list_add_tail(&handler->node, &input_handler_list);

 // 对于每个input_dev,调用input_attach_handler

 list_for_each_entry(dev, &input_dev_list, node)

  input_attach_handler(dev, handler); // 根据input_handler的id_table判断能否支持这个input_dev

 

 

注册输入设备:

input_register_device

 // 放入链表

 list_add_tail(&dev->node, &input_dev_list);

 

 // 对于每一个input_handler,都调用input_attach_handler

 list_for_each_entry(handler, &input_handler_list, node)

  input_attach_handler(dev, handler); // 根据input_handler的id_table判断能否支持这个input_dev

input_attach_handler

 id = input_match_device(handler->id_table, dev);

 

 error = handler->connect(handler, dev, id);

注册input_dev或input_handler时,会两两比较左边的input_dev和右边的input_handler,

根据input_handler的id_table判断这个input_handler能否支持这个input_dev,

如果能支持,则调用input_handler的connect函数建立"连接"

怎么建立连接?(从evdev.c中分析)

1. 分配一个input_handle结构体

2.

 input_handle.dev = input_dev;  // 指向左边的input_dev

 input_handle.handler = input_handler;  // 指向右边的input_handler

3. 注册:

   input_handler->h_list = &input_handle;

   inpu_dev->h_list      = &input_handle;

evdev_connect

 evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); // 分配一个input_handle

 

 // 设置

 evdev->handle.dev = dev;  // 指向左边的input_dev

 evdev->handle.name = evdev->name;

 evdev->handle.handler = handler;  // 指向右边的input_handler

 evdev->handle.private = evdev;

 

 // 注册

 error = input_register_handle(&evdev->handle);

 

怎么读按键?

app: read

--------------------------

   .......

     evdev_read

      // 无数据并且是非阻塞方式打开,则立刻返回

   if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))

    return -EAGAIN;

   

   // 否则休眠

   retval = wait_event_interruptible(evdev->wait,

    client->head != client->tail || !evdev->exist);

      

谁来唤醒?

evdev_event

 wake_up_interruptible(&evdev->wait);

evdev_event被谁调用?

猜:应该是硬件相关的代码,input_dev那层调用的

在设备的中断服务程序里,确定事件是什么,然后调用相应的input_handler的event处理函数

gpio_keys_isr

 // 上报事件

 input_event(input, type, button->code, !!state);

 input_sync(input);

 

input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)

 struct input_handle *handle;

 list_for_each_entry(handle, &dev->h_list, d_node)

  if (handle->open)

   handle->handler->event(handle, type, code, value);

怎么写符合输入子系统框架的驱动程序?

1. 分配一个input_dev结构体

2. 设置

3. 注册

4. 硬件相关的代码,比如在中断服务程序里上报事件

   

struct input_dev {

 void *private;

 const char *name;

 const char *phys;

 const char *uniq;

 struct input_id id;

 unsigned long evbit[NBITS(EV_MAX)];   // 表示能产生哪类事件

 unsigned long keybit[NBITS(KEY_MAX)]; // 表示能产生哪些按键

 unsigned long relbit[NBITS(REL_MAX)]; // 表示能产生哪些相对位移事件, x,y,滚轮

 unsigned long absbit[NBITS(ABS_MAX)]; // 表示能产生哪些绝对位移事件, x,y

 unsigned long mscbit[NBITS(MSC_MAX)];

 unsigned long ledbit[NBITS(LED_MAX)];

 unsigned long sndbit[NBITS(SND_MAX)];

 unsigned long ffbit[NBITS(FF_MAX)];

 unsigned long swbit[NBITS(SW_MAX)];

测试:(会在/dev/目录下产生event1设备,如果目录下有event0设备 )

crw-rw----    1 0        0          13,  64 Jan  1 00:00 /dev/event0

crw-rw----    1 0        0          13,  65 Jan  1 00:00 /dev/event1

1.

hexdump /dev/event1  (open(/dev/event1), read(), )

           秒        微秒    类  code    value

0000000 0bb2 0000 0e48 000c 0001 0026 0001 0000

0000010 0bb2 0000 0e54 000c 0000 0000 0000 0000

0000020 0bb2 0000 5815 000e 0001 0026 0000 0000

0000030 0bb2 0000 581f 000e 0000 0000 0000 0000

2. 如果没有启动QT:

cat /dev/tty1

按:s2,s3,s4

就可以得到ls

或者:

exec 0</dev/tty1

然后可以使用按键来输入

3. 如果已经启动了QT:

可以点开记事本

然后按:s2,s3,s4

三、下面是在QT6410上运行OK的代码:

buttons.c

/*
* 参考driver/input/keyboard/gpio_keys.c
*/

#include <linux/module.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/slab.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/gpio_keys.h>
#include <linux/workqueue.h>
#include <linux/gpio.h>

static struct input_dev *buttons_dev;

static struct timer_list buttons_timer;

struct pin_desc* irq_pd;

struct pin_desc{
int irq;
char *name;
unsigned int pin;
unsigned int key_val;
};

struct pin_desc pins_desc[4] = {
{IRQ_EINT(0),"K1",S3C64XX_GPN(0), KEY_L},
{IRQ_EINT(1),"K2",S3C64XX_GPN(1), KEY_S},
{IRQ_EINT(2),"K3",S3C64XX_GPN(2), KEY_ENTER},
{IRQ_EINT(3),"K4",S3C64XX_GPN(3), KEY_LEFTSHIFT},
};

/*
*中断处理程序,并置标志位为1,唤醒等待队列上等待的进程,注册用户中断处理函数,设置触发方式为双边沿触发
*/
static irqreturn_t button_irq(int irq,void *dev_id)
{
irq_pd = (struct pin_desc*)dev_id;

/* 10ms后启动定时器 */
mod_timer(&buttons_timer,jiffies + (HZ)/50);
//jiffies为系统全局变量,用cat /proc/interrupts可以查看其值一直在变化“100:    1036564   s3c-timer  S3C2410 Timer Tick”
//jiffies + (HZ)/100:表示当前时间起延时10ms后处理(HZ=1000)

return IRQ_RETVAL(IRQ_HANDLED);
}

static void buttons_timer_function(unsigned long data)
{
struct pin_desc * pindesc = irq_pd;

if(!pindesc)	//如果没有中断发生就返回
return ;
unsigned int pinval;

/* 读取中断引脚的状态 */
pinval = gpio_get_value(pindesc->pin);

/* 确定按键值 */
if (pinval)
{
/* 松开: 最后一个参数:0-松开, 1-按下 */
input_event(buttons_dev,EV_KEY,pindesc->key_val,0);		//上报事件
input_sync(buttons_dev);
}
else
{
/* 按下 */
input_event(buttons_dev,EV_KEY,pindesc->key_val,1);		//上报事件
input_sync(buttons_dev);
}
}

static int buttons_init(void)
{
int i;

/* 1、分配input_dev结构体 */

ad29
buttons_dev = input_allocate_device();
if(!buttons_dev)
return -ENOMEM;

/* 2、设置  */
/* 2.1、能产生哪类事件  */
set_bit(EV_KEY,buttons_dev->evbit);		//产生按键类事件
set_bit(EV_REP,buttons_dev->evbit);		//产生重复类事件,按下按键不松开时会一直有值

/* 2.2、能产生这些操作里到那些事件:按键L、S、ENTER、左边到shift  */
set_bit(KEY_L,buttons_dev->keybit);		//相当于按按键l
set_bit(KEY_S,buttons_dev->keybit);		//相当于按按键S
set_bit(KEY_ENTER,buttons_dev->keybit);		//相当于按按键RNTER
set_bit(KEY_LEFTSHIFT,buttons_dev->keybit);		//相当于按按键SHIFT

/* 3、注册  */
input_register_device(buttons_dev);

/* 4、硬件相关  */
init_timer(&buttons_timer);
buttons_timer.function = buttons_timer_function;
add_timer(&buttons_timer);

for(i = 0; i < 4; i ++)
{
request_irq(pins_desc[i].irq,button_irq,IRQ_TYPE_EDGE_BOTH,pins_desc[i].name,&pins_desc[i]);
}

return 0;
}

static void buttons_exit(void)
{
int i;
for(i = 0; i < 4; 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");


Makefile:

obj-m := buttons.o

KER_DIR := /work/work/linux/

all:
$(MAKE) -C $(KER_DIR) M=`pwd` modules
# cp first_drv.ko /work/tftpboot/
cp buttons.ko /work/nfsroot/

clean:
make -C $(KER_DIR) M=`pwd` modules clean
运行结果:

/ # hexdump /dev/event1

0000000 0052 0000 3546 000c 0001 0026 0001 0000

0000010 0052 0000 355d 000c 0000 0000 0000 0000

0000020 0053 0000 c38e 0000 0001 0026 0002 0000

0000030 0053 0000 c3a0 0000 0000 0000 0001 0000

0000040 0053 0000 11ac 0001 0001 0026 0000 0000

0000050 0053 0000 11bf 0001 0000 0000 0000 0000

/ # exec 0</dev/tty1

/ # s3cfb_blank: blank(mode=0, info=c049c4e4)

s3cfb_pan_display: s3c_fb_pan_display(var=c789f81c, info=c049c4e4)

ls

app             fs.tgz          sys

bin             lib             test

buttons.ko      linuxrc         tmp

buttons_drv.ko  mnt             usr

dev             proc

etc             sbin

/ #
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: