Linux嵌入式input子系统

input子系统框架
设备驱动使用内核提供的接口,向内核上报输入事件,内核处理输入事件并且给用户层提供接口
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

1.内核用input_dev结构体表示一个输入设备(鼠标,键盘,触摸屏).
在这里插入图片描述

2.输入设备需要向内核上报一个事件,内核中用input_event表示一个事件
在这里插入图片描述
其中type表示哪类事件,比如EV_REL表示相对位移(比如鼠标),EV_ABS表示绝对位置(比如触摸屏)
在这里插入图片描述
code表示哪一个事件,EV_KEY(按键)类事件,它表示键盘。键盘上有很多按键,比如数字键1、2、3,字母键A、B、C里等。
在这里插入图片描述
对于触摸屏,它提供的是绝对位置信息,有X方向、Y方向,还有压力值。所以code值有这些:
在这里插入图片描述
value:表示事件值
对于按键,它的value可以是0(表示按键被按下)、1(表示按键被松开)、2(表示长按);
对于触摸屏,它的value就是坐标值、压力值。

内核用handler数据结构来处理input_event
在这里插入图片描述

connect函数:建立input_dev和input_handler之间的联系
match函数:用来判断handler是否支持某个input_dev
filter:处理input_dev上报的事件函数(优先级高)
event:处理input_dev上报的事件函数(优先级低)

内核中用input_handle建立input_handler和input_dev之间的联系
在这里插入图片描述

触摸屏
type=EV_ABS
code=ABS_MT_POSITION_X,ABS_MT_POSITION_Y
value=坐标值和压力值
按键
type=EV_KEY
code=KEY_1
value=0
在这里插入图片描述
在这里插入图片描述
input_dev注册过程

input_register_device注册一个input设备
	list_add_tail(&dev->node, &input_dev_list); 放到input_dev_list链表
		input_attach_handler(dev, handler);将input_dev和input_hander匹配
			input_match_device具体匹配函数
			handler->connect(handler, dev, id);匹配成功后调用input_hander.connect

input_handler注册过程

input_register_handler注册一个handler
	list_add_tail(&handler->node, &input_handler_list);将handler放到input_handler_list链表中
		input_attach_handler(dev, handler);将input_dev和input_hander匹配
			input_match_device具体匹配函数
			handler->connect(handler, dev, id);匹配成功后调用input_handler.connect
				input_register_handle(&evdev->handle); 注册handle
					list_add_rcu(&handle->d_node, &dev->h_list); 将handle链表放入h_list链表中

input_handler的connect函数做了什么

创建/注册一个input_handle,把input_dev, input_handler放在里面,这样子可以通过handle找到input_dev或者input_handler
    evdev->handle.dev = input_get_device(dev);
	evdev->handle.handler = handler;
	error = input_register_handle(&evdev->handle);

注册字符设备驱动程序,上层就可以通过/dev/input/event0来操作input_dev了
cdev_init(&evdev->cdev, &evdev_fops);
error = cdev_device_add(&evdev->cdev, &evdev->dev);

读取一个数据流程

1.APP调用open函数打开/dev/input/event0
在驱动程序evdev_open里,创建一个evdev_client,表示一个"客户"

2.APP调用read/poll读取、等待数据
2.1.有数据
	input_event_to_user(buffer + read, &event)
		copy_to_user(buffer, event, sizeof(struct input_event))
2.2.没有数据
	wait_event_interruptible(evdev->wait。。。。

3.点击、操作输入设备,产生中断     
在中断服务程序里
- 从硬件读取到数据
- 使用以下函数上报数据

```cpp
void input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value);
static inline void input_sync(struct input_dev *dev); // 实质也是 input_event

input_event做什么?

  • 从dev->h_list中取出input_handle,从input_handle取出input_handler
  • 优先调用input_handler->filter来处理
  • 如果没有input_handler->filter或者没处理成功
    • 调用input_handler->events
    • 没有input_handler->events的话,调用input_handler->event
input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)
	input_handle_event(dev, type, code, value);
		input_pass_values(dev, dev->vals, dev->num_vals);//解析值
			list_for_each_entry_rcu(handle, &dev->h_list, d_node)//从dev->h_list中取出input_handle
				input_to_handler(handle, vals, count); //从input_handle取出input_handler	
					handler->filter(handle, v->type, v->code, v->value) //调用handler的filter来处理上报事件
					handler->event(handle, v->type, v->code, v->value);//如果没有filter就调用event来处理上报事件

	

在这里插入图片描述
触摸屏样例
相关知识点:input子系统,GPIO子系统,中断子系统


#include <linux/module.h>

#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/gpio_keys.h>
#include <linux/workqueue.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/spinlock.h>

#define TOUCHSCREEN_POLL_TIME_MS 10

struct qemu_ts_con {
	volatile unsigned int pressure;
	volatile unsigned int x;
	volatile unsigned int y;
	volatile unsigned int clean;
};

static struct input_dev *g_input_dev;
static int g_irq;
static struct qemu_ts_con *ts_con;
struct timer_list ts_timer;

static void ts_irq_timer(unsigned long _data)
{
	if (ts_con->pressure) // pressed
	{
		input_event(g_input_dev, EV_ABS, ABS_X, ts_con->x);
		input_event(g_input_dev, EV_ABS, ABS_Y, ts_con->y);
		input_sync(g_input_dev);

		mod_timer(&ts_timer,
			jiffies + msecs_to_jiffies(TOUCHSCREEN_POLL_TIME_MS));
	}

}

static irqreturn_t input_dev_demo_isr(int irq, void *dev_id)
{
	/* read data */

	/* report data */
	//input_event(g_input_dev, EV_KEY, XX, 0);
	//input_sync(g_input_dev);
	//printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	if (ts_con->pressure)
	{
		input_event(g_input_dev, EV_ABS, ABS_X, ts_con->x);
		input_event(g_input_dev, EV_ABS, ABS_Y, ts_con->y);
		input_event(g_input_dev, EV_KEY, BTN_TOUCH, 1);
		input_sync(g_input_dev);

		/* start timer */
		mod_timer(&ts_timer,
			jiffies + msecs_to_jiffies(TOUCHSCREEN_POLL_TIME_MS));
	}
	else
	{
		input_event(g_input_dev, EV_KEY, BTN_TOUCH, 0);
		input_sync(g_input_dev);

		/* cancel timer */
	}
	
	return IRQ_HANDLED;
}


/* alloc/set/register platform_driver */
static int input_dev_demo_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	int error;
	//struct resource *irq;
	struct resource *io;
	int gpio;
	
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);

	gpio = of_get_gpio(pdev->dev.of_node, 0);

	/* get hardware info from device tree */

	/* alloc/set/register input_dev */
	g_input_dev = devm_input_allocate_device(dev);

	g_input_dev->name = "input_dev_demo";
	g_input_dev->phys = "input_dev_demo";
	g_input_dev->dev.parent = dev;

	g_input_dev->id.bustype = BUS_HOST;
	g_input_dev->id.vendor = 0x0001;
	g_input_dev->id.product = 0x0001;
	g_input_dev->id.version = 0x0100;

	/* set 1: which type event ? */	
	__set_bit(EV_KEY, g_input_dev->evbit);
	__set_bit(EV_ABS, g_input_dev->evbit);
	__set_bit(INPUT_PROP_DIRECT, g_input_dev->propbit);

	/* set 2: which event ? */	
	__set_bit(BTN_TOUCH, g_input_dev->keybit);
	__set_bit(ABS_X, g_input_dev->absbit);
	__set_bit(ABS_Y, g_input_dev->absbit);

	/* set 3: event params ? */	
	input_set_abs_params(g_input_dev, ABS_X, 0, 0xffff, 0, 0);
	input_set_abs_params(g_input_dev, ABS_Y, 0, 0xffff, 0, 0);
	
	error = input_register_device(g_input_dev);

	/* hardware opration */
	io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	ts_con = ioremap(io->start, io->end - io->start + 1);


	//irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
	//g_irq = irq->start;
	g_irq = gpio_to_irq(gpio);
	error = request_irq(g_irq, input_dev_demo_isr, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "input_dev_demo_irq", NULL);

	setup_timer(&ts_timer,
			ts_irq_timer, (unsigned long)NULL);

	return 0;
}

static int input_dev_demo_remove(struct platform_device *pdev)
{
	del_timer_sync(&ts_timer);
	iounmap(ts_con);
	free_irq(g_irq, NULL);
	input_unregister_device(g_input_dev);
	return 0;
}

static const struct of_device_id input_dev_demo_of_match[] = {
	{ .compatible = "100ask,input_dev_demo", },
	{ },
};

static struct platform_driver input_dev_demo_driver = {
	.probe		= input_dev_demo_probe,
	.remove	    = input_dev_demo_remove,
	.driver		= {
		.name	= "input_dev_demo",
		.of_match_table = input_dev_demo_of_match,
	}
};


static int __init input_dev_demo_init(void)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	return platform_driver_register(&input_dev_demo_driver);
}

static void __exit input_dev_demo_exit(void)
{
	platform_driver_unregister(&input_dev_demo_driver);
}

module_init(input_dev_demo_init);
module_exit(input_dev_demo_exit);

MODULE_LICENSE("GPL");



我们一般只需要编写input_dev的驱动就行了,但是我们也可以了解一下input_handler是如何接收input_evnt然后处理的?
直接看evdev.c中的event函数就行了。

evdev_event(struct input_handle *handle,unsigned int type, unsigned int code, int value)
	evdev_events(struct input_handle *handle,const struct input_value *vals, unsigned int count)
		evdev_pass_values(struct evdev_client *client,const struct input_value *vals, unsigned int count,ktime_t *ev_time)
			__pass_event(struct evdev_client *client,const struct input_event *event)
				client->buffer[client->head++] = *event;

static void evdev_event(struct input_handle *handle,
			unsigned int type, unsigned int code, int value)
{
	struct input_value vals[] = { { type, code, value } };

	evdev_events(handle, vals, 1);
}

static void evdev_events(struct input_handle *handle,
			 const struct input_value *vals, unsigned int count)
{
	struct evdev *evdev = handle->private;
	struct evdev_client *client;
	ktime_t ev_time[EV_CLK_MAX];

	ev_time[EV_CLK_MONO] = ktime_get();
	ev_time[EV_CLK_REAL] = ktime_mono_to_real(ev_time[EV_CLK_MONO]);
	ev_time[EV_CLK_BOOT] = ktime_mono_to_any(ev_time[EV_CLK_MONO],
						 TK_OFFS_BOOT);

	rcu_read_lock();

	client = rcu_dereference(evdev->grab);

	if (client)
		evdev_pass_values(client, vals, count, ev_time);
	else
		list_for_each_entry_rcu(client, &evdev->client_list, node)
			evdev_pass_values(client, vals, count, ev_time);

	rcu_read_unlock();
}

static void evdev_pass_values(struct evdev_client *client,
			const struct input_value *vals, unsigned int count,
			ktime_t *ev_time)
{
	struct evdev *evdev = client->evdev;
	const struct input_value *v;
	struct input_event event;
	bool wakeup = false;

	if (client->revoked)
		return;

	event.time = ktime_to_timeval(ev_time[client->clk_type]);

	/* Interrupts are disabled, just acquire the lock. */
	spin_lock(&client->buffer_lock);

	for (v = vals; v != vals + count; v++) {
		if (__evdev_is_filtered(client, v->type, v->code))
			continue;

		if (v->type == EV_SYN && v->code == SYN_REPORT) {
			/* drop empty SYN_REPORT */
			if (client->packet_head == client->head)
				continue;

			wakeup = true;
		}

		event.type = v->type;
		event.code = v->code;
		event.value = v->value;
		__pass_event(client, &event);
	}

	spin_unlock(&client->buffer_lock);

	if (wakeup)
		wake_up_interruptible(&evdev->wait);
}

static void __pass_event(struct evdev_client *client,
			 const struct input_event *event)
{
	client->buffer[client->head++] = *event;
	client->head &= client->bufsize - 1;

	if (unlikely(client->head == client->tail)) {
		/*
		 * This effectively "drops" all unconsumed events, leaving
		 * EV_SYN/SYN_DROPPED plus the newest event in the queue.
		 */
		client->tail = (client->head - 2) & (client->bufsize - 1);

		client->buffer[client->tail].time = event->time;
		client->buffer[client->tail].type = EV_SYN;
		client->buffer[client->tail].code = SYN_DROPPED;
		client->buffer[client->tail].value = 0;

		client->packet_head = client->tail;
	}

	if (event->type == EV_SYN && event->code == SYN_REPORT) {
		client->packet_head = client->head;
		kill_fasync(&client->fasync, SIGIO, POLL_IN);
	}
}

参考:

https://blog.csdn.net/u011456016/article/details/130996646?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-0-130996646-blog-106449162.235v38pc_relevant_sort_base1&spm=1001.2101.3001.4242.1&utm_relevant_index=3

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值