Index: psm.c =================================================================== RCS file: /src/CVS/src/sys/isa/psm.c,v retrieving revision 1.23.2.5 diff -u -r1.23.2.5 psm.c --- psm.c 30 Jul 2001 16:29:05 -0000 1.23.2.5 +++ psm.c 16 Oct 2001 23:54:30 -0000 @@ -75,6 +75,7 @@ #include #include #include +#include #include #include @@ -95,6 +96,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 */ /* input queue */ @@ -138,6 +147,7 @@ /* 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 */ @@ -155,6 +165,8 @@ 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; @@ -241,8 +253,9 @@ 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 *)); @@ -626,9 +639,8 @@ * 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; @@ -638,7 +650,7 @@ 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; @@ -648,7 +660,7 @@ 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; } @@ -664,7 +676,7 @@ */ 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; } } @@ -674,7 +686,7 @@ * 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 */ @@ -689,7 +701,7 @@ 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; } } @@ -710,7 +722,8 @@ /* 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 */ @@ -720,15 +733,15 @@ /* 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 */ @@ -745,7 +758,7 @@ * 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 { @@ -754,13 +767,13 @@ /* 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, @@ -771,17 +784,103 @@ 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 *)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 */ #define endprobe(v) { if (bootverbose) \ @@ -817,6 +916,7 @@ BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_IRQ, &irq); BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_FLAGS, &flags); + sc->unit = unit; sc->kbdc = atkbdc_open(device_get_unit(device_get_parent(dev))); sc->config = flags & PSM_CONFIG_FLAGS; /* XXX: for backward compatibility */ @@ -1222,6 +1322,7 @@ /* 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)) @@ -1254,7 +1355,7 @@ splx(s); /* enable the mouse device */ - err = doopen(unit, command_byte); + err = doopen(sc, command_byte); /* done */ if (err == 0) @@ -1302,7 +1403,7 @@ splx(s); /* stop the watchdog timer */ - untimeout(psmtimeout, (void *)unit, sc->callout); + untimeout(psmtimeout, (void *)(uintptr_t)sc, sc->callout); callout_handle_init(&sc->callout); /* remove anything left in the output buffer */ @@ -1830,21 +1931,19 @@ psmtimeout(void *arg) { struct psm_softc *sc; - int unit; int s; - unit = (int)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 *)unit, hz); + sc->callout = timeout(psmtimeout, (void *)(uintptr_t)sc, hz); } static void @@ -1876,6 +1975,7 @@ }; register struct psm_softc *sc = arg; mousestatus_t ms; + struct timeval tv; int x, y, z; int c; int l; @@ -1888,6 +1988,16 @@ 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; @@ -1903,17 +2013,25 @@ 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. - */ - log(LOG_DEBUG, "psmintr: re-enable the mouse.\n"); + ++sc->syncerrors; + if (sc->syncerrors < sc->mode.packetsize) { + log(LOG_DEBUG, "psmintr: discarding 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-enabling 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: discarding 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: resetting the mouse.\n"); + reinitialize(sc, TRUE); } - continue; + continue; } /* @@ -2685,9 +2803,7 @@ { 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); @@ -2695,81 +2811,8 @@ 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 *)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