All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] Input filters
@ 2009-09-11  5:28 Dmitry Torokhov
  2009-09-11  5:28 ` [PATCH 1/2] Input: implement input filters Dmitry Torokhov
  2009-09-11  5:28 ` [PATCH 2/2] Input: Mac button emulation - implement as input filter Dmitry Torokhov
  0 siblings, 2 replies; 3+ messages in thread
From: Dmitry Torokhov @ 2009-09-11  5:28 UTC (permalink / raw)
  To: linux-input; +Cc: mjg

Hi,

The following 2 patches implement a limited version of input filters
which allow suppressing certain events from reaching regular input
handlers/user space.

The name inspired by the recent patch from Matthew, although
implementation and intended usage quite different - Matthew needs to
filter non-input (battery, wlan, AC adapter state) from the raw data
stream emitted by i8042 whereas input filters are supposed to filter
(and maybe replace) _input_ events, most likely without regard as to
which device emitted them.

The first (and quite possible only) user of the filter facility is
Mac mouse button emulation which user to be hacked into the keyboard
driver. The setup worked until keyboard was the sole source of key
events but now that X is switching to evdev (which is completely
ignorant of mac emulation) user space started getting the original
key presses along with the emulated buttons. Input filters solve the
issue while encapsulating everything in drivers/macintosh and
removing dependencies between mac_hid, keyboard and evdev.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH 1/2] Input: implement input filters
  2009-09-11  5:28 [PATCH 0/2] Input filters Dmitry Torokhov
@ 2009-09-11  5:28 ` Dmitry Torokhov
  2009-09-11  5:28 ` [PATCH 2/2] Input: Mac button emulation - implement as input filter Dmitry Torokhov
  1 sibling, 0 replies; 3+ messages in thread
From: Dmitry Torokhov @ 2009-09-11  5:28 UTC (permalink / raw)
  To: linux-input; +Cc: mjg

Sometimes it is desirable to suppress certain events from reaching
input handlers and thus user space. One such example is Mac mouse
button emulation code which catches certain key presses and converts
them into button clicks as if they were emitted by a virtual mouse.
The original key press events should be completely suppressed,
otherwise user space will be confused, and while keyboard driver
does it on its own evdev is blissfully unaware of this arrangement.

This patch adds notion of 'filter' to the standard input handlers,
which may flag event as filtered thus preventing it from reaching
other input handlers. Filters don't (nor will they ever) have a
notion of priority relative to each other, input core will run all
of them first and any one of them may mark event as filtered.

This patch is inspired by similar patch by Matthew Garret but the
implementation and intended usage are quite different.

Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
---

 drivers/input/input.c |   39 ++++++++++++++++++++++++++++++++-------
 include/linux/input.h |    8 ++++++++
 2 files changed, 40 insertions(+), 7 deletions(-)

diff --git a/drivers/input/input.c b/drivers/input/input.c
index 7c237e6..d7e1c48 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -83,12 +83,14 @@ static int input_defuzz_abs_event(int value, int old_val, int fuzz)
 }
 
 /*
- * Pass event through all open handles. This function is called with
+ * Pass event first through all filters and then, if event has not been
+ * filtered out, through all open handles. This function is called with
  * dev->event_lock held and interrupts disabled.
  */
 static void input_pass_event(struct input_dev *dev,
 			     unsigned int type, unsigned int code, int value)
 {
+	struct input_handler *handler;
 	struct input_handle *handle;
 
 	rcu_read_lock();
@@ -96,11 +98,25 @@ static void input_pass_event(struct input_dev *dev,
 	handle = rcu_dereference(dev->grab);
 	if (handle)
 		handle->handler->event(handle, type, code, value);
-	else
-		list_for_each_entry_rcu(handle, &dev->h_list, d_node)
-			if (handle->open)
-				handle->handler->event(handle,
-							type, code, value);
+	else {
+		bool filtered = false;
+
+		list_for_each_entry_rcu(handle, &dev->h_list, d_node) {
+			if (!handle->open)
+				continue;
+
+			handler = handle->handler;
+			if (!handler->filter) {
+				if (filtered)
+					break;
+
+				handler->event(handle, type, code, value);
+
+			} else if (handler->filter(handle, type, code, value))
+				filtered = true;
+		}
+	}
+
 	rcu_read_unlock();
 }
 
@@ -903,12 +919,15 @@ static int input_handlers_seq_show(struct seq_file *seq, void *v)
 
 	seq_printf(seq, "N: Number=%ld Name=%s",
 		   (unsigned long)seq->private, handler->name);
+	if (handler->filter)
+		seq_puts(seq, "(filter)");
 	if (handler->fops)
 		seq_printf(seq, " Minor=%d", handler->minor);
 	seq_putc(seq, '\n');
 
 	return 0;
 }
+
 static const struct seq_operations input_handlers_seq_ops = {
 	.start	= input_handlers_seq_start,
 	.next	= input_handlers_seq_next,
@@ -1588,8 +1607,14 @@ int input_register_handle(struct input_handle *handle)
 	 * which is mutually exclusive with ->disconnect()
 	 * we can't be racing with input_unregister_handle()
 	 * and so separate lock is not needed here.
+	 *
+	 * Filters go to the head of the list, normal handlers
+	 * to the tail.
 	 */
-	list_add_tail(&handle->h_node, &handler->h_list);
+	if (handler->filter)
+		list_add(&handle->h_node, &handler->h_list);
+	else
+		list_add_tail(&handle->h_node, &handler->h_list);
 
 	if (handler->start)
 		handler->start(handle);
diff --git a/include/linux/input.h b/include/linux/input.h
index 8b3bc3e..c8e11f1 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -1189,6 +1189,8 @@ struct input_handle;
  * @event: event handler. This method is being called by input core with
  *	interrupts disabled and dev->event_lock spinlock held and so
  *	it may not sleep
+ * @filter: similar to @event; separates normal event handlers from
+ *	"filters".
  * @connect: called when attaching a handler to an input device
  * @disconnect: disconnects a handler from input device
  * @start: starts handler for given handle. This function is called by
@@ -1210,6 +1212,11 @@ struct input_handle;
  * same time. All of them will get their copy of input event generated by
  * the device.
  *
+ * The very same structure is used to implement input filters. Input core
+ * allows filters to run first and will not pass event to regular handlers
+ * if any of the filters indicate that the event should be filtered (by
+ * returning %true from their filter() method).
+ *
  * Note that input core serializes calls to connect() and disconnect()
  * methods.
  */
@@ -1218,6 +1225,7 @@ struct input_handler {
 	void *private;
 
 	void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
+	bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
 	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);


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH 2/2] Input: Mac button emulation - implement as input filter
  2009-09-11  5:28 [PATCH 0/2] Input filters Dmitry Torokhov
  2009-09-11  5:28 ` [PATCH 1/2] Input: implement input filters Dmitry Torokhov
@ 2009-09-11  5:28 ` Dmitry Torokhov
  1 sibling, 0 replies; 3+ messages in thread
From: Dmitry Torokhov @ 2009-09-11  5:28 UTC (permalink / raw)
  To: linux-input; +Cc: mjg

Current implementation of Mac mouse button emulation plugs into legacy
keyboard driver, converts certain keys into button events on a separate
device and suppresses the real events from reaching tty. This worked
well enough until user space started using evdev which was completely
unaware of this arrangement and kept sending original key presses to
its users. Change the implementation to use newly added input filter
framework so that original key presses are not transmitted to any
handlers.

As a bonus remove SYSCTL dependencies from the code and use Kconfig
instead; also do not create the emulated mouse device until user
activates emulation.

Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
---

 drivers/char/keyboard.c     |    5 -
 drivers/macintosh/Kconfig   |    1 
 drivers/macintosh/mac_hid.c |  257 +++++++++++++++++++++++++++++++------------
 include/linux/kbd_kern.h    |    3 -
 4 files changed, 186 insertions(+), 80 deletions(-)

diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c
index 737be95..83a1a9d 100644
--- a/drivers/char/keyboard.c
+++ b/drivers/char/keyboard.c
@@ -1168,11 +1168,6 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw)
 
 	rep = (down == 2);
 
-#ifdef CONFIG_MAC_EMUMOUSEBTN
-	if (mac_hid_mouse_emulate_buttons(1, keycode, down))
-		return;
-#endif /* CONFIG_MAC_EMUMOUSEBTN */
-
 	if ((raw_mode = (kbd->kbdmode == VC_RAW)) && !hw_raw)
 		if (emulate_raw(vc, keycode, !down << 7))
 			if (keycode < BTN_MISC && printk_ratelimit())
diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig
index 3d90683..aa3c27e 100644
--- a/drivers/macintosh/Kconfig
+++ b/drivers/macintosh/Kconfig
@@ -172,6 +172,7 @@ config INPUT_ADBHID
 
 config MAC_EMUMOUSEBTN
 	bool "Support for mouse button 2+3 emulation"
+	depends on SYSCTL
 	select INPUT
 	help
 	  This provides generic support for emulating the 2nd and 3rd mouse
diff --git a/drivers/macintosh/mac_hid.c b/drivers/macintosh/mac_hid.c
index cc9f275..4c5d93d 100644
--- a/drivers/macintosh/mac_hid.c
+++ b/drivers/macintosh/mac_hid.c
@@ -13,17 +13,191 @@
 #include <linux/sysctl.h>
 #include <linux/input.h>
 #include <linux/module.h>
-#include <linux/kbd_kern.h>
 
-
-static struct input_dev *emumousebtn;
-static int emumousebtn_input_register(void);
 static int mouse_emulate_buttons;
 static int mouse_button2_keycode = KEY_RIGHTCTRL;	/* right control key */
 static int mouse_button3_keycode = KEY_RIGHTALT;	/* right option key */
-static int mouse_last_keycode;
 
-#if defined(CONFIG_SYSCTL)
+static struct input_dev *mac_hid_emumouse_dev;
+
+static int mac_hid_create_emumouse(void)
+{
+	static struct lock_class_key mac_hid_emumouse_dev_event_class;
+	static struct lock_class_key mac_hid_emumouse_dev_mutex_class;
+	int err;
+
+	mac_hid_emumouse_dev = input_allocate_device();
+	if (!mac_hid_emumouse_dev)
+		return -ENOMEM;
+
+	lockdep_set_class(&mac_hid_emumouse_dev->event_lock,
+			  &mac_hid_emumouse_dev_event_class);
+	lockdep_set_class(&mac_hid_emumouse_dev->mutex,
+			  &mac_hid_emumouse_dev_mutex_class);
+
+	mac_hid_emumouse_dev->name = "Macintosh mouse button emulation";
+	mac_hid_emumouse_dev->id.bustype = BUS_ADB;
+	mac_hid_emumouse_dev->id.vendor = 0x0001;
+	mac_hid_emumouse_dev->id.product = 0x0001;
+	mac_hid_emumouse_dev->id.version = 0x0100;
+
+	mac_hid_emumouse_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+	mac_hid_emumouse_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
+		BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
+	mac_hid_emumouse_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+
+	err = input_register_device(mac_hid_emumouse_dev);
+	if (err) {
+		input_free_device(mac_hid_emumouse_dev);
+		mac_hid_emumouse_dev = NULL;
+		return err;
+	}
+
+	return 0;
+}
+
+static void mac_hid_destroy_emumouse(void)
+{
+	input_unregister_device(mac_hid_emumouse_dev);
+	mac_hid_emumouse_dev = NULL;
+}
+
+static bool mac_hid_emumouse_filter(struct input_handle *handle,
+				    unsigned int type, unsigned int code,
+				    int value)
+{
+	unsigned int btn;
+
+	if (type != EV_KEY)
+		return false;
+
+	if (code == mouse_button2_keycode)
+		btn = BTN_MIDDLE;
+	else if (code == mouse_button3_keycode)
+		btn = BTN_RIGHT;
+	else
+		return false;
+
+	input_report_key(mac_hid_emumouse_dev, btn, value);
+	input_sync(mac_hid_emumouse_dev);
+
+	return true;
+}
+
+static int mac_hid_emumouse_connect(struct input_handler *handler,
+				    struct input_dev *dev,
+				    const struct input_device_id *id)
+{
+	struct input_handle *handle;
+	int error;
+
+	handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
+	if (!handle)
+		return -ENOMEM;
+
+	handle->dev = dev;
+	handle->handler = handler;
+	handle->name = "mac-btn-emul";
+
+	error = input_register_handle(handle);
+	if (error) {
+		printk(KERN_ERR
+			"mac_hid: Failed to register button emulation handle, "
+			"error %d\n", error);
+		goto err_free;
+	}
+
+	error = input_open_device(handle);
+	if (error) {
+		printk(KERN_ERR
+			"mac_hid: Failed to open input device, error %d\n",
+			error);
+		goto err_unregister;
+	}
+
+	return 0;
+
+ err_unregister:
+	input_unregister_handle(handle);
+ err_free:
+	kfree(handle);
+	return error;
+}
+
+static void mac_hid_emumouse_disconnect(struct input_handle *handle)
+{
+	input_close_device(handle);
+	input_unregister_handle(handle);
+	kfree(handle);
+}
+
+static const struct input_device_id mac_hid_emumouse_ids[] = {
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
+		.evbit = { BIT_MASK(EV_KEY) },
+	},
+	{ },
+};
+
+MODULE_DEVICE_TABLE(input, mac_hid_emumouse_ids);
+
+static struct input_handler mac_hid_emumouse_handler = {
+	.filter		= mac_hid_emumouse_filter,
+	.connect	= mac_hid_emumouse_connect,
+	.disconnect	= mac_hid_emumouse_disconnect,
+	.name		= "mac-btn-emul",
+	.id_table	= mac_hid_emumouse_ids,
+};
+
+static int mac_hid_start_emulation(void)
+{
+	int err;
+
+	err = mac_hid_create_emumouse();
+	if (err)
+		return err;
+
+	err = input_register_handler(&mac_hid_emumouse_handler);
+	if (err) {
+		mac_hid_destroy_emumouse();
+		return err;
+	}
+
+	return 0;
+}
+
+static void mac_hid_stop_emulation(void)
+{
+	mac_hid_destroy_emumouse();
+	input_unregister_handler(&mac_hid_emumouse_handler);
+}
+
+static int mac_hid_toggle_emumouse(ctl_table *table, int write,
+				   struct file *filp, void __user *buffer,
+				   size_t *lenp, loff_t *ppos)
+{
+	int *valp = table->data;
+	int old_val = *valp;
+	int rc;
+
+	rc = proc_dointvec(table, write, filp, buffer, lenp, ppos);
+
+	if (rc == 0 && write && *valp != old_val) {
+		if (*valp == 1)
+			rc = mac_hid_start_emulation();
+		else if (*valp == 0)
+			mac_hid_stop_emulation();
+		else
+			rc = -EINVAL;
+	}
+
+	/* Restore the old value in case of error */
+	if (rc)
+		*valp = old_val;
+
+	return rc;
+}
+
 /* file(s) in /proc/sys/dev/mac_hid */
 static ctl_table mac_hid_files[] = {
 	{
@@ -32,7 +206,7 @@ static ctl_table mac_hid_files[] = {
 		.data		= &mouse_emulate_buttons,
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= &proc_dointvec,
+		.proc_handler	= mac_hid_toggle_emumouse,
 	},
 	{
 		.ctl_name	= DEV_MAC_HID_MOUSE_BUTTON2_KEYCODE,
@@ -40,7 +214,7 @@ static ctl_table mac_hid_files[] = {
 		.data		= &mouse_button2_keycode,
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= &proc_dointvec,
+		.proc_handler	= proc_dointvec,
 	},
 	{
 		.ctl_name	= DEV_MAC_HID_MOUSE_BUTTON3_KEYCODE,
@@ -48,7 +222,7 @@ static ctl_table mac_hid_files[] = {
 		.data		= &mouse_button3_keycode,
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= &proc_dointvec,
+		.proc_handler	= proc_dointvec,
 	},
 	{ .ctl_name = 0 }
 };
@@ -79,73 +253,12 @@ static ctl_table mac_hid_root_dir[] = {
 
 static struct ctl_table_header *mac_hid_sysctl_header;
 
-#endif /* endif CONFIG_SYSCTL */
-
-int mac_hid_mouse_emulate_buttons(int caller, unsigned int keycode, int down)
-{
-	switch (caller) {
-	case 1:
-		/* Called from keyboard.c */
-		if (mouse_emulate_buttons
-		    && (keycode == mouse_button2_keycode
-			|| keycode == mouse_button3_keycode)) {
-			if (mouse_emulate_buttons == 1) {
-				input_report_key(emumousebtn,
-						 keycode == mouse_button2_keycode ? BTN_MIDDLE : BTN_RIGHT,
-						 down);
-				input_sync(emumousebtn);
-				return 1;
-			}
-			mouse_last_keycode = down ? keycode : 0;
-		}
-		break;
-	}
-	return 0;
-}
-
-static struct lock_class_key emumousebtn_event_class;
-static struct lock_class_key emumousebtn_mutex_class;
-
-static int emumousebtn_input_register(void)
-{
-	int ret;
-
-	emumousebtn = input_allocate_device();
-	if (!emumousebtn)
-		return -ENOMEM;
-
-	lockdep_set_class(&emumousebtn->event_lock, &emumousebtn_event_class);
-	lockdep_set_class(&emumousebtn->mutex, &emumousebtn_mutex_class);
-
-	emumousebtn->name = "Macintosh mouse button emulation";
-	emumousebtn->id.bustype = BUS_ADB;
-	emumousebtn->id.vendor = 0x0001;
-	emumousebtn->id.product = 0x0001;
-	emumousebtn->id.version = 0x0100;
-
-	emumousebtn->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
-	emumousebtn->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
-		BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
-	emumousebtn->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
-
-	ret = input_register_device(emumousebtn);
-	if (ret)
-		input_free_device(emumousebtn);
-
-	return ret;
-}
 
 static int __init mac_hid_init(void)
 {
-	int err;
-
-	err = emumousebtn_input_register();
-	if (err)
-		return err;
-
-#if defined(CONFIG_SYSCTL)
 	mac_hid_sysctl_header = register_sysctl_table(mac_hid_root_dir);
-#endif /* CONFIG_SYSCTL */
+	if (!mac_hid_sysctl_header)
+		return -ENOMEM;
 
 	return 0;
 }
diff --git a/include/linux/kbd_kern.h b/include/linux/kbd_kern.h
index 8bdb16b..506ad20 100644
--- a/include/linux/kbd_kern.h
+++ b/include/linux/kbd_kern.h
@@ -161,7 +161,4 @@ static inline void con_schedule_flip(struct tty_struct *t)
 	schedule_delayed_work(&t->buf.work, 0);
 }
 
-/* mac_hid.c */
-extern int mac_hid_mouse_emulate_buttons(int, unsigned int, int);
-
 #endif


^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2009-09-11  5:28 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-09-11  5:28 [PATCH 0/2] Input filters Dmitry Torokhov
2009-09-11  5:28 ` [PATCH 1/2] Input: implement input filters Dmitry Torokhov
2009-09-11  5:28 ` [PATCH 2/2] Input: Mac button emulation - implement as input filter Dmitry Torokhov

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.