xref: /reactos/drivers/bus/isapnp/pdo.c (revision c948ea85)
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 == IsaPnpReadDataPort)
482     {
483         ResourceList = IsaPnpCreateReadPortDOResources();
484         if (!ResourceList)
485             return STATUS_NO_MEMORY;
486 
487         Irp->IoStatus.Information = (ULONG_PTR)ResourceList;
488         return STATUS_SUCCESS;
489     }
490 
491     if (!PdoExt->ResourceList)
492         return Irp->IoStatus.Status;
493 
494     ListSize = PdoExt->ResourceListSize;
495     ResourceList = ExAllocatePoolWithTag(PagedPool, ListSize, TAG_ISAPNP);
496     if (!ResourceList)
497         return STATUS_NO_MEMORY;
498 
499     RtlCopyMemory(ResourceList, PdoExt->ResourceList, ListSize);
500     Irp->IoStatus.Information = (ULONG_PTR)ResourceList;
501     return STATUS_SUCCESS;
502 }
503 
504 static
505 CODE_SEG("PAGE")
506 NTSTATUS
507 IsaPdoQueryResourceRequirements(
508     _In_ PISAPNP_PDO_EXTENSION PdoExt,
509     _Inout_ PIRP Irp,
510     _In_ PIO_STACK_LOCATION IrpSp)
511 {
512     ULONG ListSize;
513     PIO_RESOURCE_REQUIREMENTS_LIST RequirementsList;
514 
515     UNREFERENCED_PARAMETER(IrpSp);
516 
517     PAGED_CODE();
518 
519     if (PdoExt->Common.Signature == IsaPnpReadDataPort)
520     {
521         RequirementsList = IsaPnpCreateReadPortDORequirements(PdoExt->SelectedPort);
522         if (!RequirementsList)
523             return STATUS_NO_MEMORY;
524 
525         Irp->IoStatus.Information = (ULONG_PTR)RequirementsList;
526         return STATUS_SUCCESS;
527     }
528 
529     if (!PdoExt->RequirementsList)
530         return Irp->IoStatus.Status;
531 
532     ListSize = PdoExt->RequirementsList->ListSize;
533     RequirementsList = ExAllocatePoolWithTag(PagedPool, ListSize, TAG_ISAPNP);
534     if (!RequirementsList)
535         return STATUS_NO_MEMORY;
536 
537     RtlCopyMemory(RequirementsList, PdoExt->RequirementsList, ListSize);
538     Irp->IoStatus.Information = (ULONG_PTR)RequirementsList;
539     return STATUS_SUCCESS;
540 }
541 
542 #define IS_READ_PORT(_d) ((_d)->Type == CmResourceTypePort && (_d)->u.Port.Length > 1)
543 
544 static
545 CODE_SEG("PAGE")
546 NTSTATUS
547 IsaPdoStartReadPort(
548     _In_ PISAPNP_PDO_EXTENSION PdoExt,
549     _In_ PCM_RESOURCE_LIST ResourceList)
550 {
551     PISAPNP_FDO_EXTENSION FdoExt = PdoExt->FdoExt;
552     NTSTATUS Status;
553     ULONG i;
554 
555     PAGED_CODE();
556 
557     if (!ResourceList)
558     {
559         DPRINT1("No resource list\n");
560         return STATUS_INSUFFICIENT_RESOURCES;
561     }
562 
563     if (ResourceList->List[0].PartialResourceList.Version != 1 ||
564         ResourceList->List[0].PartialResourceList.Revision != 1)
565     {
566         DPRINT1("Bad resource list version (%u.%u)\n",
567                 ResourceList->List[0].PartialResourceList.Version,
568                 ResourceList->List[0].PartialResourceList.Revision);
569         return STATUS_REVISION_MISMATCH;
570     }
571 
572 #if 0
573     /* Try various Read Ports from the list */
574     if (ResourceList->List[0].PartialResourceList.Count > 3)
575     {
576         ULONG SelectedPort = 0;
577 
578         for (i = 0; i < ResourceList->List[0].PartialResourceList.Count; i++)
579         {
580             PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor =
581                 &ResourceList->List[0].PartialResourceList.PartialDescriptors[i];
582 
583             if (IS_READ_PORT(PartialDescriptor))
584             {
585                 PUCHAR ReadDataPort = ULongToPtr(PartialDescriptor->u.Port.Start.u.LowPart + 3);
586                 ULONG Cards;
587 
588                 /*
589                  * Remember the first Read Port in the resource list.
590                  * It will be selected by default even if no card has been detected.
591                  */
592                 if (!SelectedPort)
593                     SelectedPort = PartialDescriptor->u.Port.Start.u.LowPart;
594 
595                 Cards = IsaHwTryReadDataPort(ReadDataPort);
596                 IsaHwWaitForKey();
597 
598                 /* We detected some ISAPNP cards */
599                 if (Cards > 0)
600                 {
601                     SelectedPort = PartialDescriptor->u.Port.Start.u.LowPart;
602                     break;
603                 }
604             }
605         }
606 
607         ASSERT(SelectedPort != 0);
608 
609         /* Discard the Read Ports at conflicting locations */
610         PdoExt->SelectedPort = SelectedPort;
611         PdoExt->Flags |= ISAPNP_READ_PORT_NEED_REBALANCE;
612         IoInvalidateDeviceState(PdoExt->Common.Self);
613 
614         return STATUS_SUCCESS;
615     }
616     /* Set the Read Port */
617     else if (ResourceList->List[0].PartialResourceList.Count == 3)
618 #else
619     if (ResourceList->List[0].PartialResourceList.Count > 3) /* Temporary HACK */
620 #endif
621     {
622         PdoExt->Flags &= ~ISAPNP_READ_PORT_NEED_REBALANCE;
623 
624         for (i = 0; i < ResourceList->List[0].PartialResourceList.Count; i++)
625         {
626             PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor =
627                 &ResourceList->List[0].PartialResourceList.PartialDescriptors[i];
628 
629             if (IS_READ_PORT(PartialDescriptor))
630             {
631                 PUCHAR ReadDataPort = ULongToPtr(PartialDescriptor->u.Port.Start.u.LowPart + 3);
632 
633                 /* Run the isolation protocol */
634                 FdoExt->Cards = IsaHwTryReadDataPort(ReadDataPort);
635 
636                 if (FdoExt->Cards > 0)
637                 {
638                     FdoExt->ReadDataPort = ReadDataPort;
639 
640                     IsaPnpAcquireDeviceDataLock(FdoExt);
641 
642                     /* Card identification */
643                     Status = IsaHwFillDeviceList(FdoExt);
644                     IsaHwWaitForKey();
645 
646                     IsaPnpReleaseDeviceDataLock(FdoExt);
647 
648                     PdoExt->Flags |= ISAPNP_READ_PORT_ALLOW_FDO_SCAN |
649                                      ISAPNP_SCANNED_BY_READ_PORT;
650 
651                     IoInvalidateDeviceRelations(FdoExt->Pdo, BusRelations);
652                     IoInvalidateDeviceRelations(FdoExt->ReadPortPdo, RemovalRelations);
653 
654                     return Status;
655                 }
656                 else
657                 {
658                     IsaHwWaitForKey();
659 #if 0 /* See the 'if 0' above */
660                     break;
661 #endif
662                 }
663             }
664         }
665     }
666     else
667     {
668         return STATUS_DEVICE_CONFIGURATION_ERROR;
669     }
670 
671     /* Mark Read Port as started, even if no card has been detected */
672     return STATUS_SUCCESS;
673 }
674 
675 static
676 CODE_SEG("PAGE")
677 NTSTATUS
678 IsaPdoFilterResourceRequirements(
679     _In_ PISAPNP_PDO_EXTENSION PdoExt,
680     _Inout_ PIRP Irp,
681     _In_ PIO_STACK_LOCATION IrpSp)
682 {
683     PAGED_CODE();
684 
685     /* TODO: Handle */
686     UNREFERENCED_PARAMETER(PdoExt);
687     UNREFERENCED_PARAMETER(IrpSp);
688     return Irp->IoStatus.Status;
689 }
690 
691 static
692 CODE_SEG("PAGE")
693 NTSTATUS
694 IsaPdoQueryBusInformation(
695     _In_ PISAPNP_PDO_EXTENSION PdoExt,
696     _Inout_ PIRP Irp)
697 {
698     PPNP_BUS_INFORMATION BusInformation;
699 
700     PAGED_CODE();
701 
702     BusInformation = ExAllocatePoolWithTag(PagedPool,
703                                            sizeof(PNP_BUS_INFORMATION),
704                                            TAG_ISAPNP);
705     if (!BusInformation)
706         return STATUS_INSUFFICIENT_RESOURCES;
707 
708     BusInformation->BusTypeGuid = GUID_BUS_TYPE_ISAPNP;
709     BusInformation->LegacyBusType = Isa;
710     BusInformation->BusNumber = PdoExt->FdoExt->BusNumber;
711 
712     Irp->IoStatus.Information = (ULONG_PTR)BusInformation;
713     return STATUS_SUCCESS;
714 }
715 
716 static
717 CODE_SEG("PAGE")
718 NTSTATUS
719 IsaPdoQueryDeviceUsageNotification(
720     _In_ PISAPNP_PDO_EXTENSION PdoExt,
721     _Inout_ PIRP Irp,
722     _In_ PIO_STACK_LOCATION IrpSp)
723 {
724     BOOLEAN InPath = IrpSp->Parameters.UsageNotification.InPath;
725 
726     PAGED_CODE();
727 
728     switch (IrpSp->Parameters.UsageNotification.Type)
729     {
730         case DeviceUsageTypePaging:
731         case DeviceUsageTypeHibernation:
732         case DeviceUsageTypeDumpFile:
733             IoAdjustPagingPathCount(&PdoExt->SpecialFiles, InPath);
734             IoInvalidateDeviceState(PdoExt->Common.Self);
735             break;
736 
737         default:
738             return Irp->IoStatus.Status;
739     }
740 
741     /* Do not send it to FDO for compatibility */
742     return STATUS_SUCCESS;
743 }
744 
745 static
746 CODE_SEG("PAGE")
747 NTSTATUS
748 IsaPdoRemoveDevice(
749     _In_ PISAPNP_PDO_EXTENSION PdoExt,
750     _In_ BOOLEAN FinalRemove)
751 {
752     PISAPNP_FDO_EXTENSION FdoExt = PdoExt->FdoExt;
753 
754     PAGED_CODE();
755 
756     /* Deactivate the device if previously activated */
757     if (PdoExt->Common.State == dsStarted)
758     {
759         IsaHwWakeDevice(PdoExt->IsaPnpDevice);
760         IsaHwDeactivateDevice(PdoExt->IsaPnpDevice);
761 
762         IsaHwWaitForKey();
763 
764         PdoExt->Common.State = dsStopped;
765     }
766 
767     if (FinalRemove && !(PdoExt->Flags & ISAPNP_ENUMERATED))
768     {
769         IsaPnpAcquireDeviceDataLock(FdoExt);
770 
771         RemoveEntryList(&PdoExt->IsaPnpDevice->DeviceLink);
772         --FdoExt->DeviceCount;
773 
774         IsaPnpReleaseDeviceDataLock(FdoExt);
775 
776         IsaPnpRemoveLogicalDeviceDO(PdoExt->Common.Self);
777     }
778 
779     return STATUS_SUCCESS;
780 }
781 
782 static
783 CODE_SEG("PAGE")
784 NTSTATUS
785 IsaReadPortRemoveDevice(
786     _In_ PISAPNP_PDO_EXTENSION PdoExt,
787     _In_ BOOLEAN FinalRemove)
788 {
789     PISAPNP_FDO_EXTENSION FdoExt = PdoExt->FdoExt;
790     PLIST_ENTRY Entry;
791 
792     PAGED_CODE();
793 
794     IsaPnpAcquireDeviceDataLock(FdoExt);
795 
796     /* Logical devices will receive a remove request afterwards */
797     for (Entry = FdoExt->DeviceListHead.Flink;
798          Entry != &FdoExt->DeviceListHead;
799          Entry = Entry->Flink)
800     {
801         PISAPNP_LOGICAL_DEVICE LogDevice = CONTAINING_RECORD(Entry,
802                                                              ISAPNP_LOGICAL_DEVICE,
803                                                              DeviceLink);
804 
805         LogDevice->Flags &= ~ISAPNP_PRESENT;
806     }
807 
808     IsaPnpReleaseDeviceDataLock(FdoExt);
809 
810     PdoExt->Flags &= ~ISAPNP_READ_PORT_ALLOW_FDO_SCAN;
811     IoInvalidateDeviceRelations(FdoExt->Pdo, BusRelations);
812 
813     if (FinalRemove && !(PdoExt->Flags & ISAPNP_ENUMERATED))
814     {
815         IsaPnpRemoveReadPortDO(PdoExt->Common.Self);
816     }
817 
818     return STATUS_SUCCESS;
819 }
820 
821 CODE_SEG("PAGE")
822 VOID
823 IsaPnpRemoveLogicalDeviceDO(
824     _In_ PDEVICE_OBJECT Pdo)
825 {
826     PISAPNP_PDO_EXTENSION PdoExt = Pdo->DeviceExtension;
827     PISAPNP_LOGICAL_DEVICE LogDev = PdoExt->IsaPnpDevice;
828     PLIST_ENTRY Entry;
829 
830     PAGED_CODE();
831     ASSERT(LogDev);
832 
833     DPRINT("Removing CSN %u, LDN %u\n", LogDev->CSN, LogDev->LDN);
834 
835     if (PdoExt->RequirementsList)
836         ExFreePoolWithTag(PdoExt->RequirementsList, TAG_ISAPNP);
837 
838     if (PdoExt->ResourceList)
839         ExFreePoolWithTag(PdoExt->ResourceList, TAG_ISAPNP);
840 
841     if (LogDev->FriendlyName)
842         ExFreePoolWithTag(LogDev->FriendlyName, TAG_ISAPNP);
843 
844     if (LogDev->Resources)
845         ExFreePoolWithTag(LogDev->Resources, TAG_ISAPNP);
846 
847     Entry = LogDev->CompatibleIdList.Flink;
848     while (Entry != &LogDev->CompatibleIdList)
849     {
850         PISAPNP_COMPATIBLE_ID_ENTRY CompatibleId =
851             CONTAINING_RECORD(Entry, ISAPNP_COMPATIBLE_ID_ENTRY, IdLink);
852 
853         RemoveEntryList(&CompatibleId->IdLink);
854 
855         Entry = Entry->Flink;
856 
857         ExFreePoolWithTag(CompatibleId, TAG_ISAPNP);
858     }
859 
860     ExFreePoolWithTag(LogDev, TAG_ISAPNP);
861 
862     IoDeleteDevice(PdoExt->Common.Self);
863 }
864 
865 CODE_SEG("PAGE")
866 NTSTATUS
867 IsaPdoPnp(
868     _In_ PISAPNP_PDO_EXTENSION PdoExt,
869     _Inout_ PIRP Irp,
870     _In_ PIO_STACK_LOCATION IrpSp)
871 {
872     NTSTATUS Status = Irp->IoStatus.Status;
873 
874     PAGED_CODE();
875 
876     if (PdoExt->Common.Signature == IsaPnpLogicalDevice)
877     {
878         DPRINT("%s(%p, %p) CSN %u, LDN %u, Minor - %X\n",
879                __FUNCTION__,
880                PdoExt,
881                Irp,
882                PdoExt->IsaPnpDevice->CSN,
883                PdoExt->IsaPnpDevice->LDN,
884                IrpSp->MinorFunction);
885     }
886     else
887     {
888         DPRINT("%s(%p, %p) ReadPort, Minor - %X\n",
889                __FUNCTION__,
890                PdoExt,
891                Irp,
892                IrpSp->MinorFunction);
893     }
894 
895     switch (IrpSp->MinorFunction)
896     {
897         case IRP_MN_START_DEVICE:
898         {
899             if (PdoExt->Common.Signature == IsaPnpLogicalDevice)
900             {
901                 IsaHwWakeDevice(PdoExt->IsaPnpDevice);
902 
903                 Status = IsaHwConfigureDevice(PdoExt->FdoExt,
904                                               PdoExt->IsaPnpDevice,
905                                               IrpSp->Parameters.StartDevice.AllocatedResources);
906                 if (NT_SUCCESS(Status))
907                 {
908                     IsaHwActivateDevice(PdoExt->FdoExt, PdoExt->IsaPnpDevice);
909                 }
910                 else
911                 {
912                     DPRINT1("Failed to configure CSN %u, LDN %u with status 0x%08lx\n",
913                             PdoExt->IsaPnpDevice->CSN, PdoExt->IsaPnpDevice->LDN, Status);
914                 }
915 
916                 IsaHwWaitForKey();
917             }
918             else
919             {
920                 Status = IsaPdoStartReadPort(PdoExt,
921                                              IrpSp->Parameters.StartDevice.AllocatedResources);
922             }
923 
924             if (NT_SUCCESS(Status))
925                 PdoExt->Common.State = dsStarted;
926             break;
927         }
928 
929         case IRP_MN_STOP_DEVICE:
930         {
931             if (PdoExt->Common.Signature == IsaPnpLogicalDevice)
932             {
933                 IsaHwWakeDevice(PdoExt->IsaPnpDevice);
934                 IsaHwDeactivateDevice(PdoExt->IsaPnpDevice);
935 
936                 IsaHwWaitForKey();
937             }
938             else
939             {
940                 PdoExt->Flags &= ~ISAPNP_READ_PORT_ALLOW_FDO_SCAN;
941             }
942 
943             Status = STATUS_SUCCESS;
944 
945             if (NT_SUCCESS(Status))
946                 PdoExt->Common.State = dsStopped;
947             break;
948         }
949 
950         case IRP_MN_QUERY_STOP_DEVICE:
951         {
952             if (PdoExt->SpecialFiles > 0)
953                 Status = STATUS_DEVICE_BUSY;
954             else if (PdoExt->Flags & ISAPNP_READ_PORT_NEED_REBALANCE)
955                 Status = STATUS_RESOURCE_REQUIREMENTS_CHANGED;
956             else
957                 Status = STATUS_SUCCESS;
958 
959             break;
960         }
961 
962         case IRP_MN_QUERY_REMOVE_DEVICE:
963         {
964             if (PdoExt->SpecialFiles > 0)
965                 Status = STATUS_DEVICE_BUSY;
966             else
967                 Status = STATUS_SUCCESS;
968             break;
969         }
970 
971         case IRP_MN_QUERY_DEVICE_RELATIONS:
972             Status = IsaPdoQueryDeviceRelations(PdoExt, Irp, IrpSp);
973             break;
974 
975         case IRP_MN_QUERY_CAPABILITIES:
976             Status = IsaPdoQueryCapabilities(PdoExt, Irp, IrpSp);
977             break;
978 
979         case IRP_MN_SURPRISE_REMOVAL:
980         case IRP_MN_REMOVE_DEVICE:
981         {
982             BOOLEAN FinalRemove = (IrpSp->MinorFunction == IRP_MN_REMOVE_DEVICE);
983 
984             if (PdoExt->Common.Signature == IsaPnpLogicalDevice)
985                 Status = IsaPdoRemoveDevice(PdoExt, FinalRemove);
986             else
987                 Status = IsaReadPortRemoveDevice(PdoExt, FinalRemove);
988             break;
989         }
990 
991         case IRP_MN_QUERY_PNP_DEVICE_STATE:
992             Status = IsaPdoQueryPnpDeviceState(PdoExt, Irp);
993             break;
994 
995         case IRP_MN_QUERY_RESOURCES:
996             Status = IsaPdoQueryResources(PdoExt, Irp, IrpSp);
997             break;
998 
999         case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
1000             Status = IsaPdoQueryResourceRequirements(PdoExt, Irp, IrpSp);
1001             break;
1002 
1003         case IRP_MN_QUERY_ID:
1004             if (PdoExt->Common.Signature == IsaPnpLogicalDevice)
1005                 Status = IsaPdoQueryId(PdoExt, Irp, IrpSp);
1006             else
1007                 Status = IsaReadPortQueryId(Irp, IrpSp);
1008             break;
1009 
1010         case IRP_MN_QUERY_DEVICE_TEXT:
1011             if (PdoExt->Common.Signature == IsaPnpLogicalDevice)
1012                 Status = IsaPdoQueryDeviceText(PdoExt, Irp, IrpSp);
1013             break;
1014 
1015         case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
1016             Status = IsaPdoFilterResourceRequirements(PdoExt, Irp, IrpSp);
1017             break;
1018 
1019         case IRP_MN_QUERY_BUS_INFORMATION:
1020             Status = IsaPdoQueryBusInformation(PdoExt, Irp);
1021             break;
1022 
1023         case IRP_MN_DEVICE_USAGE_NOTIFICATION:
1024             Status = IsaPdoQueryDeviceUsageNotification(PdoExt, Irp, IrpSp);
1025             break;
1026 
1027         case IRP_MN_CANCEL_REMOVE_DEVICE:
1028         case IRP_MN_CANCEL_STOP_DEVICE:
1029             Status = STATUS_SUCCESS;
1030             break;
1031 
1032         default:
1033             DPRINT("Unknown PnP code: %X\n", IrpSp->MinorFunction);
1034             break;
1035     }
1036 
1037     Irp->IoStatus.Status = Status;
1038     IoCompleteRequest(Irp, IO_NO_INCREMENT);
1039 
1040     return Status;
1041 }
1042