8ca0f2619d4f9632f9240500e443125246c5d424
[reactos.git] / reactos / drivers / bus / serenum / detect.c
1 /* $Id:
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS Serial enumerator driver
5 * FILE: drivers/bus/serenum/detect.c
6 * PURPOSE: Detection of serial devices
7 *
8 * PROGRAMMERS: Jason Filby (jasonfilby@yahoo.com)
9 * Filip Navara (xnavara@volny.cz)
10 * Hervé Poussineau (hpoussin@reactos.com)
11 */
12
13 #define NDEBUG
14 #include "serenum.h"
15
16 static NTSTATUS
17 SerenumDeviceIoControl(
18 IN PDEVICE_OBJECT DeviceObject,
19 IN ULONG CtlCode,
20 IN PVOID InputBuffer OPTIONAL,
21 IN ULONG InputBufferSize,
22 IN OUT PVOID OutputBuffer OPTIONAL,
23 IN OUT PULONG OutputBufferSize)
24 {
25 KEVENT Event;
26 PIRP Irp;
27 IO_STATUS_BLOCK IoStatus;
28 NTSTATUS Status;
29
30 KeInitializeEvent (&Event, NotificationEvent, FALSE);
31
32 Irp = IoBuildDeviceIoControlRequest(CtlCode,
33 DeviceObject,
34 InputBuffer,
35 InputBufferSize,
36 OutputBuffer,
37 (OutputBufferSize) ? *OutputBufferSize : 0,
38 FALSE,
39 &Event,
40 &IoStatus);
41 if (Irp == NULL)
42 {
43 DPRINT("Serenum: IoBuildDeviceIoControlRequest() failed\n");
44 return STATUS_INSUFFICIENT_RESOURCES;
45 }
46
47 Status = IoCallDriver(DeviceObject, Irp);
48
49 if (Status == STATUS_PENDING)
50 {
51 DPRINT("Serenum: Operation pending\n");
52 KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
53 Status = IoStatus.Status;
54 }
55
56 if (OutputBufferSize)
57 {
58 *OutputBufferSize = IoStatus.Information;
59 }
60
61 return Status;
62 }
63
64 static NTSTATUS
65 ReadBytes(
66 IN PDEVICE_OBJECT LowerDevice,
67 OUT PUCHAR Buffer,
68 IN ULONG BufferSize,
69 OUT PULONG FilledBytes)
70 {
71 PIRP Irp;
72 IO_STATUS_BLOCK ioStatus;
73 KEVENT event;
74 NTSTATUS Status;
75
76 KeInitializeEvent(&event, NotificationEvent, FALSE);
77 Irp = IoBuildSynchronousFsdRequest(
78 IRP_MJ_READ,
79 LowerDevice,
80 Buffer, BufferSize,
81 0,
82 &event,
83 &ioStatus);
84 if (!Irp)
85 return FALSE;
86
87 Status = IoCallDriver(LowerDevice, Irp);
88 if (Status == STATUS_PENDING)
89 {
90 KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
91 Status = ioStatus.Status;
92 }
93 DPRINT("Serenum: bytes received: %lu/%lu\n",
94 ioStatus.Information, BufferSize);
95 *FilledBytes = ioStatus.Information;
96 return Status;
97 }
98
99 static NTSTATUS
100 ReportDetectedDevice(
101 IN PDEVICE_OBJECT DeviceObject,
102 IN PUNICODE_STRING DeviceDescription,
103 IN PUNICODE_STRING DeviceId,
104 IN PUNICODE_STRING HardwareIds,
105 IN PUNICODE_STRING CompatibleIds)
106 {
107 PDEVICE_OBJECT Pdo = NULL;
108 PPDO_DEVICE_EXTENSION PdoDeviceExtension;
109 PFDO_DEVICE_EXTENSION FdoDeviceExtension;
110 NTSTATUS Status;
111
112 DPRINT("Serenum: SerenumReportDetectedDevice() called with %wZ (%wZ) detected\n", DeviceId, DeviceDescription);
113
114 Status = IoCreateDevice(
115 DeviceObject->DriverObject,
116 sizeof(PDO_DEVICE_EXTENSION),
117 NULL,
118 FILE_DEVICE_CONTROLLER,
119 FILE_AUTOGENERATED_DEVICE_NAME,
120 FALSE,
121 &Pdo);
122 if (!NT_SUCCESS(Status)) goto ByeBye;
123
124 Pdo->Flags |= DO_BUS_ENUMERATED_DEVICE;
125 Pdo->Flags |= DO_POWER_PAGABLE;
126 PdoDeviceExtension = (PPDO_DEVICE_EXTENSION)Pdo->DeviceExtension;
127 FdoDeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
128 RtlZeroMemory(PdoDeviceExtension, sizeof(PDO_DEVICE_EXTENSION));
129 PdoDeviceExtension->Common.IsFDO = FALSE;
130 Status = SerenumDuplicateUnicodeString(&PdoDeviceExtension->DeviceDescription, DeviceDescription, PagedPool);
131 if (!NT_SUCCESS(Status)) goto ByeBye;
132 Status = SerenumDuplicateUnicodeString(&PdoDeviceExtension->DeviceId, DeviceId, PagedPool);
133 if (!NT_SUCCESS(Status)) goto ByeBye;
134 Status = SerenumDuplicateUnicodeString(&PdoDeviceExtension->HardwareIds, HardwareIds, PagedPool);
135 if (!NT_SUCCESS(Status)) goto ByeBye;
136 Status = SerenumDuplicateUnicodeString(&PdoDeviceExtension->CompatibleIds, CompatibleIds, PagedPool);
137 if (!NT_SUCCESS(Status)) goto ByeBye;
138
139 /* Device attached to serial port (Pdo) may delegate work to
140 * serial port stack (Fdo = DeviceObject variable) */
141 Pdo->StackSize = DeviceObject->StackSize + 1;
142
143 FdoDeviceExtension->AttachedPdo = Pdo;
144 PdoDeviceExtension->AttachedFdo = DeviceObject;
145
146 Pdo->Flags |= DO_BUFFERED_IO;
147 Pdo->Flags &= ~DO_DEVICE_INITIALIZING;
148
149 return STATUS_SUCCESS;
150
151 ByeBye:
152 if (Pdo)
153 {
154 if (PdoDeviceExtension->DeviceDescription.Buffer)
155 RtlFreeUnicodeString(&PdoDeviceExtension->DeviceDescription);
156 if (PdoDeviceExtension->DeviceId.Buffer)
157 RtlFreeUnicodeString(&PdoDeviceExtension->DeviceId);
158 if (PdoDeviceExtension->HardwareIds.Buffer)
159 RtlFreeUnicodeString(&PdoDeviceExtension->HardwareIds);
160 if (PdoDeviceExtension->CompatibleIds.Buffer)
161 RtlFreeUnicodeString(&PdoDeviceExtension->CompatibleIds);
162 IoDeleteDevice(Pdo);
163 }
164 return Status;
165 }
166
167 static BOOLEAN
168 SerenumIsValidPnpIdString(
169 IN PUCHAR Buffer,
170 IN ULONG BufferLength)
171 {
172 /* FIXME: SerenumIsValidPnpIdString not implemented */
173 DPRINT1("Serenum: SerenumIsValidPnpIdString() unimplemented\n");
174 return STATUS_SUCCESS;
175 }
176
177 static NTSTATUS
178 ReportDetectedPnpDevice(
179 IN PUCHAR Buffer,
180 IN ULONG BufferLength)
181 {
182 ULONG i;
183 /* FIXME: ReportDetectedPnpDevice not implemented */
184 DPRINT1("Serenum: ReportDetectedPnpDevice() unimplemented\n");
185 DPRINT1("");
186 for (i = 0; i < BufferLength; i++)
187 DbgPrint("%c", Buffer[i]);
188 DbgPrint("\n");
189 /* Call ReportDetectedDevice */
190 return STATUS_SUCCESS;
191 }
192
193 #define BEGIN_ID '('
194 #define END_ID ')'
195
196 static NTSTATUS
197 SerenumWait(ULONG milliseconds)
198 {
199 KTIMER Timer;
200 LARGE_INTEGER DueTime;
201
202 DueTime.QuadPart = -milliseconds * 10;
203 KeInitializeTimer(&Timer);
204 KeSetTimer(&Timer, DueTime, NULL);
205 return KeWaitForSingleObject(&Timer, Executive, KernelMode, FALSE, NULL);
206 }
207
208 NTSTATUS
209 SerenumDetectPnpDevice(
210 IN PDEVICE_OBJECT DeviceObject,
211 IN PDEVICE_OBJECT LowerDevice)
212 {
213 UCHAR Buffer[256];
214 ULONG BaudRate;
215 ULONG TotalBytesReceived = 0;
216 ULONG Size;
217 ULONG Msr, Purge;
218 ULONG i;
219 BOOLEAN BufferContainsBeginId, BufferContainsEndId;
220 SERIAL_LINE_CONTROL Lcr;
221 SERIAL_TIMEOUTS Timeouts;
222 SERIALPERF_STATS PerfStats;
223 NTSTATUS Status;
224
225 /* 1. COM port initialization, check for device enumerate */
226 CHECKPOINT;
227 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_CLR_DTR,
228 NULL, 0, NULL, NULL);
229 if (!NT_SUCCESS(Status)) return Status;
230 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_CLR_RTS,
231 NULL, 0, NULL, NULL);
232 if (!NT_SUCCESS(Status)) return Status;
233 SerenumWait(200);
234 Size = sizeof(Msr);
235 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_GET_MODEMSTATUS,
236 NULL, 0, &Msr, &Size);
237 if (!NT_SUCCESS(Status)) return Status;
238 if ((Msr & SR_MSR_DSR) == 0) goto SerenumDisconnectIdle;
239
240 /* 2. COM port setup, 1st phase */
241 CHECKPOINT;
242 BaudRate = SERIAL_BAUD_1200;
243 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_BAUD_RATE,
244 &BaudRate, sizeof(BaudRate), NULL, 0);
245 if (!NT_SUCCESS(Status)) return Status;
246 Lcr.WordLength = 7;
247 Lcr.Parity = NO_PARITY;
248 Lcr.StopBits = STOP_BIT_1;
249 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_LINE_CONTROL,
250 &Lcr, sizeof(Lcr), NULL, NULL);
251 if (!NT_SUCCESS(Status)) return Status;
252 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_CLR_DTR,
253 NULL, 0, NULL, NULL);
254 if (!NT_SUCCESS(Status)) return Status;
255 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_CLR_RTS,
256 NULL, 0, NULL, NULL);
257 if (!NT_SUCCESS(Status)) return Status;
258 SerenumWait(200);
259 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_DTR,
260 NULL, 0, NULL, NULL);
261 if (!NT_SUCCESS(Status)) return Status;
262 SerenumWait(200);
263
264 /* 3. Wait for response, 1st phase */
265 CHECKPOINT;
266 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_RTS,
267 NULL, 0, NULL, NULL);
268 if (!NT_SUCCESS(Status)) return Status;
269 Timeouts.ReadIntervalTimeout = 0;
270 Timeouts.ReadTotalTimeoutMultiplier = 0;
271 Timeouts.ReadTotalTimeoutConstant = 200;
272 Timeouts.WriteTotalTimeoutMultiplier = Timeouts.WriteTotalTimeoutConstant = 0;
273 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_TIMEOUTS,
274 &Timeouts, sizeof(Timeouts), NULL, NULL);
275 if (!NT_SUCCESS(Status)) return Status;
276 Status = ReadBytes(LowerDevice, Buffer, sizeof(Buffer), &Size);
277 if (!NT_SUCCESS(Status)) return Status;
278 if (Size != 0) goto SerenumCollectPnpComDeviceId;
279
280 /* 4. COM port setup, 2nd phase */
281 CHECKPOINT;
282 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_CLR_DTR,
283 NULL, 0, NULL, NULL);
284 if (!NT_SUCCESS(Status)) return Status;
285 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_CLR_RTS,
286 NULL, 0, NULL, NULL);
287 if (!NT_SUCCESS(Status)) return Status;
288 Purge = SERIAL_PURGE_RXABORT | SERIAL_PURGE_RXCLEAR;
289 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_PURGE,
290 &Purge, 0, NULL, NULL);
291 if (!NT_SUCCESS(Status)) return Status;
292 SerenumWait(200);
293
294 /* 5. Wait for response, 2nd phase */
295 CHECKPOINT;
296 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_DTR,
297 NULL, 0, NULL, NULL);
298 if (!NT_SUCCESS(Status)) return Status;
299 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_RTS,
300 NULL, 0, NULL, NULL);
301 if (!NT_SUCCESS(Status)) return Status;
302 Status = ReadBytes(LowerDevice, Buffer, 1, &TotalBytesReceived);
303 if (!NT_SUCCESS(Status)) return Status;
304 if (TotalBytesReceived != 0) goto SerenumCollectPnpComDeviceId;
305 Size = sizeof(Msr);
306 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_GET_MODEMSTATUS,
307 NULL, 0, &Msr, &Size);
308 if (!NT_SUCCESS(Status)) return Status;
309 if ((Msr & SR_MSR_DSR) == 0) goto SerenumVerifyDisconnect; else goto SerenumConnectIdle;
310
311 /* 6. Collect PnP COM device ID */
312 SerenumCollectPnpComDeviceId:
313 CHECKPOINT;
314 Timeouts.ReadIntervalTimeout = 200;
315 Timeouts.ReadTotalTimeoutMultiplier = 0;
316 Timeouts.ReadTotalTimeoutConstant = 2200;
317 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_TIMEOUTS,
318 &Timeouts, sizeof(Timeouts), NULL, NULL);
319 if (!NT_SUCCESS(Status)) return Status;
320 Status = ReadBytes(LowerDevice, &Buffer[TotalBytesReceived], sizeof(Buffer) - TotalBytesReceived, &Size);
321 if (!NT_SUCCESS(Status)) return Status;
322 TotalBytesReceived += Size;
323 Size = sizeof(PerfStats);
324 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_GET_STATS,
325 NULL, 0, &PerfStats, &Size);
326 if (!NT_SUCCESS(Status)) return Status;
327 if (PerfStats.FrameErrorCount + PerfStats.ParityErrorCount != 0) goto SerenumConnectIdle;
328 BufferContainsBeginId = BufferContainsEndId = FALSE;
329 for (i = 0; i < TotalBytesReceived; i++)
330 {
331 if (Buffer[i] == BEGIN_ID) BufferContainsBeginId = TRUE;
332 if (Buffer[i] == END_ID) BufferContainsEndId = TRUE;
333 }
334 if (TotalBytesReceived == 1 || BufferContainsEndId)
335 {
336 if (SerenumIsValidPnpIdString(Buffer, TotalBytesReceived))
337 return ReportDetectedPnpDevice(Buffer, TotalBytesReceived);
338 goto SerenumConnectIdle;
339 }
340 if (!BufferContainsBeginId) goto SerenumConnectIdle;
341 if (!BufferContainsEndId) goto SerenumConnectIdle;
342 Size = sizeof(Msr);
343 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_GET_MODEMSTATUS,
344 NULL, 0, &Msr, &Size);
345 if (!NT_SUCCESS(Status)) return Status;
346 if ((Msr & SR_MSR_DSR) == 0) goto SerenumVerifyDisconnect;
347
348 /* 7. Verify disconnect */
349 SerenumVerifyDisconnect:
350 CHECKPOINT;
351 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_DTR,
352 NULL, 0, NULL, NULL);
353 if (!NT_SUCCESS(Status)) return Status;
354 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_CLR_RTS,
355 NULL, 0, NULL, NULL);
356 if (!NT_SUCCESS(Status)) return Status;
357 SerenumWait(5000);
358 goto SerenumDisconnectIdle;
359
360 /* 8. Connect idle */
361 SerenumConnectIdle:
362 CHECKPOINT;
363 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_DTR,
364 NULL, 0, NULL, NULL);
365 if (!NT_SUCCESS(Status)) return Status;
366 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_CLR_RTS,
367 NULL, 0, NULL, NULL);
368 if (!NT_SUCCESS(Status)) return Status;
369 BaudRate = SERIAL_BAUD_300;
370 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_BAUD_RATE,
371 &BaudRate, sizeof(BaudRate), NULL, NULL);
372 if (!NT_SUCCESS(Status)) return Status;
373 Lcr.WordLength = 7;
374 Lcr.Parity = NO_PARITY;
375 Lcr.StopBits = STOP_BIT_1;
376 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_LINE_CONTROL,
377 &Lcr, sizeof(Lcr), NULL, NULL);
378 if (!NT_SUCCESS(Status)) return Status;
379 if (TotalBytesReceived == 0)
380 return STATUS_DEVICE_NOT_CONNECTED;
381 else
382 return STATUS_SUCCESS;
383
384 /* 9. Disconnect idle */
385 SerenumDisconnectIdle:
386 CHECKPOINT;
387 /* FIXME: report to OS device removal, if it was present */
388 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_DTR,
389 NULL, 0, NULL, NULL);
390 if (!NT_SUCCESS(Status)) return Status;
391 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_CLR_RTS,
392 NULL, 0, NULL, NULL);
393 if (!NT_SUCCESS(Status)) return Status;
394 BaudRate = SERIAL_BAUD_300;
395 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_BAUD_RATE,
396 &BaudRate, sizeof(BaudRate), NULL, NULL);
397 if (!NT_SUCCESS(Status)) return Status;
398 Lcr.WordLength = 7;
399 Lcr.Parity = NO_PARITY;
400 Lcr.StopBits = STOP_BIT_1;
401 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_LINE_CONTROL,
402 &Lcr, sizeof(Lcr), NULL, NULL);
403 if (!NT_SUCCESS(Status)) return Status;
404 return STATUS_DEVICE_NOT_CONNECTED;
405 }
406
407 NTSTATUS
408 SerenumDetectLegacyDevice(
409 IN PDEVICE_OBJECT DeviceObject,
410 IN PDEVICE_OBJECT LowerDevice)
411 {
412 ULONG Fcr, Mcr;
413 ULONG BaudRate;
414 ULONG Command;
415 SERIAL_TIMEOUTS Timeouts;
416 SERIAL_LINE_CONTROL LCR;
417 ULONG i, Count;
418 UCHAR Buffer[16];
419 UNICODE_STRING DeviceDescription;
420 UNICODE_STRING DeviceId;
421 UNICODE_STRING HardwareIds;
422 UNICODE_STRING CompatibleIds;
423 NTSTATUS Status;
424
425 RtlZeroMemory(Buffer, sizeof(Buffer));
426
427 /* Reset UART */
428 CHECKPOINT;
429 Mcr = 0; /* MCR: DTR/RTS/OUT2 off */
430 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_MODEM_CONTROL,
431 &Mcr, sizeof(Mcr), NULL, NULL);
432 if (!NT_SUCCESS(Status)) return Status;
433
434 /* Set communications parameters */
435 CHECKPOINT;
436 /* DLAB off */
437 Fcr = 0;
438 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_FIFO_CONTROL,
439 &Fcr, sizeof(Fcr), NULL, NULL);
440 if (!NT_SUCCESS(Status)) return Status;
441 /* Set serial port speed */
442 BaudRate = SERIAL_BAUD_1200;
443 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_BAUD_RATE,
444 &BaudRate, sizeof(BaudRate), NULL, NULL);
445 if (!NT_SUCCESS(Status)) return Status;
446 /* Set LCR */
447 LCR.WordLength = 7;
448 LCR.Parity = NO_PARITY;
449 LCR.StopBits = STOP_BITS_2;
450 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_LINE_CONTROL,
451 &LCR, sizeof(LCR), NULL, NULL);
452 if (!NT_SUCCESS(Status)) return Status;
453
454 /* Flush receive buffer */
455 CHECKPOINT;
456 Command = SERIAL_PURGE_RXCLEAR;
457 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_MODEM_CONTROL,
458 &Command, sizeof(Command), NULL, NULL);
459 if (!NT_SUCCESS(Status)) return Status;
460 /* Wait 100 ms */
461 SerenumWait(100);
462
463 /* Enable DTR/RTS */
464 CHECKPOINT;
465 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_DTR,
466 NULL, 0, NULL, NULL);
467 if (!NT_SUCCESS(Status)) return Status;
468 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_RTS,
469 NULL, 0, NULL, NULL);
470 if (!NT_SUCCESS(Status)) return Status;
471
472 /* Set timeout to 500 microseconds */
473 CHECKPOINT;
474 Timeouts.ReadIntervalTimeout = 100;
475 Timeouts.ReadTotalTimeoutMultiplier = 0;
476 Timeouts.ReadTotalTimeoutConstant = 500;
477 Timeouts.WriteTotalTimeoutMultiplier = Timeouts.WriteTotalTimeoutConstant = 0;
478 Status = SerenumDeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_TIMEOUTS,
479 &Timeouts, sizeof(Timeouts), NULL, NULL);
480 if (!NT_SUCCESS(Status)) return Status;
481
482 /* Fill the read buffer */
483 CHECKPOINT;
484 Status = ReadBytes(LowerDevice, Buffer, sizeof(Buffer)/sizeof(Buffer[0]), &Count);
485 if (!NT_SUCCESS(Status)) return Status;
486
487 for (i = 0; i < Count; i++)
488 {
489 if (Buffer[i] == 'B')
490 {
491 /* Sign for Microsoft Ballpoint */
492 /* Hardware id: *PNP0F09
493 * Compatible id: *PNP0F0F, SERIAL_MOUSE
494 */
495 RtlInitUnicodeString(&DeviceDescription, L"Microsoft Ballpoint device");
496 RtlInitUnicodeString(&DeviceId, L"*PNP0F09");
497 SerenumInitMultiSzString(&HardwareIds, "*PNP0F09", NULL);
498 SerenumInitMultiSzString(&CompatibleIds, "*PNP0F0F", "SERIAL_MOUSE", NULL);
499 Status = ReportDetectedDevice(DeviceObject,
500 &DeviceDescription, &DeviceId, &HardwareIds, &CompatibleIds);
501 RtlFreeUnicodeString(&HardwareIds);
502 RtlFreeUnicodeString(&CompatibleIds);
503 return Status;
504 }
505 else if (Buffer[i] == 'M')
506 {
507 /* Sign for Microsoft Mouse protocol followed by button specifier */
508 if (i == sizeof(Buffer) - 1)
509 {
510 /* Overflow Error */
511 return STATUS_DEVICE_NOT_CONNECTED;
512 }
513 switch (Buffer[i + 1])
514 {
515 case '3':
516 /* Hardware id: *PNP0F08
517 * Compatible id: SERIAL_MOUSE
518 */
519 RtlInitUnicodeString(&DeviceDescription, L"Microsoft Mouse with 3-buttons");
520 RtlInitUnicodeString(&DeviceId, L"*PNP0F08");
521 SerenumInitMultiSzString(&HardwareIds, "*PNP0F08", NULL);
522 SerenumInitMultiSzString(&CompatibleIds, "SERIAL_MOUSE", NULL);
523 default:
524 /* Hardware id: *PNP0F01
525 * Compatible id: SERIAL_MOUSE
526 */
527 RtlInitUnicodeString(&DeviceDescription, L"Microsoft Mouse with 2-buttons or Microsoft Wheel Mouse");
528 RtlInitUnicodeString(&DeviceId, L"*PNP0F01");
529 SerenumInitMultiSzString(&HardwareIds, "*PNP0F01", NULL);
530 SerenumInitMultiSzString(&CompatibleIds, "SERIAL_MOUSE", NULL);
531 }
532 Status = ReportDetectedDevice(DeviceObject,
533 &DeviceDescription, &DeviceId, &HardwareIds, &CompatibleIds);
534 RtlFreeUnicodeString(&HardwareIds);
535 RtlFreeUnicodeString(&CompatibleIds);
536 return Status;
537 }
538 }
539
540 return STATUS_DEVICE_NOT_CONNECTED;
541 }