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