xref: /reactos/drivers/input/sermouse/detect.c (revision 1734f297)
1 /*
2  * PROJECT:     ReactOS Serial mouse driver
3  * LICENSE:     GPL - See COPYING in the top level directory
4  * FILE:        drivers/input/sermouse/detect.c
5  * PURPOSE:     Detect serial mouse type
6  * PROGRAMMERS: Copyright Jason Filby (jasonfilby@yahoo.com)
7                 Copyright Filip Navara (xnavara@volny.cz)
8                 Copyright 2005-2006 Herv� Poussineau (hpoussin@reactos.org)
9  */
10 
11 #include "sermouse.h"
12 
13 #include <ntifs.h>
14 #include <debug.h>
15 
16 /* Most of this file is ripped from reactos/drivers/bus/serenum/detect.c */
17 
18 static NTSTATUS
19 DeviceIoControl(
20 	IN PDEVICE_OBJECT DeviceObject,
21 	IN ULONG CtlCode,
22 	IN PVOID InputBuffer OPTIONAL,
23 	IN SIZE_T InputBufferSize,
24 	IN OUT PVOID OutputBuffer OPTIONAL,
25 	IN OUT PSIZE_T OutputBufferSize)
26 {
27 	KEVENT Event;
28 	PIRP Irp;
29 	IO_STATUS_BLOCK IoStatus;
30 	NTSTATUS Status;
31 
32 	KeInitializeEvent (&Event, NotificationEvent, FALSE);
33 
34 	Irp = IoBuildDeviceIoControlRequest(CtlCode,
35 		DeviceObject,
36 		InputBuffer,
37 		(ULONG)InputBufferSize,
38 		OutputBuffer,
39 		(OutputBufferSize) ? (ULONG)*OutputBufferSize : 0,
40 		FALSE,
41 		&Event,
42 		&IoStatus);
43 	if (Irp == NULL)
44 	{
45 		WARN_(SERMOUSE, "IoBuildDeviceIoControlRequest() failed\n");
46 		return STATUS_INSUFFICIENT_RESOURCES;
47 	}
48 
49 	Status = IoCallDriver(DeviceObject, Irp);
50 
51 	if (Status == STATUS_PENDING)
52 	{
53 		INFO_(SERMOUSE, "Operation pending\n");
54 		KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
55 		Status = IoStatus.Status;
56 	}
57 
58 	if (OutputBufferSize)
59 	{
60 		*OutputBufferSize = (SIZE_T)IoStatus.Information;
61 	}
62 
63 	return Status;
64 }
65 
66 static NTSTATUS
67 ReadBytes(
68 	IN PDEVICE_OBJECT LowerDevice,
69 	OUT PUCHAR Buffer,
70 	IN ULONG BufferSize,
71 	OUT PULONG_PTR FilledBytes)
72 {
73 	PIRP Irp;
74 	IO_STATUS_BLOCK ioStatus;
75 	KEVENT event;
76 	LARGE_INTEGER zero;
77 	NTSTATUS Status;
78 
79 	KeInitializeEvent(&event, NotificationEvent, FALSE);
80 	zero.QuadPart = 0;
81 	Irp = IoBuildSynchronousFsdRequest(
82 		IRP_MJ_READ,
83 		LowerDevice,
84 		Buffer, BufferSize,
85 		&zero,
86 		&event,
87 		&ioStatus);
88 	if (!Irp)
89 		return FALSE;
90 
91 	Status = IoCallDriver(LowerDevice, Irp);
92 	if (Status == STATUS_PENDING)
93 	{
94 		KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
95 		Status = ioStatus.Status;
96 	}
97 	INFO_(SERMOUSE, "Bytes received: %lu/%lu\n",
98 		ioStatus.Information, BufferSize);
99 	*FilledBytes = ioStatus.Information;
100 	return Status;
101 }
102 
103 static NTSTATUS
104 Wait(
105 	IN ULONG milliseconds)
106 {
107 	KTIMER Timer;
108 	LARGE_INTEGER DueTime;
109 
110 	DueTime.QuadPart = milliseconds * -10;
111 	KeInitializeTimer(&Timer);
112 	KeSetTimer(&Timer, DueTime, NULL);
113 	return KeWaitForSingleObject(&Timer, Executive, KernelMode, FALSE, NULL);
114 }
115 
116 SERMOUSE_MOUSE_TYPE
117 SermouseDetectLegacyDevice(
118 	IN PDEVICE_OBJECT LowerDevice)
119 {
120 	HANDLE Handle;
121 	ULONG Fcr, Mcr;
122 	ULONG BaudRate;
123 	ULONG Command;
124 	SERIAL_TIMEOUTS Timeouts;
125 	SERIAL_LINE_CONTROL LCR;
126 	ULONG_PTR i, Count = 0;
127 	UCHAR Buffer[16];
128 	SERMOUSE_MOUSE_TYPE MouseType = mtNone;
129 	NTSTATUS Status;
130 
131 	TRACE_(SERMOUSE, "SermouseDetectLegacyDevice(LowerDevice %p)\n", LowerDevice);
132 
133 	RtlZeroMemory(Buffer, sizeof(Buffer));
134 
135 	/* Open port */
136 	Status = ObOpenObjectByPointer(
137 		LowerDevice,
138 		OBJ_KERNEL_HANDLE,
139 		NULL,
140 		0,
141 		NULL,
142 		KernelMode,
143 		&Handle);
144 	if (!NT_SUCCESS(Status)) return mtNone;
145 
146 	/* Reset UART */
147 	TRACE_(SERMOUSE, "Reset UART\n");
148 	Mcr = 0; /* MCR: DTR/RTS/OUT2 off */
149 	Status = DeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_MODEM_CONTROL,
150 		&Mcr, sizeof(Mcr), NULL, NULL);
151 	if (!NT_SUCCESS(Status)) goto ByeBye;
152 
153 	/* Set communications parameters */
154 	TRACE_(SERMOUSE, "Set communications parameters\n");
155 	/* DLAB off */
156 	Fcr = 0;
157 	Status = DeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_FIFO_CONTROL,
158 		&Fcr, sizeof(Fcr), NULL, NULL);
159 	if (!NT_SUCCESS(Status)) goto ByeBye;
160 	/* Set serial port speed */
161 	BaudRate = 1200;
162 	Status = DeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_BAUD_RATE,
163 		&BaudRate, sizeof(BaudRate), NULL, NULL);
164 	if (!NT_SUCCESS(Status)) goto ByeBye;
165 	/* Set LCR */
166 	LCR.WordLength = 7;
167 	LCR.Parity = NO_PARITY;
168 	LCR.StopBits = STOP_BITS_2;
169 	Status = DeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_LINE_CONTROL,
170 		&LCR, sizeof(LCR), NULL, NULL);
171 	if (!NT_SUCCESS(Status)) goto ByeBye;
172 
173 	/* Flush receive buffer */
174 	TRACE_(SERMOUSE, "Flush receive buffer\n");
175 	Command = SERIAL_PURGE_RXCLEAR;
176 	Status = DeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_MODEM_CONTROL,
177 		&Command, sizeof(Command), NULL, NULL);
178 	if (!NT_SUCCESS(Status)) goto ByeBye;
179 	/* Wait 100 ms */
180 	Wait(100);
181 
182 	/* Enable DTR/RTS */
183 	TRACE_(SERMOUSE, "Enable DTR/RTS\n");
184 	Status = DeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_DTR,
185 		NULL, 0, NULL, NULL);
186 	if (!NT_SUCCESS(Status)) goto ByeBye;
187 	Status = DeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_RTS,
188 		NULL, 0, NULL, NULL);
189 	if (!NT_SUCCESS(Status)) goto ByeBye;
190 
191 	/* Set timeout to 500 microseconds */
192 	TRACE_(SERMOUSE, "Set timeout to 500 microseconds\n");
193 	Timeouts.ReadIntervalTimeout = 100;
194 	Timeouts.ReadTotalTimeoutMultiplier = 0;
195 	Timeouts.ReadTotalTimeoutConstant = 500;
196 	Timeouts.WriteTotalTimeoutMultiplier = Timeouts.WriteTotalTimeoutConstant = 0;
197 	Status = DeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_TIMEOUTS,
198 		&Timeouts, sizeof(Timeouts), NULL, NULL);
199 	if (!NT_SUCCESS(Status)) goto ByeBye;
200 
201 	/* Fill the read buffer */
202 	TRACE_(SERMOUSE, "Fill the read buffer\n");
203 	Status = ReadBytes(LowerDevice, Buffer, sizeof(Buffer)/sizeof(Buffer[0]), &Count);
204 	if (!NT_SUCCESS(Status)) goto ByeBye;
205 
206 	for (i = 0; i < Count; i++)
207 	{
208 		if (Buffer[i] == 'B')
209 		{
210 			/* Sign for Microsoft Ballpoint */
211 			ERR_(SERMOUSE, "Microsoft Ballpoint device detected. THIS DEVICE IS NOT YET SUPPORTED");
212 			MouseType = mtNone;
213 			goto ByeBye;
214 		}
215 		else if (Buffer[i] == 'M')
216 		{
217 			/* Sign for Microsoft Mouse protocol followed by button specifier */
218 			if (i == sizeof(Buffer) - 1)
219 			{
220 				/* Overflow Error */
221 				goto ByeBye;
222 			}
223 			switch (Buffer[i + 1])
224 			{
225 				case '3':
226 					INFO_(SERMOUSE, "Microsoft Mouse with 3-buttons detected\n");
227 					MouseType = mtLogitech;
228 					break;
229 				case 'Z':
230 					INFO_(SERMOUSE, "Microsoft Wheel Mouse detected\n");
231 					MouseType = mtWheelZ;
232 					break;
233 				default:
234 					INFO_(SERMOUSE, "Microsoft Mouse with 2-buttons detected\n");
235 					MouseType = mtMicrosoft;
236 					break;
237 			}
238 			goto ByeBye;
239 		}
240 	}
241 
242 ByeBye:
243 	/* Close port */
244 	if (Handle)
245 		ZwClose(Handle);
246 	return MouseType;
247 }
248