一.简介
二. 结构体 struct input_dev
struct input_dev {
const char *name;
const char *phys;
const char *uniq;
struct input_id id;
unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
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 *mt;
struct input_absinfo *absinfo;
unsigned long key[BITS_TO_LONGS(KEY_CNT)];
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);
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;
struct device dev;
struct list_head h_list;
struct list_head node;
unsigned int num_vals;
unsigned int max_vals;
struct input_value *vals;
bool devres_managed;
};
三. API
1.申请/释放 input设备
struct input_dev *input_allocate_device(void)
void input_free_device(struct input_dev *dev)
2. 注册/销毁 input_dev
int input_register_device(struct input_dev *dev)
void input_unregister_device(struct input_dev *dev)
综上所述, input_dev 注册过程如下:
①、使用 input_allocate_device 函数申请一个 input_dev。
②、初始化 input_dev 的事件类型以及事件值。
③、使用 input_register_device 函数向 input 子系统核心层注册前面初始化好的input_dev。
④、卸载 input 驱动
示例:
struct input_dev *inputdev; /* input 结构体变量 */
static int __init xxx_init(void)
{
/* 申请 input_dev */
inputdev = input_allocate_device();
inputdev->name = "test_inputdev"; /* 设置 input_dev 名字 */
/*********第一种设置事件和事件值的方法***********/
__set_bit(EV_KEY, inputdev->evbit); /* 设置产生按键事件 */
__set_bit(EV_REP, inputdev->evbit); /* 重复事件 */
__set_bit(KEY_0, inputdev->keybit); /* 设置产生哪些按键值 */
/************************************************/
/*********第二种设置事件和事件值的方法***********/
keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
keyinputdev.inputdev->keybit[BIT_WORD(KEY_0)] |= BIT_MASK(KEY_0);
/************************************************/
/*********第三种设置事件和事件值的方法***********/
keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
input_set_capability(keyinputdev.inputdev, EV_KEY, KEY_0);
/************************************************/
/* 注册 input_dev */
input_register_device(inputdev);
return 0;
}
/* 驱动出口函数 */
static void __exit xxx_exit(void)
{
input_unregister_device(inputdev); /* 注销 input_dev */
input_free_device(inputdev); /* 删除 input_dev */
}
四. 上报输入事件
向 input 子系统核心层注册好 input_dev 以后,将输入事件上报给 input 子系统核心层,这样 input 子系统核心层才能获取到正确的输入值。
不同的事件,上报事件的 API 函数不同,我们依次来看一下一些常用的事件上报 API 函数:
1.input_event 函数,此函数用于上报指定的事件以及对应的值
void input_event(struct input_dev *dev,
unsigned int type,
unsigned int code,
int value)
dev:需要上报的 input_dev。
type: 上报的事件类型,比如 EV_KEY。
code: 事件码,也就是我们注册的按键值,比如 KEY_0、 KEY_1 等等。
value:事件值,比如 1 表示按键按下, 0 表示按键松开
2.对应上报函数:input_report_key
static inline void input_report_key(struct input_dev *dev,
unsigned int code, int value)
{
input_event(dev, EV_KEY, code, !!value);
}
input_report_key 函数的本质就是 input_event 函数
还有:
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_report_ff_status(struct input_dev *dev, unsigned int code, int value)
void input_report_switch(struct input_dev *dev, unsigned int code, int value)
void input_mt_sync(struct input_dev *dev)
3.上报结束函数:用 input_sync 函数来告知 input 子系统上报结束
void input_sync(struct input_dev *dev)
4.举例
/* 用于按键消抖的定时器服务函数 */
void timer_function(unsigned long arg)
{
unsigned char value;
value = gpio_get_value(keydesc->gpio); /* 读取 IO 值 */
if(value == 0){ /* 按下按键 */
/* 上报按键值 */
input_report_key(inputdev, KEY_0, 1); /* 最后一个参数 1,按下 */
input_sync(inputdev); /* 同步事件 */
}
。。。
}
五.input_event 结构体
input_event 这个结构体来表示所有的输入事件.在include/uapi/linux/input.h
struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
type: 事件类型,比如 EV_KEY,表示此次事件为按键事件,此成员变量为 16 位。
code: 事件码,比如在 EV_KEY 事件中 code 就表示具体的按键码,如: KEY_0、 KEY_1 等
等这些按键。此成员变量为 16 位。
value: 值,比如 EV_KEY 事件中 value 就是按键值
六.示例
#include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/timer.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#define PS_KEY0_CODE KEY_0
/* 自定义的按键设备结构体 */
struct mykey_dev {
struct input_dev *idev; // 按键对应的input_dev指针
struct timer_list timer; // 消抖定时器
int gpio; // 按键对应的gpio编号
int irq; // 按键对应的中断号
};
/*
* @description : 定时器服务函数,用于按键消抖,定时时间到了以后
* 再读取按键值,根据按键的状态上报相应的事件
* @param - arg : arg参数可以在初始化定时器的时候进行配置
* @return : 无
*/
static void key_timer_function(unsigned long arg)
{
struct mykey_dev *key = (struct mykey_dev *)arg;
int val;
/* 读取按键值并上报按键事件 */
val = gpio_get_value(key->gpio);
input_report_key(key->idev, PS_KEY0_CODE, !val);// 上报按键事件
input_sync(key->idev); // 同步事件
/* 使能按键中断 */
enable_irq(key->irq);
}
/*
* @description : 按键中断服务函数
* @param - irq : 触发该中断事件对应的中断号
* @param - arg : arg参数可以在申请中断的时候进行配置
* @return : 中断执行结果
*/
static irqreturn_t mykey_interrupt(int irq, void *arg)
{
struct mykey_dev *key = (struct mykey_dev *)arg;
/* 判断触发中断的中断号是否是按键对应的中断号 */
if (key->irq != irq)
return IRQ_NONE;
/* 按键防抖处理,开启定时器延时15ms */
disable_irq_nosync(irq); // 禁止按键中断
mod_timer(&key->timer, jiffies + msecs_to_jiffies(15));
return IRQ_HANDLED;
}
/*
* @description : 按键初始化函数
* @param - pdev : platform设备指针
* @return : 成功返回0,失败返回负数
*/
static int mykey_init(struct platform_device *pdev)
{
struct mykey_dev *key = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
unsigned long irq_flags;
int ret;
/* 从设备树中获取GPIO */
key->gpio = of_get_named_gpio(dev->of_node, "key-gpio", 0);
if(!gpio_is_valid(key->gpio)) {
dev_err(dev, "Failed to get gpio");
return -EINVAL;
}
/* 申请使用GPIO */
ret = devm_gpio_request(dev, key->gpio, "Key Gpio");
if (ret) {
dev_err(dev, "Failed to request gpio");
return ret;
}
/* 将GPIO设置为输入模式 */
gpio_direction_input(key->gpio);
/* 获取GPIO对应的中断号 */
key->irq = irq_of_parse_and_map(dev->of_node, 0);
if (!key->irq)
return -EINVAL;
/* 获取设备树中指定的中断触发类型 */
irq_flags = irq_get_trigger_type(key->irq);
if (IRQF_TRIGGER_NONE == irq_flags)
irq_flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;
/* 申请中断 */
return devm_request_irq(dev, key->irq, mykey_interrupt,
irq_flags, "PS_Key0 IRQ", key);
}
/*
* @description : platform驱动的probe函数,当驱动与设备
* 匹配成功以后此函数会被执行
* @param - pdev : platform设备指针
* @return : 0,成功;其他负值,失败
*/
static int mykey_probe(struct platform_device *pdev)
{
struct mykey_dev *key;
struct input_dev *idev;
int ret;
dev_info(&pdev->dev, "Key device and driver matched successfully!\n");
/* 为key指针分配内存 */
key = devm_kzalloc(&pdev->dev, sizeof(struct mykey_dev), GFP_KERNEL);
if (!key)
return -ENOMEM;
platform_set_drvdata(pdev, key);
/* 初始化按键 */
ret = mykey_init(pdev);
if (ret)
return ret;
/* 定时器初始化 */
init_timer(&key->timer);
key->timer.function = key_timer_function;
key->timer.data = (unsigned long)key;
/* input_dev初始化 */
idev = devm_input_allocate_device(&pdev->dev);
if (!idev)
return -ENOMEM;
key->idev = idev;
idev->name = "mykey";
__set_bit(EV_KEY, idev->evbit); // 可产生按键事件
__set_bit(EV_REP, idev->evbit); // 可产生重复事件
__set_bit(PS_KEY0_CODE, idev->keybit); // 可产生KEY_0按键事件
/* 注册按键输入设备 */
return input_register_device(idev);
}
/*
* @description : platform驱动的remove函数,当platform驱动模块
* 卸载时此函数会被执行
* @param - dev : platform设备指针
* @return : 0,成功;其他负值,失败
*/
static int mykey_remove(struct platform_device *pdev)
{
struct mykey_dev *key = platform_get_drvdata(pdev);
/* 删除定时器 */
del_timer_sync(&key->timer);
/* 卸载按键设备 */
input_unregister_device(key->idev);
return 0;
}
/* 匹配列表 */
static const struct of_device_id key_of_match[] = {
{ .compatible = "alientek,key" },
{ /* Sentinel */ }
};
/* platform驱动结构体 */
static struct platform_driver mykey_driver = {
.driver = {
.name = "zynq-key", // 驱动名字,用于和设备匹配
.of_match_table = key_of_match, // 设备树匹配表,用于和设备树中定义的设备匹配
},
.probe = mykey_probe, // probe函数
.remove = mykey_remove, // remove函数
};
module_platform_driver(mykey_driver);
MODULE_AUTHOR("DengTao <773904075@qq.com>");
MODULE_DESCRIPTION("Key Driver Based On Input Subsystem");
MODULE_LICENSE("GPL");