summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorSeth Goldberg <Seth.Goldberg@Sun.COM>2009-07-14 19:07:47 -0700
committerSeth Goldberg <Seth.Goldberg@Sun.COM>2009-07-14 19:07:47 -0700
commit15bfc6b75fa0acab2947977e84e0a9f492aca328 (patch)
tree00c9deec184fffc9b601bc28735e697670078c0d /usr/src
parenteadbb3a2376b1b0c99cf575947d1f40b6c0886fe (diff)
downloadillumos-joyent-15bfc6b75fa0acab2947977e84e0a9f492aca328.tar.gz
6483754 kb8042_cleanup calls ddi_remove_intr/mutex_destroy in wrong order
6591233 Logitech autodetection is problematic for some laptop touchpads 6825031 MSI Netbook touchpad does not work after resume 6833689 Touch Pad on R600 (BIOS 1.80) is not functional when second resume is attempted 6854245 kb8042/mouse8042 debug code cleanup 6854263 Fix for 6848184 is suboptimal 6854265 Remove antiquated definitions from mouse.h 6859386 mouse8042 M_FLUSH handling is wrong
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/uts/common/io/i8042.c385
-rw-r--r--usr/src/uts/common/io/kb8042/at_keyprocess.c46
-rw-r--r--usr/src/uts/common/io/kb8042/kb8042.c410
-rw-r--r--usr/src/uts/common/io/kb8042/kb8042.h5
-rw-r--r--usr/src/uts/common/io/mouse8042.c478
-rw-r--r--usr/src/uts/common/io/vuidmice/vuidps2.c225
-rw-r--r--usr/src/uts/common/sys/i8042.h19
-rw-r--r--usr/src/uts/common/sys/mouse.h119
8 files changed, 768 insertions, 919 deletions
diff --git a/usr/src/uts/common/io/i8042.c b/usr/src/uts/common/io/i8042.c
index a36451680d..23489be575 100644
--- a/usr/src/uts/common/io/i8042.c
+++ b/usr/src/uts/common/io/i8042.c
@@ -147,17 +147,10 @@ struct i8042_port {
int rptr;
int overruns;
unsigned char buf[BUFSIZ];
-
/*
- * Used during i8042_rep_put8 to intercept the 8042 response in
- * i8042_intr()
+ * has_glock is 1 if this child has the [put8] exclusive-access lock.
*/
- boolean_t intercept_complete;
- boolean_t intr_intercept_enabled;
- uint8_t intercept[2];
- uint8_t intercepted_byte;
- kcondvar_t intercept_cv;
- kmutex_t intercept_mutex;
+ volatile boolean_t has_glock;
};
/*
@@ -183,14 +176,16 @@ struct i8042 {
#ifdef __sparc
timeout_id_t timeout_id;
#endif
-#ifdef DEBUG
/*
- * intr_thread is set to curthread in i8042_intr and is
- * tested against curthread in i8402_rep_put8().
+ * glock is 1 if any child has the [put8] exclusive-access lock
+ * glock_cv is associated with the condition `glock == 0'
*/
- kthread_t *intr_thread;
-#endif
- ddi_softint_handle_t intercept_sih;
+ volatile int glock;
+ /*
+ * Callers awaiting exclusive access in i8042_put8 sleep on glock_cv
+ * and are signaled when another child relinquishes exclusive access.
+ */
+ kcondvar_t glock_cv;
};
/*
@@ -401,12 +396,8 @@ static void i8042_write_command_byte(struct i8042 *, unsigned char);
static uint8_t i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr);
static void i8042_put8(ddi_acc_impl_t *handlep, uint8_t *addr,
uint8_t value);
-static void i8042_put8_nolock(ddi_acc_impl_t *handlep, uint8_t *addr,
- uint8_t value);
-static void i8042_rep_put8(ddi_acc_impl_t *handlep, uint8_t *haddr,
- uint8_t *daddr, size_t repcount, uint_t flags);
static void i8042_send(struct i8042 *global, int reg, unsigned char cmd);
-static uint_t i8042_intercept_softint(caddr_t arg1, caddr_t arg2);
+static uint8_t i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr);
unsigned int i8042_unclaimed_interrupts = 0;
@@ -492,8 +483,6 @@ i8042_cleanup(struct i8042 *global)
}
}
- (void) ddi_intr_remove_softint(global->intercept_sih);
-
if (global->init_state & I8042_INIT_MUTEXES) {
for (which_port = 0; which_port < NUM_PORTS; which_port++) {
@@ -501,9 +490,8 @@ i8042_cleanup(struct i8042 *global)
port = &global->i8042_ports[which_port];
mutex_destroy(&port->intr_mutex);
#endif
- mutex_destroy(&port->intercept_mutex);
- cv_destroy(&port->intercept_cv);
}
+ cv_destroy(&global->glock_cv);
mutex_destroy(&global->i8042_out_mutex);
mutex_destroy(&global->i8042_mutex);
}
@@ -685,20 +673,17 @@ i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
mutex_init(&global->i8042_out_mutex, NULL, MUTEX_DRIVER, NULL);
+ cv_init(&global->glock_cv, NULL, CV_DRIVER, NULL);
+
for (which_port = 0; which_port < NUM_PORTS; ++which_port) {
port = &global->i8042_ports[which_port];
port->initialized = B_FALSE;
port->i8042_global = global;
port->which = which_port;
- port->intr_intercept_enabled = B_FALSE;
- cv_init(&port->intercept_cv, NULL, CV_DRIVER, NULL);
#if defined(USE_SOFT_INTRS)
port->soft_hdl = 0;
#else
- mutex_init(&port->intercept_mutex, NULL, MUTEX_DRIVER,
- (void *)DDI_INTR_SOFTPRI_DEFAULT);
-
/*
* Assume that the interrupt block cookie for port <n>
* is iblock_cookies[<n>] (a 1:1 mapping). If there are not
@@ -714,8 +699,6 @@ i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
} else {
mutex_init(&port->intr_mutex, NULL, MUTEX_DRIVER, NULL);
- mutex_init(&port->intercept_mutex, NULL, MUTEX_DRIVER,
- NULL);
}
#endif
@@ -746,11 +729,6 @@ i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
goto fail;
- if (ddi_intr_add_softint(dip, &global->intercept_sih,
- DDI_INTR_SOFTPRI_DEFAULT, i8042_intercept_softint, global)
- != DDI_SUCCESS)
- goto fail;
-
/*
* Assume the number of interrupts is less that the number of
* bits in the variable used to keep track of which interrupt
@@ -938,6 +916,7 @@ i8042_map(
port->rptr = 0;
port->dip = dip;
port->inumber = 0;
+ port->has_glock = B_FALSE;
port->initialized = B_TRUE;
handle = mp->map_handlep;
@@ -955,7 +934,7 @@ i8042_map(
ap->ahi_get32 = NULL;
ap->ahi_put64 = NULL;
ap->ahi_get64 = NULL;
- ap->ahi_rep_put8 = i8042_rep_put8;
+ ap->ahi_rep_put8 = NULL;
ap->ahi_rep_get8 = NULL;
ap->ahi_rep_put16 = NULL;
ap->ahi_rep_get16 = NULL;
@@ -1030,9 +1009,6 @@ i8042_intr(caddr_t arg)
int new_wptr;
struct i8042_port *port;
-#ifdef DEBUG
- global->intr_thread = curthread;
-#endif
mutex_enter(&global->i8042_mutex);
stat = ddi_get8(global->io_handle, global->io_addr + I8042_STAT);
@@ -1040,9 +1016,6 @@ i8042_intr(caddr_t arg)
if (! (stat & I8042_STAT_OUTBF)) {
++i8042_unclaimed_interrupts;
mutex_exit(&global->i8042_mutex);
-#ifdef DEBUG
- global->intr_thread = NULL;
-#endif
return (DDI_INTR_UNCLAIMED);
}
@@ -1054,27 +1027,6 @@ i8042_intr(caddr_t arg)
if (! port->initialized) {
mutex_exit(&global->i8042_mutex);
-#ifdef DEBUG
- global->intr_thread = NULL;
-#endif
- return (DDI_INTR_CLAIMED);
- }
-
- /*
- * If interception is enabled, and the byte matches what is being
- * waited for, clear the interception flag and trigger a softintr
- * that will signal the waiter, then exit the interrupt handler
- * without passing the byte to the child's interrupt handler.
- */
- if (port->intr_intercept_enabled && (port->intercept[0] == byte ||
- port->intercept[1] == byte)) {
- port->intercepted_byte = byte;
- port->intr_intercept_enabled = B_FALSE;
- (void) ddi_intr_trigger_softint(global->intercept_sih, port);
- mutex_exit(&global->i8042_mutex);
-#ifdef DEBUG
- global->intr_thread = NULL;
-#endif
return (DDI_INTR_CLAIMED);
}
@@ -1089,10 +1041,6 @@ i8042_intr(caddr_t arg)
#endif
mutex_exit(&global->i8042_mutex);
-
-#ifdef DEBUG
- global->intr_thread = NULL;
-#endif
return (DDI_INTR_CLAIMED);
}
@@ -1114,9 +1062,6 @@ i8042_intr(caddr_t arg)
mutex_exit(&port->intr_mutex);
#endif
-#ifdef DEBUG
- global->intr_thread = NULL;
-#endif
return (DDI_INTR_CLAIMED);
}
@@ -1204,6 +1149,37 @@ i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr)
global = port->i8042_global;
switch ((uintptr_t)addr) {
+ case I8042_LOCK:
+ ASSERT(port->has_glock != B_TRUE); /* No reentrancy */
+ mutex_enter(&global->i8042_out_mutex);
+ /*
+ * Block other children requesting exclusive access here until
+ * the child possessing it relinquishes the lock.
+ */
+ while (global->glock) {
+ cv_wait(&global->glock_cv, &global->i8042_out_mutex);
+ }
+ port->has_glock = B_TRUE;
+ global->glock = 1;
+ mutex_exit(&global->i8042_out_mutex);
+ ret = 0;
+ break;
+
+ case I8042_UNLOCK:
+ mutex_enter(&global->i8042_out_mutex);
+ ASSERT(global->glock != 0);
+ ASSERT(port->has_glock == B_TRUE);
+ port->has_glock = B_FALSE;
+ global->glock = 0;
+ /*
+ * Signal anyone waiting for exclusive access that it is now
+ * available.
+ */
+ cv_signal(&global->glock_cv);
+ mutex_exit(&global->i8042_out_mutex);
+ ret = 0;
+ break;
+
case I8042_INT_INPUT_AVAIL:
mutex_enter(&global->i8042_mutex);
ret = port->rptr != port->wptr;
@@ -1311,211 +1287,14 @@ i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr)
return (ret);
}
-/*
- * Send the byte specified and wait for a reply -- either the retry response,
- * or another response (assumed to be an acknowledgement). If the retry
- * response is received within the timeout period, the initial byte is resent
- * to the 8042.
- */
-static int
-i8042_do_intercept(ddi_acc_impl_t *handlep, struct i8042_port *port,
- uint8_t *oaddr, uint8_t byte, uint8_t retry_response)
-{
- int timedout = 0;
- struct i8042 *global;
- clock_t tval;
-
- global = port->i8042_global;
-
- /*
- * Intercept the command response so that the 8042 interrupt handler
- * does not call the port's interrupt handler.
- */
- port->intercept_complete = B_FALSE;
- port->intr_intercept_enabled = B_TRUE;
-
- /* Maximum time to wait: */
- tval = ddi_get_lbolt() + drv_usectohz(MAX_WAIT_ITERATIONS *
- USECS_PER_WAIT);
-
- do {
- i8042_put8_nolock(handlep, oaddr, byte);
-
- /*
- * Wait for the command response
- */
- while (!port->intercept_complete) {
- if (cv_timedwait(&port->intercept_cv,
- &port->intercept_mutex, tval) < 0 &&
- !port->intercept_complete) {
- timedout = 1;
- break;
- }
- }
-
- /*
- * If the intercepted byte is the retry response, keep retrying
- * until we time out, or until the success response is received.
- */
- if (port->intercept_complete &&
- port->intercepted_byte == retry_response)
- port->intercept_complete = B_FALSE;
-
- } while (!timedout && !port->intercept_complete);
-
- port->intr_intercept_enabled = B_FALSE;
-
- if (timedout && !port->intercept_complete) {
- /*
- * Some keyboard controllers don't trigger an interrupt,
- * so check the status bits to see if there's input available
- */
- if (ddi_get8(global->io_handle, global->io_addr + I8042_STAT) &
- I8042_STAT_OUTBF) {
- byte = ddi_get8(global->io_handle,
- global->io_addr + I8042_DATA);
-
- port->intercepted_byte = byte;
-
- if (byte == retry_response)
- return (B_TRUE); /* Timed out */
- else if (port->intercept[0] == byte) {
- port->intercept_complete = B_TRUE;
- return (B_FALSE); /* Response OK */
- }
- }
- }
-
- return (timedout);
-}
-
-/*
- * The _rep_put8() operation is designed to allow child drivers to
- * execute commands that have responses or that have responses plus an
- * option byte. These commands need to be executed atomically with respect
- * to commands from other children (some 8042 implementations get confused
- * when other child devices intersperse their commands while a command
- * to a different 8042-connected device is in flight).
- *
- * haddr points to a buffer with either 3 or 4 bytes. Three bytes if a
- * command (byte 0) is being sent for which we expect a response code (byte 1)
- * (this function blocks until we either read a response code (or the retry
- * code (byte 2)) or until a timer expires).
- * Four if the command (byte 0) requires a response (byte 1) and then an
- * option byte (byte 3). The option byte is only sent iff the response code
- * expected is received before the timeout. As with the 3-byte request, byte
- * 2 is the retry response.
- *
- * While this function may technically called in interrupt context, it may
- * block (depending on the IPL of the i8042 interrupt handler vs. the handler
- * executing) for as long as the timeout (and fail if i8042_intr cannot run).
- *
- * flags are ignored.
- *
- */
-/*ARGSUSED*/
static void
-i8042_rep_put8(ddi_acc_impl_t *handlep, uint8_t *haddr, uint8_t *daddr,
- size_t repcount, uint_t flags)
+i8042_put8(ddi_acc_impl_t *handlep, uint8_t *addr, uint8_t value)
{
- struct i8042_port *port;
struct i8042 *global;
- uint8_t *oaddr;
- uintptr_t devaddr = (uintptr_t)daddr;
- int timedout = 0;
- boolean_t polled;
+ struct i8042_port *port;
ddi_acc_hdl_t *h;
h = (ddi_acc_hdl_t *)handlep;
-
- port = (struct i8042_port *)h->ah_bus_private;
- global = port->i8042_global;
-
- /*
- * If this function is called, somehow, while we're in i8042_intr,
- * the logic below will not work. That situation should never be
- * possible.
- */
- ASSERT(global->intr_thread != curthread);
-
- /*
- * Only support the main port for now
- */
- if (port->which != MAIN_PORT || (devaddr != I8042_INT_CMD_PLUS_PARAM &&
- devaddr != I8042_POLL_CMD_PLUS_PARAM)) {
-#ifdef DEBUG
- prom_printf("WARNING: i8042_rep_put8(): port or address "
- "invalid\n");
-#endif
- return;
- }
-
- /*
- * Only support commands with MAX one parameter. The format of the
- * buffer supplied must be { <CMD>, <CMD_OK_RESPONSE>[, <PARAMETER>] }
- */
- if (repcount != 3 && repcount != 4) {
-#ifdef DEBUG
- prom_printf("WARNING: i8042_rep_put8(): Invalid repetition "
- "count (%d)\n", (int)repcount);
-#endif
- return;
- }
-
- polled = (devaddr == I8042_POLL_CMD_PLUS_PARAM);
-
- if (polled) {
- oaddr = (uint8_t *)I8042_POLL_OUTPUT_DATA;
- } else {
- oaddr = (uint8_t *)I8042_INT_OUTPUT_DATA;
- /*
- * Mutexes are only required for the non-polled (polled
- * via the virtual registers, NOT via the polling mechanism
- * used for systems without 8042 interrupts) case, because
- * when polling is used, the system is single-threaded
- * with interrupts disabled.
- */
- mutex_enter(&global->i8042_out_mutex);
- }
-
- /* Initialize the response and retry bytes from the caller */
- port->intercept[0] = haddr[1];
- port->intercept[1] = haddr[2];
-
- mutex_enter(&port->intercept_mutex);
-
- timedout = i8042_do_intercept(handlep, port, oaddr, haddr[0], haddr[2]);
-
- /*
- * If the first byte was processed before the timeout period, and
- * there's an option byte, send it now.
- */
- if (!timedout && repcount == 4) {
- timedout = i8042_do_intercept(handlep, port, oaddr, haddr[3],
- haddr[2]);
- }
-
- mutex_exit(&port->intercept_mutex);
-
-#ifdef DEBUG
- if (timedout && i8042_debug)
- prom_printf("WARNING: i8042_rep_put8(): timed out waiting for "
- "command response\n");
-#endif
-
- if (!polled)
- mutex_exit(&global->i8042_out_mutex);
-}
-
-static void
-i8042_put8_nolock(ddi_acc_impl_t *handlep, uint8_t *addr, uint8_t value)
-{
- struct i8042_port *port;
- struct i8042 *global;
- ddi_acc_hdl_t *h;
-
- h = (ddi_acc_hdl_t *)handlep;
-
port = (struct i8042_port *)h->ah_bus_private;
global = port->i8042_global;
@@ -1523,43 +1302,33 @@ i8042_put8_nolock(ddi_acc_impl_t *handlep, uint8_t *addr, uint8_t value)
case I8042_INT_OUTPUT_DATA:
case I8042_POLL_OUTPUT_DATA:
+ if ((uintptr_t)addr == I8042_INT_OUTPUT_DATA) {
+ mutex_enter(&global->i8042_out_mutex);
+
+ /*
+ * If no child has exclusive access, then proceed with
+ * the put8 below. If a child (not the one making the
+ * call) has exclusive access, wait for it to be
+ * relinquished. The use of i8042_out_mutex prevents
+ * children seeking exclusive access from getting it
+ * while a child is writing to the 8042.
+ */
+ while (global->glock && !port->has_glock) {
+ cv_wait(&global->glock_cv,
+ &global->i8042_out_mutex);
+ }
+ }
+
if (port->which == AUX_PORT)
i8042_send(global, I8042_CMD, I8042_CMD_WRITE_AUX);
i8042_send(global, I8042_DATA, value);
- break;
- }
-}
-
-static void
-i8042_put8(ddi_acc_impl_t *handlep, uint8_t *addr, uint8_t value)
-{
- struct i8042 *global;
- ddi_acc_hdl_t *h;
-
- h = (ddi_acc_hdl_t *)handlep;
- global = ((struct i8042_port *)h->ah_bus_private)->i8042_global;
-
- switch ((uintptr_t)addr) {
- case I8042_INT_OUTPUT_DATA:
- case I8042_POLL_OUTPUT_DATA:
-
- if ((uintptr_t)addr == I8042_INT_OUTPUT_DATA)
- mutex_enter(&global->i8042_out_mutex);
-
- i8042_put8_nolock(handlep, addr, value);
-
if ((uintptr_t)addr == I8042_INT_OUTPUT_DATA)
mutex_exit(&global->i8042_out_mutex);
break;
- case I8042_INT_CMD_PLUS_PARAM:
- case I8042_POLL_CMD_PLUS_PARAM:
-
- break;
-
#if defined(DEBUG)
case I8042_INT_INPUT_AVAIL:
case I8042_INT_INPUT_DATA:
@@ -1915,25 +1684,3 @@ i8042_is_polling_platform(void)
return (B_FALSE);
}
#endif
-
-/*
- * arg1 is the global i8042 state pointer (not used)
- * arg2 is the port pointer for the intercepted port
- */
-/*ARGSUSED*/
-static uint_t
-i8042_intercept_softint(caddr_t arg1, caddr_t arg2)
-{
- struct i8042_port *port = (struct i8042_port *)arg2;
- ASSERT(port != NULL);
-
- mutex_enter(&port->intercept_mutex);
- if (!port->intercept_complete) {
- port->intercept_complete = B_TRUE;
- cv_signal(&port->intercept_cv);
- mutex_exit(&port->intercept_mutex);
- return (DDI_INTR_CLAIMED);
- }
- mutex_exit(&port->intercept_mutex);
- return (DDI_INTR_UNCLAIMED);
-}
diff --git a/usr/src/uts/common/io/kb8042/at_keyprocess.c b/usr/src/uts/common/io/kb8042/at_keyprocess.c
index 529407c7fa..43e356a4a2 100644
--- a/usr/src/uts/common/io/kb8042/at_keyprocess.c
+++ b/usr/src/uts/common/io/kb8042/at_keyprocess.c
@@ -33,29 +33,12 @@
#include "kb8042.h"
/*
- * Debugging for this module is enabled by the kb8042_debug flag
- * defined in kb8042.c. See that module for details. This flag is
- * hotkey enabled by F10 if the kb8042_enable_debug_hotkey flag is set.
- */
-
-#if defined(DEBUG) || defined(lint)
-#define KD_DEBUG
-#endif
-
-/*
* A note on the use of prom_printf here: Most of these routines can be
* called from "polled mode", where we're servicing I/O requests from kmdb.
* Normal system services are not available from polled mode; cmn_err will
* not work. prom_printf is the only safe output mechanism.
*/
-#if defined(KD_DEBUG)
-extern boolean_t kb8042_debug;
-#define DEBUG_KD(f) { if (kb8042_debug) prom_printf f; }
-#else
-#define DEBUG_KD(f) /* nothing */
-#endif
-
#define KEYBAD 0xff /* should generate an error */
#define KEYIGN 0xfe /* ignore this sequence */
@@ -748,8 +731,6 @@ KeyboardConvertScan_set1(
enum keystate *state,
boolean_t *synthetic_release_needed)
{
- DEBUG_KD(("KeyboardConvertScan_set1: 0x%02x ", scan));
-
*synthetic_release_needed = B_FALSE;
*state = KEY_PRESSED;
@@ -763,7 +744,6 @@ KeyboardConvertScan_set1(
* Perhaps we should reset state here,
* since we no longer know what's going on.
*/
- DEBUG_KD(("-> overrun\n"));
return (B_FALSE);
case KB_POST_FAIL:
/*
@@ -776,7 +756,6 @@ KeyboardConvertScan_set1(
* Reset to idle
*/
kb8042->parse_scan_state = STATE_IDLE;
- DEBUG_KD(("-> POST %s\n", scan == KB_POST_OK ? "OK" : "FAIL"));
return (B_FALSE);
case KXT_EXTEND:
@@ -809,12 +788,10 @@ KeyboardConvertScan_set1(
switch (scan) {
case KXT_EXTEND:
kb8042->parse_scan_state = STATE_E0;
- DEBUG_KD(("-> state E0\n"));
return (B_FALSE);
case KXT_EXTEND2:
kb8042->parse_scan_state = STATE_E1;
- DEBUG_KD(("-> state E1\n"));
return (B_FALSE);
/*
@@ -857,7 +834,6 @@ KeyboardConvertScan_set1(
switch (scan) {
case 0x1d:
kb8042->parse_scan_state = STATE_E1_1D;
- DEBUG_KD(("-> state E1 1D\n"));
return (B_FALSE);
default:
*keynum = INVALID;
@@ -888,11 +864,9 @@ KeyboardConvertScan_set1(
switch (*keynum) {
case KEYIGN: /* not a key, nor an error */
- DEBUG_KD(("-> hole -> ignored\n"));
return (B_FALSE); /* also not a final keycode */
case KEYBAD: /* not part of a legit sequence? */
- DEBUG_KD(("-> bad -> ignored\n"));
return (B_FALSE); /* and return not a final keycode */
default:
@@ -900,11 +874,6 @@ KeyboardConvertScan_set1(
* If we're here, it's a valid keycode. We've already
* filled in the return values; return success.
*/
-
- DEBUG_KD(("-> %s keypos %d\n",
- *state == KEY_RELEASED ? "released" : "pressed",
- *keynum));
-
return (B_TRUE); /* resolved to a key */
}
}
@@ -930,8 +899,6 @@ KeyboardConvertScan_set2(
enum keystate *state,
boolean_t *synthetic_release_needed)
{
- DEBUG_KD(("KeyboardConvertScan_set2: 0x%02x ", scan));
-
*synthetic_release_needed = B_FALSE;
*state = KEY_PRESSED;
@@ -949,7 +916,6 @@ KeyboardConvertScan_set2(
case KAT_BREAK:
/* Switch states so we can recognize the code that follows */
kb8042->break_received = 1;
- DEBUG_KD(("-> break prefix\n"));
return (B_FALSE); /* not a final keycode */
case KB_ERROR:
@@ -957,7 +923,6 @@ KeyboardConvertScan_set2(
* Perhaps we should reset state here,
* since we no longer know what's going on.
*/
- DEBUG_KD(("-> overrun\n"));
return (B_FALSE);
case KB_POST_OK:
@@ -972,7 +937,6 @@ KeyboardConvertScan_set2(
* Reset to idle
*/
kb8042->parse_scan_state = STATE_IDLE;
- DEBUG_KD(("-> POST %s\n", scan == KB_POST_OK ? "OK" : "FAIL"));
return (B_FALSE);
}
@@ -986,12 +950,10 @@ KeyboardConvertScan_set2(
switch (scan) {
case KXT_EXTEND:
kb8042->parse_scan_state = STATE_E0;
- DEBUG_KD(("-> state E0\n"));
return (B_FALSE);
case KXT_EXTEND2:
kb8042->parse_scan_state = STATE_E1;
- DEBUG_KD(("-> state E1\n"));
return (B_FALSE);
/*
@@ -1034,7 +996,6 @@ KeyboardConvertScan_set2(
switch (scan) {
case 0x14:
kb8042->parse_scan_state = STATE_E1_14;
- DEBUG_KD(("-> state E1 14\n"));
return (B_FALSE);
default:
*keynum = INVALID;
@@ -1112,11 +1073,9 @@ KeyboardConvertScan_set2(
switch (*keynum) {
case KEYIGN: /* not a key, nor an error */
- DEBUG_KD(("-> hole -> ignored\n"));
return (B_FALSE); /* also not a final keycode */
case KEYBAD: /* not part of a legit sequence? */
- DEBUG_KD(("-> bad -> ignored\n"));
return (B_FALSE); /* and return not a final keycode */
default:
@@ -1124,11 +1083,6 @@ KeyboardConvertScan_set2(
* If we're here, it's a valid keycode. We've already
* filled in the return values; return success.
*/
-
- DEBUG_KD(("-> %s keypos %d\n",
- *state == KEY_RELEASED ? "released" : "pressed",
- *keynum));
-
return (B_TRUE); /* resolved to a key */
}
}
diff --git a/usr/src/uts/common/io/kb8042/kb8042.c b/usr/src/uts/common/io/kb8042/kb8042.c
index cf5dd4a2ef..7497f7e6f7 100644
--- a/usr/src/uts/common/io/kb8042/kb8042.c
+++ b/usr/src/uts/common/io/kb8042/kb8042.c
@@ -103,32 +103,6 @@ static kbtrans_key_t keytab_pc2usb[KBTRANS_KEYNUMS_MAX] = {
/* 248 */ 0, 0, 0, 0
};
-/*
- * DEBUG (or KD_DEBUG for just this module) turns on a flag called
- * kb8042_enable_debug_hotkey. If kb8042_enable_debug_hotkey is true,
- * then the following hotkeys are enabled:
- * F10 - turn on debugging "normal" translations
- * F9 - turn on debugging "getchar" translations
- * F8 - turn on "low level" debugging
- * F7 - turn on terse press/release debugging
- * F1 - turn off all debugging
- * The default value for kb8042_enable_debug_hotkey is false, disabling
- * these hotkeys.
- */
-
-#if defined(DEBUG) || defined(lint)
-#define KD_DEBUG
-#endif
-
-#ifdef KD_DEBUG
-boolean_t kb8042_enable_debug_hotkey = B_FALSE;
-boolean_t kb8042_debug = B_FALSE;
-boolean_t kb8042_getchar_debug = B_FALSE;
-boolean_t kb8042_low_level_debug = B_FALSE;
-boolean_t kb8042_pressrelease_debug = B_FALSE;
-static void kb8042_debug_hotkey(int scancode);
-#endif
-
#ifdef __sparc
#define USECS_PER_WAIT 100
#define MAX_WAIT_USECS 100000 /* in usecs = 100 ms */
@@ -139,6 +113,9 @@ int kb8042_default_scanset = 2;
#endif
+#define MAX_KB8042_WAIT_MAX_MS 500 /* 500ms total */
+#define MAX_KB8042_RETRIES 5
+
enum state_return { STATE_NORMAL, STATE_INTERNAL };
static void kb8042_init(struct kb8042 *kb8042, boolean_t from_resume);
@@ -312,21 +289,24 @@ kb8042_clear_input_buffer(struct kb8042 *kb8042, boolean_t polled)
}
}
+/*
+ * kb8042_send_and_expect does all its I/O via polling interfaces
+ */
static boolean_t
kb8042_send_and_expect(struct kb8042 *kb8042, uint8_t send, uint8_t expect,
- boolean_t polled, int timeout, int *error, uint8_t *got)
+ int timeout, int *error, uint8_t *got)
{
- int port = (polled == B_TRUE) ? I8042_POLL_INPUT_DATA :
- I8042_INT_INPUT_DATA;
uint8_t datab;
int err;
boolean_t rval;
- kb8042_send_to_keyboard(kb8042, send, polled);
+ ddi_put8(kb8042->handle,
+ kb8042->addr + I8042_POLL_OUTPUT_DATA, send);
- if (kb8042_is_input_avail(kb8042, timeout, polled)) {
+ if (kb8042_is_input_avail(kb8042, timeout, B_TRUE)) {
err = 0;
- datab = ddi_get8(kb8042->handle, kb8042->addr + port);
+ datab = ddi_get8(kb8042->handle,
+ kb8042->addr + I8042_POLL_INPUT_DATA);
rval = ((datab == expect) ? B_TRUE : B_FALSE);
} else {
err = EAGAIN;
@@ -351,12 +331,16 @@ kb8042_error_string(int errcode)
}
}
+/*
+ * kb8042_read_scanset works properly because it is called before ddi_add_intr
+ * (if it is called after ddi_add_intr, i8042_intr would call kb8042_intr
+ * instead of just storing the data that comes in from the keyboard, which
+ * would prevent the code here from getting it.)
+ */
static int
-kb8042_read_scanset(struct kb8042 *kb8042, boolean_t polled)
+kb8042_read_scanset(struct kb8042 *kb8042)
{
int scanset = -1;
- int port = (polled == B_TRUE) ? I8042_POLL_INPUT_DATA :
- I8042_INT_INPUT_DATA;
int err;
uint8_t got;
@@ -366,8 +350,8 @@ kb8042_read_scanset(struct kb8042 *kb8042, boolean_t polled)
* Send a "change scan code set" command to the keyboard.
* It should respond with an ACK.
*/
- if (kb8042_send_and_expect(kb8042, KB_SET_SCAN, KB_ACK, polled,
- MAX_WAIT_USECS, &err, &got) != B_TRUE) {
+ if (kb8042_send_and_expect(kb8042, KB_SET_SCAN, KB_ACK, MAX_WAIT_USECS,
+ &err, &got) != B_TRUE) {
goto fail_read_scanset;
}
@@ -375,8 +359,8 @@ kb8042_read_scanset(struct kb8042 *kb8042, boolean_t polled)
* Send a 0. The keyboard should ACK the 0, then it should send the
* scan code set in use.
*/
- if (kb8042_send_and_expect(kb8042, 0, KB_ACK, polled,
- MAX_WAIT_USECS, &err, &got) != B_TRUE) {
+ if (kb8042_send_and_expect(kb8042, 0, KB_ACK, MAX_WAIT_USECS, &err,
+ &got) != B_TRUE) {
goto fail_read_scanset;
}
@@ -386,11 +370,11 @@ kb8042_read_scanset(struct kb8042 *kb8042, boolean_t polled)
* just for fun, so blow past those to get the keyboard scan code.
*/
while (kb8042_is_input_avail(kb8042, MAX_WAIT_USECS, B_TRUE) &&
- (scanset = ddi_get8(kb8042->handle, kb8042->addr + port))
- == KB_ACK)
+ (scanset = ddi_get8(kb8042->handle,
+ kb8042->addr + I8042_POLL_INPUT_DATA)) == KB_ACK)
;
-#ifdef KD_DEBUG
+#ifdef DEBUG
cmn_err(CE_NOTE, "!Scan code set from keyboard is `%d'.",
scanset);
#endif
@@ -398,7 +382,7 @@ kb8042_read_scanset(struct kb8042 *kb8042, boolean_t polled)
return (scanset);
fail_read_scanset:
-#ifdef KD_DEBUG
+#ifdef DEBUG
if (err == 0)
cmn_err(CE_NOTE, "Could not read current scan set from "
"keyboard: %s. (Expected 0x%x, but got 0x%x).",
@@ -473,7 +457,7 @@ kb8042_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
rc = ddi_regs_map_setup(devi, 0, (caddr_t *)&kb8042->addr,
(offset_t)0, (offset_t)0, &attr, &kb8042->handle);
if (rc != DDI_SUCCESS) {
-#if defined(KD_DEBUG)
+#ifdef DEBUG
cmn_err(CE_WARN, "kb8042_attach: can't map registers");
#endif
goto failure;
@@ -490,13 +474,14 @@ kb8042_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
mutex_init(&kb8042->w_hw_mutex, NULL, MUTEX_DRIVER, kb8042->w_iblock);
cv_init(&kb8042->ops_cv, NULL, CV_DRIVER, NULL);
cv_init(&kb8042->suspend_cv, NULL, CV_DRIVER, NULL);
+ cv_init(&kb8042->cmd_cv, NULL, CV_DRIVER, NULL);
kb8042->init_state |= KB8042_HW_MUTEX_INITTED;
kb8042_init(kb8042, B_FALSE);
#ifdef __sparc
/* Detect the scan code set currently in use */
- scanset = kb8042_read_scanset(kb8042, B_TRUE);
+ scanset = kb8042_read_scanset(kb8042);
if (scanset < 0 && kb8042_warn_unknown_scanset) {
@@ -542,7 +527,7 @@ kb8042_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
ddi_report_dev(devi);
-#ifdef KD_DEBUG
+#ifdef DEBUG
cmn_err(CE_CONT, "?%s instance #%d READY\n",
DRIVER_NAME(devi), ddi_get_instance(devi));
#endif
@@ -620,15 +605,16 @@ kb8042_cleanup(struct kb8042 *kb8042)
{
ASSERT(kb8042_dip != NULL);
+ if (kb8042->init_state & KB8042_INTR_ADDED)
+ ddi_remove_intr(kb8042_dip, 0, kb8042->w_iblock);
+
if (kb8042->init_state & KB8042_HW_MUTEX_INITTED) {
+ cv_destroy(&kb8042->cmd_cv);
cv_destroy(&kb8042->suspend_cv);
cv_destroy(&kb8042->ops_cv);
mutex_destroy(&kb8042->w_hw_mutex);
}
- if (kb8042->init_state & KB8042_INTR_ADDED)
- ddi_remove_intr(kb8042_dip, 0, kb8042->w_iblock);
-
if (kb8042->init_state & KB8042_REGS_MAPPED)
ddi_regs_map_free(&kb8042->handle);
@@ -657,8 +643,15 @@ kb8042_init(struct kb8042 *kb8042, boolean_t from_resume)
kb8042->kb_old_key_pos = 0;
+ /*
+ * Explicitly grab and release the 8042 lock outside of
+ * kb8042_send_to_keyboard, because this is the only situation
+ * where a polling interface is used with locking required.
+ */
+ (void) ddi_get8(kb8042->handle, kb8042->addr + I8042_LOCK);
/* Set up the command state machine and start it running. */
- kb8042_send_to_keyboard(kb8042, KB_ENABLE, B_FALSE);
+ kb8042_send_to_keyboard(kb8042, KB_ENABLE, B_TRUE);
+ (void) ddi_get8(kb8042->handle, kb8042->addr + I8042_UNLOCK);
kb8042->w_init++;
@@ -1013,9 +1006,23 @@ kb8042_received_byte(
enum keystate state;
boolean_t synthetic_release_needed;
-#ifdef KD_DEBUG
- kb8042_debug_hotkey(scancode);
-#endif
+ /*
+ * Intercept ACK and RESEND and signal the condition that
+ * kb8042_send_and_wait is waiting for.
+ */
+ if (scancode == KB_ACK) {
+ mutex_enter(&kb8042->w_hw_mutex);
+ kb8042->acked = 1;
+ cv_signal(&kb8042->cmd_cv);
+ mutex_exit(&kb8042->w_hw_mutex);
+ return;
+ } else if (scancode == KB_RESEND) {
+ mutex_enter(&kb8042->w_hw_mutex);
+ kb8042->need_retry = 1;
+ cv_signal(&kb8042->cmd_cv);
+ mutex_exit(&kb8042->w_hw_mutex);
+ return;
+ }
if (!kb8042->w_init) /* can't do anything anyway */
return;
@@ -1025,48 +1032,23 @@ kb8042_received_byte(
if (legit == 0) {
/* Eaten by translation */
-#ifdef KD_DEBUG
- if (kb8042_debug)
- prom_printf("kb8042_intr: 0x%x -> ignored\n", scancode);
-#endif
return;
}
-#ifdef KD_DEBUG
- if (kb8042_debug) {
- prom_printf("kb8042_intr: 0x%x -> %s %d",
- scancode,
- state == KEY_RELEASED ? "released" : "pressed",
- key_pos);
- }
-#endif
-
/*
* Don't know if we want this permanently, but it seems interesting
* for the moment.
*/
if (key_pos == kb8042->debugger.mod1) {
-#ifdef KD_DEBUG
- if (kb8042_debug)
- prom_printf(" -> debug mod1");
-#endif
kb8042->debugger.mod1_down = (state == KEY_PRESSED);
}
if (key_pos == kb8042->debugger.mod2) {
-#ifdef KD_DEBUG
- if (kb8042_debug)
- prom_printf(" -> debug mod2");
-#endif
kb8042->debugger.mod2_down = (state == KEY_PRESSED);
}
if (kb8042->debugger.enabled &&
key_pos == kb8042->debugger.trigger &&
kb8042->debugger.mod1_down &&
kb8042->debugger.mod2_down) {
-#ifdef KD_DEBUG
- if (kb8042_debug)
- prom_printf(" -> debugger\n");
-#endif
/*
* Require new presses of the modifiers.
*/
@@ -1083,10 +1065,6 @@ kb8042_received_byte(
* Ctrl-Alt-D still works.
*/
if (kb8042->w_qp == NULL) {
-#ifdef KD_DEBUG
- if (kb8042_debug)
- prom_printf(" -> nobody home\n");
-#endif
return;
}
@@ -1097,27 +1075,11 @@ kb8042_received_byte(
* Don't think so.)
*/
if (kb8042_autorepeat_detect(kb8042, key_pos, state)) {
-#ifdef KD_DEBUG
- if (kb8042_debug)
- prom_printf(" -> autorepeat ignored\n");
-#endif
return;
}
-#ifdef KD_DEBUG
- if (kb8042_debug)
- prom_printf(" -> OK\n");
-#endif
-#if defined(KD_DEBUG)
- if (kb8042_pressrelease_debug) {
- prom_printf(" %s%d ",
- state == KEY_PRESSED ? "+" : "-",
- key_pos);
- }
-#endif
-
- kb8042_process_key(kb8042, key_pos, state);
+ kb8042_process_key(kb8042, key_pos, state);
/*
* This is a total hack. For some stupid reason, the two additional
@@ -1125,12 +1087,6 @@ kb8042_received_byte(
* only. We synthesize a release immediately.
*/
if (synthetic_release_needed) {
-#if defined(KD_DEBUG)
- if (kb8042_debug)
- prom_printf("synthetic release %d\n", key_pos);
- if (kb8042_pressrelease_debug)
- prom_printf(" -%d(s) ", key_pos);
-#endif
(void) kb8042_autorepeat_detect(kb8042, key_pos, KEY_RELEASED);
kb8042_process_key(kb8042, key_pos, state);
}
@@ -1179,11 +1135,6 @@ kb8042_intr(caddr_t arg)
scancode = ddi_get8(kb8042->handle,
kb8042->addr + I8042_INT_INPUT_DATA);
-#if defined(KD_DEBUG)
- if (kb8042_low_level_debug)
- prom_printf(" <K:%x ", scancode);
-#endif
-
kb8042_received_byte(kb8042, scancode);
}
@@ -1223,10 +1174,6 @@ kb8042_polled_keycheck(
*key = kb8042->polled_synthetic_release_key;
*state = KEY_RELEASED;
kb8042->polled_synthetic_release_pending = B_FALSE;
-#if defined(KD_DEBUG)
- if (kb8042_getchar_debug)
- prom_printf("synthetic release 0x%x\n", *key);
-#endif
(void) kb8042_autorepeat_detect(kb8042, *key, *state);
return (B_TRUE);
}
@@ -1240,33 +1187,11 @@ kb8042_polled_keycheck(
scancode = ddi_get8(kb8042->handle,
kb8042->addr + I8042_POLL_INPUT_DATA);
-#if defined(KD_DEBUG)
- if (kb8042_low_level_debug)
- prom_printf(" g<%x ", scancode);
-#endif
-
-#ifdef KD_DEBUG
- kb8042_debug_hotkey(scancode);
- if (kb8042_getchar_debug)
- prom_printf("polled 0x%x", scancode);
-#endif
-
legit = KeyboardConvertScan(kb8042, scancode, key, state,
&synthetic_release_needed);
if (!legit) {
-#ifdef KD_DEBUG
- if (kb8042_getchar_debug)
- prom_printf(" -> ignored\n");
-#endif
continue;
}
-#ifdef KD_DEBUG
- if (kb8042_getchar_debug) {
- prom_printf(" -> %s %d\n",
- *state == KEY_PRESSED ? "pressed" : "released",
- *key);
- }
-#endif
/*
* For the moment at least, we rely on hardware autorepeat
* for polled I/O autorepeat. However, for coordination
@@ -1324,55 +1249,141 @@ kb8042_streams_setled(struct kbtrans_hardware *hw, int led_state)
kb8042_setled(kb8042, led_state, B_FALSE);
}
+
+static int
+kb8042_send_and_wait(struct kb8042 *kb8042, uint8_t u8, boolean_t polled)
+{
+ uint8_t *outp = kb8042->addr +
+ (polled ? I8042_POLL_OUTPUT_DATA : I8042_INT_OUTPUT_DATA);
+ uint8_t *inavp = kb8042->addr +
+ (polled ? I8042_POLL_INPUT_AVAIL : I8042_INT_INPUT_AVAIL);
+ uint8_t *inp = kb8042->addr +
+ (polled ? I8042_POLL_INPUT_DATA : I8042_INT_INPUT_DATA);
+ uint8_t b;
+ int ms_waited;
+ int timedout;
+ int expire;
+ int retries = 0;
+
+ do {
+ kb8042->acked = 0;
+ kb8042->need_retry = 0;
+ ms_waited = 0; /* Zero it whether polled or not */
+ timedout = 0;
+
+ ddi_put8(kb8042->handle, outp, u8);
+
+ while (!kb8042->acked && !kb8042->need_retry && !timedout) {
+
+ if (polled) {
+ if (ddi_get8(kb8042->handle, inavp)) {
+ b = ddi_get8(kb8042->handle, inp);
+ switch (b) {
+ case KB_ACK:
+ kb8042->acked = 1;
+ break;
+ case KB_RESEND:
+ kb8042->need_retry = 1;
+ break;
+ default:
+ /*
+ * drop it: We should never
+ * get scancodes while
+ * we're in the middle of a
+ * command anyway.
+ */
+#ifdef DEBUG
+ cmn_err(CE_WARN, "!Unexpected "
+ " byte 0x%x", b);
+#endif
+ break;
+ }
+ }
+
+ /*
+ * Wait 1ms if an ACK wasn't received yet
+ */
+ if (!kb8042->acked) {
+ drv_usecwait(1000);
+ ms_waited++;
+ if (ms_waited >= MAX_KB8042_WAIT_MAX_MS)
+ timedout = B_TRUE;
+ }
+ } else {
+ /* Interrupt-driven */
+ expire = ddi_get_lbolt() +
+ drv_usectohz(MAX_KB8042_WAIT_MAX_MS * 1000);
+
+ /*
+ * If cv_timedwait returned -1 and we neither
+ * received an ACK nor a RETRY response, then
+ * we timed out.
+ */
+ if (cv_timedwait(&kb8042->cmd_cv,
+ &kb8042->w_hw_mutex, expire) == -1 &&
+ !kb8042->acked && !kb8042->need_retry) {
+ timedout = B_TRUE;
+ }
+ }
+
+ }
+ } while ((kb8042->need_retry || timedout) &&
+ ++retries < MAX_KB8042_RETRIES);
+
+ return (kb8042->acked);
+}
+
+/*
+ * kb8042_send_to_keyboard should be called with w_hw_mutex held if
+ * polled is FALSE.
+ */
static void
kb8042_send_to_keyboard(struct kb8042 *kb8042, int byte, boolean_t polled)
{
- uint8_t led_cmd[4];
/*
- * KB_SET_LED and KB_ENABLE are special commands for which the nexus
- * driver is requested to wait for responses before proceeding.
- * KB_SET_LED also provides an option byte for the nexus to send to
- * the keyboard after the acknowledgement.
+ * KB_SET_LED and KB_ENABLE are special commands which require blocking
+ * other 8042 consumers while executing.
*
* Other commands/data are sent using the single put8 I/O access
* function.
*/
if (byte == KB_SET_LED) {
- /*
- * Initialize the buffer used with _rep_put8. We
- * expect an ACK after the SET_LED command, at which point
- * the LED byte should be sent to the keyboard.
- */
- led_cmd[0] = KB_SET_LED;
- led_cmd[1] = KB_ACK;
- led_cmd[2] = KB_RESEND;
- led_cmd[3] = kb8042_xlate_leds(kb8042->leds.desired);
- if (polled) {
- ddi_rep_put8(kb8042->handle, &led_cmd[0],
- kb8042->addr + I8042_POLL_CMD_PLUS_PARAM, 4, 0);
- } else {
- ddi_rep_put8(kb8042->handle, &led_cmd[0],
- kb8042->addr + I8042_INT_CMD_PLUS_PARAM, 4, 0);
+
+ if (!polled) {
+ (void) ddi_get8(kb8042->handle, kb8042->addr +
+ I8042_LOCK);
+ }
+
+ if (kb8042_send_and_wait(kb8042, KB_SET_LED, polled)) {
+ /*
+ * Ignore return value, as there's nothing we can
+ * do about it if the SET LED command fails.
+ */
+ (void) kb8042_send_and_wait(kb8042,
+ kb8042_xlate_leds(kb8042->leds.desired), polled);
+ }
+
+ if (!polled) {
+ (void) ddi_get8(kb8042->handle, kb8042->addr +
+ I8042_UNLOCK);
}
kb8042->leds.commanded = kb8042->leds.desired;
} else if (byte == KB_ENABLE) {
- /*
- * Initialize the buffer used with _rep_put8. We
- * expect an ACK after the KB_ENABLE command.
- */
- led_cmd[0] = KB_ENABLE;
- led_cmd[1] = KB_ACK;
- led_cmd[2] = KB_RESEND;
- if (polled) {
- ddi_rep_put8(kb8042->handle, &led_cmd[0],
- kb8042->addr + I8042_POLL_CMD_PLUS_PARAM, 3, 0);
- } else {
- ddi_rep_put8(kb8042->handle, &led_cmd[0],
- kb8042->addr + I8042_INT_CMD_PLUS_PARAM, 3, 0);
+ if (!polled) {
+ (void) ddi_get8(kb8042->handle, kb8042->addr +
+ I8042_LOCK);
}
+
+ (void) kb8042_send_and_wait(kb8042, KB_ENABLE, polled);
+
+ if (!polled) {
+ (void) ddi_get8(kb8042->handle, kb8042->addr +
+ I8042_UNLOCK);
+ }
+
} else {
/* All other commands use the "normal" virtual output port */
if (polled) {
@@ -1383,11 +1394,6 @@ kb8042_send_to_keyboard(struct kb8042 *kb8042, int byte, boolean_t polled)
kb8042->addr + I8042_INT_OUTPUT_DATA, byte);
}
}
-
-#if defined(KD_DEBUG)
- if (kb8042_low_level_debug)
- prom_printf(" >K:%x ", byte);
-#endif
}
/*
@@ -1404,7 +1410,6 @@ kb8042_wait_poweron(struct kb8042 *kb8042)
{
int cnt;
int ready;
- unsigned char byt;
/* wait for up to 250 ms for a response */
for (cnt = 0; cnt < 250; cnt++) {
@@ -1421,12 +1426,8 @@ kb8042_wait_poweron(struct kb8042 *kb8042)
* already. (On a PC, the BIOS almost certainly did.)
*/
if (ready != 0) {
- byt = ddi_get8(kb8042->handle,
+ (void) ddi_get8(kb8042->handle,
kb8042->addr + I8042_INT_INPUT_DATA);
-#if defined(KD_DEBUG)
- if (kb8042_low_level_debug)
- prom_printf(" <K:%x ", byt);
-#endif
}
}
@@ -1474,55 +1475,6 @@ kb8042_get_initial_leds(
#endif
}
-#if defined(KD_DEBUG)
-static void
-kb8042_debug_hotkey(int scancode)
-{
- if (!kb8042_enable_debug_hotkey)
- return;
-
- switch (scancode) {
- case 0x44: /* F10 in Scan Set 1 code. (Set 2 code is 0x09) */
- if (!kb8042_debug) {
- prom_printf("\nKeyboard: normal debugging on\n");
- kb8042_debug = B_TRUE;
- }
- break;
- case 0x43: /* F9 in Scan Set 1 code. (Set 2 code is 0x01) */
- if (!kb8042_getchar_debug) {
- prom_printf("\nKeyboard: getchar debugging on\n");
- kb8042_getchar_debug = B_TRUE;
- }
- break;
- case 0x42: /* F8 in Scan Set 1 code. (Set 2 code is 0x0a) */
- if (!kb8042_low_level_debug) {
- prom_printf("\nKeyboard: low-level debugging on\n");
- kb8042_low_level_debug = B_TRUE;
- }
- break;
- case 0x41: /* F7 in Scan Set 1 code. (Set 2 code is 0x83) */
- if (!kb8042_pressrelease_debug) {
- prom_printf(
- "\nKeyboard: press/release debugging on\n");
- kb8042_pressrelease_debug = B_TRUE;
- }
- break;
- case 0x3b: /* F1 in Scan Set 1 code. (Set 2 code is 0x05) */
- if (kb8042_debug ||
- kb8042_getchar_debug ||
- kb8042_low_level_debug ||
- kb8042_pressrelease_debug) {
- prom_printf("\nKeyboard: all debugging off\n");
- kb8042_debug = B_FALSE;
- kb8042_getchar_debug = B_FALSE;
- kb8042_low_level_debug = B_FALSE;
- kb8042_pressrelease_debug = B_FALSE;
- }
- break;
- }
-}
-#endif
-
static boolean_t
kb8042_autorepeat_detect(
struct kb8042 *kb8042,
diff --git a/usr/src/uts/common/io/kb8042/kb8042.h b/usr/src/uts/common/io/kb8042/kb8042.h
index b5703df3cb..d63c327755 100644
--- a/usr/src/uts/common/io/kb8042/kb8042.h
+++ b/usr/src/uts/common/io/kb8042/kb8042.h
@@ -24,7 +24,7 @@
/* All Rights Reserved */
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -112,6 +112,9 @@ struct kb8042 {
int ops;
kcondvar_t suspend_cv;
kcondvar_t ops_cv;
+ int acked;
+ int need_retry;
+ kcondvar_t cmd_cv;
};
extern boolean_t KeyboardConvertScan(struct kb8042 *, unsigned char scan,
diff --git a/usr/src/uts/common/io/mouse8042.c b/usr/src/uts/common/io/mouse8042.c
index e666caed53..1ed61b49a8 100644
--- a/usr/src/uts/common/io/mouse8042.c
+++ b/usr/src/uts/common/io/mouse8042.c
@@ -23,7 +23,7 @@
/* All Rights Reserved */
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -42,6 +42,7 @@
#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/strtty.h>
+#include <sys/strsun.h>
#include <sys/debug.h>
#include <sys/ddi.h>
#include <sys/stat.h>
@@ -53,17 +54,16 @@
#include <sys/i8042.h>
#include <sys/note.h>
+#include <sys/mouse.h>
#define DRIVER_NAME(dip) ddi_driver_name(dip)
-#ifdef DEBUG
-#define MOUSE8042_DEBUG
-#endif
-
#define MOUSE8042_INTERNAL_OPEN(minor) (((minor) & 0x1) == 1)
#define MOUSE8042_MINOR_TO_INSTANCE(minor) ((minor) / 2)
#define MOUSE8042_INTERNAL_MINOR(minor) ((minor) + 1)
+#define MOUSE8042_RESET_TIMEOUT_USECS 500000 /* 500 ms */
+
extern int ddi_create_internal_pathname(dev_info_t *, char *, int, minor_t);
extern void consconfig_link(major_t major, minor_t minor);
extern int consconfig_unlink(major_t major, minor_t minor);
@@ -85,6 +85,17 @@ extern int consconfig_unlink(major_t major, minor_t minor);
*/
static dev_info_t *mouse8042_dip;
+/*
+ * RESET states
+ */
+typedef enum {
+ MSE_RESET_IDLE, /* No reset in progress */
+ MSE_RESET_PRE, /* Send reset, waiting for ACK */
+ MSE_RESET_ACK, /* Got ACK, waiting for 0xAA */
+ MSE_RESET_AA, /* Got 0xAA, waiting for 0x00 */
+ MSE_RESET_FAILED
+} mouse8042_reset_state_e;
+
struct mouse_state {
queue_t *ms_rqp;
queue_t *ms_wqp;
@@ -95,18 +106,19 @@ struct mouse_state {
minor_t ms_minor;
boolean_t ms_opened;
+ kmutex_t reset_mutex;
+ mouse8042_reset_state_e reset_state;
+ timeout_id_t reset_tid;
+ int ready;
+ mblk_t *reply_mp;
+ bufcall_id_t bc_id;
};
-#if defined(MOUSE8042_DEBUG)
-int mouse8042_debug = 0;
-int mouse8042_debug_minimal = 0;
-#endif
-
static uint_t mouse8042_intr(caddr_t arg);
static int mouse8042_open(queue_t *q, dev_t *devp, int flag, int sflag,
cred_t *cred_p);
static int mouse8042_close(queue_t *q, int flag, cred_t *cred_p);
-static int mouse8042_wput(queue_t *q, mblk_t *mp);
+static int mouse8042_wsrv(queue_t *qp);
static int mouse8042_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd,
void *arg, void **result);
@@ -137,8 +149,8 @@ static struct qinit mouse8042_rinit = {
};
static struct qinit mouse8042_winit = {
- mouse8042_wput, /* put */
- NULL, /* service */
+ putq, /* put */
+ mouse8042_wsrv, /* service */
NULL, /* open */
NULL, /* close */
NULL, /* admin */
@@ -254,15 +266,12 @@ mouse8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
int rc;
-#ifdef MOUSE8042_DEBUG
- if (mouse8042_debug) {
- cmn_err(CE_CONT, MODULE_NAME "_attach entry\n");
- }
-#endif
-
if (cmd == DDI_RESUME) {
state = (struct mouse_state *)ddi_get_driver_private(dip);
+ /* Ready to handle inbound data from mouse8042_intr */
+ state->ready = 1;
+
/*
* Send a 0xaa 0x00 upstream.
* This causes the vuid module to reset the mouse.
@@ -289,6 +298,9 @@ mouse8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
/* allocate and initialize state structure */
state = kmem_zalloc(sizeof (struct mouse_state), KM_SLEEP);
state->ms_opened = B_FALSE;
+ state->reset_state = MSE_RESET_IDLE;
+ state->reset_tid = 0;
+ state->bc_id = 0;
ddi_set_driver_private(dip, state);
/*
@@ -310,10 +322,6 @@ mouse8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
rc = ddi_create_minor_node(dip, "mouse", S_IFCHR, instance * 2,
DDI_NT_MOUSE, NULL);
if (rc != DDI_SUCCESS) {
-#if defined(MOUSE8042_DEBUG)
- cmn_err(CE_CONT,
- MODULE_NAME "_attach: ddi_create_minor_node failed\n");
-#endif
goto fail_1;
}
@@ -325,41 +333,33 @@ mouse8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
rc = ddi_regs_map_setup(dip, 0, (caddr_t *)&state->ms_addr,
(offset_t)0, (offset_t)0, &attr, &state->ms_handle);
if (rc != DDI_SUCCESS) {
-#if defined(MOUSE8042_DEBUG)
- cmn_err(CE_WARN, MODULE_NAME "_attach: can't map registers");
-#endif
goto fail_2;
}
rc = ddi_get_iblock_cookie(dip, 0, &state->ms_iblock_cookie);
if (rc != DDI_SUCCESS) {
-#if defined(MOUSE8042_DEBUG)
- cmn_err(CE_WARN,
- MODULE_NAME "_attach: Can't get iblock cookie");
-#endif
goto fail_3;
}
mutex_init(&state->ms_mutex, NULL, MUTEX_DRIVER,
state->ms_iblock_cookie);
+ mutex_init(&state->reset_mutex, NULL, MUTEX_DRIVER,
+ state->ms_iblock_cookie);
rc = ddi_add_intr(dip, 0,
(ddi_iblock_cookie_t *)NULL, (ddi_idevice_cookie_t *)NULL,
mouse8042_intr, (caddr_t)state);
if (rc != DDI_SUCCESS) {
-#if defined(MOUSE8042_DEBUG)
- cmn_err(CE_WARN, MODULE_NAME "_attach: cannot add interrupt");
-#endif
goto fail_3;
}
mouse8042_dip = dip;
+ /* Ready to handle inbound data from mouse8042_intr */
+ state->ready = 1;
+
/* Now that we're attached, announce our presence to the world. */
ddi_report_dev(dip);
-#if defined(MOUSE8042_DEBUG)
- cmn_err(CE_CONT, "?%s #%d\n", DRIVER_NAME(dip), ddi_get_instance(dip));
-#endif
return (DDI_SUCCESS);
fail_3:
@@ -383,11 +383,14 @@ mouse8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
switch (cmd) {
case DDI_SUSPEND:
+ /* Ignore all data from mouse8042_intr until we fully resume */
+ state->ready = 0;
return (DDI_SUCCESS);
case DDI_DETACH:
ddi_remove_intr(dip, 0, state->ms_iblock_cookie);
mouse8042_dip = NULL;
+ mutex_destroy(&state->reset_mutex);
mutex_destroy(&state->ms_mutex);
ddi_prop_remove_all(dip);
ddi_regs_map_free(&state->ms_handle);
@@ -396,12 +399,6 @@ mouse8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
return (DDI_SUCCESS);
default:
-#ifdef MOUSE8042_DEBUG
- if (mouse8042_debug) {
- cmn_err(CE_CONT,
- "mouse8042_detach: cmd = %d unknown\n", cmd);
- }
-#endif
return (DDI_FAILURE);
}
}
@@ -419,10 +416,6 @@ mouse8042_getinfo(
minor_t minor = getminor(dev);
int instance = MOUSE8042_MINOR_TO_INSTANCE(minor);
-#ifdef MOUSE8042_DEBUG
- if (mouse8042_debug)
- cmn_err(CE_CONT, "mouse8042_getinfo: call\n");
-#endif
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
if (mouse8042_dip == NULL)
@@ -457,11 +450,6 @@ mouse8042_open(
state = ddi_get_driver_private(mouse8042_dip);
-#ifdef MOUSE8042_DEBUG
- if (mouse8042_debug)
- cmn_err(CE_CONT, "mouse8042_open:entered\n");
-#endif
-
mutex_enter(&state->ms_mutex);
if (state->ms_opened) {
@@ -545,15 +533,23 @@ mouse8042_close(queue_t *q, int flag, cred_t *cred_p)
state = (struct mouse_state *)q->q_ptr;
-#ifdef MOUSE8042_DEBUG
- if (mouse8042_debug)
- cmn_err(CE_CONT, "mouse8042_close:entered\n");
-#endif
-
mutex_enter(&state->ms_mutex);
qprocsoff(q);
+ if (state->reset_tid != 0) {
+ (void) quntimeout(q, state->reset_tid);
+ state->reset_tid = 0;
+ }
+ if (state->bc_id != 0) {
+ (void) qunbufcall(q, state->bc_id);
+ state->bc_id = 0;
+ }
+ if (state->reply_mp != NULL) {
+ freemsg(state->reply_mp);
+ state->reply_mp = NULL;
+ }
+
q->q_ptr = NULL;
WR(q)->q_ptr = NULL;
state->ms_rqp = NULL;
@@ -600,78 +596,267 @@ mouse8042_iocnack(
qreply(qp, mp);
}
+static void
+mouse8042_reset_timeout(void *argp)
+{
+ struct mouse_state *state = (struct mouse_state *)argp;
+ mblk_t *mp;
+
+ mutex_enter(&state->reset_mutex);
+
+ /*
+ * If the interrupt handler hasn't completed the reset handling
+ * (reset_state would be IDLE or FAILED in that case), then
+ * drop the 8042 lock, and send a faked retry reply upstream,
+ * then enable the queue for further message processing.
+ */
+ if (state->reset_state != MSE_RESET_IDLE &&
+ state->reset_state != MSE_RESET_FAILED) {
+
+ state->reset_tid = 0;
+ state->reset_state = MSE_RESET_IDLE;
+
+ (void) ddi_get8(state->ms_handle, state->ms_addr +
+ I8042_UNLOCK);
+
+ mp = state->reply_mp;
+ *mp->b_wptr++ = MSERESEND;
+ state->reply_mp = NULL;
+
+ if (state->ms_rqp != NULL)
+ putnext(state->ms_rqp, mp);
+ else
+ freemsg(mp);
+
+ ASSERT(state->ms_wqp != NULL);
+
+ enableok(state->ms_wqp);
+ qenable(state->ms_wqp);
+ }
+
+ mutex_exit(&state->reset_mutex);
+}
+
+/*
+ * Returns 1 if the caller should put the message (bp) back on the queue
+ */
static int
-mouse8042_wput(queue_t *q, mblk_t *mp)
+mouse8042_process_reset(queue_t *q, mblk_t *mp, struct mouse_state *state)
+{
+ mutex_enter(&state->reset_mutex);
+ /*
+ * If we're in the middle of a reset, put the message back on the queue
+ * for processing later.
+ */
+ if (state->reset_state != MSE_RESET_IDLE) {
+ /*
+ * We noenable the queue again here in case it was backenabled
+ * by an upper-level module.
+ */
+ noenable(q);
+
+ mutex_exit(&state->reset_mutex);
+ return (1);
+ }
+
+ /*
+ * Drop the reset state lock before allocating the response message and
+ * grabbing the 8042 exclusive-access lock (since those operations
+ * may take an extended period of time to complete).
+ */
+ mutex_exit(&state->reset_mutex);
+
+ state->reply_mp = allocb(3, BPRI_MED);
+ if (state->reply_mp == NULL) {
+ /*
+ * Allocation failed -- set up a bufcall to enable the queue
+ * whenever there is enough memory to allocate the response
+ * message.
+ */
+ state->bc_id = qbufcall(q, 3, BPRI_MED,
+ (void (*)(void *))qenable, q);
+
+ if (state->bc_id == 0) {
+ /*
+ * If the qbufcall failed, we cannot proceed, so use the
+ * message we were sent to respond with an error.
+ */
+ *mp->b_rptr = MSEERROR;
+ mp->b_wptr = mp->b_rptr + 1;
+ qreply(q, mp);
+ return (0);
+ }
+
+ return (1);
+ }
+
+ /*
+ * Gain exclusive access to the 8042 for the duration of the reset.
+ * The unlock will occur when the reset has either completed or timed
+ * out.
+ */
+ (void) ddi_get8(state->ms_handle,
+ state->ms_addr + I8042_LOCK);
+
+ mutex_enter(&state->reset_mutex);
+
+ state->reset_state = MSE_RESET_PRE;
+ noenable(q);
+
+ state->reset_tid = qtimeout(q,
+ mouse8042_reset_timeout,
+ state,
+ drv_usectohz(
+ MOUSE8042_RESET_TIMEOUT_USECS));
+
+ ddi_put8(state->ms_handle,
+ state->ms_addr +
+ I8042_INT_OUTPUT_DATA, MSERESET);
+
+ mp->b_rptr++;
+
+ mutex_exit(&state->reset_mutex);
+ return (1);
+}
+
+/*
+ * Returns 1 if the caller should stop processing messages
+ */
+static int
+mouse8042_process_data_msg(queue_t *q, mblk_t *mp, struct mouse_state *state)
{
- struct iocblk *iocbp;
mblk_t *bp;
mblk_t *next;
- struct mouse_state *state;
- state = (struct mouse_state *)q->q_ptr;
+ bp = mp;
+ do {
+ while (bp->b_rptr < bp->b_wptr) {
+ /*
+ * Detect an attempt to reset the mouse. Lock out any
+ * further mouse writes until the reset has completed.
+ */
+ if (*bp->b_rptr == MSERESET) {
+
+ /*
+ * If we couldn't allocate memory and we
+ * we couldn't register a bufcall,
+ * mouse8042_process_reset returns 0 and
+ * has already used the message to send an
+ * error reply back upstream, so there is no
+ * need to deallocate or put this message back
+ * on the queue.
+ */
+ if (mouse8042_process_reset(q, bp, state) == 0)
+ return (1);
+
+ /*
+ * If there's no data remaining in this block,
+ * free this block and put the following blocks
+ * of this message back on the queue. If putting
+ * the rest of the message back on the queue
+ * fails, free the the message.
+ */
+ if (MBLKL(bp) == 0) {
+ next = bp->b_cont;
+ freeb(bp);
+ bp = next;
+ }
+ if (bp != NULL) {
+ if (!putbq(q, bp))
+ freemsg(bp);
+ }
+
+ return (1);
+
+ }
+ ddi_put8(state->ms_handle,
+ state->ms_addr + I8042_INT_OUTPUT_DATA,
+ *bp->b_rptr++);
+ }
+ next = bp->b_cont;
+ freeb(bp);
+ } while ((bp = next) != NULL);
+
+ return (0);
+}
+
+static int
+mouse8042_process_msg(queue_t *q, mblk_t *mp, struct mouse_state *state)
+{
+ struct iocblk *iocbp;
+ int rv = 0;
-#ifdef MOUSE8042_DEBUG
- if (mouse8042_debug)
- cmn_err(CE_CONT, "mouse8042_wput:entered\n");
-#endif
iocbp = (struct iocblk *)mp->b_rptr;
+
switch (mp->b_datap->db_type) {
case M_FLUSH:
-#ifdef MOUSE8042_DEBUG
- if (mouse8042_debug)
- cmn_err(CE_CONT, "mouse8042_wput:M_FLUSH\n");
-#endif
-
- if (*mp->b_rptr & FLUSHW)
+ if (*mp->b_rptr & FLUSHW) {
flushq(q, FLUSHDATA);
- qreply(q, mp);
+ *mp->b_rptr &= ~FLUSHW;
+ }
+ if (*mp->b_rptr & FLUSHR) {
+ qreply(q, mp);
+ } else
+ freemsg(mp);
break;
case M_IOCTL:
-#ifdef MOUSE8042_DEBUG
- if (mouse8042_debug)
- cmn_err(CE_CONT, "mouse8042_wput:M_IOCTL\n");
-#endif
mouse8042_iocnack(q, mp, iocbp, EINVAL, 0);
break;
case M_IOCDATA:
-#ifdef MOUSE8042_DEBUG
- if (mouse8042_debug)
- cmn_err(CE_CONT, "mouse8042_wput:M_IOCDATA\n");
-#endif
mouse8042_iocnack(q, mp, iocbp, EINVAL, 0);
break;
case M_DATA:
- bp = mp;
- do {
- while (bp->b_rptr < bp->b_wptr) {
-#if defined(MOUSE8042_DEBUG)
- if (mouse8042_debug) {
- cmn_err(CE_CONT,
- "mouse8042: send %2x\n",
- *bp->b_rptr);
- }
- if (mouse8042_debug_minimal) {
- cmn_err(CE_CONT, ">a:%2x ",
- *bp->b_rptr);
- }
-#endif
- ddi_put8(state->ms_handle,
- state->ms_addr + I8042_INT_OUTPUT_DATA,
- *bp->b_rptr++);
- }
- next = bp->b_cont;
- freeb(bp);
- } while ((bp = next) != NULL);
+ rv = mouse8042_process_data_msg(q, mp, state);
break;
default:
freemsg(mp);
break;
}
-#ifdef MOUSE8042_DEBUG
- if (mouse8042_debug)
- cmn_err(CE_CONT, "mouse8042_wput:leaving\n");
-#endif
- return (0); /* ignored */
+
+ return (rv);
+}
+
+static int
+mouse8042_wsrv(queue_t *qp)
+{
+ mblk_t *mp;
+ struct mouse_state *state;
+ state = (struct mouse_state *)qp->q_ptr;
+
+ while ((mp = getq(qp)) != NULL) {
+ if (mouse8042_process_msg(qp, mp, state) != 0)
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * Returns the next reset state, given the current state and the byte
+ * received from the mouse. Error and Resend codes are handled by the
+ * caller.
+ */
+static mouse8042_reset_state_e
+mouse8042_reset_fsm(mouse8042_reset_state_e reset_state, uint8_t mdata)
+{
+ switch (reset_state) {
+ case MSE_RESET_PRE: /* RESET sent, now we expect an ACK */
+ if (mdata == MSE_ACK) /* Got the ACK */
+ return (MSE_RESET_ACK);
+ break;
+
+ case MSE_RESET_ACK: /* ACK received; now we expect 0xAA */
+ if (mdata == MSE_AA) /* Got the 0xAA */
+ return (MSE_RESET_AA);
+ break;
+
+ case MSE_RESET_AA: /* 0xAA received; now we expect 0x00 */
+ if (mdata == MSE_00)
+ return (MSE_RESET_IDLE);
+ break;
+ }
+
+ return (reset_state);
}
static uint_t
@@ -684,10 +869,6 @@ mouse8042_intr(caddr_t arg)
mutex_enter(&state->ms_mutex);
-#if defined(MOUSE8042_DEBUG)
- if (mouse8042_debug)
- cmn_err(CE_CONT, "mouse8042_intr()\n");
-#endif
rc = DDI_INTR_UNCLAIMED;
for (;;) {
@@ -700,24 +881,81 @@ mouse8042_intr(caddr_t arg)
mdata = ddi_get8(state->ms_handle,
state->ms_addr + I8042_INT_INPUT_DATA);
-#if defined(MOUSE8042_DEBUG)
- if (mouse8042_debug)
- cmn_err(CE_CONT, "mouse8042_intr: got %2x\n", mdata);
- if (mouse8042_debug_minimal)
- cmn_err(CE_CONT, "<A:%2x ", mdata);
-#endif
-
rc = DDI_INTR_CLAIMED;
+ /*
+ * If we're not ready for this data, discard it.
+ */
+ if (!state->ready)
+ continue;
+
+ mutex_enter(&state->reset_mutex);
+ if (state->reset_state != MSE_RESET_IDLE) {
+
+ if (mdata == MSEERROR || mdata == MSERESET) {
+ state->reset_state = MSE_RESET_FAILED;
+ } else {
+ state->reset_state =
+ mouse8042_reset_fsm(state->reset_state,
+ mdata);
+ }
+
+ /*
+ * If we transitioned back to the idle reset state (or
+ * the reset failed), disable the timeout, release the
+ * 8042 exclusive-access lock, then send the response
+ * the the upper-level modules. Finally, enable the
+ * queue and schedule queue service procedures so that
+ * upper-level modules can process the response.
+ * Otherwise, if we're still in the middle of the
+ * reset sequence, do not send the data up (since the
+ * response is sent at the end of the sequence, or
+ * on timeout/error).
+ */
+ if (state->reset_state == MSE_RESET_IDLE ||
+ state->reset_state == MSE_RESET_FAILED) {
+
+ mutex_exit(&state->reset_mutex);
+ (void) quntimeout(state->ms_wqp,
+ state->reset_tid);
+ mutex_enter(&state->reset_mutex);
+
+ (void) ddi_get8(state->ms_handle,
+ state->ms_addr + I8042_UNLOCK);
+
+ state->reset_tid = 0;
+ mp = state->reply_mp;
+ if (state->reset_state == MSE_RESET_FAILED) {
+ *mp->b_wptr++ = mdata;
+ } else {
+ *mp->b_wptr++ = MSE_ACK;
+ *mp->b_wptr++ = MSE_AA;
+ *mp->b_wptr++ = MSE_00;
+ }
+ state->reply_mp = NULL;
+
+ state->reset_state = MSE_RESET_IDLE;
+
+ if (state->ms_rqp != NULL)
+ putnext(state->ms_rqp, mp);
+ else
+ freemsg(mp);
+
+ enableok(state->ms_wqp);
+ qenable(state->ms_wqp);
+ }
+
+ mutex_exit(&state->reset_mutex);
+ mutex_exit(&state->ms_mutex);
+ return (rc);
+ }
+ mutex_exit(&state->reset_mutex);
+
if (state->ms_rqp != NULL && (mp = allocb(1, BPRI_MED))) {
*mp->b_wptr++ = mdata;
putnext(state->ms_rqp, mp);
}
}
-#ifdef MOUSE8042_DEBUG
- if (mouse8042_debug)
- cmn_err(CE_CONT, "mouse8042_intr() ok\n");
-#endif
mutex_exit(&state->ms_mutex);
return (rc);
diff --git a/usr/src/uts/common/io/vuidmice/vuidps2.c b/usr/src/uts/common/io/vuidmice/vuidps2.c
index cfc471e85d..dd8e93a7c0 100644
--- a/usr/src/uts/common/io/vuidmice/vuidps2.c
+++ b/usr/src/uts/common/io/vuidmice/vuidps2.c
@@ -59,18 +59,18 @@
#define PS2_DELTA_Y 3 /* Got delta X */
#define PS2_WHEEL_DELTA_Z 4
#define PS2_WHEEL5_DELTA_Z 5
-#define PS2_WAIT_RESET_ACK 6
-#define PS2_WAIT_RESET_AA 7
-#define PS2_WAIT_RESET_00 8
+#define PS2_WAIT_RESET_COMPLETE 6
+#define PS2_WAIT_FOR_AA 7
+#define PS2_WAIT_FOR_00 8
#define PS2_WAIT_SETRES0_ACK1 9
#define PS2_WAIT_SETRES0_ACK2 10 /* -+ must be consecutive */
#define PS2_WAIT_SCALE1_1_ACK 11 /* | */
#define PS2_WAIT_SCALE1_2_ACK 12 /* | */
#define PS2_WAIT_SCALE1_3_ACK 13 /* -+ */
-#define PS2_WAIT_STATREQ_ACK 14
-#define PS2_WAIT_STATUS_1 15
-#define PS2_WAIT_STATUS_BUTTONS 16
-#define PS2_WAIT_STATUS_REV 17
+#define PS2_WAIT_STATREQ_ACK 14 /* -+ */
+#define PS2_WAIT_STATUS_1 15 /* | */
+#define PS2_WAIT_STATUS_BUTTONS 16 /* | must stay in this order */
+#define PS2_WAIT_STATUS_REV 17 /* -+ */
#define PS2_WAIT_STATUS_3 18
#define PS2_WAIT_WHEEL_SMPL1_CMD_ACK 19 /* Set the sample rate to 200 */
#define PS2_WAIT_WHEEL_SMPL1_RATE_ACK 20
@@ -94,9 +94,6 @@
#define PS2_WAIT_STREAM_ACK 38
#define PS2_WAIT_ON_ACK 39
-#define MSE_AA 0xaa
-#define MSE_00 0x00
-
#define MOUSE_MODE_PLAIN 0 /* Normal PS/2 mouse - 3 byte msgs */
#define MOUSE_MODE_WHEEL 1 /* Wheel mouse - 4 byte msgs */
#define MOUSE_MODE_WHEEL5 2 /* Wheel + 5 btn mouse - 4 byte msgs */
@@ -115,7 +112,6 @@
#define PS2_MAX_INIT_COUNT 5
-
static void vuidmice_send_wheel_event(queue_t *const, uchar_t,
uchar_t, uchar_t, int);
extern void VUID_PUTNEXT(queue_t *const, uchar_t, uchar_t, uchar_t, int);
@@ -126,7 +122,6 @@ static void VUID_INIT_TIMEOUT(void *q);
* We apply timeout to nearly each command-response
* during initialization:
*
- * Set timeout for RESET
* Set timeout for SET RESOLUTION
* Set timeout for SET SCALE
* Set timeout for SET SAMPLE RATE
@@ -141,7 +136,8 @@ static void VUID_INIT_TIMEOUT(void *q);
static void
vuid_set_timeout(queue_t *const qp, clock_t time)
{
- ASSERT(STATEP->init_tid == 0);
+ if (STATEP->init_tid != 0)
+ (void) quntimeout(qp, STATEP->init_tid);
STATEP->init_tid = qtimeout(qp, VUID_INIT_TIMEOUT,
qp, drv_usectohz(time));
}
@@ -149,7 +145,6 @@ vuid_set_timeout(queue_t *const qp, clock_t time)
static void
vuid_cancel_timeout(queue_t *const qp)
{
- ASSERT(STATEP->init_tid != 0);
(void) quntimeout(qp, STATEP->init_tid);
STATEP->init_tid = 0;
}
@@ -214,6 +209,30 @@ put1(queue_t *const qp, int c)
}
}
+static void
+vuidmice_start_wdc_or_setres(queue_t *qp)
+{
+ /* Set timeout for set res or sample rate */
+ vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_GROUP);
+
+ /*
+ * Start the wheel-mouse detection code. First, we look
+ * for standard wheel mice. If we set the sample rate
+ * to 200, 100, and then 80 and finally request the
+ * device ID, a wheel mouse will return an ID of 0x03.
+ * After that, we'll try for the wheel+5 variety. The
+ * incantation in this case is 200, 200, and 80. We'll
+ * get 0x04 back in that case.
+ */
+ if (STATEP->inited & PS2_FLAG_NO_EXTN) {
+ STATEP->state = PS2_WAIT_SETRES3_ACK1;
+ put1(WR(qp), MSESETRES);
+ } else {
+ STATEP->state = PS2_WAIT_WHEEL_SMPL1_CMD_ACK;
+ put1(WR(qp), MSECHGMOD);
+ }
+}
+
int
VUID_OPEN(queue_t *const qp)
{
@@ -222,10 +241,7 @@ VUID_OPEN(queue_t *const qp)
STATEP->inited = 0;
STATEP->nbuttons = 3;
- STATEP->state = PS2_WAIT_RESET_ACK;
-
- /* Set timeout for reset */
- vuid_set_timeout(qp, PS2_INIT_TMOUT_RESET);
+ STATEP->state = PS2_WAIT_RESET_COMPLETE;
put1(WR(qp), MSERESET);
@@ -277,24 +293,49 @@ VUID_INIT_TIMEOUT(void *q)
STATEP->inited |= PS2_FLAG_NO_EXTN;
}
- if (++STATEP->init_count >= PS2_MAX_INIT_COUNT) {
- STATEP->inited |= PS2_FLAG_INIT_TIMEOUT;
+
+ /*
+ * If the Logitech button detection sequence timed out at some point
+ * in the sequence, ignore it and skip to the next step in
+ * initialization. Do NOT count this as a timeout, so do NOT
+ * increment init_count.
+ */
+ if (STATEP->state >= PS2_WAIT_STATREQ_ACK &&
+ STATEP->state <= PS2_WAIT_STATUS_REV) {
+
+ /* See the comment under the PS2_WAIT_STATUS_BUTTONS case */
+#if defined(VUID3PS2)
+ STATEP->nbuttons = 3;
+#else
+ STATEP->nbuttons = 2;
+#endif
+ vuidmice_start_wdc_or_setres(qp);
return;
}
+ /*
+ * If the initialization process has timed out too many times, even if
+ * a subset of the process was successful, stop trying and put the
+ * mouse in the only state from which it can recover -- waiting for an
+ * 0xAA 0x00 response (i.e. like one we get on resume from mouse8042
+ * or from a replug).
+ */
+ if (++STATEP->init_count >= PS2_MAX_INIT_COUNT) {
+ STATEP->inited |= PS2_FLAG_INIT_TIMEOUT;
+ STATEP->state = PS2_WAIT_FOR_AA;
+ } else {
- STATEP->state = PS2_WAIT_RESET_ACK;
-
- vuid_set_timeout(qp, PS2_INIT_TMOUT_RESET);
+ STATEP->state = PS2_WAIT_RESET_COMPLETE;
- /* try again */
- put1(WR(qp), MSERESET);
+ /* Try to reset the mouse again */
+ put1(WR(qp), MSERESET);
+ }
}
void
VUID_QUEUE(queue_t *const qp, mblk_t *mp)
{
- int code;
+ int code, length;
clock_t now;
clock_t elapsed;
clock_t mouse_timeout;
@@ -305,6 +346,7 @@ VUID_QUEUE(queue_t *const qp, mblk_t *mp)
STATEP->last_event_lbolt = now;
while (mp->b_rptr < mp->b_wptr) {
+ length = MBLKL(mp);
code = *mp->b_rptr++;
switch (STATEP->state) {
@@ -391,10 +433,39 @@ restart:
break;
+ case PS2_WAIT_FOR_AA:
+ /*
+ * On Dell latitude D800, the initial MSE_ACK is
+ * received after the initialization sequence
+ * times out, so restart it here.
+ */
+ if (code == MSE_ACK) {
+ STATEP->inited &= ~PS2_FLAG_INIT_TIMEOUT;
+ STATEP->init_count = 0;
+ STATEP->state = PS2_WAIT_RESET_COMPLETE;
+ put1(WR(qp), MSERESET);
+ break;
+ }
+
+ if (code != MSE_AA)
+ break;
+
+ STATEP->state = PS2_WAIT_FOR_00;
+ break;
+
+ case PS2_WAIT_FOR_00:
+ if (code != MSE_00)
+ break;
+
+ STATEP->inited &= ~PS2_FLAG_INIT_TIMEOUT;
+ STATEP->init_count = 0;
+ STATEP->state = PS2_WAIT_RESET_COMPLETE;
+ put1(WR(qp), MSERESET);
+ break;
+
case PS2_MAYBE_REATTACH:
if (code == MSE_00) {
- STATEP->state = PS2_WAIT_RESET_ACK;
- vuid_set_timeout(qp, PS2_INIT_TMOUT_RESET);
+ STATEP->state = PS2_WAIT_RESET_COMPLETE;
put1(WR(qp), MSERESET);
break;
}
@@ -542,48 +613,48 @@ packet_complete:
STATEP->deltax = STATEP->deltay = 0;
break;
- case PS2_WAIT_RESET_ACK:
- if (code != MSE_ACK) {
- break;
- }
+ case PS2_WAIT_RESET_COMPLETE:
/*
- * On Dell latitude D800, we find that the MSE_ACK is
- * coming up even after timeout in VUID_OPEN during
- * early boot. So here (PS2_WAIT_RESET_ACK) we check
- * if timeout happened before, if true, we reset the
- * timeout to restart the initialization.
+ * If length is 1, code holds the data from the message.
+ * for lengths > 1, we look at *(mp->b_rptr + offset)
+ * for the rest of the data.
*/
- if (STATEP->inited & PS2_FLAG_INIT_TIMEOUT) {
- STATEP->inited &= ~PS2_FLAG_INIT_TIMEOUT;
- STATEP->init_count = 0;
- vuid_set_timeout(qp, PS2_INIT_TMOUT_RESET);
+ if (length == 1) {
+ /*
+ * Technically, a length of 1 from the mouse
+ * driver should only contain a MSEERROR or
+ * MSERESEND value, but we don't test
+ * that here because we'd be issuing a reset
+ * anyway. For mice that are not connected,
+ * we will receive a MSERESEND quickly.
+ */
+ if (++STATEP->init_count >=
+ PS2_MAX_INIT_COUNT) {
+ STATEP->inited |= PS2_FLAG_INIT_TIMEOUT;
+ STATEP->state = PS2_WAIT_FOR_AA;
+ } else {
+ put1(WR(qp), MSERESET);
+ }
break;
- }
-
- STATEP->state = PS2_WAIT_RESET_AA;
- break;
- case PS2_WAIT_RESET_AA:
- if (code != MSE_AA) {
+ } else if (length != 3) {
break;
}
- STATEP->state = PS2_WAIT_RESET_00;
- break;
- case PS2_WAIT_RESET_00:
- if (code != MSE_00) {
- break;
- }
+ ASSERT(code == MSE_ACK && *(mp->b_rptr + 1) == 0xAA &&
+ *(mp->b_rptr + 2) == 0x00);
- /* Reset has been ok */
- vuid_cancel_timeout(qp);
+ mp->b_rptr += 2; /* Skip past the 0xAA 0x00 */
+
+ /* Reset completed successfully */
STATEP->state = PS2_WAIT_SETRES0_ACK1;
/* Set timeout for set res */
vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_GROUP);
+ /* Begin Logitech autodetect sequence */
put1(WR(qp), MSESETRES);
break;
@@ -673,30 +744,12 @@ packet_complete:
case PS2_WAIT_STATUS_3:
- /* Status request has been ok */
+ /* Status request completed successfully */
vuid_cancel_timeout(qp);
- /* Set timeout for set res or sample rate */
- vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_GROUP);
-
- /*
- * Start the wheel-mouse detection code. First, we look
- * for standard wheel mice. If we set the sample rate
- * to 200, 100, and then 80 and finally request the
- * device ID, a wheel mouse will return an ID of 0x03.
- * After that, we'll try for the wheel+5 variety. The
- * incantation in this case is 200, 200, and 80. We'll
- * get 0x04 back in that case.
- */
- if (STATEP->inited & PS2_FLAG_NO_EXTN) {
- STATEP->state = PS2_WAIT_SETRES3_ACK1;
- put1(WR(qp), MSESETRES);
- } else {
- STATEP->state = PS2_WAIT_WHEEL_SMPL1_CMD_ACK;
- put1(WR(qp), MSECHGMOD);
- }
-
+ vuidmice_start_wdc_or_setres(qp);
break;
+
case PS2_WAIT_WHEEL_SMPL1_CMD_ACK:
if (code != MSE_ACK) {
break;
@@ -741,7 +794,7 @@ packet_complete:
break;
}
- /* Set sample rate has been ok */
+ /* Set sample rate completed successfully */
vuid_cancel_timeout(qp);
STATEP->state = PS2_WAIT_WHEEL_DEV_CMD;
@@ -761,7 +814,7 @@ packet_complete:
case PS2_WAIT_WHEEL_DEV_ACK:
- /* Get dev has been ok */
+ /* Get dev completed successfully */
vuid_cancel_timeout(qp);
if (code != 0x03) {
@@ -837,7 +890,7 @@ packet_complete:
break;
}
- /* Set sample rate has been ok */
+ /* Set sample rate completed successfully */
vuid_cancel_timeout(qp);
STATEP->state = PS2_WAIT_WHEEL5_DEV_CMD;
@@ -869,7 +922,7 @@ packet_complete:
MOUSE_MODE_WHEEL;
}
- /* Wheel5 get dev has been ok */
+ /* Wheel5 get dev completed successfully */
vuid_cancel_timeout(qp);
/* FALLTHROUGH */
@@ -897,7 +950,7 @@ packet_complete:
break;
}
- /* Set res has been ok */
+ /* Set res completed successfully */
vuid_cancel_timeout(qp);
STATEP->state = PS2_WAIT_STREAM_ACK;
@@ -922,9 +975,17 @@ packet_complete:
break;
}
- /* Enable has been ok */
+ /* Enable completed successfully */
+
+ /*
+ * The entire initialization sequence
+ * is complete. Now, we can clear the
+ * init_count retry counter.
+ */
+ STATEP->init_count = 0;
vuid_cancel_timeout(qp);
+
STATEP->state = PS2_START;
break;
}
diff --git a/usr/src/uts/common/sys/i8042.h b/usr/src/uts/common/sys/i8042.h
index ad115f551b..c780ae9625 100644
--- a/usr/src/uts/common/sys/i8042.h
+++ b/usr/src/uts/common/sys/i8042.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -56,21 +56,18 @@ extern "C" {
#define I8042_INT_INPUT_AVAIL 0x00
#define I8042_INT_INPUT_DATA 0x01
#define I8042_INT_OUTPUT_DATA 0x03
-#define I8042_INT_CMD_PLUS_PARAM 0x05 /* See comment below */
+#define I8042_LOCK 0x05 /* See comment below */
#define I8042_POLL_INPUT_AVAIL 0x10
#define I8042_POLL_INPUT_DATA 0x11
#define I8042_POLL_OUTPUT_DATA 0x13
-#define I8042_POLL_CMD_PLUS_PARAM 0x15 /* See comment below */
+#define I8042_UNLOCK 0x15 /* See comment below */
/*
- * The I8042_INT_CMD_PLUS_PARAM and I8042_POLL_CMD_PLUS_PARAM virtual
- * registers are meant to be used with i8042_rep_put8() [via
- * ddi_rep_put8 in child drivers], which is designed to allow commands
- * that have responses (or that have responses plus an option byte) to
- * execute atomically with respect to commands from other children
- * (some 8042 implementations get confused when other child devices
- * intersperse their own commands while a command to a different
- * 8042-connected device is in flight).
+ * The I8042_LOCK and I8042_UNLOCK virtual
+ * registers are meant to be used by child drivers that require exclusive
+ * access to the 8042 registers for an atomic transaction (e.g. keyboard
+ * enable, mouse reset) that consists of multiple single-byte commands
+ * and (possibly) their arguments.
*/
/* Softint priority used */
diff --git a/usr/src/uts/common/sys/mouse.h b/usr/src/uts/common/sys/mouse.h
index 54d15e3315..65e4632ae1 100644
--- a/usr/src/uts/common/sys/mouse.h
+++ b/usr/src/uts/common/sys/mouse.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,8 +19,8 @@
* CDDL HEADER END
*/
/*
- * Copyright (c) 1999 by Sun Microsystems, Inc.
- * All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
*/
/* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */
@@ -31,77 +30,16 @@
#ifndef _SYS_MOUSE_H
#define _SYS_MOUSE_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifdef __cplusplus
extern "C" {
#endif
-#define BUTCHNGMASK 0x38
-#define MOVEMENT 0x40
-
-#define BUTSTATMASK 7
-#define BUT3STAT 1
-#define BUT2STAT 2
-#define BUT1STAT 4
-#define BUT3CHNG 8
-#define BUT2CHNG 0x10
-#define BUT1CHNG 0x20
-
-struct mse_event {
- uchar_t type; /* event type (see below) */
- uchar_t code; /* when type is XQ_MOTION or XQ_BUTTON, => */
- /* bit 0 clear if right button pushed; */
- /* bit 1 clear if middle button pushed; */
- /* bit 2 clear if left button pushed; */
- char x; /* delta x movement (mouse motion only) */
- char y; /* delta y movement (mouse motion only) */
-};
-
-#define MSE_BUTTON 0
-#define MSE_MOTION 1
-
-struct mouseinfo {
- unsigned char status;
- char xmotion, ymotion;
-};
-
-/* Ioctl Command definitions */
-
-#define MOUSEIOC ('M'<<8)
-#define MOUSEIOCREAD (MOUSEIOC|60)
-#define MOUSEISOPEN (MOUSEIOC|66)
-#define MOUSE320 (MOUSEIOC|67)
-#define MSEBUTTONS (MOUSEIOC|68)
-#define TS_CALIB (MOUSEIOC|70) /* Touch screen: set the calibration */
-#define TS_RECALIB (MOUSEIOC|71) /* Touch screen: disable calibration */
-#define TS_CURPOS (MOUSEIOC|72) /* Touch screen: set cursor position */
-#define MOUSEIOCDELAY (MOUSEIOC|80)
-#define MOUSEIOCNDELAY (MOUSEIOC|81)
-#define MOUSEIOCCONFIG (MOUSEIOC|100)
-#define MOUSEIOCMON (MOUSEIOC|101)
-
-#define VPC_MOUSE_READ MOUSEIOCREAD
-
-#define UPPERLIM 127
-#define LOWERLIM -128
-#define ONEBYTE(x) ((x) > UPPERLIM ? UPPERLIM : \
- (x) < LOWERLIM ? LOWERLIM : (x))
-
-/* 320 mouse command/query structure */
-
-struct cmd_320 {
- int cmd;
- int arg1;
- int arg2;
- int arg3;
-};
-
/*
* AT&T 320 (PS/2 style) Mouse Commands
*/
#define MSERESET 0xff /* reset mouse */
#define MSERESEND 0xfe /* resend last data stream */
+#define MSEERROR 0xfc /* error */
#define MSESETDEF 0xf6 /* set default status */
#define MSEOFF 0xf5 /* disable mouse */
#define MSEON 0xf4 /* enable mouse */
@@ -117,53 +55,12 @@ struct cmd_320 {
#define MSESCALE2 0xe7 /* set 2:1 scaling */
#define MSESCALE1 0xe6 /* set 1:1 scaling */
-/*
- * 320 mouse 8042 controller commands and flags
- */
-#define MSE_ROP 0xD0 /* read output port command */
-#define MSE_RIP 0xC0 /* read input port command */
-#define MSE_WOP 0xD3 /* write to loopback command */
-#define MSE_WAD 0xD4 /* write to device command */
-#define MSE_RCB 0x20 /* read command byte command */
-#define MSE_WCB 0x60 /* write command byte command */
-#define MSE_INBF 0x03 /* input/output buffer full flag */
-#define MSE_OUTBF 0x21 /* output buffer full flag */
-#define MSE_ENAB 0xA8 /* enable 8042 interface */
-#define MSE_DISAB 0xA7 /* disable 8042 interface */
#define MSE_ACK 0xFA /* Acknowledgement byte from 8042 */
-typedef struct mouseinfo MOUSEINFO;
+/* Post-reset return values */
+#define MSE_AA 0xaa
+#define MSE_00 0x00
-/*
- * Begin Carrol touch screen-specific definitions.
- */
-
-/*
- * Calibration data structure. Used with TS_CALIB ioctl to register the upper
- * left opto-coordinate that corresponds to the upper left corner of the active
- * video area, and the lower right opto-coordinate that corresponds to the
- * lower right corner of the active video area.
- */
-
-struct ts_calib {
- int c_ulx, /* upper left X opto-coordinate of active video area */
- c_uly, /* upper left Y opto-coordinate of active video area */
- c_lrx, /* lower right X opto-coordinate of active video area */
- c_lry; /* lower right Y opto-coordinate of active video area */
-};
-
-/*
- * Position cursor at the given "pixel" coordinate.
- */
-
-struct ts_curpos {
- int p_xpos, /* X cursor coordinate */
- p_ypos; /* Y cursor coordinate */
-};
-
-/*
- * End Carrol touch screen-specific definitions.
- */
#ifdef __cplusplus
}