Skip to contentSkip to conversation

2.6: New set of input patches

PATCH New Dmitry Torokhov 1 participant 7 messages Sep 30, 2003 Input drivers

lore
Share & tools
lore
#0 lore raw

Hi Vojtech,

I have a bunch of input patches that work quite nicely on my laptop, would you please consider taking them?

1. SERIO: rename serio_{register|unregister}_slave_port to __serio_{register|unregister}_port to better follow locked/lockless naming convention.

2. SERIO: There is a possibility that serio might get deleted while there are outstanding events involving that serio waiting for kseriod to process them. Invalidate them so kseriod thread will just drop dead events. (Resend)

3. Input: Introduce an optional blacklist field in input_handler structure. When loading a new device or a new handler try to match device against handler's black list before doing match on required attributes.
This allows to get rid of "surprises" in connect functions, IMO connect should only fail when it physically can not connect, not because it decides it does not like device.

4. Input: Synaptics code cleanup and credit update. The switch in synaptics_process_packet() was quite ugly.

5. SERIO: serio_reconnect added. Similar to serio_rescan but gives driver a chance to re-initialize keeping the same input device. (Resend)

6. Input/Synaptics:
- Support for pass-through port moved from Synaptics driver to psmouse itself, it is cleaner and should allow using it in other drivers if needed.
- The driver makes use of new reconnect functionality in serio. It will try to keep the same input device after resume or when it resets itself.
- If mouse is disconnected or other mouse plugged in while sleeping the driver should correctly recognize that and create a new serio/input device. (Resend, original patch split into 2 - #4 & #6)

Dmitry

#1 lore raw

PATCH 1/6 serio: rename serio_[un]register_slave_port to __serio_[un]register_port

Input: rename serio_{register|unregister}_slave_port to
__serio_{register|unregister}_port to better follow locked/lockless naming convention

 drivers/input/mouse/synaptics.c |    4 ++--
 drivers/input/serio/serio.c     |   23 ++++++++++-------------
 include/linux/serio.h           |    4 ++--
 3 files changed, 14 insertions(+), 17 deletions(-)

===================================================================

drivers/input/mouse/synaptics.c +2 -2
diff -Nru a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
@@ -294,7 +294,7 @@
 	port->driver = psmouse;
 
 	printk(KERN_INFO "serio: %s port at %s\n", port->name, psmouse->phys);
-	serio_register_slave_port(port);
+	__serio_register_port(port);	/* already have serio_sem */
 
 	/* adjust the touchpad to child's choice of protocol */
 	child = port->private;
@@ -406,7 +406,7 @@
 	if (psmouse->type == PSMOUSE_SYNAPTICS && priv) {
 		synaptics_mode_cmd(psmouse, 0);
 		if (priv->ptport) {
-			serio_unregister_slave_port(priv->ptport);
+			__serio_unregister_port(priv->ptport); /* already have serio_sem */
 			kfree(priv->ptport);
 		}
 		kfree(priv);
drivers/input/serio/serio.c +10 -13
diff -Nru a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c
@@ -49,9 +49,9 @@
 
 EXPORT_SYMBOL(serio_interrupt);
 EXPORT_SYMBOL(serio_register_port);
-EXPORT_SYMBOL(serio_register_slave_port);
+EXPORT_SYMBOL(__serio_register_port);
 EXPORT_SYMBOL(serio_unregister_port);
-EXPORT_SYMBOL(serio_unregister_slave_port);
+EXPORT_SYMBOL(__serio_unregister_port);
 EXPORT_SYMBOL(serio_register_device);
 EXPORT_SYMBOL(serio_unregister_device);
 EXPORT_SYMBOL(serio_open);
@@ -163,17 +163,16 @@
 void serio_register_port(struct serio *serio)
 {
 	down(&serio_sem);
-	list_add_tail(&serio->node, &serio_list);
-	serio_find_dev(serio);
+	__serio_register_port(serio);
 	up(&serio_sem);
 }
 
 /*
- * Same as serio_register_port but does not try to acquire serio_sem.
- * Should be used when registering a serio from other input device's
+ * Should only be called directly if serio_sem has already been taken,
+ * for example when unregistering a serio from other input device's 
  * connect() function.
  */
-void serio_register_slave_port(struct serio *serio)
+void __serio_register_port(struct serio *serio)
 {
 	list_add_tail(&serio->node, &serio_list);
 	serio_find_dev(serio);
@@ -182,18 +181,16 @@
 void serio_unregister_port(struct serio *serio)
 {
 	down(&serio_sem);
-	list_del_init(&serio->node);
-	if (serio->dev && serio->dev->disconnect)
-		serio->dev->disconnect(serio);
+	__serio_unregister_port(serio);
 	up(&serio_sem);
 }
 
 /*
- * Same as serio_unregister_port but does not try to acquire serio_sem.
- * Should be used when unregistering a serio from other input device's
+ * Should only be called directly if serio_sem has already been taken,
+ * for example when unregistering a serio from other input device's 
  * disconnect() function.
  */
-void serio_unregister_slave_port(struct serio *serio)
+void __serio_unregister_port(struct serio *serio)
 {
 	list_del_init(&serio->node);
 	if (serio->dev && serio->dev->disconnect)
include/linux/serio.h +2 -2
diff -Nru a/include/linux/serio.h b/include/linux/serio.h
@@ -65,9 +65,9 @@
 irqreturn_t serio_interrupt(struct serio *serio, unsigned char data, unsigned int flags, struct pt_regs *regs);
 
 void serio_register_port(struct serio *serio);
-void serio_register_slave_port(struct serio *serio);
+void __serio_register_port(struct serio *serio);
 void serio_unregister_port(struct serio *serio);
-void serio_unregister_slave_port(struct serio *serio);
+void __serio_unregister_port(struct serio *serio);
 void serio_register_device(struct serio_dev *dev);
 void serio_unregister_device(struct serio_dev *dev);
 
#2 lore raw

PATCH 2/6 serio: possible race between port removal and kseriod

Input: There is a possibility that serio might get deleted while there
are outstanding events involving that serio waiting for kseriod to process them. Invalidate them so kseriod thread will just drop dead events.

 serio.c |   18 ++++++++++++++++--
 1 files changed, 16 insertions(+), 2 deletions(-)

===================================================================

drivers/input/serio/serio.c +16 -2
diff -Nru a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c
@@ -87,6 +87,15 @@
 static DECLARE_WAIT_QUEUE_HEAD(serio_wait);
 static DECLARE_COMPLETION(serio_exited);
 
+static void serio_invalidate_pending_events(struct serio *serio)
+{
+	struct serio_event *event;
+	
+	list_for_each_entry(event, &serio_event_list, node)
+		if (event->serio == serio)
+			event->serio = NULL;
+}
+
 void serio_handle_events(void)
 {
 	struct list_head *node, *next;
@@ -95,17 +104,21 @@
 	list_for_each_safe(node, next, &serio_event_list) {
 		event = container_of(node, struct serio_event, node);	
 
+		down(&serio_sem);
+		if (event->serio == NULL)
+			goto event_done;
+		
 		switch (event->type) {
 			case SERIO_RESCAN :
-				down(&serio_sem);
 				if (event->serio->dev && event->serio->dev->disconnect)
 					event->serio->dev->disconnect(event->serio);
 				serio_find_dev(event->serio);
-				up(&serio_sem);
 				break;
 			default:
 				break;
 		}
+event_done:
+		up(&serio_sem);
 		list_del_init(node);
 		kfree(event);
 	}
@@ -192,6 +205,7 @@
  */
 void __serio_unregister_port(struct serio *serio)
 {
+	serio_invalidate_pending_events(serio);
 	list_del_init(&serio->node);
 	if (serio->dev && serio->dev->disconnect)
 		serio->dev->disconnect(serio);
#3 lore raw

PATCH 3/6 Add black list to handler<->device matching

Input: Introduce an optional blacklist field in input_handler structure.
When loading a new device or a new handler try to match device against handler's black list before doing match on required attributes.
This allows to get rid of "surprises" in connect functions, IMO connect should only fail when it physically can not connect, not because it decides it does not like device.

 drivers/input/input.c  |   14 ++++++++------
 drivers/input/joydev.c |   14 ++++++++++----
 include/linux/input.h  |    1 +
 3 files changed, 19 insertions(+), 10 deletions(-)

===================================================================

drivers/input/input.c +8 -6
diff -Nru a/drivers/input/input.c b/drivers/input/input.c
@@ -447,9 +447,10 @@
 	list_add_tail(&dev->node, &input_dev_list);
 
 	list_for_each_entry(handler, &input_handler_list, node)
-		if ((id = input_match_device(handler->id_table, dev)))
-			if ((handle = handler->connect(handler, dev, id)))
-				input_link_handle(handle);
+		if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
+			if ((id = input_match_device(handler->id_table, dev)))
+				if ((handle = handler->connect(handler, dev, id)))
+					input_link_handle(handle);
 
 #ifdef CONFIG_HOTPLUG
 	input_call_hotplug("add", dev);
@@ -507,9 +508,10 @@
 	list_add_tail(&handler->node, &input_handler_list);
 	
 	list_for_each_entry(dev, &input_dev_list, node)
-		if ((id = input_match_device(handler->id_table, dev)))
-			if ((handle = handler->connect(handler, dev, id)))
-				input_link_handle(handle);
+		if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
+			if ((id = input_match_device(handler->id_table, dev)))
+				if ((handle = handler->connect(handler, dev, id)))
+					input_link_handle(handle);
 
 #ifdef CONFIG_PROC_FS
 	input_devices_state++;
drivers/input/joydev.c +10 -4
diff -Nru a/drivers/input/joydev.c b/drivers/input/joydev.c
@@ -380,10 +380,6 @@
 	struct joydev *joydev;
 	int i, j, t, minor;
 
-	/* Avoid tablets */
-        if (test_bit(EV_KEY, dev->evbit) && test_bit(BTN_TOUCH, dev->keybit))
-		return NULL;
-
 	for (minor = 0; minor < JOYDEV_MINORS && joydev_table[minor]; minor++);
 	if (minor == JOYDEV_MINORS) {
 		printk(KERN_ERR "joydev: no more free joydev devices\n");
@@ -464,6 +460,15 @@
 		joydev_free(joydev);
 }
 
+static struct input_device_id joydev_blacklist[] = {
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
+		.evbit = { BIT(EV_KEY) },
+		.keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) },
+	}, 	/* Avoid itouchpads, touchscreens and tablets */
+	{ }, 	/* Terminating entry */
+};
+
 static struct input_device_id joydev_ids[] = {
 	{
 		.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
@@ -493,6 +498,7 @@
 	.minor =	JOYDEV_MINOR_BASE,
 	.name =		"joydev",
 	.id_table =	joydev_ids,
+	.blacklist = 	joydev_blacklist,
 };
 
 static int __init joydev_init(void)
include/linux/input.h +1 -0
diff -Nru a/include/linux/input.h b/include/linux/input.h
@@ -870,6 +870,7 @@
 	char *name;
 
 	struct input_device_id *id_table;
+	struct input_device_id *blacklist;
 
 	struct list_head	h_list;
 	struct list_head	node;
#4 lore raw

PATCH 4/6 Synaptics: code cleanup

Input: Synaptics code cleanup and credit update.

 synaptics.c |   79 ++++++++++++++++++++----------------------------------------
 1 files changed, 27 insertions(+), 52 deletions(-)

===================================================================

drivers/input/mouse/synaptics.c +27 -52
diff -Nru a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
@@ -2,7 +2,8 @@
  * Synaptics TouchPad PS/2 mouse driver
  *
  *   2003 Dmitry Torokhov <[email protected]>
- *     Added support for pass-through port
+ *     Added support for pass-through port. Special thanks to Peter Berg Larsen
+ *     for explaining various Synaptics quirks.
  *
  *   2003 Peter Osterlund <[email protected]>
  *     Ported to 2.5 input device infrastructure.
@@ -419,16 +420,7 @@
 
 static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data *priv, struct synaptics_hw_state *hw)
 {
-	hw->up    = 0;
-	hw->down  = 0;
-	hw->b0    = 0;
-	hw->b1    = 0;
-	hw->b2    = 0;
-	hw->b3    = 0;
-	hw->b4    = 0;
-	hw->b5    = 0;
-	hw->b6    = 0;
-	hw->b7    = 0;
+	memset(hw, 0, sizeof(struct synaptics_hw_state));
 
 	if (SYN_MODEL_NEWABS(priv->model_id)) {
 		hw->x = (((buf[3] & 0x10) << 8) |
@@ -570,44 +562,29 @@
 	input_sync(dev);
 }
 
+static int synaptics_validate_byte(struct psmouse *psmouse)
+{
+	static unsigned char newabs_mask[] = { 0xC8, 0x00, 0x00, 0xC8, 0x00 };
+	static unsigned char newabs_rslt[] = { 0x80, 0x00, 0x00, 0xC0, 0x00 };
+	static unsigned char oldabs_mask[] = { 0xC0, 0x60, 0x00, 0xC0, 0x60 };
+	static unsigned char oldabs_rslt[] = { 0xC0, 0x00, 0x00, 0x80, 0x00 };
+	struct synaptics_data *priv = psmouse->private;
+	int idx = psmouse->pktcnt - 1;
+
+	if (SYN_MODEL_NEWABS(priv->model_id))
+		return (psmouse->packet[idx] & newabs_mask[idx]) == newabs_rslt[idx];
+	else
+		return (psmouse->packet[idx] & oldabs_mask[idx]) == oldabs_rslt[idx];
+}
+
 void synaptics_process_byte(struct psmouse *psmouse, struct pt_regs *regs)
 {
 	struct input_dev *dev = &psmouse->dev;
 	struct synaptics_data *priv = psmouse->private;
-	unsigned char data = psmouse->packet[psmouse->pktcnt - 1];
-	int newabs = SYN_MODEL_NEWABS(priv->model_id);
 
 	input_regs(dev, regs);
 
-	switch (psmouse->pktcnt) {
-	case 1:
-		if (newabs ? ((data & 0xC8) != 0x80) : ((data & 0xC0) != 0xC0)) {
-			printk(KERN_WARNING "Synaptics driver lost sync at 1st byte\n");
-			goto bad_sync;
-		}
-		break;
-	case 2:
-		if (!newabs && ((data & 0x60) != 0x00)) {
-			printk(KERN_WARNING "Synaptics driver lost sync at 2nd byte\n");
-			goto bad_sync;
-		}
-		break;
-	case 4:
-		if (newabs ? ((data & 0xC8) != 0xC0) : ((data & 0xC0) != 0x80)) {
-			printk(KERN_WARNING "Synaptics driver lost sync at 4th byte\n");
-			goto bad_sync;
-		}
-		break;
-	case 5:
-		if (!newabs && ((data & 0x60) != 0x00)) {
-			printk(KERN_WARNING "Synaptics driver lost sync at 5th byte\n");
-			goto bad_sync;
-		}
-		break;
-	default:
-		if (psmouse->pktcnt < 6)
-			break;		    /* Wait for full packet */
-
+	if (psmouse->pktcnt >= 6) { /* Full packet received */
 		if (priv->out_of_sync) {
 			priv->out_of_sync = 0;
 			printk(KERN_NOTICE "Synaptics driver resynced.\n");
@@ -617,17 +594,15 @@
 			synaptics_pass_pt_packet(priv->ptport, psmouse->packet);
 		else
 			synaptics_process_packet(psmouse);
-
 		psmouse->pktcnt = 0;
-		break;
-	}
-	return;
 
- bad_sync:
-	priv->out_of_sync++;
-	psmouse->pktcnt = 0;
-	if (psmouse_resetafter > 0 && priv->out_of_sync	== psmouse_resetafter) {
-		psmouse->state = PSMOUSE_IGNORE;
-		serio_rescan(psmouse->serio);
+	} else if (psmouse->pktcnt && !synaptics_validate_byte(psmouse)) {
+		printk(KERN_WARNING "Synaptics driver lost sync at byte %d\n", psmouse->pktcnt);
+		psmouse->pktcnt = 0;
+		if (++priv->out_of_sync == psmouse_resetafter) {
+			psmouse->state = PSMOUSE_IGNORE;
+			printk(KERN_NOTICE "synaptics: issuing rescan request\n");
+			serio_rescan(psmouse->serio);
+		}
 	}
 }
#5 lore raw

PATCH 5/6 serio: reconnect facility

Input: serio_reconnect added. Similar to serio_rescan but gives driver
a chance to re-initialize keeping the same input device.

 drivers/input/serio/serio.c |   31 ++++++++++++++++++++++++-------
 include/linux/serio.h       |    2 ++
 2 files changed, 26 insertions(+), 7 deletions(-)

===================================================================

drivers/input/serio/serio.c +24 -7
diff -Nru a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c
@@ -57,6 +57,7 @@
 EXPORT_SYMBOL(serio_open);
 EXPORT_SYMBOL(serio_close);
 EXPORT_SYMBOL(serio_rescan);
+EXPORT_SYMBOL(serio_reconnect);
 
 struct serio_event {
 	int type;
@@ -83,6 +84,7 @@
 }
 
 #define SERIO_RESCAN	1
+#define SERIO_RECONNECT	2
 
 static DECLARE_WAIT_QUEUE_HEAD(serio_wait);
 static DECLARE_COMPLETION(serio_exited);
@@ -109,6 +111,12 @@
 			goto event_done;
 		
 		switch (event->type) {
+			case SERIO_RECONNECT :
+				if (event->serio->dev && event->serio->dev->reconnect)
+					if (event->serio->dev->reconnect(event->serio) == 0)
+						break;
+				/* reconnect failed - fall through to rescan */
+				
 			case SERIO_RESCAN :
 				if (event->serio->dev && event->serio->dev->disconnect)
 					event->serio->dev->disconnect(event->serio);
@@ -143,18 +151,27 @@
 	complete_and_exit(&serio_exited, 0);
 }
 
-void serio_rescan(struct serio *serio)
+static void serio_queue_event(struct serio *serio, int event_type)
 {
 	struct serio_event *event;
 
-	if (!(event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC)))
-		return;
+	if ((event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC))) {
+		event->type = event_type;
+		event->serio = serio;
+
+		list_add_tail(&event->node, &serio_event_list);
+		wake_up(&serio_wait);
+	}
+}
 
-	event->type = SERIO_RESCAN;
-	event->serio = serio;
+void serio_rescan(struct serio *serio)
+{
+	serio_queue_event(serio, SERIO_RESCAN);
+}
 
-	list_add_tail(&event->node, &serio_event_list);
-	wake_up(&serio_wait);
+void serio_reconnect(struct serio *serio)
+{
+	serio_queue_event(serio, SERIO_RECONNECT);
 }
 
 irqreturn_t serio_interrupt(struct serio *serio,
include/linux/serio.h +2 -0
diff -Nru a/include/linux/serio.h b/include/linux/serio.h
@@ -53,6 +53,7 @@
 	irqreturn_t (*interrupt)(struct serio *, unsigned char,
 			unsigned int, struct pt_regs *);
 	void (*connect)(struct serio *, struct serio_dev *dev);
+	int  (*reconnect)(struct serio *);
 	void (*disconnect)(struct serio *);
 	void (*cleanup)(struct serio *);
 
@@ -62,6 +63,7 @@
 int serio_open(struct serio *serio, struct serio_dev *dev);
 void serio_close(struct serio *serio);
 void serio_rescan(struct serio *serio);
+void serio_reconnect(struct serio *serio);
 irqreturn_t serio_interrupt(struct serio *serio, unsigned char data, unsigned int flags, struct pt_regs *regs);
 
 void serio_register_port(struct serio *serio);
#6 lore raw

PATCH 6/6 Synaptics: use serio_reconnect

Input/Synaptics:
1. Support for pass-through port moved from Synaptics driver to psmouse itself, it is cleaner and should allow using it in other drivers if needed.
2. The driver makes use of new reconnect functionality in serio. It will try to keep the same input device after resume or when it resets itself.
3. If mouse is disconnected or other mouse plugged in while sleeping the driver should correctly recognize that and create a new serio/input device.

 logips2pp.c    |    1 
 psmouse-base.c |  124 +++++++++++++++++++++++++++++++--------------
 psmouse.h      |   13 ++++
 synaptics.c    |  157 ++++++++++++++++++++++++++++++++++-----------------------
 synaptics.h    |    6 --
 5 files changed, 199 insertions(+), 102 deletions(-)

===================================================================

drivers/input/mouse/logips2pp.c +1 -0
diff -Nru a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c
@@ -10,6 +10,7 @@
  */
 
 #include <linux/input.h>
+#include <linux/serio.h>
 #include "psmouse.h"
 #include "logips2pp.h"
 
drivers/input/mouse/psmouse-base.c +88 -36

184 lines not on this page · raw message

drivers/input/mouse/psmouse.h +13 -0
diff -Nru a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
@@ -22,10 +22,20 @@
 #define PSMOUSE_ACTIVATED	1
 #define PSMOUSE_IGNORE		2
 
+struct psmouse;
+
+struct psmouse_ptport {
+	struct serio serio;
+
+	void (*activate)(struct psmouse *parent);
+	void (*deactivate)(struct psmouse *parent);
+};
+
 struct psmouse {
 	void *private;
 	struct input_dev dev;
 	struct serio *serio;
+	struct psmouse_ptport *ptport;
 	char *vendor;
 	char *name;
 	unsigned char cmdbuf[8];
@@ -41,6 +51,9 @@
 	char error;
 	char devname[64];
 	char phys[32];
+	
+	int (*reconnect)(struct psmouse *psmouse);
+	void (*disconnect)(struct psmouse *psmouse);
 };
 
 #define PSMOUSE_PS2		1
drivers/input/mouse/synaptics.c +96 -61

225 lines not on this page · raw message

drivers/input/mouse/synaptics.h +1 -5
diff -Nru a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
@@ -9,11 +9,9 @@
 #ifndef _SYNAPTICS_H
 #define _SYNAPTICS_H
 
-
 extern void synaptics_process_byte(struct psmouse *psmouse, struct pt_regs *regs);
+extern int synaptics_detect(struct psmouse *psmouse);
 extern int synaptics_init(struct psmouse *psmouse);
-extern int synaptics_pt_init(struct psmouse *psmouse);
-extern void synaptics_disconnect(struct psmouse *psmouse);
 
 /* synaptics queries */
 #define SYN_QUE_IDENTIFY		0x00
@@ -105,8 +103,6 @@
 	/* Data for normal processing */
 	unsigned int out_of_sync;		/* # of packets out of sync */
 	int old_w;				/* Previous w value */
-	
-	struct serio *ptport;			/* pass-through port */
 };
 
 #endif /* _SYNAPTICS_H */