diff options
-rw-r--r-- | sys/dev/atkbdc/psm.c | 261 | ||||
-rw-r--r-- | sys/isa/psm.c | 261 |
2 files changed, 304 insertions, 218 deletions
diff --git a/sys/dev/atkbdc/psm.c b/sys/dev/atkbdc/psm.c index 06780395086e..75446f35ec00 100644 --- a/sys/dev/atkbdc/psm.c +++ b/sys/dev/atkbdc/psm.c @@ -74,6 +74,7 @@ #include <machine/bus.h> #include <sys/rman.h> #include <sys/selinfo.h> +#include <sys/time.h> #include <sys/uio.h> #include <machine/limits.h> @@ -93,6 +94,14 @@ #define PSM_DEBUG 0 /* logging: 0: none, 1: brief, 2: verbose */ #endif +#ifndef PSM_SYNCERR_THRESHOLD1 +#define PSM_SYNCERR_THRESHOLD1 20 +#endif + +#ifndef PSM_INPUT_TIMEOUT +#define PSM_INPUT_TIMEOUT 2000000 /* 2 sec */ +#endif + /* end of driver specific options */ #define PSM_DRIVER_NAME "psm" @@ -139,6 +148,7 @@ typedef struct ringbuf { /* driver control block */ struct psm_softc { /* Driver status information */ + int unit; struct selinfo rsel; /* Process selecting for Input */ unsigned char state; /* Mouse driver state */ int config; /* driver configuration flags */ @@ -156,6 +166,8 @@ struct psm_softc { /* Driver status information */ int button; /* the latest button state */ int xold; /* previous absolute X position */ int yold; /* previous absolute Y position */ + int syncerrors; + struct timeval inputtimeout; int watchdog; /* watchdog timer flag */ struct callout_handle callout; /* watchdog timer call out */ dev_t dev; @@ -243,8 +255,9 @@ static int get_mouse_buttons __P((KBDC)); static int is_a_mouse __P((int)); static void recover_from_error __P((KBDC)); static int restore_controller __P((KBDC, int)); -static int reinitialize __P((int, mousemode_t *)); -static int doopen __P((int, int)); +static int doinitialize __P((struct psm_softc *, mousemode_t *)); +static int doopen __P((struct psm_softc *, int)); +static int reinitialize __P((struct psm_softc *, int)); static char *model_name __P((int)); static void psmintr __P((void *)); static void psmtimeout __P((void *)); @@ -620,9 +633,8 @@ restore_controller(KBDC kbdc, int command_byte) * calling this routine. */ static int -reinitialize(int unit, mousemode_t *mode) +doinitialize(struct psm_softc *sc, mousemode_t *mode) { - struct psm_softc *sc = PSM_SOFTC(unit); KBDC kbdc = sc->kbdc; int stat[3]; int i; @@ -632,7 +644,7 @@ reinitialize(int unit, mousemode_t *mode) case PSM_ACK: if (verbose) log(LOG_DEBUG, "psm%d: strange result for test aux port (%d).\n", - unit, i); + sc->unit, i); /* fall though */ case 0: /* no error */ break; @@ -642,7 +654,7 @@ reinitialize(int unit, mousemode_t *mode) if (sc->config & PSM_CONFIG_IGNPORTERROR) break; log(LOG_ERR, "psm%d: the aux port is not functioning (%d).\n", - unit, i); + sc->unit, i); return FALSE; } @@ -658,7 +670,7 @@ reinitialize(int unit, mousemode_t *mode) */ if (!reset_aux_dev(kbdc)) { recover_from_error(kbdc); - log(LOG_ERR, "psm%d: failed to reset the aux device.\n", unit); + log(LOG_ERR, "psm%d: failed to reset the aux device.\n", sc->unit); return FALSE; } } @@ -668,7 +680,7 @@ reinitialize(int unit, mousemode_t *mode) * if the device can be enabled. */ if (!enable_aux_dev(kbdc) || !disable_aux_dev(kbdc)) { - log(LOG_ERR, "psm%d: failed to enable the aux device.\n", unit); + log(LOG_ERR, "psm%d: failed to enable the aux device.\n", sc->unit); return FALSE; } empty_both_buffers(kbdc, 10); /* remove stray data if any */ @@ -683,7 +695,7 @@ reinitialize(int unit, mousemode_t *mode) if ((*vendortype[i].probefunc)(sc)) { if (verbose >= 2) log(LOG_ERR, "psm%d: found %s\n", - unit, model_name(vendortype[i].model)); + sc->unit, model_name(vendortype[i].model)); break; } } @@ -704,7 +716,8 @@ reinitialize(int unit, mousemode_t *mode) /* request a data packet and extract sync. bits */ if (get_mouse_status(kbdc, stat, 1, 3) < 3) { - log(LOG_DEBUG, "psm%d: failed to get data (reinitialize).\n", unit); + log(LOG_DEBUG, "psm%d: failed to get data (doinitialize).\n", + sc->unit); sc->mode.syncmask[0] = 0; } else { sc->mode.syncmask[1] = stat[0] & sc->mode.syncmask[0]; /* syncbits */ @@ -714,15 +727,15 @@ reinitialize(int unit, mousemode_t *mode) /* just check the status of the mouse */ if (get_mouse_status(kbdc, stat, 0, 3) < 3) - log(LOG_DEBUG, "psm%d: failed to get status (reinitialize).\n", unit); + log(LOG_DEBUG, "psm%d: failed to get status (doinitialize).\n", + sc->unit); return TRUE; } static int -doopen(int unit, int command_byte) +doopen(struct psm_softc *sc, int command_byte) { - struct psm_softc *sc = PSM_SOFTC(unit); int stat[3]; /* enable the mouse device */ @@ -739,7 +752,7 @@ doopen(int unit, int command_byte) * it again. But it will take long time and it's not a good * idea to disable the keyboard that long... */ - if (!reinitialize(unit, &sc->mode) || !enable_aux_dev(sc->kbdc)) { + if (!doinitialize(sc, &sc->mode) || !enable_aux_dev(sc->kbdc)) { recover_from_error(sc->kbdc); #else { @@ -748,13 +761,13 @@ doopen(int unit, int command_byte) /* mark this device is no longer available */ sc->state &= ~PSM_VALID; log(LOG_ERR, "psm%d: failed to enable the device (doopen).\n", - unit); + sc->unit); return (EIO); } } if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3) - log(LOG_DEBUG, "psm%d: failed to get status (doopen).\n", unit); + log(LOG_DEBUG, "psm%d: failed to get status (doopen).\n", sc->unit); /* enable the aux port and interrupt */ if (!set_controller_command_byte(sc->kbdc, @@ -765,17 +778,103 @@ doopen(int unit, int command_byte) disable_aux_dev(sc->kbdc); restore_controller(sc->kbdc, command_byte); log(LOG_ERR, "psm%d: failed to enable the aux interrupt (doopen).\n", - unit); + sc->unit); return (EIO); } /* start the watchdog timer */ sc->watchdog = FALSE; - sc->callout = timeout(psmtimeout, (void *)(uintptr_t)unit, hz*2); + sc->callout = timeout(psmtimeout, (void *)(uintptr_t)sc, hz*2); return (0); } +static int +reinitialize(struct psm_softc *sc, int doinit) +{ + int err; + int c; + int s; + + /* don't let anybody mess with the aux device */ + if (!kbdc_lock(sc->kbdc, TRUE)) + return (EIO); + s = spltty(); + + /* block our watchdog timer */ + sc->watchdog = FALSE; + untimeout(psmtimeout, (void *)(uintptr_t)sc, sc->callout); + callout_handle_init(&sc->callout); + + /* save the current controller command byte */ + empty_both_buffers(sc->kbdc, 10); + c = get_controller_command_byte(sc->kbdc); + if (verbose >= 2) + log(LOG_DEBUG, "psm%d: current command byte: %04x (reinitialize).\n", + sc->unit, c); + + /* enable the aux port but disable the aux interrupt and the keyboard */ + if ((c == -1) || !set_controller_command_byte(sc->kbdc, + kbdc_get_device_mask(sc->kbdc), + KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT + | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { + /* CONTROLLER ERROR */ + splx(s); + kbdc_lock(sc->kbdc, FALSE); + log(LOG_ERR, "psm%d: unable to set the command byte (reinitialize).\n", + sc->unit); + return (EIO); + } + + /* flush any data */ + if (sc->state & PSM_VALID) { + disable_aux_dev(sc->kbdc); /* this may fail; but never mind... */ + empty_aux_buffer(sc->kbdc, 10); + } + sc->inputbytes = 0; + sc->syncerrors = 0; + + /* try to detect the aux device; are you still there? */ + err = 0; + if (doinit) { + if (doinitialize(sc, &sc->mode)) { + /* yes */ + sc->state |= PSM_VALID; + } else { + /* the device has gone! */ + restore_controller(sc->kbdc, c); + sc->state &= ~PSM_VALID; + log(LOG_ERR, "psm%d: the aux device has gone! (reinitialize).\n", + sc->unit); + err = ENXIO; + } + } + splx(s); + + /* restore the driver state */ + if ((sc->state & PSM_OPEN) && (err == 0)) { + /* enable the aux device and the port again */ + err = doopen(sc, c); + if (err != 0) + log(LOG_ERR, "psm%d: failed to enable the device (reinitialize).\n", + sc->unit); + } else { + /* restore the keyboard port and disable the aux port */ + if (!set_controller_command_byte(sc->kbdc, + kbdc_get_device_mask(sc->kbdc), + (c & KBD_KBD_CONTROL_BITS) + | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { + /* CONTROLLER ERROR */ + log(LOG_ERR, "psm%d: failed to disable the aux port (reinitialize).\n", + sc->unit); + err = EIO; + } + } + + kbdc_lock(sc->kbdc, FALSE); + return (err); +} + /* psm driver entry points */ static void @@ -845,6 +944,7 @@ psmprobe(device_t dev) } bus_release_resource(dev, SYS_RES_IRQ, rid, sc->intr); + sc->unit = unit; sc->kbdc = atkbdc_open(device_get_unit(device_get_parent(dev))); sc->config = device_get_flags(dev) & PSM_CONFIG_FLAGS; /* XXX: for backward compatibility */ @@ -1235,6 +1335,7 @@ psmopen(dev_t dev, int flag, int fmt, struct thread *td) /* empty input buffer */ bzero(sc->ipacket, sizeof(sc->ipacket)); sc->inputbytes = 0; + sc->syncerrors = 0; /* don't let timeout routines in the keyboard driver to poll the kbdc */ if (!kbdc_lock(sc->kbdc, TRUE)) @@ -1267,7 +1368,7 @@ psmopen(dev_t dev, int flag, int fmt, struct thread *td) splx(s); /* enable the mouse device */ - err = doopen(unit, command_byte); + err = doopen(sc, command_byte); /* done */ if (err == 0) @@ -1315,7 +1416,7 @@ psmclose(dev_t dev, int flag, int fmt, struct thread *td) splx(s); /* stop the watchdog timer */ - untimeout(psmtimeout, (void *)(uintptr_t)unit, sc->callout); + untimeout(psmtimeout, (void *)(uintptr_t)sc, sc->callout); callout_handle_init(&sc->callout); /* remove anything left in the output buffer */ @@ -1843,21 +1944,19 @@ static void psmtimeout(void *arg) { struct psm_softc *sc; - int unit; int s; - unit = (int)(uintptr_t)arg; - sc = devclass_get_softc(psm_devclass, unit); + sc = (struct psm_softc *)arg; s = spltty(); if (sc->watchdog && kbdc_lock(sc->kbdc, TRUE)) { if (verbose >= 4) - log(LOG_DEBUG, "psm%d: lost interrupt?\n", unit); + log(LOG_DEBUG, "psm%d: lost interrupt?\n", sc->unit); psmintr(sc); kbdc_lock(sc->kbdc, FALSE); } sc->watchdog = TRUE; splx(s); - sc->callout = timeout(psmtimeout, (void *)(uintptr_t)unit, hz); + sc->callout = timeout(psmtimeout, (void *)(uintptr_t)sc, hz); } static void @@ -1889,6 +1988,7 @@ psmintr(void *arg) }; register struct psm_softc *sc = arg; mousestatus_t ms; + struct timeval tv; int x, y, z; int c; int l; @@ -1901,6 +2001,16 @@ psmintr(void *arg) if ((sc->state & PSM_OPEN) == 0) continue; + getmicrouptime(&tv); + if ((sc->inputbytes > 0) && timevalcmp(&tv, &sc->inputtimeout, >)) { + log(LOG_DEBUG, "psmintr: delay too long; resetting byte count\n"); + sc->inputbytes = 0; + sc->syncerrors = 0; + } + sc->inputtimeout.tv_sec = PSM_INPUT_TIMEOUT/1000000; + sc->inputtimeout.tv_usec = PSM_INPUT_TIMEOUT%1000000; + timevaladd(&sc->inputtimeout, &tv); + sc->ipacket[sc->inputbytes++] = c; if (sc->inputbytes < sc->mode.packetsize) continue; @@ -1916,17 +2026,25 @@ psmintr(void *arg) if ((c & sc->mode.syncmask[0]) != sc->mode.syncmask[1]) { log(LOG_DEBUG, "psmintr: out of sync (%04x != %04x).\n", c & sc->mode.syncmask[0], sc->mode.syncmask[1]); - sc->inputbytes = 0; - if (sc->config & PSM_CONFIG_SYNCHACK) { - /* - * XXX: this is a grotesque hack to get us out of - * dreaded "out of sync" error. - */ + ++sc->syncerrors; + if (sc->syncerrors < sc->mode.packetsize) { + log(LOG_DEBUG, "psmintr: discard a byte (%d).\n", sc->syncerrors); + --sc->inputbytes; + bcopy(&sc->ipacket[1], &sc->ipacket[0], sc->inputbytes); + } else if (sc->syncerrors == sc->mode.packetsize) { log(LOG_DEBUG, "psmintr: re-enable the mouse.\n"); + sc->inputbytes = 0; disable_aux_dev(sc->kbdc); enable_aux_dev(sc->kbdc); + } else if (sc->syncerrors < PSM_SYNCERR_THRESHOLD1) { + log(LOG_DEBUG, "psmintr: discard a byte (%d).\n", sc->syncerrors); + --sc->inputbytes; + bcopy(&sc->ipacket[1], &sc->ipacket[0], sc->inputbytes); + } else if (sc->syncerrors >= PSM_SYNCERR_THRESHOLD1) { + log(LOG_DEBUG, "psmintr: reset the mouse.\n"); + reinitialize(sc, TRUE); } - continue; + continue; } /* @@ -2698,9 +2816,7 @@ psmresume(device_t dev) { struct psm_softc *sc = device_get_softc(dev); int unit = device_get_unit(dev); - int err = 0; - int s; - int c; + int err; if (verbose >= 2) log(LOG_NOTICE, "psm%d: system resume hook called.\n", unit); @@ -2708,81 +2824,8 @@ psmresume(device_t dev) if (!(sc->config & PSM_CONFIG_HOOKRESUME)) return (0); - /* don't let anybody mess with the aux device */ - if (!kbdc_lock(sc->kbdc, TRUE)) - return (EIO); - s = spltty(); - - /* block our watchdog timer */ - sc->watchdog = FALSE; - untimeout(psmtimeout, (void *)(uintptr_t)unit, sc->callout); - callout_handle_init(&sc->callout); - - /* save the current controller command byte */ - empty_both_buffers(sc->kbdc, 10); - c = get_controller_command_byte(sc->kbdc); - if (verbose >= 2) - log(LOG_DEBUG, "psm%d: current command byte: %04x (psmresume).\n", - unit, c); - - /* enable the aux port but disable the aux interrupt and the keyboard */ - if ((c == -1) || !set_controller_command_byte(sc->kbdc, - kbdc_get_device_mask(sc->kbdc), - KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT - | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { - /* CONTROLLER ERROR */ - splx(s); - kbdc_lock(sc->kbdc, FALSE); - log(LOG_ERR, "psm%d: unable to set the command byte (psmresume).\n", - unit); - return (EIO); - } - - /* flush any data */ - if (sc->state & PSM_VALID) { - disable_aux_dev(sc->kbdc); /* this may fail; but never mind... */ - empty_aux_buffer(sc->kbdc, 10); - } - sc->inputbytes = 0; - - /* try to detect the aux device; are you still there? */ - if (sc->config & PSM_CONFIG_INITAFTERSUSPEND) { - if (reinitialize(unit, &sc->mode)) { - /* yes */ - sc->state |= PSM_VALID; - } else { - /* the device has gone! */ - restore_controller(sc->kbdc, c); - sc->state &= ~PSM_VALID; - log(LOG_ERR, "psm%d: the aux device has gone! (psmresume).\n", - unit); - err = ENXIO; - } - } - splx(s); - - /* restore the driver state */ - if ((sc->state & PSM_OPEN) && (err == 0)) { - /* enable the aux device and the port again */ - err = doopen(unit, c); - if (err != 0) - log(LOG_ERR, "psm%d: failed to enable the device (psmresume).\n", - unit); - } else { - /* restore the keyboard port and disable the aux port */ - if (!set_controller_command_byte(sc->kbdc, - kbdc_get_device_mask(sc->kbdc), - (c & KBD_KBD_CONTROL_BITS) - | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { - /* CONTROLLER ERROR */ - log(LOG_ERR, "psm%d: failed to disable the aux port (psmresume).\n", - unit); - err = EIO; - } - } + err = reinitialize(sc, sc->config & PSM_CONFIG_INITAFTERSUSPEND); - /* done */ - kbdc_lock(sc->kbdc, FALSE); if ((sc->state & PSM_ASLP) && !(sc->state & PSM_VALID)) { /* * Release the blocked process; it must be notified that the device diff --git a/sys/isa/psm.c b/sys/isa/psm.c index 06780395086e..75446f35ec00 100644 --- a/sys/isa/psm.c +++ b/sys/isa/psm.c @@ -74,6 +74,7 @@ #include <machine/bus.h> #include <sys/rman.h> #include <sys/selinfo.h> +#include <sys/time.h> #include <sys/uio.h> #include <machine/limits.h> @@ -93,6 +94,14 @@ #define PSM_DEBUG 0 /* logging: 0: none, 1: brief, 2: verbose */ #endif +#ifndef PSM_SYNCERR_THRESHOLD1 +#define PSM_SYNCERR_THRESHOLD1 20 +#endif + +#ifndef PSM_INPUT_TIMEOUT +#define PSM_INPUT_TIMEOUT 2000000 /* 2 sec */ +#endif + /* end of driver specific options */ #define PSM_DRIVER_NAME "psm" @@ -139,6 +148,7 @@ typedef struct ringbuf { /* driver control block */ struct psm_softc { /* Driver status information */ + int unit; struct selinfo rsel; /* Process selecting for Input */ unsigned char state; /* Mouse driver state */ int config; /* driver configuration flags */ @@ -156,6 +166,8 @@ struct psm_softc { /* Driver status information */ int button; /* the latest button state */ int xold; /* previous absolute X position */ int yold; /* previous absolute Y position */ + int syncerrors; + struct timeval inputtimeout; int watchdog; /* watchdog timer flag */ struct callout_handle callout; /* watchdog timer call out */ dev_t dev; @@ -243,8 +255,9 @@ static int get_mouse_buttons __P((KBDC)); static int is_a_mouse __P((int)); static void recover_from_error __P((KBDC)); static int restore_controller __P((KBDC, int)); -static int reinitialize __P((int, mousemode_t *)); -static int doopen __P((int, int)); +static int doinitialize __P((struct psm_softc *, mousemode_t *)); +static int doopen __P((struct psm_softc *, int)); +static int reinitialize __P((struct psm_softc *, int)); static char *model_name __P((int)); static void psmintr __P((void *)); static void psmtimeout __P((void *)); @@ -620,9 +633,8 @@ restore_controller(KBDC kbdc, int command_byte) * calling this routine. */ static int -reinitialize(int unit, mousemode_t *mode) +doinitialize(struct psm_softc *sc, mousemode_t *mode) { - struct psm_softc *sc = PSM_SOFTC(unit); KBDC kbdc = sc->kbdc; int stat[3]; int i; @@ -632,7 +644,7 @@ reinitialize(int unit, mousemode_t *mode) case PSM_ACK: if (verbose) log(LOG_DEBUG, "psm%d: strange result for test aux port (%d).\n", - unit, i); + sc->unit, i); /* fall though */ case 0: /* no error */ break; @@ -642,7 +654,7 @@ reinitialize(int unit, mousemode_t *mode) if (sc->config & PSM_CONFIG_IGNPORTERROR) break; log(LOG_ERR, "psm%d: the aux port is not functioning (%d).\n", - unit, i); + sc->unit, i); return FALSE; } @@ -658,7 +670,7 @@ reinitialize(int unit, mousemode_t *mode) */ if (!reset_aux_dev(kbdc)) { recover_from_error(kbdc); - log(LOG_ERR, "psm%d: failed to reset the aux device.\n", unit); + log(LOG_ERR, "psm%d: failed to reset the aux device.\n", sc->unit); return FALSE; } } @@ -668,7 +680,7 @@ reinitialize(int unit, mousemode_t *mode) * if the device can be enabled. */ if (!enable_aux_dev(kbdc) || !disable_aux_dev(kbdc)) { - log(LOG_ERR, "psm%d: failed to enable the aux device.\n", unit); + log(LOG_ERR, "psm%d: failed to enable the aux device.\n", sc->unit); return FALSE; } empty_both_buffers(kbdc, 10); /* remove stray data if any */ @@ -683,7 +695,7 @@ reinitialize(int unit, mousemode_t *mode) if ((*vendortype[i].probefunc)(sc)) { if (verbose >= 2) log(LOG_ERR, "psm%d: found %s\n", - unit, model_name(vendortype[i].model)); + sc->unit, model_name(vendortype[i].model)); break; } } @@ -704,7 +716,8 @@ reinitialize(int unit, mousemode_t *mode) /* request a data packet and extract sync. bits */ if (get_mouse_status(kbdc, stat, 1, 3) < 3) { - log(LOG_DEBUG, "psm%d: failed to get data (reinitialize).\n", unit); + log(LOG_DEBUG, "psm%d: failed to get data (doinitialize).\n", + sc->unit); sc->mode.syncmask[0] = 0; } else { sc->mode.syncmask[1] = stat[0] & sc->mode.syncmask[0]; /* syncbits */ @@ -714,15 +727,15 @@ reinitialize(int unit, mousemode_t *mode) /* just check the status of the mouse */ if (get_mouse_status(kbdc, stat, 0, 3) < 3) - log(LOG_DEBUG, "psm%d: failed to get status (reinitialize).\n", unit); + log(LOG_DEBUG, "psm%d: failed to get status (doinitialize).\n", + sc->unit); return TRUE; } static int -doopen(int unit, int command_byte) +doopen(struct psm_softc *sc, int command_byte) { - struct psm_softc *sc = PSM_SOFTC(unit); int stat[3]; /* enable the mouse device */ @@ -739,7 +752,7 @@ doopen(int unit, int command_byte) * it again. But it will take long time and it's not a good * idea to disable the keyboard that long... */ - if (!reinitialize(unit, &sc->mode) || !enable_aux_dev(sc->kbdc)) { + if (!doinitialize(sc, &sc->mode) || !enable_aux_dev(sc->kbdc)) { recover_from_error(sc->kbdc); #else { @@ -748,13 +761,13 @@ doopen(int unit, int command_byte) /* mark this device is no longer available */ sc->state &= ~PSM_VALID; log(LOG_ERR, "psm%d: failed to enable the device (doopen).\n", - unit); + sc->unit); return (EIO); } } if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3) - log(LOG_DEBUG, "psm%d: failed to get status (doopen).\n", unit); + log(LOG_DEBUG, "psm%d: failed to get status (doopen).\n", sc->unit); /* enable the aux port and interrupt */ if (!set_controller_command_byte(sc->kbdc, @@ -765,17 +778,103 @@ doopen(int unit, int command_byte) disable_aux_dev(sc->kbdc); restore_controller(sc->kbdc, command_byte); log(LOG_ERR, "psm%d: failed to enable the aux interrupt (doopen).\n", - unit); + sc->unit); return (EIO); } /* start the watchdog timer */ sc->watchdog = FALSE; - sc->callout = timeout(psmtimeout, (void *)(uintptr_t)unit, hz*2); + sc->callout = timeout(psmtimeout, (void *)(uintptr_t)sc, hz*2); return (0); } +static int +reinitialize(struct psm_softc *sc, int doinit) +{ + int err; + int c; + int s; + + /* don't let anybody mess with the aux device */ + if (!kbdc_lock(sc->kbdc, TRUE)) + return (EIO); + s = spltty(); + + /* block our watchdog timer */ + sc->watchdog = FALSE; + untimeout(psmtimeout, (void *)(uintptr_t)sc, sc->callout); + callout_handle_init(&sc->callout); + + /* save the current controller command byte */ + empty_both_buffers(sc->kbdc, 10); + c = get_controller_command_byte(sc->kbdc); + if (verbose >= 2) + log(LOG_DEBUG, "psm%d: current command byte: %04x (reinitialize).\n", + sc->unit, c); + + /* enable the aux port but disable the aux interrupt and the keyboard */ + if ((c == -1) || !set_controller_command_byte(sc->kbdc, + kbdc_get_device_mask(sc->kbdc), + KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT + | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { + /* CONTROLLER ERROR */ + splx(s); + kbdc_lock(sc->kbdc, FALSE); + log(LOG_ERR, "psm%d: unable to set the command byte (reinitialize).\n", + sc->unit); + return (EIO); + } + + /* flush any data */ + if (sc->state & PSM_VALID) { + disable_aux_dev(sc->kbdc); /* this may fail; but never mind... */ + empty_aux_buffer(sc->kbdc, 10); + } + sc->inputbytes = 0; + sc->syncerrors = 0; + + /* try to detect the aux device; are you still there? */ + err = 0; + if (doinit) { + if (doinitialize(sc, &sc->mode)) { + /* yes */ + sc->state |= PSM_VALID; + } else { + /* the device has gone! */ + restore_controller(sc->kbdc, c); + sc->state &= ~PSM_VALID; + log(LOG_ERR, "psm%d: the aux device has gone! (reinitialize).\n", + sc->unit); + err = ENXIO; + } + } + splx(s); + + /* restore the driver state */ + if ((sc->state & PSM_OPEN) && (err == 0)) { + /* enable the aux device and the port again */ + err = doopen(sc, c); + if (err != 0) + log(LOG_ERR, "psm%d: failed to enable the device (reinitialize).\n", + sc->unit); + } else { + /* restore the keyboard port and disable the aux port */ + if (!set_controller_command_byte(sc->kbdc, + kbdc_get_device_mask(sc->kbdc), + (c & KBD_KBD_CONTROL_BITS) + | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { + /* CONTROLLER ERROR */ + log(LOG_ERR, "psm%d: failed to disable the aux port (reinitialize).\n", + sc->unit); + err = EIO; + } + } + + kbdc_lock(sc->kbdc, FALSE); + return (err); +} + /* psm driver entry points */ static void @@ -845,6 +944,7 @@ psmprobe(device_t dev) } bus_release_resource(dev, SYS_RES_IRQ, rid, sc->intr); + sc->unit = unit; sc->kbdc = atkbdc_open(device_get_unit(device_get_parent(dev))); sc->config = device_get_flags(dev) & PSM_CONFIG_FLAGS; /* XXX: for backward compatibility */ @@ -1235,6 +1335,7 @@ psmopen(dev_t dev, int flag, int fmt, struct thread *td) /* empty input buffer */ bzero(sc->ipacket, sizeof(sc->ipacket)); sc->inputbytes = 0; + sc->syncerrors = 0; /* don't let timeout routines in the keyboard driver to poll the kbdc */ if (!kbdc_lock(sc->kbdc, TRUE)) @@ -1267,7 +1368,7 @@ psmopen(dev_t dev, int flag, int fmt, struct thread *td) splx(s); /* enable the mouse device */ - err = doopen(unit, command_byte); + err = doopen(sc, command_byte); /* done */ if (err == 0) @@ -1315,7 +1416,7 @@ psmclose(dev_t dev, int flag, int fmt, struct thread *td) splx(s); /* stop the watchdog timer */ - untimeout(psmtimeout, (void *)(uintptr_t)unit, sc->callout); + untimeout(psmtimeout, (void *)(uintptr_t)sc, sc->callout); callout_handle_init(&sc->callout); /* remove anything left in the output buffer */ @@ -1843,21 +1944,19 @@ static void psmtimeout(void *arg) { struct psm_softc *sc; - int unit; int s; - unit = (int)(uintptr_t)arg; - sc = devclass_get_softc(psm_devclass, unit); + sc = (struct psm_softc *)arg; s = spltty(); if (sc->watchdog && kbdc_lock(sc->kbdc, TRUE)) { if (verbose >= 4) - log(LOG_DEBUG, "psm%d: lost interrupt?\n", unit); + log(LOG_DEBUG, "psm%d: lost interrupt?\n", sc->unit); psmintr(sc); kbdc_lock(sc->kbdc, FALSE); } sc->watchdog = TRUE; splx(s); - sc->callout = timeout(psmtimeout, (void *)(uintptr_t)unit, hz); + sc->callout = timeout(psmtimeout, (void *)(uintptr_t)sc, hz); } static void @@ -1889,6 +1988,7 @@ psmintr(void *arg) }; register struct psm_softc *sc = arg; mousestatus_t ms; + struct timeval tv; int x, y, z; int c; int l; @@ -1901,6 +2001,16 @@ psmintr(void *arg) if ((sc->state & PSM_OPEN) == 0) continue; + getmicrouptime(&tv); + if ((sc->inputbytes > 0) && timevalcmp(&tv, &sc->inputtimeout, >)) { + log(LOG_DEBUG, "psmintr: delay too long; resetting byte count\n"); + sc->inputbytes = 0; + sc->syncerrors = 0; + } + sc->inputtimeout.tv_sec = PSM_INPUT_TIMEOUT/1000000; + sc->inputtimeout.tv_usec = PSM_INPUT_TIMEOUT%1000000; + timevaladd(&sc->inputtimeout, &tv); + sc->ipacket[sc->inputbytes++] = c; if (sc->inputbytes < sc->mode.packetsize) continue; @@ -1916,17 +2026,25 @@ psmintr(void *arg) if ((c & sc->mode.syncmask[0]) != sc->mode.syncmask[1]) { log(LOG_DEBUG, "psmintr: out of sync (%04x != %04x).\n", c & sc->mode.syncmask[0], sc->mode.syncmask[1]); - sc->inputbytes = 0; - if (sc->config & PSM_CONFIG_SYNCHACK) { - /* - * XXX: this is a grotesque hack to get us out of - * dreaded "out of sync" error. - */ + ++sc->syncerrors; + if (sc->syncerrors < sc->mode.packetsize) { + log(LOG_DEBUG, "psmintr: discard a byte (%d).\n", sc->syncerrors); + --sc->inputbytes; + bcopy(&sc->ipacket[1], &sc->ipacket[0], sc->inputbytes); + } else if (sc->syncerrors == sc->mode.packetsize) { log(LOG_DEBUG, "psmintr: re-enable the mouse.\n"); + sc->inputbytes = 0; disable_aux_dev(sc->kbdc); enable_aux_dev(sc->kbdc); + } else if (sc->syncerrors < PSM_SYNCERR_THRESHOLD1) { + log(LOG_DEBUG, "psmintr: discard a byte (%d).\n", sc->syncerrors); + --sc->inputbytes; + bcopy(&sc->ipacket[1], &sc->ipacket[0], sc->inputbytes); + } else if (sc->syncerrors >= PSM_SYNCERR_THRESHOLD1) { + log(LOG_DEBUG, "psmintr: reset the mouse.\n"); + reinitialize(sc, TRUE); } - continue; + continue; } /* @@ -2698,9 +2816,7 @@ psmresume(device_t dev) { struct psm_softc *sc = device_get_softc(dev); int unit = device_get_unit(dev); - int err = 0; - int s; - int c; + int err; if (verbose >= 2) log(LOG_NOTICE, "psm%d: system resume hook called.\n", unit); @@ -2708,81 +2824,8 @@ psmresume(device_t dev) if (!(sc->config & PSM_CONFIG_HOOKRESUME)) return (0); - /* don't let anybody mess with the aux device */ - if (!kbdc_lock(sc->kbdc, TRUE)) - return (EIO); - s = spltty(); - - /* block our watchdog timer */ - sc->watchdog = FALSE; - untimeout(psmtimeout, (void *)(uintptr_t)unit, sc->callout); - callout_handle_init(&sc->callout); - - /* save the current controller command byte */ - empty_both_buffers(sc->kbdc, 10); - c = get_controller_command_byte(sc->kbdc); - if (verbose >= 2) - log(LOG_DEBUG, "psm%d: current command byte: %04x (psmresume).\n", - unit, c); - - /* enable the aux port but disable the aux interrupt and the keyboard */ - if ((c == -1) || !set_controller_command_byte(sc->kbdc, - kbdc_get_device_mask(sc->kbdc), - KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT - | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { - /* CONTROLLER ERROR */ - splx(s); - kbdc_lock(sc->kbdc, FALSE); - log(LOG_ERR, "psm%d: unable to set the command byte (psmresume).\n", - unit); - return (EIO); - } - - /* flush any data */ - if (sc->state & PSM_VALID) { - disable_aux_dev(sc->kbdc); /* this may fail; but never mind... */ - empty_aux_buffer(sc->kbdc, 10); - } - sc->inputbytes = 0; - - /* try to detect the aux device; are you still there? */ - if (sc->config & PSM_CONFIG_INITAFTERSUSPEND) { - if (reinitialize(unit, &sc->mode)) { - /* yes */ - sc->state |= PSM_VALID; - } else { - /* the device has gone! */ - restore_controller(sc->kbdc, c); - sc->state &= ~PSM_VALID; - log(LOG_ERR, "psm%d: the aux device has gone! (psmresume).\n", - unit); - err = ENXIO; - } - } - splx(s); - - /* restore the driver state */ - if ((sc->state & PSM_OPEN) && (err == 0)) { - /* enable the aux device and the port again */ - err = doopen(unit, c); - if (err != 0) - log(LOG_ERR, "psm%d: failed to enable the device (psmresume).\n", - unit); - } else { - /* restore the keyboard port and disable the aux port */ - if (!set_controller_command_byte(sc->kbdc, - kbdc_get_device_mask(sc->kbdc), - (c & KBD_KBD_CONTROL_BITS) - | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { - /* CONTROLLER ERROR */ - log(LOG_ERR, "psm%d: failed to disable the aux port (psmresume).\n", - unit); - err = EIO; - } - } + err = reinitialize(sc, sc->config & PSM_CONFIG_INITAFTERSUSPEND); - /* done */ - kbdc_lock(sc->kbdc, FALSE); if ((sc->state & PSM_ASLP) && !(sc->state & PSM_VALID)) { /* * Release the blocked process; it must be notified that the device |