summaryrefslogtreecommitdiffstats
path: root/sys/i386/isa/psm.c
diff options
context:
space:
mode:
authoryokota <yokota@FreeBSD.org>1997-06-30 12:52:57 +0000
committeryokota <yokota@FreeBSD.org>1997-06-30 12:52:57 +0000
commit4590227899e89f0079d5d0a7d3aadcd23953fbb8 (patch)
tree800132d5c2b13b2fa2094c0edb242aca06240429 /sys/i386/isa/psm.c
parentf3aa4e706ec9df10aba0a7e026f660eee66f5b29 (diff)
downloadFreeBSD-src-4590227899e89f0079d5d0a7d3aadcd23953fbb8.zip
FreeBSD-src-4590227899e89f0079d5d0a7d3aadcd23953fbb8.tar.gz
Add experimental APM support for some laptops.
If the configuration option PSM_HOOKAPM is defined and the APM device is available, the psm driver will issue the ENABLE command to the pointing device at the resume APM event if the device was open when the system went into suspended mode. If the option PSM_RESETAFTERSUSPEND is specified in addition to PSM_HOOKAPM, the driver will try to reset the pointing device before sending the ENABLE command. Built-in PS/2-type pointing devices in some laptops (all the reports I heard were about Toshiba models) sometimes don't work immediately after the system is resumed. The device MAY become available after a while. The system may exhibit the same symptom in other OS's too (no, FreeBSD is not the only OS that is suffering :-). I don't know the correct way of solving this yet, but it's been reported that issuing the ENABLE command after resumption wakes up the pointing device. Without PSM_HOOKAPM, the psm driver behaves in the same way as before. Problem reported in the bsd-nomads mailing list in Japan.
Diffstat (limited to 'sys/i386/isa/psm.c')
-rw-r--r--sys/i386/isa/psm.c289
1 files changed, 220 insertions, 69 deletions
diff --git a/sys/i386/isa/psm.c b/sys/i386/isa/psm.c
index 5e28708..0af20b9 100644
--- a/sys/i386/isa/psm.c
+++ b/sys/i386/isa/psm.c
@@ -19,7 +19,7 @@
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
- * $Id: psm.c,v 1.38 1997/03/23 03:34:56 bde Exp $
+ * $Id: psm.c,v 1.39 1997/05/27 11:55:41 yokota Exp $
*/
/*
@@ -58,6 +58,7 @@
*/
#include "psm.h"
+#include "apm.h"
#include "opt_psm.h"
#if NPSM > 0
@@ -77,6 +78,7 @@
#include <i386/include/mouse.h>
#include <i386/include/clock.h>
+#include <i386/include/apm_bios.h>
#include <i386/isa/isa.h>
#include <i386/isa/isa_device.h>
@@ -102,6 +104,17 @@
/* #define PSM_EMULATION enables protocol emulation */
+/* #define PSM_HOOKAPM hook the APM resume event */
+/* #define PSM_RESETAFTERSUSPEND reset the device at the resume event */
+
+#if NAPM <= 0
+#undef PSM_HOOKAPM
+#endif /* NAPM */
+
+#ifndef PSM_HOOKAPM
+#undef PSM_RESETAFTERSUSPEND
+#endif /* PSM_HOOKAPM */
+
/* end of driver specific options */
/* some macros */
@@ -149,6 +162,9 @@ static struct psm_softc { /* Driver status information */
void *devfs_token;
void *n_devfs_token;
#endif
+#ifdef PSM_HOOKAPM
+ struct apmhook resumehook;
+#endif
} *psm_softc[NPSM];
/* driver state flags (state) */
@@ -159,11 +175,10 @@ static struct psm_softc { /* Driver status information */
/* function prototypes */
static int psmprobe __P((struct isa_device *));
static int psmattach __P((struct isa_device *));
-static int mkms __P((unsigned char *, int *, int, mousestatus_t *));
-static int mkman __P((unsigned char *, int *, int, mousestatus_t *));
-static int mkmsc __P((unsigned char *, int *, int, mousestatus_t *));
-static int mkmm __P((unsigned char *, int *, int, mousestatus_t *));
-static int mkps2 __P((unsigned char *, int *, int, mousestatus_t *));
+static void psm_drvinit __P((void *));
+#ifdef PSM_HOOKAPM
+static int psmresume __P((void *));
+#endif
static d_open_t psmopen;
static d_close_t psmclose;
@@ -171,6 +186,26 @@ static d_read_t psmread;
static d_ioctl_t psmioctl;
static d_select_t psmselect;
+static int enable_aux_dev __P((KBDC));
+static int disable_aux_dev __P((KBDC));
+static int get_mouse_status __P((KBDC, int *));
+static int get_aux_id __P((KBDC));
+static int set_mouse_sampling_rate __P((KBDC, int));
+static int set_mouse_scaling __P((KBDC));
+static int set_mouse_resolution __P((KBDC, int));
+static int set_mouse_mode __P((KBDC));
+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 mkms __P((unsigned char *, int *, int, mousestatus_t *));
+static int mkman __P((unsigned char *, int *, int, mousestatus_t *));
+static int mkmsc __P((unsigned char *, int *, int, mousestatus_t *));
+static int mkmm __P((unsigned char *, int *, int, mousestatus_t *));
+static int mkps2 __P((unsigned char *, int *, int, mousestatus_t *));
+
/* device driver declarateion */
struct isa_driver psmdriver = { psmprobe, psmattach, "psm", FALSE };
#define CDEV_MAJOR 21
@@ -403,9 +438,9 @@ restore_controller(KBDC kbdc, int command_byte)
* calling this routine.
*/
static int
-reinitialize(dev_t dev, mousemode_t *mode)
+reinitialize(int unit, mousemode_t *mode)
{
- KBDC kbdc = psm_softc[PSM_UNIT(dev)]->kbdc;
+ KBDC kbdc = psm_softc[unit]->kbdc;
int stat[3];
int i;
@@ -413,7 +448,7 @@ reinitialize(dev_t dev, mousemode_t *mode)
case 1: /* ignore this error */
if (verbose)
log(LOG_DEBUG, "psm%d: strange result for test aux port (%d).\n",
- PSM_UNIT(dev), i);
+ unit, i);
/* fall though */
case 0: /* no error */
break;
@@ -421,7 +456,7 @@ reinitialize(dev_t dev, mousemode_t *mode)
default: /* error */
recover_from_error(kbdc);
log(LOG_ERR, "psm%d: the aux port is not functioning (%d).\n",
- PSM_UNIT(dev),i);
+ unit,i);
return FALSE;
}
@@ -431,8 +466,7 @@ reinitialize(dev_t dev, mousemode_t *mode)
*/
if (!reset_aux_dev(kbdc)) {
recover_from_error(kbdc);
- log(LOG_ERR, "psm%d: failed to reset the aux device.\n",
- PSM_UNIT(dev));
+ log(LOG_ERR, "psm%d: failed to reset the aux device.\n", unit);
return FALSE;
}
@@ -441,8 +475,7 @@ reinitialize(dev_t dev, 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",
- PSM_UNIT(dev));
+ log(LOG_ERR, "psm%d: failed to enable the aux device.\n", unit);
return FALSE;
}
empty_both_buffers(kbdc, 10); /* remove stray data if any */
@@ -460,15 +493,69 @@ reinitialize(dev_t dev, mousemode_t *mode)
/* just check the status of the mouse */
i = get_mouse_status(kbdc, stat);
if (!i) {
- log(LOG_DEBUG, "psm%d: failed to get status.\n", PSM_UNIT(dev));
+ log(LOG_DEBUG, "psm%d: failed to get status.\n", unit);
} else if (verbose) {
- log(LOG_DEBUG, "psm%d: status %02x %02x %02x\n",
- PSM_UNIT(dev), stat[0], stat[1], stat[2]);
+ log(LOG_DEBUG, "psm%d: status %02x %02x %02x\n", unit,
+ stat[0], stat[1], stat[2]);
}
return TRUE;
}
+static int
+doopen(int unit, int command_byte)
+{
+ struct psm_softc *sc = psm_softc[unit];
+ int stat[3];
+
+ /* enable the mouse device */
+ if (!enable_aux_dev(sc->kbdc)) {
+ /* MOUSE ERROR: failed to enable the mouse because:
+ * 1) the mouse is faulty,
+ * 2) the mouse has been removed(!?)
+ * In the latter case, the keyboard may have hung, and need
+ * recovery procedure...
+ */
+ recover_from_error(sc->kbdc);
+#if 0
+ /* FIXME: we could reset the mouse here and try to enable
+ * 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)) {
+ if (!enable_aux_dev(sc->kbdc))
+ recover_from_error(sc->kbdc);
+#else
+ {
+#endif
+ restore_controller(sc->kbdc, 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);
+ return (EIO);
+ }
+ }
+
+ if (!get_mouse_status(sc->kbdc, stat))
+ log(LOG_DEBUG, "psm%d: failed to get status (doopen).\n", unit);
+
+ /* enable the aux port and interrupt */
+ if (!set_controller_command_byte(sc->kbdc,
+ kbdc_get_device_mask(sc->kbdc),
+ (command_byte & KBD_KBD_CONTROL_BITS)
+ | KBD_ENABLE_AUX_PORT | KBD_ENABLE_AUX_INT)) {
+ /* CONTROLLER ERROR */
+ 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);
+ return (EIO);
+ }
+
+ return (0);
+}
+
/* psm driver entry points */
#define endprobe(v) { if (bootverbose) \
@@ -744,7 +831,17 @@ psmattach(struct isa_device *dvp)
sc->n_devfs_token =
devfs_add_devswf(&psm_cdevsw, PSM_MKMINOR(unit, FALSE),
DV_CHR, 0, 0, 0666, "npsm%d", unit);
-#endif
+#endif /* DEVFS */
+
+#ifdef PSM_HOOKAPM
+ sc->resumehook.ah_name = "PS/2 mouse";
+ sc->resumehook.ah_fun = psmresume;
+ sc->resumehook.ah_arg = (void *)unit;
+ sc->resumehook.ah_order = APM_MID_ORDER;
+ apm_hook_establish(APM_HOOK_RESUME , &sc->resumehook);
+ if (verbose)
+ printf("psm%d: APM hooks installed.\n", unit);
+#endif /* PSM_HOOKAPM */
if (verbose)
printf("psm%d: device ID %d, %d buttons\n",
@@ -763,9 +860,8 @@ psmopen(dev_t dev, int flag, int fmt, struct proc *p)
{
int unit = PSM_UNIT(dev);
struct psm_softc *sc;
- int stat[3];
int command_byte;
- int ret;
+ int err;
int s;
/* Validate unit number */
@@ -828,57 +924,13 @@ psmopen(dev_t dev, int flag, int fmt, struct proc *p)
splx(s);
/* enable the mouse device */
- if (!enable_aux_dev(sc->kbdc)) {
- /* MOUSE ERROR: failed to enable the mouse because:
- * 1) the mouse is faulty,
- * 2) the mouse has been removed(!?)
- * In the latter case, the keyboard may have hung, and need
- * recovery procedure...
- */
- recover_from_error(sc->kbdc);
-#if 0
- /* FIXME: we could reset the mouse here and try to enable
- * it again. But it will take long time and it's not a good
- * idea to disable the keyboard that long...
- */
- if (!reinitialize(dev, &sc->mode) || !enable_aux_dev(sc->kbdc))
- recover_from_error(sc->kbdc);
-#endif
- restore_controller(sc->kbdc, command_byte);
- /* mark this device is no longer available */
- sc->state &= ~PSM_VALID;
- kbdc_lock(sc->kbdc, FALSE);
- log(LOG_ERR, "psm%d: failed to enable the device (psmopen).\n",
- unit);
- return (EIO);
- }
-
- ret = get_mouse_status(sc->kbdc, stat);
- if (!ret) {
- log(LOG_DEBUG, "psm%d: failed to get status (psmopen).\n", unit);
- } else if (verbose >= 2) {
- log(LOG_DEBUG, "psm%d: status %02x %02x %02x (psmopen)\n",
- unit, stat[0], stat[1], stat[2]);
- }
-
- /* enable the aux port and interrupt */
- if (!set_controller_command_byte(sc->kbdc,
- kbdc_get_device_mask(sc->kbdc),
- (command_byte & KBD_KBD_CONTROL_BITS)
- | KBD_ENABLE_AUX_PORT | KBD_ENABLE_AUX_INT)) {
- /* CONTROLLER ERROR */
- disable_aux_dev(sc->kbdc);
- restore_controller(sc->kbdc, command_byte);
- kbdc_lock(sc->kbdc, FALSE);
- log(LOG_ERR, "psm%d: failed to enable the aux interrupt (psmopen).\n",
- unit);
- return (EIO);
- }
+ err = doopen(unit, command_byte);
/* done */
- sc->state |= PSM_OPEN;
+ if (err == 0)
+ sc->state |= PSM_OPEN;
kbdc_lock(sc->kbdc, FALSE);
- return (0);
+ return (err);
}
static int
@@ -958,7 +1010,7 @@ psmclose(dev_t dev, int flag, int fmt, struct proc *p)
empty_aux_buffer(sc->kbdc, 10);
/* close is almost always successful */
- sc->state &= ~PSM_OPEN;
+ sc->state &= ~(PSM_OPEN | PSM_ASLP);
kbdc_lock(sc->kbdc, FALSE);
return (0);
}
@@ -1576,6 +1628,105 @@ psm_drvinit(void *unused)
}
}
+#ifdef PSM_HOOKAPM
+static int
+psmresume(void *dummy)
+{
+ struct psm_softc *sc = psm_softc[(int)dummy];
+ int unit = (int)dummy;
+ int err = 0;
+ int s;
+ int c;
+
+ if (verbose >= 2)
+ log(LOG_NOTICE, "psm%d: APM resume hook called.\n", unit);
+
+ /* don't let anybody mess with the aux device */
+ if (!kbdc_lock(sc->kbdc, TRUE))
+ return (EIO);
+ s = spltty();
+
+ /* 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;
+
+#ifdef PSM_RESETAFTERSUSPEND
+ /* try to detect the aux device; are you still there? */
+ 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;
+ }
+#endif /* PSM_RESETAFTERSUSPEND */
+ 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
+ * cannot be accessed anymore.
+ */
+ sc->state &= ~PSM_ASLP;
+ wakeup((caddr_t)sc);
+ }
+
+ if (verbose >= 2)
+ log(LOG_DEBUG, "psm%d: APM resume hook exiting.\n", unit);
+
+ return (err);
+}
+#endif /* PSM_HOOKAPM */
+
SYSINIT(psmdev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE + CDEV_MAJOR, psm_drvinit, NULL)
#endif /* NPSM > 0 */
OpenPOWER on IntegriCloud