Linux---input输入子系统设备驱动写法 input_allocate_device()、input_event()、input_sync()

一、输入子系统input_dev结构体注册过程及事件上报流程

1、使用 input_allocate_device 函数申请一个 input_dev

struct input_dev *input_allocate_device(void)

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)];
	... ...
};

2、设置input_dev 的事件类型以及事件值

使用__set_bit设置input结构体里面的事件类型evbit,及事件值keybit(假设是按键事件)

3、使用 input_register_device 函数向 Linux 系统注册前面申请并已初始化好的 input_dev

int input_register_device(struct input_dev *dev)

4、事件上报

使用input_event() 进行事件上报,对于确定类型的事件也可以使用对应类型的上报函数,比如key类型,可以用input_report_key(),然后进行事件同步input_sync()

void input_event(struct input_dev *dev, unsigned int type, 
				unsigned int code, int value);

static inline void input_report_key(struct input_dev *dev, 
				unsigned int code, int value)
{
	input_event(dev, EV_KEY, code, !!value);
}

static inline void input_sync(struct input_dev *dev)
{
	input_event(dev, EV_SYN, SYN_REPORT, 0);
}

dev:需要上报的 input_dev。
type: 上报的事件类型,比如 EV_KEY。
code: 事件码,也就是我们注册的按键值,比如 KEY_L、 KEY_S等等。
value:事件值,比如 1 表示按键按下, 0 表示按键松开。

二、驱动实例

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
/* https://blog.csdn.net/lihuan680680/article/details/121325980 by 面朝大海0902*/
struct gpio_key{
	int gpio;
	struct gpio_desc *gpiod;
	int flag;
	int irq;
};

struct input_dev *key_input;
static struct gpio_key *ptr_gpio_key;
struct timer_list key_timer;

void timer_function(unsigned long arg)
{
	int value;
	printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);
	struct gpio_key *ptr_gpio_key_temp = arg;
	
	if(ptr_gpio_key_temp == NULL)
		return;
	
	value = gpiod_get_value(ptr_gpio_key_temp->gpiod);

	if(value == 0)
	{
		if(ptr_gpio_key_temp->gpio == ptr_gpio_key[0].gpio)
		{
			input_report_key(key_input, KEY_L, 1);
		}
		else
		{
			input_report_key(key_input, KEY_S, 1);
		}
	}
	else
	{
		if(ptr_gpio_key_temp->gpio == ptr_gpio_key[0].gpio)
		{
			input_report_key(key_input, KEY_L, 0);
		}
		else
		{
			input_report_key(key_input, KEY_S, 0);
		}
	}
	input_sync(key_input);
}

static irqreturn_t gpio_key_isr(int irq, void *dev_id)
{
	key_timer.data =(unsigned long) dev_id;
	mod_timer(&key_timer, jiffies + HZ/4);
	printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);
	return IRQ_HANDLED;
}

static int key_probe(struct platform_device *pdev)
{
	int count = 0;
	int i=0;
	enum of_gpio_flags flag;
	struct device_node *node = pdev->dev.of_node;
	
	printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);
	count = of_gpio_count(node);
	ptr_gpio_key = kzalloc(sizeof(struct gpio_key)*count, GFP_KERNEL);
	
	for(i=0;i<count;i++)
	{
		ptr_gpio_key[i].gpio = of_get_gpio_flags(node, i, &flag);
		if(ptr_gpio_key[i].gpio < 0)
		{
			printk(KERN_ERR "of_get_gpio_flags is err\r\n");
		}
		else
		{
			printk(KERN_INFO "gpio num is %d\r\n",ptr_gpio_key[i].gpio);
		}
		ptr_gpio_key[i].gpiod = gpio_to_desc(ptr_gpio_key[i].gpio);
		ptr_gpio_key[i].flag = flag & OF_GPIO_ACTIVE_LOW;
		ptr_gpio_key[i].irq = gpio_to_irq(ptr_gpio_key[i].gpio);
		request_irq(ptr_gpio_key[i].irq, gpio_key_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "gpio_key", &ptr_gpio_key[i]);
	}

	/* 1、申请 input_dev */
	key_input = input_allocate_device();
	key_input->name = "key_input";
	
	/* 2、初始化 input_dev,设置产生哪些事件 */
	__set_bit(EV_KEY, key_input->evbit);
	__set_bit(EV_REP, key_input->evbit);

	/* 3、初始化 input_dev,设置产生哪些按键 */
	__set_bit(KEY_L, key_input->keybit);
	__set_bit(KEY_S, key_input->keybit);

	/* 4、注册输入设备 */
	input_register_device(key_input);
	
	init_timer(&key_timer);
	key_timer.function = timer_function;
	key_timer.expires = ~0;
	//add_timer(&key_timer);

	return 0;	
}



static int key_remove(struct platform_device *pdev)
{
	int count = 0;
	int i = 0;
	struct device_node *node = pdev->dev.of_node;

	printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);
	count = of_gpio_count(node);

	for(i=0;i<count;i++)
	{
		free_irq(ptr_gpio_key[i].irq, &ptr_gpio_key[i]);
	}

	kfree(ptr_gpio_key);
	del_timer(&key_timer);

	/* 释放 input_dev */
	input_unregister_device(key_input);
	input_free_device(key_input);

	return 0;	
}


static const struct of_device_id my_key[] =
{
	{.compatible = "my,key_driver"},
	{},
};


static struct platform_driver key_driver =
{
	.probe  = key_probe,
	.remove = key_remove,
	.driver = 
	{
		.name = "key_gpio",
		.of_match_table = my_key,
	},
};


static int __init my_key_init(void)
{
	int result;
	printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);
	result = platform_driver_register(&key_driver);
	return result;
}


static void __exit my_key_exit(void)
{
	printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);
	platform_driver_unregister(&key_driver);
}

module_init(my_key_init);
module_exit(my_key_exit);
MODULE_LICENSE("GPL");

驱动程序初始化了2个按键,按键按下时会上报KEY_L或KEY_S类型按键值,模拟键盘“ls”,重点关注key_probe()初始化过程及timer_function()事件上报处理。
加载设备驱动input_key.ko后我们看到多了一个/dev/input/event1节点,hexdump节点并按键可以看到有事件上报的打印。

[root:~/Test]# insmod input_key.ko
[  330.832824] /home/book/code/test/input_key.c my_key_init line is 183
[  330.847402] /home/book/code/test/input_key.c key_probe line is 90
[  330.860931] gpio num is 129
[  330.864034] gpio num is 110
[  330.874126] input: key_input as /devices/virtual/input/input1
[root:~/Test]# ls -al /dev/input/event*
crw-rw---- 1 root input 13, 64 Jan  1 00:00 /dev/input/event0
crw-rw---- 1 root input 13, 65 Jan  1 00:05 /dev/input/event1
[root:~/Test]#
[root:~/Test]# hexdump /dev/input/event1
[  367.046446] /home/book/code/test/input_key.c gpio_key_isr line is 79
[  367.298456] /home/book/code/test/input_key.c gpio_key_isr line is 79
[  367.554496] /home/book/code/test/input_key.c timer_function line is 42
[  370.181289] /home/book/code/test/input_key.c gpio_key_isr line is 79
[  370.434402] /home/book/code/test/input_key.c timer_function line is 42
0000000 0172 0000 e725 0005 0001 0026 0001 0000
0000010 0172 0000 e725 0005 0000 0000 0000 0000
0000020 0172 0000 c47e 0009 0001 0026 0002 0000
[  370.969778] /home/book/code/test/input_key.c gpio_key_isr line is 79
00000e0 0172 0000 5834 000e 0001 0026 0002 0000
00000f0 0172 0000 5834 000e 0000 0000 0001 0000

三、实例测试

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "sys/ioctl.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include 
#include 
#include 
#include 
#include 
#include 

static struct input_event key_event;
int main(int argc, char **argv)
{
	int fd;
	int ret;
	
	/* 1. 判断参数 */
	if (argc != 2) 
	{
		printf("Usage: %s \n", argv[0]);
		return -1;
	}

	/* 2. 打开文件 */
	fd = open(argv[1], O_RDWR);
	if (fd == -1)
	{
		printf("can not open file %s\n", argv[1]);
		return -1;
	}

	while(1)
	{
		ret = read(fd, &key_event, sizeof(key_event));
		if(ret > 0)
		{
			switch(key_event.code)
			{
				case KEY_L:
					printf("code is KEY_L, value is %d\r\n", key_event.value);
					break;
				case KEY_S:
					printf("code is KEY_S, value is %d\r\n", key_event.value);
					break;
				default:
					//printf("type is %d ,code is %d, value is %d\r\n",key_event.type, key_event.code, key_event.value);
					break;
			}
		}
		else
		{
			printf("read error\r\n");
		}
	}

	close(fd);
	
	return 0;
}

测试程序读设备节点文件,然后将对应上报的事件进行解析并打印,测试打印如下:

[root:~/Test]# ls
input_key.ko  input_test
[root:~/Test]# chmod +x input_test
[root:~/Test]#
[root:~/Test]# ./input_test /dev/input/event1
[  923.278688] /home/book/code/test/input_key.c gpio_key_isr line is 79
[  923.534503] /home/book/code/test/input_key.c timer_function line is 42
code is KEY_L, value is 1
[  923.568936] /home/book/code/test/input_key.c gpio_key_isr line is 79
code is KEY_L, value is 2
[  923.824495] /home/book/code/test/input_key.c timer_function line is 42
code is KEY_L, value is 0
[  927.180227] /home/book/code/test/input_key.c gpio_key_isr line is 79
[  927.434446] /home/book/code/test/input_key.c timer_function line is 42
code is KEY_S, value is 1
code is KEY_S, value is 2
[  927.707812] /home/book/code/test/input_key.c gpio_key_isr line is 79
code is KEY_S, value is 2
code is KEY_S, value is 2
[  927.964523] /home/book/code/test/input_key.c timer_function line is 42
code is KEY_S, value is 0
[  930.993742] /home/book/code/test/input_key.c gpio_key_isr line is 79
[  931.244530] /home/book/code/test/input_key.c timer_function line is 42
code is KEY_S, value is 1
[  931.293970] /home/book/code/test/input_key.c gpio_key_isr line is 79
code is KEY_S, value is 2
[  931.544497] /home/book/code/test/input_key.c timer_function line is 42
code is KEY_S, value is 0
[  932.554714] /home/book/code/test/input_key.c gpio_key_isr line is 79
[  932.771783] /home/book/code/test/input_key.c gpio_key_isr line is 79
[  933.024525] /home/book/code/test/input_key.c timer_function line is 42

可以看到按键按下value为1,持续按下value为2,按键释放时value为0。这里value为何会为2,猜测是与我们设置事件类型有EV_REP有关,这里没有再深入分析了。

你可能感兴趣的:(Linux驱动,linux,运维,服务器)