All of lore.kernel.org
 help / color / mirror / Atom feed
From: David Herrmann <dh.herrmann@gmail.com>
To: linux-input@vger.kernel.org
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>,
	Jiri Kosina <jkosina@suse.cz>,
	Benjamin Tissoires <benjamin.tissoires@gmail.com>,
	Peter Hutterer <peter.hutterer@who-t.net>,
	Antonio Ospite <ospite@studenti.unina.it>,
	linux-kernel@vger.kernel.org, input-tools@lists.freedesktop.org,
	David Herrmann <dh.herrmann@gmail.com>
Subject: [PATCH 2/4] Input: introduce ABS_MAX2/CNT2 and friends
Date: Tue, 17 Dec 2013 16:48:52 +0100	[thread overview]
Message-ID: <1387295334-1744-3-git-send-email-dh.herrmann@gmail.com> (raw)
In-Reply-To: <1387295334-1744-1-git-send-email-dh.herrmann@gmail.com>

As we painfully noticed during the 3.12 merge-window our
EVIOCGABS/EVIOCSABS API is limited to ABS_MAX<=0x3f. We tried several
hacks to work around it but if we ever decide to increase ABS_MAX, the
EVIOCSABS ioctl ABI might overflow into the next byte causing horrible
misinterpretations in the kernel that we cannot catch.

Therefore, we decided to go with ABS_MAX2/CNT2 and introduce two new
ioctls to get/set abs-params. They no longer encode the ABS code in the
ioctl number and thus allow up to 4 billion ABS codes.

The new API also allows to query multiple ABS values with one call. To
allow EVIOCSABS2(code = 0, cnt = ABS_CNT2) we need to silently ignore
writes to ABS_MT_SLOT. Furthermore, for better compatibility with
newer user-space, we ignore writes to unknown codes. Hence, if we ever
increase ABS_MAX2, new user-space will work with code=0,cnt=ABS_CNT2 just
fine even on old kernels.

Note that we also need to increase EV_VERSION so user-space can reliably
know whether ABS2 is supported. Unfortunately, we return EINVAL instead of
ENOSYS for unknown evdev ioctls so it's nearly impossible to catch
reliably without EVIOCGVERSION.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/hid/hid-debug.c                  |  2 +-
 drivers/hid/hid-input.c                  |  2 +-
 drivers/input/evdev.c                    | 95 +++++++++++++++++++++++++++++++-
 drivers/input/input.c                    | 14 ++---
 drivers/input/keyboard/goldfish_events.c |  6 +-
 drivers/input/keyboard/hil_kbd.c         |  2 +-
 drivers/input/misc/uinput.c              |  6 +-
 include/linux/hid.h                      |  2 +-
 include/linux/input.h                    |  6 +-
 include/uapi/linux/input.h               | 42 +++++++++++++-
 include/uapi/linux/uinput.h              |  2 +-
 11 files changed, 155 insertions(+), 24 deletions(-)

diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
index 8453214..d32fa30 100644
--- a/drivers/hid/hid-debug.c
+++ b/drivers/hid/hid-debug.c
@@ -862,7 +862,7 @@ static const char *relatives[REL_MAX + 1] = {
 	[REL_WHEEL] = "Wheel",		[REL_MISC] = "Misc",
 };
 
-static const char *absolutes[ABS_CNT] = {
+static const char *absolutes[ABS_CNT2] = {
 	[ABS_X] = "X",			[ABS_Y] = "Y",
 	[ABS_Z] = "Z",			[ABS_RX] = "Rx",
 	[ABS_RY] = "Ry",		[ABS_RZ] = "Rz",
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index d97f232..a02721c 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -1300,7 +1300,7 @@ static bool hidinput_has_been_populated(struct hid_input *hidinput)
 	for (i = 0; i < BITS_TO_LONGS(REL_CNT); i++)
 		r |= hidinput->input->relbit[i];
 
-	for (i = 0; i < BITS_TO_LONGS(ABS_CNT); i++)
+	for (i = 0; i < BITS_TO_LONGS(ABS_CNT2); i++)
 		r |= hidinput->input->absbit[i];
 
 	for (i = 0; i < BITS_TO_LONGS(MSC_CNT); i++)
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index a06e125..32b74e5 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -643,7 +643,7 @@ static int handle_eviocgbit(struct input_dev *dev,
 	case      0: bits = dev->evbit;  len = EV_MAX;  break;
 	case EV_KEY: bits = dev->keybit; len = KEY_MAX; break;
 	case EV_REL: bits = dev->relbit; len = REL_MAX; break;
-	case EV_ABS: bits = dev->absbit; len = ABS_MAX; break;
+	case EV_ABS: bits = dev->absbit; len = ABS_MAX2; break;
 	case EV_MSC: bits = dev->mscbit; len = MSC_MAX; break;
 	case EV_LED: bits = dev->ledbit; len = LED_MAX; break;
 	case EV_SND: bits = dev->sndbit; len = SND_MAX; break;
@@ -671,6 +671,93 @@ static int handle_eviocgbit(struct input_dev *dev,
 }
 #undef OLD_KEY_MAX
 
+static int evdev_handle_get_abs2(struct input_dev *dev, void __user *p)
+{
+	u32 code, cnt, valid_cnt, i;
+	struct input_absinfo2 __user *pinfo = p;
+	struct input_absinfo abs;
+
+	if (copy_from_user(&code, &pinfo->code, sizeof(code)))
+		return -EFAULT;
+	if (copy_from_user(&cnt, &pinfo->cnt, sizeof(cnt)))
+		return -EFAULT;
+	if (!cnt)
+		return 0;
+
+	if (!dev->absinfo)
+		valid_cnt = 0;
+	else if (code > ABS_MAX2)
+		valid_cnt = 0;
+	else if (code + cnt <= code || code + cnt > ABS_MAX2)
+		valid_cnt = ABS_MAX2 - code + 1;
+	else
+		valid_cnt = cnt;
+
+	for (i = 0; i < valid_cnt; ++i) {
+		/*
+		 * Take event lock to ensure that we are not
+		 * copying data while EVIOCSABS2 changes it.
+		 * Might be inconsistent, otherwise.
+		 */
+		spin_lock_irq(&dev->event_lock);
+		abs = dev->absinfo[code + i];
+		spin_unlock_irq(&dev->event_lock);
+
+		if (copy_to_user(&pinfo->info[i], &abs, sizeof(abs)))
+			return -EFAULT;
+	}
+
+	memset(&abs, 0, sizeof(abs));
+	for (i = valid_cnt; i < cnt; ++i)
+		if (copy_to_user(&pinfo->info[i], &abs, sizeof(abs)))
+			return -EFAULT;
+
+	return 0;
+}
+
+static int evdev_handle_set_abs2(struct input_dev *dev, void __user *p)
+{
+	struct input_absinfo2 __user *pinfo = p;
+	struct input_absinfo *abs;
+	u32 code, cnt, i;
+	size_t size;
+
+	if (!dev->absinfo)
+		return 0;
+	if (copy_from_user(&code, &pinfo->code, sizeof(code)))
+		return -EFAULT;
+	if (copy_from_user(&cnt, &pinfo->cnt, sizeof(cnt)))
+		return -EFAULT;
+	if (!cnt || code > ABS_MAX2)
+		return 0;
+
+	if (code + cnt <= code || code + cnt > ABS_MAX2)
+		cnt = ABS_MAX2 - code + 1;
+
+	size = cnt * sizeof(*abs);
+	abs = memdup_user(pinfo->info, size);
+	if (IS_ERR(abs))
+		return PTR_ERR(abs);
+
+	/*
+	 * Take event lock to ensure that we are not
+	 * changing device parameters in the middle
+	 * of event.
+	 */
+	spin_lock_irq(&dev->event_lock);
+	for (i = 0; i < cnt; ++i) {
+		/* silently drop ABS_MT_SLOT */
+		if (code + i == ABS_MT_SLOT)
+			continue;
+
+		dev->absinfo[code + i] = abs[i];
+	}
+	spin_unlock_irq(&dev->event_lock);
+
+	kfree(abs);
+	return 0;
+}
+
 static int evdev_handle_get_keycode(struct input_dev *dev, void __user *p)
 {
 	struct input_keymap_entry ke = {
@@ -898,6 +985,12 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
 		client->clkid = i;
 		return 0;
 
+	case EVIOCGABS2:
+		return evdev_handle_get_abs2(dev, p);
+
+	case EVIOCSABS2:
+		return evdev_handle_set_abs2(dev, p);
+
 	case EVIOCGKEYCODE:
 		return evdev_handle_get_keycode(dev, p);
 
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 846ccdd..042157c 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -305,7 +305,7 @@ static int input_get_disposition(struct input_dev *dev,
 		break;
 
 	case EV_ABS:
-		if (is_event_supported(code, dev->absbit, ABS_MAX))
+		if (is_event_supported(code, dev->absbit, ABS_MAX2))
 			disposition = input_handle_abs_event(dev, code, &value);
 
 		break;
@@ -474,7 +474,7 @@ EXPORT_SYMBOL(input_inject_event);
 void input_alloc_absinfo(struct input_dev *dev)
 {
 	if (!dev->absinfo)
-		dev->absinfo = kcalloc(ABS_CNT, sizeof(struct input_absinfo),
+		dev->absinfo = kcalloc(ABS_CNT2, sizeof(struct input_absinfo),
 					GFP_KERNEL);
 
 	WARN(!dev->absinfo, "%s(): kcalloc() failed?\n", __func__);
@@ -954,7 +954,7 @@ static const struct input_device_id *input_match_device(struct input_handler *ha
 		if (!bitmap_subset(id->relbit, dev->relbit, REL_MAX))
 			continue;
 
-		if (!bitmap_subset(id->absbit, dev->absbit, ABS_MAX))
+		if (!bitmap_subset(id->absbit, dev->absbit, ABS_MAX2))
 			continue;
 
 		if (!bitmap_subset(id->mscbit, dev->mscbit, MSC_MAX))
@@ -1147,7 +1147,7 @@ static int input_devices_seq_show(struct seq_file *seq, void *v)
 	if (test_bit(EV_REL, dev->evbit))
 		input_seq_print_bitmap(seq, "REL", dev->relbit, REL_MAX);
 	if (test_bit(EV_ABS, dev->evbit))
-		input_seq_print_bitmap(seq, "ABS", dev->absbit, ABS_MAX);
+		input_seq_print_bitmap(seq, "ABS", dev->absbit, ABS_MAX2);
 	if (test_bit(EV_MSC, dev->evbit))
 		input_seq_print_bitmap(seq, "MSC", dev->mscbit, MSC_MAX);
 	if (test_bit(EV_LED, dev->evbit))
@@ -1333,7 +1333,7 @@ static int input_print_modalias(char *buf, int size, struct input_dev *id,
 	len += input_print_modalias_bits(buf + len, size - len,
 				'r', id->relbit, 0, REL_MAX);
 	len += input_print_modalias_bits(buf + len, size - len,
-				'a', id->absbit, 0, ABS_MAX);
+				'a', id->absbit, 0, ABS_MAX2);
 	len += input_print_modalias_bits(buf + len, size - len,
 				'm', id->mscbit, 0, MSC_MAX);
 	len += input_print_modalias_bits(buf + len, size - len,
@@ -1592,7 +1592,7 @@ static int input_dev_uevent(struct device *device, struct kobj_uevent_env *env)
 	if (test_bit(EV_REL, dev->evbit))
 		INPUT_ADD_HOTPLUG_BM_VAR("REL=", dev->relbit, REL_MAX);
 	if (test_bit(EV_ABS, dev->evbit))
-		INPUT_ADD_HOTPLUG_BM_VAR("ABS=", dev->absbit, ABS_MAX);
+		INPUT_ADD_HOTPLUG_BM_VAR("ABS=", dev->absbit, ABS_MAX2);
 	if (test_bit(EV_MSC, dev->evbit))
 		INPUT_ADD_HOTPLUG_BM_VAR("MSC=", dev->mscbit, MSC_MAX);
 	if (test_bit(EV_LED, dev->evbit))
@@ -1929,7 +1929,7 @@ static unsigned int input_estimate_events_per_packet(struct input_dev *dev)
 
 	events = mt_slots + 1; /* count SYN_MT_REPORT and SYN_REPORT */
 
-	for (i = 0; i < ABS_CNT; i++) {
+	for (i = 0; i < ABS_CNT2; i++) {
 		if (test_bit(i, dev->absbit)) {
 			if (input_is_mt_axis(i))
 				events += mt_slots;
diff --git a/drivers/input/keyboard/goldfish_events.c b/drivers/input/keyboard/goldfish_events.c
index 9f60a2e..9999cea 100644
--- a/drivers/input/keyboard/goldfish_events.c
+++ b/drivers/input/keyboard/goldfish_events.c
@@ -90,8 +90,8 @@ static void events_import_abs_params(struct event_dev *edev)
 	__raw_writel(PAGE_ABSDATA, addr + REG_SET_PAGE);
 
 	count = __raw_readl(addr + REG_LEN) / sizeof(val);
-	if (count > ABS_MAX)
-		count = ABS_MAX;
+	if (count > ABS_MAX2)
+		count = ABS_MAX2;
 
 	for (i = 0; i < count; i++) {
 		if (!test_bit(i, input_dev->absbit))
@@ -158,7 +158,7 @@ static int events_probe(struct platform_device *pdev)
 	events_import_bits(edev, input_dev->evbit, EV_SYN, EV_MAX);
 	events_import_bits(edev, input_dev->keybit, EV_KEY, KEY_MAX);
 	events_import_bits(edev, input_dev->relbit, EV_REL, REL_MAX);
-	events_import_bits(edev, input_dev->absbit, EV_ABS, ABS_MAX);
+	events_import_bits(edev, input_dev->absbit, EV_ABS, ABS_MAX2);
 	events_import_bits(edev, input_dev->mscbit, EV_MSC, MSC_MAX);
 	events_import_bits(edev, input_dev->ledbit, EV_LED, LED_MAX);
 	events_import_bits(edev, input_dev->sndbit, EV_SND, SND_MAX);
diff --git a/drivers/input/keyboard/hil_kbd.c b/drivers/input/keyboard/hil_kbd.c
index 589e3c2..4e4e010 100644
--- a/drivers/input/keyboard/hil_kbd.c
+++ b/drivers/input/keyboard/hil_kbd.c
@@ -387,7 +387,7 @@ static void hil_dev_pointer_setup(struct hil_dev *ptr)
 					0, HIL_IDD_AXIS_MAX(idd, i - 3), 0, 0);
 
 #ifdef TABLET_AUTOADJUST
-		for (i = 0; i < ABS_MAX; i++) {
+		for (i = 0; i < ABS_MAX2; i++) {
 			int diff = input_abs_get_max(input_dev, ABS_X + i) / 10;
 			input_abs_set_min(input_dev, ABS_X + i,
 				input_abs_get_min(input_dev, ABS_X + i) + diff);
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
index 927ad9a..4660ed1 100644
--- a/drivers/input/misc/uinput.c
+++ b/drivers/input/misc/uinput.c
@@ -311,7 +311,7 @@ static int uinput_validate_absbits(struct input_dev *dev)
 	unsigned int cnt;
 	int retval = 0;
 
-	for (cnt = 0; cnt < ABS_CNT; cnt++) {
+	for (cnt = 0; cnt < ABS_CNT2; cnt++) {
 		int min, max;
 		if (!test_bit(cnt, dev->absbit))
 			continue;
@@ -474,7 +474,7 @@ static int uinput_setup_device2(struct uinput_device *udev,
 		return -EINVAL;
 
 	/* rough check to avoid huge kernel space allocations */
-	max = ABS_CNT * sizeof(*user_dev2->abs) + sizeof(*user_dev2);
+	max = ABS_CNT2 * sizeof(*user_dev2->abs) + sizeof(*user_dev2);
 	if (count > max)
 		return -EINVAL;
 
@@ -780,7 +780,7 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
 			break;
 
 		case UI_SET_ABSBIT:
-			retval = uinput_set_bit(arg, absbit, ABS_MAX);
+			retval = uinput_set_bit(arg, absbit, ABS_MAX2);
 			break;
 
 		case UI_SET_MSCBIT:
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 31b9d29..c21d8bb 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -828,7 +828,7 @@ static inline void hid_map_usage(struct hid_input *hidinput,
 	switch (type) {
 	case EV_ABS:
 		*bit = input->absbit;
-		*max = ABS_MAX;
+		*max = ABS_MAX2;
 		break;
 	case EV_REL:
 		*bit = input->relbit;
diff --git a/include/linux/input.h b/include/linux/input.h
index 82ce323..c6add6f 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -129,7 +129,7 @@ struct input_dev {
 	unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
 	unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
 	unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
-	unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
+	unsigned long absbit[BITS_TO_LONGS(ABS_CNT2)];
 	unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
 	unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
 	unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
@@ -210,8 +210,8 @@ struct input_dev {
 #error "REL_MAX and INPUT_DEVICE_ID_REL_MAX do not match"
 #endif
 
-#if ABS_MAX != INPUT_DEVICE_ID_ABS_MAX
-#error "ABS_MAX and INPUT_DEVICE_ID_ABS_MAX do not match"
+#if ABS_MAX2 != INPUT_DEVICE_ID_ABS_MAX
+#error "ABS_MAX2 and INPUT_DEVICE_ID_ABS_MAX do not match"
 #endif
 
 #if MSC_MAX != INPUT_DEVICE_ID_MSC_MAX
diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h
index bd24470..1856461 100644
--- a/include/uapi/linux/input.h
+++ b/include/uapi/linux/input.h
@@ -32,7 +32,7 @@ struct input_event {
  * Protocol version.
  */
 
-#define EV_VERSION		0x010001
+#define EV_VERSION		0x010002
 
 /*
  * IOCTLs (0x00 - 0x7f)
@@ -74,6 +74,30 @@ struct input_absinfo {
 };
 
 /**
+ * struct input_absinfo2 - used by EVIOC[G/S]ABS2 ioctls
+ * @code: First ABS code to query
+ * @cnt: Number of ABS codes to query starting at @code
+ * @info: #@cnt absinfo structures to get/set abs parameters for all codes
+ *
+ * This structure is used by the new EVIOC[G/S]ABS2 ioctls which
+ * do the same as the old EVIOC[G/S]ABS ioctls but avoid encoding
+ * the ABS code in the ioctl number. This allows a much wider
+ * range of ABS codes. Furthermore, it allows to query multiple codes with a
+ * single call.
+ *
+ * Note that this silently drops any requests to set ABS_MT_SLOT. Hence, it is
+ * allowed to call this with code=0 cnt=ABS_CNT2. Furthermore, retrieving
+ * invalid codes returns all 0, setting them does nothing. So you must check
+ * with EVIOCGBIT first if you want reliable results. This behavior is needed
+ * to allow forward compatibility to new ABS codes.
+ */
+struct input_absinfo2 {
+	__u32 code;
+	__u32 cnt;
+	struct input_absinfo info[1];
+};
+
+/**
  * struct input_keymap_entry - used by EVIOCGKEYCODE/EVIOCSKEYCODE ioctls
  * @scancode: scancode represented in machine-endian form.
  * @len: length of the scancode that resides in @scancode buffer.
@@ -153,6 +177,8 @@ struct input_keymap_entry {
 
 #define EVIOCGRAB		_IOW('E', 0x90, int)			/* Grab/Release device */
 #define EVIOCREVOKE		_IOW('E', 0x91, int)			/* Revoke device access */
+#define EVIOCGABS2		_IOR('E', 0x92, struct input_absinfo2)	/* get abs value/limits */
+#define EVIOCSABS2		_IOW('E', 0x93, struct input_absinfo2)	/* set abs value/limits */
 
 #define EVIOCSCLOCKID		_IOW('E', 0xa0, int)			/* Set clockid to be used for timestamps */
 
@@ -835,11 +861,23 @@ struct input_keymap_entry {
 #define ABS_MT_TOOL_X		0x3c	/* Center X tool position */
 #define ABS_MT_TOOL_Y		0x3d	/* Center Y tool position */
 
-
+/*
+ * ABS_MAX/CNT is limited to a maximum of 0x3f due to the design of EVIOCGABS
+ * and EVIOCSABS ioctls. Other kernel APIs like uinput also hardcoded it. Do
+ * not modify this value and instead use the extended ABS_MAX2/CNT2 API.
+ */
 #define ABS_MAX			0x3f
 #define ABS_CNT			(ABS_MAX+1)
 
 /*
+ * Due to API restrictions the legacy evdev API only supports ABS values up to
+ * ABS_MAX/CNT. Use the extended *ABS2 ioctls to operate on any ABS values in
+ * between ABS_MAX and ABS_MAX2.
+ */
+#define ABS_MAX2		0x3f
+#define ABS_CNT2		(ABS_MAX2+1)
+
+/*
  * Switch events
  */
 
diff --git a/include/uapi/linux/uinput.h b/include/uapi/linux/uinput.h
index c2e8710..27ee521 100644
--- a/include/uapi/linux/uinput.h
+++ b/include/uapi/linux/uinput.h
@@ -140,7 +140,7 @@ struct uinput_user_dev2 {
 	char name[UINPUT_MAX_NAME_SIZE];
 	struct input_id id;
 	__u32 ff_effects_max;
-	struct input_absinfo abs[ABS_CNT];
+	struct input_absinfo abs[ABS_CNT2];
 };
 
 #endif /* _UAPI__UINPUT_H_ */
-- 
1.8.5.1

  parent reply	other threads:[~2013-12-17 15:48 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-12-17 15:48 [PATCH 0/4] Input: ABS2 and friends David Herrmann
2013-12-17 15:48 ` [PATCH 1/4] Input: uinput: add full absinfo support David Herrmann
2013-12-18 22:27   ` Peter Hutterer
2014-01-12 19:40     ` Dmitry Torokhov
2014-01-12 19:38   ` Dmitry Torokhov
2013-12-17 15:48 ` David Herrmann [this message]
2013-12-18 14:27   ` [PATCH 2/4] Input: introduce ABS_MAX2/CNT2 and friends Antonio Ospite
2013-12-18 14:44     ` David Herrmann
2013-12-18 16:36       ` Dmitry Torokhov
2013-12-18 23:21       ` Antonio Ospite
2013-12-18 14:47   ` Dmitry Torokhov
2013-12-18 23:40   ` Peter Hutterer
2013-12-18 23:48     ` Dmitry Torokhov
2013-12-18 23:55       ` Peter Hutterer
2013-12-19  0:05         ` Dmitry Torokhov
2013-12-19  0:25           ` Peter Hutterer
2013-12-19  0:34             ` Dmitry Torokhov
2013-12-17 15:48 ` [PATCH 3/4] Input: remove ambigious gamepad comment David Herrmann
2013-12-17 15:48 ` [PATCH 4/4] Input: add motion-tracking ABS_* bits and docs David Herrmann
2013-12-18 14:29   ` Antonio Ospite
2013-12-17 16:34 ` [PATCH 0/4] Input: ABS2 and friends David Herrmann
2013-12-17 21:28 ` simon
2013-12-17 21:28   ` simon
2013-12-18  8:12   ` David Herrmann

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1387295334-1744-3-git-send-email-dh.herrmann@gmail.com \
    --to=dh.herrmann@gmail.com \
    --cc=benjamin.tissoires@gmail.com \
    --cc=dmitry.torokhov@gmail.com \
    --cc=input-tools@lists.freedesktop.org \
    --cc=jkosina@suse.cz \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=ospite@studenti.unina.it \
    --cc=peter.hutterer@who-t.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.