VOID UfxDevice_EvtDeviceRemoteWakeupSignal ( _In_ UFXDEVICE UfxDevice ) /*++ Routine Description: Signals Remote Wakeup to the Host by issuing a link state change command. It acquires and releases the power reference to ensure a valid power state before accessing the device. Arguments: UfxDevice - UFXDEVICE object representing the device. --*/ { NTSTATUS Status; PUFXDEVICE_CONTEXT DeviceContext; PAGED_CODE(); TraceEntry(); DeviceContext = UfxDeviceGetContext(UfxDevice); // // Stop Idle to ensure the device is in working state // Status = WdfDeviceStopIdle(DeviceContext->FdoWdfDevice, TRUE); if (!NT_SUCCESS(Status)) { TraceError("Failed to stop idle %!STATUS!", Status); goto End; } // // Issue a Link State Change Request. // // // #### TODO: Insert code to issue a link state change on the controller #### // WdfDeviceResumeIdle(DeviceContext->FdoWdfDevice); End: UfxDeviceEventComplete(UfxDevice, Status); TraceExit(); }
VOID SerialFileCloseWorker( IN WDFDEVICE Device ) { ULONG flushCount; // // This "timer value" is used to wait 10 character times // after the hardware is empty before we actually "run down" // all of the flow control/break junk. // LARGE_INTEGER tenCharDelay; // // Holds a character time. // LARGE_INTEGER charTime; PSERIAL_DEVICE_EXTENSION extension = SerialGetDeviceExtension(Device); PSERIAL_INTERRUPT_CONTEXT interruptContext = SerialGetInterruptContext(extension->WdfInterrupt); SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_CREATE_CLOSE, "In SerialEvtFileClose %wZ\n", &extension->DeviceName); // // Acquire the interrupt state lock. // WdfWaitLockAcquire(interruptContext->InterruptStateLock, NULL); // // If the Interrupts are connected, then the hardware state has to be // cleaned up now. Note that the EvtFileClose callback gets called for // an open file object even though the interrupts have been disabled // possibly due to a Surprise Remove PNP event. In such a case, the // Interrupt object should not be used. // if (interruptContext->IsInterruptConnected) { charTime.QuadPart = -SerialGetCharTime(extension).QuadPart; // // Do this now so that if the isr gets called it won't do anything // to cause more chars to get sent. We want to run down the hardware. // SetDeviceIsOpened(extension, FALSE, FALSE); // // Synchronize with the isr to turn off break if it // is already on. // WdfInterruptSynchronize( extension->WdfInterrupt, SerialTurnOffBreak, extension ); // // Wait a reasonable amount of time (20 * fifodepth) until all characters // have been emptied out of the hardware. // for (flushCount = (20 * 16); flushCount != 0; flushCount--) { if ((READ_LINE_STATUS(extension, extension->Controller) & (SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) != (SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) { KeDelayExecutionThread(KernelMode, FALSE, &charTime); } else { break; } } if (flushCount == 0) { SerialMarkHardwareBroken(extension); } // // Synchronize with the ISR to let it know that interrupts are // no longer important. // WdfInterruptSynchronize( extension->WdfInterrupt, SerialMarkClose, extension ); // // If the driver has automatically transmitted an Xoff in // the context of automatic receive flow control then we // should transmit an Xon. // if (extension->RXHolding & SERIAL_RX_XOFF) { // // Loop until the holding register is empty. // while (!(READ_LINE_STATUS(extension, extension->Controller) & SERIAL_LSR_THRE)) { KeDelayExecutionThread( KernelMode, FALSE, &charTime ); } WRITE_TRANSMIT_HOLDING(extension, extension->Controller, extension->SpecialChars.XonChar ); // // Wait a reasonable amount of time for the characters // to be emptied out of the hardware. // for (flushCount = (20 * 16); flushCount != 0; flushCount--) { if ((READ_LINE_STATUS(extension, extension->Controller) & (SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) != (SERIAL_LSR_THRE | SERIAL_LSR_TEMT)) { KeDelayExecutionThread(KernelMode, FALSE, &charTime); } else { break; } } if (flushCount == 0) { SerialMarkHardwareBroken(extension); } } // // The hardware is empty. Delay 10 character times before // shut down all the flow control. // tenCharDelay.QuadPart = charTime.QuadPart * 10; KeDelayExecutionThread( KernelMode, TRUE, &tenCharDelay ); #pragma prefast(suppress: __WARNING_INFERRED_IRQ_TOO_LOW, "This warning is because we are calling interrupt synchronize routine directly.") SerialClrDTR(extension->WdfInterrupt, extension); // // We have to be very careful how we clear the RTS line. // Transmit toggling might have been on at some point. // // We know that there is nothing left that could start // out the "polling" execution path. We need to // check the counter that indicates that the execution // path is active. If it is then we loop delaying one // character time. After each delay we check to see if // the counter has gone to zero. When it has we know that // the execution path should be just about finished. We // make sure that we still aren't in the routine that // synchronized execution with the ISR by synchronizing // ourselve with the ISR. // if (extension->CountOfTryingToLowerRTS) { do { #pragma prefast(suppress: __WARNING_INFERRED_IRQ_TOO_HIGH, "This warning is due to suppressing the previous one.") KeDelayExecutionThread( KernelMode, FALSE, &charTime ); } while (extension->CountOfTryingToLowerRTS); // // The execution path should no longer exist that // is trying to push down the RTS. Well just // make sure it's down by falling through to // code that forces it down. // } #pragma prefast(suppress: __WARNING_INFERRED_IRQ_TOO_LOW, "This warning is because we are calling interrupt synchronize routine directly.") SerialClrRTS(extension->WdfInterrupt, extension); // // Clean out the holding reasons (since we are closed). // extension->RXHolding = 0; extension->TXHolding = 0; // // Mark device as not busy for WMI // extension->WmiCommData.IsBusy = FALSE; } // // Release the Interrupt state lock. // WdfWaitLockRelease(interruptContext->InterruptStateLock); // // All is done. The port has been disabled from interrupting // so there is no point in keeping the memory around. // extension->BufferSize = 0; if (extension->InterruptReadBuffer != NULL) { ExFreePool(extension->InterruptReadBuffer); } extension->InterruptReadBuffer = NULL; // // Make sure the wake is disabled. // ASSERT(!extension->IsWakeEnabled); SerialDrainTimersAndDpcs(extension); SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_CREATE_CLOSE, "DPC's drained:\n"); // // It's fine for the device to be powered off if there are no open handles. // WdfDeviceResumeIdle(Device); // // It's okay to allow the device to be stopped or removed. // // Note to anyone copying this sample as a starting point: // // This works in this driver simply because this driver supports exactly // one open handle at a time. If it supported more, then it would need // counting logic to determine when all the reasons for failing Stop/Remove // were gone. // WdfDeviceSetStaticStopRemove(Device, TRUE); return; }
NTSTATUS UfxDeviceStopOrResumeIdle ( _In_ UFXDEVICE Device, _In_ USBFN_DEVICE_STATE UsbState, _In_ USBFN_PORT_TYPE UsbPort ) /*++ Routine Description: Examines the device USB state and port type and determines if it needs to stop or resume idle. Arguments: UfxDevice - UFXDEVICE object representing the device. Return Value: STATUS_SUCCESS on success, or an appropirate NTSTATUS message on failure. --*/ { PUFXDEVICE_CONTEXT DeviceContext; PCONTROLLER_CONTEXT ControllerContext; NTSTATUS Status; BOOLEAN NeedPower; DEVICE_POWER_STATE DxState = PowerDeviceD3; PAGED_CODE(); TraceEntry(); DeviceContext = UfxDeviceGetContext(Device); ControllerContext = DeviceGetControllerContext(DeviceContext->FdoWdfDevice); switch (UsbState) { case UsbfnDeviceStateAttached: __fallthrough; case UsbfnDeviceStateDefault: switch (UsbPort) { case UsbfnStandardDownstreamPort: __fallthrough; case UsbfnChargingDownstreamPort: __fallthrough; case UsbfnUnknownPort: NeedPower = TRUE; break; case UsbfnDedicatedChargingPort: case UsbfnProprietaryDedicatedChargingPort: NeedPower = FALSE; DxState = PowerDeviceD2; break; default: NeedPower = FALSE; } break; case UsbfnDeviceStateSuspended: NeedPower = FALSE; DxState = PowerDeviceD2; break; case UsbfnDeviceStateAddressed: __fallthrough; case UsbfnDeviceStateConfigured: NeedPower = TRUE; break; default: NeedPower = FALSE; } // // Determine if our lowest idle state has changed, and set it if so // if (DxState != ControllerContext->IdleSettings.DxState) { ControllerContext->IdleSettings.DxState = DxState; Status = WdfDeviceAssignS0IdleSettings( DeviceContext->FdoWdfDevice, &ControllerContext->IdleSettings); CHK_NT_MSG(Status, "Failed to update device idle settings"); } if (NeedPower && DeviceContext->IsIdle) { // // We don't want to update the USB state /port until we stop idle to // prevent D0 -> DX path from reading the wrong state. // TraceInformation("Stopping idle"); Status = WdfDeviceStopIdle(DeviceContext->FdoWdfDevice, TRUE); CHK_NT_MSG(Status, "Failed to stop idle"); DeviceContext->UsbState = UsbState; DeviceContext->UsbPort = UsbPort; DeviceContext->IsIdle = FALSE; } else if (!NeedPower && !DeviceContext->IsIdle) { // // We need to update USB state / port before resume idle to ensure // D0 -> DX path reads the correct state. // DeviceContext->UsbState = UsbState; DeviceContext->UsbPort = UsbPort; DeviceContext->IsIdle = TRUE; TraceInformation("Resuming idle"); WdfDeviceResumeIdle(DeviceContext->FdoWdfDevice); } else { TraceInformation("No idle action"); DeviceContext->UsbState = UsbState; DeviceContext->UsbPort = UsbPort; } Status = STATUS_SUCCESS; End: TraceExit(); return Status; }