VOID
SerialTimeoutImmediate(
    IN WDFTIMER Timer
    )
{

    PSERIAL_DEVICE_EXTENSION Extension = NULL;

    Extension = SerialGetDeviceExtension(WdfTimerGetParentObject(Timer));

    SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS, ">SerialTimeoutImmediate(%p)\n",
                     Extension);

    SerialTryToCompleteCurrent(
        Extension,
        SerialGrabImmediateFromIsr,
        STATUS_TIMEOUT,
        &Extension->CurrentImmediateRequest,
        NULL,
        NULL,
        Extension->ImmediateTotalTimer,
        NULL,
        SerialGetNextImmediate,
        SERIAL_REF_TOTAL_TIMER
        );

    SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS, "<SerialTimeoutImmediate\n");
}
Exemple #2
0
VOID
SerialEvtDriverContextCleanup(
    IN WDFDRIVER Driver
    )
/*++
Routine Description:

    Free all the resources allocated in DriverEntry.

Arguments:

    Driver - handle to a WDF Driver object.

Return Value:

    VOID.

--*/
{
    UNREFERENCED_PARAMETER(Driver);

    PAGED_CODE ();

    SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_INIT,
                     "--> SerialEvtDriverContextCleanup\n");

    //
    // Stop WPP Tracing
    //
    WPP_CLEANUP( WdfDriverWdmGetDriverObject(Driver) );

    SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_INIT,
                     "<-- SerialEvtDriverContextCleanup\n");

}
VOID
SerialCompleteImmediate(
    IN WDFDPC Dpc
    )

{

    PSERIAL_DEVICE_EXTENSION Extension = NULL;

    Extension = SerialGetDeviceExtension(WdfDpcGetParentObject(Dpc));

    SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS, ">SerialCompleteImmediate(%p)\n",
                     Extension);

    SerialTryToCompleteCurrent(
        Extension,
        NULL,
        STATUS_SUCCESS,
        &Extension->CurrentImmediateRequest,
        NULL,
        NULL,
        Extension->ImmediateTotalTimer,
        NULL,
        SerialGetNextImmediate,
        SERIAL_REF_ISR
        );

    SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS, "<SerialCompleteImmediate\n");

}
BOOLEAN
SerialPutRegistryKeyValue(
    IN WDFDEVICE  WdfDevice,
    _In_ PCWSTR   Name,
    IN ULONG      Value
    )
/*++

Routine Description:

    Can be used to write any REG_DWORD registry value stored
    under Device Parameter.

Arguments:


Return Value:

   TRUE - if write is successful
   FALSE - otherwise

--*/
{
    WDFKEY          hKey = NULL;
    NTSTATUS        status;
    BOOLEAN         retValue = FALSE;
    UNICODE_STRING valueName;

    PAGED_CODE();

    SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_PNP,  "Entered PciDrvWriteRegistryValue\n");

    //
    // write the value out to the registry
    //
    status = WdfDeviceOpenRegistryKey(WdfDevice,
                                      PLUGPLAY_REGKEY_DEVICE,
                                      STANDARD_RIGHTS_ALL,
                                      WDF_NO_OBJECT_ATTRIBUTES,
                                      &hKey);

    if (NT_SUCCESS (status)) {

        RtlInitUnicodeString(&valueName,Name);

        status = WdfRegistryAssignULong (hKey,
                                  &valueName,
                                  Value
                                );

        if (NT_SUCCESS (status)) {
            retValue = TRUE;
        }

        WdfRegistryClose(hKey);
    }

    return retValue;

}
VOID
SerialEvtIoResume(
    IN WDFQUEUE   Queue,
    IN WDFREQUEST Request
    )
/*++

Routine Description:

     This callback is invoked for every request pending in the driver - in-flight
     request - to notify that the hardware is ready for contiuing the processing
     of the request.

Arguments:

    Queue - Queue the request currently belongs to
    Request - Request that is currently out of queue and being processed by the driver

Return Value:

    None.

--*/
{
    PREQUEST_CONTEXT reqContext;

    UNREFERENCED_PARAMETER(Queue);

    SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE,
            "--> SerialEvtIoResume %p \n", Request);

    reqContext = SerialGetRequestContext(Request);

    //
    // If we unmarked cancelable on suspend, let us mark it cancelable again.
    //
    if (reqContext->MarkCancelableOnResume) {
        SerialSetCancelRoutine(Request, reqContext->CancelRoutine);
        reqContext->MarkCancelableOnResume = FALSE;
    }

    SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE,
        "<-- SerialEvtIoResume \n");
}
VOID
SerialEvtDeviceFileCreate (
    IN WDFDEVICE     Device,
    IN WDFREQUEST    Request,
    IN WDFFILEOBJECT FileObject
    )
/*++

Routine Description:

    The framework calls a driver's EvtDeviceFileCreate callback
    when the framework receives an IRP_MJ_CREATE request.
    The system sends this request when a user application opens the
    device to perform an I/O operation, such as reading or writing a file.
    This callback is called synchronously, in the context of the thread
    that created the IRP_MJ_CREATE request.

Arguments:

    Device - Handle to a framework device object.
    FileObject - Pointer to fileobject that represents the open handle.
    CreateParams - Copy of the Create IO_STACK_LOCATION

Return Value:

   VOID.

--*/
{
    NTSTATUS status;
    PSERIAL_DEVICE_EXTENSION extension = SerialGetDeviceExtension (Device);

    UNREFERENCED_PARAMETER(FileObject);

    PAGED_CODE();

    SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_CREATE_CLOSE,
                     "SerialEvtDeviceFileCreate %wZ\n", &extension->DeviceName);

    status = SerialDeviceFileCreateWorker(Device);

    //
    // Complete the WDF request.
    //
    WdfRequestComplete(Request, status);

    return;

}
Exemple #7
0
NTSTATUS
SerialEvtInterruptDisable(
    IN WDFINTERRUPT  Interrupt,
    IN WDFDEVICE     AssociatedDevice
    )
/*++

Routine Description:

    This event is called before the Framework moves the device to D1, D2 or D3
    and before EvtDeviceD0Exit.  The driver should disable its interrupt here.

    This function will be called at the device's assigned interrupt
    IRQL (DIRQL.)

Arguments:

    Interrupt - Handle to a Framework interrupt object.

    AssociatedDevice - Handle to a Framework device object.

Return Value:

    BOOLEAN - TRUE indicates that the interrupt was successfully disabled.

--*/
{
    UNREFERENCED_PARAMETER(Interrupt);
    UNREFERENCED_PARAMETER(AssociatedDevice);

    SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_PNP, "--> SerialEvtInterruptDisable\n");

    SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_PNP, "<-- SerialEvtInterruptDisable\n");

    return STATUS_SUCCESS;
}
VOID
SerialSaveDeviceState(IN PSERIAL_DEVICE_EXTENSION PDevExt)
/*++

Routine Description:

    This routine saves the device state of the UART

Arguments:

    PDevExt - Pointer to the device extension for the devobj to save the state
              for.

Return Value:

    VOID


--*/
{
   PSERIAL_DEVICE_STATE pDevState = &PDevExt->DeviceState;

   PAGED_CODE();

   SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_POWER, "Entering SerialSaveDeviceState\n");

   //
   // Read necessary registers direct
   //

   pDevState->IER = READ_INTERRUPT_ENABLE(PDevExt, PDevExt->Controller);
   pDevState->MCR = READ_MODEM_CONTROL(PDevExt, PDevExt->Controller);
   pDevState->LCR = READ_LINE_CONTROL(PDevExt, PDevExt->Controller);

   SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_POWER, "Leaving SerialSaveDeviceState\n");
}
NTSTATUS
SerialWdmDeviceFileCreate (
    IN WDFDEVICE Device,
    IN PIRP Irp
    )
/*++

Routine Description:

    This is the dispatch routine for IRP_MJ_CREATE. The system sends this
    request when a user application opens the device to perform an I/O
    operation, such as reading or writing a file.

Arguments:

    DeviceObject - Pointer to the device object for this device
    Irp - Pointer to the IRP for the current request

Return Value:

   NT status code

--*/
{
    NTSTATUS status;
    PSERIAL_DEVICE_EXTENSION extension = SerialGetDeviceExtension (Device);

    SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_CREATE_CLOSE,
                     "SerialWdmDeviceFileCreate %wZ\n", &extension->DeviceName);

    status = SerialDeviceFileCreateWorker(Device);

    //
    // Complete the WDM request.
    //
    Irp->IoStatus.Information = 0L;
    Irp->IoStatus.Status = status;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    return status;
}
BOOLEAN
SerialGetFdoRegistryKeyValue(
    IN PWDFDEVICE_INIT  DeviceInit,
    _In_ PCWSTR         Name,
    OUT PULONG          Value
    )
/*++

Routine Description:

    Can be used to read any REG_DWORD registry value stored
    under Device Parameter.

Arguments:

    FdoData - pointer to the device extension
    Name - Name of the registry value
    Value -


Return Value:

   TRUE if successful
   FALSE if not present/error in reading registry

--*/
{
    WDFKEY      hKey = NULL;
    NTSTATUS    status;
    BOOLEAN     retValue = FALSE;
    UNICODE_STRING valueName;

    PAGED_CODE();

    SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_PNP,
                     "-->SerialGetFdoRegistryKeyValue\n");

    *Value = 0;

    status = WdfFdoInitOpenRegistryKey(DeviceInit,
                                  PLUGPLAY_REGKEY_DEVICE,
                                  STANDARD_RIGHTS_ALL,
                                  WDF_NO_OBJECT_ATTRIBUTES,
                                  &hKey);

    if (NT_SUCCESS (status)) {

        RtlInitUnicodeString(&valueName,Name);

        status = WdfRegistryQueryULong (hKey, &valueName, Value);

        if (NT_SUCCESS (status)) {
            retValue = TRUE;
        }

        WdfRegistryClose(hKey);
    }

    SerialDbgPrintEx(TRACE_LEVEL_VERBOSE, DBG_PNP,
                     "<--SerialGetFdoRegistryKeyValue %ws %d \n",
                            Name, *Value);

    return retValue;
}
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
SerialEvtDeviceD0Entry(
    IN  WDFDEVICE Device,
    IN  WDF_POWER_DEVICE_STATE PreviousState
    )
/*++

Routine Description:

    EvtDeviceD0Entry event callback must perform any operations that are
    necessary before the specified device is used.  It will be called every
    time the hardware needs to be (re-)initialized.  This includes after
    IRP_MN_START_DEVICE, IRP_MN_CANCEL_STOP_DEVICE, IRP_MN_CANCEL_REMOVE_DEVICE,
    IRP_MN_SET_POWER-D0.

    This function is not marked pageable because this function is in the
    device power up path. When a function is marked pagable and the code
    section is paged out, it will generate a page fault which could impact
    the fast resume behavior because the client driver will have to wait
    until the system drivers can service this page fault.

    This function runs at PASSIVE_LEVEL, even though it is not paged.  A
    driver can optionally make this function pageable if DO_POWER_PAGABLE
    is set.  Even if DO_POWER_PAGABLE isn't set, this function still runs
    at PASSIVE_LEVEL.  In this case, though, the function absolutely must
    not do anything that will cause a page fault.

Arguments:

    Device - Handle to a framework device object.

    PreviousState - Device power state which the device was in most recently.
        If the device is being newly started, this will be
        PowerDeviceUnspecified.

Return Value:

    NTSTATUS

--*/
{
    PSERIAL_DEVICE_EXTENSION deviceExtension;
    PSERIAL_DEVICE_STATE pDevState;
    SHORT divisor;
    SERIAL_IOCTL_SYNC S;

    SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_POWER,
                "-->SerialEvtDeviceD0Entry - coming from %s\n", DbgDevicePowerString(PreviousState));

    deviceExtension = SerialGetDeviceExtension (Device);
    pDevState = &deviceExtension->DeviceState;

    //
    // Restore the state of the UART.  First, that involves disabling
    // interrupts both via OUT2 and IER.
    //

    WRITE_MODEM_CONTROL(deviceExtension, deviceExtension->Controller, 0);
    DISABLE_ALL_INTERRUPTS(deviceExtension, deviceExtension->Controller);

    //
    // Set the baud rate
    //

    SerialGetDivisorFromBaud(deviceExtension->ClockRate, deviceExtension->CurrentBaud, &divisor);
    S.Extension = deviceExtension;
    S.Data = (PVOID) (ULONG_PTR) divisor;

#pragma prefast(suppress: __WARNING_INFERRED_IRQ_TOO_LOW, "PFD warning that we are calling interrupt synchronize routine directly. Suppress it because interrupt is disabled above.")
    SerialSetBaud(deviceExtension->WdfInterrupt, &S);

    //
    // Reset / Re-enable the FIFO's
    //

    if (deviceExtension->FifoPresent) {
       WRITE_FIFO_CONTROL(deviceExtension, deviceExtension->Controller, (UCHAR)0);
       READ_RECEIVE_BUFFER(deviceExtension, deviceExtension->Controller);
       WRITE_FIFO_CONTROL(deviceExtension, deviceExtension->Controller,
                          (UCHAR)(SERIAL_FCR_ENABLE | deviceExtension->RxFifoTrigger
                                  | SERIAL_FCR_RCVR_RESET
                                  | SERIAL_FCR_TXMT_RESET));
    } else {
       WRITE_FIFO_CONTROL(deviceExtension, deviceExtension->Controller, (UCHAR)0);
    }

    //
    // Restore a couple more registers
    //

    WRITE_INTERRUPT_ENABLE(deviceExtension, deviceExtension->Controller, pDevState->IER);
    WRITE_LINE_CONTROL(deviceExtension, deviceExtension->Controller, pDevState->LCR);

    //
    // Clear out any stale interrupts
    //

    READ_INTERRUPT_ID_REG(deviceExtension, deviceExtension->Controller);
    READ_LINE_STATUS(deviceExtension, deviceExtension->Controller);
    READ_MODEM_STATUS(deviceExtension, deviceExtension->Controller);

    //
    // TODO:  move this code to EvtInterruptEnable.
    //

    if (deviceExtension->DeviceState.Reopen == TRUE) {
       SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_POWER, "Reopening device\n");

       SetDeviceIsOpened(deviceExtension, TRUE, FALSE);

       //
       // This enables interrupts on the device!
       //

       WRITE_MODEM_CONTROL(deviceExtension, deviceExtension->Controller,
                           (UCHAR)(pDevState->MCR | SERIAL_MCR_OUT2));

       //
       // Refire the state machine
       //

       DISABLE_ALL_INTERRUPTS(deviceExtension, deviceExtension->Controller);
       ENABLE_ALL_INTERRUPTS(deviceExtension, deviceExtension->Controller);
    }

    SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_POWER, "<--SerialEvtDeviceD0Entry\n");

    return STATUS_SUCCESS;
}
NTSTATUS
SerialEvtDeviceD0Exit(
    IN  WDFDEVICE Device,
    IN  WDF_POWER_DEVICE_STATE TargetState
    )
/*++

Routine Description:

   EvtDeviceD0Exit event callback must perform any operations that are
   necessary before the specified device is moved out of the D0 state.  If the
   driver needs to save hardware state before the device is powered down, then
   that should be done here.

   This function runs at PASSIVE_LEVEL, though it is generally not paged.  A
   driver can optionally make this function pageable if DO_POWER_PAGABLE is set.

   Even if DO_POWER_PAGABLE isn't set, this function still runs at
   PASSIVE_LEVEL.  In this case, though, the function absolutely must not do
   anything that will cause a page fault.

Arguments:

    Device - Handle to a framework device object.

    TargetState - Device power state which the device will be put in once this
        callback is complete.

Return Value:

    NTSTATUS

--*/
{
    PSERIAL_DEVICE_EXTENSION deviceExtension;

    SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_POWER,
                "-->SerialEvtDeviceD0Exit - moving to %s\n", DbgDevicePowerString(TargetState));

    PAGED_CODE();

    deviceExtension = SerialGetDeviceExtension (Device);

    if (deviceExtension->DeviceIsOpened == TRUE) {
        LARGE_INTEGER charTime;

        SetDeviceIsOpened(deviceExtension, FALSE, TRUE);

        charTime.QuadPart = -SerialGetCharTime(deviceExtension).QuadPart;

        //
        // Shut down the chip
        //

        SerialDisableUART(deviceExtension);

        //
        // Drain the device
        //

        SerialDrainUART(deviceExtension, &charTime);

        //
        // Save the device state
        //

        SerialSaveDeviceState(deviceExtension);
    }
    else
    {
        SetDeviceIsOpened(deviceExtension, FALSE, FALSE);
    }

    SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_POWER, "<--SerialEvtDeviceD0Exit\n");

    return STATUS_SUCCESS;
}
Exemple #14
0
NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath
    )
/*++

Routine Description:

    The entry point that the system point calls to initialize
    any driver.

Arguments:

    DriverObject - Just what it says,  really of little use
    to the driver itself, it is something that the IO system
    cares more about.

    PathToRegistry - points to the entry for this driver
    in the current control set of the registry.

Return Value:

    Always STATUS_SUCCESS

--*/

{
    WDF_DRIVER_CONFIG config;
    WDFDRIVER hDriver;
    NTSTATUS status;
    WDF_OBJECT_ATTRIBUTES attributes;

    //
    // Initialize WPP Tracing
    //
    WPP_INIT_TRACING( DriverObject, RegistryPath );

    SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_INIT,
                    "Serial Sample (WDF Version) - Built %s %s\n",
                     __DATE__, __TIME__);
    //
    // Register a cleanup callback so that we can call WPP_CLEANUP when
    // the framework driver object is deleted during driver unload.
    //
    WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
    attributes.EvtCleanupCallback = SerialEvtDriverContextCleanup;

    WDF_DRIVER_CONFIG_INIT(&config, SerialEvtDeviceAdd);

    status = WdfDriverCreate(DriverObject,
                           RegistryPath,
                           &attributes,
                           &config,
                           &hDriver);
    if (!NT_SUCCESS(status)) {
        SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_INIT,
                         "WdfDriverCreate failed with status 0x%x\n",
                         status);
        //
        // Cleanup tracing here because DriverContextCleanup will not be called
        // as we have failed to create WDFDRIVER object itself.
        // Please note that if your return failure from DriverEntry after the
        // WDFDRIVER object is created successfully, you don't have to
        // call WPP cleanup because in those cases DriverContextCleanup
        // will be executed when the framework deletes the DriverObject.
        //
        WPP_CLEANUP(DriverObject);
        return status;
    }

    //
    // Call to find out default values to use for all the devices that the
    // driver controls, including whether or not to break on entry.
    //

    SerialGetConfigDefaults(&driverDefaults, hDriver);

    //
    // Break on entry if requested via registry
    //
    if (driverDefaults.ShouldBreakOnEntry) {
        DbgBreakPoint();
    }


    return status;
}
VOID
SerialStartImmediate(
    IN PSERIAL_DEVICE_EXTENSION Extension
    )

/*++

Routine Description:

    This routine will calculate the timeouts needed for the
    write.  It will then hand the request off to the isr.  It
    will need to be careful incase the request has been canceled.

Arguments:

    Extension - A pointer to the serial device extension.

Return Value:

    None.

--*/

{
    LARGE_INTEGER TotalTime = {0};
    BOOLEAN UseATimer;
    SERIAL_TIMEOUTS Timeouts;
    PREQUEST_CONTEXT reqContext;

    reqContext = SerialGetRequestContext(Extension->CurrentImmediateRequest);

    SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS, ">SerialStartImmediate(%p)\n",
                     Extension);

    UseATimer = FALSE;
    reqContext->Status = STATUS_PENDING;

    //
    // Calculate the timeout value needed for the
    // request.  Note that the values stored in the
    // timeout record are in milliseconds.  Note that
    // if the timeout values are zero then we won't start
    // the timer.
    //

    Timeouts = Extension->Timeouts;

    if (Timeouts.WriteTotalTimeoutConstant ||
        Timeouts.WriteTotalTimeoutMultiplier) {

        UseATimer = TRUE;

        //
        // We have some timer values to calculate.
        //

        TotalTime.QuadPart
           = (LONGLONG)((ULONG)Timeouts.WriteTotalTimeoutMultiplier);

        TotalTime.QuadPart += Timeouts.WriteTotalTimeoutConstant;

        TotalTime.QuadPart *= -10000;

    }

    //
    // As the request might be going to the isr, this is a good time
    // to initialize the reference count.
    //

    SERIAL_INIT_REFERENCE(reqContext);

     //
     // We give the request to to the isr to write out.
     // We set a cancel routine that knows how to
     // grab the current write away from the isr.
     //
    SerialSetCancelRoutine(Extension->CurrentImmediateRequest,
                                    SerialCancelImmediate);

    if (UseATimer) {
        BOOLEAN result;

        result = SerialSetTimer(
            Extension->ImmediateTotalTimer,
            TotalTime
            );

        if(result == FALSE) {
            //
            // Since the timer knows about the request we increment
            // the reference count.
            //

            SERIAL_SET_REFERENCE(
                reqContext,
                SERIAL_REF_TOTAL_TIMER
                );
        }
    }

    WdfInterruptSynchronize(
        Extension->WdfInterrupt,
        SerialGiveImmediateToIsr,
        Extension
        );


    SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
                     "<SerialStartImmediate\n");

}
VOID
SerialEvtIoStop(
    IN WDFQUEUE                 Queue,
    IN WDFREQUEST               Request,
    IN ULONG                    ActionFlags
    )
/*++

Routine Description:

     This callback is invoked for every request pending in the driver (not queue) -
     in-flight request. The Action parameter tells us why the callback is invoked -
     because the device is being stopped, removed or suspended. In this
     driver, we have told the framework not to stop or remove when there
     are pending requests, so only reason for this callback is when the system is
     suspending.

Arguments:

    Queue - Queue the request currently belongs to
    Request - Request that is currently out of queue and being processed by the driver
    Action - Reason for this callback

Return Value:

    None. Acknowledge the request so that framework can contiue suspending the
    device.

--*/
{
    PREQUEST_CONTEXT reqContext;

    UNREFERENCED_PARAMETER(Queue);

    reqContext = SerialGetRequestContext(Request);

    SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE,
                    "--> SerialEvtIoStop %x %p\n", ActionFlags, Request);

    //
    // System suspends all the timers before asking the driver to goto
    // sleep. So let us not worry about cancelling the timers. Also the
    // framework will disconnect the interrupt before calling our
    // D0Exit handler so we can be sure that nobody will touch the hardware.
    // So just acknowledge callback to say that we are okay to stop due to
    // system suspend. Please note that since we have taken a power reference
    // we will never idle out when there is an open handle. Also we have told
    // the framework to not stop for resource rebalancing or remove when there are
    // open handles, so let us not worry about that either.
    //
    if (ActionFlags & WdfRequestStopRequestCancelable) {
        PFN_WDF_REQUEST_CANCEL cancelRoutine;

        //
        // Request is in a cancelable state. So unmark cancelable before you
        // acknowledge. We will mark the request cancelable when we resume.
        //
        cancelRoutine = reqContext->CancelRoutine;

        SerialClearCancelRoutine(Request, TRUE);

        //
        // SerialClearCancelRoutine clears the cancel-routine. So set it back
        // in the context. We will need that when we resume.
        //
        reqContext->CancelRoutine = cancelRoutine;

        reqContext->MarkCancelableOnResume = TRUE;

        ActionFlags &= ~WdfRequestStopRequestCancelable;
    }

    ASSERT(ActionFlags == WdfRequestStopActionSuspend);

    WdfRequestStopAcknowledge(Request, FALSE); // Don't requeue the request

    SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_WRITE,
                        "<-- SerialEvtIoStop \n");
}
VOID
SerialStartOrQueue(
    IN PSERIAL_DEVICE_EXTENSION Extension,
    IN WDFREQUEST Request,
    IN WDFQUEUE QueueToExamine,
    IN WDFREQUEST *CurrentOpRequest,
    IN PSERIAL_START_ROUTINE Starter
    )

/*++

Routine Description:

    This routine is used to either start or queue any requst
    that can be queued in the driver.

Arguments:

    Extension - Points to the serial device extension.

    Request - The request to either queue or start.  In either
          case the request will be marked pending.

    QueueToExamine - The queue the request will be place on if there
                     is already an operation in progress.

    CurrentOpRequest - Pointer to a pointer to the request the is current
                   for the queue.  The pointer pointed to will be
                   set with to Request if what CurrentOpRequest points to
                   is NULL.

    Starter - The routine to call if the queue is empty.

Return Value:


--*/

{

    NTSTATUS status;
    PREQUEST_CONTEXT reqContext;
    WDF_REQUEST_PARAMETERS  params;

    reqContext = SerialGetRequestContext(Request);

    WDF_REQUEST_PARAMETERS_INIT(&params);

    WdfRequestGetParameters(
             Request,
             &params);

    //
    // If this is a write request then take the amount of characters
    // to write and add it to the count of characters to write.
    //

    if (params.Type == WdfRequestTypeWrite) {

        Extension->TotalCharsQueued += reqContext->Length;

    } else if ((params.Type == WdfRequestTypeDeviceControl) &&
               ((params.Parameters.DeviceIoControl.IoControlCode == IOCTL_SERIAL_IMMEDIATE_CHAR) ||
                (params.Parameters.DeviceIoControl.IoControlCode == IOCTL_SERIAL_XOFF_COUNTER))) {

        reqContext->IoctlCode = params.Parameters.DeviceIoControl.IoControlCode; // We need this in the destroy callback

        Extension->TotalCharsQueued++;

    }

    if (IsQueueEmpty(QueueToExamine) &&  !(*CurrentOpRequest)) {

        //
        // There were no current operation.  Mark this one as
        // current and start it up.
        //

        *CurrentOpRequest = Request;

        Starter(Extension);

        return;

    } else {

        //
        // We don't know how long the request will be in the
        // queue.  If it gets cancelled while waiting in the queue, we will
        // be notified by EvtCanceledOnQueue callback so that we can readjust
        // the lenght or free the buffer.
        //
        reqContext->Extension = Extension; // We need this in the destroy callback

        status = WdfRequestForwardToIoQueue(Request,  QueueToExamine);
        if(!NT_SUCCESS(status)) {
            SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_READ, "WdfRequestForwardToIoQueue failed%X\n", status);
            ASSERTMSG("WdfRequestForwardToIoQueue failed ", FALSE);
            SerialCompleteRequest(Request, status, 0);
        }

        return;
    }
}
NTSTATUS
SerialCreateTimersAndDpcs(
    IN PSERIAL_DEVICE_EXTENSION pDevExt
    )
/*++

Routine Description:

   This function creates all the timers and DPC objects. All the objects
   are associated with the WDFDEVICE and the callbacks are serialized
   with the device callbacks. Also these objects will be deleted automatically
   when the device is deleted, so there is no need for the driver to explicitly
   delete the objects.

Arguments:

   PDevExt - Pointer to the device extension for the device

Return Value:

    return NTSTATUS

--*/
{
   WDF_DPC_CONFIG dpcConfig;
   WDF_TIMER_CONFIG timerConfig;
   NTSTATUS status;
   WDF_OBJECT_ATTRIBUTES dpcAttributes;
   WDF_OBJECT_ATTRIBUTES timerAttributes;

   //
   // Initialize all the timers used to timeout operations.
   //
   //
   // This timer dpc is fired off if the timer for the total timeout
   // for the read expires.  It will cause the current read to complete.
   //

   WDF_TIMER_CONFIG_INIT(&timerConfig, SerialReadTimeout);

   timerConfig.AutomaticSerialization = TRUE;

   WDF_OBJECT_ATTRIBUTES_INIT(&timerAttributes);
   timerAttributes.ParentObject = pDevExt->WdfDevice;

   status = WdfTimerCreate(&timerConfig,
                           &timerAttributes,
                                    &pDevExt->ReadRequestTotalTimer);

   if (!NT_SUCCESS(status)) {
      SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,  "WdfTimerCreate(ReadRequestTotalTimer) failed  [%#08lx]\n",   status);
      return status;
   }

   //
   // This dpc is fired off if the timer for the interval timeout
   // expires.  If no more characters have been read then the
   // dpc routine will cause the read to complete.  However, if
   // more characters have been read then the dpc routine will
   // resubmit the timer.
   //
   WDF_TIMER_CONFIG_INIT(&timerConfig,   SerialIntervalReadTimeout);

   timerConfig.AutomaticSerialization = TRUE;

   WDF_OBJECT_ATTRIBUTES_INIT(&timerAttributes);
   timerAttributes.ParentObject = pDevExt->WdfDevice;

   status = WdfTimerCreate(&timerConfig,
                           &timerAttributes,
                                        &pDevExt->ReadRequestIntervalTimer);

   if (!NT_SUCCESS(status)) {
      SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,  "WdfTimerCreate(ReadRequestIntervalTimer) failed  [%#08lx]\n",   status);
      return status;
   }

   //
   // This dpc is fired off if the timer for the total timeout
   // for the write expires.  It will queue a dpc routine that
   // will cause the current write to complete.
   //
   //

   WDF_TIMER_CONFIG_INIT(&timerConfig,    SerialWriteTimeout);

   timerConfig.AutomaticSerialization = TRUE;

   WDF_OBJECT_ATTRIBUTES_INIT(&timerAttributes);
   timerAttributes.ParentObject = pDevExt->WdfDevice;

   status = WdfTimerCreate(&timerConfig,
                                &timerAttributes,
                                &pDevExt->WriteRequestTotalTimer);

   if (!NT_SUCCESS(status)) {
      SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,  "WdfTimerCreate(WriteRequestTotalTimer) failed  [%#08lx]\n",   status);
      return status;
   }

   //
   // This dpc is fired off if the transmit immediate char
   // character times out.  The dpc routine will "grab" the
   // request from the isr and time it out.
   //
   WDF_TIMER_CONFIG_INIT(&timerConfig,   SerialTimeoutImmediate);

   timerConfig.AutomaticSerialization = TRUE;

   WDF_OBJECT_ATTRIBUTES_INIT(&timerAttributes);
   timerAttributes.ParentObject = pDevExt->WdfDevice;

   status = WdfTimerCreate(&timerConfig,
                           &timerAttributes,
                                        &pDevExt->ImmediateTotalTimer);

   if (!NT_SUCCESS(status)) {
      SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,  "WdfTimerCreate(ImmediateTotalTimer) failed  [%#08lx]\n",   status);
      return status;
   }

   //
   // This dpc is fired off if the timer used to "timeout" counting
   // the number of characters received after the Xoff ioctl is started
   // expired.
   //

   WDF_TIMER_CONFIG_INIT(&timerConfig,   SerialTimeoutXoff);

   timerConfig.AutomaticSerialization = TRUE;

   WDF_OBJECT_ATTRIBUTES_INIT(&timerAttributes);
   timerAttributes.ParentObject = pDevExt->WdfDevice;

   status = WdfTimerCreate(&timerConfig,
                                    &timerAttributes,
                                    &pDevExt->XoffCountTimer);

    if (!NT_SUCCESS(status)) {
      SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,  "WdfTimerCreate(XoffCountTimer) failed  [%#08lx]\n",   status);
      return status;
   }

   //
   // This dpc is fired off when a timer expires (after one
   // character time), so that code can be invoked that will
   // check to see if we should lower the RTS line when
   // doing transmit toggling.
   //
   WDF_TIMER_CONFIG_INIT(&timerConfig,  SerialInvokePerhapsLowerRTS);

   timerConfig.AutomaticSerialization = TRUE;

   WDF_OBJECT_ATTRIBUTES_INIT(&timerAttributes);
   timerAttributes.ParentObject = pDevExt->WdfDevice;

   status = WdfTimerCreate(&timerConfig,
                           &timerAttributes,
                                    &pDevExt->LowerRTSTimer);
    if (!NT_SUCCESS(status)) {
        SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,  "WdfTimerCreate(LowerRTSTimer) failed  [%#08lx]\n",   status);
        return status;
    }

    //
    // Create a DPC to complete read requests.
    //

   WDF_DPC_CONFIG_INIT(&dpcConfig, SerialCompleteWrite);

   dpcConfig.AutomaticSerialization = TRUE;

   WDF_OBJECT_ATTRIBUTES_INIT(&dpcAttributes);
   dpcAttributes.ParentObject = pDevExt->WdfDevice;

   status = WdfDpcCreate(&dpcConfig,
                                    &dpcAttributes,
                                    &pDevExt->CompleteWriteDpc);
    if (!NT_SUCCESS(status)) {

        SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,  "WdfDpcCreate(CompleteWriteDpc) failed  [%#08lx]\n",   status);
        return status;
    }


    //
    // Create a DPC to complete read requests.
    //

    WDF_DPC_CONFIG_INIT(&dpcConfig, SerialCompleteRead);

    dpcConfig.AutomaticSerialization = TRUE;

    WDF_OBJECT_ATTRIBUTES_INIT(&dpcAttributes);
    dpcAttributes.ParentObject = pDevExt->WdfDevice;

    status = WdfDpcCreate(&dpcConfig,
                                &dpcAttributes,
                                &pDevExt->CompleteReadDpc);

    if (!NT_SUCCESS(status)) {
        SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,  "WdfDpcCreate(CompleteReadDpc) failed  [%#08lx]\n",   status);
        return status;
    }

    //
    // This dpc is fired off if a comm error occurs.  It will
    // cancel all pending reads and writes.
    //
    WDF_DPC_CONFIG_INIT(&dpcConfig, SerialCommError);

    dpcConfig.AutomaticSerialization = TRUE;

    WDF_OBJECT_ATTRIBUTES_INIT(&dpcAttributes);
    dpcAttributes.ParentObject = pDevExt->WdfDevice;

    status = WdfDpcCreate(&dpcConfig,
                                &dpcAttributes,
                                &pDevExt->CommErrorDpc);


    if (!NT_SUCCESS(status)) {

        SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,  "WdfDpcCreate(CommErrorDpc) failed  [%#08lx]\n",   status);
        return status;
    }

    //
    // This dpc is fired off when the transmit immediate char
    // character is given to the hardware.  It will simply complete
    // the request.
    //

   WDF_DPC_CONFIG_INIT(&dpcConfig, SerialCompleteImmediate);

   dpcConfig.AutomaticSerialization = TRUE;

   WDF_OBJECT_ATTRIBUTES_INIT(&dpcAttributes);
   dpcAttributes.ParentObject = pDevExt->WdfDevice;

   status = WdfDpcCreate(&dpcConfig,
                                    &dpcAttributes,
                                    &pDevExt->CompleteImmediateDpc);
    if (!NT_SUCCESS(status)) {
        SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,  "WdfDpcCreate(CompleteImmediateDpc) failed  [%#08lx]\n",   status);
        return status;
    }

    //
    // This dpc is fired off if an event occurs and there was
    // a request waiting on that event.  A dpc routine will execute
    // that completes the request.
    //
    WDF_DPC_CONFIG_INIT(&dpcConfig, SerialCompleteWait);

    dpcConfig.AutomaticSerialization = TRUE;

    WDF_OBJECT_ATTRIBUTES_INIT(&dpcAttributes);
    dpcAttributes.ParentObject = pDevExt->WdfDevice;

    status = WdfDpcCreate(&dpcConfig,
                                &dpcAttributes,
                                &pDevExt->CommWaitDpc);
    if (!NT_SUCCESS(status)) {

        SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,  "WdfDpcCreate(CommWaitDpc) failed  [%#08lx]\n",   status);
        return status;
    }

    //
    // This dpc is fired off if the xoff counter actually runs down
    // to zero.
    //
    WDF_DPC_CONFIG_INIT(&dpcConfig, SerialCompleteXoff);

    dpcConfig.AutomaticSerialization = TRUE;

    WDF_OBJECT_ATTRIBUTES_INIT(&dpcAttributes);
    dpcAttributes.ParentObject = pDevExt->WdfDevice;

    status = WdfDpcCreate(&dpcConfig,
                                &dpcAttributes,
                                &pDevExt->XoffCountCompleteDpc);

    if (!NT_SUCCESS(status)) {
        SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,  "WdfDpcCreate(XoffCountCompleteDpc) failed  [%#08lx]\n",   status);
        return status;
    }


    //
    // This dpc is fired off only from device level to start off
    // a timer that will queue a dpc to check if the RTS line
    // should be lowered when we are doing transmit toggling.
    //
    WDF_DPC_CONFIG_INIT(&dpcConfig, SerialStartTimerLowerRTS);

    dpcConfig.AutomaticSerialization = TRUE;

    WDF_OBJECT_ATTRIBUTES_INIT(&dpcAttributes);
    dpcAttributes.ParentObject = pDevExt->WdfDevice;

    status = WdfDpcCreate(&dpcConfig,
                                &dpcAttributes,
                                &pDevExt->StartTimerLowerRTSDpc);
    if (!NT_SUCCESS(status)) {
        SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,  "WdfDpcCreate(StartTimerLowerRTSDpc) failed  [%#08lx]\n",   status);
        return status;
    }

    return status;
}