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