Re: [PATCH 8/8] Add BTN_TOUCH to Synaptics driver. Update mousedev.

From: Peter Osterlund
Date: Fri Sep 26 2003 - 00:27:19 EST


On Fri, 26 Sep 2003, Vojtech Pavlik wrote:

> On Thu, Sep 25, 2003 at 01:23:33PM -0500, Dmitry Torokhov wrote:
>
> > BTW, any chance on including pass-through patches? Do you want me to re-diff
> > them?
>
> Hmm, I thought I've merged them in already, but obviously I did not.
> Please resend them (rediffed if possible). Thanks.

If these patches are the same as the "reconnect" patches I have in my
tree, I already have them rediffed (attached).

--
Peter Osterlund - petero2@xxxxxxxxx
http://w1.894.telia.com/~u89404340

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


linux-petero/drivers/input/serio/serio.c | 31 ++++++++++++++++++++++++-------
linux-petero/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
--- linux/drivers/input/serio/serio.c~serio-reconnect 2003-09-25 20:18:59.000000000 +0200
+++ linux-petero/drivers/input/serio/serio.c 2003-09-25 20:18:59.000000000 +0200
@@ -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
--- linux/include/linux/serio.h~serio-reconnect 2003-09-25 20:18:59.000000000 +0200
+++ linux-petero/include/linux/serio.h 2003-09-25 20:18:59.000000000 +0200
@@ -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);

_

Here is the update to the Synaptics touchpad driver.

The changes are:
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.

Dmitry


linux-petero/drivers/input/mouse/logips2pp.c | 1
linux-petero/drivers/input/mouse/psmouse-base.c | 94 +++++++++++---
linux-petero/drivers/input/mouse/psmouse.h | 13 ++
linux-petero/drivers/input/mouse/synaptics.c | 153 +++++++++++++++---------
linux-petero/drivers/input/mouse/synaptics.h | 4
5 files changed, 186 insertions(+), 79 deletions(-)

diff -puN drivers/input/mouse/logips2pp.c~synaptics-reconnect drivers/input/mouse/logips2pp.c
--- linux/drivers/input/mouse/logips2pp.c~synaptics-reconnect 2003-09-25 20:19:02.000000000 +0200
+++ linux-petero/drivers/input/mouse/logips2pp.c 2003-09-25 20:19:02.000000000 +0200
@@ -10,6 +10,7 @@
*/

#include <linux/input.h>
+#include <linux/serio.h>
#include "psmouse.h"
#include "logips2pp.h"

diff -puN drivers/input/mouse/psmouse-base.c~synaptics-reconnect drivers/input/mouse/psmouse-base.c
--- linux/drivers/input/mouse/psmouse-base.c~synaptics-reconnect 2003-09-25 20:19:02.000000000 +0200
+++ linux-petero/drivers/input/mouse/psmouse-base.c 2003-09-25 20:19:02.000000000 +0200
@@ -141,7 +141,8 @@ static irqreturn_t psmouse_interrupt(str
goto out;
}

- if (psmouse->pktcnt && time_after(jiffies, psmouse->last + HZ/2)) {
+ if (psmouse->state == PSMOUSE_ACTIVATED &&
+ psmouse->pktcnt && time_after(jiffies, psmouse->last + HZ/2)) {
printk(KERN_WARNING "psmouse.c: %s at %s lost synchronization, throwing %d bytes away.\n",
psmouse->name, psmouse->phys, psmouse->pktcnt);
psmouse->pktcnt = 0;
@@ -519,7 +520,16 @@ static void psmouse_disconnect(struct se
struct psmouse *psmouse = serio->private;

psmouse->state = PSMOUSE_IGNORE;
- synaptics_disconnect(psmouse);
+
+ if (psmouse->ptport) {
+ if (psmouse->ptport->deactivate)
+ psmouse->ptport->deactivate(psmouse);
+ serio_unregister_slave_port(&psmouse->ptport->serio);
+ }
+
+ if (psmouse->disconnect)
+ psmouse->disconnect(psmouse);
+
input_unregister_device(&psmouse->dev);
serio_close(serio);
kfree(psmouse);
@@ -532,19 +542,9 @@ static void psmouse_disconnect(struct se
static int psmouse_pm_callback(struct pm_dev *dev, pm_request_t request, void *data)
{
struct psmouse *psmouse = dev->data;
- struct serio_dev *ser_dev = psmouse->serio->dev;
-
- synaptics_disconnect(psmouse);

- /* We need to reopen the serio port to reinitialize the i8042 controller */
- serio_close(psmouse->serio);
- serio_open(psmouse->serio, ser_dev);
-
- /* Probe and re-initialize the mouse */
- psmouse_probe(psmouse);
- psmouse_initialize(psmouse);
- synaptics_pt_init(psmouse);
- psmouse_activate(psmouse);
+ psmouse->state = PSMOUSE_IGNORE;
+ serio_reconnect(psmouse->serio);

return 0;
}
@@ -590,10 +590,12 @@ static void psmouse_connect(struct serio
return;
}

- pmdev = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, psmouse_pm_callback);
- if (pmdev) {
- psmouse->dev.pm_dev = pmdev;
- pmdev->data = psmouse;
+ if (serio->type != SERIO_PS_PSTHRU) {
+ pmdev = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, psmouse_pm_callback);
+ if (pmdev) {
+ psmouse->dev.pm_dev = pmdev;
+ pmdev->data = psmouse;
+ }
}

sprintf(psmouse->devname, "%s %s %s",
@@ -614,14 +616,68 @@ static void psmouse_connect(struct serio

psmouse_initialize(psmouse);

- synaptics_pt_init(psmouse);
+ if (psmouse->ptport) {
+ printk(KERN_INFO "serio: %s port at %s\n", psmouse->ptport->serio.name, psmouse->phys);
+ serio_register_slave_port(&psmouse->ptport->serio);
+ if (psmouse->ptport->activate)
+ psmouse->ptport->activate(psmouse);
+ }

psmouse_activate(psmouse);
}

+static int psmouse_reconnect(struct serio *serio)
+{
+ struct psmouse *psmouse = serio->private;
+ struct serio_dev *dev = serio->dev;
+ int old_type = psmouse->type;
+
+ if (!dev) {
+ printk(KERN_DEBUG "psmouse: reconnect request, but serio is disconnected, ignoring...\n");
+ return -1;
+ }
+
+ /* We need to reopen the serio port to reinitialize the i8042 controller */
+ serio_close(serio);
+ if (serio_open(serio, dev)) {
+ /* do a disconnect here as serio_open leaves dev as NULL so disconnect
+ * will not be called automatically later
+ */
+ psmouse_disconnect(serio);
+ return -1;
+ }
+
+ psmouse->state = PSMOUSE_NEW_DEVICE;
+ psmouse->type = psmouse->acking = psmouse->cmdcnt = psmouse->pktcnt = 0;
+ if (psmouse->reconnect) {
+ if (psmouse->reconnect(psmouse))
+ return -1;
+ } else if (psmouse_probe(psmouse) != old_type)
+ return -1;
+
+ /* ok, the device type (and capabilities) match the old one,
+ * we can continue using it, complete intialization
+ */
+ psmouse->type = old_type;
+ psmouse_initialize(psmouse);
+
+ if (psmouse->ptport) {
+ if (psmouse_reconnect(&psmouse->ptport->serio)) {
+ serio_unregister_slave_port(&psmouse->ptport->serio);
+ serio_register_slave_port(&psmouse->ptport->serio);
+ if (psmouse->ptport->activate)
+ psmouse->ptport->activate(psmouse);
+ }
+ }
+
+ psmouse_activate(psmouse);
+ return 0;
+}
+
static struct serio_dev psmouse_dev = {
.interrupt = psmouse_interrupt,
.connect = psmouse_connect,
+ .reconnect = psmouse_reconnect,
.disconnect = psmouse_disconnect,
.cleanup = psmouse_cleanup,
};
diff -puN drivers/input/mouse/psmouse.h~synaptics-reconnect drivers/input/mouse/psmouse.h
--- linux/drivers/input/mouse/psmouse.h~synaptics-reconnect 2003-09-25 20:19:02.000000000 +0200
+++ linux-petero/drivers/input/mouse/psmouse.h 2003-09-25 20:19:02.000000000 +0200
@@ -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 @@ struct psmouse {
char error;
char devname[64];
char phys[32];
+
+ int (*reconnect)(struct psmouse *psmouse);
+ void (*disconnect)(struct psmouse *psmouse);
};

#define PSMOUSE_PS2 1
diff -puN drivers/input/mouse/synaptics.c~synaptics-reconnect drivers/input/mouse/synaptics.c
--- linux/drivers/input/mouse/synaptics.c~synaptics-reconnect 2003-09-25 20:19:02.000000000 +0200
+++ linux-petero/drivers/input/mouse/synaptics.c 2003-09-25 20:19:02.000000000 +0200
@@ -192,11 +192,24 @@ static void print_ident(struct synaptics
}
}

+static int synaptics_detect(struct psmouse *psmouse)
+{
+ unsigned char param[4];
+
+ param[0] = 0;
+
+ psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
+ psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
+ psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
+ psmouse_command(psmouse, param, PSMOUSE_CMD_SETRES);
+ psmouse_command(psmouse, param, PSMOUSE_CMD_GETINFO);
+
+ return param[1] == 0x47 ? 0 : -1;
+}
+
static int synaptics_query_hardware(struct psmouse *psmouse)
{
- struct synaptics_data *priv = psmouse->private;
int retries = 0;
- int mode;

while ((retries++ < 3) && synaptics_reset(psmouse))
printk(KERN_ERR "synaptics reset failed\n");
@@ -208,7 +221,14 @@ static int synaptics_query_hardware(stru
if (synaptics_capability(psmouse))
return -1;

- mode = SYN_BIT_ABSOLUTE_MODE | SYN_BIT_HIGH_RATE;
+ return 0;
+}
+
+static int synaptics_set_mode(struct psmouse *psmouse, int mode)
+{
+ struct synaptics_data *priv = psmouse->private;
+
+ mode |= SYN_BIT_ABSOLUTE_MODE | SYN_BIT_HIGH_RATE;
if (SYN_ID_MAJOR(priv->identity) >= 4)
mode |= SYN_BIT_DISABLE_GESTURE;
if (SYN_CAP_EXTENDED(priv->capabilities))
@@ -265,49 +285,38 @@ static void synaptics_pass_pt_packet(str
}
}

-int synaptics_pt_init(struct psmouse *psmouse)
+static void synaptics_pt_activate(struct psmouse *psmouse)
{
- struct synaptics_data *priv = psmouse->private;
- struct serio *port;
- struct psmouse *child;
+ struct psmouse *child = psmouse->ptport->serio.private;

- if (psmouse->type != PSMOUSE_SYNAPTICS)
- return -1;
- if (!SYN_CAP_EXTENDED(priv->capabilities))
- return -1;
- if (!SYN_CAP_PASS_THROUGH(priv->capabilities))
- return -1;
+ /* adjust the touchpad to child's choice of protocol */
+ if (child && child->type >= PSMOUSE_GENPS) {
+ if (synaptics_set_mode(psmouse, SYN_BIT_FOUR_BYTE_CLIENT))
+ printk(KERN_INFO "synaptics: failed to enable 4-byte guest protocol\n");
+ }
+}
+
+static void synaptics_pt_create(struct psmouse *psmouse)
+{
+ struct psmouse_ptport *port;

- priv->ptport = port = kmalloc(sizeof(struct serio), GFP_KERNEL);
+ psmouse->ptport = port = kmalloc(sizeof(struct psmouse_ptport), GFP_KERNEL);
if (!port) {
- printk(KERN_ERR "synaptics: not enough memory to allocate serio port\n");
- return -1;
+ printk(KERN_ERR "synaptics: not enough memory to allocate pass-through port\n");
+ return;
}

- memset(port, 0, sizeof(struct serio));
- port->type = SERIO_PS_PSTHRU;
- port->name = "Synaptics pass-through";
- port->phys = "synaptics-pt/serio0";
- port->write = synaptics_pt_write;
- port->open = synaptics_pt_open;
- port->close = synaptics_pt_close;
- port->driver = psmouse;
+ memset(port, 0, sizeof(struct psmouse_ptport));

- printk(KERN_INFO "serio: %s port at %s\n", port->name, psmouse->phys);
- serio_register_slave_port(port);
+ port->serio.type = SERIO_PS_PSTHRU;
+ port->serio.name = "Synaptics pass-through";
+ port->serio.phys = "synaptics-pt/serio0";
+ port->serio.write = synaptics_pt_write;
+ port->serio.open = synaptics_pt_open;
+ port->serio.close = synaptics_pt_close;
+ port->serio.driver = psmouse;

- /* adjust the touchpad to child's choice of protocol */
- child = port->private;
- if (child && child->type >= PSMOUSE_GENPS) {
- if (synaptics_mode_cmd(psmouse, (SYN_BIT_ABSOLUTE_MODE |
- SYN_BIT_HIGH_RATE |
- SYN_BIT_DISABLE_GESTURE |
- SYN_BIT_FOUR_BYTE_CLIENT |
- SYN_BIT_W_MODE)))
- printk(KERN_INFO "synaptics: failed to enable 4-byte guest protocol\n");
- }
-
- return 0;
+ port->activate = synaptics_pt_activate;
}

/*****************************************************************************
@@ -371,6 +380,39 @@ static void set_input_params(struct inpu
clear_bit(REL_Y, dev->relbit);
}

+static void synaptics_disconnect(struct psmouse *psmouse)
+{
+ synaptics_mode_cmd(psmouse, 0);
+ kfree(psmouse->private);
+}
+
+static int synaptics_reconnect(struct psmouse *psmouse)
+{
+ struct synaptics_data *priv = psmouse->private;
+ struct synaptics_data old_priv = *priv;
+
+ if (synaptics_detect(psmouse))
+ return -1;
+
+ if (synaptics_query_hardware(psmouse)) {
+ printk(KERN_ERR "Unable to query Synaptics hardware.\n");
+ return -1;
+ }
+
+ if (old_priv.identity != priv->identity ||
+ old_priv.model_id != priv->model_id ||
+ old_priv.capabilities != priv->capabilities ||
+ old_priv.ext_cap != priv->ext_cap)
+ return -1;
+
+ if (synaptics_set_mode(psmouse, 0)) {
+ printk(KERN_ERR "Unable to initialize Synaptics hardware.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
int synaptics_init(struct psmouse *psmouse)
{
struct synaptics_data *priv;
@@ -385,13 +427,24 @@ int synaptics_init(struct psmouse *psmou
memset(priv, 0, sizeof(struct synaptics_data));

if (synaptics_query_hardware(psmouse)) {
- printk(KERN_ERR "Unable to query/initialize Synaptics hardware.\n");
+ printk(KERN_ERR "Unable to query Synaptics hardware.\n");
+ goto init_fail;
+ }
+
+ if (synaptics_set_mode(psmouse, 0)) {
+ printk(KERN_ERR "Unable to initialize Synaptics hardware.\n");
goto init_fail;
}

+ if (SYN_CAP_EXTENDED(priv->capabilities) && SYN_CAP_PASS_THROUGH(priv->capabilities))
+ synaptics_pt_create(psmouse);
+
print_ident(priv);
set_input_params(&psmouse->dev, priv);

+ psmouse->disconnect = synaptics_disconnect;
+ psmouse->reconnect = synaptics_reconnect;
+
return 0;

init_fail:
@@ -399,20 +452,6 @@ int synaptics_init(struct psmouse *psmou
return -1;
}

-void synaptics_disconnect(struct psmouse *psmouse)
-{
- struct synaptics_data *priv = psmouse->private;
-
- if (psmouse->type == PSMOUSE_SYNAPTICS && priv) {
- synaptics_mode_cmd(psmouse, 0);
- if (priv->ptport) {
- serio_unregister_slave_port(priv->ptport);
- kfree(priv->ptport);
- }
- kfree(priv);
- }
-}
-
/*****************************************************************************
* Functions to interpret the absolute mode packets
****************************************************************************/
@@ -613,8 +652,9 @@ void synaptics_process_byte(struct psmou
printk(KERN_NOTICE "Synaptics driver resynced.\n");
}

- if (priv->ptport && synaptics_is_pt_packet(psmouse->packet))
- synaptics_pass_pt_packet(priv->ptport, psmouse->packet);
+ if (psmouse->ptport && psmouse->ptport->serio.dev &&
+ synaptics_is_pt_packet(psmouse->packet))
+ synaptics_pass_pt_packet(&psmouse->ptport->serio, psmouse->packet);
else
synaptics_process_packet(psmouse);

@@ -628,6 +668,7 @@ void synaptics_process_byte(struct psmou
psmouse->pktcnt = 0;
if (psmouse_resetafter > 0 && priv->out_of_sync == psmouse_resetafter) {
psmouse->state = PSMOUSE_IGNORE;
- serio_rescan(psmouse->serio);
+ printk(KERN_NOTICE "synaptics: issuing reconnect request\n");
+ serio_reconnect(psmouse->serio);
}
}
diff -puN drivers/input/mouse/synaptics.h~synaptics-reconnect drivers/input/mouse/synaptics.h
--- linux/drivers/input/mouse/synaptics.h~synaptics-reconnect 2003-09-25 20:19:02.000000000 +0200
+++ linux-petero/drivers/input/mouse/synaptics.h 2003-09-25 20:19:02.000000000 +0200
@@ -12,8 +12,6 @@

extern void synaptics_process_byte(struct psmouse *psmouse, struct pt_regs *regs);
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 @@ struct synaptics_data {
/* 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 */

_