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