Linux之Input驱动总结
--本贴所贴代码均在mtk系统上完成测试
对于嵌入式产品而言,人机交互的一部分“输入设备”扮演了一个不可或缺的角色,下面来分析在Linux下的输入设备。
一、概述Linux下的input设备
所有的用于对设备进行输入操作的设备,如:鼠标、触摸屏、摇杆、键盘等都是输入设备,这种设备在Linux都是典型的字符设备。其工作机制一般都是输入操作产生一个外部中断或通过查询扫描当前状态,然后启动某种通信方式来获取按键值或坐标值或状态值放在一个缓冲区,字符设备驱动管理该缓冲区,而驱动的read()接口让用户可以读取键值,坐标等数据。
贴一下ipnut设备在Linux下的官方结构图,方便理解:
这是输入子系统的组成结构,它是由输入子系统设备驱动层、输入子系统核心层(Input Core)和输入子系统事件处理层(Event Handler)组成。其中设备驱动层提供对硬件各寄存器的读写访问和将底层硬件对用户输入访问的响应转换为标准的输入事件,再通过核心层提交给事件处理层;而核心层对下提供了设备驱动层的编程接口,对上又提供了事件处理层的编程接口;而事件处理层就为我们用户空间的应用程序提供了统一访问设备的接口和驱动层提交来的事件处理。所以这使得我们输入设备的驱动部分不在用关心对设备文件的操作,而是要关心对各硬件寄存器的操作和提交的输入事件。
下图是Linux下的input系统的分层图:
二、input驱动程序分析
Input设备的最重要的结构体是input_dev结构体,它定义在input.h文件中,它包含了你所要该输入设备驱动的所有信息,包括一些api的接口;下面按照下面的步骤我们创建一个在混杂设备驱动中注册的input设备,在源码中分析一些重要接口函数
1)、分配一个输入子设备;
2)、在驱动模块加载函数中设置Input设备支持input子系统的哪些事件;
3)、将Input设备注册到input子系统中;
4)、在Input设备发生输入操作时(如:键盘被按下/抬起、触摸屏被触摸/抬起/移动、鼠标被移动/单击/抬起时等),提交所发生的事件及对应的键值/坐标等状态;
5)、释放和卸载。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <mach/hardware.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/miscdevice.h>
#include <asm/uaccess.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/workqueue.h>
#include <linux/kobject.h>
#include <linux/earlysuspend.h>
#include <linux/platform_device.h>
#include <asm/atomic.h>
#define DEVICE_NAME "Driver_Modules" //在dev下的,该misc设备节点名称
#define ON 1
#define OFF 0
static struct input_dev *Modules_input_dev;
//在这个ioctl函数中做的事情就是发送一个 音量+的press和reless两个状态。
static long Driver_ioctl(struct file *file, unsigned int cmd,unsigned long arg)
{
long err = 0;
printk("[Modules]Driver_ioctl\n");
input_report_key(Modules_input_dev, KEY_VOLUMEUP, 1);
input_sync(Modules_input_dev);
//发送按键状态就这两个,报告按键状态及按键值,然后报告同步事件。。
input_report_key(Modules_input_dev, KEY_VOLUMEUP, 0);
input_sync(Modules_input_dev);
return err;
}
static int Driver_open(struct inode *inode, struct file *file)
{
int err = 0;
printk("[Modules]Driver_open\n");
return err ;
}
static int Driver_close(struct inode *inode, struct file *file)
{
int err = 0;
printk("[Modules]Driver_close\n");
return err ;
}
static int Modules_input_open(struct input_dev *dev)
{
printk("[Modules]Modules_input_open\n");
return 0;
}
//在这个结构体我想就不用介绍了,熟悉字符设备驱动的人都知道这个东西,所有应用层
//对驱动层的操作都是通过这里实现的。
static struct file_operations DriverModules_ops = {
.owner = THIS_MODULE,
.open = Driver_open,
.release = Driver_close,
.unlocked_ioctl = Driver_ioctl,
};
static struct miscdevice DriverModules_misc_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &DriverModules_ops,
};
static int __init Driver_init(void)
{
int ret,r;
//完成对input设备的正常操作,下面的注册是最重要的。 Modules_input_dev = input_allocate_device(); //分配一个输入设备
if (!Modules_input_dev)
return -ENOMEM;//描述一些input设备的信息,通过cat /proc/bus/input/devices 命令可以查看所有的input设备的信息
Modules_input_dev->name = "Modules_input"; //input设备的名称
Modules_input_dev->id.bustype = BUS_HOST; //总线类型
Modules_input_dev->id.vendor = 0x8888; //产家编号
Modules_input_dev->id.product = 0x6575; //产品编号
Modules_input_dev->id.version = 0x0010; //版本信息
Modules_input_dev->open = Modules_input_open; //指定一个该设备的接口
__set_bit(EV_KEY, Modules_input_dev->evbit); //添加事件的支持,告诉系统将要支持哪些事件,下面再详细介绍。
//按键事件下所支持的按键值
__set_bit(KEY_VOLUMEDOWN, Modules_input_dev->keybit);
__set_bit(KEY_VOLUMEUP, Modules_input_dev->keybit);
//完成对一个输入设备的注册
r = input_register_device(Modules_input_dev);
if (r)
{
printk("[Modules]register input device failed (%d)\n", r);
input_free_device(Modules_input_dev);//释放输入设备
return r;
}
//注册一个混杂的字符设备,这种注册方式会自动创建设备节点(属于一种特殊的字符设备驱动),它的主设备号是10,注册成功会返回0,注册失败会返回一个错误码;
ret = misc_register(&DriverModules_misc_dev);
if (ret)
{
printk("[Modules]register misc_register failed (%d)\n", ret);
input_unregister_device(Modules_input_dev);
return ret;
}
printk("[Modules]Driver Modules init \n");
}
static void __exit Driver_exit(void)
{
misc_deregister(&DriverModules_misc_dev); //卸载混杂设备
input_unregister_device(Modules_input_dev); //卸载输入设备
printk("[Modules]Driver Modules exit \n");
}
module_init(Driver_init);
module_exit(Driver_exit);
MODULE_AUTHOR("tangh in szsimtech");
MODULE_DESCRIPTION("char Driver modules");
MODULE_LICENSE("GPL");
下面的补充我也是找了贴上去的,至于这个两种上报方式有啥区别,我没去深究,在以后用的时候再做补充吧(或者好心网友帮我补充也行)。。。。
**__set_bit(EV_KEY, Modules_input_dev->evbit);
Struct input_dev中有两个成员,一个是evbit;一个是keybit.分别用来表示设备所支持的事件类型和按键类型。
Linux中输入设备的事件类型有(这里只列出了常用的一些,更多请看linux/input.h中):EV_SYN 0x00 同步事件
EV_KEY 0x01 按键事件
EV_REL 0x02 相对坐标
EV_ABS 0x03 绝对坐标
EV_MSC 0x04 其它
EV_LED 0x11 LED
EV_SND 0x12 声音
EV_REP 0x14 Repeat
EV_FF 0x15 力反馈
//报告指定type,code的输入事件
Void input_event(struct input_dev *dev,unsigned int type,unsigned int code,int value);
//报告键值
Void input_report_key(struct input_dev *dev,unsigned int code,int value);
//报告相对坐标
Void input_report_rel(struct input_dev *dev,unsigned int code,int value);
//报告绝对坐标
Void input_report_abs(struct input_dev *dev,unsigned int code,int value);Void input_sync(struct input_dev *dev);//报告同步事件
在触摸屏驱动设计中,一次坐标及按下状态的整个报告过程如下:
Input_report_abs(input_dev,ABS_X,x);//X坐标
Input_report_abs(input_dev,ABS_Y,y);//Y坐标
Input_report_abs(input_dev,ABS_PRESSURE,pres);//压力
input_sync(struct input_dev *dev);//同步
三、虚拟按键的处理
四、测试