mirror of https://github.com/tongzx/nt5src
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.
695 lines
20 KiB
695 lines
20 KiB
/*++
|
|
|
|
Copyright (c) 1991-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ixinfo.c
|
|
|
|
Abstract:
|
|
|
|
Author:
|
|
|
|
Ken Reneris (kenr) 08-Aug-1994
|
|
|
|
Environment:
|
|
|
|
Kernel mode only.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "halp.h"
|
|
#include "pci.h"
|
|
#include "pcip.h"
|
|
|
|
#ifdef _PNP_POWER_
|
|
HAL_CALLBACKS HalCallback;
|
|
extern WCHAR rgzSuspendCallbackName[];
|
|
|
|
VOID
|
|
HalInitSystemPhase2 (
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
HalpLockSuspendCode (
|
|
IN PVOID CallbackContext,
|
|
IN PVOID Argument1,
|
|
IN PVOID Argument2
|
|
);
|
|
#endif
|
|
|
|
NTSTATUS
|
|
HalpQueryInstalledBusInformation (
|
|
OUT PVOID Buffer,
|
|
IN ULONG BufferLength,
|
|
OUT PULONG ReturnedLength
|
|
);
|
|
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE,HaliQuerySystemInformation)
|
|
#pragma alloc_text(PAGE,HaliSetSystemInformation)
|
|
#pragma alloc_text(INIT,HalInitSystemPhase2)
|
|
|
|
#ifdef _PNP_POWER_
|
|
#pragma alloc_text(PAGE,HalpLockSuspendCode)
|
|
#endif
|
|
|
|
#endif
|
|
|
|
//
|
|
// HalQueryMcaInterface
|
|
//
|
|
|
|
VOID
|
|
HalpMcaLockInterface(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
HalpMcaUnlockInterface(
|
|
VOID
|
|
);
|
|
|
|
NTSTATUS
|
|
HalpMcaReadRegisterInterface(
|
|
IN UCHAR BankNumber,
|
|
IN OUT PMCA_EXCEPTION Exception
|
|
);
|
|
|
|
#ifdef ACPI_HAL
|
|
extern PHYSICAL_ADDRESS HalpMaxHotPlugMemoryAddress;
|
|
#endif
|
|
|
|
#if defined(ACPI_HAL) && defined(_HALPAE_) && !defined(NT_UP)
|
|
|
|
extern PVOID HalpAcpiSrat;
|
|
|
|
NTSTATUS
|
|
HalpGetAcpiStaticNumaTopology(
|
|
HAL_NUMA_TOPOLOGY_INTERFACE * NumaInfo
|
|
);
|
|
|
|
#endif
|
|
|
|
VOID
|
|
HalInitSystemPhase2 (
|
|
VOID
|
|
)
|
|
{
|
|
#ifdef _PNP_POWER_
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
NTSTATUS Status;
|
|
UNICODE_STRING unicodeString;
|
|
PCALLBACK_OBJECT CallbackObject;
|
|
|
|
//
|
|
// Create hal callbacks
|
|
//
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
NULL,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
|
|
ExCreateCallback (&HalCallback.SetSystemInformation, &ObjectAttributes, TRUE, TRUE);
|
|
ExCreateCallback (&HalCallback.BusCheck, &ObjectAttributes, TRUE, TRUE);
|
|
|
|
//
|
|
// Connect to suspend callback to lock hal hibaration code
|
|
//
|
|
|
|
RtlInitUnicodeString(&unicodeString, rgzSuspendCallbackName);
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&unicodeString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
Status = ExCreateCallback (&CallbackObject, &ObjectAttributes, FALSE, FALSE);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
ExRegisterCallback (
|
|
CallbackObject,
|
|
HalpLockSuspendCode,
|
|
NULL
|
|
);
|
|
|
|
ObDereferenceObject (CallbackObject);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
#if defined(_HALPAE_) && !defined(NT_UP)
|
|
|
|
NTSTATUS
|
|
HalpGetApicIdByProcessorNumber(
|
|
IN UCHAR Processor,
|
|
IN OUT USHORT *ApicId
|
|
);
|
|
|
|
ULONG HalpFakeNumaNodes;
|
|
ULONG HalpFakeNumaAffinity;
|
|
ULONG HalpFakeNumaAffinityShift;
|
|
ULONG HalpFakeNumaPageMask;
|
|
ULONG HalpFakeNumaPageShift;
|
|
|
|
ULONG
|
|
HalpFakeNumaQueryPageToNode(
|
|
IN ULONG_PTR PhysicalPageNumber
|
|
)
|
|
{
|
|
ULONG Node;
|
|
|
|
Node = (ULONG)PhysicalPageNumber >> HalpFakeNumaPageShift;
|
|
Node &= HalpFakeNumaPageMask;
|
|
return Node;
|
|
}
|
|
|
|
NTSTATUS
|
|
HalpFakeNumaQueryProcessorNode(
|
|
IN ULONG ProcessorNumber,
|
|
OUT PUSHORT ProcessorId,
|
|
OUT PUCHAR NodeNumber
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
KAFFINITY ProcessorMask;
|
|
KAFFINITY Mask;
|
|
UCHAR Node;
|
|
USHORT ApicId;
|
|
|
|
#if defined(APIC_HAL) && !defined(NT_UP)
|
|
|
|
Status = HalpGetApicIdByProcessorNumber((UCHAR)ProcessorNumber, &ApicId);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
*ProcessorId = ApicId;
|
|
|
|
#else
|
|
|
|
*ProcessorId = (USHORT)ProcessorNumber;
|
|
Status = STATUS_SUCCESS;
|
|
|
|
#endif
|
|
|
|
ProcessorMask = 1 << ProcessorNumber;
|
|
for (Node = 0; Node < HalpFakeNumaNodes; Node++) {
|
|
Mask = HalpFakeNumaAffinity << (Node * HalpFakeNumaAffinityShift);
|
|
if ((Mask & ProcessorMask) != 0) {
|
|
*NodeNumber = Node;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
HaliQuerySystemInformation(
|
|
IN HAL_QUERY_INFORMATION_CLASS InformationClass,
|
|
IN ULONG BufferSize,
|
|
OUT PVOID Buffer,
|
|
OUT PULONG ReturnedLength
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PVOID InternalBuffer;
|
|
ULONG Length;
|
|
union {
|
|
HAL_POWER_INFORMATION PowerInf;
|
|
HAL_PROCESSOR_SPEED_INFORMATION ProcessorInf;
|
|
MCA_EXCEPTION McaException;
|
|
HAL_DISPLAY_BIOS_INFORMATION DisplayBiosInf;
|
|
} U;
|
|
|
|
BOOLEAN bUseFrameBufferCaching;
|
|
|
|
PAGED_CODE();
|
|
|
|
Status = STATUS_SUCCESS;
|
|
*ReturnedLength = 0;
|
|
Length = 0;
|
|
|
|
switch (InformationClass) {
|
|
#ifndef ACPI_HAL
|
|
case HalInstalledBusInformation:
|
|
Status = HalpQueryInstalledBusInformation (
|
|
Buffer,
|
|
BufferSize,
|
|
ReturnedLength
|
|
);
|
|
break;
|
|
#endif
|
|
case HalFrameBufferCachingInformation:
|
|
|
|
// Note - we want to return TRUE here to enable USWC in all
|
|
// cases except in a "Shared Memory Cluster" machine.
|
|
bUseFrameBufferCaching = TRUE;
|
|
InternalBuffer = &bUseFrameBufferCaching;
|
|
Length = sizeof (BOOLEAN);
|
|
break;
|
|
|
|
|
|
case HalMcaLogInformation:
|
|
InternalBuffer = &U.McaException;
|
|
Status = HalpGetMcaLog ((PMCA_EXCEPTION)Buffer,
|
|
BufferSize,
|
|
ReturnedLength);
|
|
break;
|
|
|
|
#if !defined(_WIN64)
|
|
case HalDisplayBiosInformation:
|
|
InternalBuffer = &U.DisplayBiosInf;
|
|
Length = sizeof(U.DisplayBiosInf);
|
|
U.DisplayBiosInf = HalpGetDisplayBiosInformation ();
|
|
break;
|
|
#endif
|
|
|
|
#ifdef _PNP_POWER_
|
|
case HalPowerInformation:
|
|
RtlZeroMemory (&U.PowerInf, sizeof(HAL_POWER_INFORMATION));
|
|
|
|
InternalBuffer = &U.PowerInf;
|
|
Length = sizeof (HAL_POWER_INFORMATION);
|
|
break;
|
|
|
|
|
|
case HalProcessorSpeedInformation:
|
|
RtlZeroMemory (&U.ProcessorInf, sizeof(HAL_POWER_INFORMATION));
|
|
|
|
U.ProcessorInf.MaximumProcessorSpeed = 100;
|
|
U.ProcessorInf.CurrentAvailableSpeed = 100;
|
|
U.ProcessorInf.ConfiguredSpeedLimit = 100;
|
|
|
|
InternalBuffer = &U.PowerInf;
|
|
Length = sizeof (HAL_PROCESSOR_SPEED_INFORMATION);
|
|
break;
|
|
|
|
case HalCallbackInformation:
|
|
InternalBuffer = &HalCallback;
|
|
Length = sizeof (HAL_CALLBACKS);
|
|
break;
|
|
#endif
|
|
|
|
#if defined(_HALPAE_) && !defined(NT_UP)
|
|
|
|
case HalNumaTopologyInterface:
|
|
if ((BufferSize == sizeof(HAL_NUMA_TOPOLOGY_INTERFACE)) &&
|
|
(HalPaeEnabled() == TRUE)) {
|
|
|
|
Status = STATUS_INVALID_LEVEL;
|
|
|
|
#if defined(ACPI_HAL)
|
|
|
|
if (HalpAcpiSrat) {
|
|
Status = HalpGetAcpiStaticNumaTopology(Buffer);
|
|
if (NT_SUCCESS(Status)) {
|
|
*ReturnedLength = sizeof(HAL_NUMA_TOPOLOGY_INTERFACE);
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Mega Kludge:
|
|
//
|
|
// Testing Testing Testing. MM may supply a
|
|
// fake NUMA configuration for testing purposes.
|
|
// In this case information is passed IN to the
|
|
// HAL using this query only interface by passing
|
|
// information in the output buffer and in the
|
|
// returned length field.
|
|
//
|
|
// The returned length field will have been
|
|
// initialized to the special value 'NUMA', in
|
|
// this case, the incoming information buffer
|
|
// contains the NUMA configuration.
|
|
//
|
|
|
|
if (HalpFakeNumaNodes == 0) {
|
|
|
|
struct {
|
|
ULONG Nodes:3;
|
|
ULONG AffinityShift:6;
|
|
ULONG PageShift:6;
|
|
ULONG Signature:17;
|
|
ULONG Affinity;
|
|
ULONG Mask;
|
|
} Fake;
|
|
|
|
ULONG ValidateNodes;
|
|
ULONG ValidateAffinity1;
|
|
ULONG ValidateAffinity2;
|
|
|
|
RtlCopyMemory(&Fake, Buffer, sizeof(Fake));
|
|
|
|
//
|
|
// Do a little validation.
|
|
//
|
|
|
|
if ((Fake.Signature != 0x15a5a) ||
|
|
(Fake.Nodes == 0) ||
|
|
((Fake.AffinityShift | Fake.PageShift) & 0x20) ||
|
|
(Fake.Affinity == 0) ||
|
|
(Fake.Mask == 0) ||
|
|
(Fake.Mask >= Fake.Nodes)) {
|
|
Status = STATUS_INVALID_LEVEL;
|
|
break;
|
|
}
|
|
|
|
HalpFakeNumaAffinity = Fake.Affinity;
|
|
HalpFakeNumaAffinityShift = Fake.AffinityShift;
|
|
HalpFakeNumaPageMask = Fake.Mask;
|
|
HalpFakeNumaPageShift = Fake.PageShift;
|
|
|
|
//
|
|
// Generate the affinity mask for each node and
|
|
// make sure there's no overlapping affinity masks.
|
|
//
|
|
|
|
ValidateNodes = Fake.Nodes;
|
|
ValidateAffinity1 = 0;
|
|
ValidateAffinity2 = 0;
|
|
while (ValidateNodes) {
|
|
ValidateNodes--;
|
|
ValidateAffinity1 = HalpFakeNumaAffinity <<
|
|
(HalpFakeNumaAffinityShift * ValidateNodes);
|
|
if (ValidateAffinity1 == 0) {
|
|
break;
|
|
}
|
|
if ((ValidateAffinity1 & ValidateAffinity2) != 0) {
|
|
ValidateAffinity1 = 0;
|
|
break;
|
|
}
|
|
ValidateAffinity2 |= ValidateAffinity1;
|
|
}
|
|
|
|
if (!ValidateAffinity1) {
|
|
Status = STATUS_INVALID_LEVEL;
|
|
break;
|
|
}
|
|
|
|
HalpFakeNumaNodes = Fake.Nodes;
|
|
}
|
|
|
|
//
|
|
// End Mega Kludge.
|
|
//
|
|
|
|
if ((HalPaeEnabled() == TRUE) &&
|
|
(HalpFakeNumaNodes != 0) &&
|
|
(HalpFakeNumaAffinity != 0) &&
|
|
(HalpFakeNumaAffinityShift != 0) &&
|
|
(HalpFakeNumaPageMask != 0) &&
|
|
(HalpFakeNumaPageShift != 0) ) {
|
|
|
|
//
|
|
// Pretend we have a numa configuration,...
|
|
// for testing purposes only.
|
|
//
|
|
|
|
HAL_NUMA_TOPOLOGY_INTERFACE * NumaInfo;
|
|
NumaInfo = (HAL_NUMA_TOPOLOGY_INTERFACE *)Buffer;
|
|
|
|
NumaInfo->NumberOfNodes = HalpFakeNumaNodes;
|
|
NumaInfo->QueryProcessorNode = HalpFakeNumaQueryProcessorNode;
|
|
NumaInfo->PageToNode = HalpFakeNumaQueryPageToNode;
|
|
*ReturnedLength = sizeof(HAL_NUMA_TOPOLOGY_INTERFACE);
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Buffer size is wrong, we could return valid data
|
|
// if the buffer is too big,.... but instead we will
|
|
// use this as an indication that we're not compatible
|
|
// with the kernel.
|
|
//
|
|
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
break;
|
|
|
|
#endif
|
|
|
|
case HalQueryMcaInterface:
|
|
if (BufferSize == sizeof(HAL_MCA_INTERFACE)) {
|
|
HAL_MCA_INTERFACE *McaInterface;
|
|
|
|
McaInterface = (HAL_MCA_INTERFACE *)Buffer;
|
|
McaInterface->Lock = HalpMcaLockInterface;
|
|
McaInterface->Unlock = HalpMcaUnlockInterface;
|
|
McaInterface->ReadRegister = HalpMcaReadRegisterInterface;
|
|
|
|
*ReturnedLength = sizeof(HAL_MCA_INTERFACE);
|
|
Status = STATUS_SUCCESS;
|
|
} else {
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
break;
|
|
|
|
case HalQueryMaxHotPlugMemoryAddress:
|
|
if (BufferSize == sizeof(PHYSICAL_ADDRESS)) {
|
|
#ifdef ACPI_HAL
|
|
*((PPHYSICAL_ADDRESS) Buffer) = HalpMaxHotPlugMemoryAddress;
|
|
*ReturnedLength = sizeof(PHYSICAL_ADDRESS);
|
|
Status = STATUS_SUCCESS;
|
|
#else
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
#endif
|
|
} else {
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
break;
|
|
case HalQueryAMLIIllegalIOPortAddresses:
|
|
{
|
|
HAL_AMLI_BAD_IO_ADDRESS_LIST BadIOAddrList[] =
|
|
{
|
|
{0x000, 0x10, 0x1, NULL }, // ISA DMA
|
|
{0x020, 0x2, 0x0, NULL }, // PIC
|
|
{0x040, 0x4, 0x1, NULL }, // Timer1, Referesh, Speaker, Control Word
|
|
{0x048, 0x4, 0x1, NULL }, // Timer2, Failsafe
|
|
{0x070, 0x2, 0x1, NULL }, // Cmos/NMI enable
|
|
{0x074, 0x3, 0x1, NULL }, // Extended Cmos
|
|
{0x081, 0x3, 0x1, NULL }, // DMA
|
|
{0x087, 0x1, 0x1, NULL }, // DMA
|
|
{0x089, 0x1, 0x1, NULL }, // DMA
|
|
{0x08a, 0x2, 0x1, NULL }, // DMA
|
|
{0x08f, 0x1, 0x1, NULL }, // DMA
|
|
{0x090, 0x2, 0x1, NULL }, // Arbritration Control Port, Card Select Feedback
|
|
{0x093, 0x2, 0x1, NULL }, // Reserved, System board setup
|
|
{0x096, 0x2, 0x1, NULL }, // POS channel select
|
|
{0x0A0, 0x2, 0x0, NULL }, // Cascaded PIC
|
|
{0x0C0, 0x20, 0x1, NULL }, // ISA DMA
|
|
{0x4D0, 0x2, 0x0, NULL }, // Edge, level control registers for PIC
|
|
{0xCF8, 0x8, 0x1, &HaliHandlePCIConfigSpaceAccess}, // PCI Config Space Access Pair
|
|
{0x0, 0x0, 0x0, NULL }
|
|
};
|
|
|
|
if(BufferSize < sizeof(BadIOAddrList))
|
|
{
|
|
*ReturnedLength = sizeof(BadIOAddrList);
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
else
|
|
{
|
|
Length = sizeof(BadIOAddrList);
|
|
InternalBuffer = BadIOAddrList;
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
Status = STATUS_INVALID_LEVEL;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If non-zero Length copy data to callers buffer
|
|
//
|
|
|
|
if (Length) {
|
|
if (BufferSize < Length) {
|
|
Length = BufferSize;
|
|
}
|
|
|
|
*ReturnedLength = Length;
|
|
RtlCopyMemory (Buffer, InternalBuffer, Length);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
HaliSetSystemInformation (
|
|
IN HAL_SET_INFORMATION_CLASS InformationClass,
|
|
IN ULONG BufferSize,
|
|
IN PVOID Buffer
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
switch (InformationClass) {
|
|
|
|
case HalMcaRegisterDriver:
|
|
Status = HalpMcaRegisterDriver(
|
|
(PMCA_DRIVER_INFO) Buffer // Info about registering driver
|
|
);
|
|
break;
|
|
|
|
default:
|
|
Status = STATUS_INVALID_LEVEL;
|
|
break;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
#ifdef _PNP_POWER_
|
|
|
|
VOID
|
|
HalpLockSuspendCode (
|
|
IN PVOID CallbackContext,
|
|
IN PVOID Argument1,
|
|
IN PVOID Argument2
|
|
)
|
|
{
|
|
static PVOID CodeLock;
|
|
|
|
switch ((ULONG) Argument1) {
|
|
case 0:
|
|
//
|
|
// Lock code down which might be needed to perform a suspend
|
|
//
|
|
|
|
ASSERT (CodeLock == NULL);
|
|
CodeLock = MmLockPagableCodeSection (&HaliSuspendHibernateSystem);
|
|
break;
|
|
|
|
case 1:
|
|
//
|
|
// Release the code lock
|
|
//
|
|
|
|
MmUnlockPagableImageSection (CodeLock);
|
|
CodeLock = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
/*** HaliHandlePCIConfigSpaceAccess - Check to see if the Firmware is attempting to
|
|
* access to PCI Config space. If so, intercept
|
|
* the read/write call and do it using HAL API's.
|
|
* This way we can make these calls sync.
|
|
*
|
|
* ENTRY
|
|
* BOOLEAN Read - TRUE iff read
|
|
* ULONG Addr - Address to do the operation on
|
|
* ULONG Size - Size of data
|
|
* PULONG pData - Pointer to the data buffer.
|
|
*
|
|
* EXIT
|
|
* STATUS_SUCCESS if we do the PCI config space access.
|
|
*/
|
|
NTSTATUS HaliHandlePCIConfigSpaceAccess( BOOLEAN Read,
|
|
ULONG Addr,
|
|
ULONG Size,
|
|
PULONG pData
|
|
)
|
|
{
|
|
static PCI_TYPE1_CFG_BITS CF8_Data = {0};
|
|
static BOOLEAN CF8_Called = FALSE;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
if(Addr == 0xCF8)
|
|
{
|
|
CF8_Data.u.AsULONG = *pData;
|
|
CF8_Called = TRUE;
|
|
}
|
|
else if(Addr >= 0xCFC && Addr <= 0xCFF)
|
|
{
|
|
if(CF8_Called)
|
|
{
|
|
ULONG Offset = 0;
|
|
ULONG Return = 0;
|
|
PCI_SLOT_NUMBER SlotNumber = {0};
|
|
|
|
Offset = (CF8_Data.u.bits.RegisterNumber << 2) + (Addr - 0xCFC);
|
|
SlotNumber.u.bits.FunctionNumber = CF8_Data.u.bits.FunctionNumber;
|
|
SlotNumber.u.bits.DeviceNumber = CF8_Data.u.bits.DeviceNumber;
|
|
|
|
if (Read)
|
|
{
|
|
//
|
|
// Do PCI config space read through HAL
|
|
//
|
|
Return = HaliPciInterfaceReadConfig( NULL,
|
|
(UCHAR)CF8_Data.u.bits.BusNumber,
|
|
SlotNumber.u.AsULONG,
|
|
pData,
|
|
Offset,
|
|
Size
|
|
);
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Do PCI config space write through HAL
|
|
//
|
|
Return = HaliPciInterfaceWriteConfig( NULL,
|
|
(UCHAR)CF8_Data.u.bits.BusNumber,
|
|
SlotNumber.u.AsULONG,
|
|
pData,
|
|
Offset,
|
|
Size
|
|
);
|
|
|
|
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
return Status;
|
|
|
|
}
|
|
|