1、概念:内核对分散的,多种不同类别的输入设备进行统一处理的驱动程序。
2、输入子系统的好处:
a) 统一物理形态各异的相似的输入设备的处理功能
b) 提供了用于分发输入报告给用户应用程序的简单事件(event)接口。驱动程序不必创建、管理/dev节点以及相关的访问方法,因此它能方便地被调用输入API以发送鼠标移动,键盘按键或者触摸事件给用户空间
c) 抽取了输入驱动程序的通用部分,简化驱动程序并引入一致性
3、输入子系统架构图
图解:
设备驱动层:将底层的硬件输入转化成统一事件形式,向输入核心汇报
输入核心层:为设备驱动层提供输入设备注册与操作接口
事件驱动层:主要作用是和用户空间层进行数据交互
4、输入设备驱动程序: /driver/input 目录下
a) serio:该层提供了访问老式输入硬件的库例程。为了与serio提供服务的硬件通讯,如发送命令给PS/2鼠标,需要用到:
serio_register_driver():向serio注册规定的回调例程
serio_register_port():注册open/close/write/stop/start入口函数
b) 键盘(EV_KEY/0X01):键盘驱动程序的独特之处在于它传送数据给另外一个内核子系统(TTY层),而不是通过 /dev目录下的节点传送给用户空间。驱动文件在/dev/char目录下
可以通过以下命令查看矩阵键盘的扫描码:showkey -s 或者 showkey
根据加载的键盘映射,键盘事件驱动程序进行键值翻译(查看loadkeys的操作帮助和/lib/kbd/keymaps中提供的映射文件)
c) 鼠标:鼠标输入事件驱动称为 mousedev, 通过/dev/input/mice报告鼠标事件给用户
d) 指点杆(EV_REL/0X02)(Trackpoint):是一个指定设备
详细配置选项:/sys/devices/platform/i8042/serioX/serioY
e) 触摸板(EV_ABS/0x03):
f) 触摸驱动控制器:以N_TCH线路规程的形式实现了串行触摸控制器的设备驱动程序。由于制造过程的细微差别,同一款触摸板可能会产生略有不同的坐标范围,为了使应用程序不受影响,在使用之前一般通过GUI发起校准,如果支持自校准就不用了。
5、Evdev 接口:Evdev是一个通用的输入事件驱动程序。Evdev产生的每个事件包都有如下格式:
<linux/input.h>头文件中
struct input_event {
struct timeval time;
__u16 type; /*事件类型*/
__u16 code; /*事件编码*/
__s32 value; /*事件的数值*/
};
6、调试
编写输入驱动程序时,可以使用evbug模块辅助调试。它dump输入子系统产生的事件对应的type,code,value元组,
7、输入事件驱动程序的使用
初始化:
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);
/*
* Buttons Example for Matrix V
*
* Copyright (C) 2004 capbily - friendly-arm
*/
#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;
}