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