Leaked source code of windows server 2003
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.
 
 
 
 
 
 

707 lines
22 KiB

//**************************************************************************
//
// PNP.C -- Xena Gaming Project
//
// This module contains PnP Start, Stop, Remove, Power dispatch routines
// and the IRP cancel routine.
//
// Version 3.XX
//
// Copyright (c) 1997 Microsoft Corporation. All rights reserved.
//
// @doc
// @module PNP.C | Supports PnP Start, Stop, Remove, Power dispatch routines
// and the IRP cancel routine.
//**************************************************************************
#include <msgame.h>
//---------------------------------------------------------------------------
// Alloc_text pragma to specify routines that can be paged out.
//---------------------------------------------------------------------------
#ifdef ALLOC_PRAGMA
#pragma alloc_text (PAGE, MSGAME_Power)
#pragma alloc_text (PAGE, MSGAME_PnP)
#pragma alloc_text (PAGE, MSGAME_StopDevice)
#pragma alloc_text (PAGE, MSGAME_GetResources)
#endif
//---------------------------------------------------------------------------
// Private Data
//---------------------------------------------------------------------------
static PVOID CurrentGameContext = NULL;
//---------------------------------------------------------------------------
// @func The plug and play dispatch routines.
// @parm PDEVICE_OBJECT | DeviceObject | Pointer to device object
// @parm PIRP | pIrp | Pointer to IO request packet
// @rdesc Returns NT status code
// @comm Public function
//---------------------------------------------------------------------------
NTSTATUS MSGAME_PnP (IN PDEVICE_OBJECT DeviceObject, IN PIRP pIrp)
{
LONG i;
NTSTATUS ntStatus;
PDEVICE_EXTENSION pDevExt;
PIO_STACK_LOCATION pIrpStack;
PAGED_CODE ();
MsGamePrint ((DBG_INFORM, "%s: %s_PnP Enter\n", MSGAME_NAME, MSGAME_NAME));
pDevExt = GET_MINIDRIVER_DEVICE_EXTENSION (DeviceObject);
pIrpStack = IoGetCurrentIrpStackLocation (pIrp);
InterlockedIncrement (&pDevExt->IrpCount);
if (pDevExt->Removed)
{
//
// Someone sent us another plug and play IRP after removed
//
MsGamePrint ((DBG_SEVERE, "%s: PnP Irp after device removed\n", MSGAME_NAME));
ASSERT (FALSE);
if (!InterlockedDecrement (&pDevExt->IrpCount))
KeSetEvent (&pDevExt->RemoveEvent, 0, FALSE);
pIrp->IoStatus.Information = 0;
pIrp->IoStatus.Status = STATUS_DELETE_PENDING;
IoCompleteRequest (pIrp, IO_NO_INCREMENT);
return (STATUS_DELETE_PENDING);
}
switch (pIrpStack->MinorFunction)
{
case IRP_MN_START_DEVICE:
//
// We cannot touch the device (send it any non-Pnp Irps) until a
// start device has been passed down to the lower drivers.
//
IoCopyCurrentIrpStackLocationToNext (pIrp);
IoSetCompletionRoutine (pIrp, MSGAME_PnPComplete, pDevExt, TRUE, TRUE, TRUE);
ntStatus = IoCallDriver (pDevExt->TopOfStack, pIrp);
if (ntStatus == STATUS_PENDING)
KeWaitForSingleObject (
&pDevExt->StartEvent,
Executive, // Waiting for reason of a driver
KernelMode, // Waiting in kernel mode
FALSE, // No allert
NULL); // No timeout
if (NT_SUCCESS (ntStatus))
{
//
// As we are now back from our start device we can do work.
//
ntStatus = MSGAME_StartDevice (pDevExt, pIrp);
}
//
// Return Status
//
pIrp->IoStatus.Information = 0;
pIrp->IoStatus.Status = ntStatus;
IoCompleteRequest (pIrp, IO_NO_INCREMENT);
break;
case IRP_MN_STOP_DEVICE:
//
// After the start IRP has been sent to the lower driver object, the bus may
// NOT send any more IRPS down ``touch'' until another START has occured.
// Whatever access is required must be done before Irp passed on.
//
MSGAME_StopDevice (pDevExt, TRUE);
//
// We don't need a completion routine so fire and forget.
// Set the current stack location to the next stack location and
// call the next device object.
//
IoSkipCurrentIrpStackLocation (pIrp);
ntStatus = IoCallDriver (pDevExt->TopOfStack, pIrp);
break;
case IRP_MN_SURPRISE_REMOVAL:
//
// We have been unexpectedly removed by the user. Stop the device,
// set status to SUCCESS and call next stack location with this IRP.
//
if (!pDevExt->Surprised && pDevExt->Started)
MSGAME_StopDevice (pDevExt, TRUE);
pDevExt->Surprised = TRUE;
//
// We don't want a completion routine so fire and forget.
// Set the current stack location to the next location and
// call the next device after setting status to success.
//
pIrp->IoStatus.Information = 0;
pIrp->IoStatus.Status = STATUS_SUCCESS;
IoSkipCurrentIrpStackLocation (pIrp);
ntStatus = IoCallDriver (pDevExt->TopOfStack, pIrp);
break;
case IRP_MN_REMOVE_DEVICE:
//
// The PlugPlay system has dictacted the removal of this device. We
// have no choice but to detach and delete the device object.
// (If we wanted to express an interest in preventing this removal,
// we should have filtered the query remove and query stop routines.)
// Note: we might receive a remove WITHOUT first receiving a stop.
if (pDevExt->Started)
{
//
// Stop the device without touching the hardware.
//
MSGAME_StopDevice (pDevExt, FALSE);
}
pDevExt->Removed = TRUE;
//
// Send on the remove IRP
//
IoSkipCurrentIrpStackLocation (pIrp);
ntStatus = IoCallDriver (pDevExt->TopOfStack, pIrp);
//
// Must double-decrement because we start at One
//
i = InterlockedDecrement (&pDevExt->IrpCount);
ASSERT(i>0);
if (InterlockedDecrement (&pDevExt->IrpCount) > 0)
KeWaitForSingleObject (&pDevExt->RemoveEvent, Executive, KernelMode, FALSE, NULL);
//
// Return success
//
return (STATUS_SUCCESS);
default:
//
// Here the filter driver might modify the behavior of these IRPS
// Please see PlugPlay documentation for use of these IRPs.
//
IoSkipCurrentIrpStackLocation (pIrp);
MsGamePrint ((DBG_INFORM, "%s_PnP calling next driver with minor function %ld at IRQL %ld\n", MSGAME_NAME, pIrpStack->MinorFunction, KeGetCurrentIrql()));
ntStatus = IoCallDriver (pDevExt->TopOfStack, pIrp);
break;
}
if (!InterlockedDecrement (&pDevExt->IrpCount))
KeSetEvent (&pDevExt->RemoveEvent, 0, FALSE);
MsGamePrint ((DBG_INFORM, "%s: %s_PnP exit\n", MSGAME_NAME, MSGAME_NAME));
return (ntStatus);
}
//---------------------------------------------------------------------------
// @func Completion routine for Pnp start device
// @parm PDEVICE_OBJECT | DeviceObject | Pointer to device object
// @parm PIRP | pIrp | Pointer to IO request packet
// @parm PVOID | Context | Pointer to device context
// @rdesc Returns NT status code
// @comm Public function
//---------------------------------------------------------------------------
NTSTATUS MSGAME_PnPComplete (IN PDEVICE_OBJECT DeviceObject, IN PIRP pIrp, IN PVOID Context)
{
PIO_STACK_LOCATION pIrpStack;
PDEVICE_EXTENSION pDevExt;
NTSTATUS ntStatus = STATUS_SUCCESS;
UNREFERENCED_PARAMETER (DeviceObject);
MsGamePrint ((DBG_INFORM, "%s: %s_PnPComplete enter\n", MSGAME_NAME, MSGAME_NAME));
pDevExt = (PDEVICE_EXTENSION) Context;
pIrpStack = IoGetCurrentIrpStackLocation (pIrp);
switch (pIrpStack->MajorFunction)
{
case IRP_MJ_PNP:
switch (pIrpStack->MinorFunction)
{
case IRP_MN_START_DEVICE:
KeSetEvent (&pDevExt->StartEvent, 0, FALSE);
//
// Take IRP back so we can continue using it during the IRP_MN_START_DEVICE
// dispatch routine. We will have to call IoCompleteRequest there.
//
return (STATUS_MORE_PROCESSING_REQUIRED);
default:
break;
}
break;
default:
break;
}
MsGamePrint ((DBG_INFORM, "%s: %s_PnPComplete Exit\n", MSGAME_NAME, MSGAME_NAME));
return (ntStatus);
}
//---------------------------------------------------------------------------
// @func PnP start device IRP handler
// @parm PDEVICE_EXTENSION | pDevExt | Pointer to device extenstion
// @parm PIRP | pIrp | Pointer to IO request packet
// @rdesc Returns NT status code
// @comm Public function
//---------------------------------------------------------------------------
NTSTATUS MSGAME_StartDevice (IN PDEVICE_EXTENSION pDevExt, IN PIRP pIrp)
{
PWCHAR HardwareId;
NTSTATUS ntStatus;
PDEVICEINFO DevInfo;
PDEVICE_OBJECT RemoveObject;
PAGED_CODE ();
MsGamePrint ((DBG_INFORM, "%s: %s_StartDevice Enter\n", MSGAME_NAME, MSGAME_NAME));
//
// The PlugPlay system should not have started a removed device!
//
ASSERT (!pDevExt->Removed);
if (pDevExt->Started)
return (STATUS_SUCCESS);
//
// Acquire resources we need for this device
//
ntStatus = MSGAME_GetResources (pDevExt, pIrp);
if (!NT_SUCCESS(ntStatus))
return (ntStatus);
//
// Dump debug OEM Data fields
//
MsGamePrint ((DBG_CONTROL, "%s: %s_StartDevice Called With OEM_DATA[0] = 0x%X\n", MSGAME_NAME, MSGAME_NAME, pDevExt->PortInfo.OemData[0]));
MsGamePrint ((DBG_CONTROL, "%s: %s_StartDevice Called With OEM_DATA[1] = 0x%X\n", MSGAME_NAME, MSGAME_NAME, pDevExt->PortInfo.OemData[1]));
MsGamePrint ((DBG_CONTROL, "%s: %s_StartDevice Called With OEM_DATA[2] = 0x%X\n", MSGAME_NAME, MSGAME_NAME, pDevExt->PortInfo.OemData[2]));
MsGamePrint ((DBG_CONTROL, "%s: %s_StartDevice Called With OEM_DATA[3] = 0x%X\n", MSGAME_NAME, MSGAME_NAME, pDevExt->PortInfo.OemData[3]));
MsGamePrint ((DBG_CONTROL, "%s: %s_StartDevice Called With OEM_DATA[4] = 0x%X\n", MSGAME_NAME, MSGAME_NAME, pDevExt->PortInfo.OemData[4]));
MsGamePrint ((DBG_CONTROL, "%s: %s_StartDevice Called With OEM_DATA[5] = 0x%X\n", MSGAME_NAME, MSGAME_NAME, pDevExt->PortInfo.OemData[5]));
MsGamePrint ((DBG_CONTROL, "%s: %s_StartDevice Called With OEM_DATA[6] = 0x%X\n", MSGAME_NAME, MSGAME_NAME, pDevExt->PortInfo.OemData[6]));
MsGamePrint ((DBG_CONTROL, "%s: %s_StartDevice Called With OEM_DATA[7] = 0x%X\n", MSGAME_NAME, MSGAME_NAME, pDevExt->PortInfo.OemData[7]));
//
// Make sure we are only on one gameport
//
if (CurrentGameContext && (CurrentGameContext != pDevExt->PortInfo.GameContext))
{
MsGamePrint ((DBG_SEVERE, "%s: %s_StartDevice Cannot Load on Multiple Gameports: 0x%X and 0x%X\n",\
CurrentGameContext, pDevExt->PortInfo.GameContext, MSGAME_NAME, MSGAME_NAME));
return (STATUS_DEVICE_CONFIGURATION_ERROR);
}
CurrentGameContext = pDevExt->PortInfo.GameContext;
//
// Get the HardwareId for this Start request
//
HardwareId = MSGAME_GetHardwareId (pDevExt->Self);
if (!HardwareId)
{
MsGamePrint ((DBG_SEVERE, "%s: %s_GetHardwareId Failed\n", MSGAME_NAME, MSGAME_NAME));
return (STATUS_DEVICE_CONFIGURATION_ERROR);
}
//
// Initialize OEM Data
//
SET_DEVICE_OBJECT(&pDevExt->PortInfo, pDevExt->Self);
//
// Now start the low level device
//
ntStatus = DEVICE_StartDevice (&pDevExt->PortInfo, HardwareId);
//
// Free HardwareId right away
//
MSGAME_FreeHardwareId (HardwareId);
//
// Check if low-level start device failed
//
if (NT_ERROR(ntStatus))
{
MsGamePrint ((DBG_SEVERE, "%s: %s_StartDevice Failed\n", MSGAME_NAME, MSGAME_NAME));
return (ntStatus);
}
//
// Everything is fine so let's say device has started
//
pDevExt->Started = TRUE;
//
// Return status
//
MsGamePrint ((DBG_INFORM, "%s: %s_StartDevice Exit\n", MSGAME_NAME, MSGAME_NAME));
return (STATUS_SUCCESS);
}
//---------------------------------------------------------------------------
// @func PnP start device IRP handler
// @parm PDEVICE_EXTENSION | pDevExt | Pointer to device extenstion
// @parm BOOLEAN | TouchTheHardware | Flag to send non PnP Irps to device
// @rdesc Returns NT status code
// @comm Public function <en->
// The PlugPlay system has dictacted the removal of this device.
// We have no choise but to detach and delete the device object.
// (If we wanted to express and interest in preventing this removal,
// we should have filtered the query remove and query stop routines.)
// Note! we might receive a remove WITHOUT first receiving a stop
//---------------------------------------------------------------------------
VOID MSGAME_StopDevice (IN PDEVICE_EXTENSION pDevExt, IN BOOLEAN TouchTheHardware)
{
PAGED_CODE ();
MsGamePrint ((DBG_INFORM, "%s: %s_StopDevice enter \n", MSGAME_NAME, MSGAME_NAME));
//
// The PlugPlay system should not have started a removed device!
//
ASSERT (!pDevExt->Removed);
if (!pDevExt->Started)
return;
//
// Now stop the low level device
//
DEVICE_StopDevice (&pDevExt->PortInfo, TouchTheHardware);
//
// Everything is fine so let's say device has stopped
//
pDevExt->Started = FALSE;
MsGamePrint ((DBG_INFORM, "%s: %s_StopDevice exit \n", MSGAME_NAME, MSGAME_NAME));
}
//---------------------------------------------------------------------------
// @func Power dispatch routine.
// @parm PDEVICE_OBJECT | DeviceObject | Pointer to device object
// @parm PIRP | pIrp | Pointer to IO request packet
// @rdesc Returns NT status code
// @comm Public function
//---------------------------------------------------------------------------
NTSTATUS MSGAME_Power (IN PDEVICE_OBJECT DeviceObject, IN PIRP pIrp)
{
PDEVICE_EXTENSION pDevExt;
NTSTATUS ntStatus;
PIO_STACK_LOCATION pIrpStack;
PAGED_CODE ();
pIrpStack = IoGetCurrentIrpStackLocation (pIrp);
MsGamePrint ((DBG_CONTROL, "%s: %s_Power Enter MN_Function %x type %x State %x\n", MSGAME_NAME, MSGAME_NAME,pIrpStack->MinorFunction,pIrpStack->Parameters.Power.Type,pIrpStack->Parameters.Power.State));
pDevExt = GET_MINIDRIVER_DEVICE_EXTENSION (DeviceObject);
//
// This IRP was sent to the filter driver. Since we do not know what
// to do with the IRP, we should pass it on along down the stack.
//
InterlockedIncrement (&pDevExt->IrpCount);
if (pDevExt->Removed)
{
ntStatus = STATUS_DELETE_PENDING;
pIrp->IoStatus.Information = 0;
pIrp->IoStatus.Status = ntStatus;
IoCompleteRequest (pIrp, IO_NO_INCREMENT);
}
else
{
//Is System trying to wake up device
if ((2 == (pIrpStack->MinorFunction)) && (1 == (pIrpStack->Parameters.Power.Type)) &&( 1 == (pIrpStack->Parameters.Power.State.SystemState)))
{
// Clear DeviceDetected to force reset and redetect
SET_DEVICE_INFO(&(pDevExt->PortInfo),0);
MsGamePrint ((DBG_CONTROL, "%s: %s_Power Resetting Device Detected\n", MSGAME_NAME, MSGAME_NAME));
}
//
// Power IRPS come synchronously; drivers must call
// PoStartNextPowerIrp, when they are ready for the next power irp.
// This can be called here, or in the completetion routine.
//
PoStartNextPowerIrp (pIrp);
//
// PoCallDriver NOT IoCallDriver.
//
IoSkipCurrentIrpStackLocation (pIrp);
ntStatus = PoCallDriver (pDevExt->TopOfStack, pIrp);
}
if (!InterlockedDecrement (&pDevExt->IrpCount))
KeSetEvent (&pDevExt->RemoveEvent, 0, FALSE);
MsGamePrint ((DBG_INFORM, "%s: %s_Power Exit\n", MSGAME_NAME, MSGAME_NAME));
return (ntStatus);
}
//---------------------------------------------------------------------------
// @func Calls GameEnum to request gameport parameters
// @parm PDEVICE_EXTENSION | pDevExt | Pointer to device extenstion
// @parm PIRP | pIrp | Pointer to IO request packet
// @rdesc Returns NT status code
// @comm Public function
//---------------------------------------------------------------------------
NTSTATUS MSGAME_GetResources (IN PDEVICE_EXTENSION pDevExt, IN PIRP pIrp)
{
NTSTATUS ntStatus = STATUS_SUCCESS;
KEVENT IoctlCompleteEvent;
IO_STATUS_BLOCK IoStatus;
PIO_STACK_LOCATION pIrpStack, nextStack;
PAGED_CODE ();
MsGamePrint ((DBG_INFORM, "%s: %s_GetResources Enter\n", MSGAME_NAME, MSGAME_NAME));
//
// Issue a synchronous request to get the resources info from GameEnum
//
KeInitializeEvent (&IoctlCompleteEvent, NotificationEvent, FALSE);
pIrpStack = IoGetCurrentIrpStackLocation (pIrp);
nextStack = IoGetNextIrpStackLocation (pIrp);
ASSERT (nextStack);
//
// Pass the Portinfo buffer of the DeviceExtension
//
pDevExt->PortInfo.Size = sizeof (GAMEPORT);
nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_GAMEENUM_PORT_PARAMETERS;
nextStack->Parameters.DeviceIoControl.InputBufferLength = sizeof (GAMEPORT);
nextStack->Parameters.DeviceIoControl.OutputBufferLength = sizeof (GAMEPORT);
pIrp->UserBuffer = &pDevExt->PortInfo;
IoSetCompletionRoutine (pIrp, MSGAME_GetResourcesComplete, &IoctlCompleteEvent, TRUE, TRUE, TRUE);
MsGamePrint ((DBG_CONTROL, "%s: Calling GameEnum to Get Resources at IRQL=%lu\n", MSGAME_NAME, KeGetCurrentIrql()));
ntStatus = IoCallDriver (pDevExt->TopOfStack, pIrp);
if (ntStatus == STATUS_PENDING)
ntStatus = KeWaitForSingleObject (&IoctlCompleteEvent, Suspended, KernelMode, FALSE, NULL);
if (NT_SUCCESS(ntStatus))
MsGamePrint ((DBG_VERBOSE, "%s: %s_GetResources Port Obtained = 0x%lX\n", MSGAME_NAME, MSGAME_NAME, pDevExt->PortInfo.GameContext));
else MsGamePrint ((DBG_SEVERE, "%s: GameEnum Failed to Provide Resources, Status = %X\n", MSGAME_NAME, ntStatus));
//
// Return Status
//
MsGamePrint ((DBG_INFORM, "%s: %s_GetResources Exit\n", MSGAME_NAME, MSGAME_NAME));
return (pIrp->IoStatus.Status);
}
//---------------------------------------------------------------------------
// @func Completion routine for GameEnum get reosources driver call
// @parm PDEVICE_OBJECT | DeviceObject | Pointer to device object
// @parm PIRP | pIrp | Pointer to IO request packet
// @parm PVOID | Context | Pointer to device context
// @rdesc Returns NT status code
// @comm Public function
//---------------------------------------------------------------------------
NTSTATUS MSGAME_GetResourcesComplete (IN PDEVICE_OBJECT DeviceObject, IN PIRP pIrp, IN PVOID Context)
{
UNREFERENCED_PARAMETER (DeviceObject);
KeSetEvent ((PKEVENT)Context, 0, FALSE);
if (pIrp->PendingReturned)
IoMarkIrpPending (pIrp);
return (STATUS_MORE_PROCESSING_REQUIRED);
}
//---------------------------------------------------------------------------
// @func Gets HardwareId string for device object (assumes caller frees)
// @parm PDEVICE_OBJECT | DeviceObject | Pointer to device object
// @rdesc Pointer to allocated memory containing string
// @comm Public function
//---------------------------------------------------------------------------
PWCHAR MSGAME_GetHardwareId (IN PDEVICE_OBJECT DeviceObject)
{
LONG BufferLength = 0;
PWCHAR Buffer = NULL;
NTSTATUS ntStatus;
PDEVICE_OBJECT pPDO;
MsGamePrint ((DBG_INFORM, "%s: %s_GetHardwareId\n", MSGAME_NAME, MSGAME_NAME));
//
// Walk to end of stack and get pointer to PDO
//
pPDO = DeviceObject;
while (GET_NEXT_DEVICE_OBJECT(pPDO))
pPDO = GET_NEXT_DEVICE_OBJECT(pPDO);
//
// Get Buffer length
//
ntStatus = IoGetDeviceProperty(
pPDO,
DevicePropertyHardwareID,
BufferLength,
Buffer,
&BufferLength);
ASSERT(ntStatus==STATUS_BUFFER_TOO_SMALL);
//
// Allocate room for HardwareID
//
Buffer = ExAllocatePool(PagedPool, BufferLength);
if (!Buffer)
{
//
// If we cannot get the memory to try this, then just say it is a no match
//
MsGamePrint ((DBG_SEVERE, "%s: %s_GetHardwareId failed ExAllocate\n", MSGAME_NAME, MSGAME_NAME));
return (NULL);
}
//
// Now get the data
//
ntStatus = IoGetDeviceProperty(
pPDO,
DevicePropertyHardwareID,
BufferLength,
Buffer,
&BufferLength);
//
// On error, free memory and return NULL
//
if (!NT_SUCCESS(ntStatus))
{
MsGamePrint ((DBG_SEVERE, "%s: %s_GetHardwareId couldn't get id from PDO\n", MSGAME_NAME, MSGAME_NAME));
ExFreePool(Buffer);
return (NULL);
}
//
// Return buffer containing hardware Id - must be freed by caller
//
return (Buffer);
}
//---------------------------------------------------------------------------
// @func Compares HardwareId strings
// @parm PWCHAR | HardwareId | Pointer to object hardware id
// @parm PWCHAR | DeviceId | Pointer to device's hardware id
// @rdesc True if strings are the same, false if different
// @comm Public function
//---------------------------------------------------------------------------
BOOLEAN MSGAME_CompareHardwareIds (IN PWCHAR HardwareId, IN PWCHAR DeviceId)
{
MsGamePrint ((DBG_INFORM, "%s: %s_CompareHardwareIds\n", MSGAME_NAME, MSGAME_NAME));
//
// Peform runtime parameter checks
//
if (!HardwareId || !DeviceId)
{
MsGamePrint ((DBG_SEVERE, "%s: %s_CompareHardwareIds - Bogus Strings\n", MSGAME_NAME, MSGAME_NAME));
return (FALSE);
}
//
// Perform char-by-char string compare
//
while (*HardwareId && *DeviceId)
{
if (TOUPPER(*HardwareId) != TOUPPER(*DeviceId))
return (FALSE);
HardwareId++;
DeviceId++;
}
//
// Return success
//
return (TRUE);
}
//---------------------------------------------------------------------------
// @func Frees HardwareId allocated from MSGAME_GetHardwareId
// @parm PWCHAR | HardwareId | Pointer to hardware id to free
// @rdesc None
// @comm Public function
//---------------------------------------------------------------------------
VOID MSGAME_FreeHardwareId (IN PWCHAR HardwareId)
{
MsGamePrint ((DBG_INFORM, "%s: %s_FreeHardwareId\n", MSGAME_NAME, MSGAME_NAME));
//
// Free memory pool
//
if (HardwareId)
ExFreePool(HardwareId);
}