* Sync up to trunk head (r65481).
[reactos.git] / 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 = (PPCI_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 }
236
237 Size = sizeof(DEVICE_RELATIONS) +
238 sizeof(Relations->Objects) * (DeviceExtension->DeviceListCount - 1);
239 Relations = (PDEVICE_RELATIONS)ExAllocatePool(PagedPool, Size);
240 if (!Relations)
241 return STATUS_INSUFFICIENT_RESOURCES;
242
243 Relations->Count = DeviceExtension->DeviceListCount;
244
245 i = 0;
246 CurrentEntry = DeviceExtension->DeviceListHead.Flink;
247 while (CurrentEntry != &DeviceExtension->DeviceListHead)
248 {
249 Device = CONTAINING_RECORD(CurrentEntry, PCI_DEVICE, ListEntry);
250
251 PdoDeviceExtension = NULL;
252
253 if (!Device->Pdo)
254 {
255 /* Create a physical device object for the
256 device as it does not already have one */
257 Status = IoCreateDevice(DeviceObject->DriverObject,
258 sizeof(PDO_DEVICE_EXTENSION),
259 NULL,
260 FILE_DEVICE_CONTROLLER,
261 FILE_AUTOGENERATED_DEVICE_NAME,
262 FALSE,
263 &Device->Pdo);
264 if (!NT_SUCCESS(Status))
265 {
266 DPRINT("IoCreateDevice() failed with status 0x%X\n", Status);
267 ErrorStatus = Status;
268 ErrorOccurred = TRUE;
269 break;
270 }
271
272 Device->Pdo->Flags &= ~DO_DEVICE_INITIALIZING;
273
274 //Device->Pdo->Flags |= DO_POWER_PAGABLE;
275
276 PdoDeviceExtension = (PPDO_DEVICE_EXTENSION)Device->Pdo->DeviceExtension;
277
278 RtlZeroMemory(PdoDeviceExtension, sizeof(PDO_DEVICE_EXTENSION));
279
280 PdoDeviceExtension->Common.IsFDO = FALSE;
281
282 PdoDeviceExtension->Common.DeviceObject = Device->Pdo;
283
284 PdoDeviceExtension->Common.DevicePowerState = PowerDeviceD0;
285
286 PdoDeviceExtension->Fdo = DeviceObject;
287
288 PdoDeviceExtension->PciDevice = Device;
289
290 /* Add Device ID string */
291 Status = PciCreateDeviceIDString(&PdoDeviceExtension->DeviceID, Device);
292 if (!NT_SUCCESS(Status))
293 {
294 ErrorStatus = Status;
295 ErrorOccurred = TRUE;
296 break;
297 }
298
299 DPRINT("DeviceID: %S\n", PdoDeviceExtension->DeviceID.Buffer);
300
301 /* Add Instance ID string */
302 Status = PciCreateInstanceIDString(&PdoDeviceExtension->InstanceID, Device);
303 if (!NT_SUCCESS(Status))
304 {
305 ErrorStatus = Status;
306 ErrorOccurred = TRUE;
307 break;
308 }
309
310 /* Add Hardware IDs string */
311 Status = PciCreateHardwareIDsString(&PdoDeviceExtension->HardwareIDs, Device);
312 if (!NT_SUCCESS(Status))
313 {
314 ErrorStatus = Status;
315 ErrorOccurred = TRUE;
316 break;
317 }
318
319 /* Add Compatible IDs string */
320 Status = PciCreateCompatibleIDsString(&PdoDeviceExtension->CompatibleIDs, Device);
321 if (!NT_SUCCESS(Status))
322 {
323 ErrorStatus = Status;
324 ErrorOccurred = TRUE;
325 break;
326 }
327
328 /* Add device description string */
329 Status = PciCreateDeviceDescriptionString(&PdoDeviceExtension->DeviceDescription, Device);
330 if (!NT_SUCCESS(Status))
331 {
332 ErrorStatus = Status;
333 ErrorOccurred = TRUE;
334 break;
335 }
336
337 /* Add device location string */
338 Status = PciCreateDeviceLocationString(&PdoDeviceExtension->DeviceLocation, Device);
339 if (!NT_SUCCESS(Status))
340 {
341 ErrorStatus = Status;
342 ErrorOccurred = TRUE;
343 break;
344 }
345 }
346
347 /* Reference the physical device object. The PnP manager
348 will dereference it again when it is no longer needed */
349 ObReferenceObject(Device->Pdo);
350
351 Relations->Objects[i] = Device->Pdo;
352
353 i++;
354
355 CurrentEntry = CurrentEntry->Flink;
356 }
357
358 if (ErrorOccurred)
359 {
360 /* FIXME: Cleanup all new PDOs created in this call. Please give me SEH!!! ;-) */
361 /* FIXME: Should IoAttachDeviceToDeviceStack() be undone? */
362 if (PdoDeviceExtension)
363 {
364 RtlFreeUnicodeString(&PdoDeviceExtension->DeviceID);
365 RtlFreeUnicodeString(&PdoDeviceExtension->InstanceID);
366 RtlFreeUnicodeString(&PdoDeviceExtension->HardwareIDs);
367 RtlFreeUnicodeString(&PdoDeviceExtension->CompatibleIDs);
368 RtlFreeUnicodeString(&PdoDeviceExtension->DeviceDescription);
369 RtlFreeUnicodeString(&PdoDeviceExtension->DeviceLocation);
370 }
371
372 ExFreePool(Relations);
373 return ErrorStatus;
374 }
375
376 Irp->IoStatus.Information = (ULONG_PTR)Relations;
377
378 DPRINT("Done\n");
379
380 return Status;
381 }
382
383
384 static NTSTATUS
385 FdoStartDevice(
386 IN PDEVICE_OBJECT DeviceObject,
387 IN PIRP Irp)
388 {
389 PFDO_DEVICE_EXTENSION DeviceExtension;
390 PCM_RESOURCE_LIST AllocatedResources;
391 PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor;
392 ULONG FoundBusNumber = FALSE;
393 ULONG i;
394
395 DPRINT("Called\n");
396
397 DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
398
399 AllocatedResources = IoGetCurrentIrpStackLocation(Irp)->Parameters.StartDevice.AllocatedResources;
400 if (!AllocatedResources)
401 {
402 DPRINT("No allocated resources sent to driver\n");
403 return STATUS_INSUFFICIENT_RESOURCES;
404 }
405
406 if (AllocatedResources->Count < 1)
407 {
408 DPRINT("Not enough allocated resources sent to driver\n");
409 return STATUS_INSUFFICIENT_RESOURCES;
410 }
411
412 if (AllocatedResources->List[0].PartialResourceList.Version != 1 ||
413 AllocatedResources->List[0].PartialResourceList.Revision != 1)
414 return STATUS_REVISION_MISMATCH;
415
416 ASSERT(DeviceExtension->State == dsStopped);
417
418 /* By default, use the bus number in the resource list header */
419 DeviceExtension->BusNumber = AllocatedResources->List[0].BusNumber;
420
421 for (i = 0; i < AllocatedResources->List[0].PartialResourceList.Count; i++)
422 {
423 ResourceDescriptor = &AllocatedResources->List[0].PartialResourceList.PartialDescriptors[i];
424 switch (ResourceDescriptor->Type)
425 {
426 case CmResourceTypeBusNumber:
427 if (FoundBusNumber || ResourceDescriptor->u.BusNumber.Length != 1)
428 return STATUS_INVALID_PARAMETER;
429
430 /* Use this one instead */
431 ASSERT(AllocatedResources->List[0].BusNumber == ResourceDescriptor->u.BusNumber.Start);
432 DeviceExtension->BusNumber = ResourceDescriptor->u.BusNumber.Start;
433 DPRINT("Found bus number resource: %lu\n", DeviceExtension->BusNumber);
434 FoundBusNumber = TRUE;
435 break;
436
437 default:
438 DPRINT("Unknown resource descriptor type 0x%x\n", ResourceDescriptor->Type);
439 }
440 }
441
442 InitializeListHead(&DeviceExtension->DeviceListHead);
443 KeInitializeSpinLock(&DeviceExtension->DeviceListLock);
444 DeviceExtension->DeviceListCount = 0;
445 DeviceExtension->State = dsStarted;
446
447 ExInterlockedInsertTailList(
448 &DriverExtension->BusListHead,
449 &DeviceExtension->ListEntry,
450 &DriverExtension->BusListLock);
451
452 Irp->IoStatus.Information = 0;
453
454 return STATUS_SUCCESS;
455 }
456
457
458 static NTSTATUS
459 FdoSetPower(
460 IN PDEVICE_OBJECT DeviceObject,
461 IN PIRP Irp,
462 PIO_STACK_LOCATION IrpSp)
463 {
464 NTSTATUS Status;
465
466 UNREFERENCED_PARAMETER(DeviceObject);
467 UNREFERENCED_PARAMETER(Irp);
468
469 DPRINT("Called\n");
470
471 if (IrpSp->Parameters.Power.Type == DevicePowerState)
472 {
473 /* FIXME: Set device power state for the device */
474 Status = STATUS_UNSUCCESSFUL;
475 }
476 else
477 {
478 Status = STATUS_UNSUCCESSFUL;
479 }
480
481 return Status;
482 }
483
484
485 /*** PUBLIC ******************************************************************/
486
487 NTSTATUS
488 FdoPnpControl(
489 PDEVICE_OBJECT DeviceObject,
490 PIRP Irp)
491 /*
492 * FUNCTION: Handle Plug and Play IRPs for the PCI device object
493 * ARGUMENTS:
494 * DeviceObject = Pointer to functional device object of the PCI driver
495 * Irp = Pointer to IRP that should be handled
496 * RETURNS:
497 * Status
498 */
499 {
500 PFDO_DEVICE_EXTENSION DeviceExtension;
501 PIO_STACK_LOCATION IrpSp;
502 NTSTATUS Status = Irp->IoStatus.Status;
503
504 DPRINT("Called\n");
505
506 DeviceExtension = DeviceObject->DeviceExtension;
507
508 IrpSp = IoGetCurrentIrpStackLocation(Irp);
509 switch (IrpSp->MinorFunction)
510 {
511 #if 0
512 case IRP_MN_CANCEL_REMOVE_DEVICE:
513 Status = STATUS_NOT_IMPLEMENTED;
514 break;
515
516 case IRP_MN_CANCEL_STOP_DEVICE:
517 Status = STATUS_NOT_IMPLEMENTED;
518 break;
519
520 case IRP_MN_DEVICE_USAGE_NOTIFICATION:
521 Status = STATUS_NOT_IMPLEMENTED;
522 break;
523
524 case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
525 Status = STATUS_NOT_IMPLEMENTED;
526 break;
527 #endif
528 case IRP_MN_QUERY_DEVICE_RELATIONS:
529 if (IrpSp->Parameters.QueryDeviceRelations.Type != BusRelations)
530 break;
531
532 Status = FdoQueryBusRelations(DeviceObject, Irp, IrpSp);
533 Irp->IoStatus.Status = Status;
534 IoCompleteRequest(Irp, IO_NO_INCREMENT);
535 return Status;
536 #if 0
537 case IRP_MN_QUERY_PNP_DEVICE_STATE:
538 Status = STATUS_NOT_IMPLEMENTED;
539 break;
540
541 case IRP_MN_QUERY_REMOVE_DEVICE:
542 Status = STATUS_NOT_IMPLEMENTED;
543 break;
544 #endif
545 case IRP_MN_START_DEVICE:
546 DPRINT("IRP_MN_START_DEVICE received\n");
547 Status = ForwardIrpAndWait(DeviceObject, Irp);
548 if (NT_SUCCESS(Status))
549 Status = FdoStartDevice(DeviceObject, Irp);
550
551 Irp->IoStatus.Status = Status;
552 IoCompleteRequest(Irp, IO_NO_INCREMENT);
553 return Status;
554
555 case IRP_MN_QUERY_STOP_DEVICE:
556 /* We don't support stopping yet */
557 Status = STATUS_UNSUCCESSFUL;
558 Irp->IoStatus.Status = Status;
559 IoCompleteRequest(Irp, IO_NO_INCREMENT);
560 return Status;
561
562 case IRP_MN_STOP_DEVICE:
563 /* We can't fail this one so we fail the QUERY_STOP request that precedes it */
564 break;
565 #if 0
566 case IRP_MN_SURPRISE_REMOVAL:
567 Status = STATUS_NOT_IMPLEMENTED;
568 break;
569 #endif
570
571 case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
572 break;
573
574 case IRP_MN_REMOVE_DEVICE:
575 /* Detach the device object from the device stack */
576 IoDetachDevice(DeviceExtension->Ldo);
577
578 /* Delete the device object */
579 IoDeleteDevice(DeviceObject);
580
581 /* Return success */
582 Status = STATUS_SUCCESS;
583 break;
584
585 case IRP_MN_QUERY_CAPABILITIES:
586 case IRP_MN_QUERY_PNP_DEVICE_STATE:
587 /* Don't print the warning, too much noise */
588 break;
589
590 default:
591 DPRINT1("Unknown PNP minor function 0x%x\n", IrpSp->MinorFunction);
592 break;
593 }
594
595 Irp->IoStatus.Status = Status;
596 IoSkipCurrentIrpStackLocation(Irp);
597 Status = IoCallDriver(DeviceExtension->Ldo, Irp);
598
599 DPRINT("Leaving. Status 0x%lx\n", Status);
600
601 return Status;
602 }
603
604
605 NTSTATUS
606 FdoPowerControl(
607 PDEVICE_OBJECT DeviceObject,
608 PIRP Irp)
609 /*
610 * FUNCTION: Handle power management IRPs for the PCI device object
611 * ARGUMENTS:
612 * DeviceObject = Pointer to functional device object of the PCI driver
613 * Irp = Pointer to IRP that should be handled
614 * RETURNS:
615 * Status
616 */
617 {
618 PIO_STACK_LOCATION IrpSp;
619 NTSTATUS Status;
620
621 DPRINT("Called\n");
622
623 IrpSp = IoGetCurrentIrpStackLocation(Irp);
624
625 switch (IrpSp->MinorFunction)
626 {
627 case IRP_MN_SET_POWER:
628 Status = FdoSetPower(DeviceObject, Irp, IrpSp);
629 break;
630
631 default:
632 DPRINT("Unknown IOCTL 0x%X\n", IrpSp->MinorFunction);
633 Status = STATUS_NOT_IMPLEMENTED;
634 break;
635 }
636
637 if (Status != STATUS_PENDING)
638 {
639 Irp->IoStatus.Status = Status;
640 IoCompleteRequest(Irp, IO_NO_INCREMENT);
641 }
642
643 DPRINT("Leaving. Status 0x%X\n", Status);
644
645 return Status;
646 }
647
648 /* EOF */