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