You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
706 lines
16 KiB
706 lines
16 KiB
/*++
|
|
|
|
Copyright (c) 1990-1998 Microsoft Corporation, All Rights Reserved
|
|
Copyright (c) 1993 Logitech Inc.
|
|
|
|
Module Name:
|
|
|
|
cseries.c
|
|
|
|
Abstract:
|
|
|
|
Environment:
|
|
|
|
Kernel mode only.
|
|
|
|
Notes:
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
//
|
|
// Includes.
|
|
//
|
|
|
|
#include "ntddk.h"
|
|
#include "mouser.h"
|
|
#include "cseries.h"
|
|
#include "debug.h"
|
|
|
|
//
|
|
// Use the alloc_text pragma to specify the driver initialization routines
|
|
// (they can be paged out).
|
|
//
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE,CSerPowerUp)
|
|
#pragma alloc_text(PAGE,CSerSetReportRate)
|
|
#pragma alloc_text(PAGE,CSerSetBaudRate)
|
|
#pragma alloc_text(PAGE,CSerSetProtocol)
|
|
#pragma alloc_text(PAGE,CSerDetect)
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
//
|
|
// Constants.
|
|
//
|
|
|
|
//
|
|
// The status command sent to the mouse.
|
|
//
|
|
|
|
#define CSER_STATUS_COMMAND 's'
|
|
|
|
//
|
|
// The query number of mouse buttons command sent to the mouse.
|
|
//
|
|
|
|
#define CSER_QUERY_BUTTONS_COMMAND 'k'
|
|
|
|
//
|
|
// Status report from a CSeries mouse.
|
|
//
|
|
|
|
#define CSER_STATUS 0x4F
|
|
|
|
//
|
|
// Timeout value for the status returned by a CSeries mouse.
|
|
//
|
|
// #define CSER_STATUS_DELAY (50 * MS_TO_100_NS)
|
|
#define CSER_STATUS_DELAY 50
|
|
|
|
//
|
|
// Time (in microconds) needed by the mouse to adapt to a new baud rate.
|
|
//
|
|
|
|
#define CSER_BAUDRATE_DELAY (2 * MS_TO_100_NS)
|
|
|
|
//
|
|
// Default baud rate and report rate.
|
|
//
|
|
|
|
#define CSER_DEFAULT_BAUDRATE 1200
|
|
#define CSER_DEFAULT_REPORTRATE 150
|
|
|
|
//
|
|
// Button/status definitions.
|
|
//
|
|
|
|
#define CSER_SYNCH_BIT 0x80
|
|
|
|
#define CSER_BUTTON_LEFT 0x04
|
|
#define CSER_BUTTON_RIGHT 0x01
|
|
#define CSER_BUTTON_MIDDLE 0x02
|
|
|
|
#define CSER_BUTTON_LEFT_SR 2
|
|
#define CSER_BUTTON_RIGHT_SL 1
|
|
#define CSER_BUTTON_MIDDLE_SL 1
|
|
|
|
#define SIGN_X 0x10
|
|
#define SIGN_Y 0x08
|
|
|
|
//
|
|
// Macros.
|
|
//
|
|
|
|
#define sizeofel(x) (sizeof(x)/sizeof(*x))
|
|
|
|
//
|
|
// Type definitions.
|
|
//
|
|
|
|
typedef struct _REPORT_RATE {
|
|
CHAR Command;
|
|
UCHAR ReportRate;
|
|
} REPORT_RATE;
|
|
|
|
typedef struct _PROTOCOL {
|
|
CHAR Command;
|
|
SERIAL_LINE_CONTROL LineCtrl;
|
|
PPROTOCOL_HANDLER Handler;
|
|
} PROTOCOL;
|
|
|
|
typedef struct _CSER_BAUDRATE {
|
|
CHAR *Command;
|
|
ULONG BaudRate;
|
|
} CSER_BAUDRATE;
|
|
|
|
//
|
|
// Globals.
|
|
//
|
|
|
|
//
|
|
// The baud rate at which we try to detect a mouse.
|
|
//
|
|
|
|
static ULONG BaudRateDetect[] = { 1200, 2400, 4800, 9600 };
|
|
|
|
//
|
|
// This list is indexed by protocol values PROTOCOL_*.
|
|
//
|
|
|
|
PROTOCOL Protocol[] = {
|
|
{'S',
|
|
// ACE_8BW | ACE_PEN | ACE_1SB,
|
|
{ STOP_BIT_1, 0, 8 },
|
|
CSerHandlerMM
|
|
},
|
|
{'T',
|
|
// ACE_8BW | ACE_1SB,
|
|
{ STOP_BIT_1, NO_PARITY, 8 },
|
|
NULL
|
|
},
|
|
{'U',
|
|
// ACE_8BW | ACE_1SB,
|
|
{ STOP_BIT_1, NO_PARITY, 8 },
|
|
NULL
|
|
},
|
|
{'V',
|
|
// ACE_7BW | ACE_1SB,
|
|
{ STOP_BIT_1, NO_PARITY, 7 },
|
|
NULL
|
|
},
|
|
{'B',
|
|
// ACE_7BW | ACE_PEN | ACE_EPS | ACE_1SB,
|
|
{ STOP_BIT_1, EVEN_PARITY, 7 },
|
|
NULL
|
|
},
|
|
{'A',
|
|
// ACE_7BW | ACE_PEN | ACE_EPS | ACE_1SB,
|
|
{ STOP_BIT_1, EVEN_PARITY, 7 },
|
|
NULL
|
|
}
|
|
};
|
|
|
|
static REPORT_RATE ReportRateTable[] = {
|
|
{'D', 0 },
|
|
{'J', 10},
|
|
{'K', 20},
|
|
{'L', 35},
|
|
{'R', 50},
|
|
{'M', 70},
|
|
{'Q', 100},
|
|
{'N', 150},
|
|
{'O', 151} // Continuous
|
|
};
|
|
static CSER_BAUDRATE CserBaudRateTable[] = {
|
|
{ "*n", 1200 },
|
|
{ "*o", 2400 },
|
|
{ "*p", 4800 },
|
|
{ "*q", 9600 }
|
|
};
|
|
|
|
NTSTATUS
|
|
CSerPowerUp(
|
|
PDEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Powers up the mouse by making the RTS and DTR active.
|
|
|
|
Arguments:
|
|
|
|
Port - Pointer to the serial port.
|
|
|
|
Return Value:
|
|
|
|
TRUE.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
IO_STATUS_BLOCK iosb;
|
|
KEVENT event;
|
|
ULONG bits;
|
|
ULONG rtsDtr = SERIAL_RTS_STATE | SERIAL_DTR_STATE;
|
|
|
|
PAGED_CODE();
|
|
|
|
Print(DeviceExtension, DBG_SS_TRACE, ("(c) PowerUp called\n"));
|
|
|
|
KeInitializeEvent(&event,
|
|
NotificationEvent,
|
|
FALSE
|
|
);
|
|
//
|
|
// set DTR
|
|
//
|
|
Print(DeviceExtension, DBG_SS_NOISE, ("(c) Setting DTR...\n"));
|
|
status = SerialMouseIoSyncIoctl(IOCTL_SERIAL_SET_DTR,
|
|
DeviceExtension->TopOfStack,
|
|
&event,
|
|
&iosb
|
|
);
|
|
|
|
//
|
|
// set RTS
|
|
//
|
|
Print(DeviceExtension, DBG_SS_NOISE, ("(c) Setting RTS...\n"));
|
|
status = SerialMouseIoSyncIoctl(IOCTL_SERIAL_SET_RTS,
|
|
DeviceExtension->TopOfStack,
|
|
&event,
|
|
&iosb
|
|
);
|
|
|
|
//
|
|
// If the lines are high, the power is on for at least 500 ms due to the
|
|
// MSeries detection.
|
|
//
|
|
status = SerialMouseIoSyncIoctlEx(IOCTL_SERIAL_GET_MODEMSTATUS,
|
|
DeviceExtension->TopOfStack,
|
|
&event,
|
|
&iosb,
|
|
NULL,
|
|
0,
|
|
&bits,
|
|
sizeof(ULONG)
|
|
);
|
|
|
|
if (NT_SUCCESS(status) && ((rtsDtr & bits) == rtsDtr)) {
|
|
//
|
|
// Wait CSER_POWER_UP milliseconds for the mouse to power up
|
|
// correctly.
|
|
//
|
|
Print(DeviceExtension, DBG_SS_INFO,
|
|
("(c) Waiting awhile for the mouse to power up\n"));
|
|
SerialMouseWait(DeviceExtension,
|
|
-CSER_POWER_UP
|
|
);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
VOID
|
|
CSerSetReportRate(
|
|
PDEVICE_EXTENSION DeviceExtension,
|
|
UCHAR ReportRate
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set the mouse report rate. This can range from 0 (prompt mode) to
|
|
continuous report rate.
|
|
|
|
Arguments:
|
|
|
|
Port - Pointer to serial port.
|
|
|
|
ReportRate - The desired report rate.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
LONG count;
|
|
|
|
PAGED_CODE();
|
|
|
|
Print(DeviceExtension, DBG_SS_TRACE, ("CSerSetReportRate called\n"));
|
|
|
|
for (count = sizeofel(ReportRateTable) - 1; count >= 0; count--) {
|
|
|
|
//
|
|
// Get the character to send from the table.
|
|
//
|
|
|
|
if (ReportRate >= ReportRateTable[count].ReportRate) {
|
|
|
|
//
|
|
// Set the baud rate.
|
|
//
|
|
|
|
Print(DeviceExtension, DBG_SS_INFO,
|
|
("New ReportRate: %u\n",
|
|
ReportRateTable[count].ReportRate
|
|
));
|
|
|
|
SerialMouseWriteChar(DeviceExtension, ReportRateTable[count].Command);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
CSerSetBaudRate(
|
|
PDEVICE_EXTENSION DeviceExtension,
|
|
ULONG BaudRate
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set the new mouse baud rate. This will change the serial port baud rate.
|
|
|
|
Arguments:
|
|
|
|
Port - Pointer to the serial port.
|
|
|
|
BaudRate - Desired baud rate.
|
|
|
|
BaudClock - The external frequency driving the serial chip.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
LONG count;
|
|
|
|
PAGED_CODE();
|
|
|
|
Print(DeviceExtension, DBG_SS_TRACE, ("CSerSetBaudRate called\n"));
|
|
|
|
//
|
|
// Before we mess with the baud rate, put the mouse in prompt mode.
|
|
//
|
|
CSerSetReportRate(DeviceExtension, 0);
|
|
|
|
for (count = sizeofel(CserBaudRateTable) - 1; count >= 0; count--) {
|
|
if (BaudRate >= CserBaudRateTable[count].BaudRate) {
|
|
|
|
//
|
|
// Set the baud rate.
|
|
//
|
|
|
|
SerialMouseWriteString(DeviceExtension, CserBaudRateTable[count].Command);
|
|
SerialMouseSetBaudRate(DeviceExtension, CserBaudRateTable[count].BaudRate);
|
|
|
|
//
|
|
// Delay to allow the UART and the mouse to synchronize
|
|
// correctly.
|
|
//
|
|
SerialMouseWait(DeviceExtension,
|
|
-CSER_BAUDRATE_DELAY
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
PPROTOCOL_HANDLER
|
|
CSerSetProtocol(
|
|
PDEVICE_EXTENSION DeviceExtension,
|
|
UCHAR NewProtocol
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Change the mouse protocol.
|
|
|
|
Note: Not all the protocols are implemented in this driver.
|
|
|
|
Arguments:
|
|
|
|
Port - Pointer to the serial port.
|
|
|
|
|
|
Return Value:
|
|
|
|
Address of the protocol handler function. See the interrupt service
|
|
routine.
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
Print(DeviceExtension, DBG_SS_TRACE, ("CSerSetProtocol called\n"));
|
|
|
|
ASSERT(NewProtocol < CSER_PROTOCOL_MAX);
|
|
|
|
//
|
|
// Set the protocol.
|
|
//
|
|
SerialMouseWriteChar(DeviceExtension, Protocol[NewProtocol].Command);
|
|
SerialMouseSetLineCtrl(DeviceExtension, &Protocol[NewProtocol].LineCtrl);
|
|
|
|
Print(DeviceExtension, DBG_SS_INFO, ("NewProtocol: %u\n", NewProtocol & 0xFF));
|
|
|
|
return Protocol[NewProtocol].Handler;
|
|
}
|
|
|
|
BOOLEAN
|
|
CSerDetect(
|
|
PDEVICE_EXTENSION DeviceExtension,
|
|
PULONG HardwareButtons
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Detection of a CSeries type mouse. The main steps are:
|
|
|
|
- Power up the mouse.
|
|
- Cycle through the available baud rates and try to get an answer
|
|
from the mouse.
|
|
|
|
At the end of the routine, a default baud rate and report rate are set.
|
|
|
|
Arguments:
|
|
|
|
Port - Pointer to the serial port.
|
|
|
|
HardwareButtons - Returns the number of hardware buttons detected.
|
|
|
|
Return Value:
|
|
|
|
TRUE if a CSeries type mouse is detected, otherwise FALSE.
|
|
|
|
--*/
|
|
{
|
|
UCHAR status, numButtons;
|
|
ULONG count;
|
|
BOOLEAN detected = FALSE;
|
|
|
|
Print(DeviceExtension, DBG_SS_TRACE, ("CSerDetect called\n"));
|
|
|
|
//
|
|
// Power up the mouse if necessary.
|
|
//
|
|
|
|
CSerPowerUp(DeviceExtension);
|
|
|
|
//
|
|
// Set the line control register to a format that the mouse can
|
|
// understand (see below: the line is set after the report rate).
|
|
//
|
|
|
|
SerialMouseSetLineCtrl(DeviceExtension, &Protocol[CSER_PROTOCOL_MM].LineCtrl);
|
|
|
|
//
|
|
// Cycle through the different baud rates to detect the mouse.
|
|
//
|
|
|
|
for (count = 0; count < sizeofel(BaudRateDetect); count++) {
|
|
|
|
SerialMouseSetBaudRate(DeviceExtension, BaudRateDetect[count]);
|
|
|
|
//
|
|
// Put the mouse in prompt mode.
|
|
//
|
|
|
|
CSerSetReportRate(DeviceExtension, 0);
|
|
|
|
//
|
|
// Set the MM protocol. This way we get the mouse to talk to us in a
|
|
// specific format. This avoids receiving errors from the line
|
|
// register.
|
|
//
|
|
|
|
CSerSetProtocol(DeviceExtension, CSER_PROTOCOL_MM);
|
|
|
|
//
|
|
// Try to get the status byte.
|
|
//
|
|
|
|
SerialMouseWriteChar(DeviceExtension, CSER_STATUS_COMMAND);
|
|
|
|
//
|
|
// In case something is already there...
|
|
//
|
|
|
|
SerialMouseFlushReadBuffer(DeviceExtension);
|
|
|
|
SerialMouseSetReadTimeouts(DeviceExtension, 50);
|
|
//
|
|
// Read back the status character.
|
|
//
|
|
if (NT_SUCCESS(SerialMouseReadChar(DeviceExtension, &status)) &&
|
|
(status == CSER_STATUS)) {
|
|
detected = TRUE;
|
|
Print(DeviceExtension, DBG_SS_INFO,
|
|
("Detected mouse at %u baud\n",
|
|
BaudRateDetect[count]
|
|
));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (detected) {
|
|
|
|
//
|
|
// Get the number of buttons back from the mouse.
|
|
//
|
|
SerialMouseWriteChar(DeviceExtension, CSER_QUERY_BUTTONS_COMMAND);
|
|
|
|
//
|
|
// In case something is already there...
|
|
//
|
|
|
|
SerialMouseFlushReadBuffer(DeviceExtension);
|
|
|
|
//
|
|
// Read back the number of buttons.
|
|
//
|
|
SerialMouseSetReadTimeouts(DeviceExtension, CSER_STATUS_DELAY);
|
|
if (NT_SUCCESS(SerialMouseReadChar(DeviceExtension, &numButtons))) {
|
|
|
|
numButtons &= 0x0F;
|
|
Print(DeviceExtension, DBG_SS_NOISE,
|
|
("Successfully read number of buttons (%1u)\n", numButtons));
|
|
|
|
if (numButtons == 2 || numButtons == 3) {
|
|
*HardwareButtons = numButtons;
|
|
} else {
|
|
*HardwareButtons = MOUSE_NUMBER_OF_BUTTONS;
|
|
}
|
|
} else {
|
|
*HardwareButtons = MOUSE_NUMBER_OF_BUTTONS;
|
|
}
|
|
|
|
//
|
|
// Make sure that all subsequent reads are blocking and do not timeout
|
|
//
|
|
SerialMouseSetReadTimeouts(DeviceExtension, 0);
|
|
}
|
|
|
|
//
|
|
// Put the mouse back in a default mode. The protocol is already set.
|
|
//
|
|
CSerSetBaudRate(DeviceExtension, CSER_DEFAULT_BAUDRATE);
|
|
CSerSetReportRate(DeviceExtension, CSER_DEFAULT_REPORTRATE);
|
|
|
|
Print(DeviceExtension, DBG_SS_INFO,
|
|
("Detected: %s\n", detected ? "true" : "false"));
|
|
Print(DeviceExtension, DBG_SS_INFO, ("Status byte: %#x\n", status));
|
|
|
|
return detected;
|
|
}
|
|
|
|
BOOLEAN
|
|
CSerHandlerMM(
|
|
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 MM protocol.
|
|
|
|
Arguments:
|
|
|
|
CurrentInput - Pointer to the report packet.
|
|
|
|
Value - The input buffer value.
|
|
|
|
LineState - The serial port line state.
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE if the handler has a completed report.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOLEAN retval = FALSE;
|
|
|
|
Print(DeviceExtension, DBG_HANDLER_TRACE, ("MMHandler, enter\n"));
|
|
|
|
if ((Value & CSER_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 & CSER_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.
|
|
//
|
|
Print(DeviceExtension, DBG_HANDLER_INFO,
|
|
("State%u\n", HandlerData->State));
|
|
HandlerData->Raw[HandlerData->State] = Value;
|
|
|
|
switch (HandlerData->State) {
|
|
case STATE0:
|
|
case STATE1:
|
|
HandlerData->State++;
|
|
break;
|
|
|
|
case STATE2:
|
|
HandlerData->State = STATE0;
|
|
|
|
//
|
|
// Buttons formatting.
|
|
//
|
|
CurrentInput->RawButtons =
|
|
(HandlerData->Raw[STATE0] & CSER_BUTTON_LEFT) >> CSER_BUTTON_LEFT_SR;
|
|
CurrentInput->RawButtons |=
|
|
(HandlerData->Raw[STATE0] & CSER_BUTTON_RIGHT) << CSER_BUTTON_RIGHT_SL;
|
|
CurrentInput->RawButtons |=
|
|
(HandlerData->Raw[STATE0] & CSER_BUTTON_MIDDLE) << CSER_BUTTON_MIDDLE_SL;
|
|
|
|
//
|
|
// Displacement formatting.
|
|
//
|
|
|
|
CurrentInput->LastX = (HandlerData->Raw[STATE0] & SIGN_X) ?
|
|
HandlerData->Raw[STATE1] :
|
|
-(LONG)HandlerData->Raw[STATE1];
|
|
|
|
//
|
|
// Note: The Y displacement is positive to the south.
|
|
//
|
|
|
|
CurrentInput->LastY = (HandlerData->Raw[STATE0] & SIGN_Y) ?
|
|
-(LONG)HandlerData->Raw[STATE2] :
|
|
HandlerData->Raw[STATE2];
|
|
|
|
Print(DeviceExtension, DBG_HANDLER_NOISE,
|
|
("Displacement X: %ld\n",
|
|
CurrentInput->LastX
|
|
));
|
|
Print(DeviceExtension, DBG_HANDLER_NOISE,
|
|
("Displacement Y: %ld\n",
|
|
CurrentInput->LastY
|
|
));
|
|
Print(DeviceExtension, DBG_HANDLER_NOISE,
|
|
("Raw Buttons: %0lx\n",
|
|
CurrentInput->RawButtons
|
|
));
|
|
|
|
//
|
|
// The report is complete. Tell the interrupt handler to send it.
|
|
//
|
|
|
|
retval = TRUE;
|
|
|
|
break;
|
|
|
|
default:
|
|
Print(DeviceExtension, DBG_HANDLER_ERROR,
|
|
("MM Handler failure: incorrect state value.\n"));
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
LExit:
|
|
Print(DeviceExtension, DBG_HANDLER_TRACE, ("MMHandler, exit\n"));
|
|
|
|
return retval;
|
|
}
|