6dffced6b3187e4a71b288dc7c88ce49c005d01e
[reactos.git] / reactos / drivers / bus / pci / fdo.c
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 static NTSTATUS
460 FdoSetPower(
461 IN PDEVICE_OBJECT DeviceObject,
462 IN PIRP Irp,
463 PIO_STACK_LOCATION IrpSp)
464 {
465 NTSTATUS Status;
466
467 UNREFERENCED_PARAMETER(DeviceObject);
468 UNREFERENCED_PARAMETER(Irp);
469
470 DPRINT("Called\n");
471
472 if (IrpSp->Parameters.Power.Type == DevicePowerState)
473 {
474 /* FIXME: Set device power state for the device */
475 Status = STATUS_UNSUCCESSFUL;
476 }
477 else
478 {
479 Status = STATUS_UNSUCCESSFUL;
480 }
481
482 return Status;
483 }
484
485
486 /*** PUBLIC ******************************************************************/
487
488 NTSTATUS
489 FdoPnpControl(
490 PDEVICE_OBJECT DeviceObject,
491 PIRP Irp)
492 /*
493 * FUNCTION: Handle Plug and Play IRPs for the PCI device object
494 * ARGUMENTS:
495 * DeviceObject = Pointer to functional device object of the PCI driver
496 * Irp = Pointer to IRP that should be handled
497 * RETURNS:
498 * Status
499 */
500 {
501 PFDO_DEVICE_EXTENSION DeviceExtension;
502 PIO_STACK_LOCATION IrpSp;
503 NTSTATUS Status = Irp->IoStatus.Status;
504
505 DPRINT("Called\n");
506
507 DeviceExtension = DeviceObject->DeviceExtension;
508
509 IrpSp = IoGetCurrentIrpStackLocation(Irp);
510 switch (IrpSp->MinorFunction)
511 {
512 #if 0
513 case IRP_MN_CANCEL_REMOVE_DEVICE:
514 Status = STATUS_NOT_IMPLEMENTED;
515 break;
516
517 case IRP_MN_CANCEL_STOP_DEVICE:
518 Status = STATUS_NOT_IMPLEMENTED;
519 break;
520
521 case IRP_MN_DEVICE_USAGE_NOTIFICATION:
522 Status = STATUS_NOT_IMPLEMENTED;
523 break;
524
525 case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
526 Status = STATUS_NOT_IMPLEMENTED;
527 break;
528 #endif
529 case IRP_MN_QUERY_DEVICE_RELATIONS:
530 if (IrpSp->Parameters.QueryDeviceRelations.Type != BusRelations)
531 break;
532
533 Status = FdoQueryBusRelations(DeviceObject, Irp, IrpSp);
534 Irp->IoStatus.Status = Status;
535 IoCompleteRequest(Irp, IO_NO_INCREMENT);
536 return Status;
537 #if 0
538 case IRP_MN_QUERY_PNP_DEVICE_STATE:
539 Status = STATUS_NOT_IMPLEMENTED;
540 break;
541
542 case IRP_MN_QUERY_REMOVE_DEVICE:
543 Status = STATUS_NOT_IMPLEMENTED;
544 break;
545 #endif
546 case IRP_MN_START_DEVICE:
547 DPRINT("IRP_MN_START_DEVICE received\n");
548 Status = ForwardIrpAndWait(DeviceObject, Irp);
549 if (NT_SUCCESS(Status))
550 Status = FdoStartDevice(DeviceObject, Irp);
551
552 Irp->IoStatus.Status = Status;
553 IoCompleteRequest(Irp, IO_NO_INCREMENT);
554 return Status;
555
556 case IRP_MN_QUERY_STOP_DEVICE:
557 /* We don't support stopping yet */
558 Status = STATUS_UNSUCCESSFUL;
559 Irp->IoStatus.Status = Status;
560 IoCompleteRequest(Irp, IO_NO_INCREMENT);
561 return Status;
562
563 case IRP_MN_STOP_DEVICE:
564 /* We can't fail this one so we fail the QUERY_STOP request that precedes it */
565 break;
566 #if 0
567 case IRP_MN_SURPRISE_REMOVAL:
568 Status = STATUS_NOT_IMPLEMENTED;
569 break;
570 #endif
571
572 case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
573 break;
574
575 case IRP_MN_REMOVE_DEVICE:
576 /* Detach the device object from the device stack */
577 IoDetachDevice(DeviceExtension->Ldo);
578
579 /* Delete the device object */
580 IoDeleteDevice(DeviceObject);
581
582 /* Return success */
583 Status = STATUS_SUCCESS;
584 break;
585
586 case IRP_MN_QUERY_CAPABILITIES:
587 case IRP_MN_QUERY_PNP_DEVICE_STATE:
588 /* Don't print the warning, too much noise */
589 break;
590
591 default:
592 DPRINT1("Unknown PNP minor function 0x%x\n", IrpSp->MinorFunction);
593 break;
594 }
595
596 Irp->IoStatus.Status = Status;
597 IoSkipCurrentIrpStackLocation(Irp);
598 Status = IoCallDriver(DeviceExtension->Ldo, Irp);
599
600 DPRINT("Leaving. Status 0x%lx\n", Status);
601
602 return Status;
603 }
604
605
606 NTSTATUS
607 FdoPowerControl(
608 PDEVICE_OBJECT DeviceObject,
609 PIRP Irp)
610 /*
611 * FUNCTION: Handle power management IRPs for the PCI device object
612 * ARGUMENTS:
613 * DeviceObject = Pointer to functional device object of the PCI driver
614 * Irp = Pointer to IRP that should be handled
615 * RETURNS:
616 * Status
617 */
618 {
619 PIO_STACK_LOCATION IrpSp;
620 NTSTATUS Status;
621
622 DPRINT("Called\n");
623
624 IrpSp = IoGetCurrentIrpStackLocation(Irp);
625
626 switch (IrpSp->MinorFunction)
627 {
628 case IRP_MN_SET_POWER:
629 Status = FdoSetPower(DeviceObject, Irp, IrpSp);
630 break;
631
632 default:
633 DPRINT("Unknown IOCTL 0x%X\n", IrpSp->MinorFunction);
634 Status = STATUS_NOT_IMPLEMENTED;
635 break;
636 }
637
638 if (Status != STATUS_PENDING)
639 {
640 Irp->IoStatus.Status = Status;
641 IoCompleteRequest(Irp, IO_NO_INCREMENT);
642 }
643
644 DPRINT("Leaving. Status 0x%X\n", Status);
645
646 return Status;
647 }
648
649 /* EOF */