Bus number can also be stored in resource list header
[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;
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 DPRINT("Called\n");
191
192 ErrorStatus = STATUS_INSUFFICIENT_RESOURCES;
193
194 Status = STATUS_SUCCESS;
195
196 ErrorOccurred = FALSE;
197
198 FdoEnumerateDevices(DeviceObject);
199
200 DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
201
202 if (Irp->IoStatus.Information) {
203 /* FIXME: Another bus driver has already created a DEVICE_RELATIONS
204 structure so we must merge this structure with our own */
205 }
206
207 Size = sizeof(DEVICE_RELATIONS) + sizeof(Relations->Objects) *
208 (DeviceExtension->DeviceListCount - 1);
209 Relations = (PDEVICE_RELATIONS)ExAllocatePoolWithTag(PagedPool, Size, TAG_PCI);
210 if (!Relations)
211 return STATUS_INSUFFICIENT_RESOURCES;
212
213 Relations->Count = DeviceExtension->DeviceListCount;
214
215 i = 0;
216 CurrentEntry = DeviceExtension->DeviceListHead.Flink;
217 while (CurrentEntry != &DeviceExtension->DeviceListHead) {
218 Device = CONTAINING_RECORD(CurrentEntry, PCI_DEVICE, ListEntry);
219
220 PdoDeviceExtension = NULL;
221
222 if (!Device->Pdo) {
223 /* Create a physical device object for the
224 device as it does not already have one */
225 Status = IoCreateDevice(
226 DeviceObject->DriverObject,
227 sizeof(PDO_DEVICE_EXTENSION),
228 NULL,
229 FILE_DEVICE_CONTROLLER,
230 FILE_AUTOGENERATED_DEVICE_NAME,
231 FALSE,
232 &Device->Pdo);
233 if (!NT_SUCCESS(Status)) {
234 DPRINT("IoCreateDevice() failed with status 0x%X\n", Status);
235 ErrorStatus = Status;
236 ErrorOccurred = TRUE;
237 break;
238 }
239
240 Device->Pdo->Flags |= DO_BUS_ENUMERATED_DEVICE;
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 DPRINT("Called\n");
436
437 DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
438
439 if (IrpSp->Parameters.Power.Type == DevicePowerState) {
440 /* FIXME: Set device power state for the device */
441 Status = STATUS_UNSUCCESSFUL;
442 } else {
443 Status = STATUS_UNSUCCESSFUL;
444 }
445
446 return Status;
447 }
448
449
450 /*** PUBLIC ******************************************************************/
451
452 NTSTATUS
453 FdoPnpControl(
454 PDEVICE_OBJECT DeviceObject,
455 PIRP Irp)
456 /*
457 * FUNCTION: Handle Plug and Play IRPs for the PCI device object
458 * ARGUMENTS:
459 * DeviceObject = Pointer to functional device object of the PCI driver
460 * Irp = Pointer to IRP that should be handled
461 * RETURNS:
462 * Status
463 */
464 {
465 PFDO_DEVICE_EXTENSION DeviceExtension;
466 PIO_STACK_LOCATION IrpSp;
467 NTSTATUS Status;
468
469 DPRINT("Called\n");
470
471 DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
472
473 IrpSp = IoGetCurrentIrpStackLocation(Irp);
474 switch (IrpSp->MinorFunction) {
475 #if 0
476 case IRP_MN_CANCEL_REMOVE_DEVICE:
477 Status = STATUS_NOT_IMPLEMENTED;
478 break;
479
480 case IRP_MN_CANCEL_STOP_DEVICE:
481 Status = STATUS_NOT_IMPLEMENTED;
482 break;
483
484 case IRP_MN_DEVICE_USAGE_NOTIFICATION:
485 Status = STATUS_NOT_IMPLEMENTED;
486 break;
487
488 case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
489 Status = STATUS_NOT_IMPLEMENTED;
490 break;
491 #endif
492 case IRP_MN_QUERY_DEVICE_RELATIONS:
493 Status = FdoQueryBusRelations(DeviceObject, Irp, IrpSp);
494 break;
495 #if 0
496 case IRP_MN_QUERY_PNP_DEVICE_STATE:
497 Status = STATUS_NOT_IMPLEMENTED;
498 break;
499
500 case IRP_MN_QUERY_REMOVE_DEVICE:
501 Status = STATUS_NOT_IMPLEMENTED;
502 break;
503
504 case IRP_MN_QUERY_STOP_DEVICE:
505 Status = STATUS_NOT_IMPLEMENTED;
506 break;
507
508 case IRP_MN_REMOVE_DEVICE:
509 Status = STATUS_NOT_IMPLEMENTED;
510 break;
511 #endif
512 case IRP_MN_START_DEVICE:
513 DPRINT("IRP_MN_START_DEVICE received\n");
514 Status = FdoStartDevice(DeviceObject, Irp);
515 break;
516 case IRP_MN_STOP_DEVICE:
517 /* Currently not supported */
518 Status = STATUS_UNSUCCESSFUL;
519 break;
520 #if 0
521 case IRP_MN_SURPRISE_REMOVAL:
522 Status = STATUS_NOT_IMPLEMENTED;
523 break;
524 #endif
525 default:
526 DPRINT1("Unknown IOCTL 0x%lx\n", IrpSp->MinorFunction);
527 /* fall through */
528
529 case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
530 /*
531 * Do NOT complete the IRP as it will be processed by the lower
532 * device object, which will complete the IRP
533 */
534 IoSkipCurrentIrpStackLocation(Irp);
535 Status = IoCallDriver(DeviceExtension->Ldo, Irp);
536 return Status;
537 break;
538 }
539
540
541 if (Status != STATUS_PENDING) {
542 if (Status != STATUS_NOT_IMPLEMENTED)
543 Irp->IoStatus.Status = Status;
544 IoCompleteRequest(Irp, IO_NO_INCREMENT);
545 }
546
547 DPRINT("Leaving. Status 0x%X\n", Status);
548
549 return Status;
550 }
551
552
553 NTSTATUS
554 FdoPowerControl(
555 PDEVICE_OBJECT DeviceObject,
556 PIRP Irp)
557 /*
558 * FUNCTION: Handle power management IRPs for the PCI device object
559 * ARGUMENTS:
560 * DeviceObject = Pointer to functional device object of the PCI driver
561 * Irp = Pointer to IRP that should be handled
562 * RETURNS:
563 * Status
564 */
565 {
566 PIO_STACK_LOCATION IrpSp;
567 NTSTATUS Status;
568
569 DPRINT("Called\n");
570
571 IrpSp = IoGetCurrentIrpStackLocation(Irp);
572
573 switch (IrpSp->MinorFunction) {
574 case IRP_MN_SET_POWER:
575 Status = FdoSetPower(DeviceObject, Irp, IrpSp);
576 break;
577
578 default:
579 DPRINT("Unknown IOCTL 0x%X\n", IrpSp->MinorFunction);
580 Status = STATUS_NOT_IMPLEMENTED;
581 break;
582 }
583
584 if (Status != STATUS_PENDING) {
585 Irp->IoStatus.Status = Status;
586 IoCompleteRequest(Irp, IO_NO_INCREMENT);
587 }
588
589 DPRINT("Leaving. Status 0x%X\n", Status);
590
591 return Status;
592 }
593
594 /* EOF */