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