| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * 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. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| */ |
| /* |
| * Copyright 2009 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| */ |
| |
| /* |
| * 2/3/5 Button PS/2 Mouse Protocol |
| * |
| * This module dynamically determines the number of buttons on the mouse. |
| */ |
| |
| #include <sys/param.h> |
| #include <sys/stream.h> |
| #include <sys/vuid_event.h> |
| #include "vuidmice.h" |
| #include <sys/vuid_wheel.h> |
| #include <sys/mouse.h> |
| #include <sys/strsun.h> |
| #include <sys/ddi.h> |
| #include <sys/sunddi.h> |
| |
| /* |
| * BUT(1) LEFT BUTTON |
| * BUT(2) MIDDLE BUTTON (if present) |
| * BUT(3) RIGHT BUTTON |
| */ |
| |
| #define PS2_BUTTONMASK 7 /* mask byte zero with this */ |
| |
| #define PS2_BUTTON_L (uchar_t)0x01 /* Left button pressed */ |
| #define PS2_BUTTON_R (uchar_t)0x02 /* Right button pressed */ |
| #define PS2_BUTTON_M (uchar_t)0x04 /* Middle button pressed */ |
| #define PS2_DATA_XSIGN (uchar_t)0x10 /* X data sign bit */ |
| #define PS2_DATA_YSIGN (uchar_t)0x20 /* Y data sign bit */ |
| |
| #define PS2_START 0 /* Beginning of packet */ |
| #define PS2_BUTTON 1 /* Got button status */ |
| #define PS2_MAYBE_REATTACH 2 /* Got button status */ |
| #define PS2_DELTA_Y 3 /* Got delta X */ |
| #define PS2_WHEEL_DELTA_Z 4 |
| #define PS2_WHEEL5_DELTA_Z 5 |
| #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 /* | 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 |
| #define PS2_WAIT_WHEEL_SMPL2_CMD_ACK 21 /* Set the sample rate to 200 */ |
| #define PS2_WAIT_WHEEL_SMPL2_RATE_ACK 22 |
| #define PS2_WAIT_WHEEL_SMPL3_CMD_ACK 23 /* Set the sample rate to 80 */ |
| #define PS2_WAIT_WHEEL_SMPL3_RATE_ACK 24 |
| #define PS2_WAIT_WHEEL_DEV_CMD 25 |
| #define PS2_WAIT_WHEEL_DEV_ACK 26 /* Detected wheel mouse */ |
| #define PS2_WAIT_WHEEL5_SMPL1_CMD_ACK 27 /* Set the sample rate to 200 */ |
| #define PS2_WAIT_WHEEL5_SMPL1_RATE_ACK 28 |
| #define PS2_WAIT_WHEEL5_SMPL2_CMD_ACK 29 /* Set the sample rate to 200 */ |
| #define PS2_WAIT_WHEEL5_SMPL2_RATE_ACK 30 |
| #define PS2_WAIT_WHEEL5_SMPL3_CMD_ACK 31 /* Set the sample rate to 100 */ |
| #define PS2_WAIT_WHEEL5_SMPL3_RATE_ACK 32 |
| #define PS2_WAIT_WHEEL5_DEV_CMD 33 |
| #define PS2_WAIT_WHEEL5_DEV_ACK 34 /* Detected 5 button mouse */ |
| #define PS2_WAIT_SETRES3_CMD 35 |
| #define PS2_WAIT_SETRES3_ACK1 36 |
| #define PS2_WAIT_SETRES3_ACK2 37 |
| #define PS2_WAIT_STREAM_ACK 38 |
| #define PS2_WAIT_ON_ACK 39 |
| |
| #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 */ |
| |
| #define PS2_FLAG_NO_EXTN 0x08 /* Mouse doesn't obey extended cmds */ |
| #define PS2_FLAG_INIT_DONE 0x01 /* Mouse has been inited successfully */ |
| #define PS2_FLAG_INIT_TIMEOUT 0x02 /* Mouse init timeout */ |
| |
| /* |
| * The RESET command takes more time |
| * before the PS/2 mouse is ready |
| */ |
| #define PS2_INIT_TMOUT_RESET 500000 /* 500ms for RESET command */ |
| #define PS2_INIT_TMOUT_PER_CMD 200000 /* 200ms for each command-response */ |
| #define PS2_INIT_TMOUT_PER_GROUP 500000 /* 500ms for group commands */ |
| |
| #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); |
| extern void uniqtime32(struct timeval32 *); |
| static void VUID_INIT_TIMEOUT(void *q); |
| |
| /* |
| * We apply timeout to nearly each command-response |
| * during initialization: |
| * |
| * Set timeout for SET RESOLUTION |
| * Set timeout for SET SCALE |
| * Set timeout for SET SAMPLE RATE |
| * Set timeout for STATUS REQUEST |
| * Set timeout for GET DEV |
| * Set timeout for SET STREAM MODE and ENABLE. |
| * |
| * But for simplicity, sometimes we just apply the timeout |
| * to a function with group commands (e.g. wheel-mouse detection). |
| * |
| */ |
| static void |
| vuid_set_timeout(queue_t *const qp, clock_t time) |
| { |
| if (STATEP->init_tid != 0) |
| (void) quntimeout(qp, STATEP->init_tid); |
| STATEP->init_tid = qtimeout(qp, VUID_INIT_TIMEOUT, |
| qp, drv_usectohz(time)); |
| } |
| |
| static void |
| vuid_cancel_timeout(queue_t *const qp) |
| { |
| (void) quntimeout(qp, STATEP->init_tid); |
| STATEP->init_tid = 0; |
| } |
| |
| /* |
| * vuidmice_send_wheel_event |
| * Convert wheel data to firm_events |
| */ |
| static void |
| vuidmice_send_wheel_event(queue_t *const qp, uchar_t event_id, |
| uchar_t event_pair_type, uchar_t event_pair, int event_value) |
| { |
| mblk_t *bp; |
| Firm_event *fep; |
| |
| if ((bp = allocb((int)sizeof (Firm_event), BPRI_HI)) == NULL) { |
| |
| return; |
| } |
| |
| fep = (void *)bp->b_wptr; |
| fep->id = vuid_id_addr(vuid_first(VUID_WHEEL)) | |
| vuid_id_offset(event_id); |
| fep->pair_type = event_pair_type; |
| fep->pair = event_pair; |
| fep->value = event_value; |
| uniqtime32(&fep->time); |
| bp->b_wptr += sizeof (Firm_event); |
| |
| if (canput(qp->q_next)) { |
| putnext(qp, bp); |
| } else { |
| (void) putbq(qp, bp); /* read side is blocked */ |
| } |
| } |
| |
| |
| static void |
| sendButtonEvent(queue_t *const qp) |
| { |
| static int bmap[3] = {1, 3, 2}; |
| uint_t b; |
| |
| /* for each button, see if it has changed */ |
| for (b = 0; b < STATEP->nbuttons; b++) { |
| uchar_t mask = 0x1 << b; |
| |
| if ((STATEP->buttons & mask) != (STATEP->oldbuttons & mask)) |
| VUID_PUTNEXT(qp, (uchar_t)BUT(bmap[b]), FE_PAIR_NONE, 0, |
| (STATEP->buttons & mask ? 1 : 0)); |
| } |
| } |
| |
| void |
| put1(queue_t *const qp, int c) |
| { |
| mblk_t *bp; |
| |
| if (bp = allocb(1, BPRI_MED)) { |
| *bp->b_wptr++ = (char)c; |
| putnext(qp, bp); |
| } |
| } |
| |
| 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) |
| { |
| STATEP->format = VUID_FIRM_EVENT; |
| STATEP->vuid_mouse_mode = MOUSE_MODE_PLAIN; |
| STATEP->inited = 0; |
| STATEP->nbuttons = 3; |
| |
| STATEP->state = PS2_WAIT_RESET_COMPLETE; |
| |
| put1(WR(qp), MSERESET); |
| |
| while ((STATEP->state != PS2_START) && |
| !(STATEP->inited & PS2_FLAG_INIT_TIMEOUT)) { |
| if (qwait_sig(qp) == 0) |
| break; |
| } |
| |
| /* |
| * Later the PS/2 mouse maybe re-attach, so here |
| * clear the init_count. |
| */ |
| STATEP->init_count = 0; |
| |
| return (0); |
| } |
| |
| void |
| VUID_CLOSE(queue_t *const qp) |
| { |
| if (STATEP->init_tid != 0) |
| vuid_cancel_timeout(qp); |
| } |
| |
| static void |
| VUID_INIT_TIMEOUT(void *q) |
| { |
| queue_t *qp = q; |
| |
| STATEP->init_tid = 0; |
| |
| /* |
| * Some mice do not even send an error in response to |
| * the wheel mouse sample commands, so if we're in any of |
| * the PS2_WAIT_WHEEL_SMPL* states, and there has been |
| * a timeout, assume the mouse cannot handle the extended |
| * (wheel mouse) commands. |
| */ |
| if ((STATEP->state == PS2_WAIT_WHEEL_SMPL1_CMD_ACK) || |
| (STATEP->state == PS2_WAIT_WHEEL_SMPL1_RATE_ACK) || |
| (STATEP->state == PS2_WAIT_WHEEL_SMPL2_RATE_ACK) || |
| (STATEP->state == PS2_WAIT_WHEEL_SMPL3_RATE_ACK)) { |
| /* |
| * We overload 'inited' to mark the PS/2 mouse |
| * as one which doesn't respond to extended commands. |
| */ |
| |
| STATEP->inited |= PS2_FLAG_NO_EXTN; |
| } |
| |
| |
| /* |
| * 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_COMPLETE; |
| |
| /* Try to reset the mouse again */ |
| put1(WR(qp), MSERESET); |
| } |
| } |
| |
| void |
| VUID_QUEUE(queue_t *const qp, mblk_t *mp) |
| { |
| int code, length; |
| clock_t now; |
| clock_t elapsed; |
| clock_t mouse_timeout; |
| |
| mouse_timeout = drv_usectohz(250000); |
| now = ddi_get_lbolt(); |
| elapsed = now - STATEP->last_event_lbolt; |
| STATEP->last_event_lbolt = now; |
| |
| while (mp->b_rptr < mp->b_wptr) { |
| length = MBLKL(mp); |
| code = *mp->b_rptr++; |
| |
| switch (STATEP->state) { |
| |
| /* |
| * Start state. We stay here if the start code is not |
| * received thus forcing us back into sync. When we get a |
| * start code the button mask comes with it forcing us to |
| * to the next state. |
| */ |
| restart: |
| case PS2_START: |
| |
| /* |
| * 3-byte packet format |
| * |
| * Bit 7 6 5 4 3 2 1 0 |
| * Byte ---- ---- ----- ----- -- ------ ------ ------ |
| * 1 Y_Ov X_Ov Y_Sgn X_Sgn 1 MdlBtn RgtBtn LftBtn |
| * 2 |<--------------X Movement----------------->| |
| * 3 |<--------------Y Movement----------------->| |
| * |
| * 4-byte wheel packet format |
| * |
| * Bit 7 6 5 4 3 2 1 0 |
| * Byte ---- ---- ----- ----- -- ------ ------ ------ |
| * 1 Y_Ov X_Ov Y_Sgn X_Sgn 1 MdlBtn RgtBtn LftBtn |
| * 2 |<--------------X Movement----------------->| |
| * 3 |<--------------Y Movement----------------->| |
| * 4 |<--------------Z Movement----------------->| |
| * |
| * 4-byte wheel+5 packet format |
| * |
| * Bit 7 6 5 4 3 2 1 0 |
| * Byte ---- ---- ----- ----- -- ------ ------ ------ |
| * 1 Y_Ov X_Ov Y_Sgn X_Sgn 1 MdlBtn RgtBtn LftBtn |
| * 2 |<--------------X Movement----------------->| |
| * 3 |<--------------Y Movement----------------->| |
| * 4 0 0 5_Btn 4_Btn Z3 Z2 Z1 Z0 |
| */ |
| |
| if (!(STATEP->inited & PS2_FLAG_INIT_DONE)) { |
| STATEP->sync_byte = code & 0x8; |
| STATEP->inited |= PS2_FLAG_INIT_DONE; |
| } |
| /* |
| * the PS/2 mouse data format doesn't have any sort of sync |
| * data to make sure we are in sync with the packet stream, |
| * but the Technical Reference manual states that bits 2 & 3 |
| * of the first byte are reserved. Logitech uses bit 2 for |
| * the middle button. We HOPE that noone uses bit 3 though, |
| * and decide we're out of sync if bit 3 is not set here. |
| */ |
| |
| if ((code ^ STATEP->sync_byte) & 0x08) { |
| /* bit 3 not set */ |
| STATEP->state = PS2_START; |
| break; /* toss the code */ |
| } |
| |
| /* get the button values */ |
| STATEP->buttons = code & PS2_BUTTONMASK; |
| if (STATEP->buttons != STATEP->oldbuttons) { |
| sendButtonEvent(qp); |
| STATEP->oldbuttons = STATEP->buttons; |
| } |
| |
| /* bit 5 indicates Y value is negative (the sign bit) */ |
| if (code & PS2_DATA_YSIGN) |
| STATEP->deltay = -1 & ~0xff; |
| else |
| STATEP->deltay = 0; |
| |
| /* bit 4 is X sign bit */ |
| if (code & PS2_DATA_XSIGN) |
| STATEP->deltax = -1 & ~0xff; |
| else |
| STATEP->deltax = 0; |
| |
| if (code == MSE_AA) |
| STATEP->state = PS2_MAYBE_REATTACH; |
| else |
| STATEP->state = PS2_BUTTON; |
| |
| 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_COMPLETE; |
| put1(WR(qp), MSERESET); |
| break; |
| } |
| /*FALLTHROUGH*/ |
| |
| case PS2_BUTTON: |
| /* |
| * Now for the 7 bits of delta x. "Or" in |
| * the sign bit and continue. This is ac- |
| * tually a signed 9 bit number, but I just |
| * truncate it to a signed char in order to |
| * avoid changing and retesting all of the |
| * mouse-related modules for this patch. |
| */ |
| if (elapsed > mouse_timeout) |
| goto restart; |
| STATEP->deltax |= code & 0xff; |
| STATEP->state = PS2_DELTA_Y; |
| break; |
| |
| case PS2_DELTA_Y: |
| /* |
| * This byte is delta Y. If this is a plain mouse, |
| * we're done. Wheel mice have two different flavors |
| * of fourth byte. |
| */ |
| |
| if (elapsed > mouse_timeout) { |
| goto restart; |
| } |
| STATEP->deltay |= code & 0xff; |
| |
| if (STATEP->vuid_mouse_mode == MOUSE_MODE_WHEEL) { |
| STATEP->state = PS2_WHEEL_DELTA_Z; |
| break; |
| } else if (STATEP->vuid_mouse_mode == |
| MOUSE_MODE_WHEEL5) { |
| STATEP->state = PS2_WHEEL5_DELTA_Z; |
| break; |
| } |
| goto packet_complete; |
| |
| case PS2_WHEEL5_DELTA_Z: |
| if (code & 0x10) { |
| /* fourth physical button */ |
| VUID_PUTNEXT(qp, (uchar_t)BUT(4), |
| FE_PAIR_NONE, 0, 1); |
| VUID_PUTNEXT(qp, (uchar_t)BUT(4), |
| FE_PAIR_NONE, 0, 0); |
| } else if (code & 0x20) { |
| /* fifth physical button */ |
| VUID_PUTNEXT(qp, (uchar_t)BUT(5), |
| FE_PAIR_NONE, 0, 1); |
| VUID_PUTNEXT(qp, (uchar_t)BUT(5), |
| FE_PAIR_NONE, 0, 0); |
| } |
| /*FALLTHROUGH*/ |
| |
| case PS2_WHEEL_DELTA_Z: |
| /* |
| * Check whether reporting vertical wheel |
| * movements is enabled |
| */ |
| code &= 0xf; |
| |
| if (STATEP->wheel_state_bf & (1 << |
| VUIDMICE_VERTICAL_WHEEL_ID)) { |
| /* |
| * PS/2 mouse reports -ve values |
| * when the wheel is scrolled up. So |
| * we need to convert it into +ve as |
| * X interprets a +ve value as wheel up event. |
| * Same is true for the horizontal wheel also. |
| * The mouse reports 0xf when scrolled up |
| * and 0x1 when scrolled down. This observation |
| * is based on Logitech, HCL, |
| * Microsoft and Black Cat mouse only |
| */ |
| if (code == 0xf) { |
| /* negative Z - wheel up */ |
| code |= 0xfffffff0; |
| vuidmice_send_wheel_event(qp, 0, |
| FE_PAIR_NONE, 0, -code); |
| } else if (code == 0x01) { |
| /* positive Z - wheel down */ |
| vuidmice_send_wheel_event(qp, 0, |
| FE_PAIR_NONE, 0, -code); |
| } |
| } |
| |
| /* |
| * Check whether reporting horizontal wheel |
| * movements is enabled |
| */ |
| if (STATEP->wheel_state_bf & |
| (1 << VUIDMICE_HORIZONTAL_WHEEL_ID)) { |
| |
| /* |
| * The mouse return -7 and +7 when it |
| * is scrolled horizontally |
| */ |
| if (code == 0x09) { |
| /* negative Z - wheel left */ |
| vuidmice_send_wheel_event(qp, 1, |
| FE_PAIR_NONE, 0, 1); |
| } else if (code == 0x07) { |
| /* positive Z - wheel right */ |
| vuidmice_send_wheel_event(qp, 1, |
| FE_PAIR_NONE, 0, -1); |
| } |
| } |
| |
| packet_complete: |
| STATEP->state = PS2_START; |
| /* |
| * If we can peek at the next mouse character, and |
| * its not the start of the next packet, don't use |
| * this packet. |
| */ |
| if (mp->b_wptr > mp->b_rptr && |
| ((mp->b_rptr[0] ^ STATEP->sync_byte) & 0x08)) { |
| /* |
| * bit 3 not set |
| */ |
| break; |
| } |
| |
| /* |
| * send the info to the next level -- |
| * need to send multiple events if we have both |
| * a delta *AND* button event(s) |
| */ |
| |
| /* motion has occurred ... */ |
| if (STATEP->deltax) |
| VUID_PUTNEXT(qp, (uchar_t)LOC_X_DELTA, |
| FE_PAIR_ABSOLUTE, (uchar_t)LOC_X_ABSOLUTE, |
| STATEP->deltax); |
| |
| if (STATEP->deltay) |
| VUID_PUTNEXT(qp, (uchar_t)LOC_Y_DELTA, |
| FE_PAIR_ABSOLUTE, (uchar_t)LOC_Y_ABSOLUTE, |
| STATEP->deltay); |
| |
| STATEP->deltax = STATEP->deltay = 0; |
| break; |
| |
| case PS2_WAIT_RESET_COMPLETE: |
| |
| /* |
| * 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 (length == 1) { |
| /* |
| * 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; |
| STATEP->state = PS2_WAIT_FOR_AA; |
| } else { |
| put1(WR(qp), MSERESET); |
| } |
| |
| break; |
| |
| } else if (length != 2) { |
| break; |
| } |
| |
| /* |
| * 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 */ |
| |
| 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; |
| |
| case PS2_WAIT_SETRES0_ACK1: |
| if (code != MSE_ACK) { |
| break; |
| } |
| STATEP->state = PS2_WAIT_SETRES0_ACK2; |
| put1(WR(qp), 0); |
| break; |
| |
| case PS2_WAIT_SETRES0_ACK2: |
| case PS2_WAIT_SCALE1_1_ACK: |
| case PS2_WAIT_SCALE1_2_ACK: |
| if (code != MSE_ACK) { |
| break; |
| } |
| STATEP->state++; |
| put1(WR(qp), MSESCALE1); |
| break; |
| |
| case PS2_WAIT_SCALE1_3_ACK: |
| if (code != MSE_ACK) { |
| break; |
| } |
| |
| /* Set res and scale have been ok */ |
| vuid_cancel_timeout(qp); |
| |
| STATEP->state = PS2_WAIT_STATREQ_ACK; |
| |
| /* Set timeout for status request */ |
| vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_GROUP); |
| |
| put1(WR(qp), MSESTATREQ); |
| |
| break; |
| |
| case PS2_WAIT_STATREQ_ACK: |
| if (code != MSE_ACK) { |
| break; |
| } |
| STATEP->state = PS2_WAIT_STATUS_1; |
| break; |
| |
| case PS2_WAIT_STATUS_1: |
| STATEP->state = PS2_WAIT_STATUS_BUTTONS; |
| break; |
| |
| case PS2_WAIT_STATUS_BUTTONS: |
| if (code != 0) { |
| STATEP->nbuttons = (uchar_t)code; |
| STATEP->state = (uchar_t)PS2_WAIT_STATUS_REV; |
| } else { |
| #if defined(VUID3PS2) |
| /* |
| * It seems that there are some 3-button mice |
| * that don't play the Logitech autodetect |
| * game. One is a Mouse Systems mouse OEM'ed |
| * by Intergraph. |
| * |
| * Until we find out how to autodetect these |
| * mice, we'll assume that if we're being |
| * compiled as vuid3ps2 and the mouse doesn't |
| * play the autodetect game, it's a 3-button |
| * mouse. This effectively disables |
| * autodetect for mice using vuid3ps2, but |
| * since vuid3ps2 is used only on x86 where |
| * we currently assume manual configuration, |
| * this shouldn't be a problem. At some point |
| * in the future when we *do* start using |
| * autodetect on x86, we should probably define |
| * VUIDPS2 instead of VUID3PS2. Even then, |
| * we could leave this code so that *some* |
| * mice could use autodetect and others not. |
| */ |
| STATEP->nbuttons = 3; |
| #else |
| STATEP->nbuttons = 2; |
| #endif |
| STATEP->state = PS2_WAIT_STATUS_3; |
| } |
| break; |
| |
| case PS2_WAIT_STATUS_REV: |
| /*FALLTHROUGH*/ |
| |
| case PS2_WAIT_STATUS_3: |
| |
| /* Status request completed successfully */ |
| vuid_cancel_timeout(qp); |
| |
| vuidmice_start_wdc_or_setres(qp); |
| break; |
| |
| case PS2_WAIT_WHEEL_SMPL1_CMD_ACK: |
| if (code != MSE_ACK) { |
| break; |
| } |
| STATEP->state = PS2_WAIT_WHEEL_SMPL1_RATE_ACK; |
| put1(WR(qp), 200); |
| break; |
| case PS2_WAIT_WHEEL_SMPL1_RATE_ACK: |
| if (code != MSE_ACK) { |
| break; |
| } |
| STATEP->state = PS2_WAIT_WHEEL_SMPL2_CMD_ACK; |
| put1(WR(qp), MSECHGMOD); |
| break; |
| |
| case PS2_WAIT_WHEEL_SMPL2_CMD_ACK: |
| if (code != MSE_ACK) { |
| break; |
| } |
| STATEP->state = PS2_WAIT_WHEEL_SMPL2_RATE_ACK; |
| put1(WR(qp), 100); |
| break; |
| |
| case PS2_WAIT_WHEEL_SMPL2_RATE_ACK: |
| if (code != MSE_ACK) { |
| break; |
| } |
| STATEP->state = PS2_WAIT_WHEEL_SMPL3_CMD_ACK; |
| put1(WR(qp), MSECHGMOD); |
| break; |
| |
| case PS2_WAIT_WHEEL_SMPL3_CMD_ACK: |
| if (code != MSE_ACK) { |
| break; |
| } |
| STATEP->state = PS2_WAIT_WHEEL_SMPL3_RATE_ACK; |
| put1(WR(qp), 80); |
| break; |
| |
| case PS2_WAIT_WHEEL_SMPL3_RATE_ACK: |
| if (code != MSE_ACK) { |
| break; |
| } |
| |
| /* Set sample rate completed successfully */ |
| vuid_cancel_timeout(qp); |
| |
| STATEP->state = PS2_WAIT_WHEEL_DEV_CMD; |
| |
| /* Set timeout for get dev */ |
| vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_CMD); |
| |
| put1(WR(qp), MSEGETDEV); |
| break; |
| |
| case PS2_WAIT_WHEEL_DEV_CMD: |
| if (code != MSE_ACK) { |
| break; |
| } |
| STATEP->state = PS2_WAIT_WHEEL_DEV_ACK; |
| break; |
| |
| case PS2_WAIT_WHEEL_DEV_ACK: |
| |
| /* Get dev completed successfully */ |
| vuid_cancel_timeout(qp); |
| |
| if (code != 0x03) { |
| STATEP->state = PS2_WAIT_SETRES3_ACK1; |
| |
| /* Set timeout for set res */ |
| vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_CMD); |
| |
| put1(WR(qp), MSESETRES); |
| |
| break; |
| } |
| |
| STATEP->vuid_mouse_mode = MOUSE_MODE_WHEEL; |
| |
| /* |
| * Found wheel. By default enable the wheel. |
| */ |
| STATEP->wheel_state_bf |= VUID_WHEEL_STATE_ENABLED; |
| |
| STATEP->state = PS2_WAIT_WHEEL5_SMPL1_CMD_ACK; |
| |
| /* Set timeout for set sample rate */ |
| vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_GROUP); |
| |
| /* We're on a roll - try for wheel+5 */ |
| put1(WR(qp), MSECHGMOD); |
| |
| break; |
| |
| case PS2_WAIT_WHEEL5_SMPL1_CMD_ACK: |
| if (code != MSE_ACK) { |
| break; |
| } |
| STATEP->state = PS2_WAIT_WHEEL5_SMPL1_RATE_ACK; |
| put1(WR(qp), 200); |
| break; |
| |
| case PS2_WAIT_WHEEL5_SMPL1_RATE_ACK: |
| if (code != MSE_ACK) { |
| break; |
| } |
| STATEP->state = PS2_WAIT_WHEEL5_SMPL2_CMD_ACK; |
| put1(WR(qp), MSECHGMOD); |
| break; |
| |
| case PS2_WAIT_WHEEL5_SMPL2_CMD_ACK: |
| if (code != MSE_ACK) { |
| break; |
| } |
| STATEP->state = PS2_WAIT_WHEEL5_SMPL2_RATE_ACK; |
| put1(WR(qp), 200); |
| break; |
| |
| case PS2_WAIT_WHEEL5_SMPL2_RATE_ACK: |
| if (code != MSE_ACK) { |
| break; |
| } |
| STATEP->state = PS2_WAIT_WHEEL5_SMPL3_CMD_ACK; |
| put1(WR(qp), MSECHGMOD); |
| break; |
| |
| case PS2_WAIT_WHEEL5_SMPL3_CMD_ACK: |
| if (code != MSE_ACK) { |
| break; |
| } |
| STATEP->state = PS2_WAIT_WHEEL5_SMPL3_RATE_ACK; |
| put1(WR(qp), 80); |
| break; |
| |
| case PS2_WAIT_WHEEL5_SMPL3_RATE_ACK: |
| if (code != MSE_ACK) { |
| break; |
| } |
| |
| /* Set sample rate completed successfully */ |
| vuid_cancel_timeout(qp); |
| |
| STATEP->state = PS2_WAIT_WHEEL5_DEV_CMD; |
| |
| /* Set timeout for wheel5 get dev */ |
| vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_CMD); |
| |
| put1(WR(qp), MSEGETDEV); |
| |
| break; |
| |
| case PS2_WAIT_WHEEL5_DEV_CMD: |
| if (code != MSE_ACK) { |
| break; |
| } |
| STATEP->state = PS2_WAIT_WHEEL5_DEV_ACK; |
| break; |
| |
| case PS2_WAIT_WHEEL5_DEV_ACK: |
| if (code == 0x04) { |
| STATEP->vuid_mouse_mode = MOUSE_MODE_WHEEL5; |
| STATEP->nbuttons = 5; |
| |
| /* |
| * Found wheel. By default enable the wheel. |
| */ |
| STATEP->wheel_state_bf |= |
| VUID_WHEEL_STATE_ENABLED << |
| MOUSE_MODE_WHEEL; |
| } |
| |
| /* Wheel5 get dev completed successfully */ |
| vuid_cancel_timeout(qp); |
| |
| /* FALLTHROUGH */ |
| |
| case PS2_WAIT_SETRES3_CMD: |
| STATEP->state = PS2_WAIT_SETRES3_ACK1; |
| |
| /* Set timeout for set res */ |
| vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_CMD); |
| |
| put1(WR(qp), MSESETRES); |
| |
| break; |
| |
| case PS2_WAIT_SETRES3_ACK1: |
| if (code != MSE_ACK) { |
| break; |
| } |
| STATEP->state = PS2_WAIT_SETRES3_ACK2; |
| put1(WR(qp), 3); |
| break; |
| |
| case PS2_WAIT_SETRES3_ACK2: |
| if (code != MSE_ACK) { |
| break; |
| } |
| |
| /* Set res completed successfully */ |
| vuid_cancel_timeout(qp); |
| |
| STATEP->state = PS2_WAIT_STREAM_ACK; |
| |
| /* Set timeout for enable */ |
| vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_CMD); |
| |
| put1(WR(qp), MSESTREAM); |
| |
| break; |
| |
| case PS2_WAIT_STREAM_ACK: |
| if (code != MSE_ACK) { |
| break; |
| } |
| STATEP->state = PS2_WAIT_ON_ACK; |
| put1(WR(qp), MSEON); |
| break; |
| |
| case PS2_WAIT_ON_ACK: |
| if (code != MSE_ACK) { |
| break; |
| } |
| |
| /* 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; |
| } |
| } |
| freemsg(mp); |
| } |