Mercurial > illumos > illumos-gate
changeset 10803:32de18ad59f8
6881318 mouse8042 panics on access to /dev/kdmouse (Acer Aspire One, Synaptics)
6885611 Xorg synaptics driver no longer works after 6825031
author | Seth Goldberg <Seth.Goldberg@Sun.COM> |
---|---|
date | Thu, 15 Oct 2009 16:16:48 -0700 |
parents | 371d70657c0e |
children | f38860d83eb4 |
files | usr/src/uts/common/io/mouse8042.c usr/src/uts/common/io/vuidmice/vuidps2.c |
diffstat | 2 files changed, 159 insertions(+), 37 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/uts/common/io/mouse8042.c Thu Oct 15 14:52:42 2009 -0700 +++ b/usr/src/uts/common/io/mouse8042.c Thu Oct 15 16:16:48 2009 -0700 @@ -107,10 +107,12 @@ minor_t ms_minor; boolean_t ms_opened; kmutex_t reset_mutex; + kcondvar_t reset_cv; mouse8042_reset_state_e reset_state; timeout_id_t reset_tid; int ready; mblk_t *reply_mp; + mblk_t *reset_ack_mp; bufcall_id_t bc_id; }; @@ -119,6 +121,7 @@ cred_t *cred_p); static int mouse8042_close(queue_t *q, int flag, cred_t *cred_p); static int mouse8042_wsrv(queue_t *qp); +static int mouse8042_wput(queue_t *q, mblk_t *mp); static int mouse8042_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result); @@ -149,7 +152,7 @@ }; static struct qinit mouse8042_winit = { - putq, /* put */ + mouse8042_wput, /* put */ mouse8042_wsrv, /* service */ NULL, /* open */ NULL, /* close */ @@ -345,6 +348,7 @@ state->ms_iblock_cookie); mutex_init(&state->reset_mutex, NULL, MUTEX_DRIVER, state->ms_iblock_cookie); + cv_init(&state->reset_cv, NULL, CV_DRIVER, NULL); rc = ddi_add_intr(dip, 0, (ddi_iblock_cookie_t *)NULL, (ddi_idevice_cookie_t *)NULL, @@ -390,6 +394,7 @@ case DDI_DETACH: ddi_remove_intr(dip, 0, state->ms_iblock_cookie); mouse8042_dip = NULL; + cv_destroy(&state->reset_cv); mutex_destroy(&state->reset_mutex); mutex_destroy(&state->ms_mutex); ddi_prop_remove_all(dip); @@ -533,22 +538,47 @@ state = (struct mouse_state *)q->q_ptr; - mutex_enter(&state->ms_mutex); + /* + * Disable queue processing now, so that another reset cannot get in + * after we wait for the current reset (if any) to complete. + */ + qprocsoff(q); - qprocsoff(q); + mutex_enter(&state->reset_mutex); + while (state->reset_state != MSE_RESET_IDLE) { + /* + * Waiting for the previous reset to finish is + * non-interruptible. Some upper-level clients + * cannot deal with EINTR and will not close the + * STREAM properly, resulting in failure to reopen it + * within the same process. + */ + cv_wait(&state->reset_cv, &state->reset_mutex); + } if (state->reset_tid != 0) { (void) quntimeout(q, state->reset_tid); state->reset_tid = 0; } + + if (state->reply_mp != NULL) { + freemsg(state->reply_mp); + state->reply_mp = NULL; + } + + if (state->reset_ack_mp != NULL) { + freemsg(state->reset_ack_mp); + state->reset_ack_mp = NULL; + } + + mutex_exit(&state->reset_mutex); + + mutex_enter(&state->ms_mutex); + 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; @@ -615,6 +645,7 @@ state->reset_tid = 0; state->reset_state = MSE_RESET_IDLE; + cv_signal(&state->reset_cv); (void) ddi_get8(state->ms_handle, state->ms_addr + I8042_UNLOCK); @@ -641,7 +672,7 @@ * Returns 1 if the caller should put the message (bp) back on the queue */ static int -mouse8042_process_reset(queue_t *q, mblk_t *mp, struct mouse_state *state) +mouse8042_initiate_reset(queue_t *q, mblk_t *mp, struct mouse_state *state) { mutex_enter(&state->reset_mutex); /* @@ -666,15 +697,19 @@ */ mutex_exit(&state->reset_mutex); - state->reply_mp = allocb(3, BPRI_MED); - if (state->reply_mp == NULL) { + if (state->reply_mp == NULL) + state->reply_mp = allocb(2, BPRI_MED); + if (state->reset_ack_mp == NULL) + state->reset_ack_mp = allocb(1, BPRI_MED); + + if (state->reply_mp == NULL || state->reset_ack_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); + state->bc_id = qbufcall(q, (state->reply_mp == NULL) ? 2 : 1, + BPRI_MED, (void (*)(void *))qenable, q); if (state->bc_id == 0) { /* @@ -688,6 +723,9 @@ } return (1); + } else { + /* Bufcall completed successfully (or wasn't needed) */ + state->bc_id = 0; } /* @@ -740,13 +778,13 @@ /* * If we couldn't allocate memory and we * we couldn't register a bufcall, - * mouse8042_process_reset returns 0 and + * mouse8042_initiate_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) + if (mouse8042_initiate_reset(q, bp, state) == 0) return (1); /* @@ -816,6 +854,40 @@ return (rv); } +/* + * This is the main mouse input routine. Commands and parameters + * from upstream are sent to the mouse device immediately, unless + * the mouse is in the process of being reset, in which case + * commands are queued and executed later in the service procedure. + */ +static int +mouse8042_wput(queue_t *q, mblk_t *mp) +{ + struct mouse_state *state; + state = (struct mouse_state *)q->q_ptr; + + /* + * Process all messages immediately, unless a reset is in + * progress. If a reset is in progress, deflect processing to + * the service procedure. + */ + if (state->reset_state != MSE_RESET_IDLE) + return (putq(q, mp)); + + /* + * If there are still messages outstanding in the queue that + * the service procedure hasn't processed yet, put this + * message in the queue also, to ensure proper message + * ordering. + */ + if (q->q_first) + return (putq(q, mp)); + + (void) mouse8042_process_msg(q, mp, state); + + return (0); +} + static int mouse8042_wsrv(queue_t *qp) { @@ -900,6 +972,35 @@ mdata); } + if (state->reset_state == MSE_RESET_ACK) { + + /* + * We received an ACK from the mouse, so + * send it upstream immediately so that + * consumers depending on the immediate + * ACK don't time out. + */ + if (state->reset_ack_mp != NULL) { + + mp = state->reset_ack_mp; + + state->reset_ack_mp = NULL; + + if (state->ms_rqp != NULL) { + *mp->b_wptr++ = MSE_ACK; + putnext(state->ms_rqp, mp); + } else + freemsg(mp); + } + + if (state->ms_wqp != NULL) { + enableok(state->ms_wqp); + qenable(state->ms_wqp); + } + + } else if (state->reset_state == MSE_RESET_IDLE || + state->reset_state == MSE_RESET_FAILED) { + /* * If we transitioned back to the idle reset state (or * the reset failed), disable the timeout, release the @@ -912,8 +1013,6 @@ * 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, @@ -924,25 +1023,34 @@ state->ms_addr + I8042_UNLOCK); state->reset_tid = 0; - mp = state->reply_mp; - if (state->reset_state == MSE_RESET_FAILED) { - *mp->b_wptr++ = mdata; + if (state->reply_mp != NULL) { + mp = state->reply_mp; + if (state->reset_state == + MSE_RESET_FAILED) { + *mp->b_wptr++ = mdata; + } else { + *mp->b_wptr++ = MSE_AA; + *mp->b_wptr++ = MSE_00; + } + state->reply_mp = NULL; } else { - *mp->b_wptr++ = MSE_ACK; - *mp->b_wptr++ = MSE_AA; - *mp->b_wptr++ = MSE_00; + mp = NULL; } - state->reply_mp = NULL; state->reset_state = MSE_RESET_IDLE; + cv_signal(&state->reset_cv); - if (state->ms_rqp != NULL) - putnext(state->ms_rqp, mp); - else - freemsg(mp); + if (mp != NULL) { + if (state->ms_rqp != NULL) + putnext(state->ms_rqp, mp); + else + freemsg(mp); + } - enableok(state->ms_wqp); - qenable(state->ms_wqp); + if (state->ms_wqp != NULL) { + enableok(state->ms_wqp); + qenable(state->ms_wqp); + } } mutex_exit(&state->reset_mutex);
--- a/usr/src/uts/common/io/vuidmice/vuidps2.c Thu Oct 15 14:52:42 2009 -0700 +++ b/usr/src/uts/common/io/vuidmice/vuidps2.c Thu Oct 15 16:16:48 2009 -0700 @@ -622,13 +622,18 @@ */ 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. + * A response with length 1 from the mouse + * driver can be either an ACK (the first part + * of the reset reply) or either MSEERROR or + * MSERESEND. Issue another reset if either + * of the latter are received. For mice that + * are not connected, MSERESEND is received + * quickly. */ + + if (code == MSE_ACK) + break; + if (++STATEP->init_count >= PS2_MAX_INIT_COUNT) { STATEP->inited |= PS2_FLAG_INIT_TIMEOUT; @@ -636,13 +641,22 @@ } else { put1(WR(qp), MSERESET); } + break; - } else if (length != 3) { + } else if (length != 2) { break; } - mp->b_rptr += 2; /* Skip past the 0xAA 0x00 */ + /* + * The only possible 2-byte reply from mouse8042 is + * 0xAA 0x00. If the mouse doesn't send that, mouse8042 + * will send a 1-byte error message, handled above by + * resetting the mouse. + */ + + /* Skip past the 0x00 (since `code' contains 0xAA) */ + mp->b_rptr += 1; /* Reset completed successfully */