/*++ Copyright (c) 1997-1999 Microsoft Corporation, All Rights Reserved Module Name: ptdrvpnp.c Abstract: This module contains general PnP code for the RDP remote port driver. Environment: Kernel mode. Revision History: 02/12/99 - Initial Revision based on pnpi8042 driver --*/ #include #pragma hdrstop #include "ptdrvcom.h" #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, PtAddDevice) #pragma alloc_text(PAGE, PtManuallyRemoveDevice) #pragma alloc_text(PAGE, PtPnP) //#pragma alloc_text(PAGE, PtPower) #pragma alloc_text(PAGE, PtSendIrpSynchronously) #endif NTSTATUS PtAddDevice ( IN PDRIVER_OBJECT Driver, IN PDEVICE_OBJECT PDO ) /*++ Routine Description: Adds a device to the stack and sets up the appropriate flags and device extension for the newly created device. Arguments: Driver - The driver object PDO - the device that we are attaching ourselves on top of Return Value: NTSTATUS result code. --*/ { PCOMMON_DATA commonData; IO_ERROR_LOG_PACKET errorLogEntry; PDEVICE_OBJECT device; NTSTATUS status = STATUS_SUCCESS; ULONG maxSize; UNICODE_STRING fullRDPName; UNICODE_STRING baseRDPName; UNICODE_STRING deviceNameSuffix; PAGED_CODE(); Print(DBG_PNP_TRACE, ("enter Add Device: %ld \n", Globals.ulDeviceNumber)); // // Initialize the various unicode structures for forming the device name. // if (Globals.ulDeviceNumber == 0) RtlInitUnicodeString(&fullRDPName, RDP_CONSOLE_BASE_NAME0); else RtlInitUnicodeString(&fullRDPName, RDP_CONSOLE_BASE_NAME1); maxSize = sizeof(PORT_KEYBOARD_EXTENSION) > sizeof(PORT_MOUSE_EXTENSION) ? sizeof(PORT_KEYBOARD_EXTENSION) : sizeof(PORT_MOUSE_EXTENSION); status = IoCreateDevice(Driver, // driver maxSize, // size of extension NULL, // device name FILE_DEVICE_8042_PORT, // device type ?? unknown at this time!!! FILE_DEVICE_SECURE_OPEN,// device characteristics FALSE, // exclusive &device // new device ); if (!NT_SUCCESS(status)) { Print(DBG_SS_TRACE, ("Add Device failed! (0x%x) \n", status)); return status; } Globals.ulDeviceNumber++; RtlZeroMemory(device->DeviceExtension, maxSize); // // Set up the device type // *((ULONG *)(device->DeviceExtension)) = DEV_TYPE_PORT; commonData = GET_COMMON_DATA(device->DeviceExtension); RtlInitUnicodeString(&commonData->DeviceName, fullRDPName.Buffer); commonData->TopOfStack = IoAttachDeviceToDeviceStack(device, PDO); ASSERT(commonData->TopOfStack); commonData->Self = device; commonData->PDO = PDO; device->Flags |= DO_BUFFERED_IO; device->Flags &= ~DO_DEVICE_INITIALIZING; Print(DBG_PNP_TRACE, ("Add Device (0x%x)\n", status)); return status; } NTSTATUS PtSendIrpSynchronously ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: Generic routine to send an irp DeviceObject and wait for its return up the device stack. Arguments: DeviceObject - The device object to which we want to send the Irp Irp - The Irp we want to send Return Value: return code from the Irp --*/ { KEVENT event; NTSTATUS status; PAGED_CODE(); KeInitializeEvent(&event, SynchronizationEvent, FALSE ); IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp, PtPnPComplete, &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; } return status; } NTSTATUS PtPnPComplete ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PKEVENT Event ) /*++ Routine Description: Completion routine for all PnP IRPs Arguments: DeviceObject - Pointer to the DeviceObject Irp - Pointer to the request packet Event - The event to set once processing is complete Return Value: STATUS_SUCCESSFUL if successful, an valid NTSTATUS error code otherwise --*/ { PIO_STACK_LOCATION stack; NTSTATUS status; UNREFERENCED_PARAMETER (DeviceObject); status = STATUS_SUCCESS; stack = IoGetCurrentIrpStackLocation(Irp); if (Irp->PendingReturned) { IoMarkIrpPending(Irp); } KeSetEvent(Event, 0, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS PtPnP ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This is the dispatch routine for PnP requests Arguments: DeviceObject - Pointer to the device object Irp - Pointer to the request packet Return Value: STATUS_SUCCESSFUL if successful, an valid NTSTATUS error code otherwise --*/ { PPORT_KEYBOARD_EXTENSION kbExtension; PPORT_MOUSE_EXTENSION mouseExtension; PCOMMON_DATA commonData; PIO_STACK_LOCATION stack; NTSTATUS status = STATUS_SUCCESS; KIRQL oldIrql; PAGED_CODE(); commonData = GET_COMMON_DATA(DeviceObject->DeviceExtension); stack = IoGetCurrentIrpStackLocation(Irp); Print(DBG_PNP_TRACE, ("PtPnP (%s), enter (min func=0x%x)\n", commonData->IsKeyboard ? "kb" : "mou", (ULONG) stack->MinorFunction )); switch (stack->MinorFunction) { case IRP_MN_START_DEVICE: // // The device is starting. // // We cannot touch the device (send it any non pnp irps) until a // start device has been passed down to the lower drivers. // status = PtSendIrpSynchronously(commonData->TopOfStack, Irp); if (NT_SUCCESS(status) && NT_SUCCESS(Irp->IoStatus.Status)) { // // As we are successfully now back from our start device // we can do work. ExAcquireFastMutexUnsafe(&Globals.DispatchMutex); if (commonData->Started) { Print(DBG_PNP_ERROR, ("received 1+ starts on %s\n", commonData->IsKeyboard ? "kb" : "mouse" )); } else { // // commonData->IsKeyboard is set during // IOCTL_INTERNAL_KEYBOARD_CONNECT to TRUE and // IOCTL_INTERNAL_MOUSE_CONNECT to FALSE // if (commonData->IsKeyboard) { status = PtKeyboardStartDevice( (PPORT_KEYBOARD_EXTENSION) DeviceObject->DeviceExtension, stack->Parameters.StartDevice.AllocatedResourcesTranslated ); } else { status = PtMouseStartDevice( (PPORT_MOUSE_EXTENSION) DeviceObject->DeviceExtension, stack->Parameters.StartDevice.AllocatedResourcesTranslated ); } if (NT_SUCCESS(status)) { commonData->Started = TRUE; } } ExReleaseFastMutexUnsafe(&Globals.DispatchMutex); } // // We must now complete the IRP, since we stopped it in the // completetion routine with MORE_PROCESSING_REQUIRED. // Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); break; case IRP_MN_QUERY_PNP_DEVICE_STATE: status = PtSendIrpSynchronously(commonData->TopOfStack, Irp); // // If the lower filter does not support this Irp, this is // OK, we can ignore this error // if (status == STATUS_NOT_SUPPORTED || status == STATUS_INVALID_DEVICE_REQUEST) { status = STATUS_SUCCESS; } // // do stuff here... // if (NT_SUCCESS(status)) { if (commonData->ManuallyRemoved && !(commonData->IsKeyboard ? KEYBOARD_PRESENT():MOUSE_PRESENT())) { commonData->Started = FALSE; (PNP_DEVICE_STATE) Irp->IoStatus.Information |= (PNP_DEVICE_REMOVED | PNP_DEVICE_DONT_DISPLAY_IN_UI); } // // In all cases this device must be disableable // (PNP_DEVICE_STATE) Irp->IoStatus.Information &= ~PNP_DEVICE_NOT_DISABLEABLE; // // Don't show it in the device manager // (PNP_DEVICE_STATE) Irp->IoStatus.Information |= PNP_DEVICE_DONT_DISPLAY_IN_UI; } else { Print(DBG_PNP_ERROR, ("error pending query pnp device state event (0x%x)\n", status )); } // // Irp->IoStatus.Information will contain the new i/o resource // requirements list so leave it alone // Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); break; // // Don't let either of the requests succeed, otherwise the kb/mouse // might be rendered useless. // // NOTE: this behavior is particular to i8042prt. Any other driver, // especially any other keyboard or port driver, should // succeed the query remove or stop. i8042prt has this different // behavior because of the shared I/O ports but independent interrupts. // // FURTHERMORE, if you allow the query to succeed, it should be sent // down the stack (see sermouse.sys for an example of how to do this) // case IRP_MN_QUERY_REMOVE_DEVICE: case IRP_MN_QUERY_STOP_DEVICE: status = (commonData->ManuallyRemoved ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL); Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); break; // // PnP rules dictate we send the IRP down to the PDO first // case IRP_MN_CANCEL_REMOVE_DEVICE: case IRP_MN_CANCEL_STOP_DEVICE: status = PtSendIrpSynchronously(commonData->TopOfStack, Irp); // // If the lower filter does not support this Irp, this is // OK, we can ignore this error // if (status == STATUS_NOT_SUPPORTED || status == STATUS_INVALID_DEVICE_REQUEST) { status = STATUS_SUCCESS; } Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); break; case IRP_MN_REMOVE_DEVICE: Print(DBG_PNP_TRACE, ("remove device\n")); if (commonData->Started && !commonData->ManuallyRemoved) { // // This should never happen. The only way we can get a remove is if // a start has failed. // // NOTE: Again, this should never happen for i8042prt, but any // other input port driver should allow itself to be removed // (see sermouse.sys on how to do this correctly) // Print(DBG_PNP_ERROR, ("Cannot remove a started device!!!\n")); ASSERT(FALSE); } if (commonData->Initialized) { IoWMIRegistrationControl(commonData->Self, WMIREG_ACTION_DEREGISTER ); } ExAcquireFastMutexUnsafe(&Globals.DispatchMutex); if (commonData->IsKeyboard) { PtKeyboardRemoveDevice(DeviceObject); } ExReleaseFastMutexUnsafe(&Globals.DispatchMutex); // // Nothing has been allocated or connected // Irp->IoStatus.Status = STATUS_SUCCESS; IoSkipCurrentIrpStackLocation(Irp); status = IoCallDriver(commonData->TopOfStack, Irp); IoDetachDevice(commonData->TopOfStack); IoDeleteDevice(DeviceObject); break; case IRP_MN_STOP_DEVICE: case IRP_MN_QUERY_DEVICE_RELATIONS: case IRP_MN_QUERY_INTERFACE: case IRP_MN_QUERY_CAPABILITIES: case IRP_MN_QUERY_DEVICE_TEXT: case IRP_MN_QUERY_RESOURCES: case IRP_MN_QUERY_RESOURCE_REQUIREMENTS: case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: case IRP_MN_READ_CONFIG: case IRP_MN_WRITE_CONFIG: case IRP_MN_EJECT: case IRP_MN_SET_LOCK: case IRP_MN_QUERY_ID: default: // // Here the driver below i8042prt might modify the behavior of these IRPS // Please see PlugPlay documentation for use of these IRPs. // IoSkipCurrentIrpStackLocation(Irp); status = IoCallDriver(commonData->TopOfStack, Irp); break; } Print(DBG_PNP_TRACE, ("PtPnP (%s) exit (status=0x%x)\n", commonData->IsKeyboard ? "kb" : "mou", status )); return status; } LONG PtManuallyRemoveDevice( PCOMMON_DATA CommonData ) /*++ Routine Description: Invalidates CommonData->PDO's device state and sets the manually removed flag Arguments: CommonData - represent either the keyboard or mouse Return Value: new device count for that particular type of device --*/ { LONG deviceCount; PAGED_CODE(); if (CommonData->IsKeyboard) { deviceCount = InterlockedDecrement(&Globals.AddedKeyboards); if (deviceCount < 1) { Print(DBG_PNP_INFO, ("clear kb (manually remove)\n")); CLEAR_KEYBOARD_PRESENT(); } } else { deviceCount = InterlockedDecrement(&Globals.AddedMice); if (deviceCount < 1) { Print(DBG_PNP_INFO, ("clear mou (manually remove)\n")); CLEAR_MOUSE_PRESENT(); } } CommonData->ManuallyRemoved = TRUE; IoInvalidateDeviceState(CommonData->PDO); return deviceCount; } NTSTATUS PtPower ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This is the dispatch routine for power requests. Arguments: DeviceObject - Pointer to the device object. Irp - Pointer to the request packet. Return Value: STATUS_SUCCESSFUL if successful, an valid NTSTATUS error code otherwise --*/ { PCOMMON_DATA commonData; PIO_STACK_LOCATION stack; NTSTATUS status = STATUS_SUCCESS; //PAGED_CODE(); commonData = GET_COMMON_DATA(DeviceObject->DeviceExtension); stack = IoGetCurrentIrpStackLocation(Irp); Print(DBG_POWER_TRACE, ("Power (%s), enter\n", commonData->IsKeyboard ? "keyboard" : "mouse" )); switch(stack->MinorFunction) { case IRP_MN_WAIT_WAKE: Print(DBG_POWER_NOISE, ("Got IRP_MN_WAIT_WAKE\n" )); break; case IRP_MN_POWER_SEQUENCE: Print(DBG_POWER_NOISE, ("Got IRP_MN_POWER_SEQUENCE\n" )); break; case IRP_MN_SET_POWER: Print(DBG_POWER_NOISE, ("Got IRP_MN_SET_POWER\n" )); // // Don't handle anything but DevicePowerState changes // if (stack->Parameters.Power.Type != DevicePowerState) { Print(DBG_POWER_TRACE, ("not a device power irp\n")); break; } // // Check for no change in state, and if none, do nothing // if (stack->Parameters.Power.State.DeviceState == commonData->PowerState) { Print(DBG_POWER_INFO, ("no change in state (PowerDeviceD%d)\n", commonData->PowerState-1 )); break; } switch (stack->Parameters.Power.State.DeviceState) { case PowerDeviceD0: Print(DBG_POWER_TRACE, ("Powering up to PowerDeviceD0\n")); commonData->IsKeyboard ? KEYBOARD_POWERED_UP_STARTED() : MOUSE_POWERED_UP_STARTED(); IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp, PtPowerUpToD0Complete, NULL, TRUE, // on success TRUE, // on error TRUE // on cancel ); // // PoStartNextPowerIrp() gets called when the irp gets completed // IoMarkIrpPending(Irp); PoCallDriver(commonData->TopOfStack, Irp); return STATUS_PENDING; case PowerDeviceD1: case PowerDeviceD2: case PowerDeviceD3: Print(DBG_POWER_TRACE, ("Powering down to PowerDeviceD%d\n", stack->Parameters.Power.State.DeviceState-1 )); PoSetPowerState(DeviceObject, stack->Parameters.Power.Type, stack->Parameters.Power.State ); commonData->PowerState = stack->Parameters.Power.State.DeviceState; commonData->ShutdownType = stack->Parameters.Power.ShutdownType; // // For what we are doing, we don't need a completion routine // since we don't race on the power requests. // Irp->IoStatus.Status = STATUS_SUCCESS; IoCopyCurrentIrpStackLocationToNext(Irp); // skip ? PoStartNextPowerIrp(Irp); return PoCallDriver(commonData->TopOfStack, Irp); default: Print(DBG_POWER_INFO, ("unknown state\n")); break; } break; case IRP_MN_QUERY_POWER: Print(DBG_POWER_NOISE, ("Got IRP_MN_QUERY_POWER\n" )); break; default: Print(DBG_POWER_NOISE, ("Got unhandled minor function (%d)\n", stack->MinorFunction )); break; } Print(DBG_POWER_TRACE, ("Power, exit\n")); PoStartNextPowerIrp(Irp); IoSkipCurrentIrpStackLocation(Irp); return PoCallDriver(commonData->TopOfStack, Irp); } NTSTATUS PtPowerUpToD0Complete( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) /*++ Routine Description: Reinitializes the i8042 haardware after any type of hibernation/sleep. Arguments: DeviceObject - Pointer to the device object Irp - Pointer to the request Context - Context passed in from the funciton that set the completion routine. UNUSED. Return Value: STATUS_SUCCESSFUL if successful, an valid NTSTATUS error code otherwise --*/ { NTSTATUS status; PCOMMON_DATA commonData; PIO_STACK_LOCATION stack; KIRQL irql; UNREFERENCED_PARAMETER(Context); commonData = GET_COMMON_DATA(DeviceObject->DeviceExtension); Print(DBG_POWER_TRACE, ("PowerUpToD0Complete (%s), Enter\n", commonData->IsKeyboard ? "kb" : "mouse" )); KeAcquireSpinLock(&Globals.ControllerData->PowerUpSpinLock, &irql); if (NT_SUCCESS(Irp->IoStatus.Status)) { commonData->IsKeyboard ? KEYBOARD_POWERED_UP_SUCCESSFULLY() : MOUSE_POWERED_UP_SUCCESSFULLY(); status = STATUS_MORE_PROCESSING_REQUIRED; } else { commonData->IsKeyboard ? KEYBOARD_POWERED_UP_FAILED() : MOUSE_POWERED_UP_FAILED(); status = Irp->IoStatus.Status; #if DBG if (commonData->IsKeyboard) { ASSERT(KEYBOARD_POWERED_UP_FAILED()); } else { ASSERT(MOUSE_POWERED_UP_FAILED()); } #endif // DBG } KeReleaseSpinLock(&Globals.ControllerData->PowerUpSpinLock, irql); if (NT_SUCCESS(status)) { Print(DBG_SS_NOISE, ("reinit, status == 0x%x\n", status)); stack = IoGetCurrentIrpStackLocation(Irp); ASSERT(stack->Parameters.Power.State.DeviceState == PowerDeviceD0); commonData->PowerState = stack->Parameters.Power.State.DeviceState; commonData->ShutdownType = PowerActionNone; PoSetPowerState(commonData->Self, stack->Parameters.Power.Type, stack->Parameters.Power.State ); } // // Complete the irp // Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; PoStartNextPowerIrp(Irp); IoCompleteRequest(Irp, IO_NO_INCREMENT); // // Reset PoweredDevices so that we can keep track of the powered device // the next time the machine is power managed off. // CLEAR_POWERUP_FLAGS(); return status; }