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.
 
 
 
 
 
 

2803 lines
89 KiB

/*++
Copyright (c) 1997-2000 Microsoft Corporation
Module Name:
PNP.C
Abstract:
This module contains contains the plugplay calls
PNP / WDM BUS driver.
@@BEGIN_DDKSPLIT
Author:
Kenneth D. Ray
Doron J. Holan
@@END_DDKSPLIT
Environment:
kernel mode only
Notes:
Revision History:
--*/
#include <wdm.h>
#include "gameport.h"
#include "gameenum.h"
#include "stdio.h"
#define HWID_TEMPLATE L"gameport"
#define HWID_TEMPLATE_LENGTH 8
#define LOWERCASE(_x_) (_x_|0x20)
#define MAX_DEVICE_ID_LEN 300
#ifdef ALLOC_PRAGMA
#pragma alloc_text (PAGE, Game_AddDevice)
#pragma alloc_text (PAGE, Game_SystemControl)
#pragma alloc_text (PAGE, Game_PnP)
#pragma alloc_text (PAGE, Game_Power)
#pragma alloc_text (PAGE, Game_FDO_Power)
#pragma alloc_text (PAGE, Game_PDO_Power)
#pragma alloc_text (PAGE, Game_InitializePdo)
#pragma alloc_text (PAGE, Game_CheckHardwareIDs)
#pragma alloc_text (PAGE, Game_Expose)
#pragma alloc_text (PAGE, Game_ExposeSibling)
#pragma alloc_text (PAGE, Game_Remove)
#pragma alloc_text (PAGE, Game_RemoveSelf)
#pragma alloc_text (PAGE, Game_RemoveEx)
#pragma alloc_text (PAGE, Game_RemovePdo)
#pragma alloc_text (PAGE, Game_RemoveFdo)
#pragma alloc_text (PAGE, Game_ListPorts)
#pragma alloc_text (PAGE, Game_FDO_PnP)
#pragma alloc_text (PAGE, Game_PDO_PnP)
#endif
NTSTATUS
Game_AddDevice(
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT BusPhysicalDeviceObject
)
/*++
Routine Description.
A bus has been found. Attach our FDO to it.
Allocate any required resources. Set things up. And be prepared for the
first ``start device.''
Arguments:
BusPhysicalDeviceObject - Device object representing the bus. That to which we
attach a new FDO.
DriverObject - This very self referenced driver.
--*/
{
NTSTATUS status;
PDEVICE_OBJECT deviceObject;
PFDO_DEVICE_DATA deviceData;
#if DBG
ULONG nameLength;
PWCHAR deviceName;
#endif
PAGED_CODE ();
Game_KdPrint_Def (GAME_DBG_SS_TRACE, ("Add Device: 0x%x\n",
BusPhysicalDeviceObject));
status = IoCreateDevice (
DriverObject, // our driver object
sizeof (FDO_DEVICE_DATA), // device object extension size
NULL, // FDOs do not have names
FILE_DEVICE_BUS_EXTENDER,
FILE_DEVICE_SECURE_OPEN, // No special characteristics
TRUE, // our FDO is exclusive
&deviceObject); // The device object created
if (NT_SUCCESS (status)) {
deviceData = (PFDO_DEVICE_DATA) deviceObject->DeviceExtension;
RtlFillMemory (deviceData, sizeof (FDO_DEVICE_DATA), 0);
#if DBG
deviceData->DebugLevel = GameEnumDebugLevel;
#endif
deviceData->IsFDO = TRUE;
deviceData->Self = deviceObject;
ExInitializeFastMutex (&deviceData->Mutex);
deviceData->Removed = FALSE;
InitializeListHead (&deviceData->PDOs);
// Set the PDO for use with PlugPlay functions
deviceData->UnderlyingPDO = BusPhysicalDeviceObject;
//
// Will get preincremented everytime a new PDO is created ... want the
// first ID to be zero
//
deviceData->UniqueIDCount = GAMEENUM_UNIQUEID_START;
//
// Attach our filter driver to the device stack.
// the return value of IoAttachDeviceToDeviceStack is the top of the
// attachment chain. This is where all the IRPs should be routed.
//
// Our filter will send IRPs to the top of the stack and use the PDO
// for all PlugPlay functions.
//
deviceData->TopOfStack = IoAttachDeviceToDeviceStack (
deviceObject,
BusPhysicalDeviceObject);
if (deviceData->TopOfStack == NULL) {
IoDeleteDevice(deviceObject);
return STATUS_DEVICE_NOT_CONNECTED;
}
// Bias outstanding request to 1 so that we can look for a
// transition to zero when processing the remove device PlugPlay IRP.
deviceData->OutstandingIO = 1;
KeInitializeEvent(&deviceData->RemoveEvent,
SynchronizationEvent,
FALSE); // initialized to not signalled
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
deviceObject->Flags |= DO_POWER_PAGABLE;
//
// Tell the PlugPlay system that this device will need an interface
// device class shingle.
//
// It may be that the driver cannot hang the shingle until it starts
// the device itself, so that it can query some of its properties.
// (Aka the shingles guid (or ref string) is based on the properties
// of the device.)
//
status = IoRegisterDeviceInterface (
BusPhysicalDeviceObject,
(LPGUID) &GUID_GAMEENUM_BUS_ENUMERATOR,
NULL, // No ref string
&deviceData->DevClassAssocName);
if (!NT_SUCCESS (status)) {
Game_KdPrint (deviceData, GAME_DBG_SS_ERROR,
("AddDevice: IoRegisterDeviceInterface failed (%x)", status));
IoDeleteDevice (deviceObject);
return status;
}
//
// If for any reason you need to save values in a safe location that
// clients of this DeviceInterface might be interested in reading
// here is the time to do so, with the function
// IoOpenDeviceClassRegistryKey
// the symbolic link name used is was returned in
// deviceData->DevClassAssocName (the same name which is returned by
// IoGetDeviceClassAssociations and the SetupAPI equivs.
//
#if DBG
nameLength = 0;
status = IoGetDeviceProperty (BusPhysicalDeviceObject,
DevicePropertyPhysicalDeviceObjectName,
0,
NULL,
&nameLength);
//
// Proceed only if the PDO has a name
//
if (status == STATUS_BUFFER_TOO_SMALL && nameLength != 0) {
deviceName = ExAllocatePool (NonPagedPool, nameLength);
if (NULL == deviceName) {
IoDeleteDevice (deviceObject);
Game_KdPrint (deviceData, GAME_DBG_SS_ERROR,
("AddDevice: no memory to alloc DeviceName (0x%x)",
nameLength));
return STATUS_INSUFFICIENT_RESOURCES;
}
IoGetDeviceProperty (BusPhysicalDeviceObject,
DevicePropertyPhysicalDeviceObjectName,
nameLength,
deviceName,
&nameLength);
Game_KdPrint (deviceData, GAME_DBG_SS_TRACE,
("AddDevice: %x to %x->%x (%ws) \n",
deviceObject,
deviceData->TopOfStack,
BusPhysicalDeviceObject,
deviceName));
ExFreePool(deviceName);
}
status = STATUS_SUCCESS;
#endif
if (!NT_SUCCESS (status)) {
Game_KdPrint (deviceData, GAME_DBG_SS_ERROR,
("AddDevice: IoGetDeviceClass failed (%x)", status));
return status;
}
}
return status;
}
NTSTATUS
Game_SystemControl (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PCOMMON_DEVICE_DATA commonData;
PAGED_CODE ();
commonData = (PCOMMON_DEVICE_DATA) DeviceObject->DeviceExtension;
if (commonData->IsFDO) {
IoSkipCurrentIrpStackLocation (Irp);
return IoCallDriver (((PFDO_DEVICE_DATA) commonData)->TopOfStack, Irp);
}
else {
//
// The PDO, just complete the request with the current status
//
NTSTATUS status = Irp->IoStatus.Status;
IoCompleteRequest (Irp, IO_NO_INCREMENT);
return status;
}
}
NTSTATUS
Game_PnP (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Answer the plithera of Irp Major PnP IRPS.
--*/
{
PIO_STACK_LOCATION irpStack;
NTSTATUS status;
PCOMMON_DEVICE_DATA commonData;
KIRQL oldIrq;
PAGED_CODE ();
status = STATUS_SUCCESS;
irpStack = IoGetCurrentIrpStackLocation (Irp);
ASSERT (IRP_MJ_PNP == irpStack->MajorFunction);
commonData = (PCOMMON_DEVICE_DATA) DeviceObject->DeviceExtension;
if (commonData->IsFDO) {
Game_KdPrint (commonData, GAME_DBG_PNP_TRACE,
("PNP: Functional DO: %x IRP: %x\n", DeviceObject, Irp));
status = Game_FDO_PnP (
DeviceObject,
Irp,
irpStack,
(PFDO_DEVICE_DATA) commonData);
} else {
Game_KdPrint (commonData, GAME_DBG_PNP_TRACE,
("PNP: Physical DO: %x IRP: %x\n", DeviceObject, Irp));
status = Game_PDO_PnP (
DeviceObject,
Irp,
irpStack,
(PPDO_DEVICE_DATA) commonData);
}
return status;
}
NTSTATUS
Game_FDO_PnP (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpStack,
IN PFDO_DEVICE_DATA DeviceData
)
/*++
Routine Description:
Handle requests from the PlugPlay system for the BUS itself
NB: the various Minor functions of the PlugPlay system will not be
overlapped and do not have to be reentrant
--*/
{
NTSTATUS status;
KEVENT event;
ULONG length;
ULONG i;
PLIST_ENTRY entry;
PPDO_DEVICE_DATA pdoData;
PDEVICE_RELATIONS relations, oldRelations;
PIO_STACK_LOCATION stack;
PAGED_CODE ();
status = Game_IncIoCount (DeviceData);
if (!NT_SUCCESS (status)) {
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = status;
IoCompleteRequest (Irp, IO_NO_INCREMENT);
return status;
}
stack = IoGetCurrentIrpStackLocation (Irp);
switch (IrpStack->MinorFunction) {
case IRP_MN_START_DEVICE:
//
// BEFORE you are allowed to ``touch'' the device object to which
// the FDO is attached (that send an irp from the bus to the Device
// object to which the bus is attached). You must first pass down
// the start IRP. It might not be powered on, or able to access or
// something.
//
if (DeviceData->Started) {
status = STATUS_SUCCESS;
break;
}
Game_KdPrint (DeviceData, GAME_DBG_PNP_TRACE, ("Start Device\n"));
status = Game_SendIrpSynchronously (DeviceData->TopOfStack, Irp, TRUE, TRUE);
if (NT_SUCCESS(status)) {
//
// Now we can touch the lower device object as it is now started.
//
if ((NULL == stack->Parameters.StartDevice.AllocatedResources) ||
(NULL == stack->Parameters.StartDevice.AllocatedResourcesTranslated)) {
status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
status = Game_StartFdo (DeviceData,
&stack->Parameters.StartDevice.AllocatedResources->List[0].PartialResourceList,
&stack->Parameters.StartDevice.AllocatedResourcesTranslated->List[0].PartialResourceList);
//
// find the translated resources and store them someplace
// safe for given out for the PDOs.
//
if (NT_SUCCESS (status)) {
//
// Turn on the shingle and point it to the given device object.
//
DeviceData->Started = TRUE;
IoSetDeviceInterfaceState(&DeviceData->DevClassAssocName, TRUE);
}
}
//
// We must now complete the IRP, since we stopped it in the
// completetion routine with MORE_PROCESSING_REQUIRED.
//
Irp->IoStatus.Information = 0;
break;
case IRP_MN_QUERY_STOP_DEVICE:
Game_KdPrint (DeviceData, GAME_DBG_PNP_TRACE, ("Query Stop Device\n"));
//
// Test to see if there are any PDO created as children of this FDO
// If there are then conclude the device is busy and fail the
// query stop.
//
// ISSUE
// We could do better, by seing if the children PDOs are actually
// currently open. If they are not then we could stop, get new
// resouces, fill in the new resouce values, and then when a new client
// opens the PDO use the new resources. But this works for now.
//
if (DeviceData->NumPDOs) {
status = STATUS_UNSUCCESSFUL;
Irp->IoStatus.Status = status;
IoCompleteRequest (Irp, IO_NO_INCREMENT);
} else {
status = STATUS_SUCCESS;
Irp->IoStatus.Status = status;
IoSkipCurrentIrpStackLocation (Irp);
status = IoCallDriver (DeviceData->TopOfStack, Irp);
}
Game_DecIoCount (DeviceData);
return status;
case IRP_MN_STOP_DEVICE:
Game_KdPrint (DeviceData, GAME_DBG_PNP_TRACE, ("Stop Device\n"));
//
// 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.
// What ever access is required must be done before the Irp is passed
// on.
//
// Stop device means that the resources given durring Start device
// are no revoked. So we need to stop using them
//
if (DeviceData->Started) {
DeviceData->Started = FALSE;
//
// Free resources given by start device.
//
if (DeviceData->MappedPorts) {
MmUnmapIoSpace (DeviceData->GamePortAddress,
DeviceData->GamePortAddressLength);
}
}
//
// 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.
//
Irp->IoStatus.Status = STATUS_SUCCESS;
IoSkipCurrentIrpStackLocation (Irp);
status = IoCallDriver (DeviceData->TopOfStack, Irp);
Game_DecIoCount (DeviceData);
return status;
case IRP_MN_SURPRISE_REMOVAL:
ASSERT(!DeviceData->Acquired);
Game_RemoveFdo(DeviceData);
Irp->IoStatus.Status = STATUS_SUCCESS;
IoSkipCurrentIrpStackLocation (Irp);
status = IoCallDriver (DeviceData->TopOfStack, Irp);
Game_DecIoCount (DeviceData);
return status;
case IRP_MN_REMOVE_DEVICE:
Game_KdPrint (DeviceData, GAME_DBG_PNP_TRACE, ("Remove Device\n"));
//
// We should assert this because Game_IncIoCount will not succeed if a
// remove has already been sent down.
//
ASSERT(!DeviceData->Removed);
ASSERT(!DeviceData->Acquired);
//
// The PlugPlay system has detected the removal of this device. We
// have no choise but to detach and delete the device objecct.
// (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.
// ASSERT (!DeviceData->Removed);
//
// We will accept no new requests
//
DeviceData->Removed = TRUE;
//
// Complete any outstanding IRPs queued by the driver here.
//
// Perform (surpise) remove code
Game_RemoveFdo(DeviceData);
//
// Here if we had any outstanding requests in a personal queue we should
// complete them all now.
//
// Note, the device is guarenteed stopped, so we cannot send it any non-
// PNP IRPS.
//
//
// Fire and forget
//
Irp->IoStatus.Status = STATUS_SUCCESS;
IoSkipCurrentIrpStackLocation (Irp);
status = IoCallDriver (DeviceData->TopOfStack, Irp);
//
// Wait for all outstanding requests to complete
//
i = InterlockedDecrement (&DeviceData->OutstandingIO);
ASSERT (0 < i);
if (0 != InterlockedDecrement (&DeviceData->OutstandingIO)) {
NTSTATUS waitStatus;
Game_KdPrint (DeviceData, GAME_DBG_PNP_INFO,
("Remove Device waiting for request to complete\n"));
waitStatus = KeWaitForSingleObject (&DeviceData->RemoveEvent,
Executive,
KernelMode,
FALSE, // Not Alertable
NULL); // No timeout
ASSERT (waitStatus == STATUS_SUCCESS);
}
//
// Free the associated resources
//
//
// Detatch from the undelying devices.
//
Game_KdPrint(DeviceData, GAME_DBG_PNP_INFO,
("IoDetachDevice: 0x%x\n", DeviceData->TopOfStack));
IoDetachDevice (DeviceData->TopOfStack);
Game_KdPrint(DeviceData, GAME_DBG_PNP_INFO,
("IoDeleteDevice1: 0x%x\n", DeviceObject));
ExAcquireFastMutex (&DeviceData->Mutex);
for (entry = DeviceData->PDOs.Flink;
entry != &DeviceData->PDOs;
) {
pdoData = CONTAINING_RECORD (entry, PDO_DEVICE_DATA, Link);
ASSERT (pdoData->Removed);
ASSERT (pdoData->Attached);
//
// We set this to false so that Game_RemovePdo will delete the DO
// and free any of the allocated memory associated with the PDO.
//
pdoData->Attached = FALSE;
//
// Go to the next link in the list. Once the pdo is deleted, entry
// is no longer a valid pointer.
//
entry = entry->Flink;
//
// Once Game_RemovePdo is called, pdoData and the pdo itself cannot
// be touched becuase they will have been deleted. RemoveEntryList
// does not modify the value Link->Flink, so the state after this
// one is safe
//
RemoveEntryList (&pdoData->Link);
Game_RemovePdo (pdoData->Self, pdoData);
DeviceData->NumPDOs--;
}
ASSERT(DeviceData->NumPDOs == 0);
ExReleaseFastMutex (&DeviceData->Mutex);
IoDeleteDevice (DeviceObject);
return status;
case IRP_MN_QUERY_DEVICE_RELATIONS:
if (BusRelations != IrpStack->Parameters.QueryDeviceRelations.Type) {
//
// We don't support this
//
goto GAME_FDO_PNP_DEFAULT;
}
Game_KdPrint (DeviceData, GAME_DBG_PNP_TRACE, ("Query Relations "));
//
// Tell the plug and play system about all the PDOs.
//
// There might also be device relations below and above this FDO,
// so, be sure to propagate the relations from the upper drivers.
//
// No Completion routine is needed so long as the status is preset
// to success. (PDOs complete plug and play irps with the current
// IoStatus.Status and IoStatus.Information as the default.)
//
ExAcquireFastMutex (&DeviceData->Mutex);
oldRelations = (PDEVICE_RELATIONS) Irp->IoStatus.Information;
if (oldRelations) {
i = oldRelations->Count;
if (!DeviceData->NumPDOs) {
//
// There is a device relations struct already present and we have
// nothing to add to it, so just call IoSkip and IoCall
//
ExReleaseFastMutex (&DeviceData->Mutex);
goto GAME_FDO_PNP_DEFAULT;
}
}
else {
i = 0;
}
// The current number of PDOs
Game_KdPrint_Cont (DeviceData, GAME_DBG_PNP_TRACE,
("#PDOS = %d + %d\n", i, DeviceData->NumPDOs));
//
// Need to allocate a new relations structure and add our
// PDOs to it.
//
length = sizeof(DEVICE_RELATIONS) +
((DeviceData->NumPDOs + i) * sizeof (PDEVICE_OBJECT));
relations = (PDEVICE_RELATIONS) ExAllocatePool (PagedPool, length);
if (NULL == relations) {
ExReleaseFastMutex (&DeviceData->Mutex);
status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
//
// Copy in the device objects so far
//
if (i) {
RtlCopyMemory (
relations->Objects,
oldRelations->Objects,
i * sizeof (PDEVICE_OBJECT));
}
relations->Count = DeviceData->NumPDOs + i;
//
// For each PDO on this bus add a pointer to the device relations
// buffer, being sure to take out a reference to that object.
// The PlugPlay system will dereference the object when it is done with
// it and free the device relations buffer.
//
for (entry = DeviceData->PDOs.Flink;
entry != &DeviceData->PDOs;
entry = entry->Flink, i++) {
pdoData = CONTAINING_RECORD (entry, PDO_DEVICE_DATA, Link);
ASSERT (pdoData->Attached);
relations->Objects[i] = pdoData->Self;
ObReferenceObject (pdoData->Self);
}
//
// Replace the relations structure in the IRP with the new
// one.
//
if (oldRelations) {
ExFreePool (oldRelations);
}
Irp->IoStatus.Information = (ULONG_PTR) relations;
ExReleaseFastMutex (&DeviceData->Mutex);
//
// Set up and pass the IRP further down the stack
//
Irp->IoStatus.Status = STATUS_SUCCESS;
IoSkipCurrentIrpStackLocation (Irp);
status = IoCallDriver (DeviceData->TopOfStack, Irp);
Game_DecIoCount (DeviceData);
return status;
case IRP_MN_CANCEL_REMOVE_DEVICE:
case IRP_MN_CANCEL_STOP_DEVICE:
case IRP_MN_QUERY_REMOVE_DEVICE:
//
// For query remove, if we were to fail this call then we would need to
// complete the IRP here. Since we are not, set the status to SUCCESS
// and call the next driver.
//
// For the cancel(s), we must set the status to notify the PnP subsystem
// that the irp was correctly handled
//
Irp->IoStatus.Status = STATUS_SUCCESS;
IoSkipCurrentIrpStackLocation (Irp);
status = IoCallDriver (DeviceData->TopOfStack, Irp);
Game_DecIoCount (DeviceData);
return status;
GAME_FDO_PNP_DEFAULT:
default:
//
// In the default case we merely call the next driver since
// we don't know what to do.
//
//
// Fire and Forget
//
IoSkipCurrentIrpStackLocation (Irp);
//
// Done, do NOT complete the IRP, it will be processed by the lower
// device object, which will complete the IRP
//
status = IoCallDriver (DeviceData->TopOfStack, Irp);
Game_DecIoCount (DeviceData);
return status;
}
Irp->IoStatus.Status = status;
IoCompleteRequest (Irp, IO_NO_INCREMENT);
Game_DecIoCount (DeviceData);
return status;
}
UCHAR
Game_ReadPortUchar (
IN UCHAR * x
)
{
return READ_PORT_UCHAR (x);
}
VOID
Game_WritePortUchar (
IN UCHAR * x,
IN UCHAR y
)
{
WRITE_PORT_UCHAR (x,y);
}
UCHAR
Game_ReadRegisterUchar (
IN UCHAR * x
)
{
return READ_REGISTER_UCHAR (x);
}
VOID
Game_WriteRegisterUchar (
IN UCHAR * x,
IN UCHAR y
)
{
WRITE_REGISTER_UCHAR (x,y);
}
NTSTATUS
Game_StartFdo (
IN PFDO_DEVICE_DATA FdoData,
IN PCM_PARTIAL_RESOURCE_LIST PartialResourceList,
IN PCM_PARTIAL_RESOURCE_LIST PartialResourceListTranslated
)
/*++
Routine Description:
Parses the resource lists to see what type of accessors to use.
Arguments:
DeviceObject - Pointer to the device object.
PartialResourceList - untranslated resources
PartialResourceListTranslated - translated resources
Return Value:
Status is returned.
--*/
{
ULONG i;
NTSTATUS status = STATUS_SUCCESS;
PCM_PARTIAL_RESOURCE_DESCRIPTOR resource;
PCM_PARTIAL_RESOURCE_DESCRIPTOR resourceTrans;
Game_KdPrint (FdoData, GAME_DBG_PNP_TRACE, ("StartFdo\n"));
for (i = 0,
resource = &PartialResourceList->PartialDescriptors[0],
resourceTrans = &PartialResourceListTranslated->PartialDescriptors[0];
i < PartialResourceList->Count && NT_SUCCESS(status);
i++, resource++, resourceTrans++) {
switch (resource->Type) {
case CmResourceTypePort:
#if _X86_
FdoData->ReadPort = READ_PORT_UCHAR;
FdoData->WritePort = WRITE_PORT_UCHAR;
#else
FdoData->ReadPort = Game_ReadPortUchar;
FdoData->WritePort = Game_WritePortUchar;
#endif
FdoData->PhysicalAddress = resource->u.Port.Start;
Game_KdPrint (FdoData, GAME_DBG_PNP_INFO,
("HardwareResource: Port (%x) -> ",
FdoData->PhysicalAddress.LowPart));
switch (resourceTrans->Type) {
case CmResourceTypePort:
// Nothing to do here but note the address;
//@@BEGIN_DDKSPLIT
// On Win9x, VJoyD.VxD handles the resources for gameports.
// It only uses ports and it assumes that the first range is
// always the gameport. It uses a second range of a devnode
// only if the second range is within the original standard
// range of 200-20f. All other ports are assumed to be audio
// ports on the hosting sound card.
//@@END_DDKSPLIT
// For better compatibility with Win9x, always use only the
// first port range.
if( FdoData->GamePortAddress == 0 ) {
FdoData->GamePortAddress =
(PVOID)(ULONG_PTR) resourceTrans->u.Port.Start.QuadPart;
ASSERT (resourceTrans->u.Port.Length == resource->u.Port.Length);
FdoData->GamePortAddressLength = resourceTrans->u.Port.Length;
Game_KdPrint_Cont (FdoData, GAME_DBG_PNP_INFO,
("Port: (%x)\n", FdoData->GamePortAddress));
} else {
Game_KdPrint_Cont (FdoData, GAME_DBG_PNP_INFO,
("Ignoring additional port: (%x)\n", FdoData->GamePortAddress));
}
break;
case CmResourceTypeMemory:
//
// We need to map the memory
//
FdoData->GamePortAddress =
MmMapIoSpace (resourceTrans->u.Memory.Start,
resourceTrans->u.Memory.Length,
MmNonCached);
ASSERT (resourceTrans->u.Port.Length == resource->u.Port.Length);
FdoData->GamePortAddressLength = resourceTrans->u.Memory.Length;
FdoData->MappedPorts = TRUE;
Game_KdPrint_Cont (FdoData, GAME_DBG_PNP_INFO,
("Mem: (%x)\n", FdoData->GamePortAddress));
break;
default:
Game_KdPrint_Cont (FdoData, GAME_DBG_PNP_INFO,
("Unknown \n", FdoData->GamePortAddress));
TRAP ();
}
break;
case CmResourceTypeMemory:
ASSERT (CmResourceTypeMemory == resourceTrans->Type);
#if _X86_
FdoData->ReadPort = READ_REGISTER_UCHAR;
FdoData->WritePort = WRITE_REGISTER_UCHAR;
#else
FdoData->ReadPort = Game_ReadRegisterUchar;
FdoData->WritePort = Game_WriteRegisterUchar;
#endif
FdoData->PhysicalAddress = resource->u.Memory.Start;
FdoData->GamePortAddress =
MmMapIoSpace (resourceTrans->u.Memory.Start,
resourceTrans->u.Memory.Length,
MmNonCached);
FdoData->MappedPorts = TRUE;
Game_KdPrint (FdoData, GAME_DBG_PNP_INFO,
("HardwareResource: Memory (%x) -> Mem (%x)",
FdoData->PhysicalAddress.LowPart,
FdoData->GamePortAddress));
break;
case CmResourceTypeInterrupt:
default:
// Hun? Allow this to succeed...perhaps whomever enumerated the PDO
// below us needs this resource for the game port
Game_KdPrint (FdoData, GAME_DBG_PNP_ERROR,
("Unhandled resource type (0x%x)\n",
resource->Type));
// status = STATUS_UNSUCCESSFUL;
}
}
return status;
}
void
Game_RemoveFdo (
IN PFDO_DEVICE_DATA FdoData
)
/*++
Routine Description:
Frees any memory allocated by the FDO and unmaps any IO mapped as well.
--*/
{
PAGED_CODE ();
if (FdoData->SurpriseRemoved) {
return;
}
//
// We set this b/c if we get called twice, that means a surprise removal
// called this function first
//
FdoData->SurpriseRemoved = TRUE;
//
// Clean up any resources here
//
if (FdoData->Started) {
FdoData->Started = FALSE;
//
// Free resources given by start device.
//
if (FdoData->MappedPorts) {
MmUnmapIoSpace (FdoData->GamePortAddress, 1);
// Here we are assuming that joysticks only use on port.
// This is the way it has always been, and might always
// continue to be. This assumption is everywhere in this stack.
}
IoSetDeviceInterfaceState (&FdoData->DevClassAssocName, FALSE);
}
//
// Make the DI go away. Some drivers may choose to remove the DCA
// when they receive a stop or even a query stop. We just don't care.
//
if (FdoData->DevClassAssocName.Buffer != NULL) {
ExFreePool (FdoData->DevClassAssocName.Buffer);
RtlZeroMemory (&FdoData->DevClassAssocName,
sizeof (UNICODE_STRING));
}
}
NTSTATUS
Game_SendIrpSynchronously (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN BOOLEAN NotImplementedIsValid,
IN BOOLEAN CopyToNext
)
{
KEVENT event;
NTSTATUS status;
PAGED_CODE();
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
if (CopyToNext) {
IoCopyCurrentIrpStackLocationToNext(Irp);
}
IoSetCompletionRoutine(Irp,
Game_CompletionRoutine,
&event,
TRUE,
TRUE,
TRUE
);
status = IoCallDriver(DeviceObject, Irp);
//
// Wait for lower drivers to be done with the Irp
//
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event,
Executive,
KernelMode,
FALSE,
NULL
);
status = Irp->IoStatus.Status;
}
if (NotImplementedIsValid && (status == STATUS_NOT_IMPLEMENTED ||
status == STATUS_INVALID_DEVICE_REQUEST)) {
status = STATUS_SUCCESS;
}
return status;
}
NTSTATUS
Game_CompletionRoutine (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
A completion routine for use when calling the lower device objects to
which our bus (FDO) is attached.
--*/
{
UNREFERENCED_PARAMETER (DeviceObject);
UNREFERENCED_PARAMETER (Irp);
// if (Irp->PendingReturned) {
// IoMarkIrpPending( Irp );
// }
KeSetEvent ((PKEVENT) Context, 1, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED; // Keep this IRP
}
NTSTATUS
Game_PDO_PnP (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpStack,
IN PPDO_DEVICE_DATA DeviceData
)
/*++
Routine Description:
Handle requests from the PlugPlay system for the devices on the BUS
--*/
{
PDEVICE_CAPABILITIES deviceCapabilities;
ULONG information;
PWCHAR buffer, buffer2;
ULONG length, length2, i, j;
NTSTATUS status;
PAGED_CODE ();
status = Irp->IoStatus.Status;
//
// NB: since we are a bus enumerator, we have no one to whom we could
// defer these irps. Therefore we do not pass them down but merely
// return them.
//
switch (IrpStack->MinorFunction) {
case IRP_MN_QUERY_CAPABILITIES:
Game_KdPrint (DeviceData, GAME_DBG_PNP_TRACE, ("Query Caps \n"));
//
// Get the packet.
//
deviceCapabilities=IrpStack->Parameters.DeviceCapabilities.Capabilities;
//
// Set the capabilities.
//
deviceCapabilities->Version = 1;
deviceCapabilities->Size = sizeof (DEVICE_CAPABILITIES);
// We cannot wake the system.
deviceCapabilities->SystemWake = PowerSystemUnspecified;
deviceCapabilities->DeviceWake = PowerDeviceUnspecified;
// We have no latencies
deviceCapabilities->D1Latency = 0;
deviceCapabilities->D2Latency = 0;
deviceCapabilities->D3Latency = 0;
// No locking or ejection
deviceCapabilities->LockSupported = FALSE;
deviceCapabilities->EjectSupported = FALSE;
// Device can be physically removed.
// Technically there is no physical device to remove, but this bus
// driver can yank the PDO from the PlugPlay system, when ever it
// receives an IOCTL_GAMEENUM_REMOVE_PORT device control command.
deviceCapabilities->Removable = FALSE;
deviceCapabilities->SurpriseRemovalOK = TRUE;
// not Docking device
deviceCapabilities->DockDevice = FALSE;
deviceCapabilities->UniqueID = FALSE;
status = STATUS_SUCCESS;
break;
case IRP_MN_QUERY_ID:
// Query the IDs of the device
Game_KdPrint (DeviceData, GAME_DBG_PNP_TRACE,
("QueryID: 0x%x\n", IrpStack->Parameters.QueryId.IdType));
//
// If the query requires having a hardware ID, check we have one
//
#if DBG
if (( IrpStack->Parameters.QueryId.IdType == BusQueryDeviceID )
|| ( IrpStack->Parameters.QueryId.IdType == BusQueryHardwareIDs )
|| ( IrpStack->Parameters.QueryId.IdType == BusQueryInstanceID )) {
if (DeviceData->HardwareIDs) {
ULONG tmplength = 1024; // No reason to be as long as this
ASSERT( NT_SUCCESS( Game_CheckHardwareIDs (DeviceData->HardwareIDs,
&tmplength, FDO_FROM_PDO (DeviceData) ) ) );
} else {
ASSERT( !"No hardware ID for QueryId" );
}
}
#endif
switch (IrpStack->Parameters.QueryId.IdType) {
case BusQueryDeviceID:
// this can be the same as the hardware ids (which requires a multi
// sz) ... we are just allocating more than enough memory
case BusQueryHardwareIDs:
// return a multi WCHAR (null terminated) string (null terminated)
// array for use in matching hardare ids in inf files;
//
buffer = DeviceData->HardwareIDs;
while (*(buffer++)) {
while (*(buffer++)) {
;
}
}
length = (ULONG)(buffer - DeviceData->HardwareIDs) * sizeof (WCHAR);
buffer = ExAllocatePool (PagedPool, length);
if (buffer) {
RtlCopyMemory (buffer, DeviceData->HardwareIDs, length);
status = STATUS_SUCCESS;
}
else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
Irp->IoStatus.Information = (ULONG_PTR) buffer;
break;
case BusQueryInstanceID:
//
// Take the first hardware id and append an underscore and number
// to it
// total length =
// length of hw id + underscore + number (11 digits to be safe) +
// null
//
buffer = buffer2 = DeviceData->HardwareIDs;
while (*(buffer++)) {
while (*(buffer++)) {
;
}
}
while ('\\' != *(buffer2++)) {
;
}
length = (ULONG)(buffer - buffer2) * sizeof (WCHAR);
length += 1 + 11 + 1;
buffer = ExAllocatePool (PagedPool, length);
if (buffer) {
swprintf(buffer, L"%ws_%02d", buffer2, DeviceData->UniqueID);
Game_KdPrint (DeviceData, GAME_DBG_PNP_INFO,
("UniqueID: %ws\n", buffer));
status = STATUS_SUCCESS;
}
else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
Irp->IoStatus.Information = (ULONG_PTR) buffer;
break;
case BusQueryCompatibleIDs:
// The generic ids for installation of this pdo.
if (DeviceData->AnalogCompatible) {
// Only applicable for analog devices
length = GAMEENUM_COMPATIBLE_IDS_LENGTH * sizeof (WCHAR);
buffer = ExAllocatePool (PagedPool, length);
if (buffer) {
RtlCopyMemory (buffer, GAMEENUM_COMPATIBLE_IDS, length);
status = STATUS_SUCCESS;
}
else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
Irp->IoStatus.Information = (ULONG_PTR) buffer;
}
else {
// For incompatible devices report an empty list
buffer = ExAllocatePool (PagedPool, sizeof(L"\0"));
if (buffer) {
*(ULONG *)buffer = 0; // double unicode-NULL.
status = STATUS_SUCCESS;
} else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
Irp->IoStatus.Information = (ULONG_PTR) buffer;
}
break;
}
break;
case IRP_MN_START_DEVICE:
Game_KdPrint (DeviceData, GAME_DBG_PNP_TRACE, ("Start Device \n"));
// Here we do what ever initialization and ``turning on'' that is
// required to allow others to access this device.
DeviceData->Started = TRUE;
DeviceData->Removed = FALSE;
status = STATUS_SUCCESS;
break;
case IRP_MN_STOP_DEVICE:
Game_KdPrint (DeviceData, GAME_DBG_PNP_TRACE, ("Stop Device \n"));
// Here we shut down the device. The opposite of start.
DeviceData->Started = FALSE;
status = STATUS_SUCCESS;
break;
case IRP_MN_SURPRISE_REMOVAL:
// just mark that it happened, cleaning up the device extension will
// occur later
ASSERT(!(FDO_FROM_PDO (DeviceData))->Acquired);
DeviceData->SurpriseRemoved = TRUE;
status = STATUS_SUCCESS;
break;
case IRP_MN_REMOVE_DEVICE:
Game_KdPrint (DeviceData, GAME_DBG_PNP_TRACE, ("Remove Device \n"));
ASSERT(!(FDO_FROM_PDO (DeviceData))->Acquired);
//
// The remove IRP code for a PDO uses the following steps:
//
// · Complete any requests queued in the driver
// · If the device is still attached to the system,
// then complete the request and return.
// · Otherwise, cleanup device specific allocations, memory, events...
// · Call IoDeleteDevice
// · Return from the dispatch routine.
//
status = Game_RemovePdo(DeviceObject, DeviceData);
break;
case IRP_MN_QUERY_STOP_DEVICE:
Game_KdPrint (DeviceData, GAME_DBG_PNP_TRACE, ("Q Stop Device \n"));
// No reason here why we can't stop the device.
// If there were a reason we should speak now for answering success
// here may result in a stop device irp.
status = STATUS_SUCCESS;
break;
case IRP_MN_CANCEL_STOP_DEVICE:
Game_KdPrint (DeviceData, GAME_DBG_PNP_TRACE, ("Cancel Stop Device \n"));
//
// The stop was canceled. Whatever state we set, or resources we put
// on hold in anticipation of the forcoming STOP device IRP should be
// put back to normal. Someone, in the long list of concerned parties,
// has failed the stop device query.
//
status = STATUS_SUCCESS;
break;
case IRP_MN_QUERY_REMOVE_DEVICE:
Game_KdPrint (DeviceData, GAME_DBG_PNP_TRACE, ("Q Remove Device \n"));
//
// Just like Query Stop only now the impending doom is the remove irp
//
status = STATUS_SUCCESS;
break;
case IRP_MN_CANCEL_REMOVE_DEVICE:
Game_KdPrint (DeviceData, GAME_DBG_PNP_TRACE, ("Can Remove Device \n"));
//
// Clean up a remove that did not go through, just like cancel STOP.
//
status = STATUS_SUCCESS;
break;
case IRP_MN_QUERY_DEVICE_RELATIONS:
if (TargetDeviceRelation ==
IrpStack->Parameters.QueryDeviceRelations.Type) {
PDEVICE_RELATIONS deviceRelations;
deviceRelations = (PDEVICE_RELATIONS) Irp->IoStatus.Information;
if (!deviceRelations) {
deviceRelations = (PDEVICE_RELATIONS)
ExAllocatePool(PagedPool, sizeof(DEVICE_RELATIONS));
if (!deviceRelations) {
status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
}
else if (deviceRelations->Count != 0) {
//
// Nobody but the PDO should be setting this value!
//
ASSERT(deviceRelations->Count == 0);
//
// Deref any objects that were previously in the list
//
for (i = 0; i < deviceRelations->Count; i++) {
ObDereferenceObject(deviceRelations->Objects[i]);
deviceRelations->Objects[i] = NULL;
}
}
deviceRelations->Count = 1;
deviceRelations->Objects[0] = DeviceData->Self;
ObReferenceObject(DeviceData->Self);
status = STATUS_SUCCESS;
Irp->IoStatus.Information = (ULONG_PTR) deviceRelations;
break;
}
// fall through
case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
case IRP_MN_READ_CONFIG:
case IRP_MN_WRITE_CONFIG: // we have no config space
case IRP_MN_EJECT:
case IRP_MN_SET_LOCK:
case IRP_MN_QUERY_INTERFACE: // We do not have any non IRP based interfaces.
default:
Game_KdPrint (DeviceData, GAME_DBG_PNP_TRACE,
("PNP Not handled 0x%x\n", IrpStack->MinorFunction));
// this is a leaf node
// status = STATUS_NOT_IMPLEMENTED
// For PnP requests to the PDO that we do not understand we should
// return the IRP WITHOUT setting the status or information fields.
// They may have already been set by a filter (eg acpi).
break;
}
Irp->IoStatus.Status = status;
IoCompleteRequest (Irp, IO_NO_INCREMENT);
return status;
}
NTSTATUS
Game_RemovePdo (
PDEVICE_OBJECT Device,
PPDO_DEVICE_DATA PdoData
)
/*++
Routine Description:
The PlugPlay subsystem has instructed that this PDO should be removed.
We should therefore
· Complete any requests queued in the driver
· If the device is still attached to the system,
then complete the request and return.
· Otherwise, cleanup device specific allocations, memory, events...
· Call IoDeleteDevice
· Return from the dispatch routine.
Note that if the device is still connected to the bus (IE in this case
the control panel has not yet told us that the game device has disappeared)
then the PDO must remain around, and must be returned during any
query Device relaions IRPS.
--*/
{
PAGED_CODE ();
PdoData->Removed = TRUE;
//
// Complete any outsanding requests with STATUS_DELETE_PENDING.
//
// Game enum does not queue any irps at this time so we have nothing to do.
//
// Attached is set to true when the pdo is exposed via one of the IOCTLs.
// It is set to FALSE when a remove IOCTL is received. This means that we
// can get a remove on a device that still exists, so we don't delete it.
//
if (PdoData->Attached) {
return STATUS_SUCCESS;
}
//
// Free any resources.
//
if (PdoData->HardwareIDs) {
ExFreePool (PdoData->HardwareIDs);
PdoData->HardwareIDs = NULL;
}
Game_KdPrint(PdoData, GAME_DBG_PNP_INFO,
("IoDeleteDevice2: 0x%x\n", Device));
IoDeleteDevice (Device);
return STATUS_SUCCESS;
}
VOID
Game_InitializePdo (
PDEVICE_OBJECT Pdo,
PFDO_DEVICE_DATA FdoData
)
/*++
Routine Description:
Set the PDO into a known good starting state
--*/
{
PPDO_DEVICE_DATA pdoData;
PAGED_CODE ();
pdoData = (PPDO_DEVICE_DATA) Pdo->DeviceExtension;
Game_KdPrint(pdoData, GAME_DBG_SS_NOISE,
("pdo 0x%x, extension 0x%x\n", Pdo, pdoData));
//
// Initialize the rest
//
pdoData->IsFDO = FALSE;
pdoData->Self = Pdo;
#if DBG
pdoData->DebugLevel = GameEnumDebugLevel;
#endif
pdoData->ParrentFdo = FdoData->Self;
pdoData->Started = FALSE; // irp_mn_start has yet to be received
pdoData->Attached = TRUE; // attached to the bus
pdoData->Removed = FALSE; // no irp_mn_remove as of yet
pdoData->UniqueID = InterlockedIncrement(&FdoData->UniqueIDCount);
Pdo->Flags &= ~DO_DEVICE_INITIALIZING;
Pdo->Flags |= DO_POWER_PAGABLE;
ExAcquireFastMutex (&FdoData->Mutex);
InsertTailList(&FdoData->PDOs, &pdoData->Link);
FdoData->NumPDOs++;
ExReleaseFastMutex (&FdoData->Mutex);
}
NTSTATUS
Game_CheckHardwareIDs (
PWCHAR pwszOrgId,
PULONG puLenLimit,
PFDO_DEVICE_DATA FdoData
)
/*++
Routine Description:
Check that the hardware ID we've been given is matches format "Gameport\XXX" where XXX must
be between 0x20 and 0x7f inclusive but not be a ',' or '\'. We also have to make sure that we
do not overrun our buffer length. The length of the total buffer must be less than MAX_DEVICE_ID_LEN
and each individual entry must be less than 64 characters
--*/
{
PWCHAR pwszId;
ULONG total_length=0;
UCHAR ucEntries = 0;
#if DBG
PWCHAR pwszLastId;
#else
UNREFERENCED_PARAMETER (FdoData);
#endif
PAGED_CODE ();
Game_KdPrint (FdoData, GAME_DBG_PNP_TRACE, ("Game_CheckHardwareIDs - given ID string %.64lS length %d \n",pwszOrgId,*puLenLimit));
pwszId = pwszOrgId;
//
// Trivial rejection first - null string
if (*pwszId == UNICODE_NULL)
{
Game_KdPrint (FdoData, GAME_DBG_PNP_ERROR,("hardware ID invalid - buffer NULL\n"));
return STATUS_INVALID_PARAMETER;
}
//
// Loop through at most 2 hardware IDs until the NULL terminator or end of buffer
//
while (*pwszId != UNICODE_NULL && total_length<=*puLenLimit)
{
PWCHAR pwszTemplate = HWID_TEMPLATE;
ULONG length=0;
#if DBG
//
// Keep track of the beginning of each ID for debug messages
//
pwszLastId = pwszId;
#endif
//
// Limit us to 2 entries
//
if (++ucEntries>2)
break;
//
// Length remaining must be long enough for an completion entry
// Which is template + 4 characters (slash,char,null,null)
//
if (HWID_TEMPLATE_LENGTH + 4 > (*puLenLimit)-total_length)
{
Game_KdPrint (FdoData, GAME_DBG_PNP_ERROR,
("hardware ID \"%.64lS\" invalid - entry too short\n",pwszLastId));
return STATUS_INVALID_PARAMETER;
}
//
// Hardware ID must start with HWID_TEMPLATE
//
while (++length <= HWID_TEMPLATE_LENGTH)
{
if (LOWERCASE(*(pwszId++)) != *(pwszTemplate++))
{
Game_KdPrint (FdoData, GAME_DBG_PNP_ERROR,
("hardware ID \"%.64lS\" invalid - does not match template\n",pwszLastId));
return STATUS_INVALID_PARAMETER;
}
}
//
// Must have a separator
//
if ((*(pwszId++) != OBJ_NAME_PATH_SEPARATOR))
{
Game_KdPrint (FdoData, GAME_DBG_PNP_ERROR,
("hardware ID \"%.64lS\" invalid - no separator\n",pwszLastId));
return STATUS_INVALID_PARAMETER;
}
//
// We have a successful match of HWID_TEMPLATE_LENGTH + 1 characters
// Now our Id string check - check for NULL case first
//
if (*pwszId == UNICODE_NULL)
{
Game_KdPrint (FdoData, GAME_DBG_PNP_ERROR,
("hardware ID \"%.64lS\" invalid format\n",pwszLastId));
return STATUS_INVALID_PARAMETER;
}
//
// Otherwise we loop until we overrun or hit NULL
while ((++length + total_length < *puLenLimit) && (*pwszId != UNICODE_NULL))
{
if ((*pwszId == OBJ_NAME_PATH_SEPARATOR) ||
(*pwszId < 0x20) ||
(*pwszId > 0x7f) ||
(*pwszId == L','))
{
Game_KdPrint (FdoData, GAME_DBG_PNP_ERROR,
("hardware ID \"%.64lS\" invalid - bad character at length=%d\n",pwszLastId,length));
return STATUS_INVALID_PARAMETER;
}
if (length > 64)
{
Game_KdPrint (FdoData, GAME_DBG_PNP_ERROR,
("hardware ID \"%.64lS\" invalid - ID %d too long at length=%d\n",pwszLastId,ucEntries,length));
return STATUS_INVALID_PARAMETER;
}
pwszId++;
}
//
// We need to increment to either the second NULL or next string
// If we had a null we test for either another entry or final NULL
// in the while loop
// If we ran too far we will pick it up in the while loop test and break
// out of the loop.
//
total_length += length;
pwszId++;
}
//
// If we have run off the end of the buffer return an error
//
if (total_length > *puLenLimit)
{
Game_KdPrint (FdoData, GAME_DBG_PNP_ERROR,
("hardware ID \"%.64lS\" invalid - length > buffer limit\n",pwszLastId));
return STATUS_INVALID_PARAMETER;
}
//
// Copy the actual (maybe truncated) length back to the caller
//
*puLenLimit = ++total_length;
Game_KdPrint (FdoData, GAME_DBG_PNP_TRACE, ("Game_CheckHardwareIDs - succeeded. Final ID string \"%.64lS\" length %d \n",pwszOrgId,*puLenLimit));
return STATUS_SUCCESS;
}
NTSTATUS
Game_Expose (
PGAMEENUM_EXPOSE_HARDWARE Expose,
ULONG ExposeSize,
PFDO_DEVICE_DATA FdoData
)
/*++
Routine Description:
This driver has just detected a new device on the bus. (Actually the
control panels has just told us that something has arived, but who is
counting?)
We therefore need to create a new PDO, initialize it, add it to the list
of PDOs for this FDO bus, and then tell Plug and Play that all of this
happened so that it will start sending prodding IRPs.
--*/
{
PDEVICE_OBJECT pdo, firstPdo = NULL;
PLIST_ENTRY entry;
PPDO_DEVICE_DATA pdoData;
NTSTATUS status;
ULONG length;
KIRQL irql;
BOOLEAN first = TRUE;
UCHAR i;
PAGED_CODE ();
if (FdoData->Self != Expose->PortHandle) {
return STATUS_INVALID_PARAMETER;
}
else if (FdoData->NumPDOs != 0) {
//
// Only one valid expose per PDO ... a remove hardware will decrement
// NumPDOs to 0
//
return STATUS_INVALID_DEVICE_REQUEST;
}
else if (Expose->NumberJoysticks > 2 || Expose->NumberJoysticks < 0) {
return STATUS_INVALID_PARAMETER;
}
length = (ExposeSize - sizeof (GAMEENUM_EXPOSE_HARDWARE))/sizeof(WCHAR);
if (length >MAX_DEVICE_ID_LEN) {
Game_KdPrint (FdoData, GAME_DBG_PNP_ERROR,
("Expose failed because length of Hardware ID too long at %d\n",length));
return STATUS_INVALID_PARAMETER;
}
Game_KdPrint (FdoData, GAME_DBG_PNP_INFO,
("Exposing PDO\n"
"======PortHandle: 0x%x\n"
"======NumJoysticks: %d\n"
"======NumAxis: %d\n"
"======NumButtons: %d\n"
"======HardwareId: %ws\n"
"======Length: %d\n",
Expose->PortHandle,
Expose->NumberJoysticks,
Expose->NumberAxis,
Expose->NumberButtons,
Expose->HardwareIDs,
length));
#if DBG
for (i = 0; i < SIZE_GAMEENUM_OEM_DATA; i++) {
Game_KdPrint (FdoData, GAME_DBG_PNP_INFO,
("=====OemData[%d] = 0x%x\n",
i,
Expose->OemData[i]
));
}
#endif
status = Game_CheckHardwareIDs (Expose->HardwareIDs, &length, FdoData);
if (!NT_SUCCESS (status)) {
return status;
}
//
// Create the PDOs
//
length *= sizeof(WCHAR);
Game_KdPrint(FdoData, GAME_DBG_PNP_NOISE,
("GAME: Expose->HardwareHandle = 0x%x\n", FdoData->TopOfStack));
Expose->HardwareHandle = FdoData->TopOfStack;
for (i = 0; i < Expose->NumberJoysticks; i++) {
status = IoCreateDevice(FdoData->Self->DriverObject,
sizeof (PDO_DEVICE_DATA),
NULL,
FILE_DEVICE_BUS_EXTENDER,
0,
FALSE,
&pdo);
if (!NT_SUCCESS (status)) {
pdo = NULL;
goto GameExposeError;
}
ASSERT (pdo != NULL);
if (!firstPdo) {
firstPdo = pdo;
}
pdoData = (PPDO_DEVICE_DATA) pdo->DeviceExtension;
//
// Copy the hardware IDs
//
if (NULL == (pdoData->HardwareIDs = ExAllocatePool(NonPagedPool, length))) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto GameExposeError;
}
RtlCopyMemory (pdoData->HardwareIDs, Expose->HardwareIDs, length);
//
// If there are more than two IDs, the check returns the length for
// the first two. In case there were more than two, zero out the
// last WCHAR of the copy in order to double NULL terminate.
//
pdoData->HardwareIDs[(length/sizeof(WCHAR))-1] = UNICODE_NULL;
if (1 == Expose->NumberJoysticks) {
pdoData->Portion = GameenumWhole;
}
else if (2 == Expose->NumberJoysticks) {
if (first) {
pdoData->Portion = GameenumFirstHalf;
first = FALSE;
}
else {
pdoData->Portion = GameenumSecondHalf;
}
}
pdoData->UnitID = Expose->UnitID;
pdoData->NumberAxis = Expose->NumberAxis;
pdoData->NumberButtons = Expose->NumberButtons;
#ifndef GAMEENUM_FLAG_COMPATIDCTRL
//
// The flags to control the exposing of a compatible ID were not
// implemented in Windows 2000. If the flags are not defined,
// assume this is being built in a Windows 2000 environment. The
// driver will work either way but if analog compatility is assumed,
// unsigned joystick drivers will always be outranked by the signed
// generic driver even if the generic driver does not work.
//
pdoData->AnalogCompatible = TRUE;
#else
pdoData->AnalogCompatible = ( Expose->Flags & ( GAMEENUM_FLAG_COMPATIDCTRL | GAMEENUM_FLAG_NOCOMPATID ) )
!= ( GAMEENUM_FLAG_COMPATIDCTRL | GAMEENUM_FLAG_NOCOMPATID );
#endif
RtlCopyMemory (&pdoData->OemData,
&Expose->OemData,
sizeof(GAMEENUM_OEM_DATA));
Game_InitializePdo (pdo,
FdoData);
}
IoInvalidateDeviceRelations (FdoData->UnderlyingPDO, BusRelations);
GameExposeError:
if (!NT_SUCCESS(status)) {
//
// Clean up the current pdo.
//
if (pdo) {
IoDeleteDevice(pdo);
}
//
// delete the first PDO if it exists. More to do here b/c it was
// actually fully initialized
//
if (!first) {
ASSERT(firstPdo != NULL);
pdoData = (PPDO_DEVICE_DATA) firstPdo->DeviceExtension;
ASSERT (pdoData->Portion == GameenumFirstHalf);
ExFreePool (pdoData->HardwareIDs);
pdoData->HardwareIDs = NULL;
IoDeleteDevice (firstPdo);
}
//
// remove all pdos from our linked list
//
for (entry = FdoData->PDOs.Flink;
entry != &FdoData->PDOs;
entry = entry->Flink) {
pdoData = CONTAINING_RECORD (entry, PDO_DEVICE_DATA, Link);
RemoveEntryList (&pdoData->Link);
}
FdoData->NumPDOs = 0;
FdoData->UniqueIDCount = GAMEENUM_UNIQUEID_START;
Expose->HardwareHandle = NULL;
}
return status;
}
NTSTATUS
Game_ExposeSibling (
PGAMEENUM_EXPOSE_SIBLING ExposeSibling,
PPDO_DEVICE_DATA SiblingPdo
)
/*++
Routine Description:
This driver has just detected a new device on the bus. (Actually the
control panels has just told us that something has arived, but who is
counting?)
We therefore need to create a new PDO, initialize it, add it to the list
of PDOs for this FDO bus, and then tell Plug and Play that all of this
happened so that it will start sending prodding IRPs.
--*/
{
UCHAR i;
PDEVICE_OBJECT pdo;
PPDO_DEVICE_DATA pdoData;
PFDO_DEVICE_DATA fdoData;
ULONG length;
PWCHAR buffer;
NTSTATUS status;
PAGED_CODE ();
fdoData = FDO_FROM_PDO (SiblingPdo);
//
// Check to make sure we have a valid multi sz string before we allocate
// device objects and other assorted items
//
if (ExposeSibling->HardwareIDs) {
//
// We don't know how long the hardware IDs are but the value
// of MAX_DEVICE_ID_LEN is the most allowed.
//
length = MAX_DEVICE_ID_LEN;
status = Game_CheckHardwareIDs (ExposeSibling->HardwareIDs, &length, fdoData);
}
else {
length = 0;
status = STATUS_SUCCESS;
}
Game_KdPrint (SiblingPdo, GAME_DBG_PNP_INFO,
("Exposing Sibling PDO\n"
"======HardwareHandle: 0x%x\n"
"======UnitID: %d\n"
"======Sting Length: %d\n",
ExposeSibling->HardwareHandle,
(ULONG) ExposeSibling->UnitID,
length
));
#if DBG
for (i = 0; i < SIZE_GAMEENUM_OEM_DATA; i++) {
Game_KdPrint (SiblingPdo, GAME_DBG_PNP_INFO,
("=====OemData[%d] = 0x%x\n",
i,
ExposeSibling->OemData[i]
));
}
#endif
if (!NT_SUCCESS (status)) {
return status;
}
status = IoCreateDevice(fdoData->Self->DriverObject,
sizeof (PDO_DEVICE_DATA),
NULL,
FILE_DEVICE_BUS_EXTENDER,
0,
FALSE,
&pdo);
if (!NT_SUCCESS (status)) {
return status;
}
ASSERT (pdo != NULL);
Game_KdPrint (fdoData, GAME_DBG_PNP_NOISE,
("ExposeSibling->HardwareHandle = 0x%x\n", pdo));
ExposeSibling->HardwareHandle = pdo;
pdoData = (PPDO_DEVICE_DATA) pdo->DeviceExtension;
pdoData->UnitID = ExposeSibling->UnitID;
RtlCopyMemory (&pdoData->OemData,
&ExposeSibling->OemData,
sizeof(GAMEENUM_OEM_DATA));
//
// Check to see if the multi sz was supplied
//
if (length) {
//
// Another hardware ID was given ... use it!
//
Game_KdPrint (fdoData, GAME_DBG_PNP_INFO,
("Using IDs from struct\n"));
//
// Length now represents the actual size of memory to copy instead of
// the number of chars in the array
//
length *= sizeof(WCHAR);
buffer = ExposeSibling->HardwareIDs;
}
else {
//
// No hardware ID was given, use the siblings ID
//
Game_KdPrint (fdoData, GAME_DBG_PNP_INFO,
("Using IDs from sibling\n"));
buffer = SiblingPdo->HardwareIDs;
while (*(buffer++)) {
while (*(buffer++)) {
;
}
}
length = (ULONG) (buffer - SiblingPdo->HardwareIDs) * sizeof (WCHAR);
buffer = SiblingPdo->HardwareIDs;
}
pdoData->HardwareIDs = ExAllocatePool(NonPagedPool, length);
if (NULL == pdoData->HardwareIDs) {
IoDeleteDevice (pdo);
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory (pdoData->HardwareIDs, buffer, length);
//
// If there are more than two IDs, the check returns the length for the
// first two. In case there were more than two, zero out the last WCHAR
// of the copy in order to double NULL terminate.
//
pdoData->HardwareIDs[(length/sizeof(WCHAR))-1] = UNICODE_NULL;
pdoData->AnalogCompatible = SiblingPdo->AnalogCompatible;
Game_InitializePdo (pdo,
fdoData);
IoInvalidateDeviceRelations (fdoData->UnderlyingPDO, BusRelations);
return status;
}
NTSTATUS
Game_Remove (
PGAMEENUM_REMOVE_HARDWARE Remove,
PFDO_DEVICE_DATA FdoData
)
{
PAGED_CODE ();
ASSERT (Remove->Size == sizeof(GAMEENUM_REMOVE_HARDWARE));
if (Remove->HardwareHandle != FdoData->TopOfStack) {
Game_KdPrint(FdoData, GAME_DBG_PNP_NOISE,
("GAME: Remove->HardwareHandle = 0x%x, expecting 0x%x\n",
Remove->HardwareHandle, FdoData->TopOfStack));
return STATUS_INVALID_PARAMETER;
}
return Game_RemoveEx (NULL, FdoData);
}
NTSTATUS
Game_RemoveSelf (
PPDO_DEVICE_DATA PdoData
)
{
PAGED_CODE ();
return Game_RemoveEx (PdoData->Self, FDO_FROM_PDO (PdoData) );
}
NTSTATUS
Game_RemoveEx (
PDEVICE_OBJECT RemoveDO,
PFDO_DEVICE_DATA FdoData
)
/*++
Routine Description:
This driver has just detected that a device has departed from the bus.
(Atcually either the control panel has just told us that somehting has
departed or a PDO has removed itself)
We therefore need to flag the PDO as no longer attached, remove it from
the linked list of PDOs for this bus, and then tell Plug and Play about it.
Parameters
RemoveDO - if NULL, then remove all the items in the list, otherwise
it is the PDO to remove from the list
FdoData - contains the list to iterate over
Returns:
STATUS_SUCCESS upon successful removal from the list
STATUS_INVALID_PARAMETER if the removal was unsuccessful
--*/
{
PLIST_ENTRY entry;
PPDO_DEVICE_DATA pdoData;
BOOLEAN found = FALSE, removeAll = (RemoveDO == NULL);
PVOID handle = NULL;
PAGED_CODE ();
ExAcquireFastMutex (&FdoData->Mutex);
if (removeAll) {
Game_KdPrint (FdoData, GAME_DBG_IOCTL_NOISE,
("removing all the pdos!\n"));
}
else {
Game_KdPrint (FdoData, GAME_DBG_IOCTL_NOISE,
("removing 0x%x\n", RemoveDO));
}
if (FdoData->NumPDOs == 0) {
//
// We got a 2nd remove...somebody in user space isn't playing nice!!!
//
Game_KdPrint (FdoData, GAME_DBG_IOCTL_ERROR,
("BAD BAD BAD...2 removes!!! Send only one!\n"));
ExReleaseFastMutex (&FdoData->Mutex);
return STATUS_NO_SUCH_DEVICE;
}
for (entry = FdoData->PDOs.Flink;
entry != &FdoData->PDOs;
entry = entry->Flink) {
pdoData = CONTAINING_RECORD (entry, PDO_DEVICE_DATA, Link);
handle = pdoData->Self;
Game_KdPrint (FdoData, GAME_DBG_IOCTL_NOISE,
("found DO 0x%x\n", handle));
if (removeAll || handle == RemoveDO) {
Game_KdPrint (FdoData, GAME_DBG_IOCTL_INFO,
("removed 0x%x\n", handle));
pdoData->Attached = FALSE;
RemoveEntryList (&pdoData->Link);
FdoData->NumPDOs--;
found = TRUE;
if (!removeAll) {
break;
}
}
}
ExReleaseFastMutex (&FdoData->Mutex);
if (FdoData->NumPDOs == 0) {
FdoData->UniqueIDCount = GAMEENUM_UNIQUEID_START;
}
if (found) {
IoInvalidateDeviceRelations (FdoData->UnderlyingPDO, BusRelations);
return STATUS_SUCCESS;
}
Game_KdPrint (FdoData, GAME_DBG_IOCTL_ERROR,
("0x%x was not removed (not in list)\n", RemoveDO));
return STATUS_INVALID_PARAMETER;
}
NTSTATUS
Game_ListPorts (
PGAMEENUM_PORT_DESC Desc,
PFDO_DEVICE_DATA FdoData
)
/*++
Routine Description:
This driver has just detected that a device has departed from the bus.
(Actually the control panels has just told us that something has departed,
but who is counting?
We therefore need to flag the PDO as no longer attached, remove it from
the linked list of PDOs for this bus, and then tell Plug and Play about it.
--*/
{
PAGED_CODE ();
Desc->PortHandle = FdoData->Self;
Desc->PortAddress = FdoData->PhysicalAddress;
return STATUS_SUCCESS;
}
NTSTATUS
Game_Power (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
We do nothing special for power;
--*/
{
PIO_STACK_LOCATION irpStack;
NTSTATUS status;
PCOMMON_DEVICE_DATA commonData;
PAGED_CODE ();
status = STATUS_SUCCESS;
irpStack = IoGetCurrentIrpStackLocation (Irp);
ASSERT (IRP_MJ_POWER == irpStack->MajorFunction);
commonData = (PCOMMON_DEVICE_DATA) DeviceObject->DeviceExtension;
if (commonData->IsFDO) {
status = Game_FDO_Power ((PFDO_DEVICE_DATA) DeviceObject->DeviceExtension,
Irp);
} else {
status = Game_PDO_Power ((PPDO_DEVICE_DATA) DeviceObject->DeviceExtension,
Irp);
}
return status;
}
NTSTATUS
Game_PowerComplete (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
VOID
Game_FdoPowerTransitionPoRequestComplete (
IN PDEVICE_OBJECT DeviceObject,
IN UCHAR MinorFunction,
IN POWER_STATE DevicePowerState,
IN PIRP SystemStateIrp,
IN PIO_STATUS_BLOCK IoStatus
)
{
PIO_STACK_LOCATION stack;
PFDO_DEVICE_DATA fdoData;
UNREFERENCED_PARAMETER (MinorFunction);
UNREFERENCED_PARAMETER (IoStatus);
fdoData = (PFDO_DEVICE_DATA) DeviceObject->DeviceExtension;
stack = IoGetCurrentIrpStackLocation (SystemStateIrp);
if (DevicePowerState.DeviceState == PowerDeviceD0) {
//
// We are powering up (the D0 Irp just completed). Since we sent the
// S irp down the stack and requested the D irp on the way back up the
// stack, just complete the S irp now
//
PoSetPowerState (DeviceObject,
stack->Parameters.Power.Type,
stack->Parameters.Power.State);
fdoData->SystemState = stack->Parameters.Power.State.SystemState;
SystemStateIrp->IoStatus.Status = IoStatus->Status;
PoStartNextPowerIrp (SystemStateIrp);
IoCompleteRequest (SystemStateIrp, IO_NO_INCREMENT);
//
// From Game_FDO_Power when we originally received the IRP
//
Game_DecIoCount (fdoData);
}
else {
//
// We are powering down (the D3 Irp just completed). Since we requested
// the D irp before sending the S irp down the stack, we must send it
// down now. We will catch the S irp on the way back up to record the
// S state
//
ASSERT (DevicePowerState.DeviceState == PowerDeviceD3);
IoCopyCurrentIrpStackLocationToNext (SystemStateIrp);
IoSetCompletionRoutine (SystemStateIrp,
Game_PowerComplete,
NULL,
TRUE,
TRUE,
TRUE);
PoCallDriver (fdoData->TopOfStack, SystemStateIrp);
}
}
VOID
Game_PdoPowerDownComplete (
IN PDEVICE_OBJECT DeviceObject,
IN UCHAR MinorFunction,
IN POWER_STATE PowerState,
IN PVOID Context,
IN PIO_STATUS_BLOCK IoStatus
)
{
PFDO_DEVICE_DATA data = (PFDO_DEVICE_DATA) Context;
UNREFERENCED_PARAMETER (DeviceObject);
UNREFERENCED_PARAMETER (MinorFunction);
UNREFERENCED_PARAMETER (PowerState);
#if !DBG
UNREFERENCED_PARAMETER (IoStatus);
#endif
ASSERT( NT_SUCCESS (IoStatus->Status));
if (0 == InterlockedDecrement (&data->PoweredDownDevices)) {
KeSetEvent (&data->PoweredDownEvent, 1, FALSE);
}
}
NTSTATUS
Game_PowerComplete (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
POWER_STATE powerState;
POWER_STATE_TYPE powerType;
PIO_STACK_LOCATION stack;
PFDO_DEVICE_DATA data;
NTSTATUS status;
UNREFERENCED_PARAMETER (Context);
data = (PFDO_DEVICE_DATA) DeviceObject->DeviceExtension;
stack = IoGetCurrentIrpStackLocation (Irp);
powerType = stack->Parameters.Power.Type;
powerState = stack->Parameters.Power.State;
status = STATUS_SUCCESS;
switch (stack->MinorFunction) {
case IRP_MN_SET_POWER:
switch (powerType) {
case DevicePowerState:
//
// Power up complete
//
ASSERT (powerState.DeviceState < data->DeviceState);
data->DeviceState = powerState.DeviceState;
PoSetPowerState (data->Self, powerType, powerState);
break;
case SystemPowerState:
//
// Ususally the work of requesting the Device Power IRP on
// behalf of the SystemPower Irp is work done by the Function
// (FDO) driver. In order, however that Joystick function drivers
// have a more simplified power code path (AKA they merely need
// pass on ALL power IRPS) will will do this work for them in the
// PDO.
//
// NB: This assumes that we will never have any "clever" power
// management for a gaming device attached through a legacy
// gaming port. By which I mean that the HIDGame driver will not
// be able to select a "D" state based on the "S" state; as it is
// done for the HidGame driver.
//
// Any yahoo putting wakeup capabilities into a legacy joystick
// should be shot. It will require special hardware. If you are
// adding extra hardware then you should not be doing so to this
// nasty RC circuit.
//
if (powerState.SystemState > data->SystemState) {
//
// Powering Down...
//
// We are on the completion end of an S irp. (The D3 power irp
// has already been sent and completed down this stack.) The
// remaining thing to do is set the state in the extension, then
// decrement the IoCount that was incremented when we first got
// the irp (this is done at the end of this function).
//
data->SystemState = powerState.SystemState;
PoSetPowerState (data->Self,
stack->Parameters.Power.Type,
stack->Parameters.Power.State);
}
else {
//
// Powering Up...
//
// Request a D power irp for ourself. Do not complete this S irp
// until the D irp has been completed. (Completion of the S irp
// is done in Game_FdoPowerTransitionPoRequestComplete).
// Decrementing the IO count will happen in the same function.
//
ASSERT (powerState.SystemState < data->SystemState);
powerState.DeviceState = PowerDeviceD0;
status =
PoRequestPowerIrp (data->Self,
IRP_MN_SET_POWER,
powerState,
Game_FdoPowerTransitionPoRequestComplete,
Irp,
NULL); // no return Irp
if (status != STATUS_PENDING) {
ASSERT (!NT_SUCCESS (status));
Irp->IoStatus.Status = status;
PoStartNextPowerIrp (Irp);
Game_DecIoCount (data);
}
else {
//
// We need to:
// Start next power irp, release the removelock, and complete
// the irp in the PoRequestComplete routine.
//
//
// The irp might completed by the time we get here, so call
// PoStartNextPowerIrp in the PO irp completion function.
//
status = STATUS_MORE_PROCESSING_REQUIRED;
}
return status;
}
break;
}
break;
default:
#define GAME_UNHANDLED_MN_POWER 0x0
ASSERT (0xBADBAD == GAME_UNHANDLED_MN_POWER);
#undef GAME_UNHANDLED_MN_POWER
status = STATUS_NOT_SUPPORTED;
break;
}
if (NT_SUCCESS(status)) {
PoStartNextPowerIrp (Irp);
Game_DecIoCount (data);
}
return status;
}
NTSTATUS
Game_FDO_Power (
PFDO_DEVICE_DATA Data,
PIRP Irp
)
{
NTSTATUS status;
BOOLEAN hookit = FALSE, wait = FALSE;
POWER_STATE powerState;
POWER_STATE_TYPE powerType;
PIO_STACK_LOCATION stack;
PLIST_ENTRY entry;
PPDO_DEVICE_DATA pdoData;
stack = IoGetCurrentIrpStackLocation (Irp);
powerType = stack->Parameters.Power.Type;
powerState = stack->Parameters.Power.State;
PAGED_CODE ();
status = Game_IncIoCount (Data);
if (!NT_SUCCESS (status)) {
PoStartNextPowerIrp (Irp);
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = status;
IoCompleteRequest (Irp, IO_NO_INCREMENT);
return status;
}
switch (stack->MinorFunction) {
case IRP_MN_SET_POWER:
Game_KdPrint(Data,
GAME_DBG_PNP_TRACE,
("Game-PnP Setting %s state to %d\n",
((powerType == SystemPowerState) ? "System" : "Device"),
powerState.SystemState));
switch (powerType) {
case DevicePowerState:
status = Irp->IoStatus.Status = STATUS_SUCCESS;
if (Data->DeviceState == powerState.DeviceState) {
break;
} else if (Data->DeviceState < powerState.DeviceState) {
//
// Powering down
//
//
// Iterate through the PDOs and make sure that they are all
// powered down.
//
// Initially set PoweredDownDevices to the number of PDOs. If
// a pdo is not powered down, PoweredDownDevices will be
// decremented upon completion of the power down irp sent to
// that particular PDO. Otherwise, the PDO is already powered
// down so just decrement the count.
//
Data->PoweredDownDevices = Data->NumPDOs;
KeInitializeEvent (&Data->PoweredDownEvent,
SynchronizationEvent,
FALSE);
for (entry = Data->PDOs.Flink;
entry != &Data->PDOs;
entry = entry->Flink) {
pdoData = CONTAINING_RECORD (entry, PDO_DEVICE_DATA, Link);
if (pdoData->DeviceState == PowerDeviceD0) {
wait = TRUE;
powerState.DeviceState = PowerDeviceD3;
PoRequestPowerIrp (pdoData->Self,
IRP_MN_SET_POWER,
powerState,
Game_PdoPowerDownComplete,
Data,
NULL);
}
else {
//
// All the power down irps to the PDOs can complete
// before we get to this already powered down PDO, so
// set the event if it is the last and we have a PDO
// that needed powering down.
//
if (InterlockedDecrement(&Data->PoweredDownDevices) == 0
&& wait) {
KeSetEvent (&Data->PoweredDownEvent, 1, FALSE);
}
}
}
if (wait) {
KeWaitForSingleObject (&Data->PoweredDownEvent,
Executive,
KernelMode,
FALSE,
NULL);
#if DBG
///
// Make SURE that all the PDOs are trully powered down
//
for (entry = Data->PDOs.Flink;
entry != &Data->PDOs;
entry = entry->Flink) {
pdoData = CONTAINING_RECORD (entry, PDO_DEVICE_DATA, Link);
ASSERT(pdoData->DeviceState != PowerDeviceD0);
}
#endif
}
ASSERT(Data->PoweredDownDevices == 0);
//
// Make sure powerState is the one sent down to us, not the
// modified version above
//
powerState = stack->Parameters.Power.State;
PoSetPowerState (Data->Self, powerType, powerState);
Data->DeviceState = powerState.DeviceState;
} else {
//
// Powering Up
//
hookit = TRUE;
}
break;
case SystemPowerState:
if (Data->SystemState == powerState.SystemState) {
status = STATUS_SUCCESS;
} else if (Data->SystemState < powerState.SystemState) {
//
// Powering down
//
//
// Request a D3 irp in response to this S irp. The D3 irp must
// completed before send this S irp down the stack. We will send
// the S irp down the stack when
// Game_FdoPowerTransitionPoRequestComplete is called.
//
//
// We don't need to increment our IO count b/c we incremented it
// at the beginning of this function and won't decrement it until
// the S Irp completes
//
IoMarkIrpPending (Irp);
powerState.DeviceState = PowerDeviceD3;
PoRequestPowerIrp (Data->Self,
IRP_MN_SET_POWER,
powerState,
Game_FdoPowerTransitionPoRequestComplete,
Irp,
NULL); // no IRP
return STATUS_PENDING;
} else {
//
// Powering Up
//
//
// We must request a D irp for this S irp, but only after the S
// irp has come back up the stack. Hook the return of the irp
// and request the D irp in Game_PowerComplete
//
hookit = TRUE;
}
break;
}
break;
case IRP_MN_QUERY_POWER:
status = Irp->IoStatus.Status = STATUS_SUCCESS;
break;
default:
break;
}
IoCopyCurrentIrpStackLocationToNext (Irp);
if (hookit) {
ASSERT (STATUS_SUCCESS == status);
//
// If we are returning STATUS_PENDING, the irp must marked as such as well
//
IoMarkIrpPending (Irp);
IoSetCompletionRoutine (Irp,
Game_PowerComplete,
NULL,
TRUE,
TRUE,
TRUE);
//
// NOTE!!! PoCallDriver NOT IoCallDriver.
//
PoCallDriver (Data->TopOfStack, Irp);
//
// We are returning pending instead of the result from PoCallDriver becuase:
// 1 we are changing the status in the completion routine
// 2 we will not be completing this irp in the completion routine
//
status = STATUS_PENDING;
} else {
//
// 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, but never the less must be called.
//
PoStartNextPowerIrp (Irp);
status = PoCallDriver (Data->TopOfStack, Irp);
Game_DecIoCount (Data);
}
return status;
}
VOID
Game_PdoPoRequestComplete (
IN PDEVICE_OBJECT DeviceObject,
IN UCHAR MinorFunction,
IN POWER_STATE DevicePowerState,
IN PIRP SystemStateIrp,
IN PIO_STATUS_BLOCK IoStatus
)
{
PIO_STACK_LOCATION stack;
PPDO_DEVICE_DATA pdoData;
UNREFERENCED_PARAMETER (MinorFunction);
UNREFERENCED_PARAMETER (DevicePowerState);
UNREFERENCED_PARAMETER (IoStatus);
pdoData = (PPDO_DEVICE_DATA) DeviceObject->DeviceExtension;
stack = IoGetCurrentIrpStackLocation (SystemStateIrp);
PoSetPowerState (DeviceObject,
stack->Parameters.Power.Type,
stack->Parameters.Power.State);
pdoData->SystemState = stack->Parameters.Power.State.SystemState;
//
// Set the S irp's status to the status of the D irp
//
SystemStateIrp->IoStatus.Status = IoStatus->Status;
PoStartNextPowerIrp (SystemStateIrp);
IoCompleteRequest (SystemStateIrp, IO_NO_INCREMENT);
}
NTSTATUS
Game_PDO_Power (
PPDO_DEVICE_DATA PdoData,
PIRP Irp
)
{
KIRQL irql;
NTSTATUS status = STATUS_SUCCESS;
PIO_STACK_LOCATION stack;
POWER_STATE powerState;
POWER_STATE_TYPE powerType;
PAGED_CODE();
stack = IoGetCurrentIrpStackLocation (Irp);
powerType = stack->Parameters.Power.Type;
powerState = stack->Parameters.Power.State;
switch (stack->MinorFunction) {
case IRP_MN_SET_POWER:
switch (powerType) {
case DevicePowerState:
PoSetPowerState (PdoData->Self, powerType, powerState);
PdoData->DeviceState = powerState.DeviceState;
break;
case SystemPowerState:
//
// Make the IRP pending and request a D irp for this stack. When
// the D irp completes, Game_PdoPoRequestComplete will be called. In
// that function, we complete this S irp
//
IoMarkIrpPending(Irp);
if (PowerSystemWorking == powerState.SystemState) {
powerState.DeviceState = PowerDeviceD0;
} else {
powerState.DeviceState = PowerDeviceD3;
}
status = PoRequestPowerIrp (PdoData->Self,
IRP_MN_SET_POWER,
powerState,
Game_PdoPoRequestComplete,
Irp,
NULL); // no return IRP
if (status != STATUS_PENDING) {
ASSERT (!NT_SUCCESS (status));
break;
}
return status;
default:
TRAP ();
status = STATUS_NOT_IMPLEMENTED;
break;
}
break;
case IRP_MN_QUERY_POWER:
status = STATUS_SUCCESS;
break;
case IRP_MN_WAIT_WAKE:
case IRP_MN_POWER_SEQUENCE:
default:
status = STATUS_NOT_SUPPORTED;
break;
}
PoStartNextPowerIrp (Irp);
Irp->IoStatus.Status = status;
IoCompleteRequest (Irp, IO_NO_INCREMENT);
return status;
}