From: Peter Osterlund <petero2@telia.com>

The patch below introduces a new serio_dev method "reconnect".  It's purpose
is to re-initialize attached hardware while keeping the same input device.  

Reconnect can be used for example during resume or with somewhat broken
hardware like my laptop/docking station which resets the touchpad back in
relative mode without telling anyone whenever I dock or un-dock.  The regular
disconnect/connect solution is not working because clients (like XFree) like
to keep the original input device open so after connecting the touchpad it
will create a brand new input device.  With reconnect the driver has an
option to re-initialize hardware but keep the same input device (given that
hardware didn't change).

If reconnect fails serio automatically fall back to disconnect/reconnect
scheme.


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

diff -puN drivers/input/serio/serio.c~serio-reconnect drivers/input/serio/serio.c
--- 25/drivers/input/serio/serio.c~serio-reconnect	2003-09-26 22:17:32.000000000 -0700
+++ 25-akpm/drivers/input/serio/serio.c	2003-09-26 22:17:32.000000000 -0700
@@ -57,6 +57,7 @@ EXPORT_SYMBOL(serio_unregister_device);
 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 @@ static void serio_find_dev(struct serio 
 }
 
 #define SERIO_RESCAN	1
+#define SERIO_RECONNECT	2
 
 static DECLARE_WAIT_QUEUE_HEAD(serio_wait);
 static DECLARE_COMPLETION(serio_exited);
@@ -96,6 +98,12 @@ void serio_handle_events(void)
 		event = container_of(node, struct serio_event, node);	
 
 		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 :
 				down(&serio_sem);
 				if (event->serio->dev && event->serio->dev->disconnect)
@@ -130,18 +138,27 @@ static int serio_thread(void *nothing)
 	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;
 
-	event->type = SERIO_RESCAN;
-	event->serio = serio;
+		list_add_tail(&event->node, &serio_event_list);
+		wake_up(&serio_wait);
+	}
+}
 
-	list_add_tail(&event->node, &serio_event_list);
-	wake_up(&serio_wait);
+void serio_rescan(struct serio *serio)
+{
+	serio_queue_event(serio, SERIO_RESCAN);
+}
+
+void serio_reconnect(struct serio *serio)
+{
+	serio_queue_event(serio, SERIO_RECONNECT);
 }
 
 irqreturn_t serio_interrupt(struct serio *serio,
diff -puN include/linux/serio.h~serio-reconnect include/linux/serio.h
--- 25/include/linux/serio.h~serio-reconnect	2003-09-26 22:17:32.000000000 -0700
+++ 25-akpm/include/linux/serio.h	2003-09-26 22:17:32.000000000 -0700
@@ -53,6 +53,7 @@ struct serio_dev {
 	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 @@ struct serio_dev {
 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);

_