/*++ Copyright (c) 1993 Microsoft Corporation Copyright (c) 1993 Logitech Inc. Module Name: mseries.c Abstract: Environment: Kernel mode only. Notes: Revision History: --*/ // // Includes. // #include "ntddk.h" #include "uart.h" #include "sermouse.h" #include "debug.h" #include "cseries.h" #include "mseries.h" // // Use the alloc_text pragma to specify the driver initialization routines // (they can be paged out). // #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT,MSerSetProtocol) #pragma alloc_text(INIT,MSerPowerUp) #pragma alloc_text(INIT,MSerPowerDown) #pragma alloc_text(INIT,MSerReset) #pragma alloc_text(INIT,MSerDetect) #endif // ALLOC_PRAGMA // // Constants. // #define MSER_BAUDRATE 1200 #define MAX_RESET_BUFFER 8 #define MINIMUM_RESET_TIME 200 // // Microsoft Plus. // #define MP_SYNCH_BIT 0x40 #define MP_BUTTON_LEFT 0x20 #define MP_BUTTON_RIGHT 0x10 #define MP_BUTTON_MIDDLE 0x20 #define MP_BUTTON_LEFT_SR 5 #define MP_BUTTON_RIGHT_SR 3 #define MP_BUTTON_MIDDLE_SR 3 #define MP_BUTTON_MIDDLE_MASK 0x04 #define MP_UPPER_MASKX 0x03 #define MP_UPPER_MASKY 0x0C #define MP_UPPER_MASKX_SL 6 #define MP_UPPER_MASKY_SL 4 // // Microsoft BallPoint. // #define BP_SYNCH_BIT 0x40 #define BP_BUTTON_LEFT 0x20 #define BP_BUTTON_RIGHT 0x10 #define BP_BUTTON_3 0x04 #define BP_BUTTON_4 0x08 #define BP_BUTTON_LEFT_SR 5 #define BP_BUTTON_RIGHT_SR 3 #define BP_BUTTON_3_SL 0 #define BP_BUTTON_4_SL 0 #define BP_UPPER_MASKX 0x03 #define BP_UPPER_MASKY 0x0C #define BP_UPPER_MASKX_SL 6 #define BP_UPPER_MASKY_SL 4 #define BP_SIGN_MASKX 0x01 #define BP_SIGN_MASKY 0x02 // // Microsoft Magellan Mouse. // #define Z_SYNCH_BIT 0x40 #define Z_EXTRA_BIT 0x20 #define Z_BUTTON_LEFT 0x20 #define Z_BUTTON_RIGHT 0x10 #define Z_BUTTON_MIDDLE 0x10 #define Z_BUTTON_LEFT_SR 5 #define Z_BUTTON_RIGHT_SR 3 #define Z_BUTTON_MIDDLE_SR 3 #define Z_BUTTON_MIDDLE_MASK 0x04 #define Z_UPPER_MASKX 0x03 #define Z_UPPER_MASKY 0x0C #define Z_UPPER_MASKZ 0x0F #define Z_LOWER_MASKZ 0x0F #define Z_UPPER_MASKX_SL 6 #define Z_UPPER_MASKY_SL 4 #define Z_UPPER_MASKZ_SL 4 // // Type definitions. // typedef struct _PROTOCOL { PPROTOCOL_HANDLER Handler; UCHAR LineCtrl; } PROTOCOL; // // This list is indexed by protocol values MSER_PROTOCOL_*. // static PROTOCOL Protocol[] = { { MSerHandlerMP, // Microsoft Plus ACE_7BW | ACE_1SB }, { MSerHandlerBP, // BALLPOINT ACE_7BW | ACE_1SB }, { MSerHandlerZ, // Magellan Mouse ACE_7BW | ACE_1SB } }; PPROTOCOL_HANDLER MSerSetProtocol( PUCHAR Port, UCHAR NewProtocol ) /*++ Routine Description: Set the mouse protocol. This function only sets the serial port line control register. Arguments: Port - Pointer to the serial port. NewProtocol - Index into the protocol table. Return Value: Pointer to the protocol handler function. --*/ { ASSERT(NewProtocol < MSER_PROTOCOL_MAX); // // Set the protocol // UARTSetLineCtrl(Port, Protocol[NewProtocol].LineCtrl); return Protocol[NewProtocol].Handler; } BOOLEAN MSerPowerUp( PUCHAR Port ) /*++ Routine Description: Powers up the mouse. Just sets the RTS and DTR lines and returns. Arguments: Port - Pointer to the serial port. Return Value: TRUE. --*/ { // // Turn RTS on to power the mouse up (DTR should already be on, // but make extra sure). // UARTSetModemCtrl(Port, ACE_DTR | ACE_RTS); // // Wait 10 ms. The power-up response byte(s) should take at least // this long to get transmitted. // KeStallExecutionProcessor(10 * MS_TO_MICROSECONDS); return TRUE; } BOOLEAN MSerPowerDown( PUCHAR Port ) /*++ Routine Description: Powers down the mouse. Sets the RTS line to an inactive state. Arguments: Port - Pointer to the serial port. Return Value: TRUE. --*/ { UCHAR lineCtrl = UARTGetModemCtrl(Port); SerMouPrint(( 2, "SERMOUSE-MSerPowerDown: The intial line control is: %#X\n", lineCtrl & 0xFF )); UARTSetModemCtrl(Port, (UCHAR) ((lineCtrl & ~ACE_RTS) | ACE_DTR)); // // Keep RTS low for at least 150 ms, in order to correctly power // down older Microsoft serial mice. Wait even longer to avoid // sending some Logitech CSeries mice into the floating point world... // ASSERT(CSER_POWER_DOWN >= 150); KeStallExecutionProcessor(CSER_POWER_DOWN * MS_TO_MICROSECONDS); return TRUE; } BOOLEAN MSerReset( PUCHAR Port ) /*++ Routine Description: Reset the serial mouse. Arguments: Port - Pointer to the serial port. Return Value: TRUE. --*/ { // // Remove mouse power if necessary. // MSerPowerDown(Port); // // Clean possible garbage in uart input buffer. // UARTFlushReadBuffer(Port); // // Power up the mouse (reset). // MSerPowerUp(Port); return TRUE; } MOUSETYPE MSerDetect( PUCHAR Port, ULONG BaudClock ) /*++ Routine Description: Detection code for pointing devices that identify themselves at power on time. Arguments: Port - Pointer to the serial port. BaudClock - The external frequency driving the serial chip. Return Value: The type of mouse detected. --*/ { ULONG count = 0; MOUSETYPE mouseType; CHAR receiveBuffer[MAX_RESET_BUFFER]; ULONG i; // // Set the debug output to the main display to avoid timing problems. // SerMouSetDebugOutput(DBG_COLOR); // // Set the baud rate. // UARTSetBaudRate(Port, MSER_BAUDRATE, BaudClock); // // Set the data format so that the possible answer can be recognized. // UARTSetLineCtrl(Port, Protocol[MSER_PROTOCOL_MP].LineCtrl); // // Apply the reset to the mouse. // MSerReset(Port); // // Get the possible first reset character ('M' or 'B'), followed // by any other characters the hardware happens to send back. // // Note: Typically, we expect to get just one character ('M' or // 'B'), perhaps followed by a '2' or '3' (to indicate the // number of mouse buttons. On some machines, we're // getting extraneous characters before the 'M'. Sometimes // we get extraneous characters after the expected data, as // well. They either get read in here, or get flushed // when SerMouEnableInterrupts executes. // ASSERT(CSER_POWER_UP >= MINIMUM_RESET_TIME); if (UARTReadChar(Port, &receiveBuffer[count], CSER_POWER_UP)) { count++; while (count < (MAX_RESET_BUFFER - 1)) { if (UARTReadChar(Port, &receiveBuffer[count], 100)) { count++; } else { break; } } } *(receiveBuffer + count) = 0; SerMouPrint((2, "SERMOUSE-Receive buffer:\n")); for (i = 0; i < count; i++) { SerMouPrint((2, "\t0x%x\n", receiveBuffer[i])); } SerMouPrint((2, "\n")); // // Redirect the output to the serial port. // SerMouSetDebugOutput(DBG_SERIAL); // // // Analyze the possible mouse answer. Start at the beginning of the // "good" data in the receive buffer, ignoring extraneous characters // that may have come in before the 'M' or 'B'. // for (i = 0; i < count; i++) { if (receiveBuffer[i] == 'M') { if (receiveBuffer[i + 1] == '3') { SerMouPrint((2, "SERMOUSE-Detected MSeries 3 buttons\n")); mouseType = MOUSE_3B; } else if (receiveBuffer[i + 1] == 'Z') { SerMouPrint((2, "SERMOUSE-Detected Wheel Mouse\n")); mouseType = MOUSE_Z; } else { SerMouPrint((2, "SERMOUSE-Detected MSeries 2 buttons\n")); mouseType = MOUSE_2B; } break; } else if (receiveBuffer[i] == 'B') { SerMouPrint((2, "SERMOUSE-Detected Ballpoint\n")); mouseType = BALLPOINT; break; } } if (i >= count) { // // Special case: If another device is connected (CSeries, for // example) and this device sends a character (movement), the // minimum power up time might not be respected. Take // care of this unlikely case. // if (count != 0) { KeStallExecutionProcessor(CSER_POWER_UP * MS_TO_MICROSECONDS); } SerMouPrint((1, "SERMOUSE-No MSeries detected\n")); mouseType = NO_MOUSE; } return mouseType; } BOOLEAN MSerHandlerMP( IN PMOUSE_INPUT_DATA CurrentInput, IN PHANDLER_DATA HandlerData, IN UCHAR Value, IN UCHAR LineState ) /*++ Routine Description: This is the protocol handler routine for the Microsoft Plus protocol. Arguments: CurrentInput - Pointer to the report packet. HandlerData - Instance specific static data for the handler. Value - The input buffer value. LineState - The serial port line state. Return Value: Returns TRUE if the handler has a complete report ready. --*/ { BOOLEAN retval = FALSE; ULONG middleButton; SerMouPrint((2, "SERMOUSE-MP protocol handler: enter\n")); if ((Value & MP_SYNCH_BIT) && (HandlerData->State != STATE0)) { if ((HandlerData->State != STATE3)) { // // We definitely have a synchronization problem (likely a data // overrun). // HandlerData->Error++; } else if ((HandlerData->PreviousButtons & MOUSE_BUTTON_3) != 0) { // // We didn't receive the expected fourth byte. Missed it? // Reset button 3 to zero. // HandlerData->PreviousButtons ^= MOUSE_BUTTON_3; HandlerData->Error++; } SerMouPrint(( 1, "SERMOUSE-Synch error. State: %u\n", HandlerData->State )); HandlerData->State = STATE0; } else if (!(Value & MP_SYNCH_BIT) && (HandlerData->State == STATE0)) { HandlerData->Error++; SerMouPrint(( 1, "SERMOUSE-Synch error. State: %u\n", HandlerData->State )); goto LExit; } // // Check for a line state error. // if (LineState & ACE_LERR) { // // Reset the handler state. // HandlerData->State = STATE0; HandlerData->Error++; SerMouPrint((1, "SERMOUSE-Line status error: %#x\n", LineState)); } else { // // Set the untranslated value. // HandlerData->Raw[HandlerData->State] = Value; SerMouPrint((3, "SERMOUSE-State%u\n", HandlerData->State)); switch (HandlerData->State) { case STATE0: case STATE1: HandlerData->State++; break; case STATE2: HandlerData->State++; // // Build the report. // CurrentInput->RawButtons = (HandlerData->Raw[0] & MP_BUTTON_LEFT) >> MP_BUTTON_LEFT_SR; CurrentInput->RawButtons |= (HandlerData->Raw[0] & MP_BUTTON_RIGHT) >> MP_BUTTON_RIGHT_SR; CurrentInput->RawButtons |= HandlerData->PreviousButtons & MOUSE_BUTTON_3; CurrentInput->LastX = (SCHAR)(HandlerData->Raw[1] | ((HandlerData->Raw[0] & MP_UPPER_MASKX) << MP_UPPER_MASKX_SL)); CurrentInput->LastY = (SCHAR)(HandlerData->Raw[2] | ((HandlerData->Raw[0] & MP_UPPER_MASKY) << MP_UPPER_MASKY_SL)); retval = TRUE; break; case STATE3: HandlerData->State = STATE0; middleButton = (HandlerData->Raw[STATE3] & MP_BUTTON_MIDDLE) >> MP_BUTTON_MIDDLE_SR; // // Send a report only if the middle button state changed. // if (middleButton ^ (HandlerData->PreviousButtons & MOUSE_BUTTON_3)) { // // Toggle the state of the middle button. // CurrentInput->RawButtons ^= MP_BUTTON_MIDDLE_MASK; CurrentInput->LastX = 0; CurrentInput->LastY = 0; // // Send the report one more time. // retval = TRUE; } break; default: SerMouPrint(( 0, "SERMOUSE-MP Handler failure: incorrect state value.\n" )); ASSERT(FALSE); } } LExit: SerMouPrint((2, "SERMOUSE-MP protocol handler: exit\n")); return retval; } BOOLEAN MSerHandlerBP( IN PMOUSE_INPUT_DATA CurrentInput, IN PHANDLER_DATA HandlerData, IN UCHAR Value, IN UCHAR LineState ) /*++ Routine Description: This is the protocol handler routine for the Microsoft Ballpoint protocol. Arguments: CurrentInput - Pointer to the report packet. HandlerData - Instance specific static data for the handler. Value - The input buffer value. LineState - The serial port line state. Return Value: Returns TRUE if the handler has a complete report ready. --*/ { BOOLEAN retval = FALSE; SerMouPrint((2, "SERMOUSE-BP protocol handler: enter\n")); // // Check for synchronization errors. // if ((Value & BP_SYNCH_BIT) && (HandlerData->State != STATE0)) { HandlerData->Error++; SerMouPrint(( 1, "SERMOUSE-Synch error. State: %u\n", HandlerData->State )); HandlerData->State = STATE0; } else if (!(Value & BP_SYNCH_BIT) && (HandlerData->State == STATE0)) { HandlerData->Error++; SerMouPrint(( 1, "SERMOUSE-Synch error. State: %u\n", HandlerData->State )); goto LExit; } // // Check for a line state error. // if (LineState & ACE_LERR) { // // Reset the handler state. // HandlerData->State = STATE0; HandlerData->Error++; SerMouPrint((1, "SERMOUSE-Line status error: %#x\n", LineState)); } else { // // Set the untranslated value. // HandlerData->Raw[HandlerData->State] = Value; SerMouPrint((3, "SERMOUSE-State%u\n", HandlerData->State)); switch (HandlerData->State) { case STATE0: case STATE1: case STATE2: HandlerData->State++; break; case STATE3: HandlerData->State = STATE0; // // Build the report. // CurrentInput->RawButtons = (HandlerData->Raw[0] & BP_BUTTON_LEFT) >> BP_BUTTON_LEFT_SR; CurrentInput->RawButtons |= (HandlerData->Raw[0] & BP_BUTTON_RIGHT) >> BP_BUTTON_RIGHT_SR; #if 0 CurrentInput->ButtonFlags |= (HandlerData->Raw[3] & BP_BUTTON_3) << BP_BUTTON_3_SL; CurrentInput->ButtonFlags |= (HandlerData->Raw[3] & BP_BUTTON_4) << BP_BUTTON_4_SL; #endif CurrentInput->LastX = HandlerData->Raw[3] & BP_SIGN_MASKX ? (LONG)(HandlerData->Raw[1] | (ULONG)(-1 & ~0xFF) | ((HandlerData->Raw[0] & BP_UPPER_MASKX) << BP_UPPER_MASKX_SL)): (LONG)(HandlerData->Raw[1] | ((HandlerData->Raw[0] & BP_UPPER_MASKX) << BP_UPPER_MASKX_SL)); CurrentInput->LastY = HandlerData->Raw[3] & BP_SIGN_MASKY ? (LONG)(HandlerData->Raw[2] | (ULONG)(-1 & ~0xFF) | ((HandlerData->Raw[0] & BP_UPPER_MASKY) << BP_UPPER_MASKY_SL)): (LONG)(HandlerData->Raw[2] | ((HandlerData->Raw[0] & BP_UPPER_MASKY) << BP_UPPER_MASKY_SL)); retval = TRUE; break; default: SerMouPrint(( 0, "SERMOUSE-BP Handler failure: incorrect state value.\n" )); ASSERT(FALSE); } } LExit: SerMouPrint((2, "SERMOUSE-BP protocol handler: exit\n")); return retval; } BOOLEAN MSerHandlerZ( IN PMOUSE_INPUT_DATA CurrentInput, IN PHANDLER_DATA HandlerData, IN UCHAR Value, IN UCHAR LineState ) /*++ Routine Description: This is the protocol handler routine for the Microsoft Magellan Mouse (wheel mouse) Arguments: CurrentInput - Pointer to the report packet. HandlerData - Instance specific static data for the handler. Value - The input buffer value. LineState - The serial port line state. Return Value: Returns TRUE if the handler has a complete report ready. --*/ { BOOLEAN retval = FALSE; ULONG middleButton; CHAR zMotion = 0; SerMouPrint((2, "SERMOUSE-Z protocol handler: enter\n")); if ((Value & Z_SYNCH_BIT) && (HandlerData->State != STATE0)) { if ((HandlerData->State != STATE3)) { // // We definitely have a synchronization problem (likely a data // overrun). // HandlerData->Error++; } SerMouPrint(( 1, "SERMOUSE-Z Synch error. State: %u\n", HandlerData->State )); HandlerData->State = STATE0; } else if (!(Value & Z_SYNCH_BIT) && (HandlerData->State == STATE0)) { HandlerData->Error++; SerMouPrint(( 1, "SERMOUSE-Z Synch error. State: %u\n", HandlerData->State )); goto LExit; } // // Check for a line state error. // if (LineState & ACE_LERR) { // // Reset the handler state. // HandlerData->State = STATE0; HandlerData->Error++; SerMouPrint((1, "SERMOUSE-Z Line status error: %#x\n", LineState)); } else { // // Set the untranslated value. // HandlerData->Raw[HandlerData->State] = Value; SerMouPrint((3, "SERMOUSE-Z State%u\n", HandlerData->State)); switch (HandlerData->State) { case STATE0: case STATE1: case STATE2: HandlerData->State++; break; case STATE3: // // Check to see if the mouse is going to the high bits of // the wheel movement. If not, this is the last bit - transition // back to state0 // if((HandlerData->Raw[STATE3] & Z_EXTRA_BIT) == 0) { HandlerData->State = STATE0; HandlerData->Raw[STATE4] = 0; retval = TRUE; } break; case STATE4: DbgPrint("SERMOUSE-Z Got that 5th byte\n"); HandlerData->State = STATE0; retval = TRUE; break; default: SerMouPrint(( 0, "SERMOUSE-Z Handler failure: incorrect state value.\n" )); ASSERT(FALSE); } if(retval) { CurrentInput->RawButtons = 0; if(HandlerData->Raw[STATE0] & Z_BUTTON_LEFT) { CurrentInput->RawButtons |= MOUSE_BUTTON_LEFT; } if(HandlerData->Raw[STATE0] & Z_BUTTON_RIGHT) { CurrentInput->RawButtons |= MOUSE_BUTTON_RIGHT; } if(HandlerData->Raw[STATE3] & Z_BUTTON_MIDDLE) { CurrentInput->RawButtons |= MOUSE_BUTTON_MIDDLE; } CurrentInput->LastX = (SCHAR)(HandlerData->Raw[STATE1] | ((HandlerData->Raw[0] & Z_UPPER_MASKX) << Z_UPPER_MASKX_SL)); CurrentInput->LastY = (SCHAR)(HandlerData->Raw[STATE2] | ((HandlerData->Raw[0] & Z_UPPER_MASKY) << Z_UPPER_MASKY_SL)); // // If the extra bit isn't set then the 4th byte contains // a 4 bit signed quantity for the wheel movement. if it // is set, then we need to combine the z info from the // two bytes // if((HandlerData->Raw[STATE3] & Z_EXTRA_BIT) == 0) { zMotion = HandlerData->Raw[STATE3] & Z_LOWER_MASKZ; // // Sign extend the 4 bit // if(zMotion & 0x08) { zMotion |= 0xf0; } } else { zMotion = ((HandlerData->Raw[STATE3] & Z_LOWER_MASKZ) | ((HandlerData->Raw[STATE4] & Z_UPPER_MASKZ) << Z_UPPER_MASKZ_SL)); } if(zMotion == 0) { CurrentInput->ButtonData = 0; } else { CurrentInput->ButtonData = 0x0078; if(zMotion & 0x80) { CurrentInput->ButtonData = 0x0078; } else { CurrentInput->ButtonData = 0xff88; } CurrentInput->ButtonFlags |= MOUSE_WHEEL; } } } LExit: SerMouPrint((2, "SERMOUSE-Z protocol handler: exit\n")); return retval; }