[PCI][HIDCLASS][HIDUSB][KBDHID][MOUHID][USBCCGP][USBOHCI][USBEHCI][USBHUB][USBSTOR]
[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 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 if (!Device->RemovePending) {
345 /* Reference the physical device object. The PnP manager
346 will dereference it again when it is no longer needed */
347 ObReferenceObject(Device->Pdo);
348
349 Relations->Objects[i] = Device->Pdo;
350
351 i++;
352 }
353
354 CurrentEntry = CurrentEntry->Flink;
355 }
356
357 if (ErrorOccurred) {
358 /* FIXME: Cleanup all new PDOs created in this call. Please give me SEH!!! ;-) */
359 /* FIXME: Should IoAttachDeviceToDeviceStack() be undone? */
360 if (PdoDeviceExtension) {
361 RtlFreeUnicodeString(&PdoDeviceExtension->DeviceID);
362 RtlFreeUnicodeString(&PdoDeviceExtension->InstanceID);
363 RtlFreeUnicodeString(&PdoDeviceExtension->HardwareIDs);
364 RtlFreeUnicodeString(&PdoDeviceExtension->CompatibleIDs);
365 RtlFreeUnicodeString(&PdoDeviceExtension->DeviceDescription);
366 RtlFreeUnicodeString(&PdoDeviceExtension->DeviceLocation);
367 }
368
369 ExFreePool(Relations);
370 return ErrorStatus;
371 }
372
373 Irp->IoStatus.Information = (ULONG_PTR)Relations;
374
375 DPRINT("Done\n");
376
377 return Status;
378 }
379
380
381 static NTSTATUS
382 FdoStartDevice(
383 IN PDEVICE_OBJECT DeviceObject,
384 IN PIRP Irp)
385 {
386 PFDO_DEVICE_EXTENSION DeviceExtension;
387 PCM_RESOURCE_LIST AllocatedResources;
388 PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor;
389 ULONG FoundBusNumber = FALSE;
390 ULONG i;
391
392 DPRINT("Called\n");
393
394 DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
395
396 AllocatedResources = IoGetCurrentIrpStackLocation(Irp)->Parameters.StartDevice.AllocatedResources;
397 if (!AllocatedResources)
398 {
399 DPRINT("No allocated resources sent to driver\n");
400 return STATUS_INSUFFICIENT_RESOURCES;
401 }
402 if (AllocatedResources->Count < 1)
403 {
404 DPRINT("Not enough allocated resources sent to driver\n");
405 return STATUS_INSUFFICIENT_RESOURCES;
406 }
407 if (AllocatedResources->List[0].PartialResourceList.Version != 1
408 || AllocatedResources->List[0].PartialResourceList.Revision != 1)
409 return STATUS_REVISION_MISMATCH;
410
411 ASSERT(DeviceExtension->State == dsStopped);
412
413 /* By default, use the bus number in the resource list header */
414 DeviceExtension->BusNumber = AllocatedResources->List[0].BusNumber;
415
416 for (i = 0; i < AllocatedResources->List[0].PartialResourceList.Count; i++)
417 {
418 ResourceDescriptor = &AllocatedResources->List[0].PartialResourceList.PartialDescriptors[i];
419 switch (ResourceDescriptor->Type)
420 {
421 case CmResourceTypeBusNumber:
422 {
423 if (FoundBusNumber || ResourceDescriptor->u.BusNumber.Length != 1)
424 return STATUS_INVALID_PARAMETER;
425 /* Use this one instead */
426 ASSERT(AllocatedResources->List[0].BusNumber == ResourceDescriptor->u.BusNumber.Start);
427 DeviceExtension->BusNumber = ResourceDescriptor->u.BusNumber.Start;
428 DPRINT("Found bus number resource: %lu\n", DeviceExtension->BusNumber);
429 FoundBusNumber = TRUE;
430 break;
431 }
432 default:
433 DPRINT("Unknown resource descriptor type 0x%x\n", ResourceDescriptor->Type);
434 }
435 }
436
437 InitializeListHead(&DeviceExtension->DeviceListHead);
438 KeInitializeSpinLock(&DeviceExtension->DeviceListLock);
439 DeviceExtension->DeviceListCount = 0;
440 DeviceExtension->State = dsStarted;
441
442 ExInterlockedInsertTailList(
443 &DriverExtension->BusListHead,
444 &DeviceExtension->ListEntry,
445 &DriverExtension->BusListLock);
446
447 Irp->IoStatus.Information = 0;
448
449 return STATUS_SUCCESS;
450 }
451
452
453 static NTSTATUS
454 FdoSetPower(
455 IN PDEVICE_OBJECT DeviceObject,
456 IN PIRP Irp,
457 PIO_STACK_LOCATION IrpSp)
458 {
459 NTSTATUS Status;
460
461 UNREFERENCED_PARAMETER(Irp);
462
463 DPRINT("Called\n");
464
465 if (IrpSp->Parameters.Power.Type == DevicePowerState) {
466 /* FIXME: Set device power state for the device */
467 Status = STATUS_UNSUCCESSFUL;
468 } else {
469 Status = STATUS_UNSUCCESSFUL;
470 }
471
472 return Status;
473 }
474
475
476 /*** PUBLIC ******************************************************************/
477
478 NTSTATUS
479 FdoPnpControl(
480 PDEVICE_OBJECT DeviceObject,
481 PIRP Irp)
482 /*
483 * FUNCTION: Handle Plug and Play IRPs for the PCI device object
484 * ARGUMENTS:
485 * DeviceObject = Pointer to functional device object of the PCI driver
486 * Irp = Pointer to IRP that should be handled
487 * RETURNS:
488 * Status
489 */
490 {
491 PFDO_DEVICE_EXTENSION DeviceExtension;
492 PIO_STACK_LOCATION IrpSp;
493 NTSTATUS Status = Irp->IoStatus.Status;
494
495 DPRINT("Called\n");
496
497 DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
498
499 IrpSp = IoGetCurrentIrpStackLocation(Irp);
500 switch (IrpSp->MinorFunction) {
501 #if 0
502 case IRP_MN_CANCEL_REMOVE_DEVICE:
503 Status = STATUS_NOT_IMPLEMENTED;
504 break;
505
506 case IRP_MN_CANCEL_STOP_DEVICE:
507 Status = STATUS_NOT_IMPLEMENTED;
508 break;
509
510 case IRP_MN_DEVICE_USAGE_NOTIFICATION:
511 Status = STATUS_NOT_IMPLEMENTED;
512 break;
513
514 case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
515 Status = STATUS_NOT_IMPLEMENTED;
516 break;
517 #endif
518 case IRP_MN_QUERY_DEVICE_RELATIONS:
519 if (IrpSp->Parameters.QueryDeviceRelations.Type != BusRelations)
520 break;
521
522 Status = FdoQueryBusRelations(DeviceObject, Irp, IrpSp);
523 Irp->IoStatus.Status = Status;
524 IoCompleteRequest(Irp, IO_NO_INCREMENT);
525 return Status;
526 #if 0
527 case IRP_MN_QUERY_PNP_DEVICE_STATE:
528 Status = STATUS_NOT_IMPLEMENTED;
529 break;
530
531 case IRP_MN_QUERY_REMOVE_DEVICE:
532 Status = STATUS_NOT_IMPLEMENTED;
533 break;
534 #endif
535 case IRP_MN_START_DEVICE:
536 DPRINT("IRP_MN_START_DEVICE received\n");
537 Status = ForwardIrpAndWait(DeviceObject, Irp);
538 if (NT_SUCCESS(Status))
539 Status = FdoStartDevice(DeviceObject, Irp);
540
541 Irp->IoStatus.Status = Status;
542 IoCompleteRequest(Irp, IO_NO_INCREMENT);
543 return Status;
544
545 case IRP_MN_QUERY_STOP_DEVICE:
546 /* We don't support stopping yet */
547 Status = STATUS_UNSUCCESSFUL;
548 Irp->IoStatus.Status = Status;
549 IoCompleteRequest(Irp, IO_NO_INCREMENT);
550 return Status;
551
552 case IRP_MN_STOP_DEVICE:
553 /* We can't fail this one so we fail the QUERY_STOP request that precedes it */
554 break;
555 #if 0
556 case IRP_MN_SURPRISE_REMOVAL:
557 Status = STATUS_NOT_IMPLEMENTED;
558 break;
559 #endif
560 case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
561 break;
562 case IRP_MN_REMOVE_DEVICE:
563 /* Detach the device object from the device stack */
564 IoDetachDevice(DeviceExtension->Ldo);
565
566 /* Delete the device object */
567 IoDeleteDevice(DeviceObject);
568
569 /* Return success */
570 Status = STATUS_SUCCESS;
571 break;
572 default:
573 DPRINT1("Unknown IOCTL 0x%lx\n", IrpSp->MinorFunction);
574 break;
575 }
576
577 Irp->IoStatus.Status = Status;
578 IoSkipCurrentIrpStackLocation(Irp);
579 Status = IoCallDriver(DeviceExtension->Ldo, Irp);
580
581 DPRINT("Leaving. Status 0x%X\n", Status);
582
583 return Status;
584 }
585
586
587 NTSTATUS
588 FdoPowerControl(
589 PDEVICE_OBJECT DeviceObject,
590 PIRP Irp)
591 /*
592 * FUNCTION: Handle power management IRPs for the PCI device object
593 * ARGUMENTS:
594 * DeviceObject = Pointer to functional device object of the PCI driver
595 * Irp = Pointer to IRP that should be handled
596 * RETURNS:
597 * Status
598 */
599 {
600 PIO_STACK_LOCATION IrpSp;
601 NTSTATUS Status;
602
603 DPRINT("Called\n");
604
605 IrpSp = IoGetCurrentIrpStackLocation(Irp);
606
607 switch (IrpSp->MinorFunction) {
608 case IRP_MN_SET_POWER:
609 Status = FdoSetPower(DeviceObject, Irp, IrpSp);
610 break;
611
612 default:
613 DPRINT("Unknown IOCTL 0x%X\n", IrpSp->MinorFunction);
614 Status = STATUS_NOT_IMPLEMENTED;
615 break;
616 }
617
618 if (Status != STATUS_PENDING) {
619 Irp->IoStatus.Status = Status;
620 IoCompleteRequest(Irp, IO_NO_INCREMENT);
621 }
622
623 DPRINT("Leaving. Status 0x%X\n", Status);
624
625 return Status;
626 }
627
628 /* EOF */