/*++ Copyright (c) 1990-1998 Microsoft Corporation, All Rights Reserved Copyright (c) 1993 Logitech Inc. Module Name: mseries.c Abstract: Environment: Kernel mode only. Notes: Revision History: --*/ // // Includes. // #include "ntddk.h" #include "mouser.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(PAGE,MSerSetProtocol) #pragma alloc_text(PAGE,MSerPowerUp) #pragma alloc_text(PAGE,MSerPowerDown) #pragma alloc_text(PAGE,MSerDetect) #endif // ALLOC_PRAGMA // // Constants. // #define MSER_BAUDRATE 1200 #define MAX_RESET_BUFFER 8 #define MINIMUM_RESET_TIME (200 * MS_TO_100_NS) // // 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; SERIAL_LINE_CONTROL LineCtrl; } PROTOCOL; // // This list is indexed by protocol values MSER_PROTOCOL_*. // static PROTOCOL Protocol[] = { { MSerHandlerMP, // Microsoft Plus // ACE_7BW | ACE_1SB { STOP_BIT_1, NO_PARITY, 7 } }, { MSerHandlerBP, // BALLPOINT // ACE_7BW | ACE_1SB { STOP_BIT_1, NO_PARITY, 7 } }, { MSerHandlerZ, // Magellan Mouse // ACE_7BW | ACE_1SB { STOP_BIT_1, NO_PARITY, 7 } } }; PPROTOCOL_HANDLER MSerSetProtocol( PDEVICE_EXTENSION DeviceExtension, 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); PAGED_CODE(); Print(DeviceExtension, DBG_SS_TRACE, ("MSerSetProtocol called\n")); // // Set the protocol // SerialMouseSetLineCtrl(DeviceExtension, &Protocol[NewProtocol].LineCtrl); return Protocol[NewProtocol].Handler; } NTSTATUS MSerPowerUp( PDEVICE_EXTENSION DeviceExtension ) /*++ 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. --*/ { IO_STATUS_BLOCK iosb; NTSTATUS status; KEVENT event; PAGED_CODE(); Print(DeviceExtension, DBG_SS_TRACE, ("MSerPowerUp called\n")); KeInitializeEvent(&event, NotificationEvent, FALSE); // // Clear DTR // Print(DeviceExtension, DBG_SS_NOISE, ("Clearing DTR...\n")); status = SerialMouseIoSyncIoctl(IOCTL_SERIAL_CLR_DTR, DeviceExtension->TopOfStack, &event, &iosb ); if (!NT_SUCCESS(status)) { return status; } // // Clear RTS // Print(DeviceExtension, DBG_SS_NOISE, ("Clearing RTS...\n")); status = SerialMouseIoSyncIoctl(IOCTL_SERIAL_CLR_RTS, DeviceExtension->TopOfStack, &event, &iosb ); if (!NT_SUCCESS(status)) { return status; } // // Set a timer for 200 ms // status = SerialMouseWait(DeviceExtension, -PAUSE_200_MS); if (!NT_SUCCESS(status)) { Print(DeviceExtension, DBG_SS_ERROR, ("Timer failed with status %x\n", status )); return status; } // // set DTR // Print(DeviceExtension, DBG_SS_NOISE, ("Setting DTR...\n")); status = SerialMouseIoSyncIoctl(IOCTL_SERIAL_SET_DTR, DeviceExtension->TopOfStack, &event, &iosb ); if (!NT_SUCCESS(status)) { return status; } status = SerialMouseWait(DeviceExtension, -PAUSE_200_MS); if (!NT_SUCCESS(status)) { Print(DeviceExtension, DBG_SS_ERROR, ("Timer failed with status %x\n", status )); return status; } // // set RTS // Print(DeviceExtension, DBG_SS_NOISE, ("Setting RTS...\n")); status = SerialMouseIoSyncIoctl(IOCTL_SERIAL_SET_RTS, DeviceExtension->TopOfStack, &event, &iosb ); status = SerialMouseWait(DeviceExtension, -175 * MS_TO_100_NS); if (!NT_SUCCESS(status)) { Print(DeviceExtension, DBG_SS_ERROR, ("Timer failed with status %x\n", status )); return status; } return status; } NTSTATUS MSerPowerDown( PDEVICE_EXTENSION DeviceExtension ) /*++ Routine Description: Powers down the mouse. Sets the RTS line to an inactive state. Arguments: Port - Pointer to the serial port. Return Value: TRUE. --*/ { IO_STATUS_BLOCK iosb; SERIAL_HANDFLOW shf; KEVENT event; NTSTATUS status; ULONG bits; PAGED_CODE(); Print(DeviceExtension, DBG_SS_TRACE, ("MSerPowerDown called\n")); KeInitializeEvent(&event, NotificationEvent, FALSE ); // // Set DTR // Print(DeviceExtension, DBG_SS_NOISE, ("Setting DTR...\n")); status = SerialMouseIoSyncIoctl(IOCTL_SERIAL_SET_DTR, DeviceExtension->TopOfStack, &event, &iosb); if (!NT_SUCCESS(status)) { return status; } // // Clear RTS // Print(DeviceExtension, DBG_SS_NOISE, ("Clearing RTS...\n")); status = SerialMouseIoSyncIoctl(IOCTL_SERIAL_CLR_RTS, DeviceExtension->TopOfStack, &event, &iosb); if (!NT_SUCCESS(status)) { return status; } // // Set a timer for 200 ms // status = SerialMouseWait(DeviceExtension, -PAUSE_200_MS); if (!NT_SUCCESS(status)) { Print(DeviceExtension, DBG_SS_ERROR, ("Timer failed with status %x\n", status)); return status; } return status; } #define BUFFER_SIZE 256 MOUSETYPE MSerDetect( PDEVICE_EXTENSION DeviceExtension ) /*++ 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 = NO_MOUSE; NTSTATUS status; ULONG i; CHAR receiveBuffer[BUFFER_SIZE]; PAGED_CODE(); Print(DeviceExtension, DBG_SS_TRACE, ("MSerDetect enter\n")); status = SerialMouseInitializePort(DeviceExtension); if (!NT_SUCCESS(status)) { Print(DeviceExtension, DBG_SS_ERROR, ("Initializing the port failed (%x)\n", status)); // return status; } status = MSerPowerDown(DeviceExtension); if (!NT_SUCCESS(status)) { Print(DeviceExtension, DBG_SS_ERROR, ("PowerDown failed (%x)\n", status)); // return status; } // // Set the baud rate. // SerialMouseSetBaudRate(DeviceExtension, MSER_BAUDRATE); // // Set the data format so that the possible answer can be recognized. // SerialMouseSetLineCtrl(DeviceExtension, &Protocol[MSER_PROTOCOL_MP].LineCtrl); // // Clean possible garbage in uart input buffer. // SerialMouseFlushReadBuffer(DeviceExtension); status = MSerPowerUp(DeviceExtension); if (!NT_SUCCESS(status)) { Print(DeviceExtension, DBG_SS_ERROR, ("Powerup failed (%x)\n", status)); } // // 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'. // We get extraneous characters after the expected data if this a // true PnP comm device // ASSERT(CSER_POWER_UP >= MINIMUM_RESET_TIME); status = SerialMouseSetReadTimeouts(DeviceExtension, 200); if (NT_SUCCESS(SerialMouseReadChar(DeviceExtension, &receiveBuffer[count]))) { count++; SerialMouseSetReadTimeouts(DeviceExtension, 100); while (count < (BUFFER_SIZE - 1)) { if (NT_SUCCESS(SerialMouseReadChar(DeviceExtension, &receiveBuffer[count]))) { count++; } else { break; } } } *(receiveBuffer + count) = 0; Print(DeviceExtension, DBG_SS_NOISE, ("Receive buffer:\n")); for (i = 0; i < count; i++) { Print(DeviceExtension, DBG_SS_NOISE, ("\t0x%x\n", receiveBuffer[i])); } // // // 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') { Print(DeviceExtension, DBG_SS_INFO, ("Detected MSeries 3 buttons\n")); mouseType = MOUSE_3B; } else if (receiveBuffer[i + 1] == 'Z') { Print(DeviceExtension, DBG_SS_INFO, ("Detected Wheel Mouse\n")); mouseType = MOUSE_Z; } else { Print(DeviceExtension, DBG_SS_INFO, ("Detected MSeries 2 buttons\n")); mouseType = MOUSE_2B; } break; } else if (receiveBuffer[i] == 'B') { Print(DeviceExtension, DBG_SS_INFO, ("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) { SerialMouseWait(DeviceExtension, -CSER_POWER_UP); } Print(DeviceExtension, DBG_SS_ERROR | DBG_SS_INFO, ("No MSeries detected\n")); mouseType = NO_MOUSE; } // // Make sure that all subsequent reads are blocking and do not timeout // if (mouseType != NO_MOUSE) { SerialMouseSetReadTimeouts(DeviceExtension, 0); } Print(DeviceExtension, DBG_SS_INFO, ("mouse type is %d\n", (ULONG) mouseType)); return mouseType; } BOOLEAN MSerHandlerMP( IN PDEVICE_EXTENSION DeviceExtension, 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; Print(DeviceExtension, DBG_HANDLER_TRACE, ("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++; } Print(DeviceExtension, DBG_HANDLER_ERROR, ("Synch error. State: %u\n", HandlerData->State )); HandlerData->State = STATE0; } else if (!(Value & MP_SYNCH_BIT) && (HandlerData->State == STATE0)) { HandlerData->Error++; Print(DeviceExtension, DBG_HANDLER_ERROR, ("Synch error. State: %u\n", HandlerData->State )); goto LExit; } // // Check for a line state error. // // // Set the untranslated value. // HandlerData->Raw[HandlerData->State] = Value; Print(DeviceExtension, DBG_HANDLER_NOISE, ("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: Print(DeviceExtension, DBG_HANDLER_ERROR, ("MP Handler failure: incorrect state value.\n" )); ASSERT(FALSE); } LExit: Print(DeviceExtension, DBG_HANDLER_TRACE, ("MP protocol handler: exit\n")); return retval; } BOOLEAN MSerHandlerBP( IN PDEVICE_EXTENSION DeviceExtension, 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; Print(DeviceExtension, DBG_HANDLER_TRACE, ("BP protocol handler, enter\n")); // // Check for synchronization errors. // if ((Value & BP_SYNCH_BIT) && (HandlerData->State != STATE0)) { HandlerData->Error++; Print(DeviceExtension, DBG_HANDLER_ERROR, ("Synch error. State: %u\n", HandlerData->State )); HandlerData->State = STATE0; } else if (!(Value & BP_SYNCH_BIT) && (HandlerData->State == STATE0)) { HandlerData->Error++; Print(DeviceExtension, DBG_HANDLER_ERROR, ("Synch error. State: %u\n", HandlerData->State )); goto LExit; } // // Check for a line state error. // // // Set the untranslated value. // HandlerData->Raw[HandlerData->State] = Value; Print(DeviceExtension, DBG_HANDLER_NOISE, ("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; 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: Print(DeviceExtension, DBG_HANDLER_ERROR, ("BP Handler failure: incorrect state value.\n" )); ASSERT(FALSE); } LExit: Print(DeviceExtension, DBG_HANDLER_TRACE, ("BP protocol handler: exit\n")); return retval; } BOOLEAN MSerHandlerZ( IN PDEVICE_EXTENSION DeviceExtension, 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; Print(DeviceExtension, DBG_HANDLER_TRACE, ("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++; } Print(DeviceExtension, DBG_HANDLER_ERROR, ("Z Synch error #1. State: %u\n", HandlerData->State )); HandlerData->State = STATE0; } else if (!(Value & Z_SYNCH_BIT) && (HandlerData->State == STATE0)) { HandlerData->Error++; Print(DeviceExtension, DBG_HANDLER_ERROR, ("Z Synch error #2. State: %u\n", HandlerData->State )); goto LExit; } // // Check for a line state error. // // // Set the untranslated value. // HandlerData->Raw[HandlerData->State] = Value; Print(DeviceExtension, DBG_HANDLER_NOISE, ("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; } else { HandlerData->State++; } break; case STATE4: Print(DeviceExtension, DBG_HANDLER_NOISE, ("Z Got that 5th byte\n")); HandlerData->State = STATE0; retval = TRUE; break; default: Print(DeviceExtension, DBG_HANDLER_ERROR, ("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: Print(DeviceExtension, DBG_HANDLER_TRACE, ("Z protocol handler: exit\n")); return retval; }