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