[PCI]
[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 #ifndef NDEBUG
13 #define NDEBUG
14 #endif
15 #include <debug.h>
16
17 /*** PRIVATE *****************************************************************/
18
19 static IO_COMPLETION_ROUTINE ForwardIrpAndWaitCompletion;
20
21 static NTSTATUS NTAPI
22 ForwardIrpAndWaitCompletion(
23 IN PDEVICE_OBJECT DeviceObject,
24 IN PIRP Irp,
25 IN PVOID Context)
26 {
27 UNREFERENCED_PARAMETER(DeviceObject);
28 if (Irp->PendingReturned)
29 KeSetEvent((PKEVENT)Context, IO_NO_INCREMENT, FALSE);
30 return STATUS_MORE_PROCESSING_REQUIRED;
31 }
32
33 NTSTATUS NTAPI
34 ForwardIrpAndWait(
35 IN PDEVICE_OBJECT DeviceObject,
36 IN PIRP Irp)
37 {
38 KEVENT Event;
39 NTSTATUS Status;
40 PDEVICE_OBJECT LowerDevice = ((PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->Ldo;
41 ASSERT(LowerDevice);
42
43 KeInitializeEvent(&Event, NotificationEvent, FALSE);
44 IoCopyCurrentIrpStackLocationToNext(Irp);
45
46 IoSetCompletionRoutine(Irp, ForwardIrpAndWaitCompletion, &Event, TRUE, TRUE, TRUE);
47
48 Status = IoCallDriver(LowerDevice, Irp);
49 if (Status == STATUS_PENDING)
50 {
51 Status = KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
52 if (NT_SUCCESS(Status))
53 Status = Irp->IoStatus.Status;
54 }
55
56 return Status;
57 }
58
59 static NTSTATUS
60 FdoLocateChildDevice(
61 PPCI_DEVICE *Device,
62 PFDO_DEVICE_EXTENSION DeviceExtension,
63 PCI_SLOT_NUMBER SlotNumber,
64 PPCI_COMMON_CONFIG PciConfig)
65 {
66 PLIST_ENTRY CurrentEntry;
67 PPCI_DEVICE CurrentDevice;
68
69 DPRINT("Called\n");
70
71 CurrentEntry = DeviceExtension->DeviceListHead.Flink;
72 while (CurrentEntry != &DeviceExtension->DeviceListHead) {
73 CurrentDevice = CONTAINING_RECORD(CurrentEntry, PCI_DEVICE, ListEntry);
74
75 /* If both vendor ID and device ID match, it is the same device */
76 if ((PciConfig->VendorID == CurrentDevice->PciConfig.VendorID) &&
77 (PciConfig->DeviceID == CurrentDevice->PciConfig.DeviceID) &&
78 (SlotNumber.u.AsULONG == CurrentDevice->SlotNumber.u.AsULONG)) {
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 PLIST_ENTRY CurrentEntry;
100 PPCI_DEVICE Device;
101 PCI_SLOT_NUMBER SlotNumber;
102 ULONG DeviceNumber;
103 ULONG FunctionNumber;
104 ULONG Size;
105 NTSTATUS Status;
106
107 DPRINT("Called\n");
108
109 DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
110
111 /* Mark all devices to be removed. If we don't discover them again during
112 enumeration, assume that they have been surprise removed */
113 CurrentEntry = DeviceExtension->DeviceListHead.Flink;
114 while (CurrentEntry != &DeviceExtension->DeviceListHead) {
115 Device = CONTAINING_RECORD(CurrentEntry, PCI_DEVICE, ListEntry);
116 Device->RemovePending = TRUE;
117 CurrentEntry = CurrentEntry->Flink;
118 }
119
120 DeviceExtension->DeviceListCount = 0;
121
122 /* Enumerate devices on the PCI bus */
123 SlotNumber.u.AsULONG = 0;
124 for (DeviceNumber = 0; DeviceNumber < PCI_MAX_DEVICES; DeviceNumber++)
125 {
126 SlotNumber.u.bits.DeviceNumber = DeviceNumber;
127 for (FunctionNumber = 0; FunctionNumber < PCI_MAX_FUNCTION; FunctionNumber++)
128 {
129 SlotNumber.u.bits.FunctionNumber = FunctionNumber;
130
131 DPRINT("Bus %1lu Device %2lu Func %1lu\n",
132 DeviceExtension->BusNumber,
133 DeviceNumber,
134 FunctionNumber);
135
136 RtlZeroMemory(&PciConfig,
137 sizeof(PCI_COMMON_CONFIG));
138
139 Size = HalGetBusData(PCIConfiguration,
140 DeviceExtension->BusNumber,
141 SlotNumber.u.AsULONG,
142 &PciConfig,
143 PCI_COMMON_HDR_LENGTH);
144 DPRINT("Size %lu\n", Size);
145 if (Size < PCI_COMMON_HDR_LENGTH)
146 {
147 if (FunctionNumber == 0)
148 {
149 break;
150 }
151 else
152 {
153 continue;
154 }
155 }
156
157 DPRINT("Bus %1lu Device %2lu Func %1lu VenID 0x%04hx DevID 0x%04hx\n",
158 DeviceExtension->BusNumber,
159 DeviceNumber,
160 FunctionNumber,
161 PciConfig.VendorID,
162 PciConfig.DeviceID);
163
164 Status = FdoLocateChildDevice(&Device, DeviceExtension, SlotNumber, &PciConfig);
165 if (!NT_SUCCESS(Status))
166 {
167 Device = (PPCI_DEVICE)ExAllocatePoolWithTag(NonPagedPool, sizeof(PCI_DEVICE),TAG_PCI);
168 if (!Device)
169 {
170 /* FIXME: Cleanup resources for already discovered devices */
171 return STATUS_INSUFFICIENT_RESOURCES;
172 }
173
174 RtlZeroMemory(Device,
175 sizeof(PCI_DEVICE));
176
177 Device->BusNumber = DeviceExtension->BusNumber;
178
179 RtlCopyMemory(&Device->SlotNumber,
180 &SlotNumber,
181 sizeof(PCI_SLOT_NUMBER));
182
183 RtlCopyMemory(&Device->PciConfig,
184 &PciConfig,
185 sizeof(PCI_COMMON_CONFIG));
186
187 ExInterlockedInsertTailList(
188 &DeviceExtension->DeviceListHead,
189 &Device->ListEntry,
190 &DeviceExtension->DeviceListLock);
191 }
192
193 /* Don't remove this device */
194 Device->RemovePending = FALSE;
195
196 DeviceExtension->DeviceListCount++;
197
198 /* Skip to next device if the current one is not a multifunction device */
199 if ((FunctionNumber == 0) &&
200 ((PciConfig.HeaderType & 0x80) == 0))
201 {
202 break;
203 }
204 }
205 }
206
207 DPRINT("Done\n");
208
209 return STATUS_SUCCESS;
210 }
211
212
213 static NTSTATUS
214 FdoQueryBusRelations(
215 IN PDEVICE_OBJECT DeviceObject,
216 IN PIRP Irp,
217 PIO_STACK_LOCATION IrpSp)
218 {
219 PPDO_DEVICE_EXTENSION PdoDeviceExtension = NULL;
220 PFDO_DEVICE_EXTENSION DeviceExtension;
221 PDEVICE_RELATIONS Relations;
222 PLIST_ENTRY CurrentEntry;
223 PPCI_DEVICE Device;
224 NTSTATUS Status;
225 BOOLEAN ErrorOccurred;
226 NTSTATUS ErrorStatus;
227 ULONG Size;
228 ULONG i;
229
230 UNREFERENCED_PARAMETER(IrpSp);
231
232 DPRINT("Called\n");
233
234 ErrorStatus = STATUS_INSUFFICIENT_RESOURCES;
235
236 Status = STATUS_SUCCESS;
237
238 ErrorOccurred = FALSE;
239
240 FdoEnumerateDevices(DeviceObject);
241
242 DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
243
244 if (Irp->IoStatus.Information) {
245 /* FIXME: Another bus driver has already created a DEVICE_RELATIONS
246 structure so we must merge this structure with our own */
247 }
248
249 Size = sizeof(DEVICE_RELATIONS) + sizeof(Relations->Objects) *
250 (DeviceExtension->DeviceListCount - 1);
251 Relations = (PDEVICE_RELATIONS)ExAllocatePool(PagedPool, Size);
252 if (!Relations)
253 return STATUS_INSUFFICIENT_RESOURCES;
254
255 Relations->Count = DeviceExtension->DeviceListCount;
256
257 i = 0;
258 CurrentEntry = DeviceExtension->DeviceListHead.Flink;
259 while (CurrentEntry != &DeviceExtension->DeviceListHead) {
260 Device = CONTAINING_RECORD(CurrentEntry, PCI_DEVICE, ListEntry);
261
262 PdoDeviceExtension = NULL;
263
264 if (!Device->Pdo) {
265 /* Create a physical device object for the
266 device as it does not already have one */
267 Status = IoCreateDevice(
268 DeviceObject->DriverObject,
269 sizeof(PDO_DEVICE_EXTENSION),
270 NULL,
271 FILE_DEVICE_CONTROLLER,
272 FILE_AUTOGENERATED_DEVICE_NAME,
273 FALSE,
274 &Device->Pdo);
275 if (!NT_SUCCESS(Status)) {
276 DPRINT("IoCreateDevice() failed with status 0x%X\n", Status);
277 ErrorStatus = Status;
278 ErrorOccurred = TRUE;
279 break;
280 }
281
282 Device->Pdo->Flags &= ~DO_DEVICE_INITIALIZING;
283
284 //Device->Pdo->Flags |= DO_POWER_PAGABLE;
285
286 PdoDeviceExtension = (PPDO_DEVICE_EXTENSION)Device->Pdo->DeviceExtension;
287
288 RtlZeroMemory(PdoDeviceExtension, sizeof(PDO_DEVICE_EXTENSION));
289
290 PdoDeviceExtension->Common.IsFDO = FALSE;
291
292 PdoDeviceExtension->Common.DeviceObject = Device->Pdo;
293
294 PdoDeviceExtension->Common.DevicePowerState = PowerDeviceD0;
295
296 PdoDeviceExtension->Fdo = DeviceObject;
297
298 PdoDeviceExtension->PciDevice = Device;
299
300 /* Add Device ID string */
301 Status = PciCreateDeviceIDString(&PdoDeviceExtension->DeviceID, Device);
302 if (!NT_SUCCESS(Status))
303 {
304 ErrorStatus = Status;
305 ErrorOccurred = TRUE;
306 break;
307 }
308
309 DPRINT("DeviceID: %S\n", PdoDeviceExtension->DeviceID.Buffer);
310
311 /* Add Instance ID string */
312 Status = PciCreateInstanceIDString(&PdoDeviceExtension->InstanceID, Device);
313 if (!NT_SUCCESS(Status))
314 {
315 ErrorStatus = Status;
316 ErrorOccurred = TRUE;
317 break;
318 }
319
320 /* Add Hardware IDs string */
321 Status = PciCreateHardwareIDsString(&PdoDeviceExtension->HardwareIDs, Device);
322 if (!NT_SUCCESS(Status))
323 {
324 ErrorStatus = Status;
325 ErrorOccurred = TRUE;
326 break;
327 }
328
329 /* Add Compatible IDs string */
330 Status = PciCreateCompatibleIDsString(&PdoDeviceExtension->CompatibleIDs, Device);
331 if (!NT_SUCCESS(Status))
332 {
333 ErrorStatus = Status;
334 ErrorOccurred = TRUE;
335 break;
336 }
337
338 /* Add device description string */
339 Status = PciCreateDeviceDescriptionString(&PdoDeviceExtension->DeviceDescription, Device);
340 if (!NT_SUCCESS(Status))
341 {
342 ErrorStatus = Status;
343 ErrorOccurred = TRUE;
344 break;
345 }
346
347 /* Add device location string */
348 Status = PciCreateDeviceLocationString(&PdoDeviceExtension->DeviceLocation, Device);
349 if (!NT_SUCCESS(Status))
350 {
351 ErrorStatus = Status;
352 ErrorOccurred = TRUE;
353 break;
354 }
355 }
356
357 if (!Device->RemovePending) {
358 /* Reference the physical device object. The PnP manager
359 will dereference it again when it is no longer needed */
360 ObReferenceObject(Device->Pdo);
361
362 Relations->Objects[i] = Device->Pdo;
363
364 i++;
365 }
366
367 CurrentEntry = CurrentEntry->Flink;
368 }
369
370 if (ErrorOccurred) {
371 /* FIXME: Cleanup all new PDOs created in this call. Please give me SEH!!! ;-) */
372 /* FIXME: Should IoAttachDeviceToDeviceStack() be undone? */
373 if (PdoDeviceExtension) {
374 RtlFreeUnicodeString(&PdoDeviceExtension->DeviceID);
375 RtlFreeUnicodeString(&PdoDeviceExtension->InstanceID);
376 RtlFreeUnicodeString(&PdoDeviceExtension->HardwareIDs);
377 RtlFreeUnicodeString(&PdoDeviceExtension->CompatibleIDs);
378 RtlFreeUnicodeString(&PdoDeviceExtension->DeviceDescription);
379 RtlFreeUnicodeString(&PdoDeviceExtension->DeviceLocation);
380 }
381
382 ExFreePool(Relations);
383 return ErrorStatus;
384 }
385
386 Irp->IoStatus.Information = (ULONG_PTR)Relations;
387
388 DPRINT("Done\n");
389
390 return Status;
391 }
392
393
394 static NTSTATUS
395 FdoStartDevice(
396 IN PDEVICE_OBJECT DeviceObject,
397 IN PIRP Irp)
398 {
399 PFDO_DEVICE_EXTENSION DeviceExtension;
400 PCM_RESOURCE_LIST AllocatedResources;
401 PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor;
402 ULONG FoundBusNumber = FALSE;
403 ULONG i;
404
405 DPRINT("Called\n");
406
407 DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
408
409 AllocatedResources = IoGetCurrentIrpStackLocation(Irp)->Parameters.StartDevice.AllocatedResources;
410 if (!AllocatedResources)
411 {
412 DPRINT("No allocated resources sent to driver\n");
413 return STATUS_INSUFFICIENT_RESOURCES;
414 }
415 if (AllocatedResources->Count < 1)
416 {
417 DPRINT("Not enough allocated resources sent to driver\n");
418 return STATUS_INSUFFICIENT_RESOURCES;
419 }
420 if (AllocatedResources->List[0].PartialResourceList.Version != 1
421 || AllocatedResources->List[0].PartialResourceList.Revision != 1)
422 return STATUS_REVISION_MISMATCH;
423
424 ASSERT(DeviceExtension->State == dsStopped);
425
426 /* By default, use the bus number in the resource list header */
427 DeviceExtension->BusNumber = AllocatedResources->List[0].BusNumber;
428
429 for (i = 0; i < AllocatedResources->List[0].PartialResourceList.Count; i++)
430 {
431 ResourceDescriptor = &AllocatedResources->List[0].PartialResourceList.PartialDescriptors[i];
432 switch (ResourceDescriptor->Type)
433 {
434 case CmResourceTypeBusNumber:
435 {
436 if (FoundBusNumber || ResourceDescriptor->u.BusNumber.Length != 1)
437 return STATUS_INVALID_PARAMETER;
438 /* Use this one instead */
439 ASSERT(AllocatedResources->List[0].BusNumber == ResourceDescriptor->u.BusNumber.Start);
440 DeviceExtension->BusNumber = ResourceDescriptor->u.BusNumber.Start;
441 DPRINT("Found bus number resource: %lu\n", DeviceExtension->BusNumber);
442 FoundBusNumber = TRUE;
443 break;
444 }
445 default:
446 DPRINT("Unknown resource descriptor type 0x%x\n", ResourceDescriptor->Type);
447 }
448 }
449
450 InitializeListHead(&DeviceExtension->DeviceListHead);
451 KeInitializeSpinLock(&DeviceExtension->DeviceListLock);
452 DeviceExtension->DeviceListCount = 0;
453 DeviceExtension->State = dsStarted;
454
455 ExInterlockedInsertTailList(
456 &DriverExtension->BusListHead,
457 &DeviceExtension->ListEntry,
458 &DriverExtension->BusListLock);
459
460 Irp->IoStatus.Information = 0;
461
462 return STATUS_SUCCESS;
463 }
464
465
466 static NTSTATUS
467 FdoSetPower(
468 IN PDEVICE_OBJECT DeviceObject,
469 IN PIRP Irp,
470 PIO_STACK_LOCATION IrpSp)
471 {
472 PFDO_DEVICE_EXTENSION DeviceExtension;
473 NTSTATUS Status;
474
475 UNREFERENCED_PARAMETER(Irp);
476
477 DPRINT("Called\n");
478
479 DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
480
481 if (IrpSp->Parameters.Power.Type == DevicePowerState) {
482 /* FIXME: Set device power state for the device */
483 Status = STATUS_UNSUCCESSFUL;
484 } else {
485 Status = STATUS_UNSUCCESSFUL;
486 }
487
488 return Status;
489 }
490
491
492 /*** PUBLIC ******************************************************************/
493
494 NTSTATUS
495 FdoPnpControl(
496 PDEVICE_OBJECT DeviceObject,
497 PIRP Irp)
498 /*
499 * FUNCTION: Handle Plug and Play IRPs for the PCI device object
500 * ARGUMENTS:
501 * DeviceObject = Pointer to functional device object of the PCI driver
502 * Irp = Pointer to IRP that should be handled
503 * RETURNS:
504 * Status
505 */
506 {
507 PFDO_DEVICE_EXTENSION DeviceExtension;
508 PIO_STACK_LOCATION IrpSp;
509 NTSTATUS Status = Irp->IoStatus.Status;
510
511 DPRINT("Called\n");
512
513 DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
514
515 IrpSp = IoGetCurrentIrpStackLocation(Irp);
516 switch (IrpSp->MinorFunction) {
517 #if 0
518 case IRP_MN_CANCEL_REMOVE_DEVICE:
519 Status = STATUS_NOT_IMPLEMENTED;
520 break;
521
522 case IRP_MN_CANCEL_STOP_DEVICE:
523 Status = STATUS_NOT_IMPLEMENTED;
524 break;
525
526 case IRP_MN_DEVICE_USAGE_NOTIFICATION:
527 Status = STATUS_NOT_IMPLEMENTED;
528 break;
529
530 case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
531 Status = STATUS_NOT_IMPLEMENTED;
532 break;
533 #endif
534 case IRP_MN_QUERY_DEVICE_RELATIONS:
535 if (IrpSp->Parameters.QueryDeviceRelations.Type != BusRelations)
536 break;
537
538 Status = FdoQueryBusRelations(DeviceObject, Irp, IrpSp);
539 Irp->IoStatus.Status = Status;
540 IoCompleteRequest(Irp, IO_NO_INCREMENT);
541 return Status;
542 #if 0
543 case IRP_MN_QUERY_PNP_DEVICE_STATE:
544 Status = STATUS_NOT_IMPLEMENTED;
545 break;
546
547 case IRP_MN_QUERY_REMOVE_DEVICE:
548 Status = STATUS_NOT_IMPLEMENTED;
549 break;
550
551 case IRP_MN_QUERY_STOP_DEVICE:
552 Status = STATUS_NOT_IMPLEMENTED;
553 break;
554
555 case IRP_MN_REMOVE_DEVICE:
556 Status = STATUS_NOT_IMPLEMENTED;
557 break;
558 #endif
559 case IRP_MN_START_DEVICE:
560 DPRINT("IRP_MN_START_DEVICE received\n");
561 Status = ForwardIrpAndWait(DeviceObject, Irp);
562 if (NT_SUCCESS(Status))
563 Status = FdoStartDevice(DeviceObject, Irp);
564
565 Irp->IoStatus.Status = Status;
566 IoCompleteRequest(Irp, IO_NO_INCREMENT);
567 return Status;
568 case IRP_MN_STOP_DEVICE:
569 /* Currently not supported */
570 Status = STATUS_UNSUCCESSFUL;
571 Irp->IoStatus.Status = Status;
572 IoCompleteRequest(Irp, IO_NO_INCREMENT);
573 return Status;
574 #if 0
575 case IRP_MN_SURPRISE_REMOVAL:
576 Status = STATUS_NOT_IMPLEMENTED;
577 break;
578 #endif
579 case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
580 break;
581 case IRP_MN_REMOVE_DEVICE:
582 DPRINT1("IRP_MN_REMOVE_DEVICE is UNIMPLEMENTED!\n");
583 break;
584 default:
585 DPRINT1("Unknown IOCTL 0x%lx\n", IrpSp->MinorFunction);
586 break;
587 }
588
589 Irp->IoStatus.Status = Status;
590 IoSkipCurrentIrpStackLocation(Irp);
591 Status = IoCallDriver(DeviceExtension->Ldo, Irp);
592
593 DPRINT("Leaving. Status 0x%X\n", Status);
594
595 return Status;
596 }
597
598
599 NTSTATUS
600 FdoPowerControl(
601 PDEVICE_OBJECT DeviceObject,
602 PIRP Irp)
603 /*
604 * FUNCTION: Handle power management IRPs for the PCI device object
605 * ARGUMENTS:
606 * DeviceObject = Pointer to functional device object of the PCI driver
607 * Irp = Pointer to IRP that should be handled
608 * RETURNS:
609 * Status
610 */
611 {
612 PIO_STACK_LOCATION IrpSp;
613 NTSTATUS Status;
614
615 DPRINT("Called\n");
616
617 IrpSp = IoGetCurrentIrpStackLocation(Irp);
618
619 switch (IrpSp->MinorFunction) {
620 case IRP_MN_SET_POWER:
621 Status = FdoSetPower(DeviceObject, Irp, IrpSp);
622 break;
623
624 default:
625 DPRINT("Unknown IOCTL 0x%X\n", IrpSp->MinorFunction);
626 Status = STATUS_NOT_IMPLEMENTED;
627 break;
628 }
629
630 if (Status != STATUS_PENDING) {
631 Irp->IoStatus.Status = Status;
632 IoCompleteRequest(Irp, IO_NO_INCREMENT);
633 }
634
635 DPRINT("Leaving. Status 0x%X\n", Status);
636
637 return Status;
638 }
639
640 /* EOF */