xref: /reactos/drivers/bus/pci/fdo.c (revision 53221834)
1 /*
2  * PROJECT:         ReactOS PCI bus driver
3  * FILE:            fdo.c
4  * PURPOSE:         PCI device object dispatch routines
5  * PROGRAMMERS:     Casper S. Hornstrup (chorns@users.sourceforge.net)
6  * UPDATE HISTORY:
7  *      10-09-2001  CSH  Created
8  */
9 
10 #include "pci.h"
11 
12 #define NDEBUG
13 #include <debug.h>
14 
15 /*** PRIVATE *****************************************************************/
16 
17 static IO_COMPLETION_ROUTINE ForwardIrpAndWaitCompletion;
18 
19 static NTSTATUS NTAPI
20 ForwardIrpAndWaitCompletion(
21     IN PDEVICE_OBJECT DeviceObject,
22     IN PIRP Irp,
23     IN PVOID Context)
24 {
25     UNREFERENCED_PARAMETER(DeviceObject);
26     if (Irp->PendingReturned)
27         KeSetEvent((PKEVENT)Context, IO_NO_INCREMENT, FALSE);
28     return STATUS_MORE_PROCESSING_REQUIRED;
29 }
30 
31 NTSTATUS NTAPI
32 ForwardIrpAndWait(
33     IN PDEVICE_OBJECT DeviceObject,
34     IN PIRP Irp)
35 {
36     KEVENT Event;
37     NTSTATUS Status;
38     PDEVICE_OBJECT LowerDevice = ((PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->Ldo;
39     ASSERT(LowerDevice);
40 
41     KeInitializeEvent(&Event, NotificationEvent, FALSE);
42     IoCopyCurrentIrpStackLocationToNext(Irp);
43 
44     IoSetCompletionRoutine(Irp, ForwardIrpAndWaitCompletion, &Event, TRUE, TRUE, TRUE);
45 
46     Status = IoCallDriver(LowerDevice, Irp);
47     if (Status == STATUS_PENDING)
48     {
49         Status = KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
50         if (NT_SUCCESS(Status))
51             Status = Irp->IoStatus.Status;
52     }
53 
54     return Status;
55 }
56 
57 static NTSTATUS
58 FdoLocateChildDevice(
59     PPCI_DEVICE *Device,
60     PFDO_DEVICE_EXTENSION DeviceExtension,
61     PCI_SLOT_NUMBER SlotNumber,
62     PPCI_COMMON_CONFIG PciConfig)
63 {
64     PLIST_ENTRY CurrentEntry;
65     PPCI_DEVICE CurrentDevice;
66 
67     DPRINT("Called\n");
68 
69     CurrentEntry = DeviceExtension->DeviceListHead.Flink;
70     while (CurrentEntry != &DeviceExtension->DeviceListHead)
71     {
72         CurrentDevice = CONTAINING_RECORD(CurrentEntry, PCI_DEVICE, ListEntry);
73 
74         /* If both vendor ID and device ID match, it is the same device */
75         if ((PciConfig->VendorID == CurrentDevice->PciConfig.VendorID) &&
76             (PciConfig->DeviceID == CurrentDevice->PciConfig.DeviceID) &&
77             (SlotNumber.u.AsULONG == CurrentDevice->SlotNumber.u.AsULONG))
78         {
79             *Device = CurrentDevice;
80             DPRINT("Done\n");
81             return STATUS_SUCCESS;
82         }
83 
84         CurrentEntry = CurrentEntry->Flink;
85     }
86 
87     *Device = NULL;
88     DPRINT("Done\n");
89     return STATUS_UNSUCCESSFUL;
90 }
91 
92 
93 static NTSTATUS
94 FdoEnumerateDevices(
95     PDEVICE_OBJECT DeviceObject)
96 {
97     PFDO_DEVICE_EXTENSION DeviceExtension;
98     PCI_COMMON_CONFIG PciConfig;
99     PPCI_DEVICE Device;
100     PCI_SLOT_NUMBER SlotNumber;
101     ULONG DeviceNumber;
102     ULONG FunctionNumber;
103     ULONG Size;
104     NTSTATUS Status;
105 
106     DPRINT("Called\n");
107 
108     DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
109 
110     DeviceExtension->DeviceListCount = 0;
111 
112     /* Enumerate devices on the PCI bus */
113     SlotNumber.u.AsULONG = 0;
114     for (DeviceNumber = 0; DeviceNumber < PCI_MAX_DEVICES; DeviceNumber++)
115     {
116         SlotNumber.u.bits.DeviceNumber = DeviceNumber;
117         for (FunctionNumber = 0; FunctionNumber < PCI_MAX_FUNCTION; FunctionNumber++)
118         {
119             SlotNumber.u.bits.FunctionNumber = FunctionNumber;
120 
121             DPRINT("Bus %1lu  Device %2lu  Func %1lu\n",
122                    DeviceExtension->BusNumber,
123                    DeviceNumber,
124                    FunctionNumber);
125 
126             RtlZeroMemory(&PciConfig,
127                           sizeof(PCI_COMMON_CONFIG));
128 
129             Size = HalGetBusData(PCIConfiguration,
130                                  DeviceExtension->BusNumber,
131                                  SlotNumber.u.AsULONG,
132                                  &PciConfig,
133                                  PCI_COMMON_HDR_LENGTH);
134             DPRINT("Size %lu\n", Size);
135             if (Size < PCI_COMMON_HDR_LENGTH)
136             {
137                 if (FunctionNumber == 0)
138                 {
139                     break;
140                 }
141                 else
142                 {
143                     continue;
144                 }
145             }
146 
147             DPRINT("Bus %1lu  Device %2lu  Func %1lu  VenID 0x%04hx  DevID 0x%04hx\n",
148                    DeviceExtension->BusNumber,
149                    DeviceNumber,
150                    FunctionNumber,
151                    PciConfig.VendorID,
152                    PciConfig.DeviceID);
153 
154             if (PciConfig.VendorID == 0 && PciConfig.DeviceID == 0)
155             {
156                 DPRINT("Filter out devices with null vendor and device ID\n");
157                 continue;
158             }
159 
160             Status = FdoLocateChildDevice(&Device, DeviceExtension, SlotNumber, &PciConfig);
161             if (!NT_SUCCESS(Status))
162             {
163                 Device = ExAllocatePoolWithTag(NonPagedPool, sizeof(PCI_DEVICE), TAG_PCI);
164                 if (!Device)
165                 {
166                     /* FIXME: Cleanup resources for already discovered devices */
167                     return STATUS_INSUFFICIENT_RESOURCES;
168                 }
169 
170                 RtlZeroMemory(Device,
171                               sizeof(PCI_DEVICE));
172 
173                 Device->BusNumber = DeviceExtension->BusNumber;
174 
175                 RtlCopyMemory(&Device->SlotNumber,
176                               &SlotNumber,
177                               sizeof(PCI_SLOT_NUMBER));
178 
179                 RtlCopyMemory(&Device->PciConfig,
180                               &PciConfig,
181                               sizeof(PCI_COMMON_CONFIG));
182 
183                 ExInterlockedInsertTailList(
184                     &DeviceExtension->DeviceListHead,
185                     &Device->ListEntry,
186                     &DeviceExtension->DeviceListLock);
187             }
188 
189             DeviceExtension->DeviceListCount++;
190 
191             /* Skip to next device if the current one is not a multifunction device */
192             if ((FunctionNumber == 0) &&
193                 ((PciConfig.HeaderType & 0x80) == 0))
194             {
195                 break;
196             }
197         }
198     }
199 
200     DPRINT("Done\n");
201 
202     return STATUS_SUCCESS;
203 }
204 
205 
206 static NTSTATUS
207 FdoQueryBusRelations(
208     IN PDEVICE_OBJECT DeviceObject,
209     IN PIRP Irp,
210     PIO_STACK_LOCATION IrpSp)
211 {
212     PPDO_DEVICE_EXTENSION PdoDeviceExtension = NULL;
213     PFDO_DEVICE_EXTENSION DeviceExtension;
214     PDEVICE_RELATIONS Relations;
215     PLIST_ENTRY CurrentEntry;
216     PPCI_DEVICE Device;
217     NTSTATUS Status;
218     BOOLEAN ErrorOccurred;
219     NTSTATUS ErrorStatus;
220     ULONG Size;
221     ULONG i;
222 
223     UNREFERENCED_PARAMETER(IrpSp);
224 
225     DPRINT("Called\n");
226 
227     ErrorStatus = STATUS_INSUFFICIENT_RESOURCES;
228 
229     Status = STATUS_SUCCESS;
230 
231     ErrorOccurred = FALSE;
232 
233     FdoEnumerateDevices(DeviceObject);
234 
235     DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
236 
237     if (Irp->IoStatus.Information)
238     {
239         /* FIXME: Another bus driver has already created a DEVICE_RELATIONS
240                   structure so we must merge this structure with our own */
241         DPRINT1("FIXME: leaking old bus relations\n");
242     }
243 
244     Size = sizeof(DEVICE_RELATIONS) +
245            sizeof(Relations->Objects) * (DeviceExtension->DeviceListCount - 1);
246     Relations = ExAllocatePoolWithTag(PagedPool, Size, TAG_PCI);
247     if (!Relations)
248         return STATUS_INSUFFICIENT_RESOURCES;
249 
250     Relations->Count = DeviceExtension->DeviceListCount;
251 
252     i = 0;
253     CurrentEntry = DeviceExtension->DeviceListHead.Flink;
254     while (CurrentEntry != &DeviceExtension->DeviceListHead)
255     {
256         Device = CONTAINING_RECORD(CurrentEntry, PCI_DEVICE, ListEntry);
257 
258         PdoDeviceExtension = NULL;
259 
260         if (!Device->Pdo)
261         {
262             /* Create a physical device object for the
263                device as it does not already have one */
264             Status = IoCreateDevice(DeviceObject->DriverObject,
265                                     sizeof(PDO_DEVICE_EXTENSION),
266                                     NULL,
267                                     FILE_DEVICE_CONTROLLER,
268                                     FILE_AUTOGENERATED_DEVICE_NAME,
269                                     FALSE,
270                                     &Device->Pdo);
271             if (!NT_SUCCESS(Status))
272             {
273                 DPRINT("IoCreateDevice() failed with status 0x%X\n", Status);
274                 ErrorStatus = Status;
275                 ErrorOccurred = TRUE;
276                 break;
277             }
278 
279             Device->Pdo->Flags &= ~DO_DEVICE_INITIALIZING;
280 
281             //Device->Pdo->Flags |= DO_POWER_PAGABLE;
282 
283             PdoDeviceExtension = (PPDO_DEVICE_EXTENSION)Device->Pdo->DeviceExtension;
284 
285             RtlZeroMemory(PdoDeviceExtension, sizeof(PDO_DEVICE_EXTENSION));
286 
287             PdoDeviceExtension->Common.IsFDO = FALSE;
288 
289             PdoDeviceExtension->Common.DeviceObject = Device->Pdo;
290 
291             PdoDeviceExtension->Common.DevicePowerState = PowerDeviceD0;
292 
293             PdoDeviceExtension->Fdo = DeviceObject;
294 
295             PdoDeviceExtension->PciDevice = Device;
296 
297             /* Add Device ID string */
298             Status = PciCreateDeviceIDString(&PdoDeviceExtension->DeviceID, Device);
299             if (!NT_SUCCESS(Status))
300             {
301                 ErrorStatus = Status;
302                 ErrorOccurred = TRUE;
303                 break;
304             }
305 
306             DPRINT("DeviceID: %S\n", PdoDeviceExtension->DeviceID.Buffer);
307 
308             /* Add Instance ID string */
309             Status = PciCreateInstanceIDString(&PdoDeviceExtension->InstanceID, Device);
310             if (!NT_SUCCESS(Status))
311             {
312                 ErrorStatus = Status;
313                 ErrorOccurred = TRUE;
314                 break;
315             }
316 
317             /* Add Hardware IDs string */
318             Status = PciCreateHardwareIDsString(&PdoDeviceExtension->HardwareIDs, Device);
319             if (!NT_SUCCESS(Status))
320             {
321                 ErrorStatus = Status;
322                 ErrorOccurred = TRUE;
323                 break;
324             }
325 
326             /* Add Compatible IDs string */
327             Status = PciCreateCompatibleIDsString(&PdoDeviceExtension->CompatibleIDs, Device);
328             if (!NT_SUCCESS(Status))
329             {
330                 ErrorStatus = Status;
331                 ErrorOccurred = TRUE;
332                 break;
333             }
334 
335              /* Add device description string */
336             Status = PciCreateDeviceDescriptionString(&PdoDeviceExtension->DeviceDescription, Device);
337             if (!NT_SUCCESS(Status))
338             {
339                 ErrorStatus = Status;
340                 ErrorOccurred = TRUE;
341                 break;
342             }
343 
344             /* Add device location string */
345             Status = PciCreateDeviceLocationString(&PdoDeviceExtension->DeviceLocation, Device);
346             if (!NT_SUCCESS(Status))
347             {
348                 ErrorStatus = Status;
349                 ErrorOccurred = TRUE;
350                 break;
351             }
352         }
353 
354         /* Reference the physical device object. The PnP manager
355            will dereference it again when it is no longer needed */
356         ObReferenceObject(Device->Pdo);
357 
358         Relations->Objects[i] = Device->Pdo;
359 
360         i++;
361 
362         CurrentEntry = CurrentEntry->Flink;
363     }
364 
365     if (ErrorOccurred)
366     {
367         /* FIXME: Cleanup all new PDOs created in this call. Please give me SEH!!! ;-) */
368         /* FIXME: Should IoAttachDeviceToDeviceStack() be undone? */
369         if (PdoDeviceExtension)
370         {
371             RtlFreeUnicodeString(&PdoDeviceExtension->DeviceID);
372             RtlFreeUnicodeString(&PdoDeviceExtension->InstanceID);
373             RtlFreeUnicodeString(&PdoDeviceExtension->HardwareIDs);
374             RtlFreeUnicodeString(&PdoDeviceExtension->CompatibleIDs);
375             RtlFreeUnicodeString(&PdoDeviceExtension->DeviceDescription);
376             RtlFreeUnicodeString(&PdoDeviceExtension->DeviceLocation);
377         }
378 
379         ExFreePoolWithTag(Relations, TAG_PCI);
380         return ErrorStatus;
381     }
382 
383     Irp->IoStatus.Information = (ULONG_PTR)Relations;
384 
385     DPRINT("Done\n");
386 
387     return Status;
388 }
389 
390 
391 static NTSTATUS
392 FdoStartDevice(
393     IN PDEVICE_OBJECT DeviceObject,
394     IN PIRP Irp)
395 {
396     PFDO_DEVICE_EXTENSION DeviceExtension;
397     PCM_RESOURCE_LIST AllocatedResources;
398     PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor;
399     ULONG FoundBusNumber = FALSE;
400     ULONG i;
401 
402     DPRINT("Called\n");
403 
404     DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
405 
406     AllocatedResources = IoGetCurrentIrpStackLocation(Irp)->Parameters.StartDevice.AllocatedResources;
407     if (!AllocatedResources)
408     {
409         DPRINT("No allocated resources sent to driver\n");
410         return STATUS_INSUFFICIENT_RESOURCES;
411     }
412 
413     if (AllocatedResources->Count < 1)
414     {
415         DPRINT("Not enough allocated resources sent to driver\n");
416         return STATUS_INSUFFICIENT_RESOURCES;
417     }
418 
419     if (AllocatedResources->List[0].PartialResourceList.Version != 1 ||
420         AllocatedResources->List[0].PartialResourceList.Revision != 1)
421         return STATUS_REVISION_MISMATCH;
422 
423     ASSERT(DeviceExtension->State == dsStopped);
424 
425     /* By default, use the bus number in the resource list header */
426     DeviceExtension->BusNumber = AllocatedResources->List[0].BusNumber;
427 
428     for (i = 0; i < AllocatedResources->List[0].PartialResourceList.Count; i++)
429     {
430         ResourceDescriptor = &AllocatedResources->List[0].PartialResourceList.PartialDescriptors[i];
431         switch (ResourceDescriptor->Type)
432         {
433             case CmResourceTypeBusNumber:
434                 if (FoundBusNumber || ResourceDescriptor->u.BusNumber.Length < 1)
435                     return STATUS_INVALID_PARAMETER;
436 
437                 /* Use this one instead */
438                 ASSERT(AllocatedResources->List[0].BusNumber == ResourceDescriptor->u.BusNumber.Start);
439                 DeviceExtension->BusNumber = ResourceDescriptor->u.BusNumber.Start;
440                 DPRINT("Found bus number resource: %lu\n", DeviceExtension->BusNumber);
441                 FoundBusNumber = TRUE;
442                 break;
443 
444             default:
445                 DPRINT("Unknown resource descriptor type 0x%x\n", ResourceDescriptor->Type);
446         }
447     }
448 
449     InitializeListHead(&DeviceExtension->DeviceListHead);
450     KeInitializeSpinLock(&DeviceExtension->DeviceListLock);
451     DeviceExtension->DeviceListCount = 0;
452     DeviceExtension->State = dsStarted;
453 
454     ExInterlockedInsertTailList(
455         &DriverExtension->BusListHead,
456         &DeviceExtension->ListEntry,
457         &DriverExtension->BusListLock);
458 
459   Irp->IoStatus.Information = 0;
460 
461   return STATUS_SUCCESS;
462 }
463 
464 
465 /*** PUBLIC ******************************************************************/
466 
467 NTSTATUS
468 FdoPnpControl(
469     PDEVICE_OBJECT DeviceObject,
470     PIRP Irp)
471 /*
472  * FUNCTION: Handle Plug and Play IRPs for the PCI device object
473  * ARGUMENTS:
474  *     DeviceObject = Pointer to functional device object of the PCI driver
475  *     Irp          = Pointer to IRP that should be handled
476  * RETURNS:
477  *     Status
478  */
479 {
480     PFDO_DEVICE_EXTENSION DeviceExtension;
481     PIO_STACK_LOCATION IrpSp;
482     NTSTATUS Status = Irp->IoStatus.Status;
483 
484     DPRINT("Called\n");
485 
486     DeviceExtension = DeviceObject->DeviceExtension;
487 
488     IrpSp = IoGetCurrentIrpStackLocation(Irp);
489     switch (IrpSp->MinorFunction)
490     {
491 #if 0
492         case IRP_MN_CANCEL_REMOVE_DEVICE:
493             Status = STATUS_NOT_IMPLEMENTED;
494             break;
495 
496         case IRP_MN_CANCEL_STOP_DEVICE:
497             Status = STATUS_NOT_IMPLEMENTED;
498             break;
499 
500         case IRP_MN_DEVICE_USAGE_NOTIFICATION:
501             Status = STATUS_NOT_IMPLEMENTED;
502             break;
503 
504         case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
505             Status = STATUS_NOT_IMPLEMENTED;
506             break;
507 #endif
508         case IRP_MN_QUERY_DEVICE_RELATIONS:
509             if (IrpSp->Parameters.QueryDeviceRelations.Type != BusRelations)
510                 break;
511 
512             Status = FdoQueryBusRelations(DeviceObject, Irp, IrpSp);
513             Irp->IoStatus.Status = Status;
514             IoCompleteRequest(Irp, IO_NO_INCREMENT);
515             return Status;
516 #if 0
517         case IRP_MN_QUERY_PNP_DEVICE_STATE:
518             Status = STATUS_NOT_IMPLEMENTED;
519             break;
520 
521         case IRP_MN_QUERY_REMOVE_DEVICE:
522             Status = STATUS_NOT_IMPLEMENTED;
523             break;
524 #endif
525         case IRP_MN_START_DEVICE:
526             DPRINT("IRP_MN_START_DEVICE received\n");
527             Status = ForwardIrpAndWait(DeviceObject, Irp);
528             if (NT_SUCCESS(Status))
529                 Status = FdoStartDevice(DeviceObject, Irp);
530 
531             Irp->IoStatus.Status = Status;
532             IoCompleteRequest(Irp, IO_NO_INCREMENT);
533             return Status;
534 
535         case IRP_MN_QUERY_STOP_DEVICE:
536             /* We don't support stopping yet */
537             Status = STATUS_UNSUCCESSFUL;
538             Irp->IoStatus.Status = Status;
539             IoCompleteRequest(Irp, IO_NO_INCREMENT);
540             return Status;
541 
542         case IRP_MN_STOP_DEVICE:
543             /* We can't fail this one so we fail the QUERY_STOP request that precedes it */
544             break;
545 #if 0
546         case IRP_MN_SURPRISE_REMOVAL:
547             Status = STATUS_NOT_IMPLEMENTED;
548             break;
549 #endif
550 
551         case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
552             break;
553 
554         case IRP_MN_REMOVE_DEVICE:
555             /* Detach the device object from the device stack */
556             IoDetachDevice(DeviceExtension->Ldo);
557 
558             /* Delete the device object */
559             IoDeleteDevice(DeviceObject);
560 
561             /* Return success */
562             Status = STATUS_SUCCESS;
563             break;
564 
565         case IRP_MN_QUERY_CAPABILITIES:
566         case IRP_MN_QUERY_PNP_DEVICE_STATE:
567             /* Don't print the warning, too much noise */
568             break;
569 
570         default:
571             DPRINT1("Unknown PNP minor function 0x%x\n", IrpSp->MinorFunction);
572             break;
573     }
574 
575     Irp->IoStatus.Status = Status;
576     IoSkipCurrentIrpStackLocation(Irp);
577     Status = IoCallDriver(DeviceExtension->Ldo, Irp);
578 
579     DPRINT("Leaving. Status 0x%lx\n", Status);
580 
581     return Status;
582 }
583 
584 
585 NTSTATUS
586 FdoPowerControl(
587     PDEVICE_OBJECT DeviceObject,
588     PIRP Irp)
589 /*
590  * FUNCTION: Handle power management IRPs for the PCI device object
591  * ARGUMENTS:
592  *     DeviceObject = Pointer to functional device object of the PCI driver
593  *     Irp          = Pointer to IRP that should be handled
594  * RETURNS:
595  *     Status
596  */
597 {
598     PFDO_DEVICE_EXTENSION DeviceExtension;
599     NTSTATUS Status;
600 
601     DPRINT("Called\n");
602 
603     DeviceExtension = DeviceObject->DeviceExtension;
604 
605     PoStartNextPowerIrp(Irp);
606     IoSkipCurrentIrpStackLocation(Irp);
607     Status = PoCallDriver(DeviceExtension->Ldo, Irp);
608 
609     DPRINT("Leaving. Status 0x%X\n", Status);
610 
611     return Status;
612 }
613 
614 /* EOF */
615