Linux Input驱动

一.简介

二. 结构体 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");


 

  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值