|
|
/*++
Copyright (c) 1998 - 1999 Microsoft Corporation
Module Name:
pnp.c
Abstract: This module contains PnP Start, Stop, Remove, Power dispatch routines and IRP cancel routine.
Environment:
Kernel mode
@@BEGIN_DDKSPLIT Author:
Eliyas Yakub (Mar, 11, 1997) Revision History:
Updated by Eliyas on Feb 5 1998 Om Sharma ( April 15, 1998) MarcAnd 02-Jul-98 Quick tidy for DDK
@@END_DDKSPLIT --*/
#include "hidgame.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text (PAGE, HGM_RemoveDevice)
#pragma alloc_text (PAGE, HGM_PnP)
#pragma alloc_text (PAGE, HGM_InitDevice)
#pragma alloc_text (PAGE, HGM_GetResources)
#pragma alloc_text (PAGE, HGM_Power)
#endif
/*****************************************************************************
* * @doc INTERNAL * * @func NTSTATUS | HGM_IncRequestCount | * * Try to increment the request count but fail if the device is * being removed. * * @parm IN PDEVICE_EXTENSION | DeviceExtension | * * Pointer to the device extension. * * @rvalue STATUS_SUCCESS | success * @rvalue STATUS_DELETE_PENDING | PnP IRP received after device was removed * *****************************************************************************/ NTSTATUS EXTERNAL HGM_IncRequestCount ( IN PDEVICE_EXTENSION DeviceExtension ) { NTSTATUS ntStatus;
InterlockedIncrement( &DeviceExtension->RequestCount ); ASSERT( DeviceExtension->RequestCount > 0 ); if( DeviceExtension->fRemoved ) { /*
* PnP has already told us to remove the device so fail and make * sure that the event has been set. */ if( 0 == InterlockedDecrement( &DeviceExtension->RequestCount ) ) { KeSetEvent( &DeviceExtension->RemoveEvent, IO_NO_INCREMENT, FALSE ); } ntStatus = STATUS_DELETE_PENDING; } else { ntStatus = STATUS_SUCCESS; }
return ntStatus; }
/*****************************************************************************
* * @doc INTERNAL * * @func VOID | HGM_DecRequestCount | * * Decrement the request count and set event if this is the last. * * @parm IN PDEVICE_EXTENSION | DeviceExtension | * * Pointer to the device extension. * *****************************************************************************/ VOID EXTERNAL HGM_DecRequestCount ( IN PDEVICE_EXTENSION DeviceExtension ) { LONG LocalCount;
LocalCount = InterlockedDecrement( &DeviceExtension->RequestCount );
ASSERT( DeviceExtension->RequestCount >= 0 ); if( LocalCount == 0 ) { /*
* PnP has already told us to remove the device so the PnP remove * code should have set device as removed and should be waiting on * the event. */ ASSERT( DeviceExtension->fRemoved ); KeSetEvent( &DeviceExtension->RemoveEvent, IO_NO_INCREMENT, FALSE ); }
return; }
/*****************************************************************************
* * @doc EXTERNAL * * @func VOID | HGM_RemoveDevice | * * FDO Remove routine * * @parm IN PDEVICE_EXTENSION | DeviceExtension | * * Pointer to the device extension. * *****************************************************************************/ VOID INTERNAL HGM_RemoveDevice ( PDEVICE_EXTENSION DeviceExtension ) { if (DeviceExtension->fSurpriseRemoved) { return; }
DeviceExtension->fSurpriseRemoved = TRUE;
/*
* Acquire mutex before modifying the Global Linked list of devices */ ExAcquireFastMutex (&Global.Mutex); /*
* Remove this device from the linked list of devices */ RemoveEntryList(&DeviceExtension->Link); /*
* Release the mutex */ ExReleaseFastMutex (&Global.Mutex); } /* HGM_RemoveDevice */
/*****************************************************************************
* * @doc EXTERNAL * * @func NTSTATUS | HGM_PnP | * * Plug and Play dispatch routine for this driver. * * @parm IN PDEVICE_OBJECT | DeviceObject | * * Pointer to the device object. * * @parm IN PIRP | Irp | * * Pointer to an I/O request packet. * * @rvalue STATUS_SUCCESS | success * @rvalue STATUS_DELETE_PENDING | PnP IRP received after device was removed * @rvalue ??? | Return from IoCallDriver() or HGM_InitDevice() * *****************************************************************************/ NTSTATUS EXTERNAL HGM_PnP ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { NTSTATUS ntStatus; PDEVICE_EXTENSION DeviceExtension; KEVENT StartEvent;
PAGED_CODE();
HGM_DBGPRINT(FILE_PNP | HGM_FENTRY,\ ("HGM_PnP(DeviceObject=0x%x,Irp=0x%x)",\ DeviceObject, Irp )); /*
* Get a pointer to the device extension */ DeviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject);
ntStatus = HGM_IncRequestCount( DeviceExtension ); if (!NT_SUCCESS (ntStatus)) { /*
* Someone sent us another plug and play IRP after removed */
HGM_DBGPRINT(FILE_PNP | HGM_ERROR,\ ("HGM_PnP: PnP IRP after device was removed\n")); Irp->IoStatus.Information = 0; Irp->IoStatus.Status = ntStatus; IoCompleteRequest (Irp, IO_NO_INCREMENT); } else { PIO_STACK_LOCATION IrpStack;
/*
* Get a pointer to the current location in the Irp */ IrpStack = IoGetCurrentIrpStackLocation (Irp);
switch(IrpStack->MinorFunction) { case IRP_MN_START_DEVICE:
HGM_DBGPRINT(FILE_PNP | HGM_BABBLE,\ ("HGM_Pnp: 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. */ KeInitializeEvent(&StartEvent, NotificationEvent, FALSE);
IoCopyCurrentIrpStackLocationToNext (Irp); IoSetCompletionRoutine (Irp, HGM_PnPComplete, &StartEvent, TRUE, TRUE, TRUE); ntStatus = IoCallDriver (GET_NEXT_DEVICE_OBJECT(DeviceObject), Irp);
if( NT_SUCCESS(ntStatus ) ) { ntStatus = KeWaitForSingleObject ( &StartEvent, Executive, /* Waiting for reason of a driver */ KernelMode, /* Waiting in kernel mode */ FALSE, /* No allert */ NULL /* No timeout */ ); }
if(NT_SUCCESS(ntStatus)) { ntStatus = Irp->IoStatus.Status; }
if(NT_SUCCESS (ntStatus)) { /*
* As we are now back from our start device we can do work. */ ntStatus = HGM_InitDevice (DeviceObject, Irp); } else { HGM_DBGPRINT(FILE_PNP | HGM_ERROR,\ ("HGM_Pnp: IRP_MN_START_DEVICE ntStatus =0x%x",\ ntStatus)); }
DeviceExtension->fStarted = TRUE;
/*
* Return Status */ Irp->IoStatus.Information = 0; Irp->IoStatus.Status = ntStatus; IoCompleteRequest (Irp, IO_NO_INCREMENT);
break;
case IRP_MN_STOP_DEVICE:
HGM_DBGPRINT(FILE_PNP | HGM_BABBLE,\ ("HGM_Pnp: 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. */
DeviceExtension->fStarted = FALSE;
/*
* 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 (Irp); ntStatus = IoCallDriver (GET_NEXT_DEVICE_OBJECT(DeviceObject), Irp); break;
case IRP_MN_SURPRISE_REMOVAL: HGM_DBGPRINT(FILE_PNP | HGM_BABBLE,\ ("HGM_Pnp: IRP_MN_SURPRISE_REMOVAL"));
HGM_RemoveDevice(DeviceExtension);
Irp->IoStatus.Status = STATUS_SUCCESS; IoSkipCurrentIrpStackLocation(Irp); ntStatus = IoCallDriver (GET_NEXT_DEVICE_OBJECT(DeviceObject), Irp);
break;
case IRP_MN_REMOVE_DEVICE: HGM_DBGPRINT(FILE_PNP | HGM_BABBLE,\ ("HGM_Pnp: 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. */
/*
* Make sure we do not allow more IRPs to start touching the device */ DeviceExtension->fRemoved = TRUE;
/*
* Stop the device without touching the hardware. */ HGM_RemoveDevice(DeviceExtension);
/*
* Send on the remove IRP */ IoSkipCurrentIrpStackLocation (Irp); ntStatus = IoCallDriver (GET_NEXT_DEVICE_OBJECT(DeviceObject), Irp);
/*
* Remove this IRPs hold which should leave the initial 1 plus * any other IRP holds. */ { LONG RequestCount = InterlockedDecrement( &DeviceExtension->RequestCount ); ASSERT( RequestCount > 0 ); }
/*
* If someone has already started, wait for them to finish */ if( InterlockedDecrement( &DeviceExtension->RequestCount ) > 0 ) { KeWaitForSingleObject( &DeviceExtension->RemoveEvent, Executive, KernelMode, FALSE, NULL ); }
ntStatus = STATUS_SUCCESS;
HGM_EXITPROC(FILE_IOCTL|HGM_FEXIT_STATUSOK, "HGM_PnP Exit 1", ntStatus);
return ntStatus;
default: HGM_DBGPRINT(FILE_PNP | HGM_WARN,\ ("HGM_PnP: IrpStack->MinorFunction Not handled 0x%x", \ IrpStack->MinorFunction));
IoSkipCurrentIrpStackLocation (Irp);
ntStatus = IoCallDriver(GET_NEXT_DEVICE_OBJECT(DeviceObject), Irp); break; }
HGM_DecRequestCount( DeviceExtension ); }
HGM_EXITPROC(FILE_IOCTL|HGM_FEXIT, "HGM_PnP", ntStatus);
return ntStatus; } /* HGM_PnP */
/*****************************************************************************
* * @doc EXTERNAL * * @func NTSTATUS | HGM_InitDevice | * * Get the device information and attempt to initialize a configuration * for a device. If we cannot identify this as a valid HID device or * configure the device, our start device function is failed. * * @parm IN PDEVICE_OBJECT | DeviceObject | * * Pointer to the device object. * * @parm IN PIRP | Irp | * * Pointer to an I/O request packet. * * @rvalue STATUS_SUCCESS | success * @rvalue STATUS_DEVICE_CONFIGURATION_ERROR | Resources overlap * @rvalue ??? | Return from HGM_GetResources() or HGM_JoystickConfig() * * *****************************************************************************/ NTSTATUS INTERNAL HGM_InitDevice ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { NTSTATUS ntStatus; PDEVICE_EXTENSION DeviceExtension; ULONG DescriptorLength;
PAGED_CODE();
HGM_DBGPRINT(FILE_PNP | HGM_FENTRY,\ ("HGM_InitDevice(DeviceObject=0x%x,Irp=0x%x)", \ DeviceObject,Irp));
/*
* Get a pointer to the device extension */ DeviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject);
/*
* Get resource information from GameEnum and store it in the device extension */ ntStatus = HGM_GetResources(DeviceObject,Irp); if( NT_SUCCESS(ntStatus) ) { ntStatus = HGM_InitAnalog(DeviceObject); } else { HGM_DBGPRINT(FILE_PNP | HGM_ERROR,\ ("HGM_InitDevice: HGM_GetResources Failed")); }
if( !NT_SUCCESS(ntStatus) ) { /*
* Acquire mutex before modifying the Global Linked list of devices */ ExAcquireFastMutex (&Global.Mutex);
/*
* Remove this device from the linked list of devices */ RemoveEntryList(&DeviceExtension->Link);
/*
* Release the mutex */ ExReleaseFastMutex (&Global.Mutex); }
HGM_EXITPROC(FILE_IOCTL|HGM_FEXIT_STATUSOK, "HGM_InitDevice", ntStatus);
return ntStatus; } /* HGM_InitDevice */
/*****************************************************************************
* * @doc EXTERNAL * * @func NTSTATUS | HGM_GetResources | * * Gets gameport resource information from the GameEnum driver * * @parm IN PDEVICE_OBJECT | DeviceObject | * * Pointer to the device object. * * @parm IN PIRP | Irp | * * Pointer to an I/O request packet. * * @rvalue STATUS_SUCCESS | success * @rvalue ??? | Return from IoCallDriver() * *****************************************************************************/ NTSTATUS INTERNAL HGM_GetResources ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { NTSTATUS ntStatus = STATUS_SUCCESS; GAMEENUM_PORT_PARAMETERS PortInfo; POEMDATA OemData; PDEVICE_EXTENSION DeviceExtension; KEVENT IoctlCompleteEvent; IO_STATUS_BLOCK IoStatus; PIO_STACK_LOCATION irpStack, nextStack; int i; PAGED_CODE ();
HGM_DBGPRINT(FILE_PNP | HGM_FENTRY,\ ("HGM_GetResources(DeviceObject=0x%x,Irp=0x%x)",\ DeviceObject, Irp));
/*
* Get a pointer to the device extension */
DeviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject);
/*
* issue a synchronous request to get the resources info from GameEnum */
KeInitializeEvent(&IoctlCompleteEvent, NotificationEvent, FALSE);
irpStack = IoGetCurrentIrpStackLocation(Irp); nextStack = IoGetNextIrpStackLocation(Irp); ASSERTMSG("HGM_GetResources:",nextStack != NULL);
/*
* pass the Portinfo buffer of the DeviceExtension */
nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_GAMEENUM_PORT_PARAMETERS;
PortInfo.Size = nextStack->Parameters.DeviceIoControl.InputBufferLength = nextStack->Parameters.DeviceIoControl.OutputBufferLength = sizeof (PortInfo);
Irp->UserBuffer = &PortInfo;
IoSetCompletionRoutine (Irp, HGM_PnPComplete, &IoctlCompleteEvent, TRUE, TRUE, TRUE);
HGM_DBGPRINT(FILE_PNP | HGM_BABBLE,\ ("calling GameEnum"));
ntStatus = IoCallDriver(GET_NEXT_DEVICE_OBJECT (DeviceObject), Irp);
if( NT_SUCCESS(ntStatus) ) { ntStatus = KeWaitForSingleObject( &IoctlCompleteEvent, Executive, KernelMode, FALSE, NULL);
}
if( NT_SUCCESS(ntStatus) ) { ntStatus = Irp->IoStatus.Status; } DeviceExtension->GameContext = PortInfo.GameContext; DeviceExtension->ReadAccessor = PortInfo.ReadAccessor; DeviceExtension->WriteAccessor = PortInfo.WriteAccessor; DeviceExtension->ReadAccessorDigital= PortInfo.ReadAccessorDigital; DeviceExtension->AcquirePort = PortInfo.AcquirePort; DeviceExtension->ReleasePort = PortInfo.ReleasePort; DeviceExtension->PortContext = PortInfo.PortContext; DeviceExtension->nAxes = PortInfo.NumberAxis; DeviceExtension->nButtons = PortInfo.NumberButtons;
#ifdef CHANGE_DEVICE
/*
* Stash the NextDeviceObject in the device extension so that we can * call GameEnum IRPs when we're not responding to an IRP */ DeviceExtension->NextDeviceObject = GET_NEXT_DEVICE_OBJECT(DeviceObject); #endif /* CHANGE_DEVICE */
RtlCopyMemory(DeviceExtension->HidGameOemData.Game_Oem_Data, PortInfo.OemData, sizeof(PortInfo.OemData));
for(i=0x0; i < sizeof(PortInfo.OemData)/sizeof(PortInfo.OemData[0]); i++) { HGM_DBGPRINT( FILE_HIDJOY | HGM_BABBLE2,\ ("JoystickConfig: PortInfo.OemData[%d]=0x%x",\ i, PortInfo.OemData[i]) ); }
HGM_EXITPROC(FILE_IOCTL|HGM_FEXIT_STATUSOK, "HGM_GetResources", Irp->IoStatus.Status);
return Irp->IoStatus.Status; } /* HGM_GetResources */
/*****************************************************************************
* * @doc EXTERNAL * * @func NTSTATUS | HGM_PnPComplete | * * Completion routine for PnP IRPs. * Not pageable because it is a completion routine. * * @parm IN PDEVICE_OBJECT | DeviceObject | * * Pointer to the device object. * * @parm IN PIRP | Irp | * * Pointer to an I/O request packet. * * @rvalue STATUS_MORE_PROCESSING_REQUIRED | We want the IRP back * *****************************************************************************/ NTSTATUS INTERNAL HGM_PnPComplete ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { NTSTATUS ntStatus = STATUS_MORE_PROCESSING_REQUIRED;
HGM_DBGPRINT(FILE_PNP | HGM_FENTRY,\ ("HGM_PnPComplete(DeviceObject=0x%x,Irp=0x%x,Context=0x%x)", \ DeviceObject, Irp, Context));
UNREFERENCED_PARAMETER (DeviceObject); KeSetEvent ((PKEVENT) Context, 0, FALSE);
HGM_EXITPROC(FILE_IOCTL|HGM_FEXIT, "HGM_PnpComplete", ntStatus);
return ntStatus; }
/*****************************************************************************
* * @doc EXTERNAL * * @func NTSTATUS | HGM_Power | * * The power dispatch routine. * <nl>This driver does not recognize power IRPS. It merely sends them down, * unmodified to the next device on the attachment stack. * As this is a POWER irp, and therefore a special irp, special power irp * handling is required. No completion routine is required. * * @parm IN PDEVICE_OBJECT | DeviceObject | * * Pointer to the device object. * * @parm IN PIRP | Irp | * * Pointer to an I/O request packet. * * * @rvalue STATUS_SUCCESS | success * @rvalue ??? | Return from PoCallDriver() * *****************************************************************************/ NTSTATUS INTERNAL HGM_Power ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PDEVICE_EXTENSION DeviceExtension; NTSTATUS ntStatus;
PAGED_CODE ();
HGM_DBGPRINT(FILE_PNP | HGM_FENTRY,\ ("Enter HGM_Power(DeviceObject=0x%x,Irp=0x%x)",DeviceObject, Irp));
DeviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION (DeviceObject);
/*
* Since we do not know what to do with the IRP, we should pass * it on along down the stack. */
ntStatus = HGM_IncRequestCount( DeviceExtension ); if (!NT_SUCCESS (ntStatus)) { /*
* Someone sent us another plug and play IRP after removed */
HGM_DBGPRINT(FILE_PNP | HGM_ERROR,\ ("HGM_Power: PnP IRP after device was removed\n")); Irp->IoStatus.Information = 0; Irp->IoStatus.Status = ntStatus; IoCompleteRequest (Irp, IO_NO_INCREMENT); } else { IoSkipCurrentIrpStackLocation (Irp);
/*
* 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 (Irp);
/*
* NOTE!!! PoCallDriver NOT IoCallDriver. */ ntStatus = PoCallDriver (GET_NEXT_DEVICE_OBJECT (DeviceObject), Irp);
HGM_DecRequestCount( DeviceExtension ); }
HGM_EXITPROC(FILE_IOCTL | HGM_FEXIT, "HGM_Power", ntStatus); return ntStatus; } /* HGM_Power */
|