/*++ Copyright (c) 2000 Microsoft Corporation Module Name: pnp.c Abstract: This module contains code to handle PnP and Power IRPs. Environment: Kernel mode Author: Michael Tsang (MikeTs) 13-Apr-2000 Revision History: --*/ #include "pch.h" #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, HbutPnp) #pragma alloc_text(PAGE, HbutPower) #pragma alloc_text(PAGE, StartDevice) #pragma alloc_text(PAGE, RemoveDevice) #pragma alloc_text(PAGE, SendSyncIrp) #endif /***************************************************************************** * * @doc EXTERNAL * * @func NTSTATUS | HbutPnp | * Plug and Play dispatch routine for this driver. * * @parm IN PDEVICE_OBJECT | DevObj | Pointer to the device object. * @parm IN PIRP | Irp | Pointer to an I/O request packet. * * @rvalue SUCCESS | returns STATUS_SUCCESS * @rvalue FAILURE | returns NT status code * *****************************************************************************/ NTSTATUS EXTERNAL HbutPnp( IN PDEVICE_OBJECT DevObj, IN PIRP Irp ) { PROCNAME("HbutPnp") NTSTATUS status; PIO_STACK_LOCATION irpsp; PDEVICE_EXTENSION devext; PAGED_CODE(); irpsp = IoGetCurrentIrpStackLocation(Irp); ENTER(1, ("(DevObj=%p,Irp=%p,IrpSp=%p,Minor=%s)\n", DevObj, Irp, irpsp, LookupName(irpsp->MinorFunction, PnPMinorFnNames))); devext = GET_MINIDRIVER_DEVICE_EXTENSION(DevObj); status = IoAcquireRemoveLock(&devext->RemoveLock, Irp); if (!NT_SUCCESS(status)) { // // Someone sent us another plug and play IRP after removed // ERRPRINT(("received PnP IRP after device was removed\n")); Irp->IoStatus.Information = 0; Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); } else { BOOLEAN fSkipIt = FALSE; switch (irpsp->MinorFunction) { case IRP_MN_START_DEVICE: ASSERT(!(devext->dwfHBut & HBUTF_DEVICE_STARTED)); // // Forward the IRP down the stack // status = SendSyncIrp(GET_NEXT_DEVICE_OBJECT(DevObj), Irp, TRUE); if (NT_SUCCESS(status)) { status = StartDevice(DevObj, Irp); if (NT_SUCCESS(status)) { devext->dwfHBut |= HBUTF_DEVICE_STARTED; } } else { ERRPRINT(("failed to forward start IRP (status=%x)\n", status)); } Irp->IoStatus.Information = 0; Irp->IoStatus.Status = status; IoCompleteRequest(Irp, 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. // if (devext->dwfHBut & HBUTF_DEVICE_STARTED) { devext->dwfHBut &= ~HBUTF_DEVICE_STARTED; if (devext->dwfHBut & HBUTF_INTERRUPT_CONNECTED) { IoDisconnectInterrupt(devext->InterruptObject); devext->dwfHBut &= ~HBUTF_INTERRUPT_CONNECTED; } } // // 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. // fSkipIt = TRUE; Irp->IoStatus.Status = STATUS_SUCCESS; break; case IRP_MN_REMOVE_DEVICE: case IRP_MN_SURPRISE_REMOVAL: // // The PlugPlay system has detected 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. // devext->dwfHBut &= ~HBUTF_DEVICE_STARTED; devext->dwfHBut |= HBUTF_DEVICE_REMOVED; RemoveDevice(DevObj, Irp); // // Send on the remove IRP // fSkipIt = TRUE; Irp->IoStatus.Status = STATUS_SUCCESS; break; case IRP_MN_QUERY_CAPABILITIES: status = SendSyncIrp(GET_NEXT_DEVICE_OBJECT(DevObj), Irp, TRUE); if (NT_SUCCESS(status)) { PDEVICE_CAPABILITIES devcaps; devcaps = irpsp->Parameters.DeviceCapabilities.Capabilities; if (devcaps != NULL) { SYSTEM_POWER_STATE i; // // This device is built-in to the system, so it should // be impossible to surprise remove this device, but // we will handle it anyway. // devcaps->SurpriseRemovalOK = TRUE; // // While the underlying serial bus might be able to // wake the machine from low power (via wake on ring), // the tablet cannot. // devcaps->SystemWake = PowerSystemUnspecified; devcaps->DeviceWake = PowerDeviceUnspecified; devcaps->WakeFromD0 = devcaps->WakeFromD1 = devcaps->WakeFromD2 = devcaps->WakeFromD3 = FALSE; devcaps->DeviceState[PowerSystemWorking] = PowerDeviceD0; for (i = PowerSystemSleeping1; i < PowerSystemMaximum; i++) { devcaps->DeviceState[i] = PowerDeviceD3; } } } IoCompleteRequest(Irp, IO_NO_INCREMENT); break; default: fSkipIt = TRUE; break; } if (fSkipIt) { IoSkipCurrentIrpStackLocation(Irp); ENTER(2, (".IoCallDriver(DevObj=%p,Irp=%p)\n", GET_NEXT_DEVICE_OBJECT(DevObj), Irp)); status = IoCallDriver(GET_NEXT_DEVICE_OBJECT(DevObj), Irp); EXIT(2, (".IoCallDriver=%x\n", status)); } if (irpsp->MinorFunction == IRP_MN_REMOVE_DEVICE) { // // Wait for the remove lock to free. // IoReleaseRemoveLockAndWait(&devext->RemoveLock, Irp); } else { IoReleaseRemoveLock(&devext->RemoveLock, Irp); } } EXIT(1, ("=%x\n", status)); return status; } //HbutPnp /***************************************************************************** * * @doc EXTERNAL * * @func NTSTATUS | HbutPower | The power dispatch routine for this driver. * * @parm IN PDEVICE_OBJECT | DevObj | Points to the device object. * @parm IN PIRP | Irp | Points to an I/O request packet. * * @rvalue SUCCESS | returns STATUS_SUCCESS * @rvalue FAILURE | returns NT status code * *****************************************************************************/ NTSTATUS EXTERNAL HbutPower( IN PDEVICE_OBJECT DevObj, IN PIRP Irp ) { PROCNAME("HbutPower") NTSTATUS status; PDEVICE_EXTENSION devext; PAGED_CODE(); ENTER(1, ("(DevObj=%p,Irp=%p,Minor=%s)\n", DevObj, Irp, LookupName(IoGetCurrentIrpStackLocation(Irp)->MinorFunction, PowerMinorFnNames))); devext = GET_MINIDRIVER_DEVICE_EXTENSION(DevObj); status = IoAcquireRemoveLock(&devext->RemoveLock, Irp); if (!NT_SUCCESS(status)) { // // Someone sent us another power IRP after removed // ERRPRINT(("received Power IRP after device was removed\n")); Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); } else { IoSkipCurrentIrpStackLocation(Irp); ENTER(2, (".PoCallDriver(DevObj=%p,Irp=%p)\n", GET_NEXT_DEVICE_OBJECT(DevObj), Irp)); status = PoCallDriver(GET_NEXT_DEVICE_OBJECT(DevObj), Irp); EXIT(2, (".PoCallDriver=%x\n", status)); IoReleaseRemoveLock(&devext->RemoveLock, Irp); } EXIT(1, ("=%x\n", status)); return status; } //HbutPower /***************************************************************************** * * @doc INTERNAL * * @func NTSTATUS | StartDevice | * 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 | DevObj | Points to the device object. * @parm IN PIRP | Irp | Points to an I/O request packet. * * @rvalue SUCCESS | returns STATUS_SUCCESS * @rvalue FAILURE | returns NT status code * *****************************************************************************/ NTSTATUS INTERNAL StartDevice( IN PDEVICE_OBJECT DevObj, IN PIRP Irp ) { PROCNAME("StartDevice") NTSTATUS status = STATUS_SUCCESS; PIO_STACK_LOCATION irpsp; PDEVICE_EXTENSION devext; PAGED_CODE(); ENTER(2, ("(DevObj=%p,Irp=%p)\n", DevObj, Irp)); irpsp = IoGetCurrentIrpStackLocation(Irp); devext = GET_MINIDRIVER_DEVICE_EXTENSION(DevObj); if (irpsp->Parameters.StartDevice.AllocatedResourcesTranslated == NULL) { ERRPRINT(("no resources is allocated to the button device!\n")); status = STATUS_INSUFFICIENT_RESOURCES; } else { ULONG Count; PCM_PARTIAL_RESOURCE_DESCRIPTOR pDesc; Count = 0; pDesc = RtlUnpackPartialDesc( CmResourceTypePort, irpsp->Parameters.StartDevice.AllocatedResourcesTranslated, &Count); if (pDesc == NULL) { ERRPRINT(("no allocated port resources!\n")); status = STATUS_INSUFFICIENT_RESOURCES; } else { devext->IORes = *pDesc; Count = 0; pDesc = RtlUnpackPartialDesc( CmResourceTypeInterrupt, irpsp->Parameters.StartDevice.AllocatedResourcesTranslated, &Count); if (pDesc == NULL) { ERRPRINT(("no allocated IRQ resources!\n")); status = STATUS_INSUFFICIENT_RESOURCES; } else { int i; UCHAR Buttons; ASSERT(!(devext->dwfHBut & HBUTF_INTERRUPT_CONNECTED)); devext->IRQRes = *pDesc; // // To determine potential stuck buttons, read the buttons // up to 5 times for any button down bits without an interrupt. // for (i = 0; i < STUCK_DETECTION_RETRIES; ++i) { Buttons = READBUTTONSTATE(devext); if (!(Buttons & BUTTON_INTERRUPT_MASK)) { devext->StuckButtonsMask |= Buttons & BUTTON_STATUS_MASK; } } devext->StuckButtonsMask = Buttons & BUTTON_STATUS_MASK; DBGPRINT(1, ("StuckButtonsMask=%x\n", devext->StuckButtonsMask)); status = IoConnectInterrupt( &devext->InterruptObject, OemInterruptServiceRoutine, devext, NULL, devext->IRQRes.u.Interrupt.Vector, (KIRQL)devext->IRQRes.u.Interrupt.Level, (KIRQL)devext->IRQRes.u.Interrupt.Level, (devext->IRQRes.Flags & CM_RESOURCE_INTERRUPT_LATCHED)? Latched: LevelSensitive, devext->IRQRes.ShareDisposition == CmResourceShareShared, devext->IRQRes.u.Interrupt.Affinity, FALSE); if (NT_SUCCESS(status)) { devext->dwfHBut |= HBUTF_INTERRUPT_CONNECTED; DBGPRINT(3, ("IO(Start=0x%x%x,Len=0x%x), IRQ(Level=%d,Vector=0x%x,Affinity=%d)\n", devext->IORes.u.Port.Start.HighPart, devext->IORes.u.Port.Start.LowPart, devext->IORes.u.Port.Length, devext->IRQRes.u.Interrupt.Level, devext->IRQRes.u.Interrupt.Vector, devext->IRQRes.u.Interrupt.Affinity)); } } } } EXIT(2, ("=%x\n", status)); return status; } //StartDevice /***************************************************************************** * * @doc INTERNAL * * @func VOID | RemoveDevice | FDO Remove routine * * @parm IN PDEVICE_OBJECT | DevObj | Points to the device object. * @parm IN PIRP | Irp | Points to an I/O request packet. * *****************************************************************************/ VOID INTERNAL RemoveDevice( PDEVICE_OBJECT DevObj, PIRP Irp ) { PROCNAME("RemoveDevice") PDEVICE_EXTENSION devext; PAGED_CODE(); ENTER(2, ("(DevObj=%p,Irp=%p)\n", DevObj, Irp)); devext = GET_MINIDRIVER_DEVICE_EXTENSION(DevObj); ASSERT(devext->dwfHBut & HBUTF_DEVICE_REMOVED); if (devext->dwfHBut & HBUTF_INTERRUPT_CONNECTED) { IoDisconnectInterrupt(devext->InterruptObject); devext->dwfHBut &= ~HBUTF_INTERRUPT_CONNECTED; } if (devext->dwfHBut & HBUTF_DEBOUNCE_TIMER_SET) { KeCancelTimer(&devext->DebounceTimer); } #ifdef DEBUG ExAcquireFastMutex(&gmutexDevExtList); RemoveEntryList(&devext->List); ExReleaseFastMutex (&gmutexDevExtList); #endif EXIT(2, ("!\n")); return; } //RemoveDevice /***************************************************************************** * * @doc INTERNAL * * @func NTSTATUS | SendSyncIrp | * Send an IRP synchronously down the stack. * * @parm IN PDEVICE_OBJECT | DevObj | Points to the device object. * @parm IN PIRP | Irp | Points to the IRP. * @parm IN BOOLEAN | fCopyToNext | if TRUE, copy the irpsp to next location. * * @rvalue SUCCESS | returns STATUS_SUCCESS * @rvalue FAILURE | returns NT status code * *****************************************************************************/ NTSTATUS INTERNAL SendSyncIrp( IN PDEVICE_OBJECT DevObj, IN PIRP Irp, IN BOOLEAN fCopyToNext ) { PROCNAME("SendSyncIrp") NTSTATUS status; PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(Irp); KEVENT event; PAGED_CODE(); ENTER(2, ("(DevObj=%p,Irp=%p,fCopyToNext=%x,MajorFunc=%s)\n", DevObj, Irp, fCopyToNext, LookupName(irpsp->MajorFunction, MajorFnNames))); KeInitializeEvent(&event, SynchronizationEvent, FALSE); if (fCopyToNext) { IoCopyCurrentIrpStackLocationToNext(Irp); } IoSetCompletionRoutine(Irp, IrpCompletion, &event, TRUE, TRUE, TRUE); if (irpsp->MajorFunction == IRP_MJ_POWER) { ENTER(2, (".PoCallDriver(DevObj=%p,Irp=%p)\n", DevObj, Irp)); status = PoCallDriver(DevObj, Irp); EXIT(2, (".IoCallDriver=%x\n", status)); } else { ENTER(2, (".IoCallDriver(DevObj=%p,Irp=%p)\n", DevObj, Irp)); status = IoCallDriver(DevObj, Irp); EXIT(2, (".IoCallDriver=%x\n", status)); } if (status == STATUS_PENDING) { status = KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); } if (NT_SUCCESS(status)) { status = Irp->IoStatus.Status; } EXIT(2, ("=%x\n", status)); return status; } //SendSyncIrp /***************************************************************************** * * @doc INTERNAL * * @func NTSTATUS | IrpCompletion | Completion routine for all IRPs. * * @parm IN PDEVICE_OBJECT | DevObj | Points to the device object. * @parm IN PIRP | Irp | Points to an I/O request packet. * @parm IN PKEVENT | Event | Points to the event to notify. * * @rvalue STATUS_MORE_PROCESSING_REQUIRED | We want the IRP back * *****************************************************************************/ NTSTATUS INTERNAL IrpCompletion( IN PDEVICE_OBJECT DevObj, IN PIRP Irp, IN PKEVENT Event ) { PROCNAME("IrpCompletion") ENTER(2, ("(DevObj=%p,Irp=%p,Event=%p)\n", DevObj, Irp, Event)); UNREFERENCED_PARAMETER(DevObj); KeSetEvent(Event, 0, FALSE); /* * If the lower driver returned PENDING, mark our stack location as * pending also. This prevents the IRP's thread from being freed if * the client's call returns pending. */ if (Irp->PendingReturned) { IoMarkIrpPending(Irp); } EXIT(2, ("=%x\n", STATUS_MORE_PROCESSING_REQUIRED)); return STATUS_MORE_PROCESSING_REQUIRED; } //IrpCompletion