linux kernel input event处理流程
linux kernel input event处理流程
input驱动会调用input_register_device()来注册device到input framework;
input设备驱动接收到时间输入事件时(接收的方式一般是硬件interrupt),会call input_event()上报给input framework;
input framework然后会将此input event callback给向它注册了handler的人,注册handler的API为input_register_handler(),然后handler再处理此input event。
1. 向input framework注册input device/input_register_device()
drivers/input/misc/hisi_powerkey.c
static int hi65xx_powerkey_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct input_dev *input; int irq, i, error; input = devm_input_allocate_device(dev); if (!input) { dev_err(dev, "failed to allocate input device\n"); return -ENOMEM; } input->phys = "hisi_on/input0"; input->name = "HISI 65xx PowerOn Key"; input_set_capability(input, EV_KEY, KEY_POWER); input_set_capability(input, EV_KEY, KEY_RESTART); for (i = 0; i < ARRAY_SIZE(hi65xx_irq_info); i++) { irq = platform_get_irq_byname(pdev, hi65xx_irq_info[i].name); if (irq < 0) return irq; error = devm_request_any_context_irq(dev, irq, hi65xx_irq_info[i].handler, IRQF_ONESHOT, hi65xx_irq_info[i].name, input); if (error < 0) { dev_err(dev, "couldn't request irq %s: %d\n", hi65xx_irq_info[i].name, error); return error; } } error = input_register_device(input); if (error) { dev_err(dev, "failed to register input device: %d\n", error); return error; } device_init_wakeup(dev, 1); return 0; }
2. 向input framework上报input event
static irqreturn_t hi65xx_power_press_isr(int irq, void *q) { struct input_dev *input = q; pm_wakeup_event(input->dev.parent, MAX_HELD_TIME); input_report_key(input, KEY_POWER, 1); input_sync(input); return IRQ_HANDLED; } static irqreturn_t hi65xx_power_release_isr(int irq, void *q) { struct input_dev *input = q; pm_wakeup_event(input->dev.parent, MAX_HELD_TIME); input_report_key(input, KEY_POWER, 0); input_sync(input); return IRQ_HANDLED; }
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
对于按键input event,input_report_key()参数value的值含义如下:
0: release
1: press
2: repeat
通过input_report_key()上报input event,input framework并不会马上处理此input event,而是将这些event收集在一个数组里,等input device driver call了input_sync()时才会将这些收集起来的event一起callback给handler。
static void input_handle_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { int disposition; /* filter-out events from inhibited devices */ if (dev->inhibited) return; disposition = input_get_disposition(dev, type, code, &value); if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN) add_input_randomness(type, code, value); if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event) dev->event(dev, type, code, value); if (!dev->vals) return; if (disposition & INPUT_PASS_TO_HANDLERS) { struct input_value *v; if (disposition & INPUT_SLOT) { //一般是触摸屏类输入设备 v = &dev->vals[dev->num_vals++]; v->type = EV_ABS; v->code = ABS_MT_SLOT; v->value = dev->mt->slot; } v = &dev->vals[dev->num_vals++]; v->type = type; v->code = code; v->value = value; } if (disposition & INPUT_FLUSH) { //input_sync()时将会有INPUT_FLUSH flag,调用input_pass_values()将收集到的event一起发给handler处理 if (dev->num_vals >= 2) input_pass_values(dev, dev->vals, dev->num_vals); dev->num_vals = 0; /* * Reset the timestamp on flush so we won't end up * with a stale one. Note we only need to reset the * monolithic one as we use its presence when deciding * whether to generate a synthetic timestamp. */ dev->timestamp[INPUT_CLK_MONO] = ktime_set(0, 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; } }
void input_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
input_event()参数解析:
1. type,即event type,它可能的type列举如下。比如如果是key event,则type为EV_KEY;执行input_sync()时,type为EV_SYN;
include/uapi/linux/input-event-codes.h
/* * Event types */ #define EV_SYN 0x00 #define EV_KEY 0x01 #define EV_REL 0x02 #define EV_ABS 0x03 #define EV_MSC 0x04 #define EV_SW 0x05 #define EV_LED 0x11 #define EV_SND 0x12 #define EV_REP 0x14 #define EV_FF 0x15 #define EV_PWR 0x16 #define EV_FF_STATUS 0x17 #define EV_MAX 0x1f #define EV_CNT (EV_MAX+1)
2. code,比如type为key event时,code为key code,比如按下power key时,此时key code为0x74(power key code为0x74)
key event code值的定义都在include/uapi/linux/input-event-codes.h
3. value,比如为key event时,它的取值含义如下:
0: release
1: press
2: repeat
三、执行handler callback function
注册handler
drivers/input/mousedev.c
static int __init mousedev_init(void) { int error; mousedev_mix = mousedev_create(NULL, &mousedev_handler, true); if (IS_ERR(mousedev_mix)) return PTR_ERR(mousedev_mix); error = input_register_handler(&mousedev_handler); if (error) { mousedev_destroy(mousedev_mix); return error; } mousedev_psaux_register(); pr_info("PS/2 mouse device common for all mice\n"); return 0; }
在input_register_device()时,input framework会遍历input_handler_list链表,调用input_attach_handler(),此函数会调用input_match_device(),此函数根据handler里的id_table来match input device driver,如果match,则attach handler成功:
static const struct input_device_id mousedev_ids[] = { { .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_RELBIT, .evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_REL) }, .keybit = { [BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) }, .relbit = { BIT_MASK(REL_X) | BIT_MASK(REL_Y) }, }, /* A mouse like device, at least one button, two relative axes */ { .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_RELBIT, .evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_REL) }, .relbit = { BIT_MASK(REL_WHEEL) }, }, /* A separate scrollwheel */ { .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT, .evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) }, .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) }, .absbit = { BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) }, }, /* A tablet like device, at least touch detection, two absolute axes */ { .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT, .evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) }, .keybit = { [BIT_WORD(BTN_TOOL_FINGER)] = BIT_MASK(BTN_TOOL_FINGER) }, .absbit = { BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) | BIT_MASK(ABS_PRESSURE) | BIT_MASK(ABS_TOOL_WIDTH) }, }, /* A touchpad */ { .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT, .evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) }, .keybit = { [BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) }, .absbit = { BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) }, }, /* Mouse-like device with absolute X and Y but ordinary clicks, like hp ILO2 High Performance mouse */ { }, /* Terminating entry */ }; MODULE_DEVICE_TABLE(input, mousedev_ids); static struct input_handler mousedev_handler = { .event = mousedev_event, .connect = mousedev_connect, .disconnect = mousedev_disconnect, .legacy_minors = true, .minor = MOUSEDEV_MINOR_BASE, .name = "mousedev", .id_table = mousedev_ids, };
一个实例input event上报处理flow如下:
[ 530.765795] input_to_handler+0x178/0x1d8
[ 530.769810] input_pass_values+0x164/0x17c
[ 530.773910] input_handle_event+0x8b8/0xb34
[ 530.778099] input_event+0x94/0xf8
console:/ # getevent
could not get driver version for /dev/input/mice, Not a typewriter
add device 1: /dev/input/event0
name: "xxx KEYPAD"
add device 2: /dev/input/event1
name: "xxx IR Receiver"
/dev/input/event1: EV_MSC MSC_SCAN 00807f1a
/dev/input/event1: EV_KEY KEY_RIGHT DOWN
/dev/input/event1: EV_SYN SYN_REPORT 00000000
/dev/input/event1: EV_KEY KEY_RIGHT UP
/dev/input/event1: EV_SYN SYN_REPORT 00000000