|
|
//**************************************************************************
//
// 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); }
|