输入子系统浅析

Hntea bio photo By Hntea

概念

内核对分散的,多种不同类别的输入设备进行统一处理的驱动程序。

输入子系统的优点

  • 统一物理形态各异的相似的输入设备的处理功能

  • 提供了用于分发输入报告给用户应用程序的简单事件(event)接口。驱动程序不必创建、管理/dev节点以及相关的访问方法,因此它能方便地被调用输入API以发送鼠标移动,键盘按键或者触摸事件给用户空间

  • 抽取了输入驱动程序的通用部分,简化驱动程序并引入一致性

输入子系统架构图

架构图

图解:

  • 设备驱动层:将底层的硬件输入转化成统一事件形式,向输入核心汇报

  • 输入核心层:为设备驱动层提供输入设备注册与操作接口

  • 事件驱动层:主要作用是和用户空间层进行数据交互

输入设备驱动程序

/driver/input 目录下

  • serio:该层提供了访问老式输入硬件的库例程。为了与serio提供服务的硬件通讯,如发送命令给PS/2鼠标,需要用到:

serio_register_driver() //向serio注册规定的回调例程
serio_register_port() //注册open/close/write/stop/start入口函数

  • 键盘(EV_KEY/0X01):键盘驱动程序的独特之处在于它传送数据给另外一个内核子系统(TTY层),而不是通过/dev目录下的节点传送给用户空间。驱动文件在/dev/char目录下
可以通过以下命令查看矩阵键盘的扫描码:**showkey -s **或者showkey
根据加载的键盘映射,键盘事件驱动程序进行键值翻译(查看loadkeys的操作帮助和/lib/kbd/keymaps中提供的映射文件)
  • 鼠标:鼠标输入事件驱动称为 mousedev, 通过/dev/input/mice报告鼠标事件给用户

  • 指点杆(EV_REL/0X02)(Trackpoint):是一个指定设备

详细配置选项:/sys/devices/platform/i8042/serioX/serioY

  • 触摸板(EV_ABS/0x03):

  • 触摸驱动控制器:以N_TCH线路规程的形式实现了串行触摸控制器的设备驱动程序。由于制造过程的细微差别,同一款触摸板可能会产生略有不同的坐标范围,为了使应用程序不受影响,在使用之前一般通过GUI发起校准,如果支持自校准就不用了。

Evdev 接口

Evdev是一个通用的输入事件驱动程序。Evdev产生的每个事件包都有如下格式:

<linux/input.h>头文件中

struct input_event {
	struct timeval time;

	__u16 type; /*事件类型*/

	__u16 code; /*事件编码*/

	__s32 value; /*事件的数值*/
};

调试

编写输入驱动程序时,可以使用evbug模块辅助调试。它dump输入子系统产生的事件对应的type,code,value元组,

输入事件驱动程序的使用

输入事件

初始化:

1、定义结构体:struct input_dev *xxx;

2、结构分配:xxx = input_allocate_device();

3、申明所支持的事件类型:set_bit(事件类型,xxx->evbit);

4、申明该事件类型有可能产生的事件编码:set_bit(事件编码,xxx->keybit/relbit/absbit/…)


注册: input_register_device(xxx);
注销: input_unregister_device(xxx);
事件上报: input_report_key(keyinput,KEY_3,1);
事件上报结束: input_sync(keyinput);

输入事件驱动注册完成会在 /dev 目录下创建eventx设备文件,

同时在 sys/devices/virtual/input/input3 也会创建设备文件…具体要进一步分析


按键输入事件驱动程序设计


/************************************
作者:hntea
时间:2016/3/16
函数功能:平台总线测试
问题1:input: Unspecified device as /devices/virtual/input/input3
问题2:应用软件只能接受一次事件上报
************************************/
#include<linux/init.h>
#include<linux/module.h>
#include<linux/device.h>
#include<linux/platform_device.h>
#include<linux/interrupt.h>
#include<linux/fs.h>
#include<linux/miscdevice.h>
#include<linux/mm.h>
#include<linux/io.h>
#include<linux/input.h>

struct input_dev *keyinput;
/***********************************

函数名:

函数功能:中断处理

函数参数

***********************************/

static irqreturn_t key1_hander (int irq, void *dev_id)
{

	// printk("key1 down!\n");
	input_report_key(keyinput,KEY_1,1);
	input_sync(keyinput);
	return 0;
}

static irqreturn_t key2_hander (int irq, void *dev_id)
{
	// printk("key2 down!\n");

	input_report_key(keyinput,KEY_2,1);

	input_sync(keyinput);

	return 0;
}

static irqreturn_t key3_hander (int irq, void *dev_id)
{
	// printk("key3 down!\n");
	input_report_key(keyinput,KEY_3,1);
	input_sync(keyinput);
	return 0;
}

static irqreturn_t key4_hander (int irq, void *dev_id)
{
	input_report_key(keyinput,KEY_4,1);
	input_sync(keyinput);
	// printk("key4 down!\n");
	return 0;
}

/***********************************

函数名:

函数功能:定义平台驱动

函数参数:

***********************************/

struct resource* res_irq;

struct resource* res_iomem;

unsigned int* key_ioconfig;

int key_probe(struct platform_device *pdev)
{

	/*初始化事就交到这里*/
	int ret = 0;
	int size = 0;
	unsigned int temdata=0;
	
	printk("Probe  Devices Success!\n");
	/*1、硬件初始化*/
	   res_iomem =  platform_get_resource(pdev, IORESOURCE_MEM, 0); /*最后一个参数是索引*/
	   size = (res_iomem->end - res_iomem->start); /*8个字节*/
	key_ioconfig = ioremap(res_iomem->start,size);  
	temdata = ioread32(key_ioconfig);
	temdata &= (~0xfffff);
	temdata |= 0xfffff;
	iowrite32(temdata,key_ioconfig);
	
	/*注册中断*/

	res_irq =  platform_get_resource(pdev, IORESOURCE_IRQ, 0);

	    ret = request_irq(res_irq->start + 0,key1_hander,IRQF_TRIGGER_FALLING,"key1",0);

	ret = request_irq(res_irq->start + 1,key2_hander,IRQF_TRIGGER_FALLING,"key2",0);

	ret = request_irq(res_irq->start + 2,key3_hander,IRQF_TRIGGER_FALLING,"key3",0);

	ret = request_irq(res_irq->start + 3,key4_hander,IRQF_TRIGGER_FALLING,"key4",0);
	
	return ret;
}

int key_remove(struct platform_device *pdev)
{

	/*释放中断*/
	free_irq(res_irq->start + 0, 0);
	free_irq(res_irq->start + 1, 0);
	free_irq(res_irq->start + 2, 0);
	free_irq(res_irq->start + 3, 0);
	
	/*注销内存*/
	iounmap(key_ioconfig);
	return 0;
}

 

/*平台驱动可以有多个*/

static struct platform_driver key_driver = {
	.probe = key_probe,
	.remove = key_remove,
	.driver = {
	.owner = THIS_MODULE,
	.name = "keydevices",
	},
};


/***********************************

函数名:

函数功能:

函数参数

***********************************/

static int keyPlatformDriverInit(void)
{
	int ret = 0;
	/*平台驱动注册*/
	if( (ret = platform_driver_register(&key_driver))<0)
		printk("platform_driver_register err!\n");

	/*输入事件驱动注册*/
	if(!(keyinput = input_allocate_device())){
		printk("input devices allocate err!");
	}


	set_bit(EV_KEY,keyinput->evbit);
	set_bit(KEY_1,keyinput->keybit);
	set_bit(KEY_2,keyinput->keybit);
	set_bit(KEY_3,keyinput->keybit);
	set_bit(KEY_4,keyinput->keybit);

	if ((ret = input_register_device(keyinput)) <0 ){
		printk("input devices register err!");
	}

	return ret;
}

 

static void keyPlatformDriverExit(void)
{
	/*平台驱动注销*/
	platform_driver_unregister(&key_driver);
	/*注销输入设备*/
	input_unregister_device(keyinput);
}

 

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Hntea");
module_init(keyPlatformDriverInit);
module_exit(keyPlatformDriverExit);

测试APP

/*

 *      Buttons Example for Matrix V

 *

 *      Copyright (C) 2004 capbily - friendly-arm

 * capbily@hotmail.com

 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>
#include <linux/input.h>

int main(void)
{

	int buttons_fd;
	int key_value,i=0,count;

	struct input_event ev_key;

	buttons_fd = open("/dev/event2", O_RDWR);

	if (buttons_fd < 0) {
		perror("open device buttons");
		exit(1);
	}	

	for (;;)
	{

		count = read(buttons_fd,&ev_key,sizeof(struct input_event));

		for(i=0; i<(int)count/sizeof(struct input_event); i++)
		{

			if(EV_KEY==ev_key.type)
				printf("type:%d,code:%d,value:%d\n",ev_key.type,ev_key.code-1,ev_key.value);

			if(EV_SYN==ev_key.type)
				printf("syn event\n\n");

		}

		i=0;

	}

	close(buttons_fd);
	return 0;

}