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