1、简介
1.1 概述
为输入设备(按键、键盘、触摸屏)的驱动规范完成上报输入信息任务的子系统。input是对字符设备驱动的另一种封装。
input子系统在驱动中不是必须的,它的存在只是规范了上报输入信息这一任务,减少驱动与应用开发工程师的沟通成本。
input子系统是输入设备驱动一个标准,一个约定俗成的规范,几乎所有输入设备驱动都是使用input来上报输入信息的
在Linux中,Input子系统由三大部分组成,它们是Input子系统核心层、Input子系统事件处理层和Input子系统设备驱动层。在通常情况下,Input子系统核心层和Input子系统事件处理层都已经实现了,而作为驱动开发者,我们仅仅只需要完成Input子系统设备驱动层。
1.2 编写流程
1、定义一个input_dev结构体
2、申请input_dev内存空间并初始化
3、填充input_dev部分成员
4、向core注册一个input_dev
5、获取键码并上报按键(一般在中断里)
注:input是对字符设备驱动的封装,它在底层实现了file_operations这一套机制而不用我们去填充了,只需按照以上流程即可完成驱动
1.3 主要结构体
//linux/input.h
struct input_dev{
const char *name; //设备名
const char *phys; //设备在系统中路径
const char *uniq;
struct input_id id; //用于匹配input hander参数
unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
//设备所支持事件类型,主要有EV_SYNC,EV_KEY,EV_REL,EV_ABS等
unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
//按键所对应的位图
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
//相对坐标对应位图
unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
/绝对坐标对应位图
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
unsigned int hint_events_per_packet;
unsigned int keycodemax;
unsigned int keycodesize;
void *keycode;
int (*setkeycode)(struct input_dev *dev, const struct input_keymap_entry *ke, unsigned int *old_keycode);
int (*getkeycode)(struct input_dev *dev, struct input_keymap_entry *ke);
struct ff_device *ff;
unsigned int repeat_key;
struct timer_list timer;
int rep[REP_CNT];
struct input_mt_slot *mt;
int mtsize;
int slot;
int trkid;
struct input_absinfo *absinfo;
//按键对应的键值
unsigned long key[BITS_TO_LONGS(KEY_CNT)];
//LED对应的指示灯状态
unsigned long led[BITS_TO_LONGS(LED_CNT)];
unsigned long snd[BITS_TO_LONGS(SND_CNT)];
unsigned long sw[BITS_TO_LONGS(SW_CNT)];
int (*open)(struct input_dev *dev);
void (*close)(struct input_dev *dev);
int (*flush)(struct input_dev *dev, struct file *file);
//事件处理函数,主要是接收用户下发的命令,如点亮led;
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
struct input_handle __rcu *grab;
spinlock_t event_lock;
struct mutex mutex;
unsigned int users;
bool going_away;
bool sync;
struct device dev;
//设备所支持的input handle;
struct list_headh_list;
struct list_headnode;
};
evbit的值主要有
EV_SYN 同步事件
EV_KEY 按键事件
EV_REL 相对坐标事件
EV_ABS 绝对坐标事件
keybit的值主要有
#define KEY_Q 16
#define KEY_W 17
#define KEY_E 18
#define KEY_R 19
#define KEY_T 20
1.3.2 input_dev的赋值
1.3.2.1 直接赋值
evbit[0]= BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY);
keybit[BIT_WORD(KEY_D)] |= BIT_MASK(KEY_D);
1.3.2.2 函数赋值
set_bit(nr,addr);
参数nr:要置1的那个位;
参数addr:数组首地址
set_bit( EV_SYN , evbit);
set_bit( EV_KEY , evbit);
set_bit( KEY_D , keybit);
1.4 主要方法
//申请、初始化input_dev
struct input_dev *input_allocate_device(void);
//注册input_dev
int input_register_device(struct input_dev *dev);
//注销input_dev
void input_unregister_device(struct input_dev *dev);
//上报
input_report_key(struct input_dev *dev, unsigned int code, int value);
code: 键码(填充结构体时已经注册支持)
value:按下1 或 抬起0
//同步
input_sync(struct input_dev *dev);
2、demo 基于iTop4412
不完整的demo 主要介绍input使用流程
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <asm/atomic.h>
#include <linux/input.h>
#define DEMO_DEBUG
#ifdef DEMO_DEBUG
#define dem_dbg(fmt, arg...) printk(KERN_WARNING fmt, ##arg)
#else
#define dem_dbg(fmt, arg...) printk(KERN_DEBUG fmt, ##arg)
#endif
#define KEY_CODE KEY_LEFT
static struct input_dev *input_key;
static int open_flag = 0;
static int dev_id = 123;
static int irq;
//中断回调
static irqreturn_t eint9_interrupt(int irq, void *dev_id) {
int gpio_val = 0;
printk("%s(%d)\n", __FUNCTION__, __LINE__);
/*延时去抖的改进,两次获取电平,如果是抖动直接返回*/
gpio_val = gpio_get_value(EXYNOS4_GPX1(1));
msleep(50);
if(gpio_val != gpio_get_value(EXYNOS4_GPX1(1)))
return IRQ_HANDLED;
if(gpio_val == 0){ //汇报按下事件
input_report_key(input_key, KEY_CODE, 1);
}else{ //汇报抬起事件
input_report_key(input_key, KEY_CODE, 0);
}
//汇报同步事件
input_sync(input_key);
return IRQ_HANDLED;
}
static int key_open(struct input_dev *dev)
{
dem_dbg("==> %s\n", __FUNCTION__);
if(open_flag++ == 0)
enable_irq(IRQ_EINT(19)); //打开中断
return 0;
}
static void key_close(struct input_dev *dev)
{
dem_dbg("==> %s\n", __FUNCTION__);
if(open_flag-- == 0)
disable_irq(IRQ_EINT(19));
}
static int key_probe(struct platform_device *pdev)
{
int retval;
struct resource *res = NULL;
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res == NULL) {
dem_dbg("==> no memory resource specified\n");
return PTR_ERR(res);
}
irq = res->start;
//申请中断
retval = request_irq(irq, eint9_interrupt,
IRQ_TYPE_EDGE_FALLING /*IRQF_TRIGGER_FALLING*/, "eint9", (void *)&dev_id);
if (retval < 0) {
printk("Request IRQ %d failed, %d\n", IRQ_EINT(9), retval);
goto exit;
}
disable_irq(irq); //中断关闭
//1 申请input_dev结构体变量空间
input_key = input_allocate_device();
if (!input_key) {
dem_dbg("==> allocate input device failed!\n");
retval = -ENOMEM;
goto input_alloc_err;
}
//2 input_dev结构体变量初始化
input_key->name = "key"; //查看cat /proc/bus/input/devices
input_key->phys = "inputkey";
input_key->id.bustype = BUS_HOST;
input_key->id.vendor = 0x0001;
input_key->id.product = 0x0001;
input_key->id.version = 0x0100;
//指定input设备所支持的事件:按键事件、同步事件、连续长按键事件
input_key->evbit[0] = BIT(EV_KEY) | BIT(EV_SYN) | BIT(EV_REP);
//指定input设备支持汇报的键值: KEY_CODE宏指定的KEY_LEFT
set_bit(KEY_CODE, input_key->keybit);
input_key->open = key_open;
input_key->close = key_close;
//3 input_dev结构体的注册
retval = input_register_device(input_key);
if (retval) {
dem_dbg("==> Failed to register input device!\n");
goto input_regs_err;
}
return 0;
input_regs_err:
input_free_device(input_key);
input_alloc_err:
free_irq(irq, (void *)&dev_id);
exit:
return retval;
}
static int key_remove(struct platform_device *dev)
{
dem_dbg("==> in module exit function\n");
free_irq(irq, (void *)&dev_id);
//4 input_dev结构体的注销和释放
input_unregister_device(input_key);
input_free_device(input_key);
return 0;
}
static struct platform_driver key_driver = {
.probe = key_probe,
.remove = key_remove,
.driver = {
.owner = THIS_MODULE,
.name = "demo_input",
},
};
static int __init demo_init(void)
{
dem_dbg("==>demo_init\n");
return platform_driver_register(&key_driver);
}
static void __exit demo_exit(void)
{
dem_dbg("==>demo_exit\n");
platform_driver_unregister(&key_driver);
}
module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("Dual BSD/GPL");