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