/*++ Copyright (c) 1996 Microsoft Corporation Module Name: ecpnp.c Abstract: ACPI Embedded Controller Driver, Plug and Play support Author: Bob Moore (Intel) Environment: Kernel mode Notes: Revision History: --*/ #include "ecp.h" // // List of FDOs managed by this driver // extern PDEVICE_OBJECT FdoList; // // Table of direct-call interfaces into the ACPI driver // ACPI_INTERFACE_STANDARD AcpiInterfaces; NTSTATUS AcpiEcIoCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PKEVENT pdoIoCompletedEvent ) /*++ Routine Description: Completion function for synchronous IRPs sent be this driver. Context is the event to set. --*/ { KeSetEvent(pdoIoCompletedEvent, IO_NO_INCREMENT, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS AcpiEcAddDevice( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT Pdo ) /*++ Routine Description: This routine creates functional device objects for each AcpiEc controller in the system and attaches them to the physical device objects for the controllers Arguments: DriverObject - a pointer to the object for this driver NewDeviceObject - a pointer to where the FDO is placed Return Value: Status from device creation and initialization --*/ { PDEVICE_OBJECT fdo = NULL; PDEVICE_OBJECT ownerDevice = NULL; PDEVICE_OBJECT lowerDevice = NULL; PECDATA EcData; NTSTATUS status; PAGED_CODE(); EcPrint(EC_LOW, ("AcpiEcAddDevice: Entered with pdo %x\n", Pdo)); if (Pdo == NULL) { // // Have we been asked to do detection on our own? // if so just return no more devices // EcPrint(EC_LOW, ("AcpiEcAddDevice - asked to do detection\n")); return STATUS_NO_MORE_ENTRIES; } // // Create and initialize the new functional device object // status = AcpiEcCreateFdo(DriverObject, &fdo); if (!NT_SUCCESS(status)) { EcPrint(EC_LOW, ("AcpiEcAddDevice - error creating Fdo\n")); return status; } // // Layer our FDO on top of the PDO // lowerDevice = IoAttachDeviceToDeviceStack(fdo,Pdo); // // No status. Do the best we can. // ASSERT(lowerDevice); EcData = fdo->DeviceExtension; EcData->LowerDeviceObject = lowerDevice; EcData->Pdo = Pdo; // // Allocate and hold an IRP for Query notifications and miscellaneous // EcData->QueryRequest = IoAllocateIrp (EcData->LowerDeviceObject->StackSize, FALSE); EcData->MiscRequest = IoAllocateIrp (EcData->LowerDeviceObject->StackSize, FALSE); if ((!EcData->QueryRequest) || (!EcData->MiscRequest)) { // // NOTE: This failure case and other failure cases below should do // cleanup of all previous allocations, etc performed in this function. // EcPrint(EC_ERROR, ("AcpiEcAddDevice: Couldn't allocate Irp\n")); return STATUS_INSUFFICIENT_RESOURCES; } // // Link this fdo to the list of fdo's managed by the driver // (Probably overkill since there will be only one FDO) // // EcPrint(EC_LOW, ("AcpiEcAddDevice: linking fdo to list\n")); EcData->NextFdo = FdoList; InterlockedExchangePointer((PVOID *) &FdoList, fdo); // // Initialize the Timeout DPC // KeInitializeTimer(&EcData->WatchdogTimer); KeInitializeDpc(&EcData->WatchdogDpc, AcpiEcWatchdogDpc, EcData); // // Get the GPE vector assigned to this device // status = AcpiEcGetGpeVector (EcData); if (!NT_SUCCESS(status)) { EcPrint(EC_LOW, ("AcpiEcAddDevice: Could not get GPE vector, status = %Lx\n", status)); return status; } // // Get the direct-call ACPI interfaces. // status = AcpiEcGetAcpiInterfaces (EcData); if (!NT_SUCCESS(status)) { EcPrint(EC_LOW, ("AcpiEcAddDevice: Could not get ACPI driver interfaces, status = %Lx\n", status)); return status; } // // Final flags // fdo->Flags &= ~DO_DEVICE_INITIALIZING; fdo->Flags |= DO_POWER_PAGABLE; // Don't want power Irps at irql 2 return STATUS_SUCCESS; } NTSTATUS AcpiEcCreateFdo( IN PDRIVER_OBJECT DriverObject, OUT PDEVICE_OBJECT *NewDeviceObject ) /*++ Routine Description: This routine will create and initialize a functional device object to be attached to a Embedded controller PDO. Arguments: DriverObject - a pointer to the driver object this is created under DeviceObject - a location to store the pointer to the new device object Return Value: STATUS_SUCCESS if everything was successful reason for failure otherwise --*/ { UNICODE_STRING unicodeString; PDEVICE_OBJECT deviceObject; NTSTATUS Status; PECDATA EcData; PAGED_CODE(); EcPrint(EC_LOW, ("AcpiEcCreateFdo: Entry\n") ); RtlInitUnicodeString(&unicodeString, L"\\Device\\ACPIEC"); Status = IoCreateDevice( DriverObject, sizeof (ECDATA), &unicodeString, FILE_DEVICE_UNKNOWN, // DeviceType 0, FALSE, &deviceObject ); if (Status != STATUS_SUCCESS) { EcPrint(EC_LOW, ("AcpiEcCreateFdo: unable to create device object: %X\n", Status)); return(Status); } deviceObject->Flags |= DO_BUFFERED_IO; deviceObject->StackSize = 1; // // Initialize EC device extension data // EcData = (PECDATA) deviceObject->DeviceExtension; EcData->DeviceObject = deviceObject; EcData->DeviceState = EC_DEVICE_WORKING; EcData->QueryState = EC_QUERY_IDLE; EcData->IoState = EC_IO_NONE; EcData->IsStarted = FALSE; EcData->MaxBurstStall = 50; EcData->MaxNonBurstStall = 10; EcData->InterruptEnabled = TRUE; EcData->ConsecutiveFailures = 0; KeQueryPerformanceCounter (&EcData->PerformanceFrequency); RtlFillMemory (EcData->RecentActions, ACPIEC_ACTION_COUNT * sizeof(ACPIEC_ACTION), 0); // // Initialize EC global synchronization objects // InitializeListHead (&EcData->WorkQueue); KeInitializeEvent (&EcData->Unload, NotificationEvent, FALSE); KeInitializeSpinLock (&EcData->Lock); *NewDeviceObject = deviceObject; return STATUS_SUCCESS; } NTSTATUS AcpiEcPnpDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is the dispatch routine for plug and play requests. Arguments: DeviceObject - Pointer to class device object. Irp - Pointer to the request packet. Return Value: Status is returned. --*/ { PIO_STACK_LOCATION irpStack; PECDATA EcData; NTSTATUS status; PAGED_CODE(); // // Get a pointer to the current parameters for this request. The // information is contained in the current stack location. // irpStack = IoGetCurrentIrpStackLocation(Irp); EcData = DeviceObject->DeviceExtension; EcPrint (EC_NOTE, ("AcpiEcPnpDispatch: PnP dispatch, minor = %d\n", irpStack->MinorFunction)); // // Dispatch minor function // switch (irpStack->MinorFunction) { case IRP_MN_START_DEVICE: { status = AcpiEcStartDevice (DeviceObject, Irp); Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); break; } // // We will never allow the EC driver to stop once it is started. // // Note: Stop and remove device should be implemented so that the driver // can be unloaded without reboot. Even if the device can't be removed, it // will get an IRP_MN_REMOVE_DEVICE if somthing goes wrong trying to start // the device. // case IRP_MN_QUERY_STOP_DEVICE: case IRP_MN_QUERY_REMOVE_DEVICE: case IRP_MN_STOP_DEVICE: case IRP_MN_REMOVE_DEVICE: status = Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; IoCompleteRequest( Irp, IO_NO_INCREMENT ); break; case IRP_MN_CANCEL_STOP_DEVICE: case IRP_MN_CANCEL_REMOVE_DEVICE: case IRP_MN_SURPRISE_REMOVAL: Irp->IoStatus.Status = STATUS_SUCCESS; AcpiEcCallLowerDriver(status, EcData->LowerDeviceObject, Irp); break; #if 0 case IRP_MN_STOP_DEVICE: { status = AcpiEcStopDevice(DeviceObject, Irp); Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); break; } #endif case IRP_MN_QUERY_DEVICE_RELATIONS: { EcPrint(EC_LOW, ("AcpiEcPnp: IRP_MJ_QUERY_DEVICE_RELATIONS Type: %d\n", irpStack->Parameters.QueryDeviceRelations.Type)); // // Just pass it down to ACPI // AcpiEcCallLowerDriver(status, EcData->LowerDeviceObject, Irp); break; } default: { // // Unimplemented minor, Pass this down to ACPI // EcPrint(EC_LOW, ("AcpiEcPnp: Unimplemented PNP minor code %d, forwarding\n", irpStack->MinorFunction)); AcpiEcCallLowerDriver(status, EcData->LowerDeviceObject, Irp); break; } } return status; } NTSTATUS AcpiEcGetResources( IN PCM_RESOURCE_LIST ResourceList, IN PECDATA EcData ) /*++ Routine Description: Get the resources already allocated and pointed to by the PDO. Arguments: ResourceList - Pointer to the resource list. EcData - Pointer to the extension. Return Value: Status is returned. --*/ { PCM_FULL_RESOURCE_DESCRIPTOR fullResourceDesc; PCM_PARTIAL_RESOURCE_LIST partialResourceList; PCM_PARTIAL_RESOURCE_DESCRIPTOR partialResourceDesc; ULONG i; PUCHAR port[2] = {NULL, NULL}; PAGED_CODE(); if (ResourceList == NULL) { EcPrint(EC_LOW, ("AcpiEcGetResources: Null resource pointer\n")); return STATUS_NO_MORE_ENTRIES; } if (ResourceList->Count <= 0 ) { return STATUS_UNSUCCESSFUL; } // // Traverse the resource list // fullResourceDesc=&ResourceList->List[0]; partialResourceList = &fullResourceDesc->PartialResourceList; partialResourceDesc = partialResourceList->PartialDescriptors; for (i=0; iCount; i++, partialResourceDesc++) { if (partialResourceDesc->Type == CmResourceTypePort) { port[i] = (PUCHAR)((ULONG_PTR)partialResourceDesc->u.Port.Start.LowPart); } } // // Get the important things // EcData->StatusPort = port[1]; // Status port same as Command port EcData->CommandPort = port[1]; EcData->DataPort = port[0]; EcPrint(EC_LOW, ("AcpiEcGetResources: Status/Command port %x, Data port %x\n", port[1], port[0])); return STATUS_SUCCESS; } NTSTATUS AcpiEcStartDevice( IN PDEVICE_OBJECT Fdo, IN PIRP Irp ) /*++ Routine Description: Start a device Arguments: Fdo - Pointer to the Functional Device Object. Irp - Pointer to the request packet. Return Value: Status is returned. --*/ { NTSTATUS status; PECDATA EcData = Fdo->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); EcPrint(EC_LOW, ("AcpiEcStartDevice: Entered with fdo %x\n", Fdo)); // // Always send this down to the PDO first // status = AcpiEcForwardIrpAndWait (EcData, Irp); if (!NT_SUCCESS(status)) { return status; } if (EcData->IsStarted) { // // Device is already started // EcPrint(EC_WARN, ("AcpiEcStartDevice: Fdo %x already started\n", Fdo)); return STATUS_SUCCESS; } // // Parse AllocatedResources. // status = AcpiEcGetResources (irpStack->Parameters.StartDevice.AllocatedResources, EcData); if (!NT_SUCCESS(status)) { EcPrint(EC_ERROR, ("AcpiEcStartDevice: Could not get resources, status = %x\n", status)); return status; } // // Connect to the dedicated embedded controller GPE // status = AcpiEcConnectGpeVector (EcData); if (!NT_SUCCESS(status)) { EcPrint(EC_ERROR, ("AcpiEcStartDevice: Could not attach to GPE vector, status = %Lx\n", status)); return status; } EcPrint(EC_NOTE, ("AcpiEcStartDevice: Attached to GPE vector %d\n", EcData->GpeVector)); // // Install the Operation Region handler // status = AcpiEcInstallOpRegionHandler (EcData); if (!NT_SUCCESS(status)) { EcPrint(EC_ERROR, ("AcpiEcStartDevice: Could not install Op region handler, status = %Lx\n", status)); return status; } EcData->IsStarted = TRUE; return STATUS_SUCCESS; } NTSTATUS AcpiEcStopDevice( IN PDEVICE_OBJECT Fdo, IN PIRP Irp ) /*++ Routine Description: Stop a device Arguments: Fdo - Pointer to the Functional Device Object. Irp - Pointer to the request packet. Return Value: Status is returned. --*/ { PECDATA EcData = Fdo->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); NTSTATUS status; EcPrint(EC_LOW, ("AcpiEcStopDevice: Entered with fdo %x\n", Fdo)); // // Always send this down to the PDO // status = AcpiEcForwardIrpAndWait (EcData, Irp); if (!NT_SUCCESS(status)) { return status; } if (!EcData->IsStarted) { // // Already stopped // return STATUS_SUCCESS; } // // Must disconnect from GPE // status = AcpiEcDisconnectGpeVector (EcData); if (!NT_SUCCESS(status)) { return status; } // // Must de-install Operation Region Handler // status = AcpiEcRemoveOpRegionHandler (EcData); if (!NT_SUCCESS(status)) { return status; } EcPrint(EC_LOW, ("AcpiEcStopDevice: Detached from GPE and Op Region\n")); // // Now the device is stopped. // EcData->IsStarted = FALSE; // Mark device stopped return status; }