diff mbox series

[v3] ARM: dts: motorola-mapphone-common: Add dts configureation for the android buttons beneath the screen

Message ID 20201030100550.010bfe4f3cdc2f4e5a599c8b@uvos.xyz (mailing list archive)
State Superseded
Headers show
Series [v3] ARM: dts: motorola-mapphone-common: Add dts configureation for the android buttons beneath the screen | expand

Commit Message

Carl Philipp Klemm Oct. 30, 2020, 9:05 a.m. UTC
Adds a driver for supporting permanet, physically labeled, buttons on
touchscreen surfaces. As are common on android phones designed with android 1.0
to 2.3 in mind. The driver works by attaching to another input device and
creating key events on its device when a press is registerd on a button.
It also provides a mirrored touchscreen device with the same events as the
attached device, but with the touchches that land on buttons filterd away
Buttons are arbitrary rectangles configurable via dts.

Signed-off-by: Carl Philipp Klemm <carl@uvos.xyz>

---

Comments

Tony Lindgren Oct. 31, 2020, 7:15 a.m. UTC | #1
Hi,

* Carl Philipp Klemm <philipp@uvos.xyz> [201030 09:06]:
> Adds a driver for supporting permanet, physically labeled, buttons on
> touchscreen surfaces. As are common on android phones designed with android 1.0
> to 2.3 in mind. The driver works by attaching to another input device and
> creating key events on its device when a press is registerd on a button.
> It also provides a mirrored touchscreen device with the same events as the
> attached device, but with the touchches that land on buttons filterd away
> Buttons are arbitrary rectangles configurable via dts.

Nice to have a generic way for the touchscreen buttons :)

Care to summarize here too what's currently blocking touchscreen buttons
driver only providing the keyboard events as a separate inputdevice
without having to mirror all the touchscreen events?

Regards,

Tony
Carl Philipp Klemm Oct. 31, 2020, 9:55 a.m. UTC | #2
Hi,

> Care to summarize here too what's currently blocking touchscreen buttons
> driver only providing the keyboard events as a separate inputdevice
> without having to mirror all the touchscreen events?

Of course. The touchscreen buttons driver has two primary tasks: it
needs to provide the input device with the keyevents of the buttons and
it needs to somehow prevent these surfaces generating regular touch
events. Currently the driver provides these on 2 different input
devices, one with just the key events and one with all the touch events
mirrored execpt those that the driver wants to filter away. 

It would seam best to avoid implementing the second input device by
using a filter in the input layer on the original touchscreen to filter
away the unwanted events. The interface is however not conducive to
this as it requires the filter immidatly decide to filter an event or
not. This however is impossible as a touch on a button is the
combination of many simultainus events and the touchscreen buttons
driver cant possibly know wather to filter an event or not until the
SYN report arrives. This could be worked around by filtering away all
events and then retransmitting the ones we dont want to filter later
using input_inject_event. The problem with this approch however is that
we cant do this from the interrupt context, and doing this later via a
workeque or similar mechanisum crates a very dificult to solve problem
where we must somehow disable the filter until our events are passed to
userpsace (to avoid retansmitting our own injected events in an endless
loop) whilst not loosing any events that might come in at any time via
an intterupt.

The above problem is likely solvable, somehow, but solveing it would
incure another problem: Registering an input filter counts as having
the device open, therby the underlying touchscreen device can not
sleep, this is unacceptable on the devices where this driver is
intended to be used, as they have power budgets in the tens of mW.
Right now this problem is solved by the fact that we can get events
from the input layer on the mirrored touchscreen devce when userspace
closes and opens this device. We can then unregister or register the
input event handler on the underlying touchscreen device accordingly.
If we directly filter the underlying touchscreen device via the method
outlined above, we loose this information as we can not get open() or
close() events on the underlying device, therby we are forced to have
the filter registerd at all times and power managment stopps working.
kernel test robot Nov. 3, 2020, 9:20 a.m. UTC | #3
Hi Carl,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on input/next]
[also build test WARNING on omap/for-next balbi-usb/testing/next v5.10-rc2 next-20201103]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Carl-Philipp-Klemm/ARM-dts-motorola-mapphone-common-Add-dts-configureation-for-the-android-buttons-beneath-the-screen/20201030-170644
base:   https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git next
config: riscv-randconfig-r013-20201030 (attached as .config)
compiler: riscv32-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/7a00d245fade30bef33d962c11b7bc121c007910
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Carl-Philipp-Klemm/ARM-dts-motorola-mapphone-common-Add-dts-configureation-for-the-android-buttons-beneath-the-screen/20201030-170644
        git checkout 7a00d245fade30bef33d962c11b7bc121c007910
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=riscv 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   drivers/input/misc/touchscreen-buttons.c: In function 'touchscreen_buttons_input_event':
   drivers/input/misc/touchscreen-buttons.c:187:34: warning: comparison of unsigned expression >= 0 is always true [-Wtype-limits]
     187 |      && buttons->queue.lastindex >= 0) {
         |                                  ^~
   drivers/input/misc/touchscreen-buttons.c: At top level:
>> drivers/input/misc/touchscreen-buttons.c:240:6: warning: no previous prototype for 'merge_task_handler' [-Wmissing-prototypes]
     240 | void merge_task_handler(struct work_struct *work)
         |      ^~~~~~~~~~~~~~~~~~
>> drivers/input/misc/touchscreen-buttons.c:252:6: warning: no previous prototype for 'close_task_handler' [-Wmissing-prototypes]
     252 | void close_task_handler(struct work_struct *work)
         |      ^~~~~~~~~~~~~~~~~~
>> drivers/input/misc/touchscreen-buttons.c:263:6: warning: no previous prototype for 'open_task_handler' [-Wmissing-prototypes]
     263 | void open_task_handler(struct work_struct *work)
         |      ^~~~~~~~~~~~~~~~~
>> drivers/input/misc/touchscreen-buttons.c:411:5: warning: no previous prototype for 'touchscreen_buttons_idev_opened' [-Wmissing-prototypes]
     411 | int touchscreen_buttons_idev_opened(struct input_dev *idev)
         |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/input/misc/touchscreen-buttons.c:434:6: warning: no previous prototype for 'touchscreen_buttons_idev_closed' [-Wmissing-prototypes]
     434 | void touchscreen_buttons_idev_closed(struct input_dev *idev)
         |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

vim +/merge_task_handler +240 drivers/input/misc/touchscreen-buttons.c

   239	
 > 240	void merge_task_handler(struct work_struct *work)
   241	{
   242		struct touchscreen_buttons *buttons =
   243		    container_of(work, struct touchscreen_buttons, merge_task);
   244	
   245		mutex_lock(&buttons->mutex);
   246		if (buttons->ts_handle && buttons->ts_handle->dev)
   247			touchscreen_buttons_merge_capabilitys(buttons->filtered_ts_idev,
   248							      buttons->ts_handle->dev);
   249		mutex_unlock(&buttons->mutex);
   250	}
   251	
 > 252	void close_task_handler(struct work_struct *work)
   253	{
   254		struct touchscreen_buttons *buttons =
   255		    container_of(work, struct touchscreen_buttons, close_task);
   256	
   257		mutex_lock(&buttons->mutex);
   258		if (buttons && buttons->ts_handle && buttons->ts_handle->open != 0)
   259			input_close_device(buttons->ts_handle);
   260		mutex_unlock(&buttons->mutex);
   261	}
   262	
 > 263	void open_task_handler(struct work_struct *work)
   264	{
   265		struct touchscreen_buttons *buttons =
   266		    container_of(work, struct touchscreen_buttons, open_task);
   267		int error;
   268	
   269		mutex_lock(&buttons->mutex);
   270		if (buttons && buttons->ts_handle) {
   271			error = input_open_device(buttons->ts_handle);
   272			if (error) {
   273				dev_err(buttons->dev,
   274					"Failed to open input device, error %d\n",
   275					error);
   276				input_unregister_handle(buttons->ts_handle);
   277				kfree(buttons->ts_handle);
   278				buttons->ts_handle = NULL;
   279			}
   280		}
   281		mutex_unlock(&buttons->mutex);
   282	}
   283	
   284	static int touchscreen_buttons_input_connect(struct input_handler *handler,
   285						     struct input_dev *dev,
   286						     const struct input_device_id *id)
   287	{
   288		struct touchscreen_buttons *buttons;
   289	
   290		buttons = handler->private;
   291	
   292		mutex_lock(&buttons->mutex);
   293	
   294		if ((!buttons->ts_handle
   295		     && device_match_of_node(&dev->dev, buttons->map->ts_node))
   296		    || (dev->dev.parent
   297			&& device_match_of_node(dev->dev.parent,
   298						buttons->map->ts_node))) {
   299			int error;
   300	
   301			dev_info(buttons->dev, "Binding to device: %s\n",
   302				 dev_name(&dev->dev));
   303	
   304			buttons->ts_handle =
   305			    kzalloc(sizeof(*buttons->ts_handle), GFP_KERNEL);
   306			if (!buttons->ts_handle) {
   307				mutex_unlock(&buttons->mutex);
   308				return -ENOMEM;
   309			}
   310	
   311			buttons->ts_handle->dev = dev;
   312			buttons->ts_handle->handler = handler;
   313			buttons->ts_handle->name = "touchscreen-buttons";
   314			buttons->ts_handle->private = handler->private;
   315			buttons->queue.lastindex = 0;
   316	
   317			error = input_register_handle(buttons->ts_handle);
   318			if (error) {
   319				dev_err(buttons->dev,
   320					"Failed to register input handler, error %d\n",
   321					error);
   322				kfree(buttons->ts_handle);
   323				buttons->ts_handle = NULL;
   324				mutex_unlock(&buttons->mutex);
   325				return error;
   326			}
   327	
   328			queue_work(buttons->workqueue, &buttons->merge_task);
   329	
   330			if (buttons->filtered_ts_idev->users > 0
   331			    && buttons->ts_handle->open == 0)
   332				queue_work(buttons->workqueue, &buttons->open_task);
   333		}
   334	
   335		mutex_unlock(&buttons->mutex);
   336		return 0;
   337	}
   338	
   339	static void touchscreen_buttons_input_disconnect(struct input_handle *handle)
   340	{
   341		struct touchscreen_buttons *buttons;
   342	
   343		buttons = handle->private;
   344	
   345		mutex_lock(&buttons->mutex);
   346		if (handle == buttons->ts_handle) {
   347			input_close_device(handle);
   348			input_unregister_handle(handle);
   349			kfree(handle);
   350			buttons->ts_handle = NULL;
   351			dev_info(buttons->dev,
   352				 "Touchscreen device disconnected buttons disabled\n");
   353		} else {
   354			dev_err(buttons->dev,
   355				"Unknown device disconnected, %p should be %p", handle,
   356				buttons->ts_handle);
   357		}
   358		mutex_unlock(&buttons->mutex);
   359	}
   360	
   361	static struct touchscreen_button_map
   362	*touchscreen_buttons_get_devtree_pdata(struct device *dev)
   363	{
   364		struct touchscreen_button_map *map;
   365		struct fwnode_handle *child_node;
   366		struct device_node *node;
   367		int i;
   368	
   369		map = kzalloc(sizeof(*map), GFP_KERNEL);
   370		if (!map)
   371			return ERR_PTR(-ENOMEM);
   372	
   373		map->count = device_get_child_node_count(dev);
   374		if (map->count == 0)
   375			return ERR_PTR(-ENODEV);
   376	
   377		map->buttons = kcalloc(map->count, sizeof(*map->buttons), GFP_KERNEL);
   378		if (!map->buttons)
   379			return ERR_PTR(-ENOMEM);
   380	
   381		node = dev->of_node;
   382		map->ts_node = of_parse_phandle(node, "touchscreen_phandle", 0);
   383		if (!map->ts_node) {
   384			dev_err(dev, "touchscreen_phandle node missing\n");
   385			return ERR_PTR(-ENODEV);
   386		}
   387	
   388		dev_info(dev, "Device_node name: %s\n", map->ts_node->name);
   389	
   390		i = 0;
   391		device_for_each_child_node(dev, child_node) {
   392			struct touchscreen_button *button;
   393	
   394			button = &map->buttons[i];
   395	
   396			fwnode_property_read_u32(child_node, "x-position", &button->x);
   397			fwnode_property_read_u32(child_node, "y-position", &button->y);
   398			fwnode_property_read_u32(child_node, "x-size", &button->width);
   399			fwnode_property_read_u32(child_node, "y-size", &button->height);
   400			fwnode_property_read_u32(child_node, "keycode",
   401						 &button->keycode);
   402			dev_info(dev,
   403				 "Adding button at x=%u y=%u size %u x %u keycode=%u\n",
   404				 button->x, button->y, button->width, button->height,
   405				 button->keycode);
   406			++i;
   407		}
   408		return map;
   409	}
   410	
 > 411	int touchscreen_buttons_idev_opened(struct input_dev *idev)
   412	{
   413		struct touchscreen_buttons *buttons;
   414	
   415		buttons = dev_get_drvdata(idev->dev.parent);
   416	
   417		mutex_lock(&buttons->mutex);
   418		if (buttons && buttons->ts_handle) {
   419			if (buttons->ts_handle->open == 0) {
   420				queue_work(buttons->workqueue, &buttons->open_task);
   421				dev_dbg(idev->dev.parent, "idev opened\n");
   422			} else {
   423				dev_info(idev->dev.parent, "idev allready opened\n");
   424			}
   425		} else {
   426			dev_warn(idev->dev.parent,
   427				 "Input device opend but touchscreen not opened. %p %p\n",
   428				 buttons, buttons->ts_handle);
   429		}
   430		mutex_unlock(&buttons->mutex);
   431		return 0;
   432	}
   433	
 > 434	void touchscreen_buttons_idev_closed(struct input_dev *idev)
   435	{
   436		struct touchscreen_buttons *buttons;
   437	
   438		buttons = dev_get_drvdata(idev->dev.parent);
   439	
   440		mutex_lock(&buttons->mutex);
   441		if (buttons && buttons->ts_handle && buttons->ts_handle->open != 0) {
   442			queue_work(buttons->workqueue, &buttons->close_task);
   443			dev_dbg(idev->dev.parent, "idev closed\n");
   444		}
   445		mutex_unlock(&buttons->mutex);
   446	}
   447	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
diff mbox series

Patch

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 362e8a01980c..3d487b8e7d8b 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -318,6 +318,14 @@  config INPUT_CPCAP_PWRBUTTON
 	  To compile this driver as a module, choose M here. The module will
 	  be called cpcap-pwrbutton.
 
+config INPUT_TOUCHSCREEN_BUTTONS
+	tristate "Touchscreen Buttons"
+	help
+	  Say Y here if you want to enable buttons on touchscreen devices.
+
+	  To compile this driver as a module, choose M here. The module will
+	  be called touchscreen-buttons.
+
 config INPUT_WISTRON_BTNS
 	tristate "x86 Wistron laptop button interface"
 	depends on X86_32
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index a48e5f2d859d..18032a1b2a9c 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -84,4 +84,5 @@  obj-$(CONFIG_INPUT_WM831X_ON)		+= wm831x-on.o
 obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND)	+= xen-kbdfront.o
 obj-$(CONFIG_INPUT_YEALINK)		+= yealink.o
 obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR)	+= ideapad_slidebar.o
+obj-$(CONFIG_INPUT_TOUCHSCREEN_BUTTONS)	+= touchscreen-buttons.o
 
diff --git a/drivers/input/misc/touchscreen-buttons.c b/drivers/input/misc/touchscreen-buttons.c
new file mode 100644
index 000000000000..3b1b630b4e66
--- /dev/null
+++ b/drivers/input/misc/touchscreen-buttons.c
@@ -0,0 +1,593 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+/**
+ * Touchscreen Virutal Button Input Driver
+ *
+ * Copyright (C) 2020 Carl Klemm <carl@uvos.xyz>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/kernel.h>
+#include <linux/limits.h>
+#include <linux/input/mt.h>
+#include <linux/device/bus.h>
+#include <linux/string.h>
+#include <linux/workqueue.h>
+
+#define EVENT_QUEUE_SIZE 32
+
+struct touchscreen_button {
+	u32 x;
+	u32 y;
+	u32 width;
+	u32 height;
+	u32 keycode;
+	u8 depressed;
+};
+
+struct touchscreen_button_map {
+	struct touchscreen_button *buttons;
+	u32 count;
+	struct device_node *ts_node;
+};
+
+struct event {
+	unsigned int type;
+	unsigned int code;
+	int value;
+};
+
+struct event_queue {
+	struct event events[EVENT_QUEUE_SIZE];
+	unsigned int lastindex;
+};
+
+struct touchscreen_buttons {
+	struct device *dev;
+	struct input_dev *buttons_idev;
+	struct input_dev *filtered_ts_idev;
+	struct touchscreen_button_map *map;
+	struct input_handler *handler;
+	struct input_handle *ts_handle;
+	struct event_queue queue;
+	struct workqueue_struct *workqueue;
+	struct work_struct open_task;
+	struct work_struct close_task;
+	struct work_struct merge_task;
+	struct mutex mutex;
+};
+
+static const struct input_device_id touchscreen_buttons_ids[] = {
+	{
+	 .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
+	 .evbit = {BIT_MASK(EV_ABS)},
+	 },
+	{
+	 .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
+	 .evbit = {BIT_MASK(EV_KEY)},
+	 },
+	{
+	 .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
+	 .evbit = {BIT_MASK(EV_SYN)},
+	 },
+	{},
+};
+
+static int touchscreen_buttons_process_syn(const struct event_queue *queue,
+					   const struct touchscreen_button_map
+					   *map, struct input_dev *idev)
+{
+	u32 i;
+	int x, y, ret, pressed;
+
+	x = INT_MIN;
+	y = INT_MIN;
+	pressed = -1;
+	ret = 0;
+
+	for (i = 0; i < queue->lastindex; ++i) {
+		const struct event *ev;
+
+		ev = &queue->events[i];
+		if (ev->type == EV_ABS && ev->code == ABS_X)
+			x = ev->value;
+		else if (ev->type == EV_ABS && ev->code == ABS_Y)
+			y = ev->value;
+		else if (ev->type == EV_KEY && ev->code == BTN_TOUCH)
+			pressed = ev->value;
+	}
+
+	for (i = 0; i < map->count; ++i) {
+		struct touchscreen_button *button = &map->buttons[i];
+
+		if (pressed == 1 &&
+		    button->x <= x &&
+		    button->y <= y &&
+		    button->width + button->x >= x &&
+		    button->height + button->y >= y && button->depressed == 0) {
+			input_report_key(idev, button->keycode, 1);
+			button->depressed = 1;
+			ret = 1;
+		} else if (button->depressed == 1) {
+			if (pressed == 0) {
+				input_report_key(idev, button->keycode, 0);
+				button->depressed = 0;
+			}
+			ret = 2;
+		}
+	}
+
+	if (ret != 0) {
+		input_event(idev, EV_SYN, SYN_REPORT, 0);
+	} else if (ret == 0) {
+		bool buttonpressed = false;
+
+		for (i = 0; i < map->count; ++i)
+			buttonpressed = buttonpressed
+			    || map->buttons[i].depressed;
+		if (buttonpressed)
+			ret = 3;
+	}
+
+	return ret;
+}
+
+static void touchscreen_buttons_resend_events(const struct event_queue *queue,
+					      struct input_dev *idev)
+{
+	u32 i;
+
+	for (i = 0; i < queue->lastindex; ++i)
+		input_event(idev, queue->events[i].type, queue->events[i].code,
+			    queue->events[i].value);
+	input_event(idev, EV_SYN, SYN_REPORT, 0);
+}
+
+static void touchscreen_buttons_copy_mt_slots(struct input_dev *target,
+					      struct input_dev *source)
+{
+	if (source->mt && target->mt
+	    && source->mt->num_slots == target->mt->num_slots) {
+		memcpy(target->mt->slots, source->mt->slots,
+		       sizeof(struct input_mt_slot) * source->mt->num_slots);
+	}
+}
+
+static void touchscreen_buttons_input_event(struct input_handle *handle,
+					    unsigned int type,
+					    unsigned int code, int value)
+{
+	struct touchscreen_buttons *buttons;
+
+	buttons = handle->private;
+
+	if (type == EV_SYN && code == SYN_REPORT) {
+		if (touchscreen_buttons_process_syn(&buttons->queue,
+						    buttons->map,
+						    buttons->buttons_idev) == 0)
+			touchscreen_buttons_resend_events(&buttons->queue,
+							  buttons->filtered_ts_idev);
+		buttons->queue.lastindex = 0;
+	} else if (buttons->queue.lastindex < EVENT_QUEUE_SIZE
+		   && buttons->queue.lastindex >= 0) {
+		buttons->queue.events[buttons->queue.lastindex].type = type;
+		buttons->queue.events[buttons->queue.lastindex].code = code;
+		buttons->queue.events[buttons->queue.lastindex].value = value;
+		++buttons->queue.lastindex;
+	} else {
+		dev_warn(buttons->dev,
+			 "event_qeue overrun, will not capture events until next SYN_REPORT\n");
+	}
+}
+
+static void touchscreen_buttons_merge_capabilitys(struct input_dev *target,
+						  struct input_dev *source)
+{
+	unsigned int i;
+
+	mutex_lock(&target->mutex);
+	mutex_lock(&source->mutex);
+	for (i = 0; i < BITS_TO_LONGS(INPUT_PROP_CNT); ++i)
+		target->propbit[i] = target->propbit[i] | source->propbit[i];
+	for (i = 0; i < BITS_TO_LONGS(EV_CNT); ++i)
+		target->evbit[i] = target->evbit[i] | source->evbit[i];
+	for (i = 0; i < BITS_TO_LONGS(KEY_CNT); ++i)
+		target->keybit[i] = target->keybit[i] | source->keybit[i];
+	for (i = 0; i < BITS_TO_LONGS(REL_CNT); ++i)
+		target->relbit[i] = target->relbit[i] | source->relbit[i];
+	for (i = 0; i < BITS_TO_LONGS(ABS_CNT); ++i)
+		target->absbit[i] = target->absbit[i] | source->absbit[i];
+	for (i = 0; i < BITS_TO_LONGS(MSC_CNT); ++i)
+		target->mscbit[i] = target->mscbit[i] | source->mscbit[i];
+	for (i = 0; i < BITS_TO_LONGS(LED_CNT); ++i)
+		target->ledbit[i] = target->ledbit[i] | source->ledbit[i];
+	for (i = 0; i < BITS_TO_LONGS(SND_CNT); ++i)
+		target->sndbit[i] = target->sndbit[i] | source->sndbit[i];
+	for (i = 0; i < BITS_TO_LONGS(FF_CNT); ++i)
+		target->ffbit[i] = target->ffbit[i] | source->ffbit[i];
+	for (i = 0; i < BITS_TO_LONGS(SW_CNT); ++i)
+		target->swbit[i] = target->swbit[i] | source->swbit[i];
+
+	if (*source->evbit & (1 << EV_ABS)) {
+		input_alloc_absinfo(target);
+		for (i = 0; i < ABS_CNT; ++i)
+			target->absinfo[i] = source->absinfo[i];
+		if (source->mt) {
+			input_mt_init_slots(target, source->mt->num_slots,
+					    source->mt->flags);
+			touchscreen_buttons_copy_mt_slots(target, source);
+		}
+	}
+	mutex_unlock(&source->mutex);
+	mutex_unlock(&target->mutex);
+}
+
+void merge_task_handler(struct work_struct *work)
+{
+	struct touchscreen_buttons *buttons =
+	    container_of(work, struct touchscreen_buttons, merge_task);
+
+	mutex_lock(&buttons->mutex);
+	if (buttons->ts_handle && buttons->ts_handle->dev)
+		touchscreen_buttons_merge_capabilitys(buttons->filtered_ts_idev,
+						      buttons->ts_handle->dev);
+	mutex_unlock(&buttons->mutex);
+}
+
+void close_task_handler(struct work_struct *work)
+{
+	struct touchscreen_buttons *buttons =
+	    container_of(work, struct touchscreen_buttons, close_task);
+
+	mutex_lock(&buttons->mutex);
+	if (buttons && buttons->ts_handle && buttons->ts_handle->open != 0)
+		input_close_device(buttons->ts_handle);
+	mutex_unlock(&buttons->mutex);
+}
+
+void open_task_handler(struct work_struct *work)
+{
+	struct touchscreen_buttons *buttons =
+	    container_of(work, struct touchscreen_buttons, open_task);
+	int error;
+
+	mutex_lock(&buttons->mutex);
+	if (buttons && buttons->ts_handle) {
+		error = input_open_device(buttons->ts_handle);
+		if (error) {
+			dev_err(buttons->dev,
+				"Failed to open input device, error %d\n",
+				error);
+			input_unregister_handle(buttons->ts_handle);
+			kfree(buttons->ts_handle);
+			buttons->ts_handle = NULL;
+		}
+	}
+	mutex_unlock(&buttons->mutex);
+}
+
+static int touchscreen_buttons_input_connect(struct input_handler *handler,
+					     struct input_dev *dev,
+					     const struct input_device_id *id)
+{
+	struct touchscreen_buttons *buttons;
+
+	buttons = handler->private;
+
+	mutex_lock(&buttons->mutex);
+
+	if ((!buttons->ts_handle
+	     && device_match_of_node(&dev->dev, buttons->map->ts_node))
+	    || (dev->dev.parent
+		&& device_match_of_node(dev->dev.parent,
+					buttons->map->ts_node))) {
+		int error;
+
+		dev_info(buttons->dev, "Binding to device: %s\n",
+			 dev_name(&dev->dev));
+
+		buttons->ts_handle =
+		    kzalloc(sizeof(*buttons->ts_handle), GFP_KERNEL);
+		if (!buttons->ts_handle) {
+			mutex_unlock(&buttons->mutex);
+			return -ENOMEM;
+		}
+
+		buttons->ts_handle->dev = dev;
+		buttons->ts_handle->handler = handler;
+		buttons->ts_handle->name = "touchscreen-buttons";
+		buttons->ts_handle->private = handler->private;
+		buttons->queue.lastindex = 0;
+
+		error = input_register_handle(buttons->ts_handle);
+		if (error) {
+			dev_err(buttons->dev,
+				"Failed to register input handler, error %d\n",
+				error);
+			kfree(buttons->ts_handle);
+			buttons->ts_handle = NULL;
+			mutex_unlock(&buttons->mutex);
+			return error;
+		}
+
+		queue_work(buttons->workqueue, &buttons->merge_task);
+
+		if (buttons->filtered_ts_idev->users > 0
+		    && buttons->ts_handle->open == 0)
+			queue_work(buttons->workqueue, &buttons->open_task);
+	}
+
+	mutex_unlock(&buttons->mutex);
+	return 0;
+}
+
+static void touchscreen_buttons_input_disconnect(struct input_handle *handle)
+{
+	struct touchscreen_buttons *buttons;
+
+	buttons = handle->private;
+
+	mutex_lock(&buttons->mutex);
+	if (handle == buttons->ts_handle) {
+		input_close_device(handle);
+		input_unregister_handle(handle);
+		kfree(handle);
+		buttons->ts_handle = NULL;
+		dev_info(buttons->dev,
+			 "Touchscreen device disconnected buttons disabled\n");
+	} else {
+		dev_err(buttons->dev,
+			"Unknown device disconnected, %p should be %p", handle,
+			buttons->ts_handle);
+	}
+	mutex_unlock(&buttons->mutex);
+}
+
+static struct touchscreen_button_map
+*touchscreen_buttons_get_devtree_pdata(struct device *dev)
+{
+	struct touchscreen_button_map *map;
+	struct fwnode_handle *child_node;
+	struct device_node *node;
+	int i;
+
+	map = kzalloc(sizeof(*map), GFP_KERNEL);
+	if (!map)
+		return ERR_PTR(-ENOMEM);
+
+	map->count = device_get_child_node_count(dev);
+	if (map->count == 0)
+		return ERR_PTR(-ENODEV);
+
+	map->buttons = kcalloc(map->count, sizeof(*map->buttons), GFP_KERNEL);
+	if (!map->buttons)
+		return ERR_PTR(-ENOMEM);
+
+	node = dev->of_node;
+	map->ts_node = of_parse_phandle(node, "touchscreen_phandle", 0);
+	if (!map->ts_node) {
+		dev_err(dev, "touchscreen_phandle node missing\n");
+		return ERR_PTR(-ENODEV);
+	}
+
+	dev_info(dev, "Device_node name: %s\n", map->ts_node->name);
+
+	i = 0;
+	device_for_each_child_node(dev, child_node) {
+		struct touchscreen_button *button;
+
+		button = &map->buttons[i];
+
+		fwnode_property_read_u32(child_node, "x-position", &button->x);
+		fwnode_property_read_u32(child_node, "y-position", &button->y);
+		fwnode_property_read_u32(child_node, "x-size", &button->width);
+		fwnode_property_read_u32(child_node, "y-size", &button->height);
+		fwnode_property_read_u32(child_node, "keycode",
+					 &button->keycode);
+		dev_info(dev,
+			 "Adding button at x=%u y=%u size %u x %u keycode=%u\n",
+			 button->x, button->y, button->width, button->height,
+			 button->keycode);
+		++i;
+	}
+	return map;
+}
+
+int touchscreen_buttons_idev_opened(struct input_dev *idev)
+{
+	struct touchscreen_buttons *buttons;
+
+	buttons = dev_get_drvdata(idev->dev.parent);
+
+	mutex_lock(&buttons->mutex);
+	if (buttons && buttons->ts_handle) {
+		if (buttons->ts_handle->open == 0) {
+			queue_work(buttons->workqueue, &buttons->open_task);
+			dev_dbg(idev->dev.parent, "idev opened\n");
+		} else {
+			dev_info(idev->dev.parent, "idev allready opened\n");
+		}
+	} else {
+		dev_warn(idev->dev.parent,
+			 "Input device opend but touchscreen not opened. %p %p\n",
+			 buttons, buttons->ts_handle);
+	}
+	mutex_unlock(&buttons->mutex);
+	return 0;
+}
+
+void touchscreen_buttons_idev_closed(struct input_dev *idev)
+{
+	struct touchscreen_buttons *buttons;
+
+	buttons = dev_get_drvdata(idev->dev.parent);
+
+	mutex_lock(&buttons->mutex);
+	if (buttons && buttons->ts_handle && buttons->ts_handle->open != 0) {
+		queue_work(buttons->workqueue, &buttons->close_task);
+		dev_dbg(idev->dev.parent, "idev closed\n");
+	}
+	mutex_unlock(&buttons->mutex);
+}
+
+static int touchscreen_buttons_probe(struct platform_device *pdev)
+{
+	struct touchscreen_buttons *buttons;
+	int error, i;
+
+	buttons = kzalloc(sizeof(*buttons), GFP_KERNEL);
+	if (!buttons)
+		return -ENOMEM;
+
+	dev_set_drvdata(&pdev->dev, buttons);
+
+	buttons->workqueue =
+	    create_singlethread_workqueue("touchscreen-buttons-workqueue");
+	INIT_WORK(&buttons->merge_task, merge_task_handler);
+	INIT_WORK(&buttons->open_task, open_task_handler);
+	INIT_WORK(&buttons->close_task, close_task_handler);
+
+	mutex_init(&buttons->mutex);
+
+	buttons->queue.lastindex = 0;
+	buttons->dev = &pdev->dev;
+
+	buttons->map = touchscreen_buttons_get_devtree_pdata(&pdev->dev);
+	if (IS_ERR(buttons->map))
+		return PTR_ERR(buttons->map);
+
+	/*filtered touchscreen device */
+	buttons->filtered_ts_idev = input_allocate_device();
+	if (!buttons->filtered_ts_idev)
+		return -ENOMEM;
+	buttons->filtered_ts_idev->name = "Filtered Touchscreen";
+	buttons->filtered_ts_idev->phys = "touchscreen-buttons/input1";
+	buttons->filtered_ts_idev->dev.parent = buttons->dev;
+	buttons->filtered_ts_idev->open = touchscreen_buttons_idev_opened;
+	buttons->filtered_ts_idev->close = touchscreen_buttons_idev_closed;
+
+	/*buttons input device */
+	buttons->buttons_idev = input_allocate_device();
+	if (!buttons->buttons_idev)
+		return -ENOMEM;
+	buttons->buttons_idev->name = "Touchscreen Buttons";
+	buttons->buttons_idev->phys = "touchscreen-buttons/input0";
+	buttons->buttons_idev->dev.parent = buttons->dev;
+	for (i = 0; i < buttons->map->count; ++i)
+		input_set_capability(buttons->buttons_idev, EV_KEY,
+				     buttons->map->buttons[i].keycode);
+
+	/*handler for touchscreen input device */
+	buttons->handler = kzalloc(sizeof(*buttons->handler), GFP_KERNEL);
+
+	buttons->handler->event = touchscreen_buttons_input_event;
+	buttons->handler->connect = touchscreen_buttons_input_connect;
+	buttons->handler->disconnect = touchscreen_buttons_input_disconnect;
+	buttons->handler->name = "touchscreen-buttons";
+	buttons->handler->id_table = touchscreen_buttons_ids;
+	buttons->handler->private = buttons;
+
+	error = input_register_handler(buttons->handler);
+	if (error) {
+		dev_err(&pdev->dev, "Input handler register failed: %d\n",
+			error);
+		return error;
+	}
+
+	error = input_register_device(buttons->buttons_idev);
+	if (error) {
+		dev_err(&pdev->dev, "Input device register failed: %d\n",
+			error);
+		return error;
+	}
+
+	error = input_register_device(buttons->filtered_ts_idev);
+	if (error) {
+		dev_err(&pdev->dev, "Input device register failed: %d\n",
+			error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int touchscreen_buttons_remove(struct platform_device *pdev)
+{
+	struct touchscreen_buttons *buttons;
+	struct input_handle *ts_handle;
+
+	buttons = dev_get_drvdata(&pdev->dev);
+
+	mutex_lock(&buttons->mutex);
+
+	ts_handle = buttons->ts_handle;
+
+	input_unregister_handler(buttons->handler);
+	if (buttons->ts_handle) {
+		if (buttons->ts_handle->open != 0)
+			input_close_device(buttons->ts_handle);
+		input_unregister_handle(buttons->ts_handle);
+		buttons->ts_handle = NULL;
+	}
+
+	mutex_unlock(&buttons->mutex);
+
+	flush_workqueue(buttons->workqueue);
+	destroy_workqueue(buttons->workqueue);
+
+	input_unregister_device(buttons->buttons_idev);
+	input_unregister_device(buttons->filtered_ts_idev);
+
+	kfree(ts_handle);
+
+	if (buttons->map) {
+		kfree(buttons->map->buttons);
+		kfree(buttons->map);
+	}
+	kfree(buttons->handler);
+
+	kfree(buttons);
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id touchscreen_buttons_dt_match_table[] = {
+	{.compatible = "touchscreen-buttons"},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, touchscreen_buttons_dt_match_table);
+#endif
+
+static struct platform_driver touchscreen_buttons_driver = {
+	.probe = touchscreen_buttons_probe,
+	.remove = touchscreen_buttons_remove,
+	.driver = {
+		   .name = "touchscreen-buttons",
+		   .of_match_table =
+		   of_match_ptr(touchscreen_buttons_dt_match_table),
+		   },
+};
+
+module_platform_driver(touchscreen_buttons_driver);
+
+MODULE_ALIAS("platform:touchscreen-buttons");
+MODULE_DESCRIPTION("touchscreen-buttons");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Carl Klemm <carl@uvos.xyz>");