xref: /reactos/drivers/bus/pci/fdo.c (revision 4561998a)
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             Status = FdoLocateChildDevice(&Device, DeviceExtension, SlotNumber, &PciConfig);
155             if (!NT_SUCCESS(Status))
156             {
157                 Device = ExAllocatePoolWithTag(NonPagedPool, sizeof(PCI_DEVICE), TAG_PCI);
158                 if (!Device)
159                 {
160                     /* FIXME: Cleanup resources for already discovered devices */
161                     return STATUS_INSUFFICIENT_RESOURCES;
162                 }
163 
164                 RtlZeroMemory(Device,
165                               sizeof(PCI_DEVICE));
166 
167                 Device->BusNumber = DeviceExtension->BusNumber;
168 
169                 RtlCopyMemory(&Device->SlotNumber,
170                               &SlotNumber,
171                               sizeof(PCI_SLOT_NUMBER));
172 
173                 RtlCopyMemory(&Device->PciConfig,
174                               &PciConfig,
175                               sizeof(PCI_COMMON_CONFIG));
176 
177                 ExInterlockedInsertTailList(
178                     &DeviceExtension->DeviceListHead,
179                     &Device->ListEntry,
180                     &DeviceExtension->DeviceListLock);
181             }
182 
183             DeviceExtension->DeviceListCount++;
184 
185             /* Skip to next device if the current one is not a multifunction device */
186             if ((FunctionNumber == 0) &&
187                 ((PciConfig.HeaderType & 0x80) == 0))
188             {
189                 break;
190             }
191         }
192     }
193 
194     DPRINT("Done\n");
195 
196     return STATUS_SUCCESS;
197 }
198 
199 
200 static NTSTATUS
201 FdoQueryBusRelations(
202     IN PDEVICE_OBJECT DeviceObject,
203     IN PIRP Irp,
204     PIO_STACK_LOCATION IrpSp)
205 {
206     PPDO_DEVICE_EXTENSION PdoDeviceExtension = NULL;
207     PFDO_DEVICE_EXTENSION DeviceExtension;
208     PDEVICE_RELATIONS Relations;
209     PLIST_ENTRY CurrentEntry;
210     PPCI_DEVICE Device;
211     NTSTATUS Status;
212     BOOLEAN ErrorOccurred;
213     NTSTATUS ErrorStatus;
214     ULONG Size;
215     ULONG i;
216 
217     UNREFERENCED_PARAMETER(IrpSp);
218 
219     DPRINT("Called\n");
220 
221     ErrorStatus = STATUS_INSUFFICIENT_RESOURCES;
222 
223     Status = STATUS_SUCCESS;
224 
225     ErrorOccurred = FALSE;
226 
227     FdoEnumerateDevices(DeviceObject);
228 
229     DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
230 
231     if (Irp->IoStatus.Information)
232     {
233         /* FIXME: Another bus driver has already created a DEVICE_RELATIONS
234                   structure so we must merge this structure with our own */
235         DPRINT1("FIXME: leaking old bus relations\n");
236     }
237 
238     Size = sizeof(DEVICE_RELATIONS) +
239            sizeof(Relations->Objects) * (DeviceExtension->DeviceListCount - 1);
240     Relations = ExAllocatePoolWithTag(PagedPool, Size, TAG_PCI);
241     if (!Relations)
242         return STATUS_INSUFFICIENT_RESOURCES;
243 
244     Relations->Count = DeviceExtension->DeviceListCount;
245 
246     i = 0;
247     CurrentEntry = DeviceExtension->DeviceListHead.Flink;
248     while (CurrentEntry != &DeviceExtension->DeviceListHead)
249     {
250         Device = CONTAINING_RECORD(CurrentEntry, PCI_DEVICE, ListEntry);
251 
252         PdoDeviceExtension = NULL;
253 
254         if (!Device->Pdo)
255         {
256             /* Create a physical device object for the
257                device as it does not already have one */
258             Status = IoCreateDevice(DeviceObject->DriverObject,
259                                     sizeof(PDO_DEVICE_EXTENSION),
260                                     NULL,
261                                     FILE_DEVICE_CONTROLLER,
262                                     FILE_AUTOGENERATED_DEVICE_NAME,
263                                     FALSE,
264                                     &Device->Pdo);
265             if (!NT_SUCCESS(Status))
266             {
267                 DPRINT("IoCreateDevice() failed with status 0x%X\n", Status);
268                 ErrorStatus = Status;
269                 ErrorOccurred = TRUE;
270                 break;
271             }
272 
273             Device->Pdo->Flags &= ~DO_DEVICE_INITIALIZING;
274 
275             //Device->Pdo->Flags |= DO_POWER_PAGABLE;
276 
277             PdoDeviceExtension = (PPDO_DEVICE_EXTENSION)Device->Pdo->DeviceExtension;
278 
279             RtlZeroMemory(PdoDeviceExtension, sizeof(PDO_DEVICE_EXTENSION));
280 
281             PdoDeviceExtension->Common.IsFDO = FALSE;
282 
283             PdoDeviceExtension->Common.DeviceObject = Device->Pdo;
284 
285             PdoDeviceExtension->Common.DevicePowerState = PowerDeviceD0;
286 
287             PdoDeviceExtension->Fdo = DeviceObject;
288 
289             PdoDeviceExtension->PciDevice = Device;
290 
291             /* Add Device ID string */
292             Status = PciCreateDeviceIDString(&PdoDeviceExtension->DeviceID, Device);
293             if (!NT_SUCCESS(Status))
294             {
295                 ErrorStatus = Status;
296                 ErrorOccurred = TRUE;
297                 break;
298             }
299 
300             DPRINT("DeviceID: %S\n", PdoDeviceExtension->DeviceID.Buffer);
301 
302             /* Add Instance ID string */
303             Status = PciCreateInstanceIDString(&PdoDeviceExtension->InstanceID, Device);
304             if (!NT_SUCCESS(Status))
305             {
306                 ErrorStatus = Status;
307                 ErrorOccurred = TRUE;
308                 break;
309             }
310 
311             /* Add Hardware IDs string */
312             Status = PciCreateHardwareIDsString(&PdoDeviceExtension->HardwareIDs, Device);
313             if (!NT_SUCCESS(Status))
314             {
315                 ErrorStatus = Status;
316                 ErrorOccurred = TRUE;
317                 break;
318             }
319 
320             /* Add Compatible IDs string */
321             Status = PciCreateCompatibleIDsString(&PdoDeviceExtension->CompatibleIDs, Device);
322             if (!NT_SUCCESS(Status))
323             {
324                 ErrorStatus = Status;
325                 ErrorOccurred = TRUE;
326                 break;
327             }
328 
329              /* Add device description string */
330             Status = PciCreateDeviceDescriptionString(&PdoDeviceExtension->DeviceDescription, Device);
331             if (!NT_SUCCESS(Status))
332             {
333                 ErrorStatus = Status;
334                 ErrorOccurred = TRUE;
335                 break;
336             }
337 
338             /* Add device location string */
339             Status = PciCreateDeviceLocationString(&PdoDeviceExtension->DeviceLocation, Device);
340             if (!NT_SUCCESS(Status))
341             {
342                 ErrorStatus = Status;
343                 ErrorOccurred = TRUE;
344                 break;
345             }
346         }
347 
348         /* Reference the physical device object. The PnP manager
349            will dereference it again when it is no longer needed */
350         ObReferenceObject(Device->Pdo);
351 
352         Relations->Objects[i] = Device->Pdo;
353 
354         i++;
355 
356         CurrentEntry = CurrentEntry->Flink;
357     }
358 
359     if (ErrorOccurred)
360     {
361         /* FIXME: Cleanup all new PDOs created in this call. Please give me SEH!!! ;-) */
362         /* FIXME: Should IoAttachDeviceToDeviceStack() be undone? */
363         if (PdoDeviceExtension)
364         {
365             RtlFreeUnicodeString(&PdoDeviceExtension->DeviceID);
366             RtlFreeUnicodeString(&PdoDeviceExtension->InstanceID);
367             RtlFreeUnicodeString(&PdoDeviceExtension->HardwareIDs);
368             RtlFreeUnicodeString(&PdoDeviceExtension->CompatibleIDs);
369             RtlFreeUnicodeString(&PdoDeviceExtension->DeviceDescription);
370             RtlFreeUnicodeString(&PdoDeviceExtension->DeviceLocation);
371         }
372 
373         ExFreePoolWithTag(Relations, TAG_PCI);
374         return ErrorStatus;
375     }
376 
377     Irp->IoStatus.Information = (ULONG_PTR)Relations;
378 
379     DPRINT("Done\n");
380 
381     return Status;
382 }
383 
384 
385 static NTSTATUS
386 FdoStartDevice(
387     IN PDEVICE_OBJECT DeviceObject,
388     IN PIRP Irp)
389 {
390     PFDO_DEVICE_EXTENSION DeviceExtension;
391     PCM_RESOURCE_LIST AllocatedResources;
392     PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor;
393     ULONG FoundBusNumber = FALSE;
394     ULONG i;
395 
396     DPRINT("Called\n");
397 
398     DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
399 
400     AllocatedResources = IoGetCurrentIrpStackLocation(Irp)->Parameters.StartDevice.AllocatedResources;
401     if (!AllocatedResources)
402     {
403         DPRINT("No allocated resources sent to driver\n");
404         return STATUS_INSUFFICIENT_RESOURCES;
405     }
406 
407     if (AllocatedResources->Count < 1)
408     {
409         DPRINT("Not enough allocated resources sent to driver\n");
410         return STATUS_INSUFFICIENT_RESOURCES;
411     }
412 
413     if (AllocatedResources->List[0].PartialResourceList.Version != 1 ||
414         AllocatedResources->List[0].PartialResourceList.Revision != 1)
415         return STATUS_REVISION_MISMATCH;
416 
417     ASSERT(DeviceExtension->State == dsStopped);
418 
419     /* By default, use the bus number in the resource list header */
420     DeviceExtension->BusNumber = AllocatedResources->List[0].BusNumber;
421 
422     for (i = 0; i < AllocatedResources->List[0].PartialResourceList.Count; i++)
423     {
424         ResourceDescriptor = &AllocatedResources->List[0].PartialResourceList.PartialDescriptors[i];
425         switch (ResourceDescriptor->Type)
426         {
427             case CmResourceTypeBusNumber:
428                 if (FoundBusNumber || ResourceDescriptor->u.BusNumber.Length != 1)
429                     return STATUS_INVALID_PARAMETER;
430 
431                 /* Use this one instead */
432                 ASSERT(AllocatedResources->List[0].BusNumber == ResourceDescriptor->u.BusNumber.Start);
433                 DeviceExtension->BusNumber = ResourceDescriptor->u.BusNumber.Start;
434                 DPRINT("Found bus number resource: %lu\n", DeviceExtension->BusNumber);
435                 FoundBusNumber = TRUE;
436                 break;
437 
438             default:
439                 DPRINT("Unknown resource descriptor type 0x%x\n", ResourceDescriptor->Type);
440         }
441     }
442 
443     InitializeListHead(&DeviceExtension->DeviceListHead);
444     KeInitializeSpinLock(&DeviceExtension->DeviceListLock);
445     DeviceExtension->DeviceListCount = 0;
446     DeviceExtension->State = dsStarted;
447 
448     ExInterlockedInsertTailList(
449         &DriverExtension->BusListHead,
450         &DeviceExtension->ListEntry,
451         &DriverExtension->BusListLock);
452 
453   Irp->IoStatus.Information = 0;
454 
455   return STATUS_SUCCESS;
456 }
457 
458 
459 /*** PUBLIC ******************************************************************/
460 
461 NTSTATUS
462 FdoPnpControl(
463     PDEVICE_OBJECT DeviceObject,
464     PIRP Irp)
465 /*
466  * FUNCTION: Handle Plug and Play IRPs for the PCI device object
467  * ARGUMENTS:
468  *     DeviceObject = Pointer to functional device object of the PCI driver
469  *     Irp          = Pointer to IRP that should be handled
470  * RETURNS:
471  *     Status
472  */
473 {
474     PFDO_DEVICE_EXTENSION DeviceExtension;
475     PIO_STACK_LOCATION IrpSp;
476     NTSTATUS Status = Irp->IoStatus.Status;
477 
478     DPRINT("Called\n");
479 
480     DeviceExtension = DeviceObject->DeviceExtension;
481 
482     IrpSp = IoGetCurrentIrpStackLocation(Irp);
483     switch (IrpSp->MinorFunction)
484     {
485 #if 0
486         case IRP_MN_CANCEL_REMOVE_DEVICE:
487             Status = STATUS_NOT_IMPLEMENTED;
488             break;
489 
490         case IRP_MN_CANCEL_STOP_DEVICE:
491             Status = STATUS_NOT_IMPLEMENTED;
492             break;
493 
494         case IRP_MN_DEVICE_USAGE_NOTIFICATION:
495             Status = STATUS_NOT_IMPLEMENTED;
496             break;
497 
498         case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
499             Status = STATUS_NOT_IMPLEMENTED;
500             break;
501 #endif
502         case IRP_MN_QUERY_DEVICE_RELATIONS:
503             if (IrpSp->Parameters.QueryDeviceRelations.Type != BusRelations)
504                 break;
505 
506             Status = FdoQueryBusRelations(DeviceObject, Irp, IrpSp);
507             Irp->IoStatus.Status = Status;
508             IoCompleteRequest(Irp, IO_NO_INCREMENT);
509             return Status;
510 #if 0
511         case IRP_MN_QUERY_PNP_DEVICE_STATE:
512             Status = STATUS_NOT_IMPLEMENTED;
513             break;
514 
515         case IRP_MN_QUERY_REMOVE_DEVICE:
516             Status = STATUS_NOT_IMPLEMENTED;
517             break;
518 #endif
519         case IRP_MN_START_DEVICE:
520             DPRINT("IRP_MN_START_DEVICE received\n");
521             Status = ForwardIrpAndWait(DeviceObject, Irp);
522             if (NT_SUCCESS(Status))
523                 Status = FdoStartDevice(DeviceObject, Irp);
524 
525             Irp->IoStatus.Status = Status;
526             IoCompleteRequest(Irp, IO_NO_INCREMENT);
527             return Status;
528 
529         case IRP_MN_QUERY_STOP_DEVICE:
530             /* We don't support stopping yet */
531             Status = STATUS_UNSUCCESSFUL;
532             Irp->IoStatus.Status = Status;
533             IoCompleteRequest(Irp, IO_NO_INCREMENT);
534             return Status;
535 
536         case IRP_MN_STOP_DEVICE:
537             /* We can't fail this one so we fail the QUERY_STOP request that precedes it */
538             break;
539 #if 0
540         case IRP_MN_SURPRISE_REMOVAL:
541             Status = STATUS_NOT_IMPLEMENTED;
542             break;
543 #endif
544 
545         case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
546             break;
547 
548         case IRP_MN_REMOVE_DEVICE:
549             /* Detach the device object from the device stack */
550             IoDetachDevice(DeviceExtension->Ldo);
551 
552             /* Delete the device object */
553             IoDeleteDevice(DeviceObject);
554 
555             /* Return success */
556             Status = STATUS_SUCCESS;
557             break;
558 
559         case IRP_MN_QUERY_CAPABILITIES:
560         case IRP_MN_QUERY_PNP_DEVICE_STATE:
561             /* Don't print the warning, too much noise */
562             break;
563 
564         default:
565             DPRINT1("Unknown PNP minor function 0x%x\n", IrpSp->MinorFunction);
566             break;
567     }
568 
569     Irp->IoStatus.Status = Status;
570     IoSkipCurrentIrpStackLocation(Irp);
571     Status = IoCallDriver(DeviceExtension->Ldo, Irp);
572 
573     DPRINT("Leaving. Status 0x%lx\n", Status);
574 
575     return Status;
576 }
577 
578 
579 NTSTATUS
580 FdoPowerControl(
581     PDEVICE_OBJECT DeviceObject,
582     PIRP Irp)
583 /*
584  * FUNCTION: Handle power management IRPs for the PCI device object
585  * ARGUMENTS:
586  *     DeviceObject = Pointer to functional device object of the PCI driver
587  *     Irp          = Pointer to IRP that should be handled
588  * RETURNS:
589  *     Status
590  */
591 {
592     PFDO_DEVICE_EXTENSION DeviceExtension;
593     NTSTATUS Status;
594 
595     DPRINT("Called\n");
596 
597     DeviceExtension = DeviceObject->DeviceExtension;
598 
599     PoStartNextPowerIrp(Irp);
600     IoSkipCurrentIrpStackLocation(Irp);
601     Status = PoCallDriver(DeviceExtension->Ldo, Irp);
602 
603     DPRINT("Leaving. Status 0x%X\n", Status);
604 
605     return Status;
606 }
607 
608 /* EOF */
609