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