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