做爲雞生蛋系列文章,這裏主要關注Linux input系統,
主要爲觸摸事件上報流程.node
讀該文章最好有對linux驅動的入門知識.
其實當你本身去分析了input系統後,再分析別的就相對很輕鬆了,
linux裏好多套路都差很少的.linux
本文例子以ft6236.c驅動爲例, 固然你也能夠用goodix或者別的觸摸來分析.
可是分析基於的內核版本用4.19.6(我寫這篇文檔時最新穩定版)
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/?h=v4.19.6
文檔可參看
<<linux-4.19.6>>/Documentation/input/input.rst
<<linux-4.19.6>>/Documentation/input/input-programming.rstandroid
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);
簡單說明下實現一個觸屏驅動包含如下內容segmentfault
注意, 觸屏上報有個多點觸摸協議,可參看文檔
<<linux-4.19.6>>/Documentation/input/multi-touch-protocol.rstapi
咱們的重點是想知道數據上報流程, 因此天然要分析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, 座標值)async
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; } }
還記得在驅動中斷回調函數ft6236_interrupt()裏,上報值時,咱們調用了這些函數,函數
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);
這些值到input_event()對應着
input_report_abs() -| |- EV_ABS input_sync() -|--input_event() --|- EV_SYN, SYN_REPORT input_mt_sync() -+ +- EV_SYN, SYN_MT_REPORT
因此咱們能夠簡單看下input_handle_event() --> input_get_disposition()
EV_SYN事件和EV_ABS的返回值
static int input_get_disposition(struct input_dev *dev, unsigned int type, unsigned int code, int *pval) { int disposition = INPUT_IGNORE_EVENT; ...... switch (type) { case EV_SYN: switch (code) { case SYN_CONFIG: disposition = INPUT_PASS_TO_ALL; break; case SYN_REPORT: disposition = INPUT_PASS_TO_HANDLERS | INPUT_FLUSH; break; case SYN_MT_REPORT: disposition = INPUT_PASS_TO_HANDLERS; break; } break; ...... case EV_ABS: if (is_event_supported(code, dev->absbit, ABS_MAX)) disposition = input_handle_abs_event(dev, code, &value);//這個能夠看看,他會對相同值進行過濾,返回INPUT_IGNORE_EVENT break; ...... return disposition; }
讓咱們回到input_handle_event() --> input_pass_values()
static void input_pass_values(struct input_dev *dev, struct input_value *vals, unsigned int count) { ...... if (handle) { count = input_to_handler(handle, vals, count); } else { list_for_each_entry_rcu(handle, &dev->h_list, d_node) if (handle->open) { count = input_to_handler(handle, vals, count); if (!count) break; } } ...... }
其重點函數爲input_to_handler()
static unsigned int input_to_handler(struct input_handle *handle, struct input_value *vals, unsigned int count) { struct input_handler *handler = handle->handler; ...... if (handler->filter) { for (v = vals; v != vals + count; v++) { if (handler->filter(handle, v->type, v->code, v->value)) continue; ....... } ...... if (handler->events) handler->events(handle, vals, count); //<--handler的events. else if (handler->event) for (v = vals; v != vals + count; v++) handler->event(handle, v->type, v->code, v->value); return count; }
分析到這個函數的時候, 彷佛有些斷了,
咱們看到有三個handler->filter(), handler->events(), handler->event()函數調用,
哪這三個函數又調用到哪兒去了呢?這時又該如何繼續分析呢?
對此,
static int ft6236_probe(struct i2c_client *client, const struct i2c_device_id *id) { ......// 3. input設備申請 input = devm_input_allocate_device(dev); ......// 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); ......4. 註冊爲Input類設備 error = input_register_device(input);
因此有很大機率是在申請設備, 設備能力, slots設置,註冊input類這幾個函數裏面實現的.
咱們這裏就直接看答案
int input_register_device(struct input_dev *dev) { ......//前面有些默認能力參數等的設置,略過 error = device_add(&dev->dev); ......//將設備節點加入到input_dev_list list_add_tail(&dev->node, &input_dev_list); //遍歷input_handler_list, 而後調用input_attach_handler,看匹配的handler list_for_each_entry(handler, &input_handler_list, node) input_attach_handler(dev, handler); ....... }
input_dev_list 和 input_handler_list, 是定義的兩個list,
static LIST_HEAD(input_dev_list);
static LIST_HEAD(input_handler_list);
咱們能夠猜想,全部的input dev和handler都會掛在這兩個list裏,
而後調用上面的input_attach_handler()進行二者的匹配,
對於dev list咱們不關注,有興趣的同窗可本身看下,
重點想要知道的是handler相關的,
那咱們的問題天然又轉爲
哪些會掛到input_handler_list上?
搞明白這個問題,而後進一步的分析input_attach_handler()匹配.
經過對drivers/input/input.c搜索, 以爲input_register_handler()這個的可能性最大,
由於list嘛,確定有對他進行add的地方, 別的地方代碼都沒有add
int input_register_handler(struct input_handler *handler) { ......//初始化h_list INIT_LIST_HEAD(&handler->h_list); //將node加到list尾部 list_add_tail(&handler->node, &input_handler_list); //在註冊handler的時候也對已有設備調用一次attach() list_for_each_entry(dev, &input_dev_list, node) input_attach_handler(dev, handler); ...... }
先看下input_handler定義,裏面就有咱們想找的event() filter()函數
include/linux/input.h struct input_handler { void *private; void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value); void (*events)(struct input_handle *handle, const struct input_value *vals, unsigned int count); bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value); bool (*match)(struct input_handler *handler, struct input_dev *dev); int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id); void (*disconnect)(struct input_handle *handle); void (*start)(struct input_handle *handle); ...... const char *name; const struct input_device_id *id_table; struct list_head h_list; struct list_head node; };
而後再進一步,咱們就想要知道誰在調用input_register_handler()註冊handler了.
經過搜索代碼,我這裏列舉下
File | handler名 | 在哪一個函數裏註冊的 |
---|---|---|
drivers/input/apm-power.c | apmpower_handler | apmpower_init() |
drivers/input/evbug.c | evbug_handler | evbug_init() |
drivers/input/input-leds.c | input_leds_handler | input_leds_init() |
drivers/input/joydev.c | joydev_handler | joydev_init() |
drivers/input/mousedev.c | mousedev_handler | mousedev_init() |
drivers/input/evdev.c | evdev_handler | evdev_init() |
drivers/tty/serial/ | kgdboc.c kgdboc_reset_handler | kgdboc_restore_input_helper() |
drivers/macintosh/mac_hid.c | mac_hid_emumouse_handler | mac_hid_start_emulation() |
net/rfkill/input.c | rfkill_handler | rfkill_handler_init() |
drivers/tty/sysrq.c | sysrq_handler | sysrq_register_handler() |
drivers/tty/vt/keyboard.c | kbd_handler | kbd_init() |
由上咱們知道,在各個模塊的init裏,註冊了所支持的handler,
用來處理幾類常見的事件,如鼠標、鍵盤、搖桿等(其中最爲基礎的是evdev_handler,
它可以接收任意類型的事件,任意id的設備均可以和它匹配鏈接)
也就是說,最終的handler的調用函數是上面的handler中的一個。
哪咱們的觸屏究竟用的哪個handler呢?這就得接下來看attach裏的匹配過程了
input_attach_handler() --> input_match_device() --> input_match_device_id() static int input_attach_handler(struct input_dev *dev, struct input_handler *handler) { const struct input_device_id *id; int error; id = input_match_device(handler, dev); //-->匹配 if (!id) return -ENODEV; error = handler->connect(handler, dev, id); //-->鏈接 ...... return error; } bool input_match_device_id(const struct input_dev *dev, const struct input_device_id *id) { if (id->flags & INPUT_DEVICE_ID_MATCH_BUS) //Bus總線的匹配 ...... if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR) //Vendor匹配 ...... if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT) //Product匹配 ...... if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION) ...... if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX) || //匹配id的evbit和input_dev中evbit的各個位 !bitmap_subset(id->keybit, dev->keybit, KEY_MAX) || .....) { return false; } return true; } static const struct input_device_id *input_match_device(struct input_handler *handler, struct input_dev *dev) { const struct input_device_id *id; for (id = handler->id_table; id->flags || id->driver_info; id++) { //進行id的一個匹配, 若是有match爲空或者match成功, 返回id if (input_match_device_id(dev, id) && (!handler->match || handler->match(handler, dev))) { return id; } } return NULL; }
device id的定義以下,
struct input_device_id { kernel_ulong_t flags; __u16 bustype; __u16 vendor; __u16 product; __u16 version; kernel_ulong_t evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG + 1]; kernel_ulong_t keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG + 1]; kernel_ulong_t relbit[INPUT_DEVICE_ID_REL_MAX / BITS_PER_LONG + 1]; kernel_ulong_t absbit[INPUT_DEVICE_ID_ABS_MAX / BITS_PER_LONG + 1]; ...... kernel_ulong_t driver_info; };
基實整個匹配也就是進行,總線,廠商,能力(evbit, keybit), id_table的匹配,
咱們的觸屏也是匹配到的evdev_handler,
咱們能夠再看一下evdev_handler的定義
drivers/input/evdev.c static const struct input_device_id evdev_ids[] = { { .driver_info = 1 }, /* Matches all devices */ { }, /* Terminating zero entry */ }; MODULE_DEVICE_TABLE(input, evdev_ids); static struct input_handler evdev_handler = { .event = evdev_event, .events = evdev_events, .connect = evdev_connect, ...... .minor = EVDEV_MINOR_BASE, .name = "evdev", .id_table = evdev_ids, //<--id_table };
回到input_register_handler() --> input_attach_handler() --> handler->connect()
咱們以handler drivers/input/evdev.c爲例分析
其connect()裏作的事情
static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id) { ......//從這個定義咱們可知,input的從設備號從64開始,可爲32個, 因此從設備號爲64~95 minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true); ...... evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); ...... INIT_LIST_HEAD(&evdev->client_list); ...... dev_no = minor; ...... dev_set_name(&evdev->dev, "event%d", dev_no); //<--名字爲eventN evdev->handle.dev = input_get_device(dev); //<--handle.dev evdev->handle.name = dev_name(&evdev->dev); evdev->handle.handler = handler; //<--注意一個是handle,一個是handler evdev->handle.private = evdev; evdev->dev.devt = MKDEV(INPUT_MAJOR, minor); <--//主設備號INPUT_MAJOR爲13,include/uapi/linux/major.h evdev->dev.class = &input_class; //<--- 類別爲input_class, 即/sys/class/input/ evdev->dev.parent = &dev->dev; evdev->dev.release = evdev_free; device_initialize(&evdev->dev); error = input_register_handle(&evdev->handle); //註冊handle, 注意咱們以前分析的是handler,表示鍵盤,搖桿等的可處理. ...... cdev_init(&evdev->cdev, &evdev_fops); //<--注意把 file_operations 和cdev->ops關聯起來了. error = cdev_device_add(&evdev->cdev, &evdev->dev); //<--cdev添加, 這個時候就能夠在/dev/input/看到了 ...... }
input_register_handle()所作的就是將handle句柄掛到dev和handler的list裏,
當有事件來時就知道咋處理,至此也表示一個handle和dev匹配成功.
/** .....//能夠看看這個註釋 * This function puts a new input handle onto device's * and handler's lists so that events can flow through * it once it is opened using input_open_device(). ...... */ int input_register_handle(struct input_handle *handle) { ...... if (handler->filter) list_add_rcu(&handle->d_node, &dev->h_list); else list_add_tail_rcu(&handle->d_node, &dev->h_list); ...... list_add_tail_rcu(&handle->h_node, &handler->h_list); ...... }
因此到目前爲至,咱們知道了
當各個handler init時 --> input_register_handler() --> input_attach_handler() --> handler->connect() 或者驅動 --> probe() --> input_register_device() --> input_attach_handler --> handler->connect() +--> input_register_handle() dev和handler關聯 handler->connect()--> eg:evdev.c events() --+ +-->cdev_device_add() 註冊字符設備
對於input_report_abs()上報我這也列舉整個流程, 代碼再也不詳細看了
input_report_abs() --> input_event(, EV_ABS, , ) --> input_handle_event() --> input_pass_values() --> input_to_handler() --> handler->events()/event() --> eg:evdev.c events() --> evdev_pass_values() --> 數據填充 --> __pass_event() --> client->buffer[]
static void evdev_events(struct input_handle *handle, const struct input_value *vals, unsigned int count) { ...... 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); ...... } 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; struct timespec64 ts; ......//時間 event.input_event_sec = ts.tv_sec; event.input_event_usec = ts.tv_nsec / NSEC_PER_USEC; ...... for (v = vals; v != vals + count; v++) { ......//事件數據填充 event.type = v->type; event.code = v->code; event.value = v->value; __pass_event(client, &event); //<--放到client->buffer裏 } ...... } __pass_event()將event放到client->buffer[]裏 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].input_event_sec = event->input_event_sec; client->buffer[client->tail].input_event_usec = event->input_event_usec; 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); } }
咱們從上面分析,看到數據已經放到了client->buffer[], 那讀取也確定也是從這裏讀,
具體分析就不講了,我這裏只列下
還記得evdev_connect()時將file_operations和dev關聯起來了
cdev_init(&evdev->cdev, &evdev_fops);
evdev的file_operations定義以下:
static const struct file_operations evdev_fops = { .owner = THIS_MODULE, .read = evdev_read, .write = evdev_write, .poll = evdev_poll, .open = evdev_open, .release = evdev_release, .unlocked_ioctl = evdev_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = evdev_ioctl_compat, ...... }
(evdev_open分析略過)
因此咱們很容易想到讀數據其實就是調用evdev_read(),
static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { struct evdev_client *client = file->private_data; struct evdev *evdev = client->evdev; ...... for (;;) { ......//循環讀取下一個事件, 並經過input_event_to_user() --> copy_to_user()給用戶空間, 這樣上面就讀到數據了. while (read + input_event_size() <= count && evdev_fetch_next_event(client, &event)) { if (input_event_to_user(buffer + read, &event)) ...... return read; } static int evdev_fetch_next_event(struct evdev_client *client, struct input_event *event) { int have_event; spin_lock_irq(&client->buffer_lock); have_event = client->packet_head != client->tail; if (have_event) { *event = client->buffer[client->tail++]; client->tail &= client->bufsize - 1; } spin_unlock_irq(&client->buffer_lock); return have_event; }
read時候 evdev_read--> 從client->buffer[]循環獲取事件 evdev_fetch_next_event() --> input_event_to_user() --> copy_to_user()
涉及到的一些數據結構
struct evdev { int open; struct input_handle handle; --> wait_queue_head_t wait; struct evdev_client __rcu *grab; struct list_head client_list; spinlock_t client_lock; /* protects client_list */ struct mutex mutex; struct device dev; struct cdev cdev; bool exist; }; struct evdev_client { unsigned int head; unsigned int tail; ...... struct evdev *evdev; struct list_head node; unsigned int clk_type; bool revoked; unsigned long *evmasks[EV_CNT]; unsigned int bufsize; struct input_event buffer[]; }; struct input_handler { void *private; void (*event)(....); void (*events)(....); ...... int (*connect)(......); void (*disconnect)(struct input_handle *handle); void (*start)(struct input_handle *handle); ...... int minor; const char *name; const struct input_device_id *id_table; struct list_head h_list; struct list_head node; --> input_handler_list }; struct input_handle { ...... int open; const char *name; struct input_dev *dev; struct input_handler *handler; struct list_head d_node; struct list_head h_node; };
他們可簡單用以下圖表示, 即有兩個列表, input_handler_list和input_dev_list
分別是全部可用的handler和input dev,
他們之間靠input_handle連在一塊兒.
input_handler_list[hander1|hander2|...] input_dev_list[dev1|dev2|...] ^ ^ ^ ^ | | | | | | | | [handle1{handler|dev}]--| ----------------------------------+ | [handle2{handler|dev}]---------------------------------+ [handle..{handler|dev}]略...
對於android可用命令
sendevent/getevent
發送或獲取event事件
也可查看一些節點得到信息
/proc/bus/input/
/sys/class/input/
/dev/input/
因此總的來講, 內容有以下
當各個handler init或者驅動註冊input device時,會進行handler的匹配,
匹配成功後調用handler的connect()經過handle進行device handler的關聯,
並註冊字符設備
當各個handler init時 --> input_register_handler() --> input_attach_handler() --> handler->connect() 或者驅動 --> probe() --> input_register_device() --> input_attach_handler --> handler->connect() +--> input_register_handle() dev和handler關聯 handler->connect()--> eg:evdev.c events() --+ +-->cdev_device_add() 註冊字符設備
當點擊觸屏後, 進到中斷處理,而後讀取數據,再report,並存到client的buffer[]裏
input_report_abs() --> input_event(, EV_ABS, , ) --> input_handle_event() --> input_pass_values() --> input_to_handler() --> handler->events()/event() --> eg:evdev.c events() --> evdev_pass_values() --> 數據填充 --> __pass_event() --> client->buffer[]
上層用戶空調read時(open咱們略過了), 只要有數據,不斷從client->buffer[]讀取並經過copy_to_user()拷到用戶空間, 因此上層就拿到數據了.
read時候...--> evdev_read--> 從client->buffer[]循環獲取事件 evdev_fetch_next_event() --> input_event_to_user() --> copy_to_user()
大致流向
userpace open()/read() /dev/input/event* ---------------------------------------- kernel ↑ input handler evdev.c ↑ input core input.c ↑ device driver