xref: /reactos/drivers/bus/isapnp/isapnp.c (revision 8f44930f)
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:         Driver entry
5  * COPYRIGHT:       Copyright 2010 Cameron Gutman <cameron.gutman@reactos.org>
6  *                  Copyright 2020 Hervé Poussineau <hpoussin@reactos.org>
7  */
8 
9 #include "isapnp.h"
10 
11 #define NDEBUG
12 #include <debug.h>
13 
14 NTSTATUS
15 NTAPI
16 IsaPnpDuplicateUnicodeString(
17     IN ULONG Flags,
18     IN PCUNICODE_STRING SourceString,
19     OUT PUNICODE_STRING DestinationString)
20 {
21     if (SourceString == NULL ||
22         DestinationString == NULL ||
23         SourceString->Length > SourceString->MaximumLength ||
24         (SourceString->Length == 0 && SourceString->MaximumLength > 0 && SourceString->Buffer == NULL) ||
25         Flags == RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING ||
26         Flags >= 4)
27     {
28         return STATUS_INVALID_PARAMETER;
29     }
30 
31     if ((SourceString->Length == 0) &&
32         (Flags != (RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE |
33                    RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING)))
34     {
35         DestinationString->Length = 0;
36         DestinationString->MaximumLength = 0;
37         DestinationString->Buffer = NULL;
38     }
39     else
40     {
41         USHORT DestMaxLength = SourceString->Length;
42 
43         if (Flags & RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE)
44             DestMaxLength += sizeof(UNICODE_NULL);
45 
46         DestinationString->Buffer = ExAllocatePool(PagedPool, DestMaxLength);
47         if (DestinationString->Buffer == NULL)
48             return STATUS_NO_MEMORY;
49 
50         RtlCopyMemory(DestinationString->Buffer, SourceString->Buffer, SourceString->Length);
51         DestinationString->Length = SourceString->Length;
52         DestinationString->MaximumLength = DestMaxLength;
53 
54         if (Flags & RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE)
55             DestinationString->Buffer[DestinationString->Length / sizeof(WCHAR)] = 0;
56     }
57 
58     return STATUS_SUCCESS;
59 }
60 
61 static
62 NTSTATUS
63 NTAPI
64 IsaFdoCreateDeviceIDs(
65     IN PISAPNP_PDO_EXTENSION PdoExt)
66 {
67     PISAPNP_LOGICAL_DEVICE LogDev = PdoExt->IsaPnpDevice;
68     UNICODE_STRING TempString;
69     WCHAR TempBuffer[256];
70     PWCHAR End;
71     NTSTATUS Status;
72     USHORT i;
73 
74     TempString.Buffer = TempBuffer;
75     TempString.MaximumLength = sizeof(TempBuffer);
76     TempString.Length = 0;
77 
78     /* Device ID */
79     Status = RtlStringCbPrintfExW(TempString.Buffer,
80                                   TempString.MaximumLength / sizeof(WCHAR),
81                                   &End,
82                                   NULL, 0,
83                                   L"ISAPNP\\%.3S%04x",
84                                   LogDev->VendorId,
85                                   LogDev->ProdId);
86     if (!NT_SUCCESS(Status))
87         return Status;
88     TempString.Length = (USHORT)((End - TempString.Buffer) * sizeof(WCHAR));
89     Status = IsaPnpDuplicateUnicodeString(
90         RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
91         &TempString,
92         &PdoExt->DeviceID);
93     if (!NT_SUCCESS(Status))
94         return Status;
95 
96     /* HardwareIDs */
97     Status = RtlStringCbPrintfExW(TempString.Buffer,
98                                   TempString.MaximumLength / sizeof(WCHAR),
99                                   &End,
100                                   NULL, 0,
101                                   L"ISAPNP\\%.3S%04x@"
102                                   L"*%.3S%04x@",
103                                   LogDev->VendorId,
104                                   LogDev->ProdId,
105                                   LogDev->VendorId,
106                                   LogDev->ProdId);
107     if (!NT_SUCCESS(Status))
108         return Status;
109     TempString.Length = (USHORT)((End - TempString.Buffer) * sizeof(WCHAR));
110     Status = IsaPnpDuplicateUnicodeString(
111         RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
112         &TempString,
113         &PdoExt->HardwareIDs);
114     if (!NT_SUCCESS(Status))
115         return Status;
116     for (i = 0; i < PdoExt->HardwareIDs.Length / sizeof(WCHAR); i++)
117         if (PdoExt->HardwareIDs.Buffer[i] == '@')
118             PdoExt->HardwareIDs.Buffer[i] = UNICODE_NULL;
119 
120     /* InstanceID */
121     Status = RtlStringCbPrintfExW(TempString.Buffer,
122                                   TempString.MaximumLength / sizeof(WCHAR),
123                                   &End,
124                                   NULL, 0,
125                                   L"%X",
126                                   LogDev->SerialNumber);
127     if (!NT_SUCCESS(Status))
128         return Status;
129     TempString.Length = (USHORT)((End - TempString.Buffer) * sizeof(WCHAR));
130     Status = IsaPnpDuplicateUnicodeString(
131         RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
132         &TempString,
133         &PdoExt->InstanceID);
134     if (!NT_SUCCESS(Status))
135         return Status;
136 
137     return STATUS_SUCCESS;
138 }
139 
140 static
141 NTSTATUS
142 NTAPI
143 IsaPnpCreateLogicalDeviceRequirements(
144     _In_ PISAPNP_PDO_EXTENSION PdoExt)
145 {
146     PISAPNP_LOGICAL_DEVICE LogDev = PdoExt->IsaPnpDevice;
147     RTL_BITMAP IrqBitmap[RTL_NUMBER_OF(LogDev->Irq)];
148     RTL_BITMAP DmaBitmap[RTL_NUMBER_OF(LogDev->Dma)];
149     ULONG IrqData[RTL_NUMBER_OF(LogDev->Irq)];
150     ULONG DmaData[RTL_NUMBER_OF(LogDev->Dma)];
151     ULONG ResourceCount = 0;
152     ULONG ListSize, i, j;
153     BOOLEAN FirstIrq = TRUE, FirstDma = TRUE;
154     PIO_RESOURCE_REQUIREMENTS_LIST RequirementsList;
155     PIO_RESOURCE_DESCRIPTOR Descriptor;
156 
157     /* Count number of requirements */
158     for (i = 0; i < RTL_NUMBER_OF(LogDev->Io); i++)
159     {
160         if (!LogDev->Io[i].Description.Length)
161             break;
162 
163         ResourceCount++;
164     }
165     for (i = 0; i < RTL_NUMBER_OF(LogDev->Irq); i++)
166     {
167         if (!LogDev->Irq[i].Description.Mask)
168             break;
169 
170         IrqData[i] = LogDev->Irq[i].Description.Mask;
171         RtlInitializeBitMap(&IrqBitmap[i], &IrqData[i], 16);
172         ResourceCount += RtlNumberOfSetBits(&IrqBitmap[i]);
173 
174         if (LogDev->Irq[i].Description.Information & 0x4)
175         {
176             /* Add room for level sensitive */
177             ResourceCount += RtlNumberOfSetBits(&IrqBitmap[i]);
178         }
179     }
180     if (ResourceCount == 0)
181         return STATUS_SUCCESS;
182     for (i = 0; i < RTL_NUMBER_OF(LogDev->Irq); i++)
183     {
184         if (!LogDev->Dma[i].Description.Mask)
185             break;
186 
187         DmaData[i] = LogDev->Dma[i].Description.Mask;
188         RtlInitializeBitMap(&DmaBitmap[i], &DmaData[i], 8);
189         ResourceCount += RtlNumberOfSetBits(&DmaBitmap[i]);
190     }
191 
192     /* Allocate memory to store requirements */
193     ListSize = sizeof(IO_RESOURCE_REQUIREMENTS_LIST)
194                + ResourceCount * sizeof(IO_RESOURCE_DESCRIPTOR);
195     RequirementsList = ExAllocatePool(PagedPool, ListSize);
196     if (!RequirementsList)
197         return STATUS_NO_MEMORY;
198 
199     RtlZeroMemory(RequirementsList, ListSize);
200     RequirementsList->ListSize = ListSize;
201     RequirementsList->InterfaceType = Isa;
202     RequirementsList->AlternativeLists = 1;
203 
204     RequirementsList->List[0].Version = 1;
205     RequirementsList->List[0].Revision = 1;
206     RequirementsList->List[0].Count = ResourceCount;
207 
208     /* Store requirements */
209     Descriptor = RequirementsList->List[0].Descriptors;
210     for (i = 0; i < RTL_NUMBER_OF(LogDev->Io); i++)
211     {
212         if (!LogDev->Io[i].Description.Length)
213             break;
214 
215         DPRINT("Device.Io[%d].Information = 0x%02x\n", i, LogDev->Io[i].Description.Information);
216         DPRINT("Device.Io[%d].Minimum = 0x%02x\n", i, LogDev->Io[i].Description.Minimum);
217         DPRINT("Device.Io[%d].Maximum = 0x%02x\n", i, LogDev->Io[i].Description.Maximum);
218         DPRINT("Device.Io[%d].Alignment = 0x%02x\n", i, LogDev->Io[i].Description.Alignment);
219         DPRINT("Device.Io[%d].Length = 0x%02x\n", i, LogDev->Io[i].Description.Length);
220 
221         Descriptor->Type = CmResourceTypePort;
222         Descriptor->ShareDisposition = CmResourceShareDeviceExclusive;
223         if (LogDev->Io[i].Description.Information & 0x1)
224             Descriptor->Flags = CM_RESOURCE_PORT_16_BIT_DECODE;
225         else
226             Descriptor->Flags = CM_RESOURCE_PORT_10_BIT_DECODE;
227         Descriptor->u.Port.Length = LogDev->Io[i].Description.Length;
228         Descriptor->u.Port.Alignment = LogDev->Io[i].Description.Alignment;
229         Descriptor->u.Port.MinimumAddress.LowPart = LogDev->Io[i].Description.Minimum;
230         Descriptor->u.Port.MaximumAddress.LowPart =
231             LogDev->Io[i].Description.Maximum + LogDev->Io[i].Description.Length - 1;
232         Descriptor++;
233     }
234     for (i = 0; i < RTL_NUMBER_OF(LogDev->Irq); i++)
235     {
236         if (!LogDev->Irq[i].Description.Mask)
237             break;
238 
239         DPRINT("Device.Irq[%d].Mask = 0x%02x\n", i, LogDev->Irq[i].Description.Mask);
240         DPRINT("Device.Irq[%d].Information = 0x%02x\n", i, LogDev->Irq[i].Description.Information);
241 
242         for (j = 0; j < 15; j++)
243         {
244             if (!RtlCheckBit(&IrqBitmap[i], j))
245                 continue;
246 
247             if (FirstIrq)
248                 FirstIrq = FALSE;
249             else
250                 Descriptor->Option = IO_RESOURCE_ALTERNATIVE;
251             Descriptor->Type = CmResourceTypeInterrupt;
252             Descriptor->Flags = CM_RESOURCE_INTERRUPT_LATCHED;
253             Descriptor->u.Interrupt.MinimumVector = Descriptor->u.Interrupt.MaximumVector = j;
254             Descriptor++;
255 
256             if (LogDev->Irq[i].Description.Information & 0x4)
257             {
258                 /* Level interrupt */
259                 Descriptor->Option = IO_RESOURCE_ALTERNATIVE;
260                 Descriptor->Type = CmResourceTypeInterrupt;
261                 Descriptor->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
262                 Descriptor->u.Interrupt.MinimumVector = Descriptor->u.Interrupt.MaximumVector = j;
263                 Descriptor++;
264             }
265         }
266     }
267     for (i = 0; i < RTL_NUMBER_OF(LogDev->Dma); i++)
268     {
269         if (!LogDev->Dma[i].Description.Mask)
270             break;
271 
272         DPRINT("Device.Dma[%d].Mask = 0x%02x\n", i, LogDev->Dma[i].Description.Mask);
273         DPRINT("Device.Dma[%d].Information = 0x%02x\n", i, LogDev->Dma[i].Description.Information);
274 
275         for (j = 0; j < 8; j++)
276         {
277             if (!RtlCheckBit(&DmaBitmap[i], j))
278                 continue;
279 
280             if (FirstDma)
281                 FirstDma = FALSE;
282             else
283                 Descriptor->Option = IO_RESOURCE_ALTERNATIVE;
284             Descriptor->Type = CmResourceTypeDma;
285             switch (LogDev->Dma[i].Description.Information & 0x3)
286             {
287                 case 0x0: Descriptor->Flags |= CM_RESOURCE_DMA_8; break;
288                 case 0x1: Descriptor->Flags |= CM_RESOURCE_DMA_8_AND_16; break;
289                 case 0x2: Descriptor->Flags |= CM_RESOURCE_DMA_16; break;
290                 default: break;
291             }
292             if (LogDev->Dma[i].Description.Information & 0x4)
293                 Descriptor->Flags |= CM_RESOURCE_DMA_BUS_MASTER;
294             switch ((LogDev->Dma[i].Description.Information >> 5) & 0x3)
295             {
296                 case 0x1: Descriptor->Flags |= CM_RESOURCE_DMA_TYPE_A; break;
297                 case 0x2: Descriptor->Flags |= CM_RESOURCE_DMA_TYPE_B; break;
298                 case 0x3: Descriptor->Flags |= CM_RESOURCE_DMA_TYPE_F; break;
299                 default: break;
300             }
301             Descriptor->u.Dma.MinimumChannel = Descriptor->u.Dma.MaximumChannel = j;
302             Descriptor++;
303         }
304     }
305 
306     PdoExt->RequirementsList = RequirementsList;
307     return STATUS_SUCCESS;
308 }
309 
310 static
311 NTSTATUS
312 NTAPI
313 IsaPnpCreateLogicalDeviceResources(
314     _In_ PISAPNP_PDO_EXTENSION PdoExt)
315 {
316     PISAPNP_LOGICAL_DEVICE LogDev = PdoExt->IsaPnpDevice;
317     ULONG ResourceCount = 0;
318     ULONG ListSize, i;
319     PCM_RESOURCE_LIST ResourceList;
320     PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor;
321 
322     /* Count number of required resources */
323     for (i = 0; i < RTL_NUMBER_OF(LogDev->Io); i++)
324     {
325         if (LogDev->Io[i].CurrentBase)
326             ResourceCount++;
327         else
328             break;
329     }
330     for (i = 0; i < RTL_NUMBER_OF(LogDev->Irq); i++)
331     {
332         if (LogDev->Irq[i].CurrentNo)
333             ResourceCount++;
334         else
335             break;
336     }
337     for (i = 0; i < RTL_NUMBER_OF(LogDev->Dma); i++)
338     {
339         if (LogDev->Dma[i].CurrentChannel != 4)
340             ResourceCount++;
341         else
342             break;
343     }
344     if (ResourceCount == 0)
345         return STATUS_SUCCESS;
346 
347     /* Allocate memory to store resources */
348     ListSize = sizeof(CM_RESOURCE_LIST)
349                + (ResourceCount - 1) * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
350     ResourceList = ExAllocatePool(PagedPool, ListSize);
351     if (!ResourceList)
352         return STATUS_NO_MEMORY;
353 
354     RtlZeroMemory(ResourceList, ListSize);
355     ResourceList->Count = 1;
356     ResourceList->List[0].InterfaceType = Isa;
357     ResourceList->List[0].PartialResourceList.Version = 1;
358     ResourceList->List[0].PartialResourceList.Revision = 1;
359     ResourceList->List[0].PartialResourceList.Count = ResourceCount;
360 
361     /* Store resources */
362     ResourceCount = 0;
363     for (i = 0; i < RTL_NUMBER_OF(LogDev->Io); i++)
364     {
365         if (!LogDev->Io[i].CurrentBase)
366             continue;
367 
368         Descriptor = &ResourceList->List[0].PartialResourceList.PartialDescriptors[ResourceCount++];
369         Descriptor->Type = CmResourceTypePort;
370         Descriptor->ShareDisposition = CmResourceShareDeviceExclusive;
371         if (LogDev->Io[i].Description.Information & 0x1)
372             Descriptor->Flags = CM_RESOURCE_PORT_16_BIT_DECODE;
373         else
374             Descriptor->Flags = CM_RESOURCE_PORT_10_BIT_DECODE;
375         Descriptor->u.Port.Length = LogDev->Io[i].Description.Length;
376         Descriptor->u.Port.Start.LowPart = LogDev->Io[i].CurrentBase;
377     }
378     for (i = 0; i < RTL_NUMBER_OF(LogDev->Irq); i++)
379     {
380         if (!LogDev->Irq[i].CurrentNo)
381             continue;
382 
383         Descriptor = &ResourceList->List[0].PartialResourceList.PartialDescriptors[ResourceCount++];
384         Descriptor->Type = CmResourceTypeInterrupt;
385         Descriptor->ShareDisposition = CmResourceShareDeviceExclusive;
386         if (LogDev->Irq[i].CurrentType & 0x01)
387             Descriptor->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
388         else
389             Descriptor->Flags = CM_RESOURCE_INTERRUPT_LATCHED;
390         Descriptor->u.Interrupt.Level = LogDev->Irq[i].CurrentNo;
391         Descriptor->u.Interrupt.Vector = LogDev->Irq[i].CurrentNo;
392         Descriptor->u.Interrupt.Affinity = -1;
393     }
394     for (i = 0; i < RTL_NUMBER_OF(LogDev->Dma); i++)
395     {
396         if (LogDev->Dma[i].CurrentChannel == 4)
397             continue;
398 
399         Descriptor = &ResourceList->List[0].PartialResourceList.PartialDescriptors[ResourceCount++];
400         Descriptor->Type = CmResourceTypeDma;
401         Descriptor->ShareDisposition = CmResourceShareDeviceExclusive;
402         switch (LogDev->Dma[i].Description.Information & 0x3)
403         {
404             case 0x0: Descriptor->Flags |= CM_RESOURCE_DMA_8; break;
405             case 0x1: Descriptor->Flags |= CM_RESOURCE_DMA_8 | CM_RESOURCE_DMA_16; break;
406             case 0x2: Descriptor->Flags |= CM_RESOURCE_DMA_16; break;
407             default: break;
408         }
409         if (LogDev->Dma[i].Description.Information & 0x4)
410             Descriptor->Flags |= CM_RESOURCE_DMA_BUS_MASTER;
411         switch ((LogDev->Dma[i].Description.Information >> 5) & 0x3)
412         {
413             case 0x1: Descriptor->Flags |= CM_RESOURCE_DMA_TYPE_A; break;
414             case 0x2: Descriptor->Flags |= CM_RESOURCE_DMA_TYPE_B; break;
415             case 0x3: Descriptor->Flags |= CM_RESOURCE_DMA_TYPE_F; break;
416             default: break;
417         }
418         Descriptor->u.Dma.Channel = LogDev->Dma[i].CurrentChannel;
419     }
420 
421     PdoExt->ResourceList = ResourceList;
422     PdoExt->ResourceListSize = ListSize;
423     return STATUS_SUCCESS;
424 }
425 
426 NTSTATUS
427 NTAPI
428 IsaPnpFillDeviceRelations(
429     _In_ PISAPNP_FDO_EXTENSION FdoExt,
430     _Inout_ PIRP Irp,
431     _In_ BOOLEAN IncludeDataPort)
432 {
433     PISAPNP_PDO_EXTENSION PdoExt;
434     NTSTATUS Status = STATUS_SUCCESS;
435     PLIST_ENTRY CurrentEntry;
436     PISAPNP_LOGICAL_DEVICE IsaDevice;
437     PDEVICE_RELATIONS DeviceRelations;
438     ULONG i = 0;
439 
440     DeviceRelations = ExAllocatePool(NonPagedPool,
441                                      sizeof(DEVICE_RELATIONS) + sizeof(DEVICE_OBJECT) * FdoExt->DeviceCount);
442     if (!DeviceRelations)
443     {
444         return STATUS_NO_MEMORY;
445     }
446 
447     if (IncludeDataPort)
448     {
449         DeviceRelations->Objects[i++] = FdoExt->ReadPortPdo;
450         ObReferenceObject(FdoExt->ReadPortPdo);
451     }
452 
453     CurrentEntry = FdoExt->DeviceListHead.Flink;
454     while (CurrentEntry != &FdoExt->DeviceListHead)
455     {
456         IsaDevice = CONTAINING_RECORD(CurrentEntry, ISAPNP_LOGICAL_DEVICE, DeviceLink);
457 
458         if (!IsaDevice->Pdo)
459         {
460             Status = IoCreateDevice(FdoExt->DriverObject,
461                                     sizeof(ISAPNP_PDO_EXTENSION),
462                                     NULL,
463                                     FILE_DEVICE_CONTROLLER,
464                                     FILE_DEVICE_SECURE_OPEN | FILE_AUTOGENERATED_DEVICE_NAME,
465                                     FALSE,
466                                     &IsaDevice->Pdo);
467             if (!NT_SUCCESS(Status))
468             {
469                 break;
470             }
471 
472             IsaDevice->Pdo->Flags &= ~DO_DEVICE_INITIALIZING;
473 
474             //Device->Pdo->Flags |= DO_POWER_PAGABLE;
475 
476             PdoExt = IsaDevice->Pdo->DeviceExtension;
477 
478             RtlZeroMemory(PdoExt, sizeof(ISAPNP_PDO_EXTENSION));
479 
480             PdoExt->Common.IsFdo = FALSE;
481             PdoExt->Common.Self = IsaDevice->Pdo;
482             PdoExt->Common.State = dsStopped;
483             PdoExt->IsaPnpDevice = IsaDevice;
484             PdoExt->FdoExt = FdoExt;
485 
486             Status = IsaFdoCreateDeviceIDs(PdoExt);
487 
488             if (NT_SUCCESS(Status))
489                 Status = IsaPnpCreateLogicalDeviceRequirements(PdoExt);
490 
491             if (NT_SUCCESS(Status))
492                 Status = IsaPnpCreateLogicalDeviceResources(PdoExt);
493 
494             if (!NT_SUCCESS(Status))
495             {
496                 IoDeleteDevice(IsaDevice->Pdo);
497                 IsaDevice->Pdo = NULL;
498                 break;
499             }
500         }
501         DeviceRelations->Objects[i++] = IsaDevice->Pdo;
502 
503         ObReferenceObject(IsaDevice->Pdo);
504 
505         CurrentEntry = CurrentEntry->Flink;
506     }
507 
508     DeviceRelations->Count = i;
509 
510     Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations;
511 
512     return Status;
513 }
514 
515 static IO_COMPLETION_ROUTINE ForwardIrpCompletion;
516 
517 static
518 NTSTATUS
519 NTAPI
520 ForwardIrpCompletion(
521     IN PDEVICE_OBJECT DeviceObject,
522     IN PIRP Irp,
523     IN PVOID Context)
524 {
525     UNREFERENCED_PARAMETER(DeviceObject);
526 
527     if (Irp->PendingReturned)
528         KeSetEvent((PKEVENT)Context, IO_NO_INCREMENT, FALSE);
529 
530     return STATUS_MORE_PROCESSING_REQUIRED;
531 }
532 
533 NTSTATUS
534 NTAPI
535 IsaForwardIrpSynchronous(
536     IN PISAPNP_FDO_EXTENSION FdoExt,
537     IN PIRP Irp)
538 {
539     KEVENT Event;
540     NTSTATUS Status;
541 
542     KeInitializeEvent(&Event, NotificationEvent, FALSE);
543     IoCopyCurrentIrpStackLocationToNext(Irp);
544 
545     IoSetCompletionRoutine(Irp, ForwardIrpCompletion, &Event, TRUE, TRUE, TRUE);
546 
547     Status = IoCallDriver(FdoExt->Ldo, Irp);
548     if (Status == STATUS_PENDING)
549     {
550         Status = KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
551         if (NT_SUCCESS(Status))
552             Status = Irp->IoStatus.Status;
553     }
554 
555     return Status;
556 }
557 
558 _Dispatch_type_(IRP_MJ_CREATE)
559 _Dispatch_type_(IRP_MJ_CLOSE)
560 static DRIVER_DISPATCH IsaCreateClose;
561 
562 static
563 NTSTATUS
564 NTAPI
565 IsaCreateClose(
566     _In_ PDEVICE_OBJECT DeviceObject,
567     _Inout_ PIRP Irp)
568 {
569     Irp->IoStatus.Status = STATUS_SUCCESS;
570     Irp->IoStatus.Information = FILE_OPENED;
571 
572     DPRINT("%s(%p, %p)\n", __FUNCTION__, DeviceObject, Irp);
573 
574     IoCompleteRequest(Irp, IO_NO_INCREMENT);
575 
576     return STATUS_SUCCESS;
577 }
578 
579 static DRIVER_DISPATCH IsaIoctl;
580 
581 static
582 NTSTATUS
583 NTAPI
584 IsaIoctl(
585     IN PDEVICE_OBJECT DeviceObject,
586     IN PIRP Irp)
587 {
588     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
589     NTSTATUS Status;
590 
591     DPRINT("%s(%p, %p)\n", __FUNCTION__, DeviceObject, Irp);
592 
593     switch (IrpSp->Parameters.DeviceIoControl.IoControlCode)
594     {
595         default:
596             DPRINT1("Unknown ioctl code: %x\n", IrpSp->Parameters.DeviceIoControl.IoControlCode);
597             Status = STATUS_NOT_SUPPORTED;
598             break;
599     }
600 
601     Irp->IoStatus.Status = Status;
602     IoCompleteRequest(Irp, IO_NO_INCREMENT);
603 
604     return Status;
605 }
606 
607 static DRIVER_DISPATCH IsaReadWrite;
608 
609 static
610 NTSTATUS
611 NTAPI
612 IsaReadWrite(
613     IN PDEVICE_OBJECT DeviceObject,
614     IN PIRP Irp)
615 {
616     DPRINT("%s(%p, %p)\n", __FUNCTION__, DeviceObject, Irp);
617 
618     Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
619     Irp->IoStatus.Information = 0;
620 
621     IoCompleteRequest(Irp, IO_NO_INCREMENT);
622 
623     return STATUS_NOT_SUPPORTED;
624 }
625 
626 static
627 NTSTATUS
628 NTAPI
629 IsaPnpCreateReadPortDORequirements(
630     _In_ PISAPNP_PDO_EXTENSION PdoExt)
631 {
632     ULONG ListSize, i;
633     PIO_RESOURCE_REQUIREMENTS_LIST RequirementsList;
634     PIO_RESOURCE_DESCRIPTOR Descriptor;
635     const ULONG Ports[] = { ISAPNP_WRITE_DATA, ISAPNP_ADDRESS,
636                             0x274, 0x3E4, 0x204, 0x2E4, 0x354, 0x2F4 };
637 
638     ListSize = sizeof(IO_RESOURCE_REQUIREMENTS_LIST)
639                + 2 * RTL_NUMBER_OF(Ports) * sizeof(IO_RESOURCE_DESCRIPTOR);
640     RequirementsList = ExAllocatePool(PagedPool, ListSize);
641     if (!RequirementsList)
642         return STATUS_NO_MEMORY;
643 
644     RtlZeroMemory(RequirementsList, ListSize);
645     RequirementsList->ListSize = ListSize;
646     RequirementsList->AlternativeLists = 1;
647 
648     RequirementsList->List[0].Version = 1;
649     RequirementsList->List[0].Revision = 1;
650     RequirementsList->List[0].Count = 2 * RTL_NUMBER_OF(Ports);
651 
652     for (i = 0; i < 2 * RTL_NUMBER_OF(Ports); i += 2)
653     {
654         Descriptor = &RequirementsList->List[0].Descriptors[i];
655 
656         /* Expected port */
657         Descriptor[0].Type = CmResourceTypePort;
658         Descriptor[0].ShareDisposition = CmResourceShareDeviceExclusive;
659         Descriptor[0].Flags = CM_RESOURCE_PORT_16_BIT_DECODE;
660         Descriptor[0].u.Port.Length = Ports[i / 2] & 1 ? 0x01 : 0x04;
661         Descriptor[0].u.Port.Alignment = 0x01;
662         Descriptor[0].u.Port.MinimumAddress.LowPart = Ports[i / 2];
663         Descriptor[0].u.Port.MaximumAddress.LowPart = Ports[i / 2] + Descriptor[0].u.Port.Length - 1;
664 
665         /* ... but mark it as optional */
666         Descriptor[1].Option = IO_RESOURCE_ALTERNATIVE;
667         Descriptor[1].Type = CmResourceTypePort;
668         Descriptor[1].ShareDisposition = CmResourceShareDeviceExclusive;
669         Descriptor[1].Flags = CM_RESOURCE_PORT_16_BIT_DECODE;
670         Descriptor[1].u.Port.Alignment = 0x01;
671     }
672 
673     PdoExt->RequirementsList = RequirementsList;
674     return STATUS_SUCCESS;
675 }
676 
677 static
678 NTSTATUS
679 NTAPI
680 IsaPnpCreateReadPortDOResources(
681     _In_ PISAPNP_PDO_EXTENSION PdoExt)
682 {
683     const USHORT Ports[] = { ISAPNP_WRITE_DATA, ISAPNP_ADDRESS };
684     ULONG ListSize, i;
685     PCM_RESOURCE_LIST ResourceList;
686     PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor;
687 
688     ListSize = sizeof(CM_RESOURCE_LIST) +
689                sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR) * (RTL_NUMBER_OF(Ports) - 1);
690     ResourceList = ExAllocatePool(PagedPool, ListSize);
691     if (!ResourceList)
692         return STATUS_NO_MEMORY;
693 
694     RtlZeroMemory(ResourceList, ListSize);
695     ResourceList->Count = 1;
696     ResourceList->List[0].InterfaceType = Internal;
697     ResourceList->List[0].PartialResourceList.Version = 1;
698     ResourceList->List[0].PartialResourceList.Revision = 1;
699     ResourceList->List[0].PartialResourceList.Count = RTL_NUMBER_OF(Ports);
700 
701     for (i = 0; i < RTL_NUMBER_OF(Ports); i++)
702     {
703         Descriptor = &ResourceList->List[0].PartialResourceList.PartialDescriptors[i];
704         Descriptor->Type = CmResourceTypePort;
705         Descriptor->ShareDisposition = CmResourceShareDeviceExclusive;
706         Descriptor->Flags = CM_RESOURCE_PORT_16_BIT_DECODE;
707         Descriptor->u.Port.Length = 0x01;
708         Descriptor->u.Port.Start.LowPart = Ports[i];
709     }
710 
711     PdoExt->ResourceList = ResourceList;
712     PdoExt->ResourceListSize = ListSize;
713     return STATUS_SUCCESS;
714 }
715 
716 static
717 NTSTATUS
718 NTAPI
719 IsaPnpCreateReadPortDO(
720     _In_ PISAPNP_FDO_EXTENSION FdoExt)
721 {
722     UNICODE_STRING DeviceID = RTL_CONSTANT_STRING(L"ISAPNP\\ReadDataPort\0");
723     UNICODE_STRING HardwareIDs = RTL_CONSTANT_STRING(L"ISAPNP\\ReadDataPort\0\0");
724     UNICODE_STRING CompatibleIDs = RTL_CONSTANT_STRING(L"\0\0");
725     UNICODE_STRING InstanceID = RTL_CONSTANT_STRING(L"0\0");
726     PISAPNP_PDO_EXTENSION PdoExt;
727     NTSTATUS Status;
728 
729     Status = IoCreateDevice(FdoExt->DriverObject,
730                             sizeof(ISAPNP_PDO_EXTENSION),
731                             NULL,
732                             FILE_DEVICE_CONTROLLER,
733                             FILE_DEVICE_SECURE_OPEN,
734                             FALSE,
735                             &FdoExt->ReadPortPdo);
736     if (!NT_SUCCESS(Status))
737         return Status;
738 
739     PdoExt = FdoExt->ReadPortPdo->DeviceExtension;
740     RtlZeroMemory(PdoExt, sizeof(ISAPNP_PDO_EXTENSION));
741     PdoExt->Common.IsFdo = FALSE;
742     PdoExt->Common.Self = FdoExt->ReadPortPdo;
743     PdoExt->Common.State = dsStopped;
744     PdoExt->FdoExt = FdoExt;
745 
746     Status = IsaPnpDuplicateUnicodeString(0,
747                                           &DeviceID,
748                                           &PdoExt->DeviceID);
749     if (!NT_SUCCESS(Status))
750         return Status;
751 
752     Status = IsaPnpDuplicateUnicodeString(0,
753                                           &HardwareIDs,
754                                           &PdoExt->HardwareIDs);
755     if (!NT_SUCCESS(Status))
756         return Status;
757 
758     Status = IsaPnpDuplicateUnicodeString(0,
759                                           &CompatibleIDs,
760                                           &PdoExt->CompatibleIDs);
761     if (!NT_SUCCESS(Status))
762         return Status;
763 
764     Status = IsaPnpDuplicateUnicodeString(0,
765                                           &InstanceID,
766                                           &PdoExt->InstanceID);
767     if (!NT_SUCCESS(Status))
768         return Status;
769 
770     Status = IsaPnpCreateReadPortDORequirements(PdoExt);
771     if (!NT_SUCCESS(Status))
772         return Status;
773 
774     Status = IsaPnpCreateReadPortDOResources(PdoExt);
775     if (!NT_SUCCESS(Status))
776         return Status;
777 
778     return Status;
779 }
780 
781 static
782 NTSTATUS
783 NTAPI
784 IsaAddDevice(
785     _In_ PDRIVER_OBJECT DriverObject,
786     _In_ PDEVICE_OBJECT PhysicalDeviceObject)
787 {
788     PDEVICE_OBJECT Fdo;
789     PISAPNP_FDO_EXTENSION FdoExt;
790     NTSTATUS Status;
791 
792     DPRINT("%s(%p, %p)\n", __FUNCTION__, DriverObject, PhysicalDeviceObject);
793 
794     Status = IoCreateDevice(DriverObject,
795                             sizeof(*FdoExt),
796                             NULL,
797                             FILE_DEVICE_BUS_EXTENDER,
798                             FILE_DEVICE_SECURE_OPEN,
799                             TRUE,
800                             &Fdo);
801     if (!NT_SUCCESS(Status))
802     {
803         DPRINT1("Failed to create FDO (0x%08lx)\n", Status);
804         return Status;
805     }
806 
807     FdoExt = Fdo->DeviceExtension;
808     RtlZeroMemory(FdoExt, sizeof(*FdoExt));
809 
810     FdoExt->Common.Self = Fdo;
811     FdoExt->Common.IsFdo = TRUE;
812     FdoExt->Common.State = dsStopped;
813     FdoExt->DriverObject = DriverObject;
814     FdoExt->Pdo = PhysicalDeviceObject;
815     FdoExt->Ldo = IoAttachDeviceToDeviceStack(Fdo,
816                                               PhysicalDeviceObject);
817 
818     InitializeListHead(&FdoExt->DeviceListHead);
819     KeInitializeSpinLock(&FdoExt->Lock);
820 
821     Status = IsaPnpCreateReadPortDO(FdoExt);
822     if (!NT_SUCCESS(Status))
823         return Status;
824 
825     Fdo->Flags &= ~DO_DEVICE_INITIALIZING;
826     FdoExt->ReadPortPdo->Flags &= ~DO_DEVICE_INITIALIZING;
827 
828     return STATUS_SUCCESS;
829 }
830 
831 _Dispatch_type_(IRP_MJ_POWER)
832 DRIVER_DISPATCH IsaPower;
833 
834 NTSTATUS
835 NTAPI
836 IsaPower(
837     _In_ PDEVICE_OBJECT DeviceObject,
838     _Inout_ PIRP Irp)
839 {
840     PISAPNP_COMMON_EXTENSION DevExt = DeviceObject->DeviceExtension;
841     NTSTATUS Status;
842 
843     if (!DevExt->IsFdo)
844     {
845         Status = Irp->IoStatus.Status;
846         IoCompleteRequest(Irp, IO_NO_INCREMENT);
847         return Status;
848     }
849 
850     PoStartNextPowerIrp(Irp);
851     IoSkipCurrentIrpStackLocation(Irp);
852     return PoCallDriver(((PISAPNP_FDO_EXTENSION)DevExt)->Ldo, Irp);
853 }
854 
855 _Dispatch_type_(IRP_MJ_PNP)
856 static DRIVER_DISPATCH IsaPnp;
857 
858 static
859 NTSTATUS
860 NTAPI
861 IsaPnp(
862     _In_ PDEVICE_OBJECT DeviceObject,
863     _Inout_ PIRP Irp)
864 {
865     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
866     PISAPNP_COMMON_EXTENSION DevExt = DeviceObject->DeviceExtension;
867 
868     DPRINT("%s(%p, %p)\n", __FUNCTION__, DeviceObject, Irp);
869 
870     if (DevExt->IsFdo)
871         return IsaFdoPnp((PISAPNP_FDO_EXTENSION)DevExt, Irp, IrpSp);
872     else
873         return IsaPdoPnp((PISAPNP_PDO_EXTENSION)DevExt, Irp, IrpSp);
874 }
875 
876 NTSTATUS
877 NTAPI
878 DriverEntry(
879     _In_ PDRIVER_OBJECT DriverObject,
880     _In_ PUNICODE_STRING RegistryPath)
881 {
882     DPRINT("%s(%p, %wZ)\n", __FUNCTION__, DriverObject, RegistryPath);
883 
884     DriverObject->MajorFunction[IRP_MJ_CREATE] = IsaCreateClose;
885     DriverObject->MajorFunction[IRP_MJ_CLOSE] = IsaCreateClose;
886     DriverObject->MajorFunction[IRP_MJ_READ] = IsaReadWrite;
887     DriverObject->MajorFunction[IRP_MJ_WRITE] = IsaReadWrite;
888     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IsaIoctl;
889     DriverObject->MajorFunction[IRP_MJ_PNP] = IsaPnp;
890     DriverObject->MajorFunction[IRP_MJ_POWER] = IsaPower;
891     DriverObject->DriverExtension->AddDevice = IsaAddDevice;
892 
893     return STATUS_SUCCESS;
894 }
895 
896 /* EOF */
897