1308 lines
28 KiB
C
1308 lines
28 KiB
C
/*++
|
||
|
||
Copyright (c) 1996 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
internal.c
|
||
|
||
Abstract:
|
||
|
||
This file contains those functions which didn't easily fit into any
|
||
of the other project files. They are typically accessory functions
|
||
used to prevent repeatitive and tedious coding.
|
||
|
||
Author:
|
||
|
||
Stephane Plante (splante)
|
||
|
||
Environment:
|
||
|
||
NT Kernel Model Driver only
|
||
|
||
--*/
|
||
|
||
#include "pch.h"
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE,ACPIInternalGetDeviceCapabilities)
|
||
#pragma alloc_text(PAGE,ACPIInternalIsPci)
|
||
#pragma alloc_text(PAGE,ACPIInternalGrowBuffer)
|
||
#pragma alloc_text(PAGE,ACPIInternalSendSynchronousIrp)
|
||
#endif
|
||
|
||
//
|
||
// For IA32 bit machines, which don't have a 64 bit compare-exchange
|
||
// instruction, we need a spinlock so that the OS can simulate it
|
||
//
|
||
KSPIN_LOCK AcpiUpdateFlagsLock;
|
||
|
||
//
|
||
// We need to have a table of HexDigits so that we can easily generate
|
||
// the proper nane for a GPE method
|
||
//
|
||
UCHAR HexDigit[] = "0123456789ABCDEF";
|
||
|
||
//
|
||
// This is a look-up table. The entry into the table corresponds to the
|
||
// first bit set (in an x86-architecture, this is the left most bit set to
|
||
// one...
|
||
//
|
||
UCHAR FirstSetLeftBit[256] = {
|
||
0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
|
||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
|
||
};
|
||
|
||
|
||
|
||
|
||
BOOLEAN
|
||
ACPIInternalConvertToNumber(
|
||
IN UCHAR ValueLow,
|
||
IN UCHAR ValueHigh,
|
||
IN PULONG Output
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine takes the supplied values (in ASCII format) and converts
|
||
them into numerical format. The ValueLow is the the low nibble of a uchar,
|
||
and the ValueHigh is the high nibble of a uchar. The input ASCII format
|
||
is HEX
|
||
|
||
Arguments:
|
||
|
||
ValueLow - ASCII Hex representation of low nibble
|
||
ValueHigh - ASCII Hex representation of high nibble
|
||
Output - Where to write the resulting UCHAR.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if converstion went okay
|
||
- FALSE otherwise
|
||
--*/
|
||
{
|
||
UCHAR Number;
|
||
UCHAR Scratch;
|
||
|
||
//
|
||
// Calculate the high nibble
|
||
//
|
||
if ( (ValueHigh < '0') || (ValueHigh > '9') ) {
|
||
|
||
if ( (ValueHigh < 'A') || (ValueHigh > 'F') ) {
|
||
|
||
return FALSE;
|
||
|
||
} else {
|
||
|
||
Scratch = (ValueHigh - 'A') + 10;
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
Scratch = (ValueHigh - '0');
|
||
|
||
}
|
||
|
||
//
|
||
// We now have the high nibble
|
||
//
|
||
Number = (UCHAR)Scratch;
|
||
Number <<=4;
|
||
|
||
//
|
||
// Calculate the low nibble
|
||
//
|
||
if ( (ValueLow < '0') || (ValueLow > '9') ) {
|
||
|
||
if ( (ValueLow < 'A') || (ValueLow > 'F') ) {
|
||
|
||
return FALSE;
|
||
|
||
} else {
|
||
|
||
Scratch = (ValueLow - 'A') + 10;
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
Scratch = (ValueLow - '0' );
|
||
|
||
}
|
||
|
||
//
|
||
// We now have the low nibble
|
||
//
|
||
Number |= ((UCHAR)Scratch);
|
||
|
||
//
|
||
// Store the result
|
||
//
|
||
if ( Output ) {
|
||
|
||
*Output = Number;
|
||
return TRUE;
|
||
|
||
} else {
|
||
|
||
return FALSE;
|
||
|
||
}
|
||
}
|
||
|
||
VOID
|
||
ACPIInternalDecrementIrpReferenceCount(
|
||
IN PDEVICE_EXTENSION DeviceExtension
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine decrements the number of outstanding request count in the
|
||
device extension and does the correct thing when this goes to zero
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - The Extension to decrement the count
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
LONG oldReferenceCount;
|
||
|
||
//
|
||
// Decrement the reference count since we are done processing
|
||
// the irp by the time we get back here
|
||
//
|
||
oldReferenceCount = InterlockedDecrement(
|
||
&(DeviceExtension->OutstandingIrpCount)
|
||
);
|
||
if (oldReferenceCount == 0) {
|
||
|
||
KeSetEvent( DeviceExtension->RemoveEvent, 0, FALSE );
|
||
|
||
}
|
||
}
|
||
|
||
NTSTATUS
|
||
ACPIInternalGetDeviceCapabilities(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PDEVICE_CAPABILITIES DeviceCapabilities
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sends get the capabilities of the given stack
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - The object that we want to know about
|
||
DeviceCapabilities - The capabilities of that device
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
IO_STACK_LOCATION irpSp;
|
||
NTSTATUS status;
|
||
PUCHAR dummy;
|
||
|
||
PAGED_CODE();
|
||
|
||
ASSERT( DeviceObject != NULL );
|
||
ASSERT( DeviceCapabilities != NULL );
|
||
|
||
//
|
||
// Initialize the stack location that we will use
|
||
//
|
||
RtlZeroMemory( &irpSp, sizeof(IO_STACK_LOCATION) );
|
||
irpSp.MajorFunction = IRP_MJ_PNP;
|
||
irpSp.MinorFunction = IRP_MN_QUERY_CAPABILITIES;
|
||
irpSp.Parameters.DeviceCapabilities.Capabilities = DeviceCapabilities;
|
||
|
||
//
|
||
// Initialize the capabilities that we will send down
|
||
//
|
||
RtlZeroMemory( DeviceCapabilities, sizeof(DEVICE_CAPABILITIES) );
|
||
DeviceCapabilities->Size = sizeof(DEVICE_CAPABILITIES);
|
||
DeviceCapabilities->Version = 1;
|
||
DeviceCapabilities->Address = (ULONG) -1;
|
||
DeviceCapabilities->UINumber = (ULONG) -1;
|
||
|
||
//
|
||
// Make the call now...
|
||
//
|
||
status = ACPIInternalSendSynchronousIrp(
|
||
DeviceObject,
|
||
&irpSp,
|
||
(PVOID) &dummy
|
||
);
|
||
|
||
// Done
|
||
//
|
||
return status;
|
||
}
|
||
|
||
PDEVICE_EXTENSION
|
||
ACPIInternalGetDeviceExtension(
|
||
IN PDEVICE_OBJECT DeviceObject
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The ACPI Driver can no longer just get the device extension by
|
||
Dereferencing DeviceObject->DeviceExtension because it allows a
|
||
race condition when dealing with the surprise remove case
|
||
|
||
This routine is called to turn the Device Object into a Device Extension
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - The Device Object
|
||
|
||
Return Value:
|
||
|
||
PDEVICE_EXTENSION
|
||
|
||
--*/
|
||
{
|
||
KIRQL oldIrql;
|
||
PDEVICE_EXTENSION deviceExtension;
|
||
|
||
//
|
||
// Acquire the device tree lock
|
||
//
|
||
KeAcquireSpinLock( &AcpiDeviceTreeLock, &oldIrql );
|
||
|
||
//
|
||
// Dereference the device extension
|
||
//
|
||
deviceExtension = DeviceObject->DeviceExtension;
|
||
|
||
#if 0
|
||
//
|
||
// Is this a surprise removed device extension?
|
||
//
|
||
if (deviceExtension != NULL &&
|
||
deviceExtension->Flags & DEV_TYPE_SURPRISE_REMOVED) {
|
||
|
||
//
|
||
// Get the "real" extension
|
||
//
|
||
deviceExtension = deviceExtension->Removed.OriginalAcpiExtension;
|
||
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Done with the lock
|
||
//
|
||
KeReleaseSpinLock( &AcpiDeviceTreeLock, oldIrql );
|
||
|
||
//
|
||
// Return the device extension
|
||
//
|
||
return deviceExtension;
|
||
}
|
||
|
||
NTSTATUS
|
||
ACPIInternalGetDispatchTable(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
OUT PDEVICE_EXTENSION *DeviceExtension,
|
||
OUT PIRP_DISPATCH_TABLE *DispatchTable
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine returns the deviceExtension and dispatch table that is
|
||
to be used by the target object
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - The Device Object
|
||
DeviceExtension - Where to store the deviceExtension
|
||
DispatchTable - Where to store the dispatchTable
|
||
Return Value:
|
||
|
||
PDEVICE_EXTENSION
|
||
|
||
--*/
|
||
{
|
||
KIRQL oldIrql;
|
||
|
||
//
|
||
// Acquire the device tree lock
|
||
//
|
||
KeAcquireSpinLock( &AcpiDeviceTreeLock, &oldIrql );
|
||
|
||
//
|
||
// Dereference the device extension
|
||
//
|
||
*DeviceExtension = DeviceObject->DeviceExtension;
|
||
if (DeviceObject->DeviceExtension) {
|
||
|
||
//
|
||
// Dereference the dispatch table
|
||
//
|
||
*DispatchTable = (*DeviceExtension)->DispatchTable;
|
||
|
||
} else {
|
||
|
||
//
|
||
// No dispatch table to hand back
|
||
//
|
||
*DispatchTable = NULL;
|
||
|
||
}
|
||
|
||
//
|
||
// Done with the lock
|
||
//
|
||
KeReleaseSpinLock( &AcpiDeviceTreeLock, oldIrql );
|
||
|
||
//
|
||
// Return
|
||
//
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
ACPIInternalGrowBuffer(
|
||
IN OUT PVOID *Buffer,
|
||
IN ULONG OriginalSize,
|
||
IN ULONG NewSize
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is used to grow a buffer. It allocates memory, zeroes it out,
|
||
and copies the original information over.
|
||
|
||
Note: I suppose it can *shrink* a buffer as well, but I wouldn't bet my life
|
||
on it. The caller is responsible for freeing allocated memory
|
||
|
||
Arguments
|
||
|
||
Buffer - Points to the Pointer to the Buffer that we want to change
|
||
OriginalSize - How big the buffer was originally
|
||
NewSize - How big we want to make the buffer
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
PUCHAR temp;
|
||
|
||
PAGED_CODE();
|
||
ASSERT( Buffer != NULL );
|
||
|
||
temp = ExAllocatePoolWithTag(
|
||
PagedPool,
|
||
NewSize,
|
||
ACPI_RESOURCE_POOLTAG
|
||
);
|
||
if (temp == NULL) {
|
||
|
||
if (*Buffer) {
|
||
|
||
ExFreePool ( *Buffer );
|
||
*Buffer = NULL;
|
||
|
||
}
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
}
|
||
|
||
RtlZeroMemory ( temp, NewSize );
|
||
if ( *Buffer ) {
|
||
|
||
RtlCopyMemory ( temp, *Buffer, OriginalSize );
|
||
ExFreePool( *Buffer );
|
||
|
||
}
|
||
|
||
*Buffer = temp;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
ACPIInternalIsPci(
|
||
IN PDEVICE_OBJECT DeviceObject
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine determines if the specified device object is part of a
|
||
PCI stack, either as a PCI device or as a PCI Bus.
|
||
|
||
This routine will then set the flags that if it is a PCI device, then
|
||
it will always be remembered as such
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - The device object to check
|
||
|
||
--*/
|
||
{
|
||
AMLISUPP_CONTEXT_PASSIVE isPciDeviceContext;
|
||
BOOLEAN pciDevice;
|
||
KEVENT removeEvent;
|
||
NTSTATUS status;
|
||
PDEVICE_EXTENSION deviceExtension = ACPIInternalGetDeviceExtension(DeviceObject);
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Is this already a PCI device?
|
||
//
|
||
if ( (deviceExtension->Flags & DEV_CAP_PCI) ||
|
||
(deviceExtension->Flags & DEV_CAP_PCI_DEVICE) ) {
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
//
|
||
// Is this a PCI bus?
|
||
//
|
||
if (IsPciBus(deviceExtension->DeviceObject)) {
|
||
|
||
//
|
||
// Remember that we are a PCI bus
|
||
//
|
||
ACPIInternalUpdateFlags(
|
||
&(deviceExtension->Flags),
|
||
(DEV_CAP_PCI),
|
||
FALSE
|
||
);
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
//
|
||
// Are we a PCI device?
|
||
//
|
||
isPciDeviceContext.Status = STATUS_NOT_FOUND;
|
||
KeInitializeEvent(
|
||
&isPciDeviceContext.Event,
|
||
SynchronizationEvent,
|
||
FALSE
|
||
);
|
||
status = IsPciDevice(
|
||
deviceExtension->AcpiObject,
|
||
AmlisuppCompletePassive,
|
||
(PVOID) &isPciDeviceContext,
|
||
&pciDevice
|
||
);
|
||
if (status == STATUS_PENDING) {
|
||
|
||
KeWaitForSingleObject(
|
||
&isPciDeviceContext.Event,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL
|
||
);
|
||
status = isPciDeviceContext.Status;
|
||
|
||
}
|
||
if (NT_SUCCESS(status) && pciDevice) {
|
||
|
||
//
|
||
// Remember that we are a PCI device
|
||
//
|
||
ACPIInternalUpdateFlags(
|
||
&(deviceExtension->Flags),
|
||
(DEV_CAP_PCI_DEVICE),
|
||
FALSE
|
||
);
|
||
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
BOOLEAN
|
||
ACPIInternalIsReportedMissing(
|
||
IN PDEVICE_EXTENSION DeviceExtension
|
||
)
|
||
{
|
||
KIRQL oldIrql;
|
||
PDEVICE_EXTENSION currentExtension;
|
||
BOOLEAN reportedMissing;
|
||
|
||
//
|
||
// Preinit
|
||
//
|
||
reportedMissing = FALSE;
|
||
|
||
//
|
||
// Acquire the device tree lock
|
||
//
|
||
KeAcquireSpinLock( &AcpiDeviceTreeLock, &oldIrql );
|
||
|
||
currentExtension = DeviceExtension;
|
||
do {
|
||
|
||
if ( currentExtension->Flags & DEV_TYPE_NOT_ENUMERATED ) {
|
||
|
||
reportedMissing = TRUE;
|
||
break;
|
||
|
||
}
|
||
|
||
currentExtension = currentExtension->ParentExtension;
|
||
|
||
} while ( currentExtension );
|
||
|
||
KeReleaseSpinLock( &AcpiDeviceTreeLock, oldIrql );
|
||
|
||
return reportedMissing;
|
||
}
|
||
|
||
VOID
|
||
ACPIInternalMoveList(
|
||
IN PLIST_ENTRY FromList,
|
||
IN PLIST_ENTRY ToList
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine moves entire list arounds.
|
||
|
||
Arguments:
|
||
|
||
FromList - the List to move items from
|
||
ToList - the List to move items to
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
PLIST_ENTRY oldHead;
|
||
PLIST_ENTRY oldTail;
|
||
PLIST_ENTRY newTail;
|
||
|
||
//
|
||
// We have to check to see if the from list is empty, otherwise, the
|
||
// direct pointer hacking will make a mess of things
|
||
//
|
||
if (!IsListEmpty(FromList)) {
|
||
|
||
newTail = ToList->Blink;
|
||
oldTail = FromList->Blink;
|
||
oldHead = FromList->Flink;
|
||
|
||
//
|
||
// Move the pointers around some
|
||
//
|
||
oldTail->Flink = ToList;
|
||
ToList->Blink = oldTail;
|
||
oldHead->Blink = newTail;
|
||
newTail->Flink = oldHead;
|
||
InitializeListHead( FromList );
|
||
|
||
}
|
||
|
||
}
|
||
|
||
VOID
|
||
ACPIInternalMovePowerList(
|
||
IN PLIST_ENTRY FromList,
|
||
IN PLIST_ENTRY ToList
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine moves entire list arounds. Since this routine is only
|
||
used for Device Power Management, we also take the time to reset the
|
||
amount of work done to NULL.
|
||
|
||
Arguments:
|
||
|
||
FromList - the List to move items from
|
||
ToList - the List to move items to
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
PACPI_POWER_REQUEST powerRequest;
|
||
PLIST_ENTRY oldHead = FromList->Flink;
|
||
|
||
//
|
||
// Before we do anything, walk the From and reset the amount of work that
|
||
// was done
|
||
//
|
||
while (oldHead != FromList) {
|
||
|
||
//
|
||
// Obtain the power request that this entry contains
|
||
//
|
||
powerRequest = CONTAINING_RECORD(
|
||
oldHead,
|
||
ACPI_POWER_REQUEST,
|
||
ListEntry
|
||
);
|
||
#if DBG
|
||
if (oldHead == &AcpiPowerPhase0List ||
|
||
oldHead == &AcpiPowerPhase1List ||
|
||
oldHead == &AcpiPowerPhase2List ||
|
||
oldHead == &AcpiPowerPhase3List ||
|
||
oldHead == &AcpiPowerPhase4List ||
|
||
oldHead == &AcpiPowerPhase5List ||
|
||
oldHead == &AcpiPowerWaitWakeList) {
|
||
|
||
ACPIPrint( (
|
||
ACPI_PRINT_CRITICAL,
|
||
"ACPIInternalMoveList: %08x is linked into %08lx\n",
|
||
oldHead,
|
||
FromList
|
||
) );
|
||
DbgBreakPoint();
|
||
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Grab the next entry
|
||
//
|
||
oldHead = oldHead->Flink;
|
||
|
||
//
|
||
// Reset the amount of work done. Note: This could be a CompareExchange
|
||
// with the Comparand being WORK_DONE_COMPLETED
|
||
//
|
||
InterlockedExchange(
|
||
&(powerRequest->WorkDone),
|
||
WORK_DONE_STEP_0
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// Actually Move the list here...
|
||
//
|
||
ACPIInternalMoveList( FromList, ToList );
|
||
}
|
||
|
||
NTSTATUS
|
||
ACPIInternalRegisterPowerCallBack(
|
||
IN PDEVICE_EXTENSION DeviceExtension,
|
||
IN PCALLBACK_FUNCTION CallBackFunction
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to register a Power Call on the appropriate
|
||
device extension.
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - This will be the context field of the CallBackFunction
|
||
CallBackFunction - The function to invoke
|
||
|
||
Return Value:
|
||
|
||
NSTATUS
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
OBJECT_ATTRIBUTES objAttributes;
|
||
PCALLBACK_OBJECT callBack;
|
||
PVOID callBackRegistration;
|
||
UNICODE_STRING callBackName;
|
||
|
||
//
|
||
// if there is already a callback present, this is a nop
|
||
//
|
||
if (DeviceExtension->Flags & DEV_PROP_CALLBACK) {
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
//
|
||
// Remember that we have a callback
|
||
//
|
||
ACPIInternalUpdateFlags(
|
||
&(DeviceExtension->Flags),
|
||
DEV_PROP_CALLBACK,
|
||
FALSE
|
||
);
|
||
|
||
//
|
||
// Register a callback that tells us when the user changes the
|
||
// system power policy
|
||
//
|
||
RtlInitUnicodeString( &callBackName, L"\\Callback\\PowerState" );
|
||
InitializeObjectAttributes(
|
||
&objAttributes,
|
||
&callBackName,
|
||
OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
|
||
NULL,
|
||
NULL
|
||
);
|
||
status = ExCreateCallback(
|
||
&callBack,
|
||
&objAttributes,
|
||
FALSE,
|
||
TRUE
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
ExRegisterCallback(
|
||
callBack,
|
||
CallBackFunction,
|
||
DeviceExtension
|
||
);
|
||
|
||
}
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// Ignored failed registrations
|
||
//
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_FAILURE,
|
||
DeviceExtension,
|
||
"ACPIInternalRegisterPowerCallBack: Failed to register callback %x",
|
||
status
|
||
) );
|
||
status = STATUS_SUCCESS;
|
||
|
||
//
|
||
// Remember that we don't have a callback
|
||
//
|
||
ACPIInternalUpdateFlags(
|
||
&(DeviceExtension->Flags),
|
||
DEV_PROP_CALLBACK,
|
||
TRUE
|
||
);
|
||
|
||
}
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
ACPIInternalSendSynchronousIrp(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIO_STACK_LOCATION TopStackLocation,
|
||
OUT PVOID *Information
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Builds a PNP Irp and sends it down to DeviceObject
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Target DeviceObject
|
||
TopStackLocation - Specifies the Parameters for the Irp
|
||
Information - The returned IoStatus.Information field
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
IO_STATUS_BLOCK ioStatus;
|
||
KEVENT pnpEvent;
|
||
NTSTATUS status;
|
||
PDEVICE_OBJECT targetObject;
|
||
PIO_STACK_LOCATION irpStack;
|
||
PIRP pnpIrp;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Initialize the event
|
||
//
|
||
KeInitializeEvent( &pnpEvent, SynchronizationEvent, FALSE );
|
||
|
||
//
|
||
// Get the irp that we will send the request to
|
||
//
|
||
targetObject = IoGetAttachedDeviceReference( DeviceObject );
|
||
|
||
//
|
||
// Build an IRP
|
||
//
|
||
pnpIrp = IoBuildSynchronousFsdRequest(
|
||
IRP_MJ_PNP,
|
||
targetObject,
|
||
NULL, // I don't need a buffer
|
||
0, // Size is empty
|
||
NULL, // Don't have to worry about the starting location
|
||
&pnpEvent,
|
||
&ioStatus
|
||
);
|
||
|
||
if (pnpIrp == NULL) {
|
||
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto ACPIInternalSendSynchronousIrpExit;
|
||
|
||
}
|
||
|
||
//
|
||
// PNP Irps all begin life as STATUS_NOT_SUPPORTED.
|
||
//
|
||
pnpIrp->IoStatus.Status = STATUS_NOT_SUPPORTED ;
|
||
pnpIrp->IoStatus.Information = 0;
|
||
|
||
//
|
||
// Get the top of stack ...
|
||
//
|
||
irpStack = IoGetNextIrpStackLocation ( pnpIrp );
|
||
if (irpStack == NULL) {
|
||
|
||
status = STATUS_INVALID_PARAMETER;
|
||
goto ACPIInternalSendSynchronousIrpExit;
|
||
|
||
}
|
||
|
||
//
|
||
// Set the top of stack
|
||
//
|
||
*irpStack = *TopStackLocation;
|
||
|
||
//
|
||
// Make sure that there are no completion routine set
|
||
//
|
||
IoSetCompletionRoutine(
|
||
pnpIrp,
|
||
NULL,
|
||
NULL,
|
||
FALSE,
|
||
FALSE,
|
||
FALSE
|
||
);
|
||
|
||
//
|
||
// Call the driver
|
||
//
|
||
status = IoCallDriver( targetObject, pnpIrp );
|
||
if (status == STATUS_PENDING) {
|
||
|
||
//
|
||
// If the status is STATUS_PENDING, than we must block until the irp completes
|
||
// and pull the true status out
|
||
//
|
||
KeWaitForSingleObject(
|
||
&pnpEvent,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL);
|
||
|
||
status = ioStatus.Status;
|
||
|
||
}
|
||
|
||
//
|
||
// Tell the user how much information was passed (if necessary)
|
||
//
|
||
if (NT_SUCCESS(status) && (Information != NULL)) {
|
||
|
||
*Information = (PVOID)ioStatus.Information;
|
||
|
||
}
|
||
|
||
ACPIInternalSendSynchronousIrpExit:
|
||
ACPIPrint( (
|
||
ACPI_PRINT_IRP,
|
||
"ACPIInternalSendSynchronousIrp: %#08lx Status = %#08lx\n",
|
||
DeviceObject, status
|
||
) );
|
||
|
||
//
|
||
// Done with reference
|
||
//
|
||
ObDereferenceObject( targetObject );
|
||
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
ACPIInternalSetDeviceInterface (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN LPGUID InterfaceGuid
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine does all the grunt work for registering an interface and
|
||
enabling it
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - The device we wish to register the interface on
|
||
InterfaceGuid - The interface we wish to register
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
PDEVICE_EXTENSION deviceExtension = ACPIInternalGetDeviceExtension(DeviceObject);
|
||
UNICODE_STRING symbolicLinkName;
|
||
|
||
//
|
||
// Register the interface
|
||
//
|
||
status = IoRegisterDeviceInterface(
|
||
DeviceObject,
|
||
InterfaceGuid,
|
||
NULL,
|
||
&symbolicLinkName
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_FAILURE,
|
||
deviceExtension,
|
||
"ACPIInternalSetDeviceInterface: IoRegisterDeviceInterface = %08lx",
|
||
status
|
||
) );
|
||
return status;
|
||
|
||
}
|
||
|
||
//
|
||
// Turn on the interface
|
||
//
|
||
status = IoSetDeviceInterfaceState(&symbolicLinkName, TRUE);
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
ACPIDevPrint( (
|
||
ACPI_PRINT_FAILURE,
|
||
deviceExtension,
|
||
"ACPIInternalSetDeviceInterface: IoSetDeviceInterfaceState = %08lx",
|
||
status
|
||
) );
|
||
goto ACPIInternalSetDeviceInterfaceExit;
|
||
|
||
}
|
||
|
||
ACPIInternalSetDeviceInterfaceExit:
|
||
//
|
||
// Done
|
||
//
|
||
return status;
|
||
}
|
||
|
||
VOID
|
||
ACPIInternalUpdateDeviceStatus(
|
||
IN PDEVICE_EXTENSION DeviceExtension,
|
||
IN ULONG DeviceStatus
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to update the status of the DeviceExtension based
|
||
upon the result of the _STA, which are passed as DeviceStatus
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - The extension whose status is to be updated
|
||
DeviceState - The status of the device
|
||
|
||
Return Value:
|
||
|
||
VOID
|
||
|
||
--*/
|
||
{
|
||
KIRQL oldIrql;
|
||
ULONGLONG originalFlags;
|
||
PDEVICE_EXTENSION parentExtension = NULL;
|
||
BOOLEAN bPreviouslyPresent;
|
||
|
||
//
|
||
// Is the device working okay?
|
||
//
|
||
originalFlags = ACPIInternalUpdateFlags(
|
||
&(DeviceExtension->Flags),
|
||
DEV_PROP_DEVICE_FAILED,
|
||
(BOOLEAN) (DeviceStatus & STA_STATUS_WORKING_OK)
|
||
);
|
||
|
||
//
|
||
// Is the device meant to be shown in the UI?
|
||
//
|
||
originalFlags = ACPIInternalUpdateFlags(
|
||
&(DeviceExtension->Flags),
|
||
DEV_CAP_NO_SHOW_IN_UI,
|
||
(BOOLEAN) (DeviceStatus & STA_STATUS_USER_INTERFACE)
|
||
);
|
||
|
||
//
|
||
// Is the device decoding its resources?
|
||
//
|
||
originalFlags = ACPIInternalUpdateFlags(
|
||
&(DeviceExtension->Flags),
|
||
DEV_PROP_DEVICE_ENABLED,
|
||
(BOOLEAN) !(DeviceStatus & STA_STATUS_ENABLED)
|
||
);
|
||
|
||
//
|
||
// Update the extensions flags bassed on wether or not STA_STATUS_PRESENT is
|
||
// set
|
||
//
|
||
originalFlags = ACPIInternalUpdateFlags(
|
||
&(DeviceExtension->Flags),
|
||
DEV_TYPE_NOT_PRESENT,
|
||
(BOOLEAN) (DeviceStatus & STA_STATUS_PRESENT)
|
||
);
|
||
|
||
//
|
||
// If the original flags do not contain the set value, but we are setting
|
||
// the flags, then we must call IoInvalidDeviceRelations on the parent
|
||
//
|
||
if (!(originalFlags & DEV_TYPE_NOT_PRESENT) &&
|
||
!(DeviceStatus & STA_STATUS_PRESENT)) {
|
||
|
||
//
|
||
// Need the device tree lock
|
||
//
|
||
KeAcquireSpinLock( &AcpiDeviceTreeLock, &oldIrql );
|
||
|
||
parentExtension = DeviceExtension->ParentExtension;
|
||
while (parentExtension && (parentExtension->Flags & DEV_TYPE_NOT_FOUND)) {
|
||
|
||
parentExtension = parentExtension->ParentExtension;
|
||
|
||
}
|
||
|
||
IoInvalidateDeviceRelations(
|
||
parentExtension->PhysicalDeviceObject,
|
||
BusRelations
|
||
);
|
||
|
||
//
|
||
// Done with the lock
|
||
//
|
||
KeReleaseSpinLock( &AcpiDeviceTreeLock, oldIrql );
|
||
|
||
}
|
||
}
|
||
|
||
ULONGLONG
|
||
ACPIInternalUpdateFlags(
|
||
IN PULONGLONG FlagLocation,
|
||
IN ULONGLONG NewFlags,
|
||
IN BOOLEAN Clear
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine updates flags in the specified location
|
||
|
||
Arguments:
|
||
|
||
FlagLocation - Where the flags are located
|
||
NewFlags - The bits that should be set or cleared
|
||
Clear - Wether the bits should be set or cleared
|
||
|
||
Return Value:
|
||
|
||
Original Flags
|
||
|
||
--*/
|
||
{
|
||
ULONGLONG originalFlags;
|
||
ULONGLONG tempFlags;
|
||
ULONGLONG flags;
|
||
ULONG uFlags;
|
||
ULONG uTempFlags;
|
||
ULONG uOriginalFlags;
|
||
|
||
#if 0
|
||
if (Clear) {
|
||
|
||
//
|
||
// Clear the bits
|
||
//
|
||
originalFlags = *FlagLocation;
|
||
do {
|
||
|
||
tempFlags = originalFlags;
|
||
flags = tempFlags & ~NewFlags;
|
||
|
||
//
|
||
// Calculate the low part
|
||
//
|
||
uFlags = (ULONG) flags;
|
||
uTempFlags = (ULONG) tempFlags;
|
||
originalFlags = InterlockedCompareExchange(
|
||
(PULONG) FlagLocation,
|
||
uFlags,
|
||
uTempFlags
|
||
);
|
||
|
||
//
|
||
// Calculate the high part
|
||
//
|
||
uFlags = (ULONG) (flags >> 32);
|
||
uTempFlags = (ULONG) (tempFlags >> 32);
|
||
uOriginalFlags = InterlockedCompareExchange(
|
||
(PULONG) FlagLocation+1,
|
||
uFlags,
|
||
uTempFlags
|
||
);
|
||
|
||
//
|
||
// Rebuild the original flags
|
||
//
|
||
originalFlags |= (uOriginalFlags << 32);
|
||
tempFlags |= (uTempFlags << 32);
|
||
|
||
} while ( tempFlags != originalFlags );
|
||
|
||
} else {
|
||
|
||
//
|
||
// Set the bits
|
||
//
|
||
originalFlags = *FlagLocation;
|
||
do {
|
||
|
||
tempFlags = originalFlags;
|
||
flags = tempFlags | NewFlags;
|
||
|
||
//
|
||
// Calculate the low part
|
||
//
|
||
uFlags = (ULONG) flags;
|
||
uTempFlags = (ULONG) tempFlags;
|
||
originalFlags = InterlockedCompareExchange(
|
||
(PULONG) FlagLocation,
|
||
uFlags,
|
||
uTempFlags
|
||
);
|
||
|
||
//
|
||
// Calculate the high part
|
||
//
|
||
uFlags = (ULONG) (flags >> 32);
|
||
uTempFlags = (ULONG) (tempFlags >> 32);
|
||
uOriginalFlags = InterlockedCompareExchange(
|
||
(PULONG) FlagLocation+1,
|
||
uFlags,
|
||
uTempFlags
|
||
);
|
||
|
||
//
|
||
// Rebuild the original flags
|
||
//
|
||
originalFlags |= (uOriginalFlags << 32);
|
||
tempFlags |= (uTempFlags << 32);
|
||
|
||
} while ( tempFlags != originalFlags );
|
||
}
|
||
#else
|
||
|
||
if (Clear) {
|
||
|
||
//
|
||
// Clear the bits
|
||
//
|
||
originalFlags = *FlagLocation;
|
||
do {
|
||
|
||
tempFlags = originalFlags;
|
||
flags = tempFlags & ~NewFlags;
|
||
|
||
//
|
||
// Exchange the bits
|
||
//
|
||
originalFlags = ExInterlockedCompareExchange64(
|
||
(PLONGLONG) FlagLocation,
|
||
(PLONGLONG) &flags,
|
||
(PLONGLONG) &tempFlags,
|
||
&AcpiUpdateFlagsLock
|
||
);
|
||
|
||
} while ( tempFlags != originalFlags );
|
||
|
||
} else {
|
||
|
||
//
|
||
// Set the bits
|
||
//
|
||
originalFlags = *FlagLocation;
|
||
do {
|
||
|
||
tempFlags = originalFlags;
|
||
flags = tempFlags | NewFlags;
|
||
|
||
//
|
||
// Exchange teh bits
|
||
//
|
||
originalFlags = ExInterlockedCompareExchange64(
|
||
(PLONGLONG) FlagLocation,
|
||
(PLONGLONG) &flags,
|
||
(PLONGLONG) &tempFlags,
|
||
&AcpiUpdateFlagsLock
|
||
);
|
||
|
||
} while ( tempFlags != originalFlags );
|
||
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// return the original flags
|
||
//
|
||
return originalFlags;
|
||
}
|
||
|
||
|
||
|