// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2012-2013, 2016-2017 The Linux Foundation. All rights reserved. */ #include #include #include #include #include #include #include #include "usfcdev.h" #define UNDEF_ID 0xffffffff #define SLOT_CMD_ID 0 #define MAX_RETRIES 10 enum usdev_event_status { USFCDEV_EVENT_ENABLED, USFCDEV_EVENT_DISABLING, USFCDEV_EVENT_DISABLED, }; struct usfcdev_event { bool (*match_cb)(uint16_t, struct input_dev *dev); bool registered_event; bool interleaved; enum usdev_event_status event_status; }; static struct usfcdev_event s_usfcdev_events[MAX_EVENT_TYPE_NUM]; struct usfcdev_input_command { unsigned int type; unsigned int code; unsigned int value; }; static long s_usf_pid; static bool usfcdev_filter(struct input_handle *handle, unsigned int type, unsigned int code, int value); static bool usfcdev_match(struct input_handler *handler, struct input_dev *dev); static int usfcdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id); static void usfcdev_disconnect(struct input_handle *handle); static const struct input_device_id usfc_tsc_ids[] = { { .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT, .evbit = { BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY) }, .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) }, /* assumption: ABS_X & ABS_Y are in the same long */ .absbit = { [BIT_WORD(ABS_X)] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) }, }, { .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT, .evbit = { BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY) }, .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) }, /* assumption: MT_.._X & MT_.._Y are in the same long */ .absbit = { [BIT_WORD(ABS_MT_POSITION_X)] = BIT_MASK(ABS_MT_POSITION_X) | BIT_MASK(ABS_MT_POSITION_Y) }, }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(input, usfc_tsc_ids); static struct input_handler s_usfc_handlers[MAX_EVENT_TYPE_NUM] = { { /* TSC handler */ .filter = usfcdev_filter, .match = usfcdev_match, .connect = usfcdev_connect, .disconnect = usfcdev_disconnect, /* .minor can be used as index in the container, */ /* because .fops isn't supported */ .minor = TSC_EVENT_TYPE_IND, .name = "usfc_tsc_handler", .id_table = usfc_tsc_ids, }, }; /* * For each event type, there are a number conflicting devices (handles) * The first registered device (primary) is real TSC device; it's mandatory * Optionally, later registered devices are simulated ones. * They are dynamically managed * The primary device's handles are stored in the below static array */ static struct input_handle s_usfc_primary_handles[MAX_EVENT_TYPE_NUM] = { { /* TSC handle */ .handler = &s_usfc_handlers[TSC_EVENT_TYPE_IND], .name = "usfc_tsc_handle", }, }; static struct usfcdev_input_command initial_clear_cmds[] = { {EV_ABS, ABS_PRESSURE, 0}, {EV_KEY, BTN_TOUCH, 0}, }; static struct usfcdev_input_command slot_clear_cmds[] = { {EV_ABS, ABS_MT_SLOT, 0}, {EV_ABS, ABS_MT_TRACKING_ID, UNDEF_ID}, }; static struct usfcdev_input_command no_filter_cmds[] = { {EV_ABS, ABS_MT_SLOT, 0}, {EV_ABS, ABS_MT_TRACKING_ID, UNDEF_ID}, {EV_SYN, SYN_REPORT, 0}, }; static bool usfcdev_match(struct input_handler *handler, struct input_dev *dev) { bool rc = false; int ind = handler->minor; pr_debug("%s: name=[%s]; ind=%d\n", __func__, dev->name, ind); if (s_usfcdev_events[ind].registered_event && s_usfcdev_events[ind].match_cb) { rc = (*s_usfcdev_events[ind].match_cb)((uint16_t)ind, dev); pr_debug("%s: [%s]; rc=%d\n", __func__, dev->name, rc); } return rc; } static int usfcdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id) { int ret = 0; uint16_t ind = handler->minor; struct input_handle *usfc_handle = NULL; if (s_usfc_primary_handles[ind].dev == NULL) { pr_debug("%s: primary device; ind=%d\n", __func__, ind); usfc_handle = &s_usfc_primary_handles[ind]; } else { pr_debug("%s: secondary device; ind=%d\n", __func__, ind); usfc_handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); if (!usfc_handle) return -ENOMEM; usfc_handle->handler = &s_usfc_handlers[ind]; usfc_handle->name = s_usfc_primary_handles[ind].name; } usfc_handle->dev = dev; ret = input_register_handle(usfc_handle); pr_debug("%s: name=[%s]; ind=%d; dev=0x%pK\n", __func__, dev->name, ind, usfc_handle->dev); if (ret) pr_err("%s: input_register_handle[%d] failed: ret=%d\n", __func__, ind, ret); else { ret = input_open_device(usfc_handle); if (ret) { pr_err("%s: input_open_device[%d] failed: ret=%d\n", __func__, ind, ret); input_unregister_handle(usfc_handle); } else pr_debug("%s: device[%d] is opened\n", __func__, ind); } return ret; } static void usfcdev_disconnect(struct input_handle *handle) { int ind = handle->handler->minor; input_close_device(handle); input_unregister_handle(handle); pr_debug("%s: handle[%d], name=[%s] is disconnected\n", __func__, ind, handle->dev->name); if (s_usfc_primary_handles[ind].dev == handle->dev) s_usfc_primary_handles[ind].dev = NULL; else kfree(handle); } static bool usfcdev_filter(struct input_handle *handle, unsigned int type, unsigned int code, int value) { uint16_t i = 0; uint16_t ind = (uint16_t)handle->handler->minor; bool rc = (s_usfcdev_events[ind].event_status != USFCDEV_EVENT_ENABLED); if (s_usf_pid == current->pid) { /* Pass events from usfcdev driver */ rc = false; pr_debug("%s: event_type=%d; type=%d; code=%d; val=%d", __func__, ind, type, code, value); } else if (s_usfcdev_events[ind].event_status == USFCDEV_EVENT_DISABLING) { uint32_t u_value = value; s_usfcdev_events[ind].interleaved = true; /* Pass events for freeing slots from TSC driver */ for (i = 0; i < ARRAY_SIZE(no_filter_cmds); ++i) { if ((no_filter_cmds[i].type == type) && (no_filter_cmds[i].code == code) && (no_filter_cmds[i].value <= u_value)) { rc = false; pr_debug("%s: no_filter_cmds[%d]; %d", __func__, i, no_filter_cmds[i].value); break; } } } return rc; } bool usfcdev_register( uint16_t event_type_ind, bool (*match_cb)(uint16_t, struct input_dev *dev)) { int ret = 0; bool rc = false; if ((event_type_ind >= MAX_EVENT_TYPE_NUM) || !match_cb) { pr_err("%s: wrong input: event_type_ind=%d; match_cb=0x%pK\n", __func__, event_type_ind, match_cb); return false; } if (s_usfcdev_events[event_type_ind].registered_event) { pr_info("%s: handler[%d] was already registered\n", __func__, event_type_ind); return true; } s_usfcdev_events[event_type_ind].registered_event = true; s_usfcdev_events[event_type_ind].match_cb = match_cb; s_usfcdev_events[event_type_ind].event_status = USFCDEV_EVENT_ENABLED; ret = input_register_handler(&s_usfc_handlers[event_type_ind]); if (!ret) { rc = true; pr_debug("%s: handler[%d] was registered\n", __func__, event_type_ind); } else { s_usfcdev_events[event_type_ind].registered_event = false; s_usfcdev_events[event_type_ind].match_cb = NULL; pr_err("%s: handler[%d] registration failed: ret=%d\n", __func__, event_type_ind, ret); } return rc; } void usfcdev_unregister(uint16_t event_type_ind) { if (event_type_ind >= MAX_EVENT_TYPE_NUM) { pr_err("%s: wrong input: event_type_ind=%d\n", __func__, event_type_ind); return; } if (s_usfcdev_events[event_type_ind].registered_event) { input_unregister_handler(&s_usfc_handlers[event_type_ind]); pr_debug("%s: handler[%d] was unregistered\n", __func__, event_type_ind); s_usfcdev_events[event_type_ind].registered_event = false; s_usfcdev_events[event_type_ind].match_cb = NULL; s_usfcdev_events[event_type_ind].event_status = USFCDEV_EVENT_ENABLED; } } static inline void usfcdev_send_cmd( struct input_dev *dev, struct usfcdev_input_command cmd) { input_event(dev, cmd.type, cmd.code, cmd.value); } static void usfcdev_clean_dev(uint16_t event_type_ind) { struct input_dev *dev = NULL; int i; int j; int retries = 0; if (event_type_ind >= MAX_EVENT_TYPE_NUM) { pr_err("%s: wrong input: event_type_ind=%d\n", __func__, event_type_ind); return; } /* Only primary device must exist */ dev = s_usfc_primary_handles[event_type_ind].dev; if (dev == NULL) { pr_err("%s: NULL primary device\n", __func__); return; } for (i = 0; i < ARRAY_SIZE(initial_clear_cmds); i++) usfcdev_send_cmd(dev, initial_clear_cmds[i]); input_sync(dev); /* Send commands to free all slots */ for (i = 0; i < dev->mt->num_slots; i++) { s_usfcdev_events[event_type_ind].interleaved = false; if (input_mt_get_value(&dev->mt->slots[i], ABS_MT_TRACKING_ID) < 0) { pr_debug("%s: skipping slot %d", __func__, i); continue; } slot_clear_cmds[SLOT_CMD_ID].value = i; for (j = 0; j < ARRAY_SIZE(slot_clear_cmds); j++) usfcdev_send_cmd(dev, slot_clear_cmds[j]); if (s_usfcdev_events[event_type_ind].interleaved) { pr_debug("%s: interleaved(%d): slot(%d)", __func__, i, dev->mt->slot); if (retries++ < MAX_RETRIES) { --i; continue; } pr_warn("%s: index(%d) reached max retires", __func__, i); } retries = 0; input_sync(dev); } } bool usfcdev_set_filter(uint16_t event_type_ind, bool filter) { bool rc = true; if (event_type_ind >= MAX_EVENT_TYPE_NUM) { pr_err("%s: wrong input: event_type_ind=%d\n", __func__, event_type_ind); return false; } if (s_usfcdev_events[event_type_ind].registered_event) { pr_debug("%s: event_type[%d]; filter=%d\n", __func__, event_type_ind, filter ); if (filter) { s_usfcdev_events[event_type_ind].event_status = USFCDEV_EVENT_DISABLING; s_usf_pid = current->pid; usfcdev_clean_dev(event_type_ind); s_usfcdev_events[event_type_ind].event_status = USFCDEV_EVENT_DISABLED; } else s_usfcdev_events[event_type_ind].event_status = USFCDEV_EVENT_ENABLED; } else { pr_err("%s: event_type[%d] isn't registered\n", __func__, event_type_ind); rc = false; } return rc; }