diff options
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/uts/common/io/i8042.c | 385 | ||||
-rw-r--r-- | usr/src/uts/common/io/kb8042/at_keyprocess.c | 46 | ||||
-rw-r--r-- | usr/src/uts/common/io/kb8042/kb8042.c | 410 | ||||
-rw-r--r-- | usr/src/uts/common/io/kb8042/kb8042.h | 5 | ||||
-rw-r--r-- | usr/src/uts/common/io/mouse8042.c | 478 | ||||
-rw-r--r-- | usr/src/uts/common/io/vuidmice/vuidps2.c | 225 | ||||
-rw-r--r-- | usr/src/uts/common/sys/i8042.h | 19 | ||||
-rw-r--r-- | usr/src/uts/common/sys/mouse.h | 119 |
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 } |