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