xref: /reactos/drivers/bus/isapnp/pdo.c (revision 3e1f4074)
1 /*
2  * PROJECT:         ReactOS ISA PnP Bus driver
3  * LICENSE:         GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:         PDO-specific code
5  * COPYRIGHT:       Copyright 2010 Cameron Gutman <cameron.gutman@reactos.org>
6  *                  Copyright 2020 Hervé Poussineau <hpoussin@reactos.org>
7  *                  Copyright 2021 Dmitry Borisov <di.sean@protonmail.com>
8  */
9 
10 #include "isapnp.h"
11 
12 #define NDEBUG
13 #include <debug.h>
14 
15 static
16 CODE_SEG("PAGE")
17 NTSTATUS
18 IsaPdoQueryDeviceRelations(
19     _In_ PISAPNP_PDO_EXTENSION PdoExt,
20     _Inout_ PIRP Irp,
21     _In_ PIO_STACK_LOCATION IrpSp)
22 {
23     PDEVICE_RELATIONS DeviceRelations;
24 
25     PAGED_CODE();
26 
27     if (IrpSp->Parameters.QueryDeviceRelations.Type == RemovalRelations &&
28         PdoExt->Common.Signature == IsaPnpReadDataPort)
29     {
30         return IsaPnpFillDeviceRelations(PdoExt->FdoExt, Irp, FALSE);
31     }
32 
33     if (IrpSp->Parameters.QueryDeviceRelations.Type != TargetDeviceRelation)
34         return Irp->IoStatus.Status;
35 
36     DeviceRelations = ExAllocatePoolWithTag(PagedPool, sizeof(*DeviceRelations), TAG_ISAPNP);
37     if (!DeviceRelations)
38         return STATUS_NO_MEMORY;
39 
40     DeviceRelations->Count = 1;
41     DeviceRelations->Objects[0] = PdoExt->Common.Self;
42     ObReferenceObject(PdoExt->Common.Self);
43 
44     Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations;
45     return STATUS_SUCCESS;
46 }
47 
48 static
49 CODE_SEG("PAGE")
50 NTSTATUS
51 IsaPdoQueryCapabilities(
52     _In_ PISAPNP_PDO_EXTENSION PdoExt,
53     _Inout_ PIRP Irp,
54     _In_ PIO_STACK_LOCATION IrpSp)
55 {
56     PDEVICE_CAPABILITIES DeviceCapabilities;
57     ULONG i;
58 
59     UNREFERENCED_PARAMETER(Irp);
60 
61     PAGED_CODE();
62 
63     DeviceCapabilities = IrpSp->Parameters.DeviceCapabilities.Capabilities;
64     if (DeviceCapabilities->Version != 1)
65         return STATUS_REVISION_MISMATCH;
66 
67     DeviceCapabilities->LockSupported =
68     DeviceCapabilities->EjectSupported =
69     DeviceCapabilities->Removable =
70     DeviceCapabilities->DockDevice = FALSE;
71 
72     DeviceCapabilities->UniqueID = TRUE;
73 
74     if (PdoExt->Common.Signature == IsaPnpReadDataPort)
75     {
76         DeviceCapabilities->RawDeviceOK = TRUE;
77         DeviceCapabilities->SilentInstall = TRUE;
78     }
79 
80     for (i = 0; i < POWER_SYSTEM_MAXIMUM; i++)
81         DeviceCapabilities->DeviceState[i] = PowerDeviceD3;
82     DeviceCapabilities->DeviceState[PowerSystemWorking] = PowerDeviceD0;
83 
84     return STATUS_SUCCESS;
85 }
86 
87 static
88 CODE_SEG("PAGE")
89 NTSTATUS
90 IsaPdoQueryPnpDeviceState(
91     _In_ PISAPNP_PDO_EXTENSION PdoExt,
92     _Inout_ PIRP Irp)
93 {
94     PAGED_CODE();
95 
96     if (PdoExt->Flags & ISAPNP_READ_PORT_NEED_REBALANCE)
97     {
98         Irp->IoStatus.Information |= PNP_DEVICE_NOT_DISABLEABLE |
99                                      PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED |
100                                      PNP_DEVICE_FAILED;
101         return STATUS_SUCCESS;
102     }
103     else if (PdoExt->SpecialFiles > 0)
104     {
105         Irp->IoStatus.Information |= PNP_DEVICE_NOT_DISABLEABLE;
106         return STATUS_SUCCESS;
107     }
108 
109     return Irp->IoStatus.Status;
110 }
111 
112 static
113 CODE_SEG("PAGE")
114 NTSTATUS
115 IsaPdoQueryId(
116     _In_ PISAPNP_PDO_EXTENSION PdoExt,
117     _Inout_ PIRP Irp,
118     _In_ PIO_STACK_LOCATION IrpSp)
119 {
120     PISAPNP_LOGICAL_DEVICE LogDev = PdoExt->IsaPnpDevice;
121     NTSTATUS Status;
122     PWCHAR Buffer, End, IdStart;
123     size_t CharCount, Remaining;
124 
125     PAGED_CODE();
126 
127     switch (IrpSp->Parameters.QueryId.IdType)
128     {
129         case BusQueryDeviceID:
130         {
131             CharCount = sizeof("ISAPNP\\XXXFFFF");
132 
133             if (LogDev->Flags & ISAPNP_HAS_MULTIPLE_LOGDEVS)
134             {
135                 CharCount += sizeof("_DEV1234") - sizeof(ANSI_NULL);
136             }
137 
138             Buffer = ExAllocatePoolWithTag(PagedPool,
139                                            CharCount * sizeof(WCHAR),
140                                            TAG_ISAPNP);
141             if (!Buffer)
142                 return STATUS_INSUFFICIENT_RESOURCES;
143 
144             Status = RtlStringCchPrintfExW(Buffer,
145                                            CharCount,
146                                            &End,
147                                            &Remaining,
148                                            0,
149                                            L"ISAPNP\\%.3S%04X",
150                                            LogDev->VendorId,
151                                            LogDev->ProdId);
152             if (!NT_VERIFY(NT_SUCCESS(Status)))
153                 goto Failure;
154 
155             if (LogDev->Flags & ISAPNP_HAS_MULTIPLE_LOGDEVS)
156             {
157                 Status = RtlStringCchPrintfExW(End,
158                                                Remaining,
159                                                NULL,
160                                                NULL,
161                                                0,
162                                                L"_DEV%04X",
163                                                LogDev->LDN);
164                 if (!NT_VERIFY(NT_SUCCESS(Status)))
165                     goto Failure;
166             }
167 
168             DPRINT("Device ID: '%S'\n", Buffer);
169             break;
170         }
171 
172         case BusQueryHardwareIDs:
173         {
174             CharCount = sizeof("ISAPNP\\XXXFFFF") +
175                         sizeof("*PNPxxxx") +
176                         sizeof(ANSI_NULL); /* multi-string */
177 
178             if (LogDev->Flags & ISAPNP_HAS_MULTIPLE_LOGDEVS)
179             {
180                 CharCount += sizeof("_DEV1234") - sizeof(ANSI_NULL);
181             }
182 
183             Buffer = ExAllocatePoolWithTag(PagedPool,
184                                            CharCount * sizeof(WCHAR),
185                                            TAG_ISAPNP);
186             if (!Buffer)
187                 return STATUS_INSUFFICIENT_RESOURCES;
188 
189             DPRINT("Hardware IDs:\n");
190 
191             /* 1 */
192             Status = RtlStringCchPrintfExW(Buffer,
193                                            CharCount,
194                                            &End,
195                                            &Remaining,
196                                            0,
197                                            L"ISAPNP\\%.3S%04X",
198                                            LogDev->VendorId,
199                                            LogDev->ProdId);
200             if (!NT_VERIFY(NT_SUCCESS(Status)))
201                 goto Failure;
202 
203             if (LogDev->Flags & ISAPNP_HAS_MULTIPLE_LOGDEVS)
204             {
205                 Status = RtlStringCchPrintfExW(End,
206                                                Remaining,
207                                                &End,
208                                                &Remaining,
209                                                0,
210                                                L"_DEV%04X",
211                                                LogDev->LDN);
212                 if (!NT_VERIFY(NT_SUCCESS(Status)))
213                     goto Failure;
214             }
215 
216             DPRINT("  '%S'\n", Buffer);
217 
218             ++End;
219             --Remaining;
220 
221             /* 2 */
222             IdStart = End;
223             Status = RtlStringCchPrintfExW(End,
224                                            Remaining,
225                                            &End,
226                                            &Remaining,
227                                            0,
228                                            L"*%.3S%04X",
229                                            LogDev->LogVendorId,
230                                            LogDev->LogProdId);
231             if (!NT_VERIFY(NT_SUCCESS(Status)))
232                 goto Failure;
233 
234             DPRINT("  '%S'\n", IdStart);
235 
236             *++End = UNICODE_NULL;
237             --Remaining;
238 
239             break;
240         }
241 
242         case BusQueryCompatibleIDs:
243         {
244             PLIST_ENTRY Entry;
245 
246             for (Entry = LogDev->CompatibleIdList.Flink, CharCount = 0;
247                  Entry != &LogDev->CompatibleIdList;
248                  Entry = Entry->Flink)
249             {
250                 CharCount += sizeof("*PNPxxxx");
251             }
252             CharCount += sizeof(ANSI_NULL); /* multi-string */
253 
254             if (CharCount == sizeof(ANSI_NULL))
255                 return Irp->IoStatus.Status;
256 
257             Buffer = ExAllocatePoolWithTag(PagedPool,
258                                            CharCount * sizeof(WCHAR),
259                                            TAG_ISAPNP);
260             if (!Buffer)
261                 return STATUS_INSUFFICIENT_RESOURCES;
262 
263             DPRINT("Compatible IDs:\n");
264 
265             for (Entry = LogDev->CompatibleIdList.Flink, End = Buffer, Remaining = CharCount;
266                  Entry != &LogDev->CompatibleIdList;
267                  Entry = Entry->Flink)
268             {
269                 PISAPNP_COMPATIBLE_ID_ENTRY CompatibleId =
270                     CONTAINING_RECORD(Entry, ISAPNP_COMPATIBLE_ID_ENTRY, IdLink);
271 
272                 IdStart = End;
273                 Status = RtlStringCchPrintfExW(End,
274                                                Remaining,
275                                                &End,
276                                                &Remaining,
277                                                0,
278                                                L"*%.3S%04X",
279                                                CompatibleId->VendorId,
280                                                CompatibleId->ProdId);
281                 if (!NT_VERIFY(NT_SUCCESS(Status)))
282                     goto Failure;
283 
284                 DPRINT("  '%S'\n", IdStart);
285 
286                 ++End;
287                 --Remaining;
288             }
289 
290             *End = UNICODE_NULL;
291 
292             break;
293         }
294 
295         case BusQueryInstanceID:
296         {
297             CharCount = sizeof(LogDev->SerialNumber) * 2 + sizeof(ANSI_NULL);
298 
299             Buffer = ExAllocatePoolWithTag(PagedPool,
300                                            CharCount * sizeof(WCHAR),
301                                            TAG_ISAPNP);
302             if (!Buffer)
303                 return STATUS_INSUFFICIENT_RESOURCES;
304 
305             Status = RtlStringCchPrintfExW(Buffer,
306                                            CharCount,
307                                            NULL,
308                                            NULL,
309                                            0,
310                                            L"%X",
311                                            LogDev->SerialNumber);
312             if (!NT_VERIFY(NT_SUCCESS(Status)))
313                 goto Failure;
314 
315             DPRINT("Instance ID: '%S'\n", Buffer);
316             break;
317         }
318 
319         default:
320             return Irp->IoStatus.Status;
321     }
322 
323     Irp->IoStatus.Information = (ULONG_PTR)Buffer;
324     return STATUS_SUCCESS;
325 
326 Failure:
327     ExFreePoolWithTag(Buffer, TAG_ISAPNP);
328 
329     return Status;
330 }
331 
332 static
333 CODE_SEG("PAGE")
334 NTSTATUS
335 IsaReadPortQueryId(
336     _Inout_ PIRP Irp,
337     _In_ PIO_STACK_LOCATION IrpSp)
338 {
339     PWCHAR Buffer;
340     static const WCHAR ReadPortId[] = L"ISAPNP\\ReadDataPort";
341 
342     PAGED_CODE();
343 
344     switch (IrpSp->Parameters.QueryId.IdType)
345     {
346         case BusQueryDeviceID:
347         {
348             Buffer = ExAllocatePoolWithTag(PagedPool, sizeof(ReadPortId), TAG_ISAPNP);
349             if (!Buffer)
350                 return STATUS_INSUFFICIENT_RESOURCES;
351 
352             RtlCopyMemory(Buffer, ReadPortId, sizeof(ReadPortId));
353 
354             DPRINT("Device ID: '%S'\n", Buffer);
355             break;
356         }
357 
358         case BusQueryHardwareIDs:
359         {
360             Buffer = ExAllocatePoolWithTag(PagedPool,
361                                            sizeof(ReadPortId) + sizeof(UNICODE_NULL),
362                                            TAG_ISAPNP);
363             if (!Buffer)
364                 return STATUS_INSUFFICIENT_RESOURCES;
365 
366             RtlCopyMemory(Buffer, ReadPortId, sizeof(ReadPortId));
367 
368             Buffer[sizeof(ReadPortId) / sizeof(WCHAR)] = UNICODE_NULL; /* multi-string */
369 
370             DPRINT("Hardware ID: '%S'\n", Buffer);
371             break;
372         }
373 
374         case BusQueryCompatibleIDs:
375         {
376             /* Empty multi-string */
377             Buffer = ExAllocatePoolZero(PagedPool, sizeof(UNICODE_NULL) * 2, TAG_ISAPNP);
378             if (!Buffer)
379                 return STATUS_INSUFFICIENT_RESOURCES;
380 
381             DPRINT("Compatible ID: '%S'\n", Buffer);
382             break;
383         }
384 
385         case BusQueryInstanceID:
386         {
387             /* Even if there are multiple ISA buses, the driver has only one Read Port */
388             static const WCHAR InstanceId[] = L"0";
389 
390             Buffer = ExAllocatePoolWithTag(PagedPool, sizeof(InstanceId), TAG_ISAPNP);
391             if (!Buffer)
392                 return STATUS_INSUFFICIENT_RESOURCES;
393 
394             RtlCopyMemory(Buffer, InstanceId, sizeof(InstanceId));
395 
396             DPRINT("Instance ID: '%S'\n", Buffer);
397             break;
398         }
399 
400         default:
401             return Irp->IoStatus.Status;
402     }
403 
404     Irp->IoStatus.Information = (ULONG_PTR)Buffer;
405     return STATUS_SUCCESS;
406 }
407 
408 static
409 CODE_SEG("PAGE")
410 NTSTATUS
411 IsaPdoQueryDeviceText(
412     _In_ PISAPNP_PDO_EXTENSION PdoExt,
413     _Inout_ PIRP Irp,
414     _In_ PIO_STACK_LOCATION IrpSp)
415 {
416     NTSTATUS Status;
417     PWCHAR Buffer;
418     size_t CharCount;
419 
420     PAGED_CODE();
421 
422     switch (IrpSp->Parameters.QueryDeviceText.DeviceTextType)
423     {
424         case DeviceTextDescription:
425         {
426             if (!PdoExt->IsaPnpDevice->FriendlyName)
427                 return Irp->IoStatus.Status;
428 
429             CharCount = strlen(PdoExt->IsaPnpDevice->FriendlyName) +
430                         sizeof(ANSI_NULL);
431 
432             if (CharCount == sizeof(ANSI_NULL))
433                 return Irp->IoStatus.Status;
434 
435             Buffer = ExAllocatePoolWithTag(PagedPool,
436                                            CharCount * sizeof(WCHAR),
437                                            TAG_ISAPNP);
438             if (!Buffer)
439                 return STATUS_INSUFFICIENT_RESOURCES;
440 
441             Status = RtlStringCchPrintfExW(Buffer,
442                                            CharCount,
443                                            NULL,
444                                            NULL,
445                                            0,
446                                            L"%hs",
447                                            PdoExt->IsaPnpDevice->FriendlyName);
448             if (!NT_VERIFY(NT_SUCCESS(Status)))
449             {
450                 ExFreePoolWithTag(Buffer, TAG_ISAPNP);
451                 return Status;
452             }
453 
454             DPRINT("TextDescription: '%S'\n", Buffer);
455             break;
456         }
457 
458         default:
459             return Irp->IoStatus.Status;
460     }
461 
462     Irp->IoStatus.Information = (ULONG_PTR)Buffer;
463     return STATUS_SUCCESS;
464 }
465 
466 static
467 CODE_SEG("PAGE")
468 NTSTATUS
469 IsaPdoQueryResources(
470     _In_ PISAPNP_PDO_EXTENSION PdoExt,
471     _Inout_ PIRP Irp,
472     _In_ PIO_STACK_LOCATION IrpSp)
473 {
474     ULONG ListSize;
475     PCM_RESOURCE_LIST ResourceList;
476 
477     UNREFERENCED_PARAMETER(IrpSp);
478 
479     PAGED_CODE();
480 
481     if (PdoExt->Common.Signature == IsaPnpLogicalDevice &&
482         !(PdoExt->IsaPnpDevice->Flags & ISAPNP_HAS_RESOURCES))
483     {
484         Irp->IoStatus.Information = 0;
485         return STATUS_SUCCESS;
486     }
487 
488     if (!PdoExt->ResourceList)
489         return Irp->IoStatus.Status;
490 
491     ListSize = PdoExt->ResourceListSize;
492     ResourceList = ExAllocatePoolWithTag(PagedPool, ListSize, TAG_ISAPNP);
493     if (!ResourceList)
494         return STATUS_NO_MEMORY;
495 
496     RtlCopyMemory(ResourceList, PdoExt->ResourceList, ListSize);
497     Irp->IoStatus.Information = (ULONG_PTR)ResourceList;
498     return STATUS_SUCCESS;
499 }
500 
501 static
502 CODE_SEG("PAGE")
503 NTSTATUS
504 IsaPdoQueryResourceRequirements(
505     _In_ PISAPNP_PDO_EXTENSION PdoExt,
506     _Inout_ PIRP Irp,
507     _In_ PIO_STACK_LOCATION IrpSp)
508 {
509     ULONG ListSize;
510     PIO_RESOURCE_REQUIREMENTS_LIST RequirementsList;
511 
512     UNREFERENCED_PARAMETER(IrpSp);
513 
514     PAGED_CODE();
515 
516     if (!PdoExt->RequirementsList)
517         return Irp->IoStatus.Status;
518 
519     ListSize = PdoExt->RequirementsList->ListSize;
520     RequirementsList = ExAllocatePoolWithTag(PagedPool, ListSize, TAG_ISAPNP);
521     if (!RequirementsList)
522         return STATUS_NO_MEMORY;
523 
524     RtlCopyMemory(RequirementsList, PdoExt->RequirementsList, ListSize);
525     Irp->IoStatus.Information = (ULONG_PTR)RequirementsList;
526     return STATUS_SUCCESS;
527 }
528 
529 #define IS_READ_PORT(_d) ((_d)->Type == CmResourceTypePort && (_d)->u.Port.Length > 1)
530 
531 static
532 CODE_SEG("PAGE")
533 NTSTATUS
534 IsaPdoStartReadPort(
535     _In_ PISAPNP_PDO_EXTENSION PdoExt,
536     _In_ PCM_RESOURCE_LIST ResourceList)
537 {
538     PISAPNP_FDO_EXTENSION FdoExt = PdoExt->FdoExt;
539     NTSTATUS Status;
540     ULONG i;
541 
542     PAGED_CODE();
543 
544     if (!ResourceList)
545     {
546         DPRINT1("No resource list\n");
547         return STATUS_INSUFFICIENT_RESOURCES;
548     }
549 
550     if (ResourceList->List[0].PartialResourceList.Version != 1 ||
551         ResourceList->List[0].PartialResourceList.Revision != 1)
552     {
553         DPRINT1("Bad resource list version (%u.%u)\n",
554                 ResourceList->List[0].PartialResourceList.Version,
555                 ResourceList->List[0].PartialResourceList.Revision);
556         return STATUS_REVISION_MISMATCH;
557     }
558 
559 #if 0
560     /* Try various Read Ports from the list */
561     if (ResourceList->List[0].PartialResourceList.Count > 3)
562     {
563         ULONG SelectedPort = 0;
564 
565         for (i = 0; i < ResourceList->List[0].PartialResourceList.Count; i++)
566         {
567             PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor =
568                 &ResourceList->List[0].PartialResourceList.PartialDescriptors[i];
569 
570             if (IS_READ_PORT(PartialDescriptor))
571             {
572                 PUCHAR ReadDataPort = ULongToPtr(PartialDescriptor->u.Port.Start.u.LowPart + 3);
573                 ULONG Cards;
574 
575                 /*
576                  * Remember the first Read Port in the resource list.
577                  * It will be selected by default even if no card has been detected.
578                  */
579                 if (!SelectedPort)
580                     SelectedPort = PartialDescriptor->u.Port.Start.u.LowPart;
581 
582                 Cards = IsaHwTryReadDataPort(ReadDataPort);
583                 IsaHwWaitForKey();
584 
585                 /* We detected some ISAPNP cards */
586                 if (Cards > 0)
587                 {
588                     SelectedPort = PartialDescriptor->u.Port.Start.u.LowPart;
589                     break;
590                 }
591             }
592         }
593 
594         ASSERT(SelectedPort != 0);
595 
596         if (PdoExt->RequirementsList)
597         {
598             ExFreePoolWithTag(PdoExt->RequirementsList, TAG_ISAPNP);
599             PdoExt->RequirementsList = NULL;
600         }
601 
602         /* Discard the Read Ports at conflicting locations */
603         Status = IsaPnpCreateReadPortDORequirements(PdoExt, SelectedPort);
604         if (!NT_SUCCESS(Status))
605             return Status;
606 
607         PdoExt->Flags |= ISAPNP_READ_PORT_NEED_REBALANCE;
608 
609         IoInvalidateDeviceState(PdoExt->Common.Self);
610 
611         return STATUS_SUCCESS;
612     }
613     /* Set the Read Port */
614     else if (ResourceList->List[0].PartialResourceList.Count == 3)
615 #else
616     if (ResourceList->List[0].PartialResourceList.Count > 3) /* Temporary HACK */
617 #endif
618     {
619         PdoExt->Flags &= ~ISAPNP_READ_PORT_NEED_REBALANCE;
620 
621         for (i = 0; i < ResourceList->List[0].PartialResourceList.Count; i++)
622         {
623             PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor =
624                 &ResourceList->List[0].PartialResourceList.PartialDescriptors[i];
625 
626             if (IS_READ_PORT(PartialDescriptor))
627             {
628                 PUCHAR ReadDataPort = ULongToPtr(PartialDescriptor->u.Port.Start.u.LowPart + 3);
629 
630                 /* Run the isolation protocol */
631                 FdoExt->Cards = IsaHwTryReadDataPort(ReadDataPort);
632 
633                 if (FdoExt->Cards > 0)
634                 {
635                     FdoExt->ReadDataPort = ReadDataPort;
636 
637                     IsaPnpAcquireDeviceDataLock(FdoExt);
638 
639                     /* Card identification */
640                     Status = IsaHwFillDeviceList(FdoExt);
641                     IsaHwWaitForKey();
642 
643                     IsaPnpReleaseDeviceDataLock(FdoExt);
644 
645                     PdoExt->Flags |= ISAPNP_READ_PORT_ALLOW_FDO_SCAN |
646                                      ISAPNP_SCANNED_BY_READ_PORT;
647 
648                     IoInvalidateDeviceRelations(FdoExt->Pdo, BusRelations);
649                     IoInvalidateDeviceRelations(FdoExt->ReadPortPdo, RemovalRelations);
650 
651                     return Status;
652                 }
653                 else
654                 {
655                     IsaHwWaitForKey();
656 #if 0 /* See the 'if 0' above */
657                     break;
658 #endif
659                 }
660             }
661         }
662     }
663     else
664     {
665         return STATUS_DEVICE_CONFIGURATION_ERROR;
666     }
667 
668     /* Mark Read Port as started, even if no card has been detected */
669     return STATUS_SUCCESS;
670 }
671 
672 static
673 CODE_SEG("PAGE")
674 NTSTATUS
675 IsaPdoFilterResourceRequirements(
676     _In_ PISAPNP_PDO_EXTENSION PdoExt,
677     _Inout_ PIRP Irp,
678     _In_ PIO_STACK_LOCATION IrpSp)
679 {
680     PAGED_CODE();
681 
682     /* TODO: Handle */
683     UNREFERENCED_PARAMETER(PdoExt);
684     UNREFERENCED_PARAMETER(IrpSp);
685     return Irp->IoStatus.Status;
686 }
687 
688 static
689 CODE_SEG("PAGE")
690 NTSTATUS
691 IsaPdoQueryBusInformation(
692     _In_ PISAPNP_PDO_EXTENSION PdoExt,
693     _Inout_ PIRP Irp)
694 {
695     PPNP_BUS_INFORMATION BusInformation;
696 
697     PAGED_CODE();
698 
699     BusInformation = ExAllocatePoolWithTag(PagedPool,
700                                            sizeof(PNP_BUS_INFORMATION),
701                                            TAG_ISAPNP);
702     if (!BusInformation)
703         return STATUS_INSUFFICIENT_RESOURCES;
704 
705     BusInformation->BusTypeGuid = GUID_BUS_TYPE_ISAPNP;
706     BusInformation->LegacyBusType = Isa;
707     BusInformation->BusNumber = PdoExt->FdoExt->BusNumber;
708 
709     Irp->IoStatus.Information = (ULONG_PTR)BusInformation;
710     return STATUS_SUCCESS;
711 }
712 
713 static
714 CODE_SEG("PAGE")
715 NTSTATUS
716 IsaPdoQueryDeviceUsageNotification(
717     _In_ PISAPNP_PDO_EXTENSION PdoExt,
718     _Inout_ PIRP Irp,
719     _In_ PIO_STACK_LOCATION IrpSp)
720 {
721     BOOLEAN InPath = IrpSp->Parameters.UsageNotification.InPath;
722 
723     PAGED_CODE();
724 
725     switch (IrpSp->Parameters.UsageNotification.Type)
726     {
727         case DeviceUsageTypePaging:
728         case DeviceUsageTypeHibernation:
729         case DeviceUsageTypeDumpFile:
730             IoAdjustPagingPathCount(&PdoExt->SpecialFiles, InPath);
731             IoInvalidateDeviceState(PdoExt->Common.Self);
732             break;
733 
734         default:
735             return Irp->IoStatus.Status;
736     }
737 
738     /* Do not send it to FDO for compatibility */
739     return STATUS_SUCCESS;
740 }
741 
742 static
743 CODE_SEG("PAGE")
744 NTSTATUS
745 IsaPdoRemoveDevice(
746     _In_ PISAPNP_PDO_EXTENSION PdoExt,
747     _In_ BOOLEAN FinalRemove)
748 {
749     PISAPNP_FDO_EXTENSION FdoExt = PdoExt->FdoExt;
750 
751     PAGED_CODE();
752 
753     /* Deactivate the device if previously activated */
754     if (PdoExt->Common.State == dsStarted)
755     {
756         IsaHwWakeDevice(PdoExt->IsaPnpDevice);
757         IsaHwDeactivateDevice(PdoExt->IsaPnpDevice);
758 
759         IsaHwWaitForKey();
760 
761         PdoExt->Common.State = dsStopped;
762     }
763 
764     if (FinalRemove && !(PdoExt->Flags & ISAPNP_ENUMERATED))
765     {
766         IsaPnpAcquireDeviceDataLock(FdoExt);
767 
768         RemoveEntryList(&PdoExt->IsaPnpDevice->DeviceLink);
769         --FdoExt->DeviceCount;
770 
771         IsaPnpReleaseDeviceDataLock(FdoExt);
772 
773         IsaPnpRemoveLogicalDeviceDO(PdoExt->Common.Self);
774     }
775 
776     return STATUS_SUCCESS;
777 }
778 
779 static
780 CODE_SEG("PAGE")
781 NTSTATUS
782 IsaReadPortRemoveDevice(
783     _In_ PISAPNP_PDO_EXTENSION PdoExt,
784     _In_ BOOLEAN FinalRemove)
785 {
786     PISAPNP_FDO_EXTENSION FdoExt = PdoExt->FdoExt;
787     PLIST_ENTRY Entry;
788 
789     PAGED_CODE();
790 
791     IsaPnpAcquireDeviceDataLock(FdoExt);
792 
793     /* Logical devices will receive a remove request afterwards */
794     for (Entry = FdoExt->DeviceListHead.Flink;
795          Entry != &FdoExt->DeviceListHead;
796          Entry = Entry->Flink)
797     {
798         PISAPNP_LOGICAL_DEVICE LogDevice = CONTAINING_RECORD(Entry,
799                                                              ISAPNP_LOGICAL_DEVICE,
800                                                              DeviceLink);
801 
802         LogDevice->Flags &= ~ISAPNP_PRESENT;
803     }
804 
805     IsaPnpReleaseDeviceDataLock(FdoExt);
806 
807     PdoExt->Flags &= ~ISAPNP_READ_PORT_ALLOW_FDO_SCAN;
808     IoInvalidateDeviceRelations(FdoExt->Pdo, BusRelations);
809 
810     if (FinalRemove && !(PdoExt->Flags & ISAPNP_ENUMERATED))
811     {
812         IsaPnpRemoveReadPortDO(PdoExt->Common.Self);
813     }
814 
815     return STATUS_SUCCESS;
816 }
817 
818 CODE_SEG("PAGE")
819 VOID
820 IsaPnpRemoveLogicalDeviceDO(
821     _In_ PDEVICE_OBJECT Pdo)
822 {
823     PISAPNP_PDO_EXTENSION PdoExt = Pdo->DeviceExtension;
824     PISAPNP_LOGICAL_DEVICE LogDev = PdoExt->IsaPnpDevice;
825     PLIST_ENTRY Entry;
826 
827     PAGED_CODE();
828     ASSERT(LogDev);
829 
830     DPRINT("Removing CSN %u, LDN %u\n", LogDev->CSN, LogDev->LDN);
831 
832     if (PdoExt->RequirementsList)
833         ExFreePoolWithTag(PdoExt->RequirementsList, TAG_ISAPNP);
834 
835     if (PdoExt->ResourceList)
836         ExFreePoolWithTag(PdoExt->ResourceList, TAG_ISAPNP);
837 
838     if (LogDev->FriendlyName)
839         ExFreePoolWithTag(LogDev->FriendlyName, TAG_ISAPNP);
840 
841     if (LogDev->Alternatives)
842         ExFreePoolWithTag(LogDev->Alternatives, TAG_ISAPNP);
843 
844     Entry = LogDev->CompatibleIdList.Flink;
845     while (Entry != &LogDev->CompatibleIdList)
846     {
847         PISAPNP_COMPATIBLE_ID_ENTRY CompatibleId =
848             CONTAINING_RECORD(Entry, ISAPNP_COMPATIBLE_ID_ENTRY, IdLink);
849 
850         RemoveEntryList(&CompatibleId->IdLink);
851 
852         Entry = Entry->Flink;
853 
854         ExFreePoolWithTag(CompatibleId, TAG_ISAPNP);
855     }
856 
857     ExFreePoolWithTag(LogDev, TAG_ISAPNP);
858 
859     IoDeleteDevice(PdoExt->Common.Self);
860 }
861 
862 CODE_SEG("PAGE")
863 NTSTATUS
864 IsaPdoPnp(
865     _In_ PISAPNP_PDO_EXTENSION PdoExt,
866     _Inout_ PIRP Irp,
867     _In_ PIO_STACK_LOCATION IrpSp)
868 {
869     NTSTATUS Status = Irp->IoStatus.Status;
870 
871     PAGED_CODE();
872 
873     if (PdoExt->Common.Signature == IsaPnpLogicalDevice)
874     {
875         DPRINT("%s(%p, %p) CSN %u, LDN %u, Minor - %X\n",
876                __FUNCTION__,
877                PdoExt,
878                Irp,
879                PdoExt->IsaPnpDevice->CSN,
880                PdoExt->IsaPnpDevice->LDN,
881                IrpSp->MinorFunction);
882     }
883     else
884     {
885         DPRINT("%s(%p, %p) ReadPort, Minor - %X\n",
886                __FUNCTION__,
887                PdoExt,
888                Irp,
889                IrpSp->MinorFunction);
890     }
891 
892     switch (IrpSp->MinorFunction)
893     {
894         case IRP_MN_START_DEVICE:
895         {
896             if (PdoExt->Common.Signature == IsaPnpLogicalDevice)
897             {
898                 IsaHwWakeDevice(PdoExt->IsaPnpDevice);
899 
900                 Status = IsaHwConfigureDevice(PdoExt->FdoExt,
901                                               PdoExt->IsaPnpDevice,
902                                               IrpSp->Parameters.StartDevice.AllocatedResources);
903                 if (NT_SUCCESS(Status))
904                 {
905                     IsaHwActivateDevice(PdoExt->FdoExt, PdoExt->IsaPnpDevice);
906                 }
907                 else
908                 {
909                     DPRINT1("Failed to configure CSN %u, LDN %u with status 0x%08lx\n",
910                             PdoExt->IsaPnpDevice->CSN, PdoExt->IsaPnpDevice->LDN, Status);
911                 }
912 
913                 IsaHwWaitForKey();
914             }
915             else
916             {
917                 Status = IsaPdoStartReadPort(PdoExt,
918                                              IrpSp->Parameters.StartDevice.AllocatedResources);
919             }
920 
921             if (NT_SUCCESS(Status))
922                 PdoExt->Common.State = dsStarted;
923             break;
924         }
925 
926         case IRP_MN_STOP_DEVICE:
927         {
928             if (PdoExt->Common.Signature == IsaPnpLogicalDevice)
929             {
930                 IsaHwWakeDevice(PdoExt->IsaPnpDevice);
931                 IsaHwDeactivateDevice(PdoExt->IsaPnpDevice);
932 
933                 IsaHwWaitForKey();
934             }
935             else
936             {
937                 PdoExt->Flags &= ~ISAPNP_READ_PORT_ALLOW_FDO_SCAN;
938             }
939 
940             Status = STATUS_SUCCESS;
941 
942             if (NT_SUCCESS(Status))
943                 PdoExt->Common.State = dsStopped;
944             break;
945         }
946 
947         case IRP_MN_QUERY_STOP_DEVICE:
948         {
949             if (PdoExt->SpecialFiles > 0)
950                 Status = STATUS_DEVICE_BUSY;
951             else if (PdoExt->Flags & ISAPNP_READ_PORT_NEED_REBALANCE)
952                 Status = STATUS_RESOURCE_REQUIREMENTS_CHANGED;
953             else
954                 Status = STATUS_SUCCESS;
955 
956             break;
957         }
958 
959         case IRP_MN_QUERY_REMOVE_DEVICE:
960         {
961             if (PdoExt->SpecialFiles > 0)
962                 Status = STATUS_DEVICE_BUSY;
963             else
964                 Status = STATUS_SUCCESS;
965             break;
966         }
967 
968         case IRP_MN_QUERY_DEVICE_RELATIONS:
969             Status = IsaPdoQueryDeviceRelations(PdoExt, Irp, IrpSp);
970             break;
971 
972         case IRP_MN_QUERY_CAPABILITIES:
973             Status = IsaPdoQueryCapabilities(PdoExt, Irp, IrpSp);
974             break;
975 
976         case IRP_MN_SURPRISE_REMOVAL:
977             if (PdoExt->Common.Signature == IsaPnpLogicalDevice)
978                 Status = IsaPdoRemoveDevice(PdoExt, FALSE);
979             else
980                 Status = IsaReadPortRemoveDevice(PdoExt, FALSE);
981             break;
982 
983         case IRP_MN_REMOVE_DEVICE:
984             if (PdoExt->Common.Signature == IsaPnpLogicalDevice)
985                 Status = IsaPdoRemoveDevice(PdoExt, TRUE);
986             else
987                 Status = IsaReadPortRemoveDevice(PdoExt, TRUE);
988             break;
989 
990         case IRP_MN_QUERY_PNP_DEVICE_STATE:
991             Status = IsaPdoQueryPnpDeviceState(PdoExt, Irp);
992             break;
993 
994         case IRP_MN_QUERY_RESOURCES:
995             Status = IsaPdoQueryResources(PdoExt, Irp, IrpSp);
996             break;
997 
998         case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
999             Status = IsaPdoQueryResourceRequirements(PdoExt, Irp, IrpSp);
1000             break;
1001 
1002         case IRP_MN_QUERY_ID:
1003             if (PdoExt->Common.Signature == IsaPnpLogicalDevice)
1004                 Status = IsaPdoQueryId(PdoExt, Irp, IrpSp);
1005             else
1006                 Status = IsaReadPortQueryId(Irp, IrpSp);
1007             break;
1008 
1009         case IRP_MN_QUERY_DEVICE_TEXT:
1010             if (PdoExt->Common.Signature == IsaPnpLogicalDevice)
1011                 Status = IsaPdoQueryDeviceText(PdoExt, Irp, IrpSp);
1012             break;
1013 
1014         case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
1015             Status = IsaPdoFilterResourceRequirements(PdoExt, Irp, IrpSp);
1016             break;
1017 
1018         case IRP_MN_QUERY_BUS_INFORMATION:
1019             Status = IsaPdoQueryBusInformation(PdoExt, Irp);
1020             break;
1021 
1022         case IRP_MN_DEVICE_USAGE_NOTIFICATION:
1023             Status = IsaPdoQueryDeviceUsageNotification(PdoExt, Irp, IrpSp);
1024             break;
1025 
1026         case IRP_MN_CANCEL_REMOVE_DEVICE:
1027         case IRP_MN_CANCEL_STOP_DEVICE:
1028             Status = STATUS_SUCCESS;
1029             break;
1030 
1031         default:
1032             DPRINT("Unknown PnP code: %X\n", IrpSp->MinorFunction);
1033             break;
1034     }
1035 
1036     Irp->IoStatus.Status = Status;
1037     IoCompleteRequest(Irp, IO_NO_INCREMENT);
1038 
1039     return Status;
1040 }
1041