I've got a synaptics touchpad which is taking a relatively large
amount of time to respond to the synaptics magic query during resume.

pms0 at pckbc0 (aux slot)
wsmouse0 at pms0 mux 0
wsmouse1 at pms0 mux 0
pms0: Synaptics clickpad, firmware 8.0

The pms(4) driver gives up on it quickly, and then I end up with no
mouse in X after resume. I have to restart X to get the mouse back,
which works because at that time the touchpad has become responsive again.

This diff fixes that issue by making the pms driver wait longer.
But I don't want it to wait this long during usual touchpad detection,
since that would get in the way when no synaptics touchpad is present.

I had to tweak the state machine to always go into 'suspend' state
to allow the driver to decide to wait longer only when resuming.
Before this change, the driver only transitioned into 'suspend'
state if the device was still open when pmsactivate() was called.
But since apparently the X server closes mouse devices on suspend,
the current state is already 'disabled' when we get to pmsactivate().
I could never see the driver go into 'suspend' state without this diff.

FWIW, I usually see 3 'pms0: device not resuming, retrying' lines
during resume but I gave it some additional headroom. Does anyone
using a synaptics touchpad see a different number of one or more lines?

Testing with any touchpad appreciated.

Index: pms.c
===================================================================
RCS file: /cvs/src/sys/dev/pckbc/pms.c,v
retrieving revision 1.45
diff -u -p -r1.45 pms.c
--- pms.c       16 Jul 2013 08:11:39 -0000      1.45
+++ pms.c       2 Sep 2013 09:16:56 -0000
@@ -220,6 +220,8 @@ int pmsactivate(struct device *, int);
 void   pmsinput(void *, int);
 
 int    pms_change_state(struct pms_softc *, int, int);
+void   pms_suspend_device(struct pms_softc *);
+void   pms_resume_device(struct pms_softc *);
 
 int    pms_ioctl(void *, u_long, caddr_t, int, struct proc *);
 int    pms_enable(void *);
@@ -685,14 +687,10 @@ pmsactivate(struct device *self, int act
 
        switch (act) {
        case DVACT_SUSPEND:
-               if (sc->sc_state == PMS_STATE_ENABLED)
-                       pms_change_state(sc, PMS_STATE_SUSPENDED,
-                           PMS_DEV_IGNORE);
+               pms_change_state(sc, PMS_STATE_SUSPENDED, PMS_DEV_IGNORE);
                break;
        case DVACT_RESUME:
-               if (sc->sc_state == PMS_STATE_SUSPENDED)
-                       pms_change_state(sc, PMS_STATE_ENABLED,
-                           PMS_DEV_IGNORE);
+               pms_change_state(sc, PMS_STATE_ENABLED, PMS_DEV_IGNORE);
                break;
        }
        return (0);
@@ -725,37 +723,61 @@ pms_change_state(struct pms_softc *sc, i
 
        switch (newstate) {
        case PMS_STATE_ENABLED:
-               sc->inputstate = 0;
-
-               pckbc_slot_enable(sc->sc_kbctag, PCKBC_AUX_SLOT, 1);
-
-               if (sc->poll)
-                       pckbc_flush(sc->sc_kbctag, PCKBC_AUX_SLOT);
-
-               pms_reset(sc);
-               if (sc->protocol->type == PMS_STANDARD ||
-                   sc->protocol->enable(sc) == 0)
-                       pms_protocol_lookup(sc);
-
-               pms_dev_enable(sc);
+               pms_resume_device(sc);
                break;
        case PMS_STATE_DISABLED:
        case PMS_STATE_SUSPENDED:
-               pms_dev_disable(sc);
-
-               if (sc->protocol->disable)
-                       sc->protocol->disable(sc);
-
-               pckbc_slot_enable(sc->sc_kbctag, PCKBC_AUX_SLOT, 0);
+               pms_suspend_device(sc);
                break;
        }
 
-       sc->sc_state = newstate;
+       if (sc->sc_state == PMS_STATE_SUSPENDED) {
+               /* 
+                * We enabled the device during resume to re-detect it.
+                * If the device isn't open we can disable it again now.
+                */
+               if (sc->sc_dev_enable == 0) {
+                       pms_suspend_device(sc);
+                       sc->sc_state = PMS_STATE_DISABLED;
+               } else
+                       sc->sc_state = newstate;
+       } else
+               sc->sc_state = newstate;
+
        sc->poll = (newstate == PMS_STATE_SUSPENDED) ? 1 : 0;
 
        return (0);
 }
 
+void
+pms_resume_device(struct pms_softc *sc)
+{
+       sc->inputstate = 0;
+
+       pckbc_slot_enable(sc->sc_kbctag, PCKBC_AUX_SLOT, 1);
+
+       if (sc->poll)
+               pckbc_flush(sc->sc_kbctag, PCKBC_AUX_SLOT);
+
+       pms_reset(sc);
+       if (sc->protocol->type == PMS_STANDARD ||
+           sc->protocol->enable(sc) == 0)
+               pms_protocol_lookup(sc);
+
+       pms_dev_enable(sc);
+}
+
+void
+pms_suspend_device(struct pms_softc *sc)
+{
+       pms_dev_disable(sc);
+
+       if (sc->protocol->disable)
+               sc->protocol->disable(sc);
+
+       pckbc_slot_enable(sc->sc_kbctag, PCKBC_AUX_SLOT, 0);
+}
+
 int
 pms_enable(void *v)
 {
@@ -937,15 +959,38 @@ pms_enable_synaptics(struct pms_softc *s
        struct synaptics_softc *syn = sc->synaptics;
        struct wsmousedev_attach_args a;
        u_char resp[3];
-       int mode;
+       int mode, i;
 
        if (pms_set_resolution(sc, 0) ||
            pms_set_resolution(sc, 0) ||
            pms_set_resolution(sc, 0) ||
            pms_set_resolution(sc, 0) ||
            pms_get_status(sc, resp) ||
-           resp[1] != SYNAPTICS_ID_MAGIC)
-               goto err;
+           resp[1] != SYNAPTICS_ID_MAGIC) {
+               if (sc->sc_state != PMS_STATE_SUSPENDED)
+                       goto err;
+               /* 
+                * Some synaptics touchpads don't resume quickly.
+                * Retry a few times.
+                */
+               for (i = 10; i > 0; --i) {
+                       printf("%s: device not resuming, retrying\n",
+                           DEVNAME(sc));
+                       pms_reset(sc);
+                       if (pms_set_resolution(sc, 0) ||
+                           pms_set_resolution(sc, 0) ||
+                           pms_set_resolution(sc, 0) ||
+                           pms_set_resolution(sc, 0) ||
+                           pms_get_status(sc, resp) ||
+                           resp[1] == SYNAPTICS_ID_MAGIC)
+                               break;
+                       delay(100000);
+               }
+               if (i == 0) {
+                       printf("%s: lost device\n", DEVNAME(sc));
+                       goto err;
+               }
+       }
 
        if (sc->synaptics == NULL) {
                sc->synaptics = syn = malloc(sizeof(struct synaptics_softc),

Reply via email to