3/3 Serio: support reconnecting keeping same input device
PATCH 1/1 3/3 Serio: support reconnecting keeping same input device
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.
What you think?
Dmitry
P.S. I have a new patch for Synaptics touchpad that makes use of this new functionality that I will sent to the LKML shortly
drivers/input/serio/serio.c +24 -7
@@ -55,6 +55,7 @@EXPORT_SYMBOL (serio_open);EXPORT_SYMBOL (serio_close);EXPORT_SYMBOL (serio_rescan); +EXPORT_SYMBOL (serio_reconnect);struct serio_event {int type;
@@ -81,6 +82,7 @@ }#define SERIO_RESCAN 1 +#define SERIO_RECONNECT 2static DECLARE_WAIT_QUEUE_HEAD (serio_wait);static DECLARE_COMPLETION (serio_exited);
@@ -108,6 +110,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);
@@ -142,18 +150,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
@@ -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);