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