xref: /reactos/drivers/bus/pci/fdo.c (revision 7b1049c8)
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