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