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