作为鸡生蛋系列文章,这里主要关注Linux input系统,
主要为触摸事件上报流程.
读该文章最好有对linux驱动的入门知识.
其实当你自己去分析了input系统后,再分析别的就相对很轻松了,
linux里好多套路都差不多的.
本文例子以ft6236.c驱动为例, 当然你也可以用goodix或者别的触摸来分析.
但是分析基于的内核版本用4.19.6(我写这篇文档时最新稳定版)
(https://git.kernel.org/pub/sc...
文档可参看
<<linux-4.19.6>>/Documentation/input/input.rst
<<linux-4.19.6>>/Documentation/input/input-programming.rst
触屏设备驱动
eg:
(https://source.codeaurora.org...
static irqreturn_t ft6236_interrupt(int irq, void *dev_id)
{
......//5. 中断处理中读数据
error = ft6236_read(ft6236->client, 0, sizeof(buf), &buf);
......
for (i = 0; i < touches; i++) {
struct ft6236_touchpoint *point = &buf.points[i];
u16 x = ((point->xhi & 0xf) << 8) | buf.points[i].xlo;
u16 y = ((point->yhi & 0xf) << 8) | buf.points[i].ylo;
......
input_mt_slot(input, id);
input_mt_report_slot_state(input, MT_TOOL_FINGER, act);
......//5. 上报数据, ABS即坐标的绝对值
input_report_abs(input, ABS_MT_POSITION_X, x);
input_report_abs(input, ABS_MT_POSITION_Y, y);
......
input_mt_sync_frame(input);
input_sync(input);
......
}
//2. probe函数, 当设备与驱动匹配上时会执行该函数
static int ft6236_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
......// 3. input设备申请
input = devm_input_allocate_device(dev);
......
ft6236->input = input;
input->name = client->name;
input->id.bustype = BUS_I2C;
......// 3. input设备参数/能力申明
input_set_abs_params(input, ABS_MT_POSITION_X, 0,
ft6236->max_x, fuzz_x, 0);
input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
ft6236->max_y, fuzz_y, 0);
......
error = input_mt_init_slots(input, FT6236_MAX_TOUCH_POINTS,
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
...... 5. 中断来时回调到ft6236_interrupt
error = devm_request_threaded_irq(dev, client->irq, NULL,
ft6236_interrupt, IRQF_ONESHOT,
client->name, ft6236);
......4. 注册为Input类设备
error = input_register_device(input);
......
}
//of table和id table在设备和驱动匹配时会用到
#ifdef CONFIG_OF
static const struct of_device_id ft6236_of_match[] = {
.....
MODULE_DEVICE_TABLE(of, ft6236_of_match);
#endif
static const struct i2c_device_id ft6236_id[] = {
.....
MODULE_DEVICE_TABLE(i2c, ft6236_id);
static struct i2c_driver ft6236_driver = {
.driver = {
.name = "ft6236",
.of_match_table = of_match_ptr(ft6236_of_match),
},
.probe = ft6236_probe,
.id_table = ft6236_id,
};
//1. 模块init, 这是一个宏定义, 里面包含了, module_init, i2c的添加驱动注册,
//module_init可以理解为对该文件的加载顺序,其它的还有core_initcall late_initcall等
module_i2c_driver(ft6236_driver);
简单说明下实现一个触屏驱动包含以下内容
- 文件和模块init
- 按照linux设备模型填充i2c驱动(设备一般在dts里配置,这里不提)
- 设备和驱动匹配上后,执行驱动的probe()函数, probe()里申请input device, 能力填充, 再在里面将设备注册为input类
- 当点击屏后,中断来了,回调中断处理函数
- 中断处理里, 通过i2c的方法从硬件读取数据,并进行上报.
注意, 触屏上报有个多点触摸协议,可参看文档
<<linux-4.19.6>>/Documentation/input/multi-touch-protocol.rst
上报--input_report_abs()
我们的重点是想知道数据上报流程, 所以自然要分析input_report_abs()
include/linux/input.h
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_ABS, code, value);
}
可以看到其为内联函数, 为input_event(,EV_ABS, ...)的二次封装;
input_report_key() -+ +- EV_KEY
input_report_rel() -| |- EV_REL
input_report_abs() -| |- EV_ABS
input_report_ff_status() -|--input_event() --|- EV_FF_STATUS
input_report_switch() -| |- EV_SW
input_sync() -| |- EV_SYN, SYN_REPORT
input_mt_sync() -+ +- EV_SYN, SYN_MT_REPORT
对于我们的根据来说,即
input_event(dev, EV_ABS, ABS_MT_POSITION_X, 坐标值)
drivers/input/input.c
void input_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
....//event是否支持, 这个和驱动里probe()时填充能力,设置参数有关,略过
if (is_event_supported(type, dev->evbit, EV_MAX)) {
....
input_handle_event(dev, type, code, value);
...
}
static void input_handle_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
int disposition = input_get_disposition(dev, type, code, &value); //得到disposition
......
if (disposition & INPUT_FLUSH) {
if (dev->num_vals >= 2)
input_pass_values(dev, dev->vals, dev->num_vals);
dev->num_vals = 0;
} else if (dev->num_vals >= dev->max_vals - 2) {
dev->vals[dev->num_vals++] = input_value_sync;
input_pass_values(dev, dev->vals, dev->num_vals); //**<--> 重点,
dev->num_vals = 0;
}
}
还记