|
|
/*++
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
--*/
{ #if DEBUG
UNICODE_STRING unicodeString; #endif
PDEVICE_OBJECT deviceObject; NTSTATUS Status; PECDATA EcData;
PAGED_CODE();
EcPrint(EC_LOW, ("AcpiEcCreateFdo: Entry\n") );
#if DEBUG
RtlInitUnicodeString(&unicodeString, L"\\Device\\ACPIEC"); #endif
Status = IoCreateDevice( DriverObject, sizeof (ECDATA), #if DEBUG
&unicodeString, #else
NULL, #endif
FILE_DEVICE_UNKNOWN, // DeviceType
FILE_DEVICE_SECURE_OPEN, 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; i<partialResourceList->Count; 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; }
|