Hack for the "out-of-sync" error.
- Count the number of this error. - When the error is detected for the first time, the psm driver will throw few data bytes (up to entire packet size) and see if it can get back to sync. - If the error still persists, the psm driver disable/enable the mouse and see if it works. - If the error still persists and the count goes up to 20, the psm driver reset and reinitialize the mouse. The counter is reset to zero. - It also discards an incomplete data packet when the interval between two consequtive bytes are longer than pre-defined timeout (2 seconds). The last byte which arrived late will be regarded as the first byte of a new packet. This is louie's idea. You may see the following error logs during the above operations: "psmintr: delay too long; resetting byte count" "psmintr: out of sync (%04x != %04x)" "psmintr: discard a byte (%d)" "psmintr: re-enable the mouse" "psmintr: reset the mouse" MFC after: 1 month
This commit is contained in:
parent
ce9d2b59b2
commit
8166ba27bd
@ -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();
|
||||
err = reinitialize(sc, sc->config & PSM_CONFIG_INITAFTERSUSPEND);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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
|
||||
|
261
sys/isa/psm.c
261
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();
|
||||
err = reinitialize(sc, sc->config & PSM_CONFIG_INITAFTERSUSPEND);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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
|
||||
|
Loading…
Reference in New Issue
Block a user