You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
640 lines
15 KiB
640 lines
15 KiB
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
query.c
|
|
|
|
Abstract:
|
|
|
|
ACPI Embedded Controller Driver - query dispatching
|
|
|
|
Author:
|
|
|
|
Ken Reneris
|
|
|
|
Environment:
|
|
|
|
Notes:
|
|
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "ecp.h"
|
|
|
|
|
|
|
|
NTSTATUS
|
|
AcpiEcCompleteQueryMethod (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE,AcpiEcUnloadPending)
|
|
#pragma alloc_text(PAGE,AcpiEcConnectHandler)
|
|
#pragma alloc_text(PAGE,AcpiEcDisconnectHandler)
|
|
#endif
|
|
|
|
UCHAR rgHexDigit[] = "0123456789ABCDEF";
|
|
|
|
|
|
NTSTATUS
|
|
AcpiEcRunQueryMethod (
|
|
IN PECDATA EcData,
|
|
IN ULONG QueryIndex
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine runs the query control method that corresponds to the QueryIndex.
|
|
|
|
Arguments:
|
|
|
|
EcData - Pointer to the EC extension
|
|
QueryIndex - The query to run
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
ACPI_EVAL_INPUT_BUFFER inputBuffer;
|
|
NTSTATUS status;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PIRP irp;
|
|
|
|
ASSERT (QueryIndex <= MAX_QUERY);
|
|
|
|
//
|
|
// Note: because the ACPI control method is using INPUT data only and
|
|
// this information is grabbed before STATUS_PENDING is returned, it is
|
|
// safe to allocate the storage for this data on the stack.
|
|
//
|
|
// However, because this is a method that can be called at DISPATCH_LEVEL
|
|
// and because we want to reuse the same irp over and over again, it is not
|
|
// safe to call IoBuildDeviceIoControlRequest for this request
|
|
//
|
|
|
|
//
|
|
// Initialize the input data
|
|
//
|
|
RtlZeroMemory( &inputBuffer, sizeof(ACPI_EVAL_INPUT_BUFFER) );
|
|
inputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE;
|
|
inputBuffer.MethodNameAsUlong = '00Q_';
|
|
inputBuffer.MethodName[2] = rgHexDigit[ QueryIndex / 16];
|
|
inputBuffer.MethodName[3] = rgHexDigit[ QueryIndex % 16];
|
|
|
|
EcPrint(
|
|
EC_NOTE,
|
|
("AcpiEcRunQueryMethod: Running query control method %.4s\n",
|
|
inputBuffer.MethodName )
|
|
);
|
|
|
|
//
|
|
// Setup the (pre-allocated) Irp
|
|
//
|
|
irp = EcData->QueryRequest;
|
|
irpSp = IoGetNextIrpStackLocation(irp);
|
|
|
|
//
|
|
// Setup the call
|
|
//
|
|
irpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
|
|
irpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_ACPI_ASYNC_EVAL_METHOD;
|
|
irpSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(ACPI_EVAL_INPUT_BUFFER);
|
|
irpSp->Parameters.DeviceIoControl.OutputBufferLength = 0;
|
|
irp->AssociatedIrp.SystemBuffer = &inputBuffer;
|
|
|
|
//
|
|
// only matters if it is buffered
|
|
//
|
|
irp->Flags |= IRP_INPUT_OPERATION;
|
|
|
|
//
|
|
// We want to reuse this irp, so we need to set a completion routine.
|
|
// This will also let us know when the irp is done
|
|
//
|
|
IoSetCompletionRoutine(
|
|
irp,
|
|
AcpiEcCompleteQueryMethod,
|
|
EcData,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
|
|
//
|
|
// Pass request to Pdo (ACPI driver). This is an asynchronous request
|
|
//
|
|
status = IoCallDriver(EcData->Pdo, irp);
|
|
|
|
//
|
|
// What happened?
|
|
//
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
EcPrint(
|
|
EC_LOW,
|
|
("AcpiEcRunQueryMethod: Query Control Method failed, status = %Lx\n",
|
|
status )
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// Done
|
|
//
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpiEcCompleteQueryMethod (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the routine that is called after the ACPI driver has finished
|
|
running the _Qxx method. This routine is here so that we can do the
|
|
'correct' thing after the method is complete.
|
|
|
|
Note: We cannot touch Irp->AssociatedIrp.SystemBuffer here because the
|
|
stack that it might have been on has probably been reclaimed. If it becomes
|
|
important to touch this data, then we must allocate the parameters as
|
|
part of non-paged pool
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Us
|
|
Irp - Request that was completed
|
|
Context - EcData;
|
|
|
|
--*/
|
|
{
|
|
KIRQL OldIrql;
|
|
PECDATA EcData = (PECDATA) Context;
|
|
BOOLEAN ProcessQuery;
|
|
|
|
#if DEBUG
|
|
//
|
|
// What happened to the irp?
|
|
//
|
|
if (!NT_SUCCESS(Irp->IoStatus.Status)) {
|
|
|
|
EcPrint(
|
|
EC_LOW,
|
|
("AcpiEcCompleteQueryMethod: Query Method failed, status = %08x\n",
|
|
Irp->IoStatus.Status )
|
|
);
|
|
|
|
} else {
|
|
|
|
EcPrint(
|
|
EC_NOTE,
|
|
("AcpiEcCompleteQueryMethod: QueryMethod succeeded.\n")
|
|
);
|
|
|
|
}
|
|
#endif
|
|
|
|
ProcessQuery = FALSE;
|
|
KeAcquireSpinLock (&EcData->Lock, &OldIrql);
|
|
|
|
switch (EcData->QueryState) {
|
|
case EC_QUERY_DISPATCH:
|
|
EcData->QueryState = EC_QUERY_DISPATCH_COMPLETE;
|
|
break;
|
|
|
|
case EC_QUERY_DISPATCH_WAITING:
|
|
EcData->QueryState = EC_QUERY_IDLE;
|
|
ProcessQuery = TRUE;
|
|
break;
|
|
|
|
default:
|
|
// internal error
|
|
ASSERT (FALSE);
|
|
break;
|
|
}
|
|
|
|
KeReleaseSpinLock (&EcData->Lock, OldIrql);
|
|
|
|
if (ProcessQuery) {
|
|
AcpiEcDispatchQueries(EcData);
|
|
}
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
AcpiEcDispatchQueries (
|
|
IN PECDATA EcData
|
|
)
|
|
{
|
|
KIRQL OldIrql;
|
|
ULONG i, j;
|
|
ULONG Id, Vector;
|
|
PVECTOR_HANDLER Handler;
|
|
PVOID Context;
|
|
|
|
|
|
KeAcquireSpinLock (&EcData->Lock, &OldIrql);
|
|
|
|
//
|
|
// Run the vector pending list
|
|
//
|
|
|
|
while (EcData->VectorHead) {
|
|
|
|
Id = EcData->VectorHead;
|
|
Vector = EcData->VectorTable[Id].Vector;
|
|
i = Vector / BITS_PER_ULONG;
|
|
j = 1 << (Vector % BITS_PER_ULONG);
|
|
|
|
//
|
|
// Remove vector from list
|
|
//
|
|
|
|
EcData->QuerySet[i] &= ~j;
|
|
EcData->VectorHead = EcData->VectorTable[Id].Next;
|
|
|
|
//
|
|
// Dispatch it
|
|
//
|
|
|
|
Handler = EcData->VectorTable[Id].Handler;
|
|
Context = EcData->VectorTable[Id].Context;
|
|
KeReleaseSpinLock (&EcData->Lock, OldIrql);
|
|
|
|
Handler (Vector, Context);
|
|
|
|
KeAcquireSpinLock (&EcData->Lock, &OldIrql);
|
|
}
|
|
|
|
//
|
|
// If QueryState is idle, start dispatching
|
|
//
|
|
|
|
if (EcData->QueryState == EC_QUERY_IDLE) {
|
|
|
|
//
|
|
// Run query pending list
|
|
//
|
|
|
|
while (EcData->QueryHead) {
|
|
|
|
Id = EcData->QueryHead;
|
|
i = Id / BITS_PER_ULONG;
|
|
j = 1 << (Id % BITS_PER_ULONG);
|
|
|
|
//
|
|
// Remove query from list
|
|
//
|
|
|
|
EcData->QuerySet[i] &= ~j;
|
|
EcData->QueryHead = EcData->QueryMap[Id];
|
|
|
|
EcData->QueryState = EC_QUERY_DISPATCH;
|
|
KeReleaseSpinLock (&EcData->Lock, OldIrql);
|
|
|
|
//
|
|
// Run control method for this event
|
|
//
|
|
|
|
EcPrint(EC_NOTE, ("AcpiEcDispatchQueries: Query %x\n", Id));
|
|
AcpiEcRunQueryMethod (EcData, Id);
|
|
|
|
|
|
//
|
|
// If irp is complete the state will be dispatch_complete, loop
|
|
// and process the next bit. Else, wait for irp to return
|
|
//
|
|
|
|
KeAcquireSpinLock (&EcData->Lock, &OldIrql);
|
|
if (EcData->QueryState == EC_QUERY_DISPATCH) {
|
|
//
|
|
// It's not complete, wait for it to complete
|
|
//
|
|
|
|
EcData->QueryState = EC_QUERY_DISPATCH_WAITING;
|
|
KeReleaseSpinLock (&EcData->Lock, OldIrql);
|
|
return ;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// No longer dispatching query events
|
|
//
|
|
|
|
EcData->QueryState = EC_QUERY_IDLE;
|
|
|
|
//
|
|
// If unload is pending, check to see if the device can be unloaded now
|
|
//
|
|
|
|
if (EcData->DeviceState == EC_DEVICE_UNLOAD_PENDING) {
|
|
AcpiEcUnloadPending (EcData);
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLock (&EcData->Lock, OldIrql);
|
|
}
|
|
|
|
VOID
|
|
AcpiEcUnloadPending (
|
|
IN PECDATA EcData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called when state is unload pending and some portion of the state
|
|
has gone idle. If the entire device state is idle, the unload is
|
|
stated.
|
|
|
|
Arguments:
|
|
|
|
EcData - Pointer to embedded controller to service.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
|
|
ASSERT (EcData->DeviceState == EC_DEVICE_UNLOAD_PENDING);
|
|
|
|
//
|
|
// Check if device is idle for unload operation
|
|
//
|
|
|
|
if (EcData->QueryState == EC_QUERY_IDLE &&
|
|
EcData->InService == FALSE &&
|
|
EcData->IoState == EC_IO_NONE) {
|
|
|
|
//
|
|
// Promote unloading device state to next step (which
|
|
// is to clean up the fake ISR timer)
|
|
//
|
|
|
|
EcData->DeviceState = EC_DEVICE_UNLOAD_CANCEL_TIMER;
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AcpiEcConnectHandler (
|
|
IN PECDATA EcData,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This functions connects a specific handled to an Ec query vector
|
|
|
|
Arguments:
|
|
|
|
EcData - Pointer to embedded controller to service.
|
|
|
|
Irp - IOCTL conntain connect request
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
KIRQL OldIrql;
|
|
PVOID LockPtr;
|
|
NTSTATUS Status;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
PEC_HANDLER_REQUEST Req;
|
|
PVECTOR_TABLE Vector;
|
|
ULONG by, bi, i, j;
|
|
ULONG TableIndex;
|
|
|
|
PAGED_CODE ();
|
|
|
|
//
|
|
// Get request
|
|
//
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
Req = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(EC_HANDLER_REQUEST)) {
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
//
|
|
// Setup data concerning request
|
|
//
|
|
|
|
by = Req->Vector / BITS_PER_ULONG;
|
|
bi = 1 << (Req->Vector % BITS_PER_ULONG);
|
|
|
|
//
|
|
// Lock device
|
|
//
|
|
|
|
LockPtr = MmLockPagableCodeSection(AcpiEcConnectHandler);
|
|
KeAcquireSpinLock (&EcData->Lock, &OldIrql);
|
|
|
|
//
|
|
// If device handler already set, then fail the request
|
|
//
|
|
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
if (!(EcData->QueryType[by] & bi)) {
|
|
//
|
|
// No handler set, allocate vector entry for it
|
|
//
|
|
|
|
EcData->QueryType[by] |= bi;
|
|
if (!EcData->VectorFree) {
|
|
//
|
|
// No free entries on vector table, make some
|
|
//
|
|
|
|
i = EcData->VectorTableSize;
|
|
Vector = ExAllocatePoolWithTag (
|
|
NonPagedPool,
|
|
sizeof (VECTOR_TABLE) * (i + 4),
|
|
'V_CE'
|
|
);
|
|
|
|
if (!Vector) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto AcpiEcConnectHandlerExit;
|
|
}
|
|
|
|
if (EcData->VectorTable) {
|
|
memcpy (Vector, EcData->VectorTable, sizeof (VECTOR_TABLE) * i);
|
|
ExFreePool (EcData->VectorTable);
|
|
}
|
|
|
|
EcData->VectorTableSize += 4;
|
|
EcData->VectorTable = Vector;
|
|
|
|
for (j=0; j < 4; j++) {
|
|
EcData->VectorTable[i+j].Next = EcData->VectorFree;
|
|
EcData->VectorFree = (UCHAR) (i+j);
|
|
}
|
|
}
|
|
|
|
TableIndex = EcData->VectorFree;
|
|
Vector = &EcData->VectorTable[TableIndex];
|
|
EcData->VectorFree = Vector->Next;
|
|
|
|
//
|
|
// Build mapping for the vector
|
|
//
|
|
|
|
if (EcData->QueryMap[Req->Vector]) {
|
|
//
|
|
// Vector is in query pending list, remove it
|
|
//
|
|
|
|
EcData->QuerySet[by] &= ~bi;
|
|
for (i = EcData->QueryHead; i; i = EcData->QueryMap[i]) {
|
|
if (EcData->QueryMap[i] == Req->Vector) {
|
|
EcData->QueryMap[i] = EcData->QueryMap[Req->Vector];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
EcData->QueryMap[Req->Vector] = (UCHAR) TableIndex;
|
|
|
|
//
|
|
// Initialize vector handler
|
|
//
|
|
|
|
Vector->Next = 0;
|
|
Vector->Vector = (UCHAR) Req->Vector;
|
|
Vector->Handler = Req->Handler;
|
|
Vector->Context = Req->Context;
|
|
Req->AllocationHandle = (PVOID)((ULONG_PTR)TableIndex);
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
AcpiEcConnectHandlerExit:
|
|
//
|
|
// Unlock device and return status
|
|
//
|
|
|
|
KeReleaseSpinLock (&EcData->Lock, OldIrql);
|
|
MmUnlockPagableImageSection(LockPtr);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpiEcDisconnectHandler (
|
|
IN PECDATA EcData,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This functions disconnects a specific handled to an Ec query vector
|
|
|
|
Arguments:
|
|
|
|
EcData - Pointer to embedded controller to service.
|
|
|
|
Irp - IOCTL conntain connect request
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
KIRQL OldIrql;
|
|
PVOID LockPtr;
|
|
NTSTATUS Status;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
PEC_HANDLER_REQUEST Req;
|
|
ULONG by, bi, i;
|
|
ULONG TableIndex;
|
|
|
|
PAGED_CODE ();
|
|
|
|
//
|
|
// Get request
|
|
//
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
Req = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(EC_HANDLER_REQUEST)) {
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
//
|
|
// Setup data concerning request
|
|
//
|
|
|
|
by = Req->Vector / BITS_PER_ULONG;
|
|
bi = 1 << (Req->Vector % BITS_PER_ULONG);
|
|
|
|
//
|
|
// Lock device
|
|
//
|
|
|
|
LockPtr = MmLockPagableCodeSection(AcpiEcDisconnectHandler);
|
|
KeAcquireSpinLock (&EcData->Lock, &OldIrql);
|
|
|
|
//
|
|
// If device handler already set, then fail the request
|
|
//
|
|
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
if (EcData->QueryType[by] & bi) {
|
|
//
|
|
// Clear handler
|
|
//
|
|
|
|
EcData->QueryType[by] &= ~bi;
|
|
TableIndex = EcData->QueryMap[Req->Vector];
|
|
ASSERT (Req->AllocationHandle == (PVOID)((ULONG_PTR)TableIndex));
|
|
|
|
//
|
|
// If pending, drop it
|
|
//
|
|
|
|
if (EcData->QuerySet[by] & bi) {
|
|
EcData->QuerySet[by] &= ~bi;
|
|
|
|
for (i = EcData->VectorHead; i; i = EcData->VectorTable[i].Next) {
|
|
if (EcData->VectorTable[i].Next == TableIndex) {
|
|
EcData->VectorTable[i].Next = EcData->VectorTable[TableIndex].Next;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Put onto free list
|
|
//
|
|
|
|
EcData->VectorTable[TableIndex].Next = EcData->VectorFree;
|
|
EcData->VectorFree = (UCHAR) TableIndex;
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Unlock device and return status
|
|
//
|
|
|
|
KeReleaseSpinLock (&EcData->Lock, OldIrql);
|
|
MmUnlockPagableImageSection(LockPtr);
|
|
return Status;
|
|
}
|