summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
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
}